headers_ext/common/
retry_after.rs

1use std::time::{Duration, SystemTime};
2
3use util::{HttpDate, Seconds, TryFromValues};
4use ::HeaderValue;
5
6/// The `Retry-After` header.
7///
8/// The `Retry-After` response-header field can be used with a 503 (Service
9/// Unavailable) response to indicate how long the service is expected to be
10/// unavailable to the requesting client. This field MAY also be used with any
11/// 3xx (Redirection) response to indicate the minimum time the user-agent is
12/// asked wait before issuing the redirected request. The value of this field
13/// can be either an HTTP-date or an integer number of seconds (in decimal)
14/// after the time of the response.
15///
16/// # Examples
17/// ```
18/// # extern crate headers_ext as headers;
19/// use std::time::{Duration, SystemTime};
20/// use headers::RetryAfter;
21///
22/// let delay = RetryAfter::delay(Duration::from_secs(300));
23/// let date = RetryAfter::date(SystemTime::now());
24/// ```
25
26/// Retry-After header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.3)
27#[derive(Debug, Clone, PartialEq, Eq, Header)]
28pub struct RetryAfter(After);
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31enum After {
32    /// Retry after the given DateTime
33    DateTime(HttpDate),
34    /// Retry after this duration has elapsed
35    Delay(Seconds),
36}
37
38impl RetryAfter {
39    /// Create an `RetryAfter` header with a date value.
40    pub fn date(time: SystemTime) -> RetryAfter {
41        RetryAfter(After::DateTime(time.into()))
42    }
43
44    /// Create an `RetryAfter` header with a date value.
45    pub fn delay(dur: Duration) -> RetryAfter {
46        RetryAfter(After::Delay(dur.into()))
47    }
48}
49
50impl TryFromValues for After {
51    fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
52    where
53        I: Iterator<Item = &'i HeaderValue>,
54    {
55        values
56            .next()
57            .and_then(|val| {
58                if let Some(delay) = Seconds::from_val(val) {
59                    return Some(After::Delay(delay));
60                }
61
62                let date = HttpDate::from_val(val)?;
63                Some(After::DateTime(date))
64            })
65            .ok_or_else(::Error::invalid)
66    }
67}
68
69impl<'a> From<&'a After> for HeaderValue {
70    fn from(after: &'a After) -> HeaderValue {
71        match *after {
72            After::Delay(ref delay) => delay.into(),
73            After::DateTime(ref date) => date.into(),
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use std::time::Duration;
81    use util::HttpDate;
82    use super::RetryAfter;
83    use super::super::{test_decode};
84
85    #[test]
86    fn delay_decode() {
87        let r: RetryAfter = test_decode(&["1234"]).unwrap();
88        assert_eq!(
89            r,
90            RetryAfter::delay(Duration::from_secs(1234)),
91        );
92    }
93
94    macro_rules! test_retry_after_datetime {
95        ($name:ident, $s:expr) => {
96            #[test]
97            fn $name() {
98                let r: RetryAfter = test_decode(&[$s]).unwrap();
99                let dt = "Sun, 06 Nov 1994 08:49:37 GMT".parse::<HttpDate>().unwrap();
100
101                assert_eq!(r, RetryAfter(super::After::DateTime(dt)));
102            }
103        }
104    }
105
106    test_retry_after_datetime!(date_decode_rfc1123, "Sun, 06 Nov 1994 08:49:37 GMT");
107    test_retry_after_datetime!(date_decode_rfc850, "Sunday, 06-Nov-94 08:49:37 GMT");
108    test_retry_after_datetime!(date_decode_asctime, "Sun Nov  6 08:49:37 1994");
109}