use std::time::{Duration, SystemTime};
use reqwest::header::HeaderValue;
const MAX_HONOURED: Duration = Duration::from_secs(300);
#[must_use]
pub(crate) fn parse_retry_after(value: Option<&HeaderValue>) -> Option<Duration> {
let raw = value?.to_str().ok()?.trim();
if let Ok(secs) = raw.parse::<u64>() {
return Some(Duration::from_secs(secs).min(MAX_HONOURED));
}
let target = httpdate::parse_http_date(raw).ok()?;
let now = SystemTime::now();
let delta = target.duration_since(now).ok()?;
Some(delta.min(MAX_HONOURED))
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
reason = "test code: unwrap and expect on well-formed fixtures are the expected diagnostics"
)]
mod tests {
use std::time::{Duration, SystemTime};
use reqwest::header::HeaderValue;
use super::{MAX_HONOURED, parse_retry_after};
fn header(s: &str) -> HeaderValue {
HeaderValue::from_str(s).expect("test fixtures are well-formed header values")
}
#[test]
fn delta_seconds_30_returns_30s() {
let v = header("30");
assert_eq!(parse_retry_after(Some(&v)), Some(Duration::from_secs(30)));
}
#[test]
fn delta_seconds_zero_returns_zero() {
let v = header("0");
assert_eq!(parse_retry_after(Some(&v)), Some(Duration::ZERO));
}
#[test]
fn delta_seconds_huge_caps_at_5min() {
let v = header("86400");
assert_eq!(parse_retry_after(Some(&v)), Some(MAX_HONOURED));
}
#[test]
fn delta_seconds_exactly_at_cap_returns_cap() {
let v = header("300");
assert_eq!(parse_retry_after(Some(&v)), Some(MAX_HONOURED));
}
#[test]
fn http_date_in_future_returns_correct_delta() {
let target = SystemTime::now() + Duration::from_secs(60);
let formatted = httpdate::fmt_http_date(target);
let v = header(&formatted);
let parsed = parse_retry_after(Some(&v)).expect("future HTTP-date must parse");
let expected = Duration::from_secs(60);
let slack = Duration::from_secs(2);
assert!(
parsed >= expected.saturating_sub(slack) && parsed <= expected + slack,
"expected ~60s, got {parsed:?}"
);
}
#[test]
fn http_date_in_past_returns_none() {
let target = SystemTime::now() - Duration::from_secs(60);
let formatted = httpdate::fmt_http_date(target);
let v = header(&formatted);
assert_eq!(parse_retry_after(Some(&v)), None);
}
#[test]
fn malformed_returns_none() {
let v = header("not a number and not a date");
assert_eq!(parse_retry_after(Some(&v)), None);
}
#[test]
fn absent_returns_none() {
assert_eq!(parse_retry_after(None), None);
}
}