diff --git a/README.md b/README.md
index 3b6ec6d4..3b52482b 100644
--- a/README.md
+++ b/README.md
@@ -238,7 +238,6 @@ $socket->on('error', function (Exception $e) {
Note that this is not a fatal error event, i.e. the server keeps listening for
new connections even after this event.
-
#### getAddress()
The `getAddress(): ?string` method can be used to
diff --git a/examples/01-echo-server.php b/examples/01-echo-server.php
index a690f07a..e073c230 100644
--- a/examples/01-echo-server.php
+++ b/examples/01-echo-server.php
@@ -19,6 +19,7 @@
// You can also use systemd socket activation and listen on an inherited file descriptor:
//
// $ systemd-socket-activate -l 8000 php examples/01-echo-server.php php://fd/3
+// $ telnet localhost 8000
require __DIR__ . '/../vendor/autoload.php';
@@ -31,8 +32,14 @@
$socket->on('connection', function (React\Socket\ConnectionInterface $connection) {
echo '[' . $connection->getRemoteAddress() . ' connected]' . PHP_EOL;
$connection->pipe($connection);
+
+ $connection->on('close', function () use ($connection) {
+ echo '[' . $connection->getRemoteAddress() . ' disconnected]' . PHP_EOL;
+ });
});
-$socket->on('error', 'printf');
+$socket->on('error', function (Exception $e) {
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
+});
echo 'Listening on ' . $socket->getAddress() . PHP_EOL;
diff --git a/examples/02-chat-server.php b/examples/02-chat-server.php
index 3e2f354c..862a22de 100644
--- a/examples/02-chat-server.php
+++ b/examples/02-chat-server.php
@@ -19,6 +19,7 @@
// You can also use systemd socket activation and listen on an inherited file descriptor:
//
// $ systemd-socket-activate -l 8000 php examples/02-chat-server.php php://fd/3
+// $ telnet localhost 8000
require __DIR__ . '/../vendor/autoload.php';
@@ -30,9 +31,11 @@
$socket = new React\Socket\LimitingServer($socket, null);
-$socket->on('connection', function (React\Socket\ConnectionInterface $client) use ($socket) {
+$socket->on('connection', function (React\Socket\ConnectionInterface $connection) use ($socket) {
+ echo '[' . $connection->getRemoteAddress() . ' connected]' . PHP_EOL;
+
// whenever a new message comes in
- $client->on('data', function ($data) use ($client, $socket) {
+ $connection->on('data', function ($data) use ($connection, $socket) {
// remove any non-word characters (just for the demo)
$data = trim(preg_replace('/[^\w\d \.\,\-\!\?]/u', '', $data));
@@ -42,13 +45,19 @@
}
// prefix with client IP and broadcast to all connected clients
- $data = trim(parse_url($client->getRemoteAddress(), PHP_URL_HOST), '[]') . ': ' . $data . PHP_EOL;
+ $data = trim(parse_url($connection->getRemoteAddress(), PHP_URL_HOST), '[]') . ': ' . $data . PHP_EOL;
foreach ($socket->getConnections() as $connection) {
$connection->write($data);
}
});
+
+ $connection->on('close', function () use ($connection) {
+ echo '[' . $connection->getRemoteAddress() . ' disconnected]' . PHP_EOL;
+ });
});
-$socket->on('error', 'printf');
+$socket->on('error', function (Exception $e) {
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
+});
echo 'Listening on ' . $socket->getAddress() . PHP_EOL;
diff --git a/examples/03-http-server.php b/examples/03-http-server.php
index 09606ab7..dc861a1c 100644
--- a/examples/03-http-server.php
+++ b/examples/03-http-server.php
@@ -15,14 +15,14 @@
// $ php examples/03-http-server.php 127.0.0.1:8000
// $ curl -v http://localhost:8000/
// $ ab -n1000 -c10 http://localhost:8000/
-// $ docker run -it --rm --net=host jordi/ab ab -n1000 -c10 http://localhost:8000/
+// $ docker run -it --rm --net=host jordi/ab -n1000 -c10 http://localhost:8000/
//
// You can also run a secure HTTPS echo server like this:
//
// $ php examples/03-http-server.php tls://127.0.0.1:8000 examples/localhost.pem
// $ curl -v --insecure https://localhost:8000/
// $ ab -n1000 -c10 https://localhost:8000/
-// $ docker run -it --rm --net=host jordi/ab ab -n1000 -c10 https://localhost:8000/
+// $ docker run -it --rm --net=host jordi/ab -n1000 -c10 https://localhost:8000/
//
// You can also run a Unix domain socket (UDS) server like this:
//
@@ -32,6 +32,9 @@
// You can also use systemd socket activation and listen on an inherited file descriptor:
//
// $ systemd-socket-activate -l 8000 php examples/03-http-server.php php://fd/3
+// $ curl -v --insecure https://localhost:8000/
+// $ ab -n1000 -c10 https://localhost:8000/
+// $ docker run -it --rm --net=host jordi/ab -n1000 -c10 https://localhost:8000/
require __DIR__ . '/../vendor/autoload.php';
@@ -42,12 +45,20 @@
));
$socket->on('connection', function (React\Socket\ConnectionInterface $connection) {
+ echo '[' . $connection->getRemoteAddress() . ' connected]' . PHP_EOL;
+
$connection->once('data', function () use ($connection) {
$body = "
Hello world!
\r\n";
$connection->end("HTTP/1.1 200 OK\r\nContent-Length: " . strlen($body) . "\r\nConnection: close\r\n\r\n" . $body);
});
+
+ $connection->on('close', function () use ($connection) {
+ echo '[' . $connection->getRemoteAddress() . ' disconnected]' . PHP_EOL;
+ });
});
-$socket->on('error', 'printf');
+$socket->on('error', function (Exception $e) {
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
+});
echo 'Listening on ' . strtr($socket->getAddress(), array('tcp:' => 'http:', 'tls:' => 'https:')) . PHP_EOL;
diff --git a/examples/91-benchmark-server.php b/examples/91-benchmark-server.php
index 0e3e2025..6a0e7828 100644
--- a/examples/91-benchmark-server.php
+++ b/examples/91-benchmark-server.php
@@ -21,6 +21,13 @@
// $ php examples/91-benchmark-server.php unix:///tmp/server.sock
// $ nc -N -U /tmp/server.sock
// $ dd if=/dev/zero bs=1M count=1000 | nc -N -U /tmp/server.sock
+//
+// You can also use systemd socket activation and listen on an inherited file descriptor:
+//
+// $ systemd-socket-activate -l 8000 php examples/91-benchmark-server.php php://fd/3
+// $ telnet localhost 8000
+// $ echo hello world | nc -N localhost 8000
+// $ dd if=/dev/zero bs=1M count=1000 | nc -N localhost 8000
require __DIR__ . '/../vendor/autoload.php';
@@ -31,7 +38,7 @@
));
$socket->on('connection', function (React\Socket\ConnectionInterface $connection) {
- echo '[connected]' . PHP_EOL;
+ echo '[' . $connection->getRemoteAddress() . ' connected]' . PHP_EOL;
// count the number of bytes received from this connection
$bytes = 0;
@@ -43,10 +50,12 @@
$t = microtime(true);
$connection->on('close', function () use ($connection, $t, &$bytes) {
$t = microtime(true) - $t;
- echo '[disconnected after receiving ' . $bytes . ' bytes in ' . round($t, 3) . 's => ' . round($bytes / $t / 1024 / 1024, 1) . ' MiB/s]' . PHP_EOL;
+ echo '[' . $connection->getRemoteAddress() . ' disconnected after receiving ' . $bytes . ' bytes in ' . round($t, 3) . 's => ' . round($bytes / $t / 1024 / 1024, 1) . ' MiB/s]' . PHP_EOL;
});
});
-$socket->on('error', 'printf');
+$socket->on('error', function (Exception $e) {
+ echo 'Error: ' . $e->getMessage() . PHP_EOL;
+});
echo 'Listening on ' . $socket->getAddress() . PHP_EOL;
diff --git a/src/Connector.php b/src/Connector.php
index 02c9561b..93477bd7 100644
--- a/src/Connector.php
+++ b/src/Connector.php
@@ -169,7 +169,8 @@ public function connect($uri)
if (!isset($this->connectors[$scheme])) {
return \React\Promise\reject(new \RuntimeException(
- 'No connector available for URI scheme "' . $scheme . '"'
+ 'No connector available for URI scheme "' . $scheme . '" (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
));
}
diff --git a/src/DnsConnector.php b/src/DnsConnector.php
index b68d7ea6..0b51a52c 100644
--- a/src/DnsConnector.php
+++ b/src/DnsConnector.php
@@ -29,7 +29,10 @@ public function connect($uri)
}
if (!$parts || !isset($parts['host'])) {
- return Promise\reject(new \InvalidArgumentException('Given URI "' . $original . '" is invalid'));
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $original . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
}
$host = \trim($parts['host'], '[]');
@@ -91,7 +94,10 @@ function ($_, $reject) use (&$promise, &$resolved, $uri) {
// cancellation should reject connection attempt
// reject DNS resolution with custom reason, otherwise rely on connection cancellation below
if ($resolved === null) {
- $reject(new \RuntimeException('Connection to ' . $uri . ' cancelled during DNS lookup'));
+ $reject(new \RuntimeException(
+ 'Connection to ' . $uri . ' cancelled during DNS lookup (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ ));
}
// (try to) cancel pending DNS lookup / connection attempt
diff --git a/src/FdServer.php b/src/FdServer.php
index 4032d043..2c7a6c4d 100644
--- a/src/FdServer.php
+++ b/src/FdServer.php
@@ -81,7 +81,10 @@ public function __construct($fd, LoopInterface $loop = null)
$fd = (int) $m[1];
}
if (!\is_int($fd) || $fd < 0 || $fd >= \PHP_INT_MAX) {
- throw new \InvalidArgumentException('Invalid FD number given');
+ throw new \InvalidArgumentException(
+ 'Invalid FD number given (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
}
$this->loop = $loop ?: Loop::get();
@@ -95,7 +98,10 @@ public function __construct($fd, LoopInterface $loop = null)
$errno = isset($m[1]) ? (int) $m[1] : 0;
$errstr = isset($m[2]) ? $m[2] : $error['message'];
- throw new \RuntimeException('Failed to listen on FD ' . $fd . ': ' . $errstr, $errno);
+ throw new \RuntimeException(
+ 'Failed to listen on FD ' . $fd . ': ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ );
}
$meta = \stream_get_meta_data($this->master);
@@ -105,7 +111,10 @@ public function __construct($fd, LoopInterface $loop = null)
$errno = \defined('SOCKET_ENOTSOCK') ? \SOCKET_ENOTSOCK : 88;
$errstr = \function_exists('socket_strerror') ? \socket_strerror($errno) : 'Not a socket';
- throw new \RuntimeException('Failed to listen on FD ' . $fd . ': ' . $errstr, $errno);
+ throw new \RuntimeException(
+ 'Failed to listen on FD ' . $fd . ': ' . $errstr . ' (ENOTSOCK)',
+ $errno
+ );
}
// Socket should not have a peer address if this is a listening socket.
@@ -116,7 +125,10 @@ public function __construct($fd, LoopInterface $loop = null)
$errno = \defined('SOCKET_EISCONN') ? \SOCKET_EISCONN : 106;
$errstr = \function_exists('socket_strerror') ? \socket_strerror($errno) : 'Socket is connected';
- throw new \RuntimeException('Failed to listen on FD ' . $fd . ': ' . $errstr, $errno);
+ throw new \RuntimeException(
+ 'Failed to listen on FD ' . $fd . ': ' . $errstr . ' (EISCONN)',
+ $errno
+ );
}
// Assume this is a Unix domain socket (UDS) when its listening address doesn't parse as a valid URL with a port.
diff --git a/src/HappyEyeBallsConnectionBuilder.php b/src/HappyEyeBallsConnectionBuilder.php
index 6183b177..6bd07168 100644
--- a/src/HappyEyeBallsConnectionBuilder.php
+++ b/src/HappyEyeBallsConnectionBuilder.php
@@ -103,7 +103,10 @@ public function connect()
return $deferred->promise();
})->then($lookupResolve(Message::TYPE_A));
}, function ($_, $reject) use ($that, &$timer) {
- $reject(new \RuntimeException('Connection to ' . $that->uri . ' cancelled' . (!$that->connectionPromises ? ' during DNS lookup' : '')));
+ $reject(new \RuntimeException(
+ 'Connection to ' . $that->uri . ' cancelled' . (!$that->connectionPromises ? ' during DNS lookup' : '') . ' (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ ));
$_ = $reject = null;
$that->cleanUp();
@@ -143,7 +146,11 @@ public function resolve($type, $reject)
}
if ($that->hasBeenResolved() && $that->ipsCount === 0) {
- $reject(new \RuntimeException($that->error()));
+ $reject(new \RuntimeException(
+ $that->error(),
+ 0,
+ $e
+ ));
}
// Exception already handled above, so don't throw an unhandled rejection here
@@ -201,7 +208,11 @@ public function check($resolve, $reject)
if ($that->ipsCount === $that->failureCount) {
$that->cleanUp();
- $reject(new \RuntimeException($that->error()));
+ $reject(new \RuntimeException(
+ $that->error(),
+ $e->getCode(),
+ $e
+ ));
}
});
diff --git a/src/HappyEyeBallsConnector.php b/src/HappyEyeBallsConnector.php
index f7ea0ecf..0a7c6ecb 100644
--- a/src/HappyEyeBallsConnector.php
+++ b/src/HappyEyeBallsConnector.php
@@ -41,7 +41,10 @@ public function connect($uri)
}
if (!$parts || !isset($parts['host'])) {
- return Promise\reject(new \InvalidArgumentException('Given URI "' . $original . '" is invalid'));
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $original . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
}
$host = \trim($parts['host'], '[]');
diff --git a/src/SecureConnector.php b/src/SecureConnector.php
index e6e85c4d..03c6e361 100644
--- a/src/SecureConnector.php
+++ b/src/SecureConnector.php
@@ -34,7 +34,10 @@ public function connect($uri)
$parts = \parse_url($uri);
if (!$parts || !isset($parts['scheme']) || $parts['scheme'] !== 'tls') {
- return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $uri . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
}
$context = $this->context;
@@ -105,7 +108,10 @@ function ($resolve, $reject) use ($promise) {
},
function ($_, $reject) use (&$promise, $uri, &$connected) {
if ($connected) {
- $reject(new \RuntimeException('Connection to ' . $uri . ' cancelled during TLS handshake'));
+ $reject(new \RuntimeException(
+ 'Connection to ' . $uri . ' cancelled during TLS handshake (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ ));
}
$promise->cancel();
diff --git a/src/SocketServer.php b/src/SocketServer.php
index fa379732..2ea03bae 100644
--- a/src/SocketServer.php
+++ b/src/SocketServer.php
@@ -52,7 +52,10 @@ public function __construct($uri, array $context = array(), LoopInterface $loop
$server = new FdServer($uri, $loop);
} else {
if (preg_match('#^(?:\w+://)?\d+$#', $uri)) {
- throw new \InvalidArgumentException('Invalid URI given');
+ throw new \InvalidArgumentException(
+ 'Invalid URI given (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
}
$server = new TcpServer(str_replace('tls://', '', $uri), $loop, $context['tcp']);
@@ -110,26 +113,75 @@ public static function accept($socket)
// stream_socket_accept(): accept failed: Connection timed out
$error = \error_get_last();
$errstr = \preg_replace('#.*: #', '', $error['message']);
-
- // Go through list of possible error constants to find matching errno.
- // @codeCoverageIgnoreStart
- $errno = 0;
- if (\function_exists('socket_strerror')) {
- foreach (\get_defined_constants(false) as $name => $value) {
- if (\strpos($name, 'SOCKET_E') === 0 && \socket_strerror($value) === $errstr) {
- $errno = $value;
- break;
- }
- }
- }
- // @codeCoverageIgnoreEnd
+ $errno = self::errno($errstr);
throw new \RuntimeException(
- 'Unable to accept new connection: ' . $errstr,
+ 'Unable to accept new connection: ' . $errstr . self::errconst($errno),
$errno
);
}
return $newSocket;
}
+
+ /**
+ * [Internal] Returns errno value for given errstr
+ *
+ * The errno and errstr values describes the type of error that has been
+ * encountered. This method tries to look up the given errstr and find a
+ * matching errno value which can be useful to provide more context to error
+ * messages. It goes through the list of known errno constants when
+ * ext-sockets is available to find an errno matching the given errstr.
+ *
+ * @param string $errstr
+ * @return int errno value (e.g. value of `SOCKET_ECONNREFUSED`) or 0 if not found
+ * @internal
+ * @copyright Copyright (c) 2018 Christian Lück, taken from https://github.com/clue/errno with permission
+ * @codeCoverageIgnore
+ */
+ public static function errno($errstr)
+ {
+ if (\function_exists('socket_strerror')) {
+ foreach (\get_defined_constants(false) as $name => $value) {
+ if (\strpos($name, 'SOCKET_E') === 0 && \socket_strerror($value) === $errstr) {
+ return $value;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * [Internal] Returns errno constant name for given errno value
+ *
+ * The errno value describes the type of error that has been encountered.
+ * This method tries to look up the given errno value and find a matching
+ * errno constant name which can be useful to provide more context and more
+ * descriptive error messages. It goes through the list of known errno
+ * constants when ext-sockets is available to find the matching errno
+ * constant name.
+ *
+ * Because this method is used to append more context to error messages, the
+ * constant name will be prefixed with a space and put between parenthesis
+ * when found.
+ *
+ * @param int $errno
+ * @return string e.g. ` (ECONNREFUSED)` or empty string if no matching const for the given errno could be found
+ * @internal
+ * @copyright Copyright (c) 2018 Christian Lück, taken from https://github.com/clue/errno with permission
+ * @codeCoverageIgnore
+ */
+ public static function errconst($errno)
+ {
+ if (\function_exists('socket_strerror')) {
+ foreach (\get_defined_constants(false) as $name => $value) {
+ if ($value === $errno && \strpos($name, 'SOCKET_E') === 0) {
+ return ' (' . \substr($name, 7) . ')';
+ }
+ }
+ }
+
+ return '';
+ }
}
diff --git a/src/StreamEncryption.php b/src/StreamEncryption.php
index 8321b699..4aa7fca0 100644
--- a/src/StreamEncryption.php
+++ b/src/StreamEncryption.php
@@ -125,13 +125,13 @@ public function toggleCrypto($socket, Deferred $deferred, $toggle, $method)
if (\feof($socket) || $error === null) {
// EOF or failed without error => connection closed during handshake
$d->reject(new \UnexpectedValueException(
- 'Connection lost during TLS handshake',
- \defined('SOCKET_ECONNRESET') ? \SOCKET_ECONNRESET : 0
+ 'Connection lost during TLS handshake (ECONNRESET)',
+ \defined('SOCKET_ECONNRESET') ? \SOCKET_ECONNRESET : 104
));
} else {
// handshake failed with error message
$d->reject(new \UnexpectedValueException(
- 'Unable to complete TLS handshake: ' . $error
+ $error
));
}
} else {
diff --git a/src/TcpConnector.php b/src/TcpConnector.php
index 9e0a8bc6..6195c6a7 100644
--- a/src/TcpConnector.php
+++ b/src/TcpConnector.php
@@ -27,12 +27,18 @@ public function connect($uri)
$parts = \parse_url($uri);
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
- return Promise\reject(new \InvalidArgumentException('Given URI "' . $uri . '" is invalid'));
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $uri . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
}
$ip = \trim($parts['host'], '[]');
if (false === \filter_var($ip, \FILTER_VALIDATE_IP)) {
- return Promise\reject(new \InvalidArgumentException('Given URI "' . $ip . '" does not contain a valid host IP'));
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
}
// use context given in constructor
@@ -85,7 +91,7 @@ public function connect($uri)
if (false === $stream) {
return Promise\reject(new \RuntimeException(
- \sprintf("Connection to %s failed: %s", $uri, $errstr),
+ 'Connection to ' . $uri . ' failed: ' . $errstr . SocketServer::errconst($errno),
$errno
));
}
@@ -125,7 +131,10 @@ public function connect($uri)
// @codeCoverageIgnoreEnd
\fclose($stream);
- $reject(new \RuntimeException('Connection to ' . $uri . ' failed: ' . $errstr, $errno));
+ $reject(new \RuntimeException(
+ 'Connection to ' . $uri . ' failed: ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ ));
} else {
$resolve(new Connection($stream, $loop));
}
@@ -141,7 +150,10 @@ public function connect($uri)
}
// @codeCoverageIgnoreEnd
- throw new \RuntimeException('Connection to ' . $uri . ' cancelled during TCP/IP handshake');
+ throw new \RuntimeException(
+ 'Connection to ' . $uri . ' cancelled during TCP/IP handshake (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ );
});
}
}
diff --git a/src/TcpServer.php b/src/TcpServer.php
index 622d5575..53d5317b 100644
--- a/src/TcpServer.php
+++ b/src/TcpServer.php
@@ -154,11 +154,17 @@ public function __construct($uri, LoopInterface $loop = null, array $context = a
// ensure URI contains TCP scheme, host and port
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
- throw new \InvalidArgumentException('Invalid URI "' . $uri . '" given');
+ throw new \InvalidArgumentException(
+ 'Invalid URI "' . $uri . '" given (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
}
if (false === \filter_var(\trim($parts['host'], '[]'), \FILTER_VALIDATE_IP)) {
- throw new \InvalidArgumentException('Given URI "' . $uri . '" does not contain a valid host IP');
+ throw new \InvalidArgumentException(
+ 'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
}
$this->master = @\stream_socket_server(
@@ -169,7 +175,16 @@ public function __construct($uri, LoopInterface $loop = null, array $context = a
\stream_context_create(array('socket' => $context + array('backlog' => 511)))
);
if (false === $this->master) {
- throw new \RuntimeException('Failed to listen on "' . $uri . '": ' . $errstr, $errno);
+ if ($errno === 0) {
+ // PHP does not seem to report errno, so match errno from errstr
+ // @link https://3v4l.org/3qOBl
+ $errno = SocketServer::errno($errstr);
+ }
+
+ throw new \RuntimeException(
+ 'Failed to listen on "' . $uri . '": ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ );
}
\stream_set_blocking($this->master, false);
diff --git a/src/TimeoutConnector.php b/src/TimeoutConnector.php
index 02ccceee..332369f8 100644
--- a/src/TimeoutConnector.php
+++ b/src/TimeoutConnector.php
@@ -40,8 +40,8 @@ private static function handler($uri)
return function (\Exception $e) use ($uri) {
if ($e instanceof TimeoutException) {
throw new \RuntimeException(
- 'Connection to ' . $uri . ' timed out after ' . $e->getTimeout() . ' seconds',
- \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 0
+ 'Connection to ' . $uri . ' timed out after ' . $e->getTimeout() . ' seconds (ETIMEDOUT)',
+ \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110
);
}
diff --git a/src/UnixConnector.php b/src/UnixConnector.php
index 4cfb5a37..513fb51b 100644
--- a/src/UnixConnector.php
+++ b/src/UnixConnector.php
@@ -28,13 +28,19 @@ public function connect($path)
if (\strpos($path, '://') === false) {
$path = 'unix://' . $path;
} elseif (\substr($path, 0, 7) !== 'unix://') {
- return Promise\reject(new \InvalidArgumentException('Given URI "' . $path . '" is invalid'));
+ return Promise\reject(new \InvalidArgumentException(
+ 'Given URI "' . $path . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ ));
}
$resource = @\stream_socket_client($path, $errno, $errstr, 1.0);
if (!$resource) {
- return Promise\reject(new \RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno));
+ return Promise\reject(new \RuntimeException(
+ 'Unable to connect to unix domain socket "' . $path . '": ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ ));
}
$connection = new Connection($resource, $this->loop);
diff --git a/src/UnixServer.php b/src/UnixServer.php
index 25accbe0..668e8cb3 100644
--- a/src/UnixServer.php
+++ b/src/UnixServer.php
@@ -57,7 +57,10 @@ public function __construct($path, LoopInterface $loop = null, array $context =
if (\strpos($path, '://') === false) {
$path = 'unix://' . $path;
} elseif (\substr($path, 0, 7) !== 'unix://') {
- throw new \InvalidArgumentException('Given URI "' . $path . '" is invalid');
+ throw new \InvalidArgumentException(
+ 'Given URI "' . $path . '" is invalid (EINVAL)',
+ \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
+ );
}
$this->master = @\stream_socket_server(
@@ -79,7 +82,10 @@ public function __construct($path, LoopInterface $loop = null, array $context =
}
}
- throw new \RuntimeException('Failed to listen on Unix domain socket "' . $path . '": ' . $errstr, $errno);
+ throw new \RuntimeException(
+ 'Failed to listen on Unix domain socket "' . $path . '": ' . $errstr . SocketServer::errconst($errno),
+ $errno
+ );
}
\stream_set_blocking($this->master, 0);
diff --git a/tests/ConnectorTest.php b/tests/ConnectorTest.php
index f85d8b1e..b8ac04c2 100644
--- a/tests/ConnectorTest.php
+++ b/tests/ConnectorTest.php
@@ -169,7 +169,12 @@ public function testConnectorWithUnknownSchemeAlwaysFails()
$connector = new Connector(array(), $loop);
$promise = $connector->connect('unknown://google.com:80');
- $promise->then(null, $this->expectCallableOnce());
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'RuntimeException',
+ 'No connector available for URI scheme "unknown" (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testConnectorWithDisabledTcpDefaultSchemeAlwaysFails()
@@ -180,7 +185,12 @@ public function testConnectorWithDisabledTcpDefaultSchemeAlwaysFails()
), $loop);
$promise = $connector->connect('google.com:80');
- $promise->then(null, $this->expectCallableOnce());
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'RuntimeException',
+ 'No connector available for URI scheme "tcp" (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testConnectorWithDisabledTcpSchemeAlwaysFails()
@@ -191,7 +201,12 @@ public function testConnectorWithDisabledTcpSchemeAlwaysFails()
), $loop);
$promise = $connector->connect('tcp://google.com:80');
- $promise->then(null, $this->expectCallableOnce());
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'RuntimeException',
+ 'No connector available for URI scheme "tcp" (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testConnectorWithDisabledTlsSchemeAlwaysFails()
@@ -202,7 +217,12 @@ public function testConnectorWithDisabledTlsSchemeAlwaysFails()
), $loop);
$promise = $connector->connect('tls://google.com:443');
- $promise->then(null, $this->expectCallableOnce());
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'RuntimeException',
+ 'No connector available for URI scheme "tls" (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testConnectorWithDisabledUnixSchemeAlwaysFails()
@@ -213,7 +233,12 @@ public function testConnectorWithDisabledUnixSchemeAlwaysFails()
), $loop);
$promise = $connector->connect('unix://demo.sock');
- $promise->then(null, $this->expectCallableOnce());
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'RuntimeException',
+ 'No connector available for URI scheme "unix" (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testConnectorUsesGivenResolverInstance()
diff --git a/tests/DnsConnectorTest.php b/tests/DnsConnectorTest.php
index 16e73fd6..2dbc4020 100644
--- a/tests/DnsConnectorTest.php
+++ b/tests/DnsConnectorTest.php
@@ -78,7 +78,11 @@ public function testRejectsImmediatelyIfUriIsInvalid()
$promise = $this->connector->connect('////');
- $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'InvalidArgumentException',
+ 'Given URI "////" is invalid (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testConnectRejectsIfGivenIpAndTcpConnectorRejectsWithRuntimeException()
@@ -167,7 +171,11 @@ public function testCancelDuringDnsCancelsDnsAndDoesNotStartTcpConnection()
$promise = $this->connector->connect('example.com:80');
$promise->cancel();
- $this->setExpectedException('RuntimeException', 'Connection to tcp://example.com:80 cancelled during DNS lookup');
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection to tcp://example.com:80 cancelled during DNS lookup (ECONNABORTED)',
+ defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
+ );
$this->throwRejection($promise);
}
@@ -196,7 +204,10 @@ public function testCancelDuringTcpConnectionCancelsTcpConnectionWithTcpRejectio
$first = new Deferred();
$this->resolver->expects($this->once())->method('resolve')->with($this->equalTo('example.com'))->willReturn($first->promise());
$pending = new Promise\Promise(function () { }, function () {
- throw new \RuntimeException('Connection cancelled');
+ throw new \RuntimeException(
+ 'Connection cancelled',
+ defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
+ );
});
$this->tcp->expects($this->once())->method('connect')->with($this->equalTo('1.2.3.4:80?hostname=example.com'))->willReturn($pending);
@@ -205,7 +216,11 @@ public function testCancelDuringTcpConnectionCancelsTcpConnectionWithTcpRejectio
$promise->cancel();
- $this->setExpectedException('RuntimeException', 'Connection cancelled');
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection cancelled',
+ defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
+ );
$this->throwRejection($promise);
}
diff --git a/tests/FdServerTest.php b/tests/FdServerTest.php
index b23231e4..3df1b296 100644
--- a/tests/FdServerTest.php
+++ b/tests/FdServerTest.php
@@ -30,7 +30,11 @@ public function testCtorThrowsForInvalidFd()
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
$loop->expects($this->never())->method('addReadStream');
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid FD number given (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new FdServer(-1, $loop);
}
@@ -39,7 +43,11 @@ public function testCtorThrowsForInvalidUrl()
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
$loop->expects($this->never())->method('addReadStream');
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid FD number given (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new FdServer('tcp://127.0.0.1:8080', $loop);
}
@@ -56,7 +64,7 @@ public function testCtorThrowsForUnknownFd()
$this->setExpectedException(
'RuntimeException',
- 'Failed to listen on FD ' . $fd . ': ' . (function_exists('socket_strerror') ? socket_strerror(SOCKET_EBADF) : 'Bad file descriptor'),
+ 'Failed to listen on FD ' . $fd . ': ' . (function_exists('socket_strerror') ? socket_strerror(SOCKET_EBADF) . ' (EBADF)' : 'Bad file descriptor'),
defined('SOCKET_EBADF') ? SOCKET_EBADF : 9
);
new FdServer($fd, $loop);
@@ -77,7 +85,7 @@ public function testCtorThrowsIfFdIsAFileAndNotASocket()
$this->setExpectedException(
'RuntimeException',
- 'Failed to listen on FD ' . $fd . ': ' . (function_exists('socket_strerror') ? socket_strerror(SOCKET_ENOTSOCK) : 'Not a socket'),
+ 'Failed to listen on FD ' . $fd . ': ' . (function_exists('socket_strerror') ? socket_strerror(SOCKET_ENOTSOCK) : 'Not a socket') . ' (ENOTSOCK)',
defined('SOCKET_ENOTSOCK') ? SOCKET_ENOTSOCK : 88
);
new FdServer($fd, $loop);
@@ -100,7 +108,7 @@ public function testCtorThrowsIfFdIsAConnectedSocketInsteadOfServerSocket()
$this->setExpectedException(
'RuntimeException',
- 'Failed to listen on FD ' . $fd . ': ' . (function_exists('socket_strerror') ? socket_strerror(SOCKET_EISCONN) : 'Socket is connected'),
+ 'Failed to listen on FD ' . $fd . ': ' . (function_exists('socket_strerror') ? socket_strerror(SOCKET_EISCONN) : 'Socket is connected') . ' (EISCONN)',
defined('SOCKET_EISCONN') ? SOCKET_EISCONN : 106
);
new FdServer($fd, $loop);
@@ -353,7 +361,7 @@ public function testEmitsErrorWhenAcceptListenerFails()
*/
public function testEmitsTimeoutErrorWhenAcceptListenerFails(\RuntimeException $exception)
{
- $this->assertEquals('Unable to accept new connection: ' . socket_strerror(SOCKET_ETIMEDOUT), $exception->getMessage());
+ $this->assertEquals('Unable to accept new connection: ' . socket_strerror(SOCKET_ETIMEDOUT) . ' (ETIMEDOUT)', $exception->getMessage());
$this->assertEquals(SOCKET_ETIMEDOUT, $exception->getCode());
}
diff --git a/tests/FunctionalConnectorTest.php b/tests/FunctionalConnectorTest.php
index f591295a..1536a2ea 100644
--- a/tests/FunctionalConnectorTest.php
+++ b/tests/FunctionalConnectorTest.php
@@ -168,7 +168,7 @@ public function testCancelPendingTlsConnectionDuringTlsHandshakeShouldCloseTcpCo
$this->fail();
} catch (\Exception $e) {
$this->assertInstanceOf('RuntimeException', $e);
- $this->assertEquals('Connection to ' . $uri . ' cancelled during TLS handshake', $e->getMessage());
+ $this->assertEquals('Connection to ' . $uri . ' cancelled during TLS handshake (ECONNABORTED)', $e->getMessage());
}
}
diff --git a/tests/FunctionalSecureServerTest.php b/tests/FunctionalSecureServerTest.php
index 58b1cf4d..b81be4d5 100644
--- a/tests/FunctionalSecureServerTest.php
+++ b/tests/FunctionalSecureServerTest.php
@@ -664,11 +664,11 @@ public function testEmitsErrorIfConnectionIsClosedBeforeHandshake()
$error = Block\await($errorEvent, $loop, self::TIMEOUT);
- // Connection from tcp://127.0.0.1:39528 failed during TLS handshake: Connection lost during TLS handshak
+ // Connection from tcp://127.0.0.1:39528 failed during TLS handshake: Connection lost during TLS handshake (ECONNRESET)
$this->assertInstanceOf('RuntimeException', $error);
$this->assertStringStartsWith('Connection from tcp://', $error->getMessage());
- $this->assertStringEndsWith('failed during TLS handshake: Connection lost during TLS handshake', $error->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 0, $error->getCode());
+ $this->assertStringEndsWith('failed during TLS handshake: Connection lost during TLS handshake (ECONNRESET)', $error->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104, $error->getCode());
$this->assertNull($error->getPrevious());
}
@@ -692,11 +692,11 @@ public function testEmitsErrorIfConnectionIsClosedWithIncompleteHandshake()
$error = Block\await($errorEvent, $loop, self::TIMEOUT);
- // Connection from tcp://127.0.0.1:39528 failed during TLS handshake: Connection lost during TLS handshak
+ // Connection from tcp://127.0.0.1:39528 failed during TLS handshake: Connection lost during TLS handshake (ECONNRESET)
$this->assertInstanceOf('RuntimeException', $error);
$this->assertStringStartsWith('Connection from tcp://', $error->getMessage());
- $this->assertStringEndsWith('failed during TLS handshake: Connection lost during TLS handshake', $error->getMessage());
- $this->assertEquals(defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 0, $error->getCode());
+ $this->assertStringEndsWith('failed during TLS handshake: Connection lost during TLS handshake (ECONNRESET)', $error->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNRESET') ? SOCKET_ECONNRESET : 104, $error->getCode());
$this->assertNull($error->getPrevious());
}
diff --git a/tests/FunctionalTcpServerTest.php b/tests/FunctionalTcpServerTest.php
index 3f228a06..eae1ceaa 100644
--- a/tests/FunctionalTcpServerTest.php
+++ b/tests/FunctionalTcpServerTest.php
@@ -350,7 +350,11 @@ public function testFailsToListenOnInvalidUri()
{
$loop = Factory::create();
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid URI "tcp://///" given (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new TcpServer('///', $loop);
}
@@ -358,7 +362,11 @@ public function testFailsToListenOnUriWithoutPort()
{
$loop = Factory::create();
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid URI "tcp://127.0.0.1" given (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new TcpServer('127.0.0.1', $loop);
}
@@ -366,7 +374,11 @@ public function testFailsToListenOnUriWithWrongScheme()
{
$loop = Factory::create();
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid URI "udp://127.0.0.1:0" given (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new TcpServer('udp://127.0.0.1:0', $loop);
}
@@ -374,7 +386,11 @@ public function testFailsToListenOnUriWIthHostname()
{
$loop = Factory::create();
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Given URI "tcp://localhost:8080" does not contain a valid host IP (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new TcpServer('localhost:8080', $loop);
}
}
diff --git a/tests/HappyEyeBallsConnectionBuilderTest.php b/tests/HappyEyeBallsConnectionBuilderTest.php
index 1d7f815e..59b1c1fd 100644
--- a/tests/HappyEyeBallsConnectionBuilderTest.php
+++ b/tests/HappyEyeBallsConnectionBuilderTest.php
@@ -62,7 +62,11 @@ public function testConnectWillRejectWhenBothDnsLookupsReject()
});
$this->assertInstanceOf('RuntimeException', $exception);
+ assert($exception instanceof \RuntimeException);
+
$this->assertEquals('Connection to tcp://reactphp.org:80 failed during DNS lookup: DNS lookup error', $exception->getMessage());
+ $this->assertEquals(0, $exception->getCode());
+ $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
}
public function testConnectWillRejectWhenBothDnsLookupsRejectWithDifferentMessages()
@@ -98,7 +102,11 @@ public function testConnectWillRejectWhenBothDnsLookupsRejectWithDifferentMessag
});
$this->assertInstanceOf('RuntimeException', $exception);
+ assert($exception instanceof \RuntimeException);
+
$this->assertEquals('Connection to tcp://reactphp.org:80 failed during DNS lookup. Last error for IPv6: DNS6 error. Previous error for IPv4: DNS4 error', $exception->getMessage());
+ $this->assertEquals(0, $exception->getCode());
+ $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
}
public function testConnectWillStartDelayTimerWhenIpv4ResolvesAndIpv6IsPending()
@@ -468,7 +476,10 @@ public function testConnectWillRejectWhenOnlyTcp6ConnectionRejectsAndCancelNextA
$builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
$promise = $builder->connect();
- $deferred->reject(new \RuntimeException('Connection refused'));
+ $deferred->reject(new \RuntimeException(
+ 'Connection refused (ECONNREFUSED)',
+ defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
+ ));
$exception = null;
$promise->then(null, function ($e) use (&$exception) {
@@ -476,7 +487,11 @@ public function testConnectWillRejectWhenOnlyTcp6ConnectionRejectsAndCancelNextA
});
$this->assertInstanceOf('RuntimeException', $exception);
- $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Last error for IPv6: Connection refused. Previous error for IPv4: DNS failed', $exception->getMessage());
+ assert($exception instanceof \RuntimeException);
+
+ $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Last error for IPv6: Connection refused (ECONNREFUSED). Previous error for IPv4: DNS failed', $exception->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $exception->getCode());
+ $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
}
public function testConnectWillRejectWhenOnlyTcp4ConnectionRejectsAndWillNeverStartNextAttemptTimer()
@@ -504,7 +519,10 @@ public function testConnectWillRejectWhenOnlyTcp4ConnectionRejectsAndWillNeverSt
$builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
$promise = $builder->connect();
- $deferred->reject(new \RuntimeException('Connection refused'));
+ $deferred->reject(new \RuntimeException(
+ 'Connection refused (ECONNREFUSED)',
+ defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
+ ));
$exception = null;
$promise->then(null, function ($e) use (&$exception) {
@@ -512,7 +530,11 @@ public function testConnectWillRejectWhenOnlyTcp4ConnectionRejectsAndWillNeverSt
});
$this->assertInstanceOf('RuntimeException', $exception);
- $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Last error for IPv4: Connection refused. Previous error for IPv6: DNS failed', $exception->getMessage());
+ assert($exception instanceof \RuntimeException);
+
+ $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Last error for IPv4: Connection refused (ECONNREFUSED). Previous error for IPv6: DNS failed', $exception->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $exception->getCode());
+ $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
}
public function testConnectWillRejectWhenAllConnectionsRejectAndCancelNextAttemptTimerImmediately()
@@ -542,7 +564,10 @@ public function testConnectWillRejectWhenAllConnectionsRejectAndCancelNextAttemp
$builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
$promise = $builder->connect();
- $deferred->reject(new \RuntimeException('Connection refused'));
+ $deferred->reject(new \RuntimeException(
+ 'Connection refused (ECONNREFUSED)',
+ defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
+ ));
$exception = null;
$promise->then(null, function ($e) use (&$exception) {
@@ -550,7 +575,11 @@ public function testConnectWillRejectWhenAllConnectionsRejectAndCancelNextAttemp
});
$this->assertInstanceOf('RuntimeException', $exception);
- $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Connection refused', $exception->getMessage());
+ assert($exception instanceof \RuntimeException);
+
+ $this->assertEquals('Connection to tcp://reactphp.org:80 failed: Connection refused (ECONNREFUSED)', $exception->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $exception->getCode());
+ $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
}
public function testConnectWillRejectWithMessageWithoutHostnameWhenAllConnectionsRejectAndCancelNextAttemptTimerImmediately()
@@ -564,7 +593,10 @@ public function testConnectWillRejectWithMessageWithoutHostnameWhenAllConnection
$connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
$connector->expects($this->exactly(2))->method('connect')->willReturnOnConsecutiveCalls(
$deferred->promise(),
- \React\Promise\reject(new \RuntimeException('Connection to tcp://127.0.0.1:80?hostname=localhost failed: Connection refused'))
+ \React\Promise\reject(new \RuntimeException(
+ 'Connection to tcp://127.0.0.1:80?hostname=localhost failed: Connection refused (ECONNREFUSED)',
+ defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
+ ))
);
$resolver = $this->getMockBuilder('React\Dns\Resolver\ResolverInterface')->getMock();
@@ -583,7 +615,10 @@ public function testConnectWillRejectWithMessageWithoutHostnameWhenAllConnection
$builder = new HappyEyeBallsConnectionBuilder($loop, $connector, $resolver, $uri, $host, $parts);
$promise = $builder->connect();
- $deferred->reject(new \RuntimeException('Connection to tcp://[::1]:80?hostname=localhost failed: Connection refused'));
+ $deferred->reject(new \RuntimeException(
+ 'Connection to tcp://[::1]:80?hostname=localhost failed: Connection refused (ECONNREFUSED)',
+ defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
+ ));
$exception = null;
$promise->then(null, function ($e) use (&$exception) {
@@ -591,7 +626,11 @@ public function testConnectWillRejectWithMessageWithoutHostnameWhenAllConnection
});
$this->assertInstanceOf('RuntimeException', $exception);
- $this->assertEquals('Connection to tcp://localhost:80 failed: Last error for IPv4: Connection to tcp://127.0.0.1:80 failed: Connection refused. Previous error for IPv6: Connection to tcp://[::1]:80 failed: Connection refused', $exception->getMessage());
+ assert($exception instanceof \RuntimeException);
+
+ $this->assertEquals('Connection to tcp://localhost:80 failed: Last error for IPv4: Connection to tcp://127.0.0.1:80 failed: Connection refused (ECONNREFUSED). Previous error for IPv6: Connection to tcp://[::1]:80 failed: Connection refused (ECONNREFUSED)', $exception->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111, $exception->getCode());
+ $this->assertInstanceOf('RuntimeException', $exception->getPrevious());
}
public function testCancelConnectWillRejectPromiseAndCancelBothDnsLookups()
@@ -635,7 +674,10 @@ public function testCancelConnectWillRejectPromiseAndCancelBothDnsLookups()
});
$this->assertInstanceOf('RuntimeException', $exception);
- $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled during DNS lookup', $exception->getMessage());
+ assert($exception instanceof \RuntimeException);
+
+ $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled during DNS lookup (ECONNABORTED)', $exception->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103, $exception->getCode());
}
public function testCancelConnectWillRejectPromiseAndCancelPendingIpv6LookupAndCancelDelayTimer()
@@ -672,7 +714,10 @@ public function testCancelConnectWillRejectPromiseAndCancelPendingIpv6LookupAndC
});
$this->assertInstanceOf('RuntimeException', $exception);
- $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled during DNS lookup', $exception->getMessage());
+ assert($exception instanceof \RuntimeException);
+
+ $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled during DNS lookup (ECONNABORTED)', $exception->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103, $exception->getCode());
}
public function testCancelConnectWillRejectPromiseAndCancelPendingIpv6ConnectionAttemptAndPendingIpv4LookupAndCancelAttemptTimer()
@@ -715,7 +760,10 @@ public function testCancelConnectWillRejectPromiseAndCancelPendingIpv6Connection
});
$this->assertInstanceOf('RuntimeException', $exception);
- $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled', $exception->getMessage());
+ assert($exception instanceof \RuntimeException);
+
+ $this->assertEquals('Connection to tcp://reactphp.org:80 cancelled (ECONNABORTED)', $exception->getMessage());
+ $this->assertEquals(defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103, $exception->getCode());
}
public function testResolveWillReturnResolvedPromiseWithEmptyListWhenDnsResolverFails()
diff --git a/tests/HappyEyeBallsConnectorTest.php b/tests/HappyEyeBallsConnectorTest.php
index d44b8625..6a26fd63 100644
--- a/tests/HappyEyeBallsConnectorTest.php
+++ b/tests/HappyEyeBallsConnectorTest.php
@@ -221,9 +221,11 @@ public function testRejectsImmediatelyIfUriIsInvalid()
$promise = $this->connector->connect('////');
- $promise->then($this->expectCallableNever(), $this->expectCallableOnce());
-
- $this->loop->run();
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'InvalidArgumentException',
+ 'Given URI "////" is invalid (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testRejectsWithTcpConnectorRejectionIfGivenIp()
@@ -275,7 +277,11 @@ public function testCancelDuringDnsCancelsDnsAndDoesNotStartTcpConnection()
$that->throwRejection($promise);
});
- $this->setExpectedException('RuntimeException', 'Connection to tcp://example.com:80 cancelled during DNS lookup');
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection to tcp://example.com:80 cancelled during DNS lookup (ECONNABORTED)',
+ \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
+ );
$this->loop->run();
}
diff --git a/tests/SecureConnectorTest.php b/tests/SecureConnectorTest.php
index 1e0f4f87..af3a6f58 100644
--- a/tests/SecureConnectorTest.php
+++ b/tests/SecureConnectorTest.php
@@ -65,28 +65,46 @@ public function testConnectionToInvalidSchemeWillReject()
$promise = $this->connector->connect('tcp://example.com:80');
- $promise->then(null, $this->expectCallableOnce());
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'InvalidArgumentException',
+ 'Given URI "tcp://example.com:80" is invalid (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testConnectWillRejectWithTlsUriWhenUnderlyingConnectorRejects()
{
- $this->tcp->expects($this->once())->method('connect')->with('example.com:80')->willReturn(\React\Promise\reject(new \RuntimeException('Connection to tcp://example.com:80 failed: Connection refused', 42)));
+ $this->tcp->expects($this->once())->method('connect')->with('example.com:80')->willReturn(\React\Promise\reject(new \RuntimeException(
+ 'Connection to tcp://example.com:80 failed: Connection refused (ECONNREFUSED)',
+ defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
+ )));
$promise = $this->connector->connect('example.com:80');
$promise->cancel();
- $this->setExpectedException('RuntimeException', 'Connection to tls://example.com:80 failed: Connection refused', 42);
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection to tls://example.com:80 failed: Connection refused (ECONNREFUSED)',
+ defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
+ );
$this->throwRejection($promise);
}
public function testConnectWillRejectWithOriginalMessageWhenUnderlyingConnectorRejectsWithInvalidArgumentException()
{
- $this->tcp->expects($this->once())->method('connect')->with('example.com:80')->willReturn(\React\Promise\reject(new \InvalidArgumentException('Invalid', 42)));
+ $this->tcp->expects($this->once())->method('connect')->with('example.com:80')->willReturn(\React\Promise\reject(new \InvalidArgumentException(
+ 'Invalid',
+ 42
+ )));
$promise = $this->connector->connect('example.com:80');
$promise->cancel();
- $this->setExpectedException('InvalidArgumentException', 'Invalid', 42);
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid',
+ 42
+ );
$this->throwRejection($promise);
}
@@ -101,13 +119,20 @@ public function testCancelDuringTcpConnectionCancelsTcpConnection()
public function testCancelDuringTcpConnectionCancelsTcpConnectionAndRejectsWithTcpRejection()
{
- $pending = new Promise\Promise(function () { }, function () { throw new \RuntimeException('Connection to tcp://example.com:80 cancelled', 42); });
+ $pending = new Promise\Promise(function () { }, function () { throw new \RuntimeException(
+ 'Connection to tcp://example.com:80 cancelled (ECONNABORTED)',
+ defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
+ ); });
$this->tcp->expects($this->once())->method('connect')->with($this->equalTo('example.com:80'))->will($this->returnValue($pending));
$promise = $this->connector->connect('example.com:80');
$promise->cancel();
- $this->setExpectedException('RuntimeException', 'Connection to tls://example.com:80 cancelled', 42);
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection to tls://example.com:80 cancelled (ECONNABORTED)',
+ defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
+ );
$this->throwRejection($promise);
}
@@ -187,7 +212,11 @@ public function testCancelDuringStreamEncryptionCancelsEncryptionAndClosesConnec
$promise = $this->connector->connect('example.com:80');
$promise->cancel();
- $this->setExpectedException('RuntimeException', 'Connection to tls://example.com:80 cancelled during TLS handshake');
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection to tls://example.com:80 cancelled during TLS handshake (ECONNABORTED)',
+ defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
+ );
$this->throwRejection($promise);
}
diff --git a/tests/ServerTest.php b/tests/ServerTest.php
index 02e10301..b46949ba 100644
--- a/tests/ServerTest.php
+++ b/tests/ServerTest.php
@@ -102,7 +102,7 @@ public function testConstructorThrowsForExistingUnixPath()
$this->assertStringEndsWith('Unknown error', $e->getMessage());
} else {
$this->assertEquals(SOCKET_EADDRINUSE, $e->getCode());
- $this->assertStringEndsWith('Address already in use', $e->getMessage());
+ $this->assertStringEndsWith('Address already in use (EADDRINUSE)', $e->getMessage());
}
}
}
diff --git a/tests/SocketServerTest.php b/tests/SocketServerTest.php
index 7092b8b4..8f453dd1 100644
--- a/tests/SocketServerTest.php
+++ b/tests/SocketServerTest.php
@@ -41,19 +41,31 @@ public function testCreateServerWithZeroPortAssignsRandomPort()
public function testConstructorWithInvalidUriThrows()
{
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid URI "tcp://invalid URI" given (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new SocketServer('invalid URI');
}
public function testConstructorWithInvalidUriWithPortOnlyThrows()
{
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid URI given (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new SocketServer('0');
}
public function testConstructorWithInvalidUriWithSchemaAndPortOnlyThrows()
{
- $this->setExpectedException('InvalidArgumentException');
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Invalid URI given (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
new SocketServer('tcp://0');
}
@@ -113,7 +125,7 @@ public function testConstructorThrowsForExistingUnixPath()
$this->assertStringEndsWith('Unknown error', $e->getMessage());
} else {
$this->assertEquals(SOCKET_EADDRINUSE, $e->getCode());
- $this->assertStringEndsWith('Address already in use', $e->getMessage());
+ $this->assertStringEndsWith('Address already in use (EADDRINUSE)', $e->getMessage());
}
}
}
diff --git a/tests/TcpConnectorTest.php b/tests/TcpConnectorTest.php
index ee5b480e..68dc3d76 100644
--- a/tests/TcpConnectorTest.php
+++ b/tests/TcpConnectorTest.php
@@ -32,7 +32,11 @@ public function connectionToEmptyPortShouldFail()
$connector = new TcpConnector($loop);
$promise = $connector->connect('127.0.0.1:9999');
- $this->setExpectedException('RuntimeException', 'Connection to tcp://127.0.0.1:9999 failed: Connection refused');
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection to tcp://127.0.0.1:9999 failed: Connection refused' . (function_exists('socket_import_stream') ? ' (ECONNREFUSED)' : ''),
+ defined('SOCKET_ECONNREFUSED') ? SOCKET_ECONNREFUSED : 111
+ );
Block\await($promise, $loop, self::TIMEOUT);
}
@@ -127,7 +131,7 @@ public function connectionToInvalidNetworkShouldFailWithUnreachableError()
$this->setExpectedException(
'RuntimeException',
- 'Connection to ' . $address . ' failed: ' . (function_exists('socket_strerror') ? socket_strerror($enetunreach) : 'Network is unreachable'),
+ 'Connection to ' . $address . ' failed: ' . (function_exists('socket_strerror') ? socket_strerror($enetunreach) . ' (ENETUNREACH)' : 'Network is unreachable'),
$enetunreach
);
Block\await($promise, $loop, self::TIMEOUT);
@@ -255,10 +259,13 @@ public function connectionToHostnameShouldFailImmediately()
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
$connector = new TcpConnector($loop);
- $connector->connect('www.google.com:80')->then(
- $this->expectCallableNever(),
- $this->expectCallableOnce()
- );
+ $promise = $connector->connect('www.google.com:80');
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'InvalidArgumentException',
+ 'Given URI "tcp://www.google.com:80" does not contain a valid host IP (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
/** @test */
@@ -267,10 +274,13 @@ public function connectionToInvalidPortShouldFailImmediately()
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
$connector = new TcpConnector($loop);
- $connector->connect('255.255.255.255:12345678')->then(
- $this->expectCallableNever(),
- $this->expectCallableOnce()
- );
+ $promise = $connector->connect('255.255.255.255:12345678');
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'InvalidArgumentException',
+ 'Given URI "tcp://255.255.255.255:12345678" is invalid (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
/** @test */
@@ -324,7 +334,11 @@ public function cancellingConnectionShouldRejectPromise()
$promise = $connector->connect($server->getAddress());
$promise->cancel();
- $this->setExpectedException('RuntimeException', 'Connection to ' . $server->getAddress() . ' cancelled during TCP/IP handshake');
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection to ' . $server->getAddress() . ' cancelled during TCP/IP handshake (ECONNABORTED)',
+ defined('SOCKET_ECONNABORTED') ? SOCKET_ECONNABORTED : 103
+ );
Block\await($promise, $loop);
}
diff --git a/tests/TcpServerTest.php b/tests/TcpServerTest.php
index 204d5680..b4749cf6 100644
--- a/tests/TcpServerTest.php
+++ b/tests/TcpServerTest.php
@@ -51,6 +51,7 @@ public function testConstructWithoutLoopAssignsLoopAutomatically()
public function testServerEmitsConnectionEventForNewConnection()
{
$client = stream_socket_client('tcp://localhost:'.$this->port);
+ assert($client !== false);
$server = $this->server;
$promise = new Promise(function ($resolve) use ($server) {
@@ -70,6 +71,7 @@ public function testConnectionWithManyClients()
$client1 = stream_socket_client('tcp://localhost:'.$this->port);
$client2 = stream_socket_client('tcp://localhost:'.$this->port);
$client3 = stream_socket_client('tcp://localhost:'.$this->port);
+ assert($client1 !== false && $client2 !== false && $client3 !== false);
$this->server->on('connection', $this->expectCallableExactly(3));
$this->tick();
@@ -80,6 +82,7 @@ public function testConnectionWithManyClients()
public function testDataEventWillNotBeEmittedWhenClientSendsNoData()
{
$client = stream_socket_client('tcp://localhost:'.$this->port);
+ assert($client !== false);
$mock = $this->expectCallableNever();
@@ -150,6 +153,7 @@ public function testGetAddressAfterCloseReturnsNull()
public function testLoopWillEndWhenServerIsClosedAfterSingleConnection()
{
$client = stream_socket_client('tcp://localhost:' . $this->port);
+ assert($client !== false);
// explicitly unset server because we only accept a single connection
// and then already call close()
@@ -203,6 +207,7 @@ public function testDataWillBeEmittedInMultipleChunksWhenClientSendsExcessiveAmo
public function testConnectionDoesNotEndWhenClientDoesNotClose()
{
$client = stream_socket_client('tcp://localhost:'.$this->port);
+ assert($client !== false);
$mock = $this->expectCallableNever();
@@ -236,7 +241,7 @@ public function testCtorAddsResourceToLoop()
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
$loop->expects($this->once())->method('addReadStream');
- $server = new TcpServer(0, $loop);
+ new TcpServer(0, $loop);
}
public function testResumeWithoutPauseIsNoOp()
@@ -316,10 +321,10 @@ public function testEmitsErrorWhenAcceptListenerFails()
public function testEmitsTimeoutErrorWhenAcceptListenerFails(\RuntimeException $exception)
{
if (defined('HHVM_VERSION')) {
- $this->markTestSkipped('not supported on HHVM');
+ $this->markTestSkipped('Not supported on HHVM');
}
- $this->assertEquals('Unable to accept new connection: ' . socket_strerror(SOCKET_ETIMEDOUT), $exception->getMessage());
+ $this->assertEquals('Unable to accept new connection: ' . socket_strerror(SOCKET_ETIMEDOUT) . ' (ETIMEDOUT)', $exception->getMessage());
$this->assertEquals(SOCKET_ETIMEDOUT, $exception->getCode());
}
@@ -328,9 +333,16 @@ public function testListenOnBusyPortThrows()
if (DIRECTORY_SEPARATOR === '\\') {
$this->markTestSkipped('Windows supports listening on same port multiple times');
}
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('Not supported on HHVM');
+ }
- $this->setExpectedException('RuntimeException');
- $another = new TcpServer($this->port, $this->loop);
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Failed to listen on "tcp://127.0.0.1:' . $this->port . '": ' . (function_exists('socket_strerror') ? socket_strerror(SOCKET_EADDRINUSE) . ' (EADDRINUSE)' : 'Address already in use'),
+ defined('SOCKET_EADDRINUSE') ? SOCKET_EADDRINUSE : 0
+ );
+ new TcpServer($this->port, $this->loop);
}
/**
diff --git a/tests/TestCase.php b/tests/TestCase.php
index cdb8b1bc..6010b827 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -41,6 +41,21 @@ protected function expectCallableOnceWith($value)
return $mock;
}
+ protected function expectCallableOnceWithException($type, $message = null, $code = null)
+ {
+ return $this->expectCallableOnceWith(
+ $this->logicalAnd(
+ $this->isInstanceOf($type),
+ $this->callback(function (\Exception $e) use ($message) {
+ return $message === null || $e->getMessage() === $message;
+ }),
+ $this->callback(function (\Exception $e) use ($code) {
+ return $code === null || $e->getCode() === $code;
+ })
+ )
+ );
+ }
+
protected function expectCallableNever()
{
$mock = $this->createCallableMock();
diff --git a/tests/TimeoutConnectorTest.php b/tests/TimeoutConnectorTest.php
index 98dedca7..81398279 100644
--- a/tests/TimeoutConnectorTest.php
+++ b/tests/TimeoutConnectorTest.php
@@ -34,13 +34,17 @@ public function testRejectsWithTimeoutReasonOnTimeout()
$timeout = new TimeoutConnector($connector, 0.01, $loop);
- $this->setExpectedException('RuntimeException', 'Connection to google.com:80 timed out after 0.01 seconds');
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Connection to google.com:80 timed out after 0.01 seconds (ETIMEDOUT)',
+ \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110
+ );
Block\await($timeout->connect('google.com:80'), $loop);
}
public function testRejectsWithOriginalReasonWhenConnectorRejects()
{
- $promise = Promise\reject(new \RuntimeException('Failed'));
+ $promise = Promise\reject(new \RuntimeException('Failed', 42));
$connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
$connector->expects($this->once())->method('connect')->with('google.com:80')->will($this->returnValue($promise));
@@ -49,7 +53,11 @@ public function testRejectsWithOriginalReasonWhenConnectorRejects()
$timeout = new TimeoutConnector($connector, 5.0, $loop);
- $this->setExpectedException('RuntimeException', 'Failed');
+ $this->setExpectedException(
+ 'RuntimeException',
+ 'Failed',
+ 42
+ );
Block\await($timeout->connect('google.com:80'), $loop);
}
diff --git a/tests/UnixConnectorTest.php b/tests/UnixConnectorTest.php
index 9f68c2d9..183c0d3e 100644
--- a/tests/UnixConnectorTest.php
+++ b/tests/UnixConnectorTest.php
@@ -33,13 +33,21 @@ public function testConstructWithoutLoopAssignsLoopAutomatically()
public function testInvalid()
{
$promise = $this->connector->connect('google.com:80');
- $promise->then(null, $this->expectCallableOnce());
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'RuntimeException'
+ ));
}
public function testInvalidScheme()
{
$promise = $this->connector->connect('tcp://google.com:80');
- $promise->then(null, $this->expectCallableOnce());
+
+ $promise->then(null, $this->expectCallableOnceWithException(
+ 'InvalidArgumentException',
+ 'Given URI "tcp://google.com:80" is invalid (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ ));
}
public function testValid()
diff --git a/tests/UnixServerTest.php b/tests/UnixServerTest.php
index 463fab12..b2d4b59f 100644
--- a/tests/UnixServerTest.php
+++ b/tests/UnixServerTest.php
@@ -232,8 +232,12 @@ public function testCtorThrowsForInvalidAddressScheme()
{
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
- $this->setExpectedException('InvalidArgumentException');
- $server = new UnixServer('tcp://localhost:0', $loop);
+ $this->setExpectedException(
+ 'InvalidArgumentException',
+ 'Given URI "tcp://localhost:0" is invalid (EINVAL)',
+ defined('SOCKET_EINVAL') ? SOCKET_EINVAL : 22
+ );
+ new UnixServer('tcp://localhost:0', $loop);
}
public function testCtorThrowsWhenPathIsNotWritable()
@@ -324,7 +328,7 @@ public function testEmitsTimeoutErrorWhenAcceptListenerFails(\RuntimeException $
$this->markTestSkipped('not supported on HHVM');
}
- $this->assertEquals('Unable to accept new connection: ' . socket_strerror(SOCKET_ETIMEDOUT), $exception->getMessage());
+ $this->assertEquals('Unable to accept new connection: ' . socket_strerror(SOCKET_ETIMEDOUT) . ' (ETIMEDOUT)', $exception->getMessage());
$this->assertEquals(SOCKET_ETIMEDOUT, $exception->getCode());
}