Closed
Description
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