Skip to content

Commit 9909831

Browse files
committed
Support listening on Unix domain sockets (UDS) file descriptors (FDs)
1 parent fd9a1ab commit 9909831

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed

src/FdServer.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ final class FdServer extends EventEmitter implements ServerInterface
3535
{
3636
private $master;
3737
private $loop;
38+
private $unix = false;
3839
private $listening = false;
3940

4041
/**
@@ -115,6 +116,10 @@ public function __construct($fd, LoopInterface $loop = null)
115116
throw new \RuntimeException('Failed to listen on FD ' . $fd . ': ' . $errstr, $errno);
116117
}
117118

119+
// Assume this is a Unix domain socket (UDS) when its listening address doesn't parse as a valid URL with a port.
120+
// Looks like this work-around is the closest we can get because PHP doesn't expose SO_DOMAIN even with ext-sockets.
121+
$this->unix = \parse_url($this->getAddress(), \PHP_URL_PORT) === false;
122+
118123
\stream_set_blocking($this->master, false);
119124

120125
$this->resume();
@@ -128,6 +133,10 @@ public function getAddress()
128133

129134
$address = \stream_socket_get_name($this->master, false);
130135

136+
if ($this->unix === true) {
137+
return 'unix://' . $address;
138+
}
139+
131140
// check if this is an IPv6 address which includes multiple colons but no square brackets
132141
$pos = \strrpos($address, ':');
133142
if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') {
@@ -180,8 +189,9 @@ public function close()
180189
/** @internal */
181190
public function handleConnection($socket)
182191
{
183-
$this->emit('connection', array(
184-
new Connection($socket, $this->loop)
185-
));
192+
$connection = new Connection($socket, $this->loop);
193+
$connection->unix = $this->unix;
194+
195+
$this->emit('connection', array($connection));
186196
}
187197
}

tests/FdServerTest.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function testGetAddressReturnsSameAddressAsOriginalSocketForIpv6Socket()
120120

121121
$socket = @stream_socket_server('[::1]:0');
122122
if ($socket === false) {
123-
$this->markTestSkipped('IPv6 not supported ');
123+
$this->markTestSkipped('Listening on IPv6 not supported');
124124
}
125125

126126
$fd = $this->getFdFromResource($socket);
@@ -133,6 +133,26 @@ public function testGetAddressReturnsSameAddressAsOriginalSocketForIpv6Socket()
133133
$this->assertEquals('tcp://[::1]:' . $port, $server->getAddress());
134134
}
135135

136+
public function testGetAddressReturnsSameAddressAsOriginalSocketForUnixDomainSocket()
137+
{
138+
if (!is_dir('/dev/fd') || defined('HHVM_VERSION')) {
139+
$this->markTestSkipped('Not supported on your platform');
140+
}
141+
142+
$socket = @stream_socket_server($this->getRandomSocketUri());
143+
if ($socket === false) {
144+
$this->markTestSkipped('Listening on Unix domain socket (UDS) not supported');
145+
}
146+
147+
$fd = $this->getFdFromResource($socket);
148+
149+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
150+
151+
$server = new FdServer($fd, $loop);
152+
153+
$this->assertEquals('unix://' . stream_socket_get_name($socket, false), $server->getAddress());
154+
}
155+
136156
public function testGetAddressReturnsNullAfterClose()
137157
{
138158
if (!is_dir('/dev/fd') || defined('HHVM_VERSION')) {
@@ -335,4 +355,9 @@ private function getFdFromResource($resource)
335355

336356
throw new \UnderflowException('Could not locate file descriptor for this resource');
337357
}
358+
359+
private function getRandomSocketUri()
360+
{
361+
return "unix://" . sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(rand(), true) . '.sock';
362+
}
338363
}

0 commit comments

Comments
 (0)