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

Skip to content

[HttpClient] RetryableHttpClient messes timeout exception #52587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rmikalkenas opened this issue Nov 14, 2023 · 1 comment
Closed

[HttpClient] RetryableHttpClient messes timeout exception #52587

rmikalkenas opened this issue Nov 14, 2023 · 1 comment

Comments

@rmikalkenas
Copy link
Contributor

rmikalkenas commented Nov 14, 2023

Symfony version(s) affected

6.3.8

Description

Looks like Symfony's RetryableHttpClient messes up exceptions in case of a timeout.
As showed at the reproducer if request is sent using "GET" method, client throws TimeoutException - which is correct.
But as soon as you change "GET" with "POST", exception class changes to TransportException, even though both exceptions messages are same.
If such behaviour is expected - then how to correctly identify a timeout without weirdly parsing and matching exception message?

Reproduced with php 8.2 and symfony 6.3.8

cc @jderusse maybe you will have some insights, since retryable client is written by you

How to reproduce

$client = new RetryableHttpClient(HttpClient::create());

try {
    dump('GET');
    $client->request('GET', 'https://httpbin.org/delay/5', ['timeout' => 1.0]);
} catch (\Symfony\Contracts\HttpClient\Exception\ExceptionInterface $e) {
    dump($e::class . '::' . $e->getMessage());
}

// GET
// Symfony\Component\HttpClient\Exception\TimeoutException::Idle timeout reached for "https://httpbin.org/delay/5".

try {
    dump('POST');
    $client->request('POST', 'https://httpbin.org/delay/5', ['timeout' => 1.0]);
} catch (\Symfony\Contracts\HttpClient\Exception\ExceptionInterface $e) {
    dump($e::class . '::' . $e->getMessage());
}

// POST
// Symfony\Component\HttpClient\Exception\TransportException::Idle timeout reached for "https://httpbin.org/delay/5".

Possible Solution

No response

Additional Context

No response

nicolas-grekas added a commit that referenced this issue Jan 26, 2024
…enas)

This PR was merged into the 5.4 branch.

Discussion
----------

[HttpClient] Fix error chunk creation in passthru

| Q             | A
| ------------- | ---
| Branch?       | 5.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | #52587
| License       | MIT

Timeout chunk should not be recreated as TransportException, because ErrorChunk losts information that it's timeout when passing thru.
Change solves issue mentioned at #52587

Commits
-------

5e6d218 [HttpClient] Fix error chunk creation in passthru
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Feb 14, 2024

I'm reopening, but in the light of #53889, I must say that this might be the expected behavior: timeouts thrown while requesting the status code or headers are not regular timeouts. They're unrecoverable transport errors. Semantically, timeout exceptions are meant to signal a recoverable transport error (one might just need to be more patient to get some activity back and thus recover from the timeout).

About GET vs POST behavior, this might also be the desired behavior: when a timeout is thrown, the remote server might still have processed the request, so that retrying might not be safe.

Of course, if the timeout happens before we sent the headers on our side, then we can safely retry, but we don't have this level of accuracy in the client. Maybe we could detect the situation by leveraging some "info" returned by curl and leverage this in RetryableHttpClient.

If that'd help, AsyncResponse could add the TimeoutException as the "previous" exception of TransportException.

For thoughts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants