diff --git a/README.md b/README.md index 5b5d1f1..d179a20 100644 --- a/README.md +++ b/README.md @@ -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); @@ -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 @@ -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); @@ -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 diff --git a/composer.json b/composer.json index 1d37d14..31ada21 100644 --- a/composer.json +++ b/composer.json @@ -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", @@ -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" } } diff --git a/examples/02-concurrent.php b/examples/02-concurrent.php index 2cfda8c..dab758f 100644 --- a/examples/02-concurrent.php +++ b/examples/02-concurrent.php @@ -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) { diff --git a/examples/11-socks-proxy.php b/examples/11-socks-proxy.php index 83154ed..993a4de 100644 --- a/examples/11-socks-proxy.php +++ b/examples/11-socks-proxy.php @@ -1,23 +1,28 @@ $proxy, + 'dns' => false +))); +$sender = new Sender($client); $browser = new Browser($loop, $sender); // demo fetching HTTP headers (or bail out otherwise) diff --git a/src/Io/ConnectionUpcaster.php b/src/Io/ConnectionUpcaster.php new file mode 100644 index 0000000..3ce6159 --- /dev/null +++ b/src/Io/ConnectionUpcaster.php @@ -0,0 +1,77 @@ +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; + } +} diff --git a/src/Io/ConnectorUpcaster.php b/src/Io/ConnectorUpcaster.php new file mode 100644 index 0000000..02fee8e --- /dev/null +++ b/src/Io/ConnectorUpcaster.php @@ -0,0 +1,34 @@ +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); + }); + } +} diff --git a/src/Io/Sender.php b/src/Io/Sender.php index 5a7c976..ec07050 100644 --- a/src/Io/Sender.php +++ b/src/Io/Sender.php @@ -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; @@ -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) { @@ -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 { diff --git a/tests/Io/SenderTest.php b/tests/Io/SenderTest.php index 3e975ae..b27db9a 100644 --- a/tests/Io/SenderTest.php +++ b/tests/Io/SenderTest.php @@ -84,11 +84,20 @@ 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( @@ -96,13 +105,23 @@ public function testRequestProtocolVersion(Request $Request, $method, $uri, $hea ), $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(),