Skip to content

Improve Unix domain socket (UDS) server error messages #168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,14 @@ $first = new UnixServer('/tmp/same.sock', $loop);
$second = new UnixServer('/tmp/same.sock', $loop);
```

> Note that these error conditions may vary depending on your system and/or
configuration.
In particular, Zend PHP does only report "Unknown error" when the UDS path
already exists and can not be bound. You may want to check `is_file()` on the
given UDS path to report a more user-friendly error message in this case.
See the exception message and code for more details about the actual error
condition.

Whenever a client connects, it will emit a `connection` event with a connection
instance implementing [`ConnectionInterface`](#connectioninterface):

Expand Down
13 changes: 12 additions & 1 deletion src/UnixServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,18 @@ public function __construct($path, LoopInterface $loop, array $context = array()
stream_context_create(array('socket' => $context))
);
if (false === $this->master) {
throw new RuntimeException('Failed to listen on unix domain socket "' . $path . '": ' . $errstr, $errno);
// PHP does not seem to report errno/errstr for Unix domain sockets (UDS) right now.
// This only applies to UDS server sockets, see also https://3v4l.org/NAhpr.
// Parse PHP warning message containing unknown error, HHVM reports proper info at least.
if ($errno === 0 && $errstr === '') {
$error = error_get_last();
if (preg_match('/\(([^\)]+)\)|\[(\d+)\]: (.*)/', $error['message'], $match)) {
$errstr = isset($match[3]) ? $match['3'] : $match[1];
$errno = isset($match[2]) ? (int)$match[2] : 0;
}
}

throw new RuntimeException('Failed to listen on Unix domain socket "' . $path . '": ' . $errstr, $errno);
}
stream_set_blocking($this->master, 0);

Expand Down
26 changes: 26 additions & 0 deletions tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public function testConstructorCreatesExpectedTcpServer()

public function testConstructorCreatesExpectedUnixServer()
{
if (!in_array('unix', stream_get_transports())) {
$this->markTestSkipped('Unix domain sockets (UDS) not supported on your platform (Windows?)');
}

$loop = Factory::create();

$server = new Server($this->getRandomSocketUri(), $loop);
Expand All @@ -64,6 +68,28 @@ public function testConstructorCreatesExpectedUnixServer()
$server->close();
}

public function testConstructorThrowsForExistingUnixPath()
{
if (!in_array('unix', stream_get_transports())) {
$this->markTestSkipped('Unix domain sockets (UDS) not supported on your platform (Windows?)');
}

$loop = Factory::create();

try {
$server = new Server('unix://' . __FILE__, $loop);
$this->fail();
} catch (\RuntimeException $e) {
if ($e->getCode() === 0) {
// Zend PHP does not currently report a sane error
$this->assertStringEndsWith('Unknown error', $e->getMessage());
} else {
$this->assertEquals(SOCKET_EADDRINUSE, $e->getCode());
$this->assertStringEndsWith('Address already in use', $e->getMessage());
}
}
}

public function testEmitsConnectionForNewConnection()
{
$loop = Factory::create();
Expand Down
4 changes: 4 additions & 0 deletions tests/UnixServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ class UnixServerTest extends TestCase
*/
public function setUp()
{
if (!in_array('unix', stream_get_transports())) {
$this->markTestSkipped('Unix domain sockets (UDS) not supported on your platform (Windows?)');
}

$this->loop = Factory::create();
$this->uds = $this->getRandomSocketUri();
$this->server = new UnixServer($this->uds, $this->loop);
Expand Down