Skip to content

Commit e6dc195

Browse files
committed
Improve error messages for failed TCP/IP connections without ext-sockets
1 parent 5beea91 commit e6dc195

File tree

2 files changed

+25
-12
lines changed

2 files changed

+25
-12
lines changed

src/TcpConnector.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,26 @@ public function connect($uri)
9999
// The following hack looks like the only way to
100100
// detect connection refused errors with PHP's stream sockets.
101101
if (false === \stream_socket_get_name($stream, true)) {
102-
// actual socket errno and errstr can only be retrieved when ext-sockets is available (see tests)
102+
// If we reach this point, we know the connection is dead, but we don't know the underlying error condition.
103103
// @codeCoverageIgnoreStart
104104
if (\function_exists('socket_import_stream')) {
105+
// actual socket errno and errstr can be retrieved with ext-sockets on PHP 5.4+
105106
$socket = \socket_import_stream($stream);
106107
$errno = \socket_get_option($socket, \SOL_SOCKET, \SO_ERROR);
107108
$errstr = \socket_strerror($errno);
109+
} elseif (\PHP_OS === 'Linux') {
110+
// Linux reports socket errno and errstr again when trying to write to the dead socket.
111+
// Suppress error reporting to get error message below and close dead socket before rejecting.
112+
// This is only known to work on Linux, Mac and Windows are known to not support this.
113+
@\fwrite($stream, \PHP_EOL);
114+
$error = \error_get_last();
115+
116+
// fwrite(): send of 2 bytes failed with errno=111 Connection refused
117+
\preg_match('/errno=(\d+) (.+)/', $error['message'], $m);
118+
$errno = isset($m[1]) ? (int) $m[1] : 0;
119+
$errstr = isset($m[2]) ? $m[2] : $error['message'];
108120
} else {
121+
// Not on Linux and ext-sockets not available? Too bad.
109122
$errno = \defined('SOCKET_ECONNREFUSED') ? \SOCKET_ECONNRESET : 111;
110123
$errstr = 'Connection refused?';
111124
}

tests/TcpConnectorTest.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,21 @@ public function connectionToTcpServerShouldFailIfFileDescriptorsAreExceeded()
103103
/** @test */
104104
public function connectionToInvalidNetworkShouldFailWithUnreachableError()
105105
{
106-
if (!defined('SOCKET_ENETUNREACH') || !function_exists('socket_import_stream')) {
107-
$this->markTestSkipped('Test requires ext-socket on PHP 5.4+');
106+
if (PHP_OS !== 'Linux' && !function_exists('socket_import_stream')) {
107+
$this->markTestSkipped('Test requires either Linux or ext-sockets on PHP 5.4+');
108108
}
109109

110+
$enetunreach = defined('SOCKET_ENETUNREACH') ? SOCKET_ENETUNREACH : 101;
111+
110112
// try to find an unreachable network by trying a couple of private network addresses
111-
$errno = 0; $errstr = '';
112-
for ($i = 0; $i < 20; ++$i) {
113+
$errno = 0;
114+
$errstr = '';
115+
for ($i = 0; $i < 20 && $errno !== $enetunreach; ++$i) {
113116
$address = 'tcp://192.168.' . mt_rand(0, 255) . '.' . mt_rand(1, 254) . ':8123';
114117
$client = @stream_socket_client($address, $errno, $errstr, 0.1 * $i);
115-
if ($errno === SOCKET_ENETUNREACH) {
116-
break;
117-
}
118118
}
119-
if ($client || $errno !== SOCKET_ENETUNREACH) {
120-
$this->markTestSkipped('Expected error ' . SOCKET_ENETUNREACH . ' but got ' . $errno . ' (' . $errstr . ') for ' . $address);
119+
if ($client || $errno !== $enetunreach) {
120+
$this->markTestSkipped('Expected error ' . $enetunreach . ' but got ' . $errno . ' (' . $errstr . ') for ' . $address);
121121
}
122122

123123
$loop = Factory::create();
@@ -127,8 +127,8 @@ public function connectionToInvalidNetworkShouldFailWithUnreachableError()
127127

128128
$this->setExpectedException(
129129
'RuntimeException',
130-
'Connection to ' . $address . ' failed: ' . socket_strerror(SOCKET_ENETUNREACH),
131-
SOCKET_ENETUNREACH
130+
'Connection to ' . $address . ' failed: ' . (function_exists('socket_strerror') ? socket_strerror($enetunreach) : 'Network is unreachable'),
131+
$enetunreach
132132
);
133133
Block\await($promise, $loop, self::TIMEOUT);
134134
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy