13
13
14
14
namespace RoachPHP \Downloader \Middleware ;
15
15
16
+ use InvalidArgumentException ;
16
17
use Psr \Log \LoggerInterface ;
17
18
use RoachPHP \Http \Response ;
18
19
use RoachPHP \Scheduling \RequestSchedulerInterface ;
@@ -31,54 +32,80 @@ public function __construct(
31
32
public function handleResponse (Response $ response ): Response
32
33
{
33
34
$ request = $ response ->getRequest ();
34
-
35
+
35
36
/** @var int $retryCount */
36
37
$ retryCount = $ request ->getMeta ('retry_count ' , 0 );
37
38
38
39
/** @var list<int> $retryOnStatus */
39
40
$ retryOnStatus = $ this ->option ('retryOnStatus ' );
40
-
41
+
41
42
/** @var int $maxRetries */
42
43
$ maxRetries = $ this ->option ('maxRetries ' );
43
44
44
- if (\in_array ($ response ->getStatus (), $ retryOnStatus , true ) && $ retryCount < $ maxRetries ) {
45
- /** @var int $initialDelay */
46
- $ initialDelay = $ this ->option ('initialDelay ' );
47
-
48
- /** @var float $delayMultiplier */
49
- $ delayMultiplier = $ this ->option ('delayMultiplier ' );
50
-
51
- $ delay = (int ) ($ initialDelay * ($ delayMultiplier ** $ retryCount ));
52
-
53
- $ this ->logger ->info (
54
- 'Retrying request ' ,
55
- [
56
- 'uri ' => $ request ->getUri (),
57
- 'status ' => $ response ->getStatus (),
58
- 'retry_count ' => $ retryCount + 1 ,
59
- 'delay_ms ' => $ delay ,
60
- ],
45
+ if (!\in_array ($ response ->getStatus (), $ retryOnStatus , true ) || $ retryCount >= $ maxRetries ) {
46
+ return $ response ;
47
+ }
48
+
49
+ $ delay = $ this ->getDelay ($ retryCount );
50
+
51
+ $ this ->logger ->info (
52
+ 'Retrying request ' ,
53
+ [
54
+ 'uri ' => $ request ->getUri (),
55
+ 'status ' => $ response ->getStatus (),
56
+ 'retry_count ' => $ retryCount + 1 ,
57
+ 'delay_ms ' => $ delay ,
58
+ ],
59
+ );
60
+
61
+ $ retryRequest = $ request
62
+ ->withMeta ('retry_count ' , $ retryCount + 1 )
63
+ ->addOption ('delay ' , $ delay );
64
+
65
+ $ this ->scheduler ->schedule ($ retryRequest );
66
+
67
+ return $ response ->drop ('Request being retried ' );
68
+ }
69
+
70
+ private function getDelay (int $ retryCount ): int
71
+ {
72
+ /** @var int|list<int> $backoff */
73
+ $ backoff = $ this ->option ('backoff ' );
74
+
75
+ if (\is_int ($ backoff )) {
76
+ return $ backoff * 1000 ;
77
+ }
78
+
79
+ if (!\is_array ($ backoff )) {
80
+ throw new InvalidArgumentException (
81
+ 'backoff must be an integer or array, ' . \gettype ($ backoff ) . ' given. ' ,
61
82
);
83
+ }
62
84
63
- $ retryRequest = $ request
64
- -> withMeta ( ' retry_count ' , $ retryCount + 1 )
65
- -> addOption ( ' delay ' , $ delay );
85
+ if ([] === $ backoff ) {
86
+ throw new InvalidArgumentException ( ' backoff array cannot be empty. ' );
87
+ }
66
88
67
- $ this -> scheduler -> schedule ( $ retryRequest );
89
+ $ nonIntegerValues = \array_filter ( $ backoff , static fn ( $ value ) => ! \is_int ( $ value ) );
68
90
69
- return $ response ->drop ('Request being retried ' );
91
+ if ([] !== $ nonIntegerValues ) {
92
+ throw new InvalidArgumentException (
93
+ 'backoff array must contain only integers. Found: ' .
94
+ \implode (', ' , \array_map ('gettype ' , $ nonIntegerValues )),
95
+ );
70
96
}
71
97
72
- return $ response ;
98
+ $ delay = $ backoff [$ retryCount ] ?? $ backoff [\array_key_last ($ backoff )];
99
+
100
+ return $ delay * 1000 ;
73
101
}
74
102
75
103
private static function defaultOptions (): array
76
104
{
77
105
return [
78
106
'retryOnStatus ' => [500 , 502 , 503 , 504 ],
79
107
'maxRetries ' => 3 ,
80
- 'initialDelay ' => 1000 ,
81
- 'delayMultiplier ' => 2.0 ,
108
+ 'backoff ' => [1 , 5 , 10 ],
82
109
];
83
110
}
84
111
}
0 commit comments