@@ -149,18 +149,24 @@ where
149149 // only start keeping track of time after the first request is made
150150 let start = start. get_or_insert_with ( OffsetDateTime :: now_utc) ;
151151 let ( last_error, retry_after) = match result {
152- Ok ( response) if response. status ( ) . is_success ( ) => {
153- trace ! (
154- ?request,
155- ?response,
156- "server returned success status {}" ,
157- response. status( ) ,
158- ) ;
159- return Ok ( response) ;
160- }
161152 Ok ( response) => {
162- // Error status code
163153 let status = response. status ( ) ;
154+ if !RETRY_STATUSES . contains ( & status) {
155+ if status. is_success ( ) {
156+ trace ! (
157+ ?request,
158+ ?response,
159+ "server returned success status {}" ,
160+ status,
161+ ) ;
162+ } else {
163+ debug ! (
164+ "server returned status which will not be retried: {}" ,
165+ status
166+ ) ;
167+ }
168+ return Ok ( response) ;
169+ }
164170
165171 // For a 429 response (TooManyRequests) or 503 (ServiceUnavailable),
166172 // use any "retry-after" headers returned by the server to determine how long to wait before retrying.
@@ -179,21 +185,6 @@ where
179185 http_error. error_code ( ) . map ( std:: borrow:: ToOwned :: to_owned) ,
180186 ) ;
181187
182- if !RETRY_STATUSES . contains ( & status) {
183- debug ! (
184- "server returned error status which will not be retried: {}" ,
185- status
186- ) ;
187- // Server didn't return a status we retry on so return early
188- let error = Error :: full (
189- error_kind,
190- http_error,
191- format ! (
192- "server returned error status which will not be retried: {status}"
193- ) ,
194- ) ;
195- return Err ( error) ;
196- }
197188 debug ! (
198189 "server returned error status which requires retry: {}" ,
199190 status
@@ -233,7 +224,28 @@ where
233224#[ cfg( test) ]
234225mod test {
235226 use super :: * ;
227+ use crate :: http:: {
228+ headers:: Headers , Context , FixedRetryOptions , Method , RawResponse , Request , RetryOptions ,
229+ Url ,
230+ } ;
236231 use :: time:: macros:: datetime;
232+ use std:: sync:: { Arc , Mutex } ;
233+
234+ // Policy that counts the requests it receives and returns responses having a given status code
235+ #[ derive( Debug ) ]
236+ struct StatusResponder {
237+ request_count : Arc < Mutex < u32 > > ,
238+ status : StatusCode ,
239+ }
240+
241+ #[ async_trait]
242+ impl Policy for StatusResponder {
243+ async fn send ( & self , _: & Context , _: & mut Request , _: & [ Arc < dyn Policy > ] ) -> PolicyResult {
244+ let mut count = self . request_count . lock ( ) . unwrap ( ) ;
245+ * count += 1 ;
246+ Ok ( RawResponse :: from_bytes ( self . status , Headers :: new ( ) , "" ) )
247+ }
248+ }
237249
238250 // A function that returns a fixed "now" value for testing.
239251 fn datetime_now ( ) -> OffsetDateTime {
@@ -290,4 +302,57 @@ mod test {
290302 let retry_after = get_retry_after ( & headers, datetime_now) ;
291303 assert_eq ! ( retry_after, Some ( Duration :: seconds( 123 ) ) ) ;
292304 }
305+
306+ #[ tokio:: test]
307+ async fn test_retry_statuses ( ) {
308+ let retries = 2u32 ;
309+ let retry_policy = RetryOptions :: fixed (
310+ FixedRetryOptions :: default ( )
311+ . delay ( Duration :: nanoseconds ( 1 ) )
312+ . max_retries ( retries) ,
313+ )
314+ . to_policy ( ) ;
315+ let ctx = Context :: new ( ) ;
316+ let url = Url :: parse ( "http://localhost" ) . unwrap ( ) ;
317+
318+ for & status in RETRY_STATUSES {
319+ let mut request = Request :: new ( url. clone ( ) , Method :: Get ) ;
320+ let count = Arc :: new ( Mutex :: new ( 0 ) ) ;
321+ let mock = StatusResponder {
322+ request_count : count. clone ( ) ,
323+ status,
324+ } ;
325+ let next = vec ! [ Arc :: new( mock) as Arc <dyn Policy >] ;
326+
327+ retry_policy
328+ . send ( & ctx, & mut request, & next)
329+ . await
330+ . expect_err ( "Policy should return an error after exhausting retries" ) ;
331+
332+ assert_eq ! (
333+ retries + 1 ,
334+ * count. lock( ) . unwrap( ) ,
335+ "Policy should retry {status}"
336+ ) ;
337+ }
338+
339+ let mut request = Request :: new ( url. clone ( ) , Method :: Get ) ;
340+ let count = Arc :: new ( Mutex :: new ( 0 ) ) ;
341+ let next = vec ! [ Arc :: new( StatusResponder {
342+ request_count: count. clone( ) ,
343+ status: StatusCode :: Unauthorized ,
344+ } ) as Arc <dyn Policy >] ;
345+
346+ let response = retry_policy
347+ . send ( & ctx, & mut request, & next)
348+ . await
349+ . expect ( "Policy should return a response whose status isn't in RETRY_STATUSES" ) ;
350+
351+ assert_eq ! ( response. status( ) , StatusCode :: Unauthorized ) ;
352+ assert_eq ! (
353+ 1 ,
354+ * count. lock( ) . unwrap( ) ,
355+ "Policy shouldn't retry after receiving a response whose status isn't in RETRY_STATUSES"
356+ ) ;
357+ }
293358}
0 commit comments