Skip to content

Commit

Permalink
Include exception in on_retry_callback (fixes #38)
Browse files Browse the repository at this point in the history
  • Loading branch information
caseyamcl committed Jun 19, 2024
1 parent 8e2259d commit 3825dc9
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 8 deletions.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,25 @@ use Psr\Http\Message\ResponseInterface;
* @param RequestInterface $request Request
* @param array $options Guzzle request options
* @param ResponseInterface|null $response Response (or NULL if response not sent; e.g. connect timeout)
* @param Throwable|null $exception This value will be present if the retry was triggered by onRejected
* (e.g. in the event of a connection timeout)
*/
$listener = function(int $attemptNumber, float $delay, RequestInterface &$request, array &$options, ?ResponseInterface $response) {
$listener = function(
int $attemptNumber,
float $delay,
RequestInterface &$request,
array &$options,
?ResponseInterface $response,
?Throwable $exception
) {

echo sprintf(
"Retrying request to %s. Server responded with %s. Will wait %s seconds. This is attempt #%s",
"Retrying request to %s. Server responded with %s. Will wait %s seconds. This is attempt #%s. The error was %s",
$request->getUri()->getPath(),
$response->getStatusCode(),
number_format($delay, 2),
$attemptNumber
$attemptNumber,
$exception->getMessage()
);
}

Expand Down
16 changes: 11 additions & 5 deletions src/GuzzleRetryMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,13 @@ protected function onRejected(RequestInterface $request, array $options): callab
// If was bad response exception, test if we retry based on the response headers
if ($reason instanceof BadResponseException) {
if ($this->shouldRetryHttpResponse($options, $request, $reason->getResponse())) {
return $this->doRetry($request, $options, $reason->getResponse());
return $this->doRetry($request, $options, $reason->getResponse(), $reason);
}
// If this was a connection exception, test to see if we should retry based on connect timeout rules
} elseif ($reason instanceof ConnectException) {
// If was another type of exception, test if we should retry based on timeout rules
if ($this->shouldRetryConnectException($options, $request)) {
return $this->doRetry($request, $options);
return $this->doRetry($request, $options, null, $reason);
}
}

Expand Down Expand Up @@ -338,10 +338,15 @@ protected function countRemainingRetries(array $options): int
* @param RequestInterface $request
* @param array<string,mixed> $options
* @param ResponseInterface|null $response
* @param Throwable|null $exception If this was called due to a ConnectException, then that is included
* @return Promise
*/
protected function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null): Promise
{
protected function doRetry(
RequestInterface $request,
array $options,
ResponseInterface $response = null,
?Throwable $exception = null
): Promise {
// Increment the retry count
++$options['retry_count'];

Expand All @@ -357,7 +362,8 @@ protected function doRetry(RequestInterface $request, array $options, ResponseIn
$delayTimeout,
&$request,
&$options,
$response
$response,
$exception
]
);
}
Expand Down
29 changes: 29 additions & 0 deletions tests/GuzzleRetryMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,35 @@ public function testConnectTimeoutIsHandledWhenOptionIsSetToTrue(): void
$this->assertEquals(200, $response->getStatusCode());
}

public function testConnectTimeoutIncludesExceptionInCallbackHandler(): void
{
// Send connect timeout (cURL error 28) then a good response
$responses = [
new ConnectException(
'Connection timed out',
new Request('GET', '/'),
null,
['errno' => 28]
),
new Response(200, [], 'Good')
];

$excTest = null;

$stack = HandlerStack::create(new MockHandler($responses));
$stack->push(GuzzleRetryMiddleware::factory([
'retry_on_timeout' => true, // Enable connect timeout
'on_retry_callback' => function ($nr, $d, $req, $o, $resp, $exception) use (&$excTest) {
$excTest = $exception;
}
]));

$client = new Client(['handler' => $stack]);
$client->request('GET', '/');

$this->assertInstanceOf(\Throwable::class, $excTest);
}

public function testConnectTimeoutIsNotHandledWhenOptionIsSetToFalse(): void
{
// Send connect timeout (cURL error 28) then a good response
Expand Down

0 comments on commit 3825dc9

Please sign in to comment.