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}