diff --git a/README.md b/README.md index f56c660..d69b3dc 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,6 @@ $loop->run(); ## TODO -* gzip content encoding * chunked transfer encoding * keep-alive connections * following redirections diff --git a/composer.json b/composer.json index 85adaf2..75aa754 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "react/event-loop": "0.4.*", "react/stream": "0.4.*", "react/promise": "~2.2", - "evenement/evenement": "~2.0" + "evenement/evenement": "~2.0", + "clue/zlib-react": "^0.1.0" }, "autoload": { "psr-4": { diff --git a/src/Request.php b/src/Request.php index caded60..938e09c 100644 --- a/src/Request.php +++ b/src/Request.php @@ -156,7 +156,7 @@ public function handleData($data) $this->emit('response', array($response, $this)); - $response->emit('data', array($bodyChunk, $response)); + $this->stream->emit('data', array($bodyChunk)); } } @@ -238,11 +238,15 @@ public function setResponseFactory($factory) $this->responseFactory = $factory; } - public function getResponseFactory() + public function getResponseFactory($psrResponse = null) { if (null === $factory = $this->responseFactory) { $stream = $this->stream; + if ($psrResponse != null) { + $stream = StreamDecoder::detect($stream, $psrResponse); + } + $factory = function ($protocol, $version, $code, $reasonPhrase, $headers) use ($stream) { return new Response( $stream, diff --git a/src/RequestData.php b/src/RequestData.php index 961db22..b11ed1d 100644 --- a/src/RequestData.php +++ b/src/RequestData.php @@ -25,8 +25,9 @@ private function mergeDefaultheaders(array $headers) return array_merge( array( - 'Host' => $this->getHost().$port, - 'User-Agent' => 'React/alpha', + 'Host' => $this->getHost().$port, + 'User-Agent' => 'React/alpha', + 'Accept-Encoding' => 'gzip', ), $connectionHeaders, $authHeaders, diff --git a/src/StreamDecoder.php b/src/StreamDecoder.php new file mode 100644 index 0000000..c43f990 --- /dev/null +++ b/src/StreamDecoder.php @@ -0,0 +1,32 @@ +hasHeader('content-encoding')) { + $stream = static::contentEncoding($stream, $response->getHeaderLine('content-encoding')); + } + + return $stream; + } + + protected static function contentEncoding($stream, $encoding) + { + if ($encoding == 'gzip') { + return $stream->pipe(ZlibFilterStream::createGzipDecompressor()); + } + + if ($encoding == 'deflate') { + return $stream->pipe(ZlibFilterStream::createDeflateDecompressor()); + } + + return $stream; + } +} diff --git a/tests/RequestDataTest.php b/tests/RequestDataTest.php index 6fc8f15..7e2587a 100644 --- a/tests/RequestDataTest.php +++ b/tests/RequestDataTest.php @@ -14,6 +14,7 @@ public function toStringReturnsHTTPRequestMessage() $expected = "GET / HTTP/1.0\r\n" . "Host: www.example.com\r\n" . "User-Agent: React/alpha\r\n" . + "Accept-Encoding: gzip\r\n" . "\r\n"; $this->assertSame($expected, $requestData->__toString()); @@ -28,6 +29,7 @@ public function toStringReturnsHTTPRequestMessageWithProtocolVersion() $expected = "GET / HTTP/1.1\r\n" . "Host: www.example.com\r\n" . "User-Agent: React/alpha\r\n" . + "Accept-Encoding: gzip\r\n" . "Connection: close\r\n" . "\r\n"; @@ -42,6 +44,7 @@ public function toStringReturnsHTTPRequestMessageWithProtocolVersionThroughConst $expected = "GET / HTTP/1.1\r\n" . "Host: www.example.com\r\n" . "User-Agent: React/alpha\r\n" . + "Accept-Encoding: gzip\r\n" . "Connection: close\r\n" . "\r\n"; @@ -56,6 +59,7 @@ public function toStringUsesUserPassFromURL() $expected = "GET / HTTP/1.0\r\n" . "Host: www.example.com\r\n" . "User-Agent: React/alpha\r\n" . + "Accept-Encoding: gzip\r\n" . "Authorization: Basic am9objpkdW1teQ==\r\n" . "\r\n"; diff --git a/tests/RequestTest.php b/tests/RequestTest.php index 48b6683..ea0551d 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -68,13 +68,13 @@ public function requestShouldBindToStreamEventsAndUseconnector() ->expects($this->at(8)) ->method('removeListener') ->with('error', $this->identicalTo(array($request, 'handleError'))); + $this->stream + ->expects($this->at(9)) + ->method('emit') + ->with('data', array('body')); $response = $this->response; - $response->expects($this->once()) - ->method('emit') - ->with('data', array('body', $response)); - $response->expects($this->at(0)) ->method('on') ->with('end', $this->anything()) @@ -249,7 +249,7 @@ public function postRequestShouldSendAPostRequest() $this->stream ->expects($this->at(4)) ->method('write') - ->with($this->matchesRegularExpression("#^POST / HTTP/1\.0\r\nHost: www.example.com\r\nUser-Agent:.*\r\n\r\n$#")); + ->with($this->matchesRegularExpression("#^POST / HTTP/1\.0\r\nHost: www.example.com\r\nUser-Agent:.*\r\nAccept-Encoding: gzip\r\n\r\n$#")); $this->stream ->expects($this->at(5)) ->method('write') @@ -279,7 +279,7 @@ public function writeWithAPostRequestShouldSendToTheStream() $this->stream ->expects($this->at(4)) ->method('write') - ->with($this->matchesRegularExpression("#^POST / HTTP/1\.0\r\nHost: www.example.com\r\nUser-Agent:.*\r\n\r\n$#")); + ->with($this->matchesRegularExpression("#^POST / HTTP/1\.0\r\nHost: www.example.com\r\nUser-Agent:.*\r\nAccept-Encoding: gzip\r\n\r\n$#")); $this->stream ->expects($this->at(5)) ->method('write') @@ -320,7 +320,7 @@ public function writeWithAPostRequestShouldSendBodyAfterHeadersAndEmitDrainEvent $this->stream ->expects($this->at(4)) ->method('write') - ->with($this->matchesRegularExpression("#^POST / HTTP/1\.0\r\nHost: www.example.com\r\nUser-Agent:.*\r\n\r\n$#")); + ->with($this->matchesRegularExpression("#^POST / HTTP/1\.0\r\nHost: www.example.com\r\nUser-Agent:.*\r\nAccept-Encoding: gzip\r\n\r\n$#")); $this->stream ->expects($this->at(5)) ->method('write') @@ -367,7 +367,7 @@ public function pipeShouldPipeDataIntoTheRequestBody() $this->stream ->expects($this->at(4)) ->method('write') - ->with($this->matchesRegularExpression("#^POST / HTTP/1\.0\r\nHost: www.example.com\r\nUser-Agent:.*\r\n\r\n$#")); + ->with($this->matchesRegularExpression("#^POST / HTTP/1\.0\r\nHost: www.example.com\r\nUser-Agent:.*\r\nAccept-Encoding: gzip\r\n\r\n$#")); $this->stream ->expects($this->at(5)) ->method('write') diff --git a/tests/StreamDecoderTest.php b/tests/StreamDecoderTest.php new file mode 100644 index 0000000..e66b27b --- /dev/null +++ b/tests/StreamDecoderTest.php @@ -0,0 +1,53 @@ + [ + 'gzip' + ], + ]), + 'Clue\React\Zlib\ZlibFilterStream', + ], + [ + new psr7Response(200, [ + 'Content-Encoding' => [ + 'deflate' + ], + ]), + 'Clue\React\Zlib\ZlibFilterStream', + ], + [ + new psr7Response(200, [ + 'Content-Encoding' => [ + 'foobar' + ], + ]), + 'React\Stream\ThroughStream', + ], + ]; + } + + /** + * @dataProvider providerDetect + */ + public function testDetect($response, $instanceOf) + { + $stream = StreamDecoder::detect(new ThroughStream(), $response); + $this->assertInstanceOf($instanceOf, $stream); + } +}