Skip to content

Commit 1807f44

Browse files
authored
Merge pull request #100 from clue-labs/unix-address
Fix unix:// addresses for Unix domain socket (UDS) paths
2 parents 0a6b649 + eeec8f2 commit 1807f44

File tree

5 files changed

+60
-5
lines changed

5 files changed

+60
-5
lines changed

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ If the remote address can not be determined or is unknown at this time (such as
149149
after the connection has been closed), it MAY return a `NULL` value instead.
150150

151151
Otherwise, it will return the full address (URI) as a string value, such
152-
as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`.
152+
as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
153+
`unix://example.sock` or `unix:///path/to/example.sock`.
153154
Note that individual URI components are application specific and depend
154155
on the underlying transport protocol.
155156

@@ -176,7 +177,8 @@ If the local address can not be determined or is unknown at this time (such as
176177
after the connection has been closed), it MAY return a `NULL` value instead.
177178

178179
Otherwise, it will return the full address (URI) as a string value, such
179-
as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`.
180+
as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
181+
`unix://example.sock` or `unix:///path/to/example.sock`.
180182
Note that individual URI components are application specific and depend
181183
on the underlying transport protocol.
182184

@@ -831,6 +833,12 @@ $connector->connect('unix:///tmp/demo.sock')->then(function (ConnectionInterface
831833
});
832834
```
833835

836+
> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
837+
Unix domain socket (UDS) path as given to the `connect()` method, including
838+
the `unix://` scheme, for example `unix:///tmp/demo.sock`.
839+
The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
840+
`null` value as this value is not applicable to UDS connections here.
841+
834842
Under the hood, the `Connector` is implemented as a *higher-level facade*
835843
for the lower-level connectors implemented in this package. This means it
836844
also shares all of their features and implementation details.
@@ -1209,6 +1217,12 @@ Connecting to Unix domain sockets is an atomic operation, i.e. its promise will
12091217
settle (either resolve or reject) immediately.
12101218
As such, calling `cancel()` on the resulting promise has no effect.
12111219

1220+
> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
1221+
Unix domain socket (UDS) path as given to the `connect()` method, prepended
1222+
with the `unix://` scheme, for example `unix:///tmp/demo.sock`.
1223+
The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
1224+
`null` value as this value is not applicable to UDS connections here.
1225+
12121226
## Install
12131227

12141228
The recommended way to install this library is [through Composer](http://getcomposer.org).

src/Connection.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
*/
2020
class Connection extends EventEmitter implements ConnectionInterface
2121
{
22+
/**
23+
* Internal flag whether this is a Unix domain socket (UDS) connection
24+
*
25+
* @internal
26+
*/
27+
public $unix = false;
28+
2229
/**
2330
* Internal flag whether encryption has been enabled on this connection
2431
*
@@ -138,6 +145,22 @@ private function parseAddress($address)
138145
return null;
139146
}
140147

148+
if ($this->unix) {
149+
// remove trailing colon from address for HHVM < 3.19: https://3v4l.org/5C1lo
150+
// note that techncially ":" is a valid address, so keep this in place otherwise
151+
if (substr($address, -1) === ':' && defined('HHVM_VERSION_ID') && HHVM_VERSION_ID < 31900) {
152+
$address = (string)substr($address, 0, -1);
153+
}
154+
155+
// work around unknown addresses should return null value: https://3v4l.org/5C1lo
156+
// PHP uses "\0" string and HHVM uses empty string (colon removed above)
157+
if ($address === "\x00" || $address === '') {
158+
return null;
159+
}
160+
161+
return 'unix://' . $address;
162+
}
163+
141164
// check if this is an IPv6 address which includes multiple colons but no square brackets
142165
$pos = strrpos($address, ':');
143166
if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') {

src/ConnectionInterface.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ interface ConnectionInterface extends DuplexStreamInterface
6666
* after the connection has been closed), it MAY return a `NULL` value instead.
6767
*
6868
* Otherwise, it will return the full address (URI) as a string value, such
69-
* as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`.
69+
* as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
70+
* `unix://example.sock` or `unix:///path/to/example.sock`.
7071
* Note that individual URI components are application specific and depend
7172
* on the underlying transport protocol.
7273
*
@@ -95,7 +96,8 @@ public function getRemoteAddress();
9596
* after the connection has been closed), it MAY return a `NULL` value instead.
9697
*
9798
* Otherwise, it will return the full address (URI) as a string value, such
98-
* as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`.
99+
* as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
100+
* `unix://example.sock` or `unix:///path/to/example.sock`.
99101
* Note that individual URI components are application specific and depend
100102
* on the underlying transport protocol.
101103
*

src/UnixConnector.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public function connect($path)
3636
return Promise\reject(new RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno));
3737
}
3838

39-
return Promise\resolve(new Connection($resource, $this->loop));
39+
$connection = new Connection($resource, $this->loop);
40+
$connection->unix = true;
41+
42+
return Promise\resolve($connection);
4043
}
4144
}

tests/UnixConnectorTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace React\Tests\Socket;
44

55
use React\Socket\UnixConnector;
6+
use Clue\React\Block;
7+
use React\Socket\ConnectionInterface;
68

79
class UnixConnectorTest extends TestCase
810
{
@@ -45,8 +47,19 @@ public function testValid()
4547
$promise = $this->connector->connect($path);
4648
$promise->then($this->expectCallableOnce());
4749

50+
// remember remote and local address of this connection and close again
51+
$remote = $local = false;
52+
$promise->then(function(ConnectionInterface $conn) use (&$remote, &$local) {
53+
$remote = $conn->getRemoteAddress();
54+
$local = $conn->getLocalAddress();
55+
$conn->close();
56+
});
57+
4858
// clean up server
4959
fclose($server);
5060
unlink($path);
61+
62+
$this->assertNull($local);
63+
$this->assertEquals('unix://' . $path, $remote);
5164
}
5265
}

0 commit comments

Comments
 (0)