Retry on any ConnectException (CURLE_COULDNT_CONNECT et. al.)
LeoniePhiline opened this issue · comments
Thank you for creating this very useful Middleware! ✨
Detailed description
GuzzleRetryMiddleware handles situations very nicely where requests connect successfully but the server is too busy to remember responding to you (e.g. https://httpstat.us/200?sleep=500000).
However, when requesting e.g. https://example.com:81 (as in https://stackoverflow.com/questions/100841/artificially-create-a-connection-timeout-error), cURL fails with an errno
of 7
(CURLE_COULDNT_CONNECT
) after the connect_timeout
has passed.
GuzzleRetryMiddleware only retries in cases of errno
being 28
(CURLE_OPERATION_TIMEOUTED
) and ignores errno
7
and other similar connection failures.
In fact, \GuzzleHttp\Handler\CurlFactory::createRejection()
checks for five error codes when deciding if to throw a \GuzzleHttp\Exception\ConnectException()
(otherwise throwing a \GuzzleHttp\Exception\RequestException
):
static $connectionErrors = [
CURLE_OPERATION_TIMEOUTED => true,
CURLE_COULDNT_RESOLVE_HOST => true,
CURLE_COULDNT_CONNECT => true,
CURLE_SSL_CONNECT_ERROR => true,
CURLE_GOT_NOTHING => true,
];
Context
The behavior of GuzzleRetryMiddleware
is unexpected and quite unintuitive. :)
It does not seem obvious why one would only retry on CURLE_OPERATION_TIMEOUTED
if at least CURLE_COULDNT_CONNECT
is definitely a timeout error as well.
Furthermore, CURLE_COULDNT_RESOLVE_HOST
, CURLE_SSL_CONNECT_ERROR
and CURLE_GOT_NOTHING
could also be of a temporary nature, and retrying in these cases would make for a more reliable client.
Possible implementation
Probably \GuzzleRetry\GuzzleRetryMiddleware::shouldRetryConnectException()
should retry all and any \GuzzleHttp\Exception\ConnectException
(as long as retry is enabled and the maximum number of attempts has not yet been made).
Thus, since we already know we are dealing with a ConnectException
, the check for the cURL errno
in this method is unnecessary.
Remove the following errno
testing switch case:
// Test if this was a connection or response timeout exception
case isset($e->getHandlerContext()['errno']) && $e->getHandlerContext()['errno'] == 28
Return true
by default:
// No exit conditions met, so return true to retry.
default:
return true;