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 | // If server doesn't provide a Retry-After header, then set a default back-off delay |
||
45 | 'default_retry_multiplier' => 1.5, |
||
46 | |||
47 | // Set a maximum number of attempts per request |
||
48 | 'max_retry_attempts' => 10, |
||
49 | |||
50 | // Set this to TRUE to retry only if the HTTP Retry-After header is specified |
||
51 | 'retry_only_if_retry_after_header' => false, |
||
52 | |||
53 | // Only retry when status is equal to these response codes |
||
54 | 'retry_on_status' => ['429', '503'], |
||
55 | |||
56 | // Callback to trigger when delay occurs (accepts count, delay, request, response, options) |
||
57 | 'on_retry_callback' => null, |
||
58 | |||
59 | // Retry on connect timeout? |
||
60 | 'retry_on_timeout' => false |
||
61 | ]; |
||
62 | |||
63 | /** |
||
64 | * @var callable |
||
65 | */ |
||
66 | private $nextHandler; |
||
67 | |||
68 | /** |
||
69 | * Provides a closure that can be pushed onto the handler stack |
||
70 | * |
||
71 | * Example: |
||
72 | * <code>$handlerStack->push(GuzzleRetryMiddleware::factory());</code> |
||
73 | * |
||
74 | * @param array $defaultOptions |
||
75 | * @return \Closure |
||
76 | */ |
||
77 | 54 | public static function factory(array $defaultOptions = []) |
|
83 | |||
84 | /** |
||
85 | * GuzzleRetryMiddleware constructor. |
||
86 | * |
||
87 | * @param callable $nextHandler |
||
88 | * @param array $defaultOptions |
||
89 | */ |
||
90 | 57 | public function __construct(callable $nextHandler, array $defaultOptions = []) |
|
95 | |||
96 | /** |
||
97 | * @param RequestInterface $request |
||
98 | * @param array $options |
||
99 | */ |
||
100 | 54 | public function __invoke(RequestInterface $request, array $options) |
|
118 | |||
119 | /** |
||
120 | * No exceptions were thrown during processing |
||
121 | * |
||
122 | * Depending on where this middleware is in the stack, the response could still |
||
123 | * be unsuccessful (e.g. 429 or 503), so check to see if it should be retried |
||
124 | * |
||
125 | * @param RequestInterface $request |
||
126 | * @param array $options |
||
127 | * @return callable |
||
128 | */ |
||
129 | 54 | private function onFulfilled(RequestInterface $request, array $options) |
|
137 | |||
138 | /** |
||
139 | * An exception or error was thrown during processing |
||
140 | * |
||
141 | * If the reason is a BadResponseException exception, check to see if |
||
142 | * the request can be retried. Otherwise, pass it on. |
||
143 | * |
||
144 | * @param RequestInterface $request |
||
145 | * @param array $options |
||
146 | * @return callable |
||
147 | */ |
||
148 | private function onRejected(RequestInterface $request, array $options) |
||
172 | |||
173 | 12 | private function shouldRetryConnectException(ConnectException $e, array $options) |
|
188 | |||
189 | /** |
||
190 | * Check to see if a request can be retried |
||
191 | * |
||
192 | * This checks two things: |
||
193 | * |
||
194 | * 1. The response status code against the status codes that should be retried |
||
195 | * 2. The number of attempts made thus far for this request |
||
196 | * |
||
197 | * @param array $options |
||
198 | * @param ResponseInterface|null $response |
||
199 | * @return bool TRUE if the response should be retried, FALSE if not |
||
200 | */ |
||
201 | 42 | private function shouldRetryHttpResponse(array $options, ResponseInterface $response) |
|
218 | |||
219 | /** |
||
220 | * Count the number of retries remaining. Always returns 0 or greater. |
||
221 | * @param array $options |
||
222 | * @return int |
||
223 | */ |
||
224 | 51 | private function countRemainingRetries(array $options) |
|
235 | |||
236 | /** |
||
237 | * Retry the request |
||
238 | * |
||
239 | * Increments the retry count, determines the delay (timeout), executes callbacks, sleeps, and re-send the request |
||
240 | * |
||
241 | * @param RequestInterface $request |
||
242 | * @param array $options |
||
243 | * @param ResponseInterface|null $response |
||
244 | * @return |
||
245 | */ |
||
246 | 39 | protected function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null) |
|
272 | |||
273 | /** |
||
274 | * Determine the delay timeout |
||
275 | * |
||
276 | * Attempts to read and interpret the HTTP `Retry-After` header, or defaults |
||
277 | * to a built-in incremental back-off algorithm. |
||
278 | * |
||
279 | * @param ResponseInterface $response |
||
280 | * @param array $options |
||
281 | * @return float Delay timeout, in seconds |
||
282 | */ |
||
283 | 39 | protected function determineDelayTimeout(array $options, ResponseInterface $response = null) |
|
297 | |||
298 | /** |
||
299 | * Attempt to derive the timeout from the HTTP `Retry-After` header |
||
300 | * |
||
301 | * The spec allows the header value to either be a number of seconds or a datetime. |
||
302 | * |
||
303 | * @param string $headerValue |
||
304 | * @return float|null The number of seconds to wait, or NULL if unsuccessful (invalid header) |
||
305 | */ |
||
306 | 9 | private function deriveTimeoutFromHeader($headerValue) |
|
318 | } |
||
319 |
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: