8000 feature #50 Add support for streamed response (danizord) · symfony/symfony@c1cb51c · GitHub
[go: up one dir, main page]

Skip to content
10000

Commit c1cb51c

Browse files
feature #50 Add support for streamed response (danizord)
This PR was merged into the 1.2-dev branch. Discussion ---------- Add support for streamed response Here's a simple implementation of response streaming heavily inspired by Diactoros' response emitter, using HttpFoundation's `StreamedResponse`. It lacks support for `Content-Range`, but I can provide that in a future PR. Closes https://github.com/symfony/psr-http-message-bridge/issues/3 Commits ------- 7cc1605 Add support for streamed response
2 parents 4133c7a + 7cc1605 commit c1cb51c

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

Factory/HttpFoundationFactory.php

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313

1414
use Psr\Http\Message\ResponseInterface;
1515
use Psr\Http\Message\ServerRequestInterface;
16+
use Psr\Http\Message\StreamInterface;
1617
use Psr\Http\Message\UploadedFileInterface;
1718
use Psr\Http\Message\UriInterface;
1819
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
1920
use Symfony\Component\HttpFoundation\Cookie;
2021
use Symfony\Component\HttpFoundation\File\UploadedFile;
2122
use Symfony\Component\HttpFoundation\Request;
2223
use Symfony\Component\HttpFoundation\Response;
24+
use Symfony\Component\HttpFoundation\StreamedResponse;
2325

2426
/**
2527
* {@inheritdoc}
@@ -28,6 +30,16 @@
2830
*/
2931
class HttpFoundationFactory implements HttpFoundationFactoryInterface
3032
{
33+
/**
34+
* @var int The maximum output buffering size for each iteration when sending the response
35+
*/
36+
private $responseBufferMaxLength;
37+
38+
public function __construct(int $responseBufferMaxLength = 16372)
39+
{
40+
$this->responseBufferMaxLength = $responseBufferMaxLength;
41+
}
42+
3143
/**
3244
* {@inheritdoc}
3345
*/
@@ -138,16 +150,25 @@ protected function getTemporaryPath()
138150
/**
139151
* {@inheritdoc}
140152
*/
141-
public function createResponse(ResponseInterface $psrResponse)
153+
public function createResponse(ResponseInterface $psrResponse, bool $streamed = false)
142154
{
143155
$cookies = $psrResponse->getHeader('Set-Cookie');
144156
$psrResponse = $psrResponse->withoutHeader('Set-Cookie');
145157

146-
$response = new Response(
147-
$psrResponse->getBody()->__toString(),
148-
$psrResponse->getStatusCode(),
149-
$psrResponse->getHeaders()
150-
);
158+
if ($streamed) {
159+
$response = new StreamedResponse(
160+
$this->createStreamedResponseCallback($psrResponse->getBody()),
161+
$psrResponse->getStatusCode(),
162+
$psrResponse->getHeaders()
163+
);
164+
} else {
165+
$response = new Response(
166+
$psrResponse->getBody()->__toString(),
167+
$psrResponse->getStatusCode(),
168+
$psrResponse->getHeaders()
169+
);
170+
}
171+
151172
$response->setProtocolVersion($psrResponse->getProtocolVersion());
152173

153174
foreach ($cookies as $cookie) {
@@ -237,4 +258,23 @@ private function createCookie($cookie)
237258
isset($samesite) ? $samesite : null
238259
);
239260
}
261+
262+
private function createStreamedResponseCallback(StreamInterface $body): callable
263+
{
264+
return function () use ($body) {
265+
if ($body->isSeekable()) {
266+
$body->rewind();
267+
}
268+
269+
if (!$body->isReadable()) {
270+
echo $body;
271+
272+
return;
273+
}
274+
275+
while (!$body->eof()) {
276+
echo $body->read($this->responseBufferMaxLength);
277+
}
278+
};
279+
}
240280
}

Tests/Factory/HttpFoundationFactoryTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,5 +237,14 @@ public function testCreateResponse()
237237

238238
$this->assertEquals('The response body', $symfonyResponse->getContent());
239239
$this->assertEquals(200, $symfonyResponse->getStatusCode());
240+
241+
$symfonyResponse = $this->factory->createResponse($response, true);
242+
243+
ob_start();
244+
$symfonyResponse->sendContent();
245+
$sentContent = ob_get_clean();
246+
247+
$this->assertEquals('The response body', $sentContent);
248+
$this->assertEquals(200, $symfonyResponse->getStatusCode());
240249
}
241250
}

Tests/Fixtures/Stream.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
class Stream implements StreamInterface
2020
{
2121
private $stringContent;
22+
private $eof = true;
2223

2324
public function __construct($stringContent = '')
2425
{
@@ -49,12 +50,12 @@ public function tell()
4950

5051
public function eof()
5152
{
52-
return true;
53+
return $this->eof;
5354
}
5455

5556
public function isSeekable()
5657
{
57-
return false;
58+
return true;
5859
}
5960

6061
public function seek($offset, $whence = SEEK_SET)
@@ -63,6 +64,7 @@ public function seek($offset, $whence = SEEK_SET)
6364

6465
public function rewind()
6566
{
67+
$this->eof = false;
6668
}
6769

6870
public function isWritable()
@@ -81,6 +83,8 @@ public function isReadable()
8183

8284
public function read($length)
8385
{
86+
$this->eof = true;
87+
8488
return $this->stringContent;
8589
}
8690

0 commit comments

Comments
 (0)
0