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