actix_http/header/shared/
http_date.rs1use 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#[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 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 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}