Chris@0
|
1 <?php
|
Chris@0
|
2 namespace GuzzleHttp;
|
Chris@0
|
3
|
Chris@0
|
4 use GuzzleHttp\Promise\PromiseInterface;
|
Chris@0
|
5 use GuzzleHttp\Promise\RejectedPromise;
|
Chris@0
|
6 use GuzzleHttp\Psr7;
|
Chris@0
|
7 use Psr\Http\Message\RequestInterface;
|
Chris@0
|
8 use Psr\Http\Message\ResponseInterface;
|
Chris@0
|
9
|
Chris@0
|
10 /**
|
Chris@0
|
11 * Middleware that retries requests based on the boolean result of
|
Chris@0
|
12 * invoking the provided "decider" function.
|
Chris@0
|
13 */
|
Chris@0
|
14 class RetryMiddleware
|
Chris@0
|
15 {
|
Chris@0
|
16 /** @var callable */
|
Chris@0
|
17 private $nextHandler;
|
Chris@0
|
18
|
Chris@0
|
19 /** @var callable */
|
Chris@0
|
20 private $decider;
|
Chris@0
|
21
|
Chris@0
|
22 /**
|
Chris@0
|
23 * @param callable $decider Function that accepts the number of retries,
|
Chris@0
|
24 * a request, [response], and [exception] and
|
Chris@0
|
25 * returns true if the request is to be
|
Chris@0
|
26 * retried.
|
Chris@0
|
27 * @param callable $nextHandler Next handler to invoke.
|
Chris@0
|
28 * @param callable $delay Function that accepts the number of retries
|
Chris@0
|
29 * and [response] and returns the number of
|
Chris@0
|
30 * milliseconds to delay.
|
Chris@0
|
31 */
|
Chris@0
|
32 public function __construct(
|
Chris@0
|
33 callable $decider,
|
Chris@0
|
34 callable $nextHandler,
|
Chris@0
|
35 callable $delay = null
|
Chris@0
|
36 ) {
|
Chris@0
|
37 $this->decider = $decider;
|
Chris@0
|
38 $this->nextHandler = $nextHandler;
|
Chris@0
|
39 $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
|
Chris@0
|
40 }
|
Chris@0
|
41
|
Chris@0
|
42 /**
|
Chris@0
|
43 * Default exponential backoff delay function.
|
Chris@0
|
44 *
|
Chris@0
|
45 * @param $retries
|
Chris@0
|
46 *
|
Chris@0
|
47 * @return int
|
Chris@0
|
48 */
|
Chris@0
|
49 public static function exponentialDelay($retries)
|
Chris@0
|
50 {
|
Chris@0
|
51 return (int) pow(2, $retries - 1);
|
Chris@0
|
52 }
|
Chris@0
|
53
|
Chris@0
|
54 /**
|
Chris@0
|
55 * @param RequestInterface $request
|
Chris@0
|
56 * @param array $options
|
Chris@0
|
57 *
|
Chris@0
|
58 * @return PromiseInterface
|
Chris@0
|
59 */
|
Chris@0
|
60 public function __invoke(RequestInterface $request, array $options)
|
Chris@0
|
61 {
|
Chris@0
|
62 if (!isset($options['retries'])) {
|
Chris@0
|
63 $options['retries'] = 0;
|
Chris@0
|
64 }
|
Chris@0
|
65
|
Chris@0
|
66 $fn = $this->nextHandler;
|
Chris@0
|
67 return $fn($request, $options)
|
Chris@0
|
68 ->then(
|
Chris@0
|
69 $this->onFulfilled($request, $options),
|
Chris@0
|
70 $this->onRejected($request, $options)
|
Chris@0
|
71 );
|
Chris@0
|
72 }
|
Chris@0
|
73
|
Chris@0
|
74 private function onFulfilled(RequestInterface $req, array $options)
|
Chris@0
|
75 {
|
Chris@0
|
76 return function ($value) use ($req, $options) {
|
Chris@0
|
77 if (!call_user_func(
|
Chris@0
|
78 $this->decider,
|
Chris@0
|
79 $options['retries'],
|
Chris@0
|
80 $req,
|
Chris@0
|
81 $value,
|
Chris@0
|
82 null
|
Chris@0
|
83 )) {
|
Chris@0
|
84 return $value;
|
Chris@0
|
85 }
|
Chris@0
|
86 return $this->doRetry($req, $options, $value);
|
Chris@0
|
87 };
|
Chris@0
|
88 }
|
Chris@0
|
89
|
Chris@0
|
90 private function onRejected(RequestInterface $req, array $options)
|
Chris@0
|
91 {
|
Chris@0
|
92 return function ($reason) use ($req, $options) {
|
Chris@0
|
93 if (!call_user_func(
|
Chris@0
|
94 $this->decider,
|
Chris@0
|
95 $options['retries'],
|
Chris@0
|
96 $req,
|
Chris@0
|
97 null,
|
Chris@0
|
98 $reason
|
Chris@0
|
99 )) {
|
Chris@0
|
100 return \GuzzleHttp\Promise\rejection_for($reason);
|
Chris@0
|
101 }
|
Chris@0
|
102 return $this->doRetry($req, $options);
|
Chris@0
|
103 };
|
Chris@0
|
104 }
|
Chris@0
|
105
|
Chris@0
|
106 private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
|
Chris@0
|
107 {
|
Chris@0
|
108 $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
|
Chris@0
|
109
|
Chris@0
|
110 return $this($request, $options);
|
Chris@0
|
111 }
|
Chris@0
|
112 }
|