Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
103 - 1
<?php
2
namespace GuzzleHttp;
3
 
4
use GuzzleHttp\Promise\PromiseInterface;
5
use GuzzleHttp\Promise\RejectedPromise;
6
use GuzzleHttp\Psr7;
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
class RetryMiddleware
15
{
16
    /** @var callable  */
17
    private $nextHandler;
18
 
19
    /** @var callable */
20
    private $decider;
21
 
22
    /**
23
     * @param callable $decider     Function that accepts the number of retries,
24
     *                              a request, [response], and [exception] and
25
     *                              returns true if the request is to be
26
     *                              retried.
27
     * @param callable $nextHandler Next handler to invoke.
28
     * @param callable $delay       Function that accepts the number of retries
29
     *                              and [response] and returns the number of
30
     *                              milliseconds to delay.
31
     */
32
    public function __construct(
33
        callable $decider,
34
        callable $nextHandler,
35
        callable $delay = null
36
    ) {
37
        $this->decider = $decider;
38
        $this->nextHandler = $nextHandler;
39
        $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
40
    }
41
 
42
    /**
43
     * Default exponential backoff delay function.
44
     *
45
     * @param $retries
46
     *
47
     * @return int
48
     */
49
    public static function exponentialDelay($retries)
50
    {
51
        return (int) pow(2, $retries - 1);
52
    }
53
 
54
    /**
55
     * @param RequestInterface $request
56
     * @param array            $options
57
     *
58
     * @return PromiseInterface
59
     */
60
    public function __invoke(RequestInterface $request, array $options)
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
    private function onFulfilled(RequestInterface $req, array $options)
75
    {
76
        return function ($value) use ($req, $options) {
77
            if (!call_user_func(
78
                $this->decider,
79
                $options['retries'],
80
                $req,
81
                $value,
82
                null
83
            )) {
84
                return $value;
85
            }
86
            return $this->doRetry($req, $options, $value);
87
        };
88
    }
89
 
90
    private function onRejected(RequestInterface $req, array $options)
91
    {
92
        return function ($reason) use ($req, $options) {
93
            if (!call_user_func(
94
                $this->decider,
95
                $options['retries'],
96
                $req,
97
                null,
98
                $reason
99
            )) {
100
                return new RejectedPromise($reason);
101
            }
102
            return $this->doRetry($req, $options);
103
        };
104
    }
105
 
106
    private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
107
    {
108
        $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
109
 
110
        return $this($request, $options);
111
    }
112
}