use super::classify::{backoff_delay, is_retryable_message, is_retryable_status};
pub async fn send_response_with_retry<F, Fut>(mut f: F) -> anyhow::Result<reqwest::Response>
where
F: FnMut() -> Fut,
Fut: std::future::Future<Output = anyhow::Result<reqwest::Response>>,
{
let mut attempt = 0u32;
loop {
attempt += 1;
match f().await {
Ok(resp) if resp.status().is_success() => {
return Ok(resp);
}
Ok(resp) if is_retryable_status(resp.status()) => {
let status = resp.status();
let _ = resp.bytes().await;
let delay = backoff_delay(attempt);
tracing::warn!(
attempt, %status,
delay_secs = delay.as_secs(),
"Transient streaming error, retrying"
);
tokio::time::sleep(delay).await;
}
Ok(resp) => {
let status = resp.status();
let text = resp.text().await.unwrap_or_default();
if is_retryable_message(&text) {
let delay = backoff_delay(attempt);
tracing::warn!(
attempt, %status,
delay_secs = delay.as_secs(),
"Transient streaming error (body), retrying"
);
tokio::time::sleep(delay).await;
continue;
}
anyhow::bail!("Streaming error: {status} {text}");
}
Err(e) if is_retryable_message(&e.to_string()) => {
let delay = backoff_delay(attempt);
tracing::warn!(
attempt, error = %e,
delay_secs = delay.as_secs(),
"Transient network error, retrying"
);
tokio::time::sleep(delay).await;
}
Err(e) => return Err(e),
}
}
}