diff --git a/.travis.yml b/.travis.yml index 04f51ad..7183822 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: -# - 5.3 # requires old distro, see below - 5.4 - 5.5 - 5.6 @@ -12,9 +11,6 @@ php: dist: trusty matrix: - include: - - php: 5.3 - dist: precise allow_failures: - php: hhvm diff --git a/README.md b/README.md index d179a20..c778559 100644 --- a/README.md +++ b/README.md @@ -315,10 +315,6 @@ $request = $request->withProtocolVersion(1.1); $browser->send($request)->then(…); ``` -> Legacy compatibility: Note that the custom HTTP protocol version will be -ignored for legacy versions (PHP 5.3) because the underlying `http-client` -v0.3 API does not support this parameter. - #### withOptions() The `withOptions(array $options)` method can be used to change the [options](#options) to use: @@ -481,12 +477,6 @@ 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 legacy SocketClient version: - -```bash -$ composer require react/socket-client:^0.5 -``` - You can optionally pass additional [socket context options](http://php.net/manual/en/context.socket.php) to the constructor like this: @@ -583,7 +573,7 @@ $ composer require clue/buzz-react:^1.1.1 See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. This project aims to run on any platform and thus does not require any PHP -extensions and supports running on legacy PHP 5.3 through current PHP 7+ and +extensions and supports running on legacy PHP 5.4 through current PHP 7+ and HHVM. It's *highly recommended to use PHP 7+* for this project. diff --git a/composer.json b/composer.json index 31ada21..f1e0b03 100644 --- a/composer.json +++ b/composer.json @@ -14,14 +14,13 @@ "psr-4": { "Clue\\React\\Buzz\\": "src/" } }, "require": { - "php": ">=5.3", + "php": ">=5.4", "react/event-loop": "^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/http-client": "^0.5", "react/promise": "^2 || ^1.1", - "react/stream": "^0.4 || ^0.3.1", + "react/socket": "^0.8", + "react/socket-client": "^0.7 || ^0.6", + "react/stream": "^0.6 || ^0.5 || ^0.4.6", "psr/http-message": "^1.0", "ringcentral/psr7": "^1.2" }, diff --git a/examples/21-stream-forwarding.php b/examples/21-stream-forwarding.php index 601a644..dcc7250 100644 --- a/examples/21-stream-forwarding.php +++ b/examples/21-stream-forwarding.php @@ -1,9 +1,8 @@ pause(); - -$info = new Stream(STDERR, $loop); -$info->pause(); +$out = new WritableResourceStream(STDOUT, $loop); +$info = new WritableResourceStream(STDERR, $loop); $info->write('Requesting ' . $url . '…' . PHP_EOL); diff --git a/examples/22-stream-stdin.php b/examples/22-stream-stdin.php index 4aaaa44..44ca102 100644 --- a/examples/22-stream-stdin.php +++ b/examples/22-stream-stdin.php @@ -1,9 +1,8 @@ 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 $this->legacy->connect($uri)->then(function (DuplexStreamInterface $stream) { return new ConnectionUpcaster($stream); }); } diff --git a/src/Io/FixedUriConnector.php b/src/Io/FixedUriConnector.php new file mode 100644 index 0000000..0b4777b --- /dev/null +++ b/src/Io/FixedUriConnector.php @@ -0,0 +1,23 @@ +uri = $uri; + $this->connector = $connector; + } + + public function connect($_) + { + return $this->connector->connect($this->uri); + } +} diff --git a/src/Io/Sender.php b/src/Io/Sender.php index ec07050..bee0cc5 100644 --- a/src/Io/Sender.php +++ b/src/Io/Sender.php @@ -2,22 +2,20 @@ namespace Clue\React\Buzz\Io; -use React\HttpClient\Client as HttpClient; +use Clue\React\Buzz\Message\MessageFactory; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; +use React\EventLoop\LoopInterface; +use React\HttpClient\Client as HttpClient; use React\HttpClient\Request as RequestStream; use React\HttpClient\Response as ResponseStream; +use React\Promise; use React\Promise\Deferred; -use React\EventLoop\LoopInterface; -use React\Dns\Resolver\Factory as ResolverFactory; use React\Socket\Connector; -use React\SocketClient\Connector as LegacyConnector; use React\SocketClient\SecureConnector; use React\SocketClient\ConnectorInterface as LegacyConnectorInterface; -use React\Dns\Resolver\Resolver; -use React\Promise; -use Clue\React\Buzz\Message\MessageFactory; use React\Stream\ReadableStreamInterface; +use React\Socket\UnixConnector; class Sender { @@ -41,36 +39,23 @@ class Sender */ 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'); + return new self(new HttpClient($loop)); } /** * [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 + * @param \React\Dns\Resolver\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) { - if (!($dns instanceof Resolver)) { - $dnsResolverFactory = new ResolverFactory(); - $dns = $dnsResolverFactory->createCached($dns, $loop); - } - - $connector = new LegacyConnector($loop, $dns); - - return self::createFromLoopConnectors($loop, $connector); + return new self(new HttpClient($loop, new Connector($loop, array( + 'dns' => $dns + )))); } /** @@ -89,27 +74,13 @@ public static function createFromLoopConnectors(LoopInterface $loop, LegacyConne $secureConnector = new SecureConnector($connector, $loop); } - // 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'); - $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 { - $http = new HttpClient($loop, $connector, $secureConnector); - } - // @codeCoverageIgnoreEnd - - return new self($http); + // react/http v0.5 requires the new Socket-Connector, so we upcast from the legacy SocketClient-Connectors here + return new self(new HttpClient($loop, new Connector($loop, array( + 'tcp' => new ConnectorUpcaster($connector), + 'tls' => new ConnectorUpcaster($secureConnector), + 'dns' => false, + 'timeout' => false + )))); } /** @@ -121,9 +92,15 @@ public static function createFromLoopConnectors(LoopInterface $loop, LegacyConne */ public static function createFromLoopUnix(LoopInterface $loop, $path) { - $connector = new UnixConnector($loop, $path); - - return self::createFromLoopConnectors($loop, $connector); + return new self( + new HttpClient( + $loop, + new FixedUriConnector( + $path, + new UnixConnector($loop) + ) + ) + ); } private $http; diff --git a/src/Io/UnixConnector.php b/src/Io/UnixConnector.php deleted file mode 100644 index 5496e8b..0000000 --- a/src/Io/UnixConnector.php +++ /dev/null @@ -1,51 +0,0 @@ -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(); - } -} diff --git a/tests/FunctionalBrowserTest.php b/tests/FunctionalBrowserTest.php index 46bae23..841d1fb 100644 --- a/tests/FunctionalBrowserTest.php +++ b/tests/FunctionalBrowserTest.php @@ -183,6 +183,7 @@ public function testPostStreamChunked() $this->loop->addTimer(0.001, function () use ($stream) { $stream->emit('data', array('hello world')); + $stream->emit('end'); $stream->close(); }); diff --git a/tests/Io/FixedUriConnectorTest.php b/tests/Io/FixedUriConnectorTest.php new file mode 100644 index 0000000..02f2668 --- /dev/null +++ b/tests/Io/FixedUriConnectorTest.php @@ -0,0 +1,16 @@ +getMockBuilder('React\Socket\ConnectorInterface')->getMock(); + $base->expects($this->once())->method('connect')->with('test')->willReturn('ret'); + + $connector = new FixedUriConnector('test', $base); + + $this->assertEquals('ret', $connector->connect('ignored')); + } +} diff --git a/tests/Io/SenderTest.php b/tests/Io/SenderTest.php index b27db9a..96d0bc6 100644 --- a/tests/Io/SenderTest.php +++ b/tests/Io/SenderTest.php @@ -1,6 +1,7 @@ assertInstanceOf('Clue\React\Buzz\Io\Sender', $sender); } - public function testSenderRejection() + public function testSenderConnectorRejection() + { + $connector = $this->getMock('React\Socket\ConnectorInterface'); + $connector->expects($this->once())->method('connect')->willReturn(Promise\reject(new RuntimeException('Rejected'))); + + $sender = new Sender(new HttpClient($this->loop, $connector)); + + $request = new Request('GET', 'http://www.google.com/'); + + $promise = $sender->send($request, $this->getMock('Clue\React\Buzz\Message\MessageFactory')); + + $this->setExpectedException('RuntimeException'); + Block\await($promise, $this->loop); + } + + public function testSenderLegacyConnectorRejection() { $connector = $this->getMock('React\SocketClient\ConnectorInterface'); - $connector->expects($this->once())->method('create')->willReturn(Promise\reject(new RuntimeException('Rejected'))); + $connector->expects($this->once())->method('connect')->willReturn(Promise\reject(new RuntimeException('Rejected'))); $sender = Sender::createFromLoopConnectors($this->loop, $connector); @@ -82,51 +98,25 @@ public function provideRequestProtocolVersion() */ public function testRequestProtocolVersion(Request $Request, $method, $uri, $headers, $protocolVersion) { - $httpClientArguments = array(); - $ref = new \ReflectionClass('React\HttpClient\Client'); - $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'); - } - $http = $this->getMock( 'React\HttpClient\Client', array( 'request', ), - $httpClientArguments + array( + $this->getMock('React\EventLoop\LoopInterface') + ) ); - $requestArguments = array(); - 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[] = new RequestData($method, $uri, $headers, $protocolVersion); - $request = $this->getMock( 'React\HttpClient\Request', array(), - $requestArguments + array( + $this->getMock('React\Socket\ConnectorInterface'), + new RequestData($method, $uri, $headers, $protocolVersion) + ) ); + $http->expects($this->once())->method('request')->with($method, $uri, $headers, $protocolVersion)->willReturn($request); $sender = new Sender($http); diff --git a/tests/Io/UnixConnectorTest.php b/tests/Io/UnixConnectorTest.php deleted file mode 100644 index 2591ff5..0000000 --- a/tests/Io/UnixConnectorTest.php +++ /dev/null @@ -1,45 +0,0 @@ -getMock('React\EventLoop\LoopInterface'); - $connector = new UnixConnector($loop, $path); - - $promise = $connector->create('localhost', 80); - - $this->setExpectedException('RuntimeException'); - Block\await($promise, $loop); - } - - 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); - - $stream = Block\await($promise, $loop); - /* @var $stream React\Stream\Stream */ - $stream->close(); - - fclose($server); - unlink($path); - } -}