Skip to content

Commit

Permalink
Add Unix domain socket (UDS) support to Server with unix:// URI scheme
Browse files Browse the repository at this point in the history
  • Loading branch information
andig authored and clue committed Nov 12, 2017
1 parent 7f4814f commit 9257632
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 6 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ Calling this method more than once on the same instance is a NO-OP.
The `Server` class is the main class in this package that implements the
[`ServerInterface`](#serverinterface) and allows you to accept incoming
streaming connections, such as plaintext TCP/IP or secure TLS connection streams.
Connections can also be accepted on Unix domain sockets.

```php
$server = new Server(8080, $loop);
Expand Down Expand Up @@ -375,6 +376,13 @@ brackets:
$server = new Server('[::1]:8080', $loop);
```

To listen on a Unix domain socket (UDS) path, you MUST prefix the URI with the
`unix://` scheme:

```php
$server = new Server('unix:///tmp/server.sock', $loop);
```

If the given URI is invalid, does not contain a port, any other scheme or if it
contains a hostname, it will throw an `InvalidArgumentException`:

Expand Down
5 changes: 5 additions & 0 deletions examples/01-echo.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
//
// $ php examples/01-echo.php tls://127.0.0.1:8000 examples/localhost.pem
// $ openssl s_client -connect localhost:8000
//
// You can also run a Unix domain socket (UDS) server like this:
//
// $ php examples/01-echo.php unix:///tmp/server.sock
// $ nc -U /tmp/server.sock

use React\EventLoop\Factory;
use React\Socket\Server;
Expand Down
5 changes: 5 additions & 0 deletions examples/02-chat-server.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
//
// $ php examples/02-chat-server.php tls://127.0.0.1:8000 examples/localhost.pem
// $ openssl s_client -connect localhost:8000
//
// You can also run a Unix domain socket (UDS) server like this:
//
// $ php examples/02-chat-server.php unix:///tmp/server.sock
// $ nc -U /tmp/server.sock

use React\EventLoop\Factory;
use React\Socket\Server;
Expand Down
10 changes: 8 additions & 2 deletions examples/03-benchmark.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@
//
// $ php examples/03-benchmark.php 8000
// $ telnet localhost 8000
// $ echo hello world | nc -v localhost 8000
// $ dd if=/dev/zero bs=1M count=1000 | nc -v localhost 8000
// $ echo hello world | nc -N localhost 8000
// $ dd if=/dev/zero bs=1M count=1000 | nc -N localhost 8000
//
// You can also run a secure TLS benchmarking server like this:
//
// $ php examples/03-benchmark.php tls://127.0.0.1:8000 examples/localhost.pem
// $ openssl s_client -connect localhost:8000
// $ echo hello world | openssl s_client -connect localhost:8000
// $ dd if=/dev/zero bs=1M count=1000 | openssl s_client -connect localhost:8000
//
// You can also run a Unix domain socket (UDS) server benchmark like this:
//
// $ php examples/03-benchmark.php unix:///tmp/server.sock
// $ nc -N -U /tmp/server.sock
// $ dd if=/dev/zero bs=1M count=1000 | nc -N -U /tmp/server.sock

use React\EventLoop\Factory;
use React\Socket\Server;
Expand Down
13 changes: 9 additions & 4 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ final class Server extends EventEmitter implements ServerInterface
public function __construct($uri, LoopInterface $loop, array $context = array())
{
// sanitize TCP context options if not properly wrapped
if ($context && (!isset($context['tcp']) && !isset($context['tls']))) {
if ($context && (!isset($context['tcp']) && !isset($context['tls']) && !isset($context['unix']))) {
$context = array('tcp' => $context);
}

// apply default options if not explicitly given
$context += array(
'tcp' => array(),
'tls' => array(),
'unix' => array()
);

$scheme = 'tcp';
Expand All @@ -28,10 +29,14 @@ public function __construct($uri, LoopInterface $loop, array $context = array())
$scheme = substr($uri, 0, $pos);
}

$server = new TcpServer(str_replace('tls://', '', $uri), $loop, $context['tcp']);
if ($scheme === 'unix') {
$server = new UnixServer($uri, $loop, $context['unix']);
} else {
$server = new TcpServer(str_replace('tls://', '', $uri), $loop, $context['tcp']);

if ($scheme === 'tls') {
$server = new SecureServer($server, $loop, $context['tls']);
if ($scheme === 'tls') {
$server = new SecureServer($server, $loop, $context['tls']);
}
}

$this->server = $server;
Expand Down
41 changes: 41 additions & 0 deletions tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

use React\EventLoop\Factory;
use React\Socket\Server;
use React\Socket\TcpConnector;
use React\Socket\UnixConnector;
use Clue\React\Block;
use React\Socket\ConnectionInterface;

class ServerTest extends TestCase
{
const TIMEOUT = 0.1;

public function testCreateServer()
{
$loop = Factory::create();
Expand All @@ -26,6 +30,38 @@ public function testConstructorThrowsForInvalidUri()
$server = new Server('invalid URI', $loop);
}

public function testConstructorCreatesExpectedTcpServer()
{
$loop = Factory::create();

$server = new Server(0, $loop);

$connector = new TcpConnector($loop);
$connector->connect($server->getAddress())
->then($this->expectCallableOnce(), $this->expectCallableNever());

$connection = Block\await($connector->connect($server->getAddress()), $loop, self::TIMEOUT);

$connection->close();
$server->close();
}

public function testConstructorCreatesExpectedUnixServer()
{
$loop = Factory::create();

$server = new Server($this->getRandomSocketUri(), $loop);

$connector = new UnixConnector($loop);
$connector->connect($server->getAddress())
->then($this->expectCallableOnce(), $this->expectCallableNever());

$connection = Block\await($connector->connect($server->getAddress()), $loop, self::TIMEOUT);

$connection->close();
$server->close();
}

public function testEmitsConnectionForNewConnection()
{
$loop = Factory::create();
Expand Down Expand Up @@ -127,4 +163,9 @@ public function testDoesNotEmitSecureConnectionForNewPlainConnection()

Block\sleep(0.1, $loop);
}

private function getRandomSocketUri()
{
return "unix://" . sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid(rand(), true) . '.sock';
}
}

0 comments on commit 9257632

Please sign in to comment.