Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[HttpClient] LogicException thrown while streaming redirected requests with RetryableHttpClient #43390

Closed
@Seldaek

Description

@Seldaek

Symfony version(s) affected: 5.3.4

Description
LogicException sometimes thrown in HttpClient when combined with RetryableHttpClient and redirected URLs

How to reproduce

<?php

include 'vendor/autoload.php';

ini_set('error_reporting', -1);
ini_set('display_errors', true);

$client = Symfony\Component\HttpClient\HttpClient::create();
$client = new Symfony\Component\HttpClient\RetryableHttpClient($client); // REMOVING THIS LINE FIXES THE ISSUES
$client = new Symfony\Component\HttpClient\TraceableHttpClient($client);
$client = new Symfony\Component\HttpClient\NoPrivateNetworkHttpClient($client);

$client = $client->withOptions([
    'max_duration' => 60,
    'timeout' => 0.4, // MAY NEED TO PLAY WITH TIMEOUT A BIT TO FIND A VALUE TRIGGERING THE ISSUE, DEPENDS ON CONN SPEED
]);

$resp[] = $client->request('GET', 'http://api.github.com/repos/symfony/symfony');

foreach ($client->stream($resp) as $response => $chunk) {
    if ($chunk->isTimeout()) {
        $response->cancel(); // THIS HERE TRIGGERS THE ERROR
        var_dump('RESP CANCELLED');
    } elseif ($chunk->isFirst()) {
        if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
            $response->cancel();
            var_dump('RESP FAILED');
        }
    } elseif ($chunk->isLast()) {
        if (!$response->getInfo('canceled')) {
            var_dump('RESP SUCCESS');
        }
    }
}

var_dump('END');

I got this trace from a real application too, not from the above test case:

  "exception" => LogicException {
    #message: "A chunk passthru must yield instances of "Symfony\Contracts\HttpClient\ChunkInterface", "null" yielded."
    #code: 0
    #file: "./vendor/symfony/http-client/Response/AsyncResponse.php"
    #line: 360
    trace: {
      ./vendor/symfony/http-client/Response/AsyncResponse.php:360 { …}
      ./vendor/symfony/http-client/Response/AsyncResponse.php:331 { …}
      ./vendor/symfony/http-client/Response/AsyncResponse.php:166 { …}
      ./vendor/symfony/http-client/Response/TraceableResponse.php:131 { …}
      ./src/Command/FeedImportsCommand.php:138 {
        ›     $response->cancel(); <===
      }
      ./src/Command/FeedImportsCommand.php:83 { …}
      ./vendor/symfony/console/Command/Command.php:299 { …}
      ./vendor/symfony/console/Application.php:996 { …}
      ./vendor/symfony/framework-bundle/Console/Application.php:96 { …}
      ./vendor/symfony/console/Application.php:295 { …}
      ./vendor/symfony/framework-bundle/Console/Application.php:82 { …}
      ./vendor/symfony/console/Application.php:167 { …}
      ./bin/console:41 { …}
    }
  }

The issue seems to occur only with a combination of factors:

  • The RetryableHttpClient is used
  • The URL redirects (http to https, but probably any redirect does)
  • The redirected URL then responds too slow for the defined timeout (testing it with github API isn't so easy as you have to pick a correct timeout big enough that allows the HTTPS redirect to respond, but small enough that the real response then times out)
  • $response->cancel() is called

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