@@ -19,6 +19,9 @@ class RetryClient extends BaseClient {
19
19
/// The callback that determines whether a request should be retried.
20
20
final bool Function (BaseResponse ) _when;
21
21
22
+ /// The callback that determines whether a request when an error is thrown.
23
+ final bool Function (dynamic , StackTrace ) _whenError;
24
+
22
25
/// The callback that determines how long to wait before retrying a request.
23
26
final Duration Function (int ) _delay;
24
27
@@ -32,22 +35,27 @@ class RetryClient extends BaseClient {
32
35
///
33
36
/// By default, this retries requests whose responses have status code 503
34
37
/// Temporary Failure. If [when] is passed, it retries any request for whose
35
- /// response [when] returns `true` .
38
+ /// response [when] returns `true` . If [whenError] is passed, it also retries
39
+ /// any request that throws an error for which [whenError] returns `true` .
36
40
///
37
41
/// By default, this waits 500ms between the original request and the first
38
42
/// retry, then increases the delay by 1.5x for each subsequent retry. If
39
43
/// [delay] is passed, it's used to determine the time to wait before the
40
44
/// given (zero-based) retry.
41
45
///
42
46
/// If [onRetry] is passed, it's called immediately before each retry so that
43
- /// the client has a chance to perform side effects like logging.
47
+ /// the client has a chance to perform side effects like logging. The
48
+ /// `response` parameter will be null if the request was retried due to an
49
+ /// error for which [whenError] returned `true` .
44
50
RetryClient (this ._inner,
45
51
{int retries,
46
52
bool when (BaseResponse response),
53
+ bool whenError (error, StackTrace stackTrace),
47
54
Duration delay (int retryCount),
48
55
void onRetry (BaseRequest request, BaseResponse response, int retryCount)})
49
56
: _retries = retries ?? 3 ,
50
57
_when = when ?? ((response) => response.statusCode == 503 ),
58
+ _whenError = whenError ?? ((_, __) => false ),
51
59
_delay = delay ??
52
60
((retryCount) =>
53
61
new Duration (milliseconds: 500 ) * math.pow (1.5 , retryCount)),
@@ -63,29 +71,42 @@ class RetryClient extends BaseClient {
63
71
/// `delays[1]` after the first retry, and so on.
64
72
RetryClient .withDelays (Client inner, Iterable <Duration > delays,
65
73
{bool when (BaseResponse response),
74
+ bool whenError (error, StackTrace stackTrace),
66
75
void onRetry (BaseRequest request, BaseResponse response, int retryCount)})
67
- : this ._withDelays (inner, delays.toList (), when : when , onRetry: onRetry);
76
+ : this ._withDelays (inner, delays.toList (),
77
+ when : when , whenError: whenError, onRetry: onRetry);
68
78
69
79
RetryClient ._withDelays (Client inner, List <Duration > delays,
70
80
{bool when (BaseResponse response),
81
+ bool whenError (error, StackTrace stackTrace),
71
82
void onRetry (BaseRequest request, BaseResponse response, int retryCount)})
72
83
: this (inner,
73
84
retries: delays.length,
74
85
delay: (retryCount) => delays[retryCount],
75
86
when : when ,
87
+ whenError: whenError,
76
88
onRetry: onRetry);
77
89
78
90
Future <StreamedResponse > send (BaseRequest request) async {
79
91
var splitter = new StreamSplitter (request.finalize ());
80
92
81
93
var i = 0 ;
82
94
while (true ) {
83
- var response = await _inner.send (_copyRequest (request, splitter.split ()));
84
- if (i == _retries || ! _when (response)) return response;
95
+ StreamedResponse response;
96
+ try {
97
+ response = await _inner.send (_copyRequest (request, splitter.split ()));
98
+ } catch (error, stackTrace) {
99
+ if (i == _retries || ! _whenError (error, stackTrace)) rethrow ;
100
+ }
101
+
102
+ if (response != null ) {
103
+ if (i == _retries || ! _when (response)) return response;
104
+
105
+ // Make sure the response stream is listened to so that we don't leave
106
+ // dangling connections.
107
+ response.stream.listen ((_) {}).cancel ()? .catchError ((_) {});
108
+ }
85
109
86
- // Make sure the response stream is listened to so that we don't leave
87
- // dangling connections.
88
- response.stream.listen ((_) {}).cancel ()? .catchError ((_) {});
89
110
await new Future .delayed (_delay (i));
90
111
if (_onRetry != null ) _onRetry (request, response, i);
91
112
i++ ;
0 commit comments