From 0c61641376f85c21255ecaa4ebd74b7d7bb24438 Mon Sep 17 00:00:00 2001 From: PJ Dietz Date: Mon, 22 Feb 2016 14:45:16 -0500 Subject: [PATCH] Do not attempt to rewind unsociable streams when transmitting response --- .gitignore | 1 + src/Transmission/Transmitter.php | 4 +- .../unit/Transmission/TransmitterTest.php | 40 ++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c902ce4..08173f8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ preview workspace.xml # Vagrant +.bundle/ .vagrant/ # Vagrant sandbox site files. diff --git a/src/Transmission/Transmitter.php b/src/Transmission/Transmitter.php index b9b47af..2d2be36 100644 --- a/src/Transmission/Transmitter.php +++ b/src/Transmission/Transmitter.php @@ -98,7 +98,9 @@ class Transmitter implements TransmitterInterface private function outputBody(StreamInterface $body) { if ($this->chunkSize > 0) { - $body->rewind(); + if ($body->isSeekable()) { + $body->rewind(); + } while (!$body->eof()) { print $body->read($this->chunkSize); } diff --git a/test/tests/unit/Transmission/TransmitterTest.php b/test/tests/unit/Transmission/TransmitterTest.php index 3aecc21..4244775 100644 --- a/test/tests/unit/Transmission/TransmitterTest.php +++ b/test/tests/unit/Transmission/TransmitterTest.php @@ -135,6 +135,7 @@ class TransmitterTest extends \PHPUnit_Framework_TestCase $chunkSize = 3; $position = 0; + $this->body->isSeekable()->willReturn(true); $this->body->isReadable()->willReturn(true); $this->body->rewind()->willReturn(true); $this->body->eof()->willReturn(false); @@ -154,13 +155,50 @@ class TransmitterTest extends \PHPUnit_Framework_TestCase $transmitter->setChunkSize($chunkSize); ob_start(); - $transmitter->transmit($this->request->reveal(), $this->response->reveal(), $chunkSize); + $transmitter->transmit($this->request->reveal(), $this->response->reveal()); $captured = ob_get_contents(); ob_end_clean(); $this->assertEquals($content, $captured); } + /** + * @covers ::transmit + * @covers ::setChunkSize + * @covers ::outputBody + */ + public function testOutputsUnseekableStreamInChunks() + { + $content = "Hello, world!"; + $chunkSize = 3; + $position = 0; + + $this->body->isSeekable()->willReturn(false); + $this->body->isReadable()->willReturn(true); + $this->body->rewind()->willThrow(new \RuntimeException()); + $this->body->eof()->willReturn(false); + $this->body->read(Argument::any())->will( + function ($args) use ($content, &$position) { + $chunkSize = $args[0]; + $chunk = substr($content, $position, $chunkSize); + $position += $chunkSize; + if ($position >= strlen($content)) { + $this->eof()->willReturn(true); + } + return $chunk; + } + ); + + $transmitter = new Transmitter(); + $transmitter->setChunkSize($chunkSize); + + ob_start(); + $transmitter->transmit($this->request->reveal(), $this->response->reveal()); + $captured = ob_get_contents(); + ob_end_clean(); + + $this->assertEquals($content, $captured); + } // ------------------------------------------------------------------------ // Preparation