1use std::time::Duration;
2
3use reqwest::{Response, StatusCode, header};
4
5pub enum RetryAfter {
6 Duration(Duration),
7 After(std::time::SystemTime),
8}
9
10fn parse_retry_after(value: &str) -> Option<RetryAfter> {
13 if let Ok(seconds) = value.parse::<u64>() {
15 return Some(RetryAfter::Duration(Duration::from_secs(seconds)));
16 }
17
18 if let Ok(datetime) = httpdate::parse_http_date(value) {
21 return Some(RetryAfter::After(datetime));
22 }
23
24 None
25}
26
27pub fn calculate_retry_after_from_response_header(
28 response: &Response,
29 default_duration: Duration,
30) -> Option<Duration> {
31 if response.status() == StatusCode::TOO_MANY_REQUESTS {
32 let retry_after = response
33 .headers()
34 .get(header::RETRY_AFTER)
35 .and_then(|v| v.to_str().ok())
36 .and_then(parse_retry_after)
37 .and_then(|retry| match retry {
38 RetryAfter::Duration(d) => Some(d),
39 RetryAfter::After(after) => {
40 std::time::SystemTime::now()
42 .duration_since(std::time::UNIX_EPOCH)
43 .ok()
44 .and_then(|now| {
45 after
46 .duration_since(std::time::UNIX_EPOCH)
47 .ok()
48 .and_then(|target| target.checked_sub(now))
49 })
50 }
51 })
52 .unwrap_or(default_duration);
53 return Some(retry_after);
54 }
55 None
56}
57
58pub fn get_client_error(response: &Response) -> Option<StatusCode> {
59 let status = response.status();
60 if status.is_client_error() {
61 Some(status)
62 } else {
63 None
64 }
65}