1 use std::time::{Duration, SystemTime};
2 
3 use util::{HttpDate, Seconds, TryFromValues};
4 use 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;
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)]
28 pub struct RetryAfter(After);
29 
30 derive_header! {
31     RetryAfter(_),
32     name: RETRY_AFTER
33 }
34 
35 #[derive(Debug, Clone, PartialEq, Eq)]
36 enum After {
37     /// Retry after the given DateTime
38     DateTime(HttpDate),
39     /// Retry after this duration has elapsed
40     Delay(Seconds),
41 }
42 
43 impl RetryAfter {
44     /// Create an `RetryAfter` header with a date value.
date(time: SystemTime) -> RetryAfter45     pub fn date(time: SystemTime) -> RetryAfter {
46         RetryAfter(After::DateTime(time.into()))
47     }
48 
49     /// Create an `RetryAfter` header with a date value.
delay(dur: Duration) -> RetryAfter50     pub fn delay(dur: Duration) -> RetryAfter {
51         RetryAfter(After::Delay(dur.into()))
52     }
53 }
54 
55 impl TryFromValues for After {
try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error> where I: Iterator<Item = &'i HeaderValue>,56     fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
57     where
58         I: Iterator<Item = &'i HeaderValue>,
59     {
60         values
61             .next()
62             .and_then(|val| {
63                 if let Some(delay) = Seconds::from_val(val) {
64                     return Some(After::Delay(delay));
65                 }
66 
67                 let date = HttpDate::from_val(val)?;
68                 Some(After::DateTime(date))
69             })
70             .ok_or_else(::Error::invalid)
71     }
72 }
73 
74 impl<'a> From<&'a After> for HeaderValue {
from(after: &'a After) -> HeaderValue75     fn from(after: &'a After) -> HeaderValue {
76         match *after {
77             After::Delay(ref delay) => delay.into(),
78             After::DateTime(ref date) => date.into(),
79         }
80     }
81 }
82 
83 #[cfg(test)]
84 mod tests {
85     use super::super::test_decode;
86     use super::RetryAfter;
87     use std::time::Duration;
88     use util::HttpDate;
89 
90     #[test]
delay_decode()91     fn delay_decode() {
92         let r: RetryAfter = test_decode(&["1234"]).unwrap();
93         assert_eq!(r, RetryAfter::delay(Duration::from_secs(1234)),);
94     }
95 
96     macro_rules! test_retry_after_datetime {
97         ($name:ident, $s:expr) => {
98             #[test]
99             fn $name() {
100                 let r: RetryAfter = test_decode(&[$s]).unwrap();
101                 let dt = "Sun, 06 Nov 1994 08:49:37 GMT".parse::<HttpDate>().unwrap();
102 
103                 assert_eq!(r, RetryAfter(super::After::DateTime(dt)));
104             }
105         };
106     }
107 
108     test_retry_after_datetime!(date_decode_rfc1123, "Sun, 06 Nov 1994 08:49:37 GMT");
109     test_retry_after_datetime!(date_decode_rfc850, "Sunday, 06-Nov-94 08:49:37 GMT");
110     test_retry_after_datetime!(date_decode_asctime, "Sun Nov  6 08:49:37 1994");
111 }
112