From 84cda3f02b3f3c8f91e0b5acb7ba703f69e59e85 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 11 Oct 2020 17:05:16 +0200 Subject: [PATCH] [HttpClient] fix decorating timeout errors --- .../HttpClient/Response/AsyncResponse.php | 20 ++++++++++++++++++- .../Tests/AsyncDecoratorTraitTest.php | 18 +++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/Response/AsyncResponse.php b/src/Symfony/Component/HttpClient/Response/AsyncResponse.php index ee9b732681..69522f7e87 100644 --- a/src/Symfony/Component/HttpClient/Response/AsyncResponse.php +++ b/src/Symfony/Component/HttpClient/Response/AsyncResponse.php @@ -45,7 +45,25 @@ final class AsyncResponse implements ResponseInterface, StreamableInterface $this->response = $client->request($method, $url, ['buffer' => false] + $options); $this->passthru = $passthru; $this->initializer = static function (self $response) { - return null !== $response->shouldBuffer; + if (null === $response->shouldBuffer) { + return false; + } + + foreach (self::stream([$response]) as $chunk) { + if ($chunk->isTimeout() && $response->passthru) { + foreach (self::passthru($response->client, $response, new ErrorChunk($response->offset, new TransportException($chunk->getError()))) as $chunk) { + return !$chunk->isFirst(); + } + + return true; + } + + if ($chunk->isFirst()) { + break; + } + } + + return false; }; if (\array_key_exists('user_data', $options)) { $this->info['user_data'] = $options['user_data']; diff --git a/src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php b/src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php index 363d078ea5..82b36e1871 100644 --- a/src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php +++ b/src/Symfony/Component/HttpClient/Tests/AsyncDecoratorTraitTest.php @@ -212,4 +212,22 @@ class AsyncDecoratorTraitTest extends NativeHttpClientTest $this->assertStringContainsString('SERVER_PROTOCOL', $response->getContent()); $this->assertStringContainsString('HTTP_HOST', $response->getContent()); } + + public function testRetryTimeout() + { + $client = $this->getHttpClient(__FUNCTION__, function (ChunkInterface $chunk, AsyncContext $context) { + try { + $this->assertTrue($chunk->isTimeout()); + yield $chunk; + } catch (TransportExceptionInterface $e) { + $context->passthru(); + $context->getResponse()->cancel(); + $context->replaceRequest('GET', 'http://localhost:8057/timeout-header', ['timeout' => 1]); + } + }); + + $response = $client->request('GET', 'http://localhost:8057/timeout-header', ['timeout' => 0.1]); + + $this->assertSame(200, $response->getStatusCode()); + } }