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
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,15 @@ If you need custom DNS settings, you can explicitly create a [`Sender`](#sender)
with your DNS server address (or `React\Dns\Resolver` instance) like this:

```php
// new API for react/http 0.5
$connector = new \React\Socket\Connector($loop, array(
'dns' => '127.0.0.1'
));
$client = new \React\HttpClient\Client($loop, $connector);
$sender = new \Clue\Buzz\Io\Sender($client);
$browser = $browser->withSender($sender);

// deprecated legacy API
$dns = '127.0.0.1';
$sender = Sender::createFromLoopDns($loop, $dns);
$browser = $browser->withSender($sender);
Expand All @@ -455,9 +464,24 @@ See also [`Browser::withSender()`](#withsender) for more details.
### Connection options

If you need custom connector settings (DNS resolution, SSL/TLS parameters, timeouts etc.), you can explicitly pass a
custom instance of the [`ConnectorInterface`](https://github.com/reactphp/socket-client#connectorinterface).
custom instance of the new [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface).

```php
// new API for react/http 0.5
$connector = new \React\Socket\Connector($loop, array(
'dns' => '127.0.0.1'
));
$client = new \React\HttpClient\Client($loop, $connector);
$sender = new \Clue\Buzz\Io\Sender($client);
$browser = $browser->withSender($sender);
```

If you're still using the deprecated legacy Http component and you need custom
connector settings (DNS resolution, SSL/TLS parameters, timeouts etc.), you can
explicitly pass a custom instance of the
[legacy `ConnectorInterface`](https://github.com/reactphp/socket-client#connectorinterface).

The below examples assume you've installed the latest SocketClient version:
The below examples assume you've installed the latest legacy SocketClient version:

```bash
$ composer require react/socket-client:^0.5
Expand All @@ -468,6 +492,7 @@ You can optionally pass additional
to the constructor like this:

```php
// deprecated legacy API
// use local DNS server
$dnsResolverFactory = new DnsFactory();
$resolver = $dnsResolverFactory->createCached('127.0.0.1', $loop);
Expand All @@ -487,6 +512,7 @@ You can optionally pass additional
to the constructor like this:

```php
// deprecated legacy API
$ssl = new SecureConnector($tcp, $loop, array(
'verify_peer' => false,
'verify_peer_name' => false
Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"require": {
"php": ">=5.3",
"react/event-loop": "^0.4 || ^0.3",
"react/http-client": "^0.4 || ^0.3",
"react/http-client": "^0.5 || ^0.4 || ^0.3",
"react/socket": "^0.8",
"react/socket-client": "^0.5 || ^0.4.5",
"react/dns": "^0.4.1 || ^0.3",
"react/promise": "^2 || ^1.1",
Expand All @@ -26,10 +27,9 @@
},
"require-dev": {
"clue/block-react": "^1.0",
"clue/socks-react": "^0.6",
"clue/socks-react": "^0.8 || ^0.7",
"phpunit/phpunit": "^4.5",
"react/http": "^0.7.2",
"react/promise-stream": "^0.1.1",
"react/socket": "^0.8"
"react/promise-stream": "^0.1.1"
}
}
2 changes: 1 addition & 1 deletion examples/02-concurrent.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
$client = new Browser($loop);

$client->head('http://www.github.com/clue/http-react')->then(function (ResponseInterface $response) {
var_dump($response->getHeaders(), (string)$result->getBody());
var_dump($response->getHeaders(), (string)$response->getBody());
});

$client->get('http://google.com/')->then(function (ResponseInterface $response) {
Expand Down
19 changes: 12 additions & 7 deletions examples/11-socks-proxy.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
<?php

use Clue\React\Buzz\Io\Sender;
use React\SocketClient\TcpConnector;
use React\EventLoop\Factory as LoopFactory;
use Clue\React\Socks\Client as SocksClient;
use Clue\React\Buzz\Browser;
use Clue\React\Socks\Client as SocksClient;
use Psr\Http\Message\ResponseInterface;
use React\EventLoop\Factory as LoopFactory;
use React\HttpClient\Client as HttpClient;
use React\Socket\Connector;
use RingCentral\Psr7;

require __DIR__ . '/../vendor/autoload.php';

$loop = LoopFactory::create();

// create a new SOCKS client which connects to a SOCKS server listening on localhost:9050
// not already running a SOCKS server? Try this: ssh -D 9050 localhost
$socks = new SocksClient('127.0.0.1:9050', new TcpConnector($loop));
// create a new SOCKS client which connects to a SOCKS server listening on localhost:1080
// not already running a SOCKS server? Try this: ssh -D 1080 localhost
$proxy = new SocksClient('127.0.0.1:1080', new Connector($loop));

// create a Browser object that uses the SOCKS client for connections
$sender = Sender::createFromLoopConnectors($loop, $socks);
$client = new HttpClient($loop, new Connector($loop, array(
'tcp' => $proxy,
'dns' => false
)));
$sender = new Sender($client);
$browser = new Browser($loop, $sender);

// demo fetching HTTP headers (or bail out otherwise)
Expand Down
77 changes: 77 additions & 0 deletions src/Io/ConnectionUpcaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Clue\React\Buzz\Io;

use Evenement\EventEmitter;
use React\Socket\ConnectionInterface;
use React\Stream\DuplexStreamInterface;
use React\Stream\WritableStreamInterface;
use React\Stream\Util;

/**
* Adapter to upcast a legacy SocketClient:v0.5 Connector result to a new Socket:v0.8 ConnectionInterface
*
* @internal
*/
class ConnectionUpcaster extends EventEmitter implements ConnectionInterface
{
private $stream;

public function __construct(DuplexStreamInterface $stream)
{
$this->stream = $stream;

Util::forwardEvents($stream, $this, array('data', 'end', 'close', 'error', 'drain'));
}

public function isReadable()
{
return $this->stream->isReadable();
}

public function isWritable()
{
return $this->isWritable();
}

public function pause()
{
$this->stream->pause();
}

public function resume()
{
$this->stream->resume();
}

public function pipe(WritableStreamInterface $dest, array $options = array())
{
$this->stream->pipe($dest, $options);
}

public function write($data)
{
return $this->stream->write($data);
}

public function end($data = null)
{
return $this->stream->end($data);
}

public function close()
{
$this->stream->close();
$this->removeAllListeners();
}

public function getRemoteAddress()
{
return null;
}

public function getLocalAddress()
{
return null;
}
}
34 changes: 34 additions & 0 deletions src/Io/ConnectorUpcaster.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Clue\React\Buzz\Io;

use React\Socket\ConnectorInterface;
use React\SocketClient\ConnectorInterface as LegacyConnectorInterface;
use React\Stream\Stream;

/**
* Adapter to upcast a legacy SocketClient:v0.5 Connector to a new Socket:v0.8 Connector
*
* @internal
*/
class ConnectorUpcaster implements ConnectorInterface
{
private $legacy;

public function __construct(LegacyConnectorInterface$connector)
{
$this->legacy = $connector;
}

public function connect($uri)
{
$parts = parse_url((strpos($uri, '://') === false ? 'tcp://' : '') . $uri);
if (!$parts || !isset($parts['host'], $parts['port'])) {
return \React\Promise\reject(new \InvalidArgumentException('Unable to parse URI'));
}

return $this->legacy->create($parts['host'], $parts['port'])->then(function (Stream $stream) {
return new ConnectionUpcaster($stream);
});
}
}
55 changes: 44 additions & 11 deletions src/Io/Sender.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
use React\Promise\Deferred;
use React\EventLoop\LoopInterface;
use React\Dns\Resolver\Factory as ResolverFactory;
use React\SocketClient\Connector;
use React\Socket\Connector;
use React\SocketClient\Connector as LegacyConnector;
use React\SocketClient\SecureConnector;
use RuntimeException;
use React\SocketClient\ConnectorInterface;
use React\SocketClient\ConnectorInterface as LegacyConnectorInterface;
use React\Dns\Resolver\Resolver;
use React\Promise;
use Clue\React\Buzz\Message\MessageFactory;
Expand All @@ -24,20 +24,42 @@ class Sender
/**
* create a new default sender attached to the given event loop
*
* This method is used internally to create the "default sender".
* If you need custom DNS or connector settings, you're recommended to
* explicitly create a HttpClient instance yourself and pass this to the
* constructor of this method manually like this:
*
* ```php
* $connector = new \React\Socket\Connector($loop);
* $client = new \React\HttpClient\Client($loop, $connector);
* $sender = new \Clue\React\Buzz\Io\Sender($client);
* $browser = new \Clue\React\Buzz\Browser($loop, $sender);
* ```
*
* @param LoopInterface $loop
* @return self
*/
public static function createFromLoop(LoopInterface $loop)
{
$ref = new \ReflectionClass('React\HttpClient\Client');
$num = $ref->getConstructor()->getNumberOfRequiredParameters();
if ($num === 1) {
// react/http 0.5
return new self(new HttpClient($loop));
}

// react/http 0.4/0.3
return self::createFromLoopDns($loop, '8.8.8.8');
}

/**
* create sender attached to the given event loop and DNS resolver
* [deprecated] create sender attached to the given event loop and DNS resolver
*
* @param LoopInterface $loop
* @param Resolver|string $dns DNS resolver instance or IP address
* @return self
* @deprecated as of v1.2.0, see createFromLoop()
* @see self::createFromLoop()
*/
public static function createFromLoopDns(LoopInterface $loop, $dns)
{
Expand All @@ -46,29 +68,40 @@ public static function createFromLoopDns(LoopInterface $loop, $dns)
$dns = $dnsResolverFactory->createCached($dns, $loop);
}

$connector = new Connector($loop, $dns);
$connector = new LegacyConnector($loop, $dns);

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

/**
* create sender attached to given event loop using the given connectors
* [deprecated] create sender attached to given event loop using the given legacy connectors
*
* @param LoopInterface $loop
* @param ConnectorInterface $connector default connector to use to establish TCP/IP connections
* @param ConnectorInterface|null $secureConnector secure connector to use to establish TLS/SSL connections (optional, composed from given default connector)
* @param LegacyConnectorInterface $connector default legacy connector to use to establish TCP/IP connections
* @param LegacyConnectorInterface|null $secureConnector secure legacy connector to use to establish TLS/SSL connections (optional, composed from given default connector)
* @return self
* @deprecated as of v1.2.0, see createFromLoop()
* @see self::createFromLoop()
*/
public static function createFromLoopConnectors(LoopInterface $loop, ConnectorInterface $connector, ConnectorInterface $secureConnector = null)
public static function createFromLoopConnectors(LoopInterface $loop, LegacyConnectorInterface $connector, LegacyConnectorInterface $secureConnector = null)
{
if ($secureConnector === null) {
$secureConnector = new SecureConnector($connector, $loop);
}

// create HttpClient for React 0.4/0.3 (code coverage will be achieved by testing both versions)
// create HttpClient for React 0.5/0.4/0.3 (code coverage will be achieved by testing versions with Travis)
// @codeCoverageIgnoreStart
$ref = new \ReflectionClass('React\HttpClient\Client');
if ($ref->getConstructor()->getNumberOfRequiredParameters() == 2) {
$num = $ref->getConstructor()->getNumberOfRequiredParameters();
if ($num === 1) {
// react/http-client:0.5 only requires the loop, the connector is actually optional
// v0.5 requires the new Socket-Connector, so we upcast from the legacy SocketClient-Connectors here
$http = new HttpClient($loop, new Connector($loop, array(
'tcp' => new ConnectorUpcaster($connector),
'tls' => new ConnectorUpcaster($secureConnector),
'dns' => false
)));
} elseif ($num === 2) {
// react/http-client:0.4 removed the $loop parameter
$http = new HttpClient($connector, $secureConnector);
} else {
Expand Down
33 changes: 26 additions & 7 deletions tests/Io/SenderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,25 +84,44 @@ public function testRequestProtocolVersion(Request $Request, $method, $uri, $hea
{
$httpClientArguments = array();
$ref = new \ReflectionClass('React\HttpClient\Client');
if ($ref->getConstructor()->getNumberOfRequiredParameters() == 3) {
$num = $ref->getConstructor()->getNumberOfRequiredParameters();

if ($num === 1) {
// react/http 0.5
$httpClientArguments[] = $this->getMock('React\EventLoop\LoopInterface');
} else {
if ($num == 3) {
// only for react/http 0.3
$httpClientArguments[] = $this->getMock('React\EventLoop\LoopInterface');
}
$httpClientArguments[] = $this->getMock('React\SocketClient\ConnectorInterface');
$httpClientArguments[] = $this->getMock('React\SocketClient\ConnectorInterface');
}
$httpClientArguments[] = $this->getMock('React\SocketClient\ConnectorInterface');
$httpClientArguments[] = $this->getMock('React\SocketClient\ConnectorInterface');

$http = $this->getMock(
'React\HttpClient\Client',
array(
'request',
),
$httpClientArguments
);

$requestArguments = array();
$ref = new \ReflectionClass('React\HttpClient\Request');
if ($ref->getConstructor()->getNumberOfRequiredParameters() == 3) {
$requestArguments[] = $this->getMock('React\EventLoop\LoopInterface');
if ($num === 1) {
// react/http 0.5
$requestArguments[] = $this->getMock('React\Socket\ConnectorInterface');
} else {
// react/http 0.4/0.3
$ref = new \ReflectionClass('React\HttpClient\Request');
$num = $ref->getConstructor()->getNumberOfRequiredParameters();

if ($num === 3) {
$requestArguments[] = $this->getMock('React\EventLoop\LoopInterface');
}
$requestArguments[] = $this->getMock('React\SocketClient\ConnectorInterface');
}
$requestArguments[] = $this->getMock('React\SocketClient\ConnectorInterface');
$requestArguments[] = new RequestData($method, $uri, $headers, $protocolVersion);

$request = $this->getMock(
'React\HttpClient\Request',
array(),
Expand Down