diff --git a/src/Io/Sender.php b/src/Io/Sender.php index 6b99b6f..7ad203a 100644 --- a/src/Io/Sender.php +++ b/src/Io/Sender.php @@ -129,7 +129,10 @@ public function send(RequestInterface $request) $body = new ChunkedEncoder($body); } + // pipe body into request stream + // add dummy write to immediately start request even if body does not emit any data yet $body->pipe($requestStream); + $requestStream->write(''); } else { // stream is not readable => end request without body $requestStream->end(); diff --git a/tests/FunctionalBrowserTest.php b/tests/FunctionalBrowserTest.php index fe24577..62d5c81 100644 --- a/tests/FunctionalBrowserTest.php +++ b/tests/FunctionalBrowserTest.php @@ -327,6 +327,25 @@ public function testPostStreamKnownLength() $this->assertEquals('hello world', $data['data']); } + /** + * @doesNotPerformAssertions + */ + public function testPostStreamWillStartSendingRequestEvenWhenBodyDoesNotEmitData() + { + $server = new StreamingServer(function (ServerRequestInterface $request) { + return new Response(200); + }); + $socket = new \React\Socket\Server(0, $this->loop); + $server->listen($socket); + + $this->base = str_replace('tcp:', 'http:', $socket->getAddress()) . '/'; + + $stream = new ThroughStream(); + Block\await($this->browser->post($this->base . 'post', array(), $stream), $this->loop); + + $socket->close(); + } + /** @group online */ public function testPostStreamClosed() { diff --git a/tests/Io/SenderTest.php b/tests/Io/SenderTest.php index 1deb0f2..ccc07b4 100644 --- a/tests/Io/SenderTest.php +++ b/tests/Io/SenderTest.php @@ -96,13 +96,16 @@ public function testSendPostWillAutomaticallySendContentLengthZeroHeaderForEmpty public function testSendPostStreamWillAutomaticallySendTransferEncodingChunked() { + $outgoing = $this->getMockBuilder('React\HttpClient\Request')->disableOriginalConstructor()->getMock(); + $outgoing->expects($this->once())->method('write')->with(""); + $client = $this->getMockBuilder('React\HttpClient\Client')->disableOriginalConstructor()->getMock(); $client->expects($this->once())->method('request')->with( 'POST', 'http://www.google.com/', array('Host' => 'www.google.com', 'Transfer-Encoding' => 'chunked'), '1.1' - )->willReturn($this->getMockBuilder('React\HttpClient\Request')->disableOriginalConstructor()->getMock()); + )->willReturn($outgoing); $sender = new Sender($client, $this->getMockBuilder('Clue\React\Buzz\Message\MessageFactory')->getMock()); @@ -115,7 +118,7 @@ public function testSendPostStreamWillAutomaticallyPipeChunkEncodeBodyForWriteAn { $outgoing = $this->getMockBuilder('React\HttpClient\Request')->disableOriginalConstructor()->getMock(); $outgoing->expects($this->once())->method('isWritable')->willReturn(true); - $outgoing->expects($this->once())->method('write')->with("5\r\nhello\r\n")->willReturn(false); + $outgoing->expects($this->exactly(2))->method('write')->withConsecutive(array(""), array("5\r\nhello\r\n"))->willReturn(false); $client = $this->getMockBuilder('React\HttpClient\Client')->disableOriginalConstructor()->getMock(); $client->expects($this->once())->method('request')->willReturn($outgoing); @@ -134,7 +137,7 @@ public function testSendPostStreamWillAutomaticallyPipeChunkEncodeBodyForEnd() { $outgoing = $this->getMockBuilder('React\HttpClient\Request')->disableOriginalConstructor()->getMock(); $outgoing->expects($this->once())->method('isWritable')->willReturn(true); - $outgoing->expects($this->once())->method('write')->with("0\r\n\r\n")->willReturn(false); + $outgoing->expects($this->exactly(2))->method('write')->withConsecutive(array(""), array("0\r\n\r\n"))->willReturn(false); $outgoing->expects($this->once())->method('end')->with(null); $client = $this->getMockBuilder('React\HttpClient\Client')->disableOriginalConstructor()->getMock();