actix_http/header/shared/
http_date.rs

1use std::{fmt, io::Write, str::FromStr, time::SystemTime};
2
3use bytes::BytesMut;
4use http::header::{HeaderValue, InvalidHeaderValue};
5
6use crate::{
7    date::DATE_VALUE_LENGTH, error::ParseError, header::TryIntoHeaderValue, helpers::MutWriter,
8};
9
10/// A timestamp with HTTP-style formatting and parsing.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
12pub struct HttpDate(SystemTime);
13
14impl FromStr for HttpDate {
15    type Err = ParseError;
16
17    fn from_str(s: &str) -> Result<HttpDate, ParseError> {
18        match httpdate::parse_http_date(s) {
19            Ok(sys_time) => Ok(HttpDate(sys_time)),
20            Err(_) => Err(ParseError::Header),
21        }
22    }
23}
24
25impl fmt::Display for HttpDate {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        httpdate::HttpDate::from(self.0).fmt(f)
28    }
29}
30
31impl TryIntoHeaderValue for HttpDate {
32    type Error = InvalidHeaderValue;
33
34    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
35        let mut buf = BytesMut::with_capacity(DATE_VALUE_LENGTH);
36        let mut wrt = MutWriter(&mut buf);
37
38        // unwrap: date output is known to be well formed and of known length
39        write!(wrt, "{}", self).unwrap();
40
41        HeaderValue::from_maybe_shared(buf.split().freeze())
42    }
43}
44
45impl From<SystemTime> for HttpDate {
46    fn from(sys_time: SystemTime) -> HttpDate {
47        HttpDate(sys_time)
48    }
49}
50
51impl From<HttpDate> for SystemTime {
52    fn from(HttpDate(sys_time): HttpDate) -> SystemTime {
53        sys_time
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use std::time::Duration;
60
61    use super::*;
62
63    #[test]
64    fn date_header() {
65        macro_rules! assert_parsed_date {
66            ($case:expr, $exp:expr) => {
67                assert_eq!($case.parse::<HttpDate>().unwrap(), $exp);
68            };
69        }
70
71        // 784198117 = SystemTime::from(datetime!(1994-11-07 08:48:37).assume_utc()).duration_since(SystemTime::UNIX_EPOCH));
72        let nov_07 = HttpDate(SystemTime::UNIX_EPOCH + Duration::from_secs(784198117));
73
74        assert_parsed_date!("Mon, 07 Nov 1994 08:48:37 GMT", nov_07);
75        assert_parsed_date!("Monday, 07-Nov-94 08:48:37 GMT", nov_07);
76        assert_parsed_date!("Mon Nov  7 08:48:37 1994", nov_07);
77
78        assert!("this-is-no-date".parse::<HttpDate>().is_err());
79    }
80}