11use crate :: { error, Error } ;
22use base64ct:: Base64 ;
33use base64ct:: Encoding ;
4+ use rand:: Rng ;
45use std:: fmt:: Write ;
6+ use std:: thread:: sleep;
57use std:: time:: Duration ;
68
79#[ derive( Debug , Clone ) ]
@@ -39,8 +41,8 @@ impl TokenAuthParams {
3941pub ( super ) struct HttpConfig {
4042 pub ( super ) min_throughput : u64 ,
4143 pub ( super ) user_agent : Option < String > ,
42- pub ( super ) max_retries : u32 ,
43- pub ( super ) retry_interval : Duration ,
44+ pub ( super ) retry_timeout : Duration ,
45+ pub ( super ) grace_timeout : Duration ,
4446 pub ( super ) transactional : bool ,
4547}
4648
@@ -55,7 +57,7 @@ pub(super) struct HttpHandlerState {
5557 pub ( super ) auth : Option < String > ,
5658
5759 /// Additional grace period added to the timeout as calculated via `min_throughput`.
58- pub ( super ) timeout_grace_period : Duration ,
60+ pub ( super ) grace_timeout : Duration ,
5961
6062 /// HTTP params configured via the `SenderBuilder`.
6163 pub ( super ) config : HttpConfig ,
@@ -175,33 +177,50 @@ pub(super) fn is_retriable_error(err: &ureq::Error) -> bool {
175177}
176178
177179#[ allow( clippy:: result_large_err) ] // `ureq::Error` is large enough to cause this warning.
178- pub ( super ) fn retry_http_send (
180+ fn retry_http_send (
179181 request : ureq:: Request ,
180182 buf : & [ u8 ] ,
181- max_retries : u32 ,
182- mut retry_interval : Duration ,
183+ retry_timeout : Duration ,
184+ mut last_err : ureq :: Error ,
183185) -> Result < ureq:: Response , ureq:: Error > {
184- let mut counter = 0 ;
185-
186+ let mut rng = rand:: thread_rng ( ) ;
187+ let retry_end = std:: time:: Instant :: now ( ) + retry_timeout;
188+ let mut retry_interval_ms = 10 ;
186189 loop {
187- let response_or_err = request. clone ( ) . send_bytes ( buf) ;
188- let last_err = match response_or_err {
190+ let jitter_ms = rng. gen_range ( -5i32 ..5 ) ;
191+ let to_sleep_ms = retry_interval_ms + jitter_ms;
192+ let to_sleep = Duration :: from_millis ( to_sleep_ms as u64 ) ;
193+ if ( std:: time:: Instant :: now ( ) + to_sleep) > retry_end {
194+ return Err ( last_err) ;
195+ }
196+ sleep ( to_sleep) ;
197+ last_err = match request. clone ( ) . send_bytes ( buf) {
189198 Ok ( res) => return Ok ( res) ,
190199 Err ( err) => {
191- if is_retriable_error ( & err) {
192- err
193- } else {
200+ if !is_retriable_error ( & err) {
194201 return Err ( err) ;
195202 }
203+ err
196204 }
197205 } ;
206+ retry_interval_ms = ( retry_interval_ms * 2 ) . min ( 1000 ) ;
207+ }
208+ }
198209
199- counter += 1 ;
200- if counter > max_retries {
201- return Err ( last_err) ;
202- }
210+ #[ allow( clippy:: result_large_err) ] // `ureq::Error` is large enough to cause this warning.
211+ pub ( super ) fn http_send_with_retries (
212+ request : ureq:: Request ,
213+ buf : & [ u8 ] ,
214+ retry_timeout : Duration ,
215+ ) -> Result < ureq:: Response , ureq:: Error > {
216+ let last_err = match request. clone ( ) . send_bytes ( buf) {
217+ Ok ( res) => return Ok ( res) ,
218+ Err ( err) => err,
219+ } ;
203220
204- std :: thread :: sleep ( retry_interval ) ;
205- retry_interval *= 2 ;
221+ if retry_timeout . is_zero ( ) || ! is_retriable_error ( & last_err ) {
222+ return Err ( last_err ) ;
206223 }
224+
225+ retry_http_send ( request, buf, retry_timeout, last_err)
207226}
0 commit comments