123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- <?php
- namespace 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;
- /** @var callable */
- private $delay;
- /**
- * @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 int $retries
- *
- * @return int milliseconds.
- */
- public static function exponentialDelay($retries)
- {
- return (int) pow(2, $retries - 1) * 1000;
- }
- /**
- * @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)
- );
- }
- /**
- * Execute fulfilled closure
- *
- * @return mixed
- */
- 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);
- };
- }
- /**
- * Execute rejected closure
- *
- * @return callable
- */
- 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);
- };
- }
- /**
- * @return self
- */
- private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
- {
- $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
- return $this($request, $options);
- }
- }
|