diff --git a/src/Symfony/Component/HttpClient/Response/AsyncResponse.php b/src/Symfony/Component/HttpClient/Response/AsyncResponse.php index 69522f7e87..dc70ed8ae7 100644 --- a/src/Symfony/Component/HttpClient/Response/AsyncResponse.php +++ b/src/Symfony/Component/HttpClient/Response/AsyncResponse.php @@ -34,6 +34,7 @@ final class AsyncResponse implements ResponseInterface, StreamableInterface private $response; private $info = ['canceled' => false]; private $passthru; + private $lastYielded = false; /** * @param ?callable(ChunkInterface, AsyncContext): ?\Iterator $passthru @@ -255,7 +256,10 @@ final class AsyncResponse implements ResponseInterface, StreamableInterface } } - if (null === $chunk->getError() && !$chunk->isLast() && $r->response === $response && null !== $r->client) { + if (null === $chunk->getError() && $chunk->isLast()) { + $r->lastYielded = true; + } + if (null === $chunk->getError() && !$r->lastYielded && $r->response === $response && null !== $r->client) { throw new \LogicException('A chunk passthru must yield an "isLast()" chunk before ending a stream.'); } diff --git a/src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php b/src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php index 82b36e1871..66701a9f9d 100644 --- a/src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php +++ b/src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php @@ -230,4 +230,27 @@ class AsyncDecoratorTraitTest extends NativeHttpClientTest $this->assertSame(200, $response->getStatusCode()); } + + public function testRecurciveStream() + { + $client = new class(parent::getHttpClient(__FUNCTION__)) implements HttpClientInterface { + use AsyncDecoratorTrait; + + public function request(string $method, string $url, array $options = []): ResponseInterface + { + return new AsyncResponse($this->client, $method, $url, $options); + } + }; + + $response = $client->request('GET', 'http://localhost:8057/json'); + $content = ''; + foreach ($client->stream($response) as $chunk) { + $content .= $chunk->getContent(); + foreach ($client->stream($response) as $chunk) { + $content .= $chunk->getContent(); + } + } + + $this->assertSame('{"documents":[{"id":"\/json\/1"},{"id":"\/json\/2"},{"id":"\/json\/3"}]}', $content); + } }