Skip to content
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

Fix unix:// addresses for Unix domain socket (UDS) paths #100

Merged
merged 2 commits into from
May 8, 2017
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
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ If the remote address can not be determined or is unknown at this time (such as
after the connection has been closed), it MAY return a `NULL` value instead.

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

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

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

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

> The [`getRemoteAddress()`](#getremoteaddress) method will return the target
Unix domain socket (UDS) path as given to the `connect()` method, including
the `unix://` scheme, for example `unix:///tmp/demo.sock`.
The [`getLocalAddress()`](#getlocaladdress) method will most likely return a
`null` value as this value is not applicable to UDS connections here.

Under the hood, the `Connector` is implemented as a *higher-level facade*
for the lower-level connectors implemented in this package. This means it
also shares all of their features and implementation details.
Expand Down Expand Up @@ -1209,6 +1217,12 @@ Connecting to Unix domain sockets is an atomic operation, i.e. its promise will
settle (either resolve or reject) immediately.
As such, calling `cancel()` on the resulting promise has no effect.

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

## Install

The recommended way to install this library is [through Composer](http://getcomposer.org).
Expand Down
23 changes: 23 additions & 0 deletions src/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
*/
class Connection extends EventEmitter implements ConnectionInterface
{
/**
* Internal flag whether this is a Unix domain socket (UDS) connection
*
* @internal
*/
public $unix = false;

/**
* Internal flag whether encryption has been enabled on this connection
*
Expand Down Expand Up @@ -138,6 +145,22 @@ private function parseAddress($address)
return null;
}

if ($this->unix) {
// remove trailing colon from address for HHVM < 3.19: https://3v4l.org/5C1lo
// note that techncially ":" is a valid address, so keep this in place otherwise
if (substr($address, -1) === ':' && defined('HHVM_VERSION_ID') && HHVM_VERSION_ID < 31900) {
$address = (string)substr($address, 0, -1);
}

// work around unknown addresses should return null value: https://3v4l.org/5C1lo
// PHP uses "\0" string and HHVM uses empty string (colon removed above)
if ($address === "\x00" || $address === '') {
return null;
}

return 'unix://' . $address;
}

// check if this is an IPv6 address which includes multiple colons but no square brackets
$pos = strrpos($address, ':');
if ($pos !== false && strpos($address, ':') < $pos && substr($address, 0, 1) !== '[') {
Expand Down
6 changes: 4 additions & 2 deletions src/ConnectionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ interface ConnectionInterface extends DuplexStreamInterface
* after the connection has been closed), it MAY return a `NULL` value instead.
*
* Otherwise, it will return the full address (URI) as a string value, such
* as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`.
* as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
* `unix://example.sock` or `unix:///path/to/example.sock`.
* Note that individual URI components are application specific and depend
* on the underlying transport protocol.
*
Expand Down Expand Up @@ -95,7 +96,8 @@ public function getRemoteAddress();
* after the connection has been closed), it MAY return a `NULL` value instead.
*
* Otherwise, it will return the full address (URI) as a string value, such
* as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`.
* as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`,
* `unix://example.sock` or `unix:///path/to/example.sock`.
* Note that individual URI components are application specific and depend
* on the underlying transport protocol.
*
Expand Down
5 changes: 4 additions & 1 deletion src/UnixConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public function connect($path)
return Promise\reject(new RuntimeException('Unable to connect to unix domain socket "' . $path . '": ' . $errstr, $errno));
}

return Promise\resolve(new Connection($resource, $this->loop));
$connection = new Connection($resource, $this->loop);
$connection->unix = true;

return Promise\resolve($connection);
}
}
13 changes: 13 additions & 0 deletions tests/UnixConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace React\Tests\Socket;

use React\Socket\UnixConnector;
use Clue\React\Block;
use React\Socket\ConnectionInterface;

class UnixConnectorTest extends TestCase
{
Expand Down Expand Up @@ -45,8 +47,19 @@ public function testValid()
$promise = $this->connector->connect($path);
$promise->then($this->expectCallableOnce());

// remember remote and local address of this connection and close again
$remote = $local = false;
$promise->then(function(ConnectionInterface $conn) use (&$remote, &$local) {
$remote = $conn->getRemoteAddress();
$local = $conn->getLocalAddress();
$conn->close();
});

// clean up server
fclose($server);
unlink($path);

$this->assertNull($local);
$this->assertEquals('unix://' . $path, $remote);
}
}