Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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