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