Skip to content

Commit bbe6cd5

Browse files
committed
Expose Transfer-Encoding: chunked header and fix chunked HEAD response
1 parent a9505c6 commit bbe6cd5

4 files changed

Lines changed: 37 additions & 89 deletions

File tree

src/Client/Response.php

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace React\Http\Client;
44

55
use Evenement\EventEmitter;
6-
use React\Http\Io\ChunkedDecoder;
76
use React\Stream\ReadableStreamInterface;
87
use React\Stream\Util;
98
use React\Stream\WritableStreamInterface;
@@ -33,11 +32,6 @@ public function __construct(ReadableStreamInterface $stream, $protocol, $version
3332
$this->reasonPhrase = $reasonPhrase;
3433
$this->headers = $headers;
3534

36-
if (strtolower($this->getHeaderLine('Transfer-Encoding')) === 'chunked') {
37-
$this->stream = new ChunkedDecoder($stream);
38-
$this->removeHeader('Transfer-Encoding');
39-
}
40-
4135
$this->stream->on('data', array($this, 'handleData'));
4236
$this->stream->on('error', array($this, 'handleError'));
4337
$this->stream->on('end', array($this, 'handleEnd'));
@@ -69,16 +63,6 @@ public function getHeaders()
6963
return $this->headers;
7064
}
7165

72-
private function removeHeader($name)
73-
{
74-
foreach ($this->headers as $key => $value) {
75-
if (strcasecmp($name, $key) === 0) {
76-
unset($this->headers[$key]);
77-
break;
78-
}
79-
}
80-
}
81-
8266
private function getHeader($name)
8367
{
8468
$name = strtolower($name);

src/Io/Sender.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,12 @@ public function send(RequestInterface $request)
110110

111111
$requestStream->on('response', function (ResponseStream $responseStream) use ($deferred, $request) {
112112
$length = null;
113+
$body = $responseStream;
113114
$code = $responseStream->getCode();
114115
if ($request->getMethod() === 'HEAD' || ($code >= 100 && $code < 200) || $code == 204 || $code == 304) {
115116
$length = 0;
117+
} elseif (\strtolower($responseStream->getHeaderLine('Transfer-Encoding')) === 'chunked') {
118+
$body = new ChunkedDecoder($body);
116119
} elseif ($responseStream->hasHeader('Content-Length')) {
117120
$length = (int) $responseStream->getHeaderLine('Content-Length');
118121
}
@@ -121,7 +124,7 @@ public function send(RequestInterface $request)
121124
$deferred->resolve(new Response(
122125
$responseStream->getCode(),
123126
$responseStream->getHeaders(),
124-
new ReadableBodyStream($responseStream, $length),
127+
new ReadableBodyStream($body, $length),
125128
$responseStream->getVersion(),
126129
$responseStream->getReasonPhrase()
127130
));

tests/Client/ResponseTest.php

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -99,70 +99,5 @@ public function closedResponseShouldNotBeResumedOrPaused()
9999
$response->getHeaders()
100100
);
101101
}
102-
103-
/** @test */
104-
public function chunkedEncodingResponse()
105-
{
106-
$stream = new ThroughStream();
107-
$response = new Response(
108-
$stream,
109-
'http',
110-
'1.0',
111-
'200',
112-
'ok',
113-
array(
114-
'content-type' => 'text/plain',
115-
'transfer-encoding' => 'chunked',
116-
)
117-
);
118-
119-
$buffer = '';
120-
$response->on('data', function ($data) use (&$buffer) {
121-
$buffer.= $data;
122-
});
123-
$this->assertSame('', $buffer);
124-
$stream->write("4; abc=def\r\n");
125-
$this->assertSame('', $buffer);
126-
$stream->write("Wiki\r\n");
127-
$this->assertSame('Wiki', $buffer);
128-
129-
$this->assertSame(
130-
array(
131-
'content-type' => 'text/plain',
132-
),
133-
$response->getHeaders()
134-
);
135-
}
136-
137-
/** @test */
138-
public function doubleChunkedEncodingResponseWillBePassedAsIs()
139-
{
140-
$stream = new ThroughStream();
141-
$response = new Response(
142-
$stream,
143-
'http',
144-
'1.0',
145-
'200',
146-
'ok',
147-
array(
148-
'content-type' => 'text/plain',
149-
'transfer-encoding' => array(
150-
'chunked',
151-
'chunked'
152-
)
153-
)
154-
);
155-
156-
$this->assertSame(
157-
array(
158-
'content-type' => 'text/plain',
159-
'transfer-encoding' => array(
160-
'chunked',
161-
'chunked'
162-
)
163-
),
164-
$response->getHeaders()
165-
);
166-
}
167102
}
168103

