1 | <?php |
||
34 | class GuzzleRetryMiddleware |
||
35 | { |
||
36 | // HTTP date format |
||
37 | const DATE_FORMAT = 'D, d M Y H:i:s T'; |
||
38 | |||
39 | /** |
||
40 | * @var array |
||
41 | */ |
||
42 | private $defaultOptions = [ |
||
43 | |||
44 | // Retry enabled. Toggle retry on or off per request |
||
45 | 'retry_enabled' => true, |
||
46 | |||
47 | // If server doesn't provide a Retry-After header, then set a default back-off delay |
||
48 | // NOTE: This can either be a float, or it can be a callable that returns a (accepts count and response|null) |
||
49 | 'default_retry_multiplier' => 1.5, |
||
50 | |||
51 | // Set a maximum number of attempts per request |
||
52 | 'max_retry_attempts' => 10, |
||
53 | |||
54 | // Set this to TRUE to retry only if the HTTP Retry-After header is specified |
||
55 | 'retry_only_if_retry_after_header' => false, |
||
56 | |||
57 | // Only retry when status is equal to these response codes |
||
58 | 'retry_on_status' => ['429', '503'], |
||
59 | |||
60 | // Callback to trigger when delay occurs (accepts count, delay, request, response, options) |
||
61 | 'on_retry_callback' => null, |
||
62 | |||
63 | // Retry on connect timeout? |
||
64 | 'retry_on_timeout' => false |
||
65 | ]; |
||
66 | |||
67 | /** |
||
68 | * @var callable |
||
69 | */ |
||
70 | private $nextHandler; |
||
71 | |||
72 | /** |
||
73 | * Provides a closure that can be pushed onto the handler stack |
||
74 | * |
||
75 | * Example: |
||
76 | * <code>$handlerStack->push(GuzzleRetryMiddleware::factory());</code> |
||
77 | * |
||
78 | * @param array $defaultOptions |
||
79 | * @return \Closure |
||
80 | */ |
||
81 | 60 | public static function factory(array $defaultOptions = []) |
|
87 | |||
88 | /** |
||
89 | * GuzzleRetryMiddleware constructor. |
||
90 | * |
||
91 | * @param callable $nextHandler |
||
92 | * @param array $defaultOptions |
||
93 | */ |
||
94 | 63 | public function __construct(callable $nextHandler, array $defaultOptions = []) |
|
99 | |||
100 | /** |
||
101 | * @param RequestInterface $request |
||
102 | * @param array $options |
||
103 | * @return Promise |
||
104 | */ |
||
105 | 60 | public function __invoke(RequestInterface $request, array $options) |
|
123 | |||
124 | /** |
||
125 | * No exceptions were thrown during processing |
||
126 | * |
||
127 | * Depending on where this middleware is in the stack, the response could still |
||
128 | * be unsuccessful (e.g. 429 or 503), so check to see if it should be retried |
||
129 | * |
||
130 | * @param RequestInterface $request |
||
131 | * @param array $options |
||
132 | * @return callable |
||
133 | */ |
||
134 | 60 | protected function onFulfilled(RequestInterface $request, array $options) |
|
142 | |||
143 | /** |
||
144 | * An exception or error was thrown during processing |
||
145 | * |
||
146 | * If the reason is a BadResponseException exception, check to see if |
||
147 | * the request can be retried. Otherwise, pass it on. |
||
148 | * |
||
149 | * @param RequestInterface $request |
||
150 | * @param array $options |
||
151 | * @return callable |
||
152 | */ |
||
153 | protected function onRejected(RequestInterface $request, array $options) |
||
173 | |||
174 | 12 | protected function shouldRetryConnectException(ConnectException $e, array $options) |
|
193 | |||
194 | /** |
||
195 | * Check to see if a request can be retried |
||
196 | * |
||
197 | * This checks two things: |
||
198 | * |
||
199 | * 1. The response status code against the status codes that should be retried |
||
200 | * 2. The number of attempts made thus far for this request |
||
201 | * |
||
202 | * @param array $options |
||
203 | * @param ResponseInterface|null $response |
||
204 | * @return bool TRUE if the response should be retried, FALSE if not |
||
205 | */ |
||
206 | 48 | protected function shouldRetryHttpResponse(array $options, ResponseInterface $response) |
|
227 | |||
228 | /** |
||
229 | * Count the number of retries remaining. Always returns 0 or greater. |
||
230 | * @param array $options |
||
231 | * @return int |
||
232 | */ |
||
233 | 57 | protected function countRemainingRetries(array $options) |
|
243 | |||
244 | /** |
||
245 | * Retry the request |
||
246 | * |
||
247 | * Increments the retry count, determines the delay (timeout), executes callbacks, sleeps, and re-send the request |
||
248 | * |
||
249 | * @param RequestInterface $request |
||
250 | * @param array $options |
||
251 | * @param ResponseInterface|null $response |
||
252 | * @return |
||
253 | */ |
||
254 | 45 | protected function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null) |
|
280 | |||
281 | /** |
||
282 | * Determine the delay timeout |
||
283 | * |
||
284 | * Attempts to read and interpret the HTTP `Retry-After` header, or defaults |
||
285 | * to a built-in incremental back-off algorithm. |
||
286 | * |
||
287 | * @param ResponseInterface $response |
||
288 | * @param array $options |
||
289 | * @return float Delay timeout, in seconds |
||
290 | */ |
||
291 | 45 | protected function determineDelayTimeout(array $options, ResponseInterface $response = null) |
|
314 | |||
315 | /** |
||
316 | * Attempt to derive the timeout from the HTTP `Retry-After` header |
||
317 | * |
||
318 | * The spec allows the header value to either be a number of seconds or a datetime. |
||
319 | * |
||
320 | * @param string $headerValue |
||
321 | * @return float|null The number of seconds to wait, or NULL if unsuccessful (invalid header) |
||
322 | */ |
||
323 | 9 | protected function deriveTimeoutFromHeader($headerValue) |
|
335 | } |
||
336 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: