Skip to content

Commit

Permalink
Only read up to Content-Length in stream wrapper
Browse files Browse the repository at this point in the history
This commit updates the stream wrapper to only read up to the number of
bytes returned in the Content-Length header when draining a stream
synchronously.
  • Loading branch information
mtdowling committed Jun 30, 2016
1 parent 79c6fbe commit 1fa6184
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 4 deletions.
26 changes: 22 additions & 4 deletions src/Handler/StreamHandler.php
Expand Up @@ -119,7 +119,11 @@ private function createResponse(
}

if ($sink !== $stream) {
$this->drain($stream, $sink);
$this->drain(
$stream,
$sink,
$response->getHeaderLine('Content-Length')
);
}

$this->invokeStats($options, $request, $startTime, $response, null);
Expand Down Expand Up @@ -181,13 +185,27 @@ private function checkDecode(array $options, array $headers, $stream)
*
* @param StreamInterface $source
* @param StreamInterface $sink
* @param string $contentLength Header specifying the amount of
* data to read.
*
* @return StreamInterface
* @throws \RuntimeException when the sink option is invalid.
*/
private function drain(StreamInterface $source, StreamInterface $sink)
{
Psr7\copy_to_stream($source, $sink);
private function drain(
StreamInterface $source,
StreamInterface $sink,
$contentLength
) {
// If a content-length header is provided, then stop reading once
// that number of bytes has been read. This can prevent infinitely
// reading from a stream when dealing with servers that do not honor
// Connection: Close headers.
Psr7\copy_to_stream(
$source,
$sink,
strlen($contentLength) > 0 ? (int) $contentLength : -1
);

$sink->seek(0);
$source->close();

Expand Down
18 changes: 18 additions & 0 deletions tests/Handler/StreamHandlerTest.php
Expand Up @@ -142,6 +142,24 @@ public function testDrainsResponseIntoSaveToBodyAtNonExistentPath()
unlink($tmpfname);
}

public function testDrainsResponseAndReadsOnlyContentLengthBytes()
{
Server::flush();
Server::enqueue([
new Response(200, [
'Foo' => 'Bar',
'Content-Length' => 8,
], 'hi there... This has way too much data!')
]);
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, [])->wait();
$body = $response->getBody();
$stream = $body->detach();
$this->assertEquals('hi there', stream_get_contents($stream));
fclose($stream);
}

public function testAutomaticallyDecompressGzip()
{
Server::flush();
Expand Down

0 comments on commit 1fa6184

Please sign in to comment.