Description
Symfony version(s) affected
symfony/http-client:7.2.0
Description
When upgrading library symfony/http-client
from 7.1.9 to 7.2.0, the RetryableHttpClient
using CurlHttpClient
and GenericRetryStrategy
won't work. (v7.2.0-RC1 works fine)
It produces an Exception on second http call (first retry http call) with:
PHP Fatal error: Uncaught Symfony\Component\HttpClient\Exception\TransportException: Idle timeout reached for "http://localhost:8098/test/500". in /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/AsyncResponse.php:70
Stack trace:
#0 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/CommonResponseTrait.php(144): Symfony\Component\HttpClient\Response\AsyncResponse::Symfony\Component\HttpClient\Response\{closure}(Object(Symfony\Component\HttpClient\Response\AsyncResponse), -0.0)
#1 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/AsyncResponse.php(98): Symfony\Component\HttpClient\Response\AsyncResponse::initialize(Object(Symfony\Component\HttpClient\Response\AsyncResponse))
#2 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Internal/HttplugWaitLoop.php(114): Symfony\Component\HttpClient\Response\AsyncResponse->getStatusCode()
#3 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Psr18Client.php(112): Symfony\Component\HttpClient\Internal\HttplugWaitLoop::createPsr7Response(Object(Nyholm\Psr7\Factory\Psr17Factory), Object(Nyholm\Psr7\Factory\Psr17Factory), Object(Symfony\Component\HttpClient\RetryableHttpClient), Object(Symfony\Component\HttpClient\Response\AsyncResponse), false)
#4 /nt/my_projects/symfony-http-client-issue/bin/http_client.php(34): Symfony\Component\HttpClient\Psr18Client->sendRequest(Object(Nyholm\Psr7\Request))
#5 {main}
Next Symfony\Component\HttpClient\Exception\TransportException: Idle timeout reached for "http://localhost:8098/test/500". in /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Chunk/ErrorChunk.php:55
Stack trace:
#0 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/AsyncResponse.php(71): Symfony\Component\HttpClient\Chunk\ErrorChunk->isFirst()
#1 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/CommonResponseTrait.php(144): Symfony\Component\HttpClient\Response\AsyncResponse::Symfony\Component\HttpClient\Response\{closure}(Object(Symfony\Component\HttpClient\Response\AsyncResponse), -0.0)
#2 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/AsyncResponse.php(98): Symfony\Component\HttpClient\Response\AsyncResponse::initialize(Object(Symfony\Component\HttpClient\Response\AsyncResponse))
#3 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Internal/HttplugWaitLoop.php(114): Symfony\Component\HttpClient\Response\AsyncResponse->getStatusCode()
#4 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Psr18Client.php(112): Symfony\Component\HttpClient\Internal\HttplugWaitLoop::createPsr7Response(Object(Nyholm\Psr7\Factory\Psr17Factory), Object(Nyholm\Psr7\Factory\Psr17Factory), Object(Symfony\Component\HttpClient\RetryableHttpClient), Object(Symfony\Component\HttpClient\Response\AsyncResponse), false)
#5 /nt/my_projects/symfony-http-client-issue/bin/http_client.php(34): Symfony\Component\HttpClient\Psr18Client->sendRequest(Object(Nyholm\Psr7\Request))
#6 {main}
Next Symfony\Component\HttpClient\Psr18NetworkException: Idle timeout reached for "http://localhost:8098/test/500". in /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Psr18Client.php:118
Stack trace:
#0 /nt/my_projects/symfony-http-client-issue/bin/http_client.php(34): Symfony\Component\HttpClient\Psr18Client->sendRequest(Object(Nyholm\Psr7\Request))
#1 {main}
thrown in /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Psr18Client.php on line 118
Fatal error: Uncaught Symfony\Component\HttpClient\Exception\TransportException: Idle timeout reached for "http://localhost:8098/test/500". in /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/AsyncResponse.php:70
Stack trace:
#0 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/CommonResponseTrait.php(144): Symfony\Component\HttpClient\Response\AsyncResponse::Symfony\Component\HttpClient\Response\{closure}(Object(Symfony\Component\HttpClient\Response\AsyncResponse), -0.0)
#1 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/AsyncResponse.php(98): Symfony\Component\HttpClient\Response\AsyncResponse::initialize(Object(Symfony\Component\HttpClient\Response\AsyncResponse))
#2 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Internal/HttplugWaitLoop.php(114): Symfony\Component\HttpClient\Response\AsyncResponse->getStatusCode()
#3 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Psr18Client.php(112): Symfony\Component\HttpClient\Internal\HttplugWaitLoop::createPsr7Response(Object(Nyholm\Psr7\Factory\Psr17Factory), Object(Nyholm\Psr7\Factory\Psr17Factory), Object(Symfony\Component\HttpClient\RetryableHttpClient), Object(Symfony\Component\HttpClient\Response\AsyncResponse), false)
#4 /nt/my_projects/symfony-http-client-issue/bin/http_client.php(34): Symfony\Component\HttpClient\Psr18Client->sendRequest(Object(Nyholm\Psr7\Request))
#5 {main}
Next Symfony\Component\HttpClient\Exception\TransportException: Idle timeout reached for "http://localhost:8098/test/500". in /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Chunk/ErrorChunk.php:55
Stack trace:
#0 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/AsyncResponse.php(71): Symfony\Component\HttpClient\Chunk\ErrorChunk->isFirst()
#1 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/CommonResponseTrait.php(144): Symfony\Component\HttpClient\Response\AsyncResponse::Symfony\Component\HttpClient\Response\{closure}(Object(Symfony\Component\HttpClient\Response\AsyncResponse), -0.0)
#2 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Response/AsyncResponse.php(98): Symfony\Component\HttpClient\Response\AsyncResponse::initialize(Object(Symfony\Component\HttpClient\Response\AsyncResponse))
#3 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Internal/HttplugWaitLoop.php(114): Symfony\Component\HttpClient\Response\AsyncResponse->getStatusCode()
#4 /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Psr18Client.php(112): Symfony\Component\HttpClient\Internal\HttplugWaitLoop::createPsr7Response(Object(Nyholm\Psr7\Factory\Psr17Factory), Object(Nyholm\Psr7\Factory\Psr17Factory), Object(Symfony\Component\HttpClient\RetryableHttpClient), Object(Symfony\Component\HttpClient\Response\AsyncResponse), false)
#5 /nt/my_projects/symfony-http-client-issue/bin/http_client.php(34): Symfony\Component\HttpClient\Psr18Client->sendRequest(Object(Nyholm\Psr7\Request))
#6 {main}
Next Symfony\Component\HttpClient\Psr18NetworkException: Idle timeout reached for "http://localhost:8098/test/500". in /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Psr18Client.php:118
Stack trace:
#0 /nt/my_projects/symfony-http-client-issue/bin/http_client.php(34): Symfony\Component\HttpClient\Psr18Client->sendRequest(Object(Nyholm\Psr7\Request))
#1 {main}
thrown in /nt/my_projects/symfony-http-client-issue/vendor/symfony/http-client/Psr18Client.php on line 118
Looks like an issue with reading response body, from change in Psr18Client.php.
From
'body' => $body->getContents(),
to:
'body' => static fn (int $size) => $body->read($size),
because when I change this directly in vendor library it works fine.
How to reproduce
I created an example here.
To reproduce execute:
composer install
Then on one terminal run:
php -S localhost:8098 -t public/
And the on other terminal:
bin/http_client.php
Which will produce an error like described.
If you downgrade symfony/http-client
to 7.1.9, this error will be gone and retry will work fine.
From repo ⬆
Important files:
bin/http_client.php
#!/usr/bin/env php
<?php
declare(strict_types=1);
use Nyholm\Psr7\Factory\Psr17Factory;
use Symfony\Component\HttpClient\CurlHttpClient;
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Component\HttpClient\Retry\GenericRetryStrategy;
use Symfony\Component\HttpClient\RetryableHttpClient;
require_once __DIR__ . '/../vendor/autoload.php';
set_time_limit(0);
ini_set('display_errors', '1');
$client = new Psr18Client(
new RetryableHttpClient(
new CurlHttpClient(),
new GenericRetryStrategy(
[500], // <- retrying HTTP Status Code 500
100
),
2 // <- maxRetries
),
new Psr17Factory()
);
$uri = 'http://localhost:8098/test/500';
$request = $client->createRequest('POST', $uri);
$request->getBody()->write(json_encode(['test']));
$response = $client->sendRequest(
$request
->withHeader('Accept', 'application/json')
->withAddedHeader('Content-Type', 'application/json')
);
dump($response->getStatusCode());
dump((string) $response->getBody());
and
public/index.php
<?php
declare(strict_types=1);
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require_once __DIR__ . '/../vendor/autoload.php';
set_time_limit(0);
ini_set('display_errors', '1');
$app = AppFactory::create();
$app->addErrorMiddleware(true, true, true);
// Add routes
$app->get('/', function (Request $request, Response $response) {
$response->getBody()->write((string) json_encode(['OK']));
return $response;
});
$app->post('/test/500', function (Request $request, Response $response, array $args) {
$response = $response->withStatus(500, 'Server error');
$response->getBody()->write((string) json_encode(['Error']));
return $response;
});
$app->run();
Possible Solution
No response
Additional Context
No response