@@ -12,7 +12,6 @@ use std::time::Duration;
1212// non-std crates
1313use anyhow:: { anyhow, Error , Result } ;
1414use chrono:: DateTime ;
15- use futures:: future:: { BoxFuture , FutureExt } ;
1615use reqwest:: header:: { HeaderMap , HeaderValue } ;
1716use reqwest:: { Client , IntoUrl , Method , Request , Response , Url } ;
1817
@@ -111,85 +110,88 @@ pub trait RestApiClient {
111110 request : Request ,
112111 rate_limit_headers : RestApiRateLimitHeaders ,
113112 retries : u64 ,
114- ) -> BoxFuture < ' static , Result < Response > > {
113+ ) -> impl Future < Output = Result < Response > > + Send {
115114 async move {
116- let result = client
117- . execute ( request. try_clone ( ) . ok_or ( anyhow ! (
118- "Failed to clone request object for recursive behavior"
119- ) ) ?)
120- . await ;
121- if let Ok ( response) = & result {
122- if [ 403u16 , 429u16 ] . contains ( & response. status ( ) . as_u16 ( ) ) {
123- // rate limit exceeded
124-
125- // check if primary rate limit was violated; panic if so.
126- let mut requests_remaining = None ;
127- if let Some ( remaining) = response. headers ( ) . get ( & rate_limit_headers. remaining ) {
128- if let Ok ( count) = remaining. to_str ( ) {
129- if let Ok ( value) = count. parse :: < i64 > ( ) {
130- requests_remaining = Some ( value) ;
131- } else {
132- log:: debug!(
115+ for i in retries..5 {
116+ let result = client
117+ . execute ( request. try_clone ( ) . ok_or ( anyhow ! (
118+ "Failed to clone request object for recursive behavior"
119+ ) ) ?)
120+ . await ;
121+ if let Ok ( response) = & result {
122+ if [ 403u16 , 429u16 ] . contains ( & response. status ( ) . as_u16 ( ) ) {
123+ // rate limit may have been exceeded
124+
125+ // check if primary rate limit was violated; panic if so.
126+ let mut requests_remaining = None ;
127+ if let Some ( remaining) =
128+ response. headers ( ) . get ( & rate_limit_headers. remaining )
129+ {
130+ if let Ok ( count) = remaining. to_str ( ) {
131+ if let Ok ( value) = count. parse :: < i64 > ( ) {
132+ requests_remaining = Some ( value) ;
133+ } else {
134+ log:: debug!(
133135 "Failed to parse i64 from remaining attempts about rate limit: {count}"
134136 ) ;
137+ }
135138 }
139+ } else {
140+ // NOTE: I guess it is sometimes valid for a request to
141+ // not include remaining rate limit attempts
142+ log:: debug!(
143+ "Response headers do not include remaining API usage count"
144+ ) ;
136145 }
137- } else {
138- // NOTE: I guess it is sometimes valid for a request to
139- // not include remaining rate limit attempts
140- log:: debug!( "Response headers do not include remaining API usage count" ) ;
141- }
142- if requests_remaining. is_some_and ( |v| v <= 0 ) {
143- if let Some ( reset_value) = response. headers ( ) . get ( & rate_limit_headers. reset )
144- {
145- if let Ok ( epoch) = reset_value. to_str ( ) {
146- if let Ok ( value) = epoch. parse :: < i64 > ( ) {
147- if let Some ( reset) = DateTime :: from_timestamp ( value, 0 ) {
148- return Err ( anyhow ! (
149- "REST API rate limit exceeded! Resets at {}" ,
150- reset
151- ) ) ;
152- }
153- } else {
154- log:: debug!(
146+ if requests_remaining. is_some_and ( |v| v <= 0 ) {
147+ if let Some ( reset_value) =
148+ response. headers ( ) . get ( & rate_limit_headers. reset )
149+ {
150+ if let Ok ( epoch) = reset_value. to_str ( ) {
151+ if let Ok ( value) = epoch. parse :: < i64 > ( ) {
152+ if let Some ( reset) = DateTime :: from_timestamp ( value, 0 ) {
153+ return Err ( anyhow ! (
154+ "REST API rate limit exceeded! Resets at {}" ,
155+ reset
156+ ) ) ;
157+ }
158+ } else {
159+ log:: debug!(
155160 "Failed to parse i64 from reset time about rate limit: {epoch}"
156161 ) ;
162+ }
157163 }
164+ } else {
165+ log:: debug!( "Response headers does not include a reset timestamp" ) ;
158166 }
159- } else {
160- log:: debug!( "Response headers does not include a reset timestamp" ) ;
167+ return Err ( anyhow ! ( "REST API rate limit exceeded!" ) ) ;
161168 }
162- return Err ( anyhow ! ( "REST API rate limit exceeded!" ) ) ;
163- }
164169
165- // check if secondary rate limit is violated; backoff and try again.
166- if retries > 4 {
167- return Err ( anyhow ! ( "REST API secondary rate limit exceeded" ) ) ;
168- }
169- if let Some ( retry_value) = response. headers ( ) . get ( & rate_limit_headers. retry ) {
170- if let Ok ( retry_str) = retry_value. to_str ( ) {
171- if let Ok ( retry) = retry_str. parse :: < u64 > ( ) {
172- let interval = Duration :: from_secs ( retry + retries. pow ( 2 ) ) ;
173- tokio:: time:: sleep ( interval) . await ;
174- } else {
175- log:: debug!(
170+ // check if secondary rate limit is violated; backoff and try again.
171+ if i >= 4 {
172+ break ;
173+ }
174+ if let Some ( retry_value) = response. headers ( ) . get ( & rate_limit_headers. retry )
175+ {
176+ if let Ok ( retry_str) = retry_value. to_str ( ) {
177+ if let Ok ( retry) = retry_str. parse :: < u64 > ( ) {
178+ let interval = Duration :: from_secs ( retry + i. pow ( 2 ) ) ;
179+ tokio:: time:: sleep ( interval) . await ;
180+ } else {
181+ log:: debug!(
176182 "Failed to parse u64 from retry interval about rate limit: {retry_str}"
177183 ) ;
184+ }
178185 }
186+ continue ;
179187 }
180- return Self :: send_api_request (
181- client,
182- request,
183- rate_limit_headers,
184- retries + 1 ,
185- )
186- . await ;
187188 }
189+ return result. map_err ( Error :: from) ;
188190 }
191+ return result. map_err ( Error :: from) ;
189192 }
190- result . map_err ( Error :: from )
193+ Err ( anyhow ! ( "REST API secondary rate limit exceeded" ) )
191194 }
192- . boxed ( )
193195 }
194196
195197 /// A way to get the list of changed files using REST API calls. It is this method's
0 commit comments