use crate::io::http::HttpMethod;
use crate::util::constants::app::{HTTP_RETRY_INITIAL_BACKOFF_MILLISECONDS, HTTP_RETRY_MAX_BACKOFF_SECONDS};
use axum::http::header::RETRY_AFTER;
use axum::http::HeaderMap;
use jiff::{SignedDuration, Timestamp};
use tower::layer::util::{Identity, Stack};
use tower::timeout::TimeoutLayer;
use tower::ServiceBuilder;
#[derive(Clone, Copy, Debug)]
pub struct HttpPolicy {
pub timeout: SignedDuration,
pub connect_timeout: SignedDuration,
pub max_retries: usize,
}
impl HttpPolicy {
pub fn max_attempts(self) -> usize {
self.max_retries.saturating_add(1)
}
}
impl Default for HttpPolicy {
fn default() -> Self {
Self {
timeout: SignedDuration::from_secs(30),
connect_timeout: SignedDuration::from_secs(10),
max_retries: 2,
}
}
}
pub fn http_timeout_layer() -> TimeoutLayer {
TimeoutLayer::new(shared_http_policy().timeout.unsigned_abs())
}
pub fn http_service_builder() -> ServiceBuilder<Stack<TimeoutLayer, Identity>> {
ServiceBuilder::new().layer(http_timeout_layer())
}
pub(crate) fn retry_delay(headers: Option<&HeaderMap>, attempt: usize, now: Timestamp) -> SignedDuration {
let retry_after = headers
.and_then(|values| values.get(RETRY_AFTER))
.and_then(|value| value.to_str().ok())
.and_then(|value| {
value.parse::<i64>().ok().map(SignedDuration::from_secs).or_else(|| {
httpdate::parse_http_date(value)
.ok()
.and_then(|retry_at| Timestamp::try_from(retry_at).ok())
.map(|retry_at| retry_at.duration_since(now).max(SignedDuration::ZERO))
})
});
let exponent = u32::try_from(attempt.saturating_sub(1)).unwrap_or(u32::MAX).min(8);
let fallback = SignedDuration::from_millis(HTTP_RETRY_INITIAL_BACKOFF_MILLISECONDS).saturating_mul(2_i32.saturating_pow(exponent));
retry_after
.unwrap_or(fallback)
.min(SignedDuration::from_secs(HTTP_RETRY_MAX_BACKOFF_SECONDS))
}
pub fn shared_http_policy() -> HttpPolicy {
HttpPolicy::default()
}
pub fn should_retry(method: &HttpMethod, status_code: Option<u16>) -> bool {
if !matches!(method, HttpMethod::Get) {
false
} else {
match status_code {
| Some(code) if code == 408 || code == 429 || code >= 500 => true,
| Some(_) => false,
| None => true,
}
}
}