Skip to content

Commit 632371d

Browse files
committed
fix(client): retry on transient connect/timeout errors
1 parent e16e1ad commit 632371d

File tree

1 file changed

+28
-14
lines changed

1 file changed

+28
-14
lines changed

src/client.rs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,43 @@ pub fn build_client() -> Result<reqwest::Client, crate::error::GwsError> {
2626
})
2727
}
2828

29-
/// Send an HTTP request with automatic retry on 429 (rate limit) responses.
29+
/// Send an HTTP request with automatic retry on 429 (rate limit) responses
30+
/// and transient connection/timeout errors.
3031
/// Respects the `Retry-After` header; falls back to exponential backoff (1s, 2s, 4s).
3132
pub async fn send_with_retry(
3233
build_request: impl Fn() -> reqwest::RequestBuilder,
3334
) -> Result<reqwest::Response, reqwest::Error> {
34-
for attempt in 0..MAX_RETRIES {
35-
let resp = build_request().send().await?;
35+
let mut last_err: Option<reqwest::Error> = None;
3636

37-
if resp.status() != reqwest::StatusCode::TOO_MANY_REQUESTS {
38-
return Ok(resp);
37+
for attempt in 0..MAX_RETRIES {
38+
match build_request().send().await {
39+
Ok(resp) => {
40+
if resp.status() != reqwest::StatusCode::TOO_MANY_REQUESTS {
41+
return Ok(resp);
42+
}
43+
44+
let header_value = resp
45+
.headers()
46+
.get("retry-after")
47+
.and_then(|v| v.to_str().ok());
48+
let retry_after = compute_retry_delay(header_value, attempt);
49+
tokio::time::sleep(std::time::Duration::from_secs(retry_after)).await;
50+
}
51+
Err(e) if e.is_connect() || e.is_timeout() => {
52+
// Transient network error — retry with exponential backoff
53+
let delay = compute_retry_delay(None, attempt);
54+
tokio::time::sleep(std::time::Duration::from_secs(delay)).await;
55+
last_err = Some(e);
56+
}
57+
Err(e) => return Err(e),
3958
}
40-
41-
let header_value = resp
42-
.headers()
43-
.get("retry-after")
44-
.and_then(|v| v.to_str().ok());
45-
let retry_after = compute_retry_delay(header_value, attempt);
46-
47-
tokio::time::sleep(std::time::Duration::from_secs(retry_after)).await;
4859
}
4960

5061
// Final attempt — return whatever we get
51-
build_request().send().await
62+
match build_request().send().await {
63+
Ok(resp) => Ok(resp),
64+
Err(e) => Err(last_err.unwrap_or(e)),
65+
}
5266
}
5367

5468
/// Compute the retry delay from a Retry-After header value and attempt number.

0 commit comments

Comments
 (0)