hyper_sync/header/common/
retry_after.rs

1// Copyright (c) 2016 retry-after Developers
2//
3// This file is dual licensed under MIT and Apache 2.0
4//
5// *******************************************************
6//
7// Permission is hereby granted, free of charge, to any
8// person obtaining a copy of this software and associated
9// documentation files (the "Software"), to deal in the
10// Software without restriction, including without
11// limitation the rights to use, copy, modify, merge,
12// publish, distribute, sublicense, and/or sell copies of
13// the Software, and to permit persons to whom the Software
14// is furnished to do so, subject to the following
15//
16// conditions:
17//
18// The above copyright notice and this permission notice
19// shall be included in all copies or substantial portions
20// of the Software.
21//
22// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
23// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
24// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
25// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
26// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
27// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
29// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30// DEALINGS IN THE SOFTWARE.
31//
32// *******************************************************
33//
34// Apache License
35// Version 2.0, January 2004
36// http://www.apache.org/licenses/
37
38use std::fmt;
39use std::time::Duration;
40
41use header::{Header, Raw};
42use header::shared::HttpDate;
43
44/// The `Retry-After` header.
45///
46/// The `Retry-After` response-header field can be used with a 503 (Service
47/// Unavailable) response to indicate how long the service is expected to be
48/// unavailable to the requesting client. This field MAY also be used with any
49/// 3xx (Redirection) response to indicate the minimum time the user-agent is
50/// asked wait before issuing the redirected request. The value of this field
51/// can be either an HTTP-date or an integer number of seconds (in decimal)
52/// after the time of the response.
53///
54/// # Examples
55/// ```
56/// use std::time::Duration;
57/// use hyper_sync::header::{Headers, RetryAfter};
58///
59/// let mut headers = Headers::new();
60/// headers.set(
61///     RetryAfter::Delay(Duration::from_secs(300))
62/// );
63/// ```
64/// ```
65/// use std::time::{SystemTime, Duration};
66/// use hyper_sync::header::{Headers, RetryAfter};
67///
68/// let mut headers = Headers::new();
69/// let date = SystemTime::now() + Duration::from_secs(300);
70/// headers.set(
71///     RetryAfter::DateTime(date.into())
72/// );
73/// ```
74
75/// Retry-After header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.3)
76#[derive(Debug, Copy, Clone, PartialEq, Eq)]
77pub enum RetryAfter {
78    /// Retry after this duration has elapsed
79    ///
80    /// This can be coupled with a response time header to produce a DateTime.
81    Delay(Duration),
82
83    /// Retry after the given DateTime
84    DateTime(HttpDate),
85}
86
87impl Header for RetryAfter {
88    fn header_name() -> &'static str {
89        static NAME: &'static str = "Retry-After";
90        NAME
91    }
92
93    fn parse_header(raw: &Raw) -> ::Result<RetryAfter> {
94        if let Some(ref line) = raw.one() {
95            let utf8_str = match ::std::str::from_utf8(line) {
96                Ok(utf8_str) => utf8_str,
97                Err(_) => return Err(::Error::Header),
98            };
99
100            if let Ok(datetime) = utf8_str.parse::<HttpDate>() {
101                return Ok(RetryAfter::DateTime(datetime))
102            }
103
104            if let Ok(seconds) = utf8_str.parse::<u64>() {
105                return Ok(RetryAfter::Delay(Duration::from_secs(seconds)));
106            }
107
108            Err(::Error::Header)
109        } else {
110            Err(::Error::Header)
111        }
112    }
113
114    fn fmt_header(&self, f: &mut ::header::Formatter) -> ::std::fmt::Result {
115        f.fmt_line(self)
116    }
117}
118
119impl fmt::Display for RetryAfter {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        match *self {
122            RetryAfter::Delay(ref duration) => {
123                write!(f, "{}", duration.as_secs())
124            },
125            RetryAfter::DateTime(ref datetime) => {
126                fmt::Display::fmt(datetime, f)
127            }
128        }
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use std::time::Duration;
135    use header::Header;
136    use header::shared::HttpDate;
137
138    use super::RetryAfter;
139
140    #[test]
141    fn header_name_regression() {
142        assert_eq!(RetryAfter::header_name(), "Retry-After");
143    }
144
145    #[test]
146    fn parse_delay() {
147        let retry_after = RetryAfter::parse_header(&vec![b"1234".to_vec()].into()).unwrap();
148
149        assert_eq!(RetryAfter::Delay(Duration::from_secs(1234)), retry_after);
150    }
151
152    macro_rules! test_retry_after_datetime {
153        ($name:ident, $bytes:expr) => {
154            #[test]
155            fn $name() {
156                let dt = "Sun, 06 Nov 1994 08:49:37 GMT".parse::<HttpDate>().unwrap();
157                let retry_after = RetryAfter::parse_header(&vec![$bytes.to_vec()].into()).expect("parse_header ok");
158
159                assert_eq!(RetryAfter::DateTime(dt), retry_after);
160            }
161        }
162    }
163
164    test_retry_after_datetime!(header_parse_rfc1123, b"Sun, 06 Nov 1994 08:49:37 GMT");
165    test_retry_after_datetime!(header_parse_rfc850, b"Sunday, 06-Nov-94 08:49:37 GMT");
166    test_retry_after_datetime!(header_parse_asctime, b"Sun Nov  6 08:49:37 1994");
167
168    #[test]
169    fn hyper_headers_from_raw_delay() {
170        let retry_after = RetryAfter::parse_header(&b"300".to_vec().into()).unwrap();
171        assert_eq!(retry_after, RetryAfter::Delay(Duration::from_secs(300)));
172    }
173
174    #[test]
175    fn hyper_headers_from_raw_datetime() {
176        let retry_after = RetryAfter::parse_header(&b"Sun, 06 Nov 1994 08:49:37 GMT".to_vec().into()).unwrap();
177        let expected = "Sun, 06 Nov 1994 08:49:37 GMT".parse::<HttpDate>().unwrap();
178
179        assert_eq!(retry_after, RetryAfter::DateTime(expected));
180    }
181}