1414use std:: collections:: HashMap ;
1515use std:: convert:: TryFrom ;
1616use std:: str:: FromStr ;
17+ use std:: thread;
18+ use std:: time:: Duration ;
1719
1820#[ allow( unused_imports) ]
1921use log:: { debug, error, info, trace} ;
2022
21- use minreq:: { Proxy , Request } ;
23+ use minreq:: { Proxy , Request , Response } ;
2224
2325use bitcoin:: consensus:: { deserialize, serialize, Decodable } ;
2426use bitcoin:: hashes:: { sha256, Hash } ;
@@ -27,7 +29,10 @@ use bitcoin::{
2729 block:: Header as BlockHeader , Block , BlockHash , MerkleBlock , Script , Transaction , Txid ,
2830} ;
2931
30- use crate :: { BlockStatus , BlockSummary , Builder , Error , MerkleProof , OutputStatus , Tx , TxStatus } ;
32+ use crate :: {
33+ BlockStatus , BlockSummary , Builder , Error , MerkleProof , OutputStatus , Tx , TxStatus ,
34+ RETRYABLE_ERROR_CODES ,
35+ } ;
3136
3237#[ derive( Debug , Clone ) ]
3338pub struct BlockingClient {
@@ -39,6 +44,10 @@ pub struct BlockingClient {
3944 pub timeout : Option < u64 > ,
4045 /// HTTP headers to set on every request made to Esplora server
4146 pub headers : HashMap < String , String > ,
47+ /// Backoff
48+ pub backoff : Duration ,
49+ /// Max retries
50+ pub max_retries : u32 ,
4251}
4352
4453impl BlockingClient {
@@ -49,6 +58,8 @@ impl BlockingClient {
4958 proxy : builder. proxy ,
5059 timeout : builder. timeout ,
5160 headers : builder. headers ,
61+ backoff : builder. backoff ,
62+ max_retries : builder. max_retries ,
5263 }
5364 }
5465
@@ -80,20 +91,20 @@ impl BlockingClient {
8091 }
8192
8293 fn get_opt_response < T : Decodable > ( & self , path : & str ) -> Result < Option < T > , Error > {
83- match self . get_request ( path) ? . send ( ) {
94+ match self . get_with_retry ( path) {
8495 Ok ( resp) if is_status_not_found ( resp. status_code ) => Ok ( None ) ,
8596 Ok ( resp) if !is_status_ok ( resp. status_code ) => {
8697 let status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?;
8798 let message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
8899 Err ( Error :: HttpResponse { status, message } )
89100 }
90101 Ok ( resp) => Ok ( Some ( deserialize :: < T > ( resp. as_bytes ( ) ) ?) ) ,
91- Err ( e) => Err ( Error :: Minreq ( e ) ) ,
102+ Err ( e) => Err ( e ) ,
92103 }
93104 }
94105
95106 fn get_opt_response_txid ( & self , path : & str ) -> Result < Option < Txid > , Error > {
96- match self . get_request ( path) ? . send ( ) {
107+ match self . get_with_retry ( path) {
97108 Ok ( resp) if is_status_not_found ( resp. status_code ) => Ok ( None ) ,
98109 Ok ( resp) if !is_status_ok ( resp. status_code ) => {
99110 let status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?;
@@ -103,12 +114,12 @@ impl BlockingClient {
103114 Ok ( resp) => Ok ( Some (
104115 Txid :: from_str ( resp. as_str ( ) . map_err ( Error :: Minreq ) ?) . map_err ( Error :: HexToArray ) ?,
105116 ) ) ,
106- Err ( e) => Err ( Error :: Minreq ( e ) ) ,
117+ Err ( e) => Err ( e ) ,
107118 }
108119 }
109120
110121 fn get_opt_response_hex < T : Decodable > ( & self , path : & str ) -> Result < Option < T > , Error > {
111- match self . get_request ( path) ? . send ( ) {
122+ match self . get_with_retry ( path) {
112123 Ok ( resp) if is_status_not_found ( resp. status_code ) => Ok ( None ) ,
113124 Ok ( resp) if !is_status_ok ( resp. status_code ) => {
114125 let status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?;
@@ -122,12 +133,12 @@ impl BlockingClient {
122133 . map_err ( Error :: BitcoinEncoding )
123134 . map ( |r| Some ( r) )
124135 }
125- Err ( e) => Err ( Error :: Minreq ( e ) ) ,
136+ Err ( e) => Err ( e ) ,
126137 }
127138 }
128139
129140 fn get_response_hex < T : Decodable > ( & self , path : & str ) -> Result < T , Error > {
130- match self . get_request ( path) ? . send ( ) {
141+ match self . get_with_retry ( path) {
131142 Ok ( resp) if !is_status_ok ( resp. status_code ) => {
132143 let status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?;
133144 let message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
@@ -138,51 +149,51 @@ impl BlockingClient {
138149 let hex_vec = Vec :: from_hex ( hex_str) . unwrap ( ) ;
139150 deserialize :: < T > ( & hex_vec) . map_err ( Error :: BitcoinEncoding )
140151 }
141- Err ( e) => Err ( Error :: Minreq ( e ) ) ,
152+ Err ( e) => Err ( e ) ,
142153 }
143154 }
144155
145156 fn get_response_json < ' a , T : serde:: de:: DeserializeOwned > (
146157 & ' a self ,
147158 path : & ' a str ,
148159 ) -> Result < T , Error > {
149- let response = self . get_request ( path) ? . send ( ) ;
160+ let response = self . get_with_retry ( path) ;
150161 match response {
151162 Ok ( resp) if !is_status_ok ( resp. status_code ) => {
152163 let status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?;
153164 let message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
154165 Err ( Error :: HttpResponse { status, message } )
155166 }
156167 Ok ( resp) => Ok ( resp. json :: < T > ( ) . map_err ( Error :: Minreq ) ?) ,
157- Err ( e) => Err ( Error :: Minreq ( e ) ) ,
168+ Err ( e) => Err ( e ) ,
158169 }
159170 }
160171
161172 fn get_opt_response_json < T : serde:: de:: DeserializeOwned > (
162173 & self ,
163174 path : & str ,
164175 ) -> Result < Option < T > , Error > {
165- match self . get_request ( path) ? . send ( ) {
176+ match self . get_with_retry ( path) {
166177 Ok ( resp) if is_status_not_found ( resp. status_code ) => Ok ( None ) ,
167178 Ok ( resp) if !is_status_ok ( resp. status_code ) => {
168179 let status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?;
169180 let message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
170181 Err ( Error :: HttpResponse { status, message } )
171182 }
172183 Ok ( resp) => Ok ( Some ( resp. json :: < T > ( ) ?) ) ,
173- Err ( e) => Err ( Error :: Minreq ( e ) ) ,
184+ Err ( e) => Err ( e ) ,
174185 }
175186 }
176187
177188 fn get_response_str ( & self , path : & str ) -> Result < String , Error > {
178- match self . get_request ( path) ? . send ( ) {
189+ match self . get_with_retry ( path) {
179190 Ok ( resp) if !is_status_ok ( resp. status_code ) => {
180191 let status = u16:: try_from ( resp. status_code ) . map_err ( Error :: StatusCode ) ?;
181192 let message = resp. as_str ( ) . unwrap_or_default ( ) . to_string ( ) ;
182193 Err ( Error :: HttpResponse { status, message } )
183194 }
184195 Ok ( resp) => Ok ( resp. as_str ( ) ?. to_string ( ) ) ,
185- Err ( e) => Err ( Error :: Minreq ( e ) ) ,
196+ Err ( e) => Err ( e ) ,
186197 }
187198 }
188199
@@ -339,6 +350,28 @@ impl BlockingClient {
339350 } ;
340351 self . get_response_json ( & path)
341352 }
353+
354+ /// Sends a GET request to the given `url`, retrying failed attempts
355+ /// for retryable error codes until max retries hit.
356+ pub fn get_with_retry ( & self , url : & str ) -> Result < Response , Error > {
357+ let mut attempts = 0 ;
358+ let mut delay = self . backoff ;
359+
360+ loop {
361+ match self . get_request ( url) ?. send ( ) {
362+ Ok ( resp)
363+ if attempts < self . max_retries
364+ && RETRYABLE_ERROR_CODES . contains ( & ( resp. status_code as u16 ) ) =>
365+ {
366+ thread:: sleep ( delay) ;
367+ attempts += 1 ;
368+ delay *= 2 ;
369+ }
370+ Ok ( resp) => return Ok ( resp) ,
371+ Err ( e) => return Err ( Error :: Minreq ( e) ) ,
372+ }
373+ }
374+ }
342375}
343376
344377fn is_status_ok ( status : i32 ) -> bool {
0 commit comments