tests/FunctionalBrowserTest.php

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ public function testPostString()
462462
$this->assertEquals('hello world', $data['data']);
463463
}
464464

465-
public function testReceiveStreamUntilConnectionsEndsForHttp10()
465+
public function testRequestStreamReturnsResponseBodyUntilConnectionsEndsForHttp10()
466466
{
467467
$response = Block\await($this->browser->withProtocolVersion('1.0')->get($this->base . 'stream/1'), $this->loop);
468468

@@ -473,21 +473,47 @@ public function testReceiveStreamUntilConnectionsEndsForHttp10()
473473
$this->assertStringEndsWith('}', (string) $response->getBody());
474474
}
475475

476-
public function testReceiveStreamChunkedForHttp11()
476+
public function testRequestStreamReturnsResponseWithTransferEncodingChunkedAndResponseBodyDecodedForHttp11()
477477
{
478-
$response = Block\await($this->browser->request('GET', $this->base . 'stream/1'), $this->loop);
478+
$response = Block\await($this->browser->get($this->base . 'stream/1'), $this->loop);
479479

480480
$this->assertEquals('1.1', $response->getProtocolVersion());
481481

482-
// underlying http-client automatically decodes and doesn't expose header
483-
// @link https://github.com/reactphp/http-client/pull/58
484-
// $this->assertEquals('chunked', $response->getHeaderLine('Transfer-Encoding'));
485-
$this->assertFalse($response->hasHeader('Transfer-Encoding'));
482+
$this->assertEquals('chunked', $response->getHeaderLine('Transfer-Encoding'));
486483

487484
$this->assertStringStartsWith('{', (string) $response->getBody());
488485
$this->assertStringEndsWith('}', (string) $response->getBody());
489486
}
490487

488+
public function testRequestStreamWithHeadRequestReturnsEmptyResponseBodWithTransferEncodingChunkedForHttp11()
489+
{
490+
$response = Block\await($this->browser->head($this->base . 'stream/1'), $this->loop);
491+
492+
$this->assertEquals('1.1', $response->getProtocolVersion());
493+
494+
$this->assertEquals('chunked', $response->getHeaderLine('Transfer-Encoding'));
495+
$this->assertEquals('', (string) $response->getBody());
496+
}
497+
498+
public function testRequestStreamReturnsResponseWithResponseBodyUndecodedWhenResponseHasDoubleTransferEncoding()
499+
{
500+
$socket = new \React\Socket\Server(0, $this->loop);
501+
$socket->on('connection', function (\React\Socket\ConnectionInterface $connection) {
502+
$connection->on('data', function () use ($connection) {
503+
$connection->end("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked, chunked\r\nConnection: close\r\n\r\nhello");
504+
});
505+
});
506+
507+
$this->base = str_replace('tcp:', 'http:', $socket->getAddress()) . '/';
508+
509+
$response = Block\await($this->browser->get($this->base . 'stream/1'), $this->loop);
510+
511+
$this->assertEquals('1.1', $response->getProtocolVersion());
512+
513+
$this->assertEquals('chunked, chunked', $response->getHeaderLine('Transfer-Encoding'));
514+
$this->assertEquals('hello', (string) $response->getBody());
515+
}
516+
491517
public function testReceiveStreamAndExplicitlyCloseConnectionEvenWhenServerKeepsConnectionOpen()
492518
{
493519
$closed = new \React\Promise\Deferred();

0 commit comments

Comments
 (0)