1414use  std:: collections:: HashMap ; 
1515use  std:: convert:: TryFrom ; 
1616use  std:: str:: FromStr ; 
17+ use  std:: thread; 
1718
1819#[ allow( unused_imports) ]  
1920use  log:: { debug,  error,  info,  trace} ; 
2021
21- use  minreq:: { Proxy ,  Request } ; 
22+ use  minreq:: { Proxy ,  Request ,   Response } ; 
2223
2324use  bitcoin:: consensus:: { deserialize,  serialize,  Decodable } ; 
2425use  bitcoin:: hashes:: { sha256,  Hash } ; 
@@ -27,7 +28,10 @@ use bitcoin::{
2728    block:: Header  as  BlockHeader ,  Block ,  BlockHash ,  MerkleBlock ,  Script ,  Transaction ,  Txid , 
2829} ; 
2930
30- use  crate :: { BlockStatus ,  BlockSummary ,  Builder ,  Error ,  MerkleProof ,  OutputStatus ,  Tx ,  TxStatus } ; 
31+ use  crate :: { 
32+     BlockStatus ,  BlockSummary ,  Builder ,  Error ,  MerkleProof ,  OutputStatus ,  Tx ,  TxStatus , 
33+     BASE_BACKOFF_MILLIS ,  RETRYABLE_ERROR_CODES , 
34+ } ; 
3135
3236#[ derive( Debug ,  Clone ) ]  
3337pub  struct  BlockingClient  { 
@@ -39,6 +43,8 @@ pub struct BlockingClient {
3943pub  timeout :  Option < u64 > , 
4044    /// HTTP headers to set on every request made to Esplora server 
4145pub  headers :  HashMap < String ,  String > , 
46+     /// Number of times to retry a request 
47+ pub  max_retries :  usize , 
4248} 
4349
4450impl  BlockingClient  { 
@@ -49,6 +55,7 @@ impl BlockingClient {
4955            proxy :  builder. proxy , 
5056            timeout :  builder. timeout , 
5157            headers :  builder. headers , 
58+             max_retries :  builder. max_retries , 
5259        } 
5360    } 
5461
@@ -80,20 +87,20 @@ impl BlockingClient {
8087    } 
8188
8289    fn  get_opt_response < T :  Decodable > ( & self ,  path :  & str )  -> Result < Option < T > ,  Error >  { 
83-         match  self . get_request ( path) ? . send ( )  { 
90+         match  self . get_with_retry ( path)  { 
8491            Ok ( resp)  if  is_status_not_found ( resp. status_code )  => Ok ( None ) , 
8592            Ok ( resp)  if  !is_status_ok ( resp. status_code )  => { 
8693                let  status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?; 
8794                let  message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ; 
8895                Err ( Error :: HttpResponse  {  status,  message } ) 
8996            } 
9097            Ok ( resp)  => Ok ( Some ( deserialize :: < T > ( resp. as_bytes ( ) ) ?) ) , 
91-             Err ( e)  => Err ( Error :: Minreq ( e ) ) , 
98+             Err ( e)  => Err ( e ) , 
9299        } 
93100    } 
94101
95102    fn  get_opt_response_txid ( & self ,  path :  & str )  -> Result < Option < Txid > ,  Error >  { 
96-         match  self . get_request ( path) ? . send ( )  { 
103+         match  self . get_with_retry ( path)  { 
97104            Ok ( resp)  if  is_status_not_found ( resp. status_code )  => Ok ( None ) , 
98105            Ok ( resp)  if  !is_status_ok ( resp. status_code )  => { 
99106                let  status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?; 
@@ -103,12 +110,12 @@ impl BlockingClient {
103110            Ok ( resp)  => Ok ( Some ( 
104111                Txid :: from_str ( resp. as_str ( ) . map_err ( Error :: Minreq ) ?) . map_err ( Error :: HexToArray ) ?, 
105112            ) ) , 
106-             Err ( e)  => Err ( Error :: Minreq ( e ) ) , 
113+             Err ( e)  => Err ( e ) , 
107114        } 
108115    } 
109116
110117    fn  get_opt_response_hex < T :  Decodable > ( & self ,  path :  & str )  -> Result < Option < T > ,  Error >  { 
111-         match  self . get_request ( path) ? . send ( )  { 
118+         match  self . get_with_retry ( path)  { 
112119            Ok ( resp)  if  is_status_not_found ( resp. status_code )  => Ok ( None ) , 
113120            Ok ( resp)  if  !is_status_ok ( resp. status_code )  => { 
114121                let  status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?; 
@@ -122,12 +129,12 @@ impl BlockingClient {
122129                    . map_err ( Error :: BitcoinEncoding ) 
123130                    . map ( |r| Some ( r) ) 
124131            } 
125-             Err ( e)  => Err ( Error :: Minreq ( e ) ) , 
132+             Err ( e)  => Err ( e ) , 
126133        } 
127134    } 
128135
129136    fn  get_response_hex < T :  Decodable > ( & self ,  path :  & str )  -> Result < T ,  Error >  { 
130-         match  self . get_request ( path) ? . send ( )  { 
137+         match  self . get_with_retry ( path)  { 
131138            Ok ( resp)  if  !is_status_ok ( resp. status_code )  => { 
132139                let  status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?; 
133140                let  message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ; 
@@ -138,51 +145,51 @@ impl BlockingClient {
138145                let  hex_vec = Vec :: from_hex ( hex_str) . unwrap ( ) ; 
139146                deserialize :: < T > ( & hex_vec) . map_err ( Error :: BitcoinEncoding ) 
140147            } 
141-             Err ( e)  => Err ( Error :: Minreq ( e ) ) , 
148+             Err ( e)  => Err ( e ) , 
142149        } 
143150    } 
144151
145152    fn  get_response_json < ' a ,  T :  serde:: de:: DeserializeOwned > ( 
146153        & ' a  self , 
147154        path :  & ' a  str , 
148155    )  -> Result < T ,  Error >  { 
149-         let  response = self . get_request ( path) ? . send ( ) ; 
156+         let  response = self . get_with_retry ( path) ; 
150157        match  response { 
151158            Ok ( resp)  if  !is_status_ok ( resp. status_code )  => { 
152159                let  status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?; 
153160                let  message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ; 
154161                Err ( Error :: HttpResponse  {  status,  message } ) 
155162            } 
156163            Ok ( resp)  => Ok ( resp. json :: < T > ( ) . map_err ( Error :: Minreq ) ?) , 
157-             Err ( e)  => Err ( Error :: Minreq ( e ) ) , 
164+             Err ( e)  => Err ( e ) , 
158165        } 
159166    } 
160167
161168    fn  get_opt_response_json < T :  serde:: de:: DeserializeOwned > ( 
162169        & self , 
163170        path :  & str , 
164171    )  -> Result < Option < T > ,  Error >  { 
165-         match  self . get_request ( path) ? . send ( )  { 
172+         match  self . get_with_retry ( path)  { 
166173            Ok ( resp)  if  is_status_not_found ( resp. status_code )  => Ok ( None ) , 
167174            Ok ( resp)  if  !is_status_ok ( resp. status_code )  => { 
168175                let  status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?; 
169176                let  message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ; 
170177                Err ( Error :: HttpResponse  {  status,  message } ) 
171178            } 
172179            Ok ( resp)  => Ok ( Some ( resp. json :: < T > ( ) ?) ) , 
173-             Err ( e)  => Err ( Error :: Minreq ( e ) ) , 
180+             Err ( e)  => Err ( e ) , 
174181        } 
175182    } 
176183
177184    fn  get_response_str ( & self ,  path :  & str )  -> Result < String ,  Error >  { 
178-         match  self . get_request ( path) ? . send ( )  { 
185+         match  self . get_with_retry ( path)  { 
179186            Ok ( resp)  if  !is_status_ok ( resp. status_code )  => { 
180187                let  status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?; 
181188                let  message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ; 
182189                Err ( Error :: HttpResponse  {  status,  message } ) 
183190            } 
184191            Ok ( resp)  => Ok ( resp. as_str ( ) ?. to_string ( ) ) , 
185-             Err ( e)  => Err ( Error :: Minreq ( e ) ) , 
192+             Err ( e)  => Err ( e ) , 
186193        } 
187194    } 
188195
@@ -339,6 +346,24 @@ impl BlockingClient {
339346        } ; 
340347        self . get_response_json ( & path) 
341348    } 
349+ 
350+     /// Sends a GET request to the given `url`, retrying failed attempts 
351+ /// for retryable error codes until max retries hit. 
352+ pub  fn  get_with_retry ( & self ,  url :  & str )  -> Result < Response ,  Error >  { 
353+         let  mut  delay = BASE_BACKOFF_MILLIS ; 
354+         let  mut  attempts = 0 ; 
355+ 
356+         loop  { 
357+             match  self . get_request ( url) ?. send ( ) ? { 
358+                 resp if  attempts < self . max_retries  && is_status_retryable ( resp. status_code )  => { 
359+                     thread:: sleep ( delay) ; 
360+                     attempts += 1 ; 
361+                     delay *= 2 ; 
362+                 } 
363+                 resp => return  Ok ( resp) , 
364+             } 
365+         } 
366+     } 
342367} 
343368
344369fn  is_status_ok ( status :  i32 )  -> bool  { 
@@ -348,3 +373,8 @@ fn is_status_ok(status: i32) -> bool {
348373fn  is_status_not_found ( status :  i32 )  -> bool  { 
349374    status == 404 
350375} 
376+ 
377+ fn  is_status_retryable ( status :  i32 )  -> bool  { 
378+     let  status = status as  u16 ; 
379+     RETRYABLE_ERROR_CODES . contains ( & status) 
380+ } 
0 commit comments