Skip to content
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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ You can also establish your outgoing connections through a SOCKS proxy server
by adding a dependency to [clue/socks-react](https://github.com/clue/php-socks-react).
See the [SOCKS example](examples/socks).

### UNIX domain sockets

This library also supports connecting to a local UNIX domain socket path.
You have to explicitly create a `Sender` that passes every request through the
given UNIX domain socket.
For consistency reasons you still have to pass full HTTP URLs for every request,
but the host and port will be ignored when establishing a connection.

```php
$path = 'unix:///tmp/daemon.sock';
$sender = Sender::createFromLoopUnix($loop, $path);
$client = new Browser($loop, $sender);

$client->get('http://localhost/demo');
```

## Install

The recommended way to install this library is [through composer](http://getcomposer.org). [New to composer?](http://getcomposer.org/doc/00-intro.md)
Expand Down
14 changes: 14 additions & 0 deletions src/Io/Sender.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ public static function createFromLoopConnectors(LoopInterface $loop, ConnectorIn
return new self($http);
}

/**
* create a sender that sends *everything* through given UNIX socket path
*
* @param LoopInterface $loop
* @param string $path
* @return self
*/
public static function createFromLoopUnix(LoopInterface $loop, $path)
{
$connector = new UnixConnector($loop, $path);

return self::createFromLoopConnectors($loop, $connector);
}

private $http;

public function __construct(HttpClient $http)
Expand Down
50 changes: 50 additions & 0 deletions src/Io/UnixConnector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Clue\React\Buzz\Io;

use React\SocketClient\ConnectorInterface;
use React\Stream\Stream;
use React\EventLoop\LoopInterface;
use React\Promise\Deferred;
use RuntimeException;
use Guzzle\Common\Exception\InvalidArgumentException;

/**
* dummy unix domain socket connector
*
* The path to connect to is set once during instantiation, the actual
* target host is then ignored.
*
* Unix domain sockets use atomic operations, so we can as well emulate
* async behavior.
*/
class UnixConnector implements ConnectorInterface
{
private $loop;
private $path;

public function __construct(LoopInterface $loop, $path)
{
if (substr($path, 0, 7) !== 'unix://') {
$path = 'unix://' . $path;
}

$this->loop = $loop;
$this->path = $path;
}

public function create($host, $port)
{
$deferred = new Deferred();

$resource = @stream_socket_client($this->path, $errno, $errstr, 1.0);

if (!$resource) {
$deferred->reject(new RuntimeException('Unable to connect to unix domain socket path: ' . $errstr, $errno));
} else {
$deferred->resolve(new Stream($resource, $this->loop));
}

return $deferred->promise();
}
}
7 changes: 7 additions & 0 deletions tests/Io/SenderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ public function testCreateFromLoopConnectors()
$this->assertInstanceOf('Clue\React\Buzz\Io\Sender', $sender);
}

public function testCreateFromLoopUnix()
{
$sender = Sender::createFromLoopUnix($this->loop, 'unix:///run/daemon.sock');

$this->assertInstanceOf('Clue\React\Buzz\Io\Sender', $sender);
}

public function testSenderRejection()
{
$connector = $this->getMock('React\SocketClient\ConnectorInterface');
Expand Down
41 changes: 41 additions & 0 deletions tests/Io/UnixConnectorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

use Clue\React\Buzz\Io\UnixConnector;
use React\EventLoop\Factory as LoopFactory;

class UnixConnectorTest extends TestCase
{
public function testInvalid()
{
$path = 'invalid://asd';

$loop = $this->getMock('React\EventLoop\LoopInterface');
$connector = new UnixConnector($loop, $path);

$promise = $connector->create('localhost', 80);

$this->expectPromiseReject($promise);
}

public function testValid()
{
$path = tempnam(sys_get_temp_dir(), 'socket');

$server = @stream_socket_server('unix://' . $path, $errno, $errstr);

if (!$server) {
$this->markTestSkipped('Unable to create socket: ' . $errstr . '(' . $errno .')');
return;
}

$loop = $this->getMock('React\EventLoop\LoopInterface');
$connector = new UnixConnector($loop, 'unix://' . $path);

$promise = $connector->create('localhost', 80);

$this->expectPromiseResolve($promise);

fclose($server);
unlink($path);
}
}