Skip to content

http-client retry issue #59629

Closed
Closed
@icarus94

Description

@icarus94

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions