From fc91b949f33779b30a47337919e17b635d097c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Mon, 6 Aug 2018 21:55:11 +0200 Subject: [PATCH] Improve TCP/IP error messages --- src/TcpConnector.php | 23 +++++++---------------- tests/TcpConnectorTest.php | 31 ++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/TcpConnector.php b/src/TcpConnector.php index 53d55a34..cb8b9e5f 100644 --- a/src/TcpConnector.php +++ b/src/TcpConnector.php @@ -71,7 +71,7 @@ public function connect($uri) // HHVM fails to parse URIs with a query but no path, so let's simplify our URI here $remote = 'tcp://' . $parts['host'] . ':' . $parts['port']; - $socket = @stream_socket_client( + $stream = @stream_socket_client( $remote, $errno, $errstr, @@ -80,26 +80,17 @@ public function connect($uri) stream_context_create($context) ); - if (false === $socket) { + if (false === $stream) { return Promise\reject(new RuntimeException( sprintf("Connection to %s failed: %s", $uri, $errstr), $errno )); } - stream_set_blocking($socket, 0); - // wait for connection - - return $this->waitForStreamOnce($socket); - } - - private function waitForStreamOnce($stream) - { $loop = $this->loop; - - return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream) { - $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject) { + return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream, $uri) { + $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject, $uri) { $loop->removeWriteStream($stream); // The following hack looks like the only way to @@ -107,12 +98,12 @@ private function waitForStreamOnce($stream) if (false === stream_socket_get_name($stream, true)) { fclose($stream); - $reject(new RuntimeException('Connection refused')); + $reject(new RuntimeException('Connection to ' . $uri . ' failed: Connection refused')); } else { $resolve(new Connection($stream, $loop)); } }); - }, function () use ($loop, $stream) { + }, function () use ($loop, $stream, $uri) { $loop->removeWriteStream($stream); fclose($stream); @@ -123,7 +114,7 @@ private function waitForStreamOnce($stream) } // @codeCoverageIgnoreEnd - throw new RuntimeException('Cancelled while waiting for TCP/IP connection to be established'); + throw new RuntimeException('Connection to ' . $uri . ' cancelled during TCP/IP handshake'); }); } } diff --git a/tests/TcpConnectorTest.php b/tests/TcpConnectorTest.php index caf826e8..65c5f248 100644 --- a/tests/TcpConnectorTest.php +++ b/tests/TcpConnectorTest.php @@ -12,16 +12,19 @@ class TcpConnectorTest extends TestCase { const TIMEOUT = 0.1; - /** @test */ + /** + * @test + * @expectedException RuntimeException + * @expectedExceptionMessage Connection to tcp://127.0.0.1:9999 failed: Connection refused + */ public function connectionToEmptyPortShouldFail() { $loop = Factory::create(); $connector = new TcpConnector($loop); - $connector->connect('127.0.0.1:9999') - ->then($this->expectCallableNever(), $this->expectCallableOnce()); + $promise = $connector->connect('127.0.0.1:9999'); - $loop->run(); + Block\await($promise, $loop, self::TIMEOUT); } /** @test */ @@ -254,7 +257,25 @@ public function cancellingConnectionShouldRejectPromise() $promise = $connector->connect($server->getAddress()); $promise->cancel(); - $this->setExpectedException('RuntimeException', 'Cancelled'); + $this->setExpectedException('RuntimeException', 'Connection to ' . $server->getAddress() . ' cancelled during TCP/IP handshake'); Block\await($promise, $loop); } + + public function testCancelDuringConnectionShouldNotCreateAnyGarbageReferences() + { + if (class_exists('React\Promise\When')) { + $this->markTestSkipped('Not supported on legacy Promise v1 API'); + } + + gc_collect_cycles(); + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $connector = new TcpConnector($loop); + $promise = $connector->connect('127.0.0.1:9999'); + + $promise->cancel(); + unset($promise); + + $this->assertEquals(0, gc_collect_cycles()); + } }