diff --git a/src/header/common/date.rs b/src/header/common/date.rs index b9e6d97b..47ca75da 100644 --- a/src/header/common/date.rs +++ b/src/header/common/date.rs @@ -1,45 +1,10 @@ -use std::fmt; -use std::str::FromStr; -use time::Tm; -use header::{Header, HeaderFormat}; -use header::parsing::from_one_raw_str; -use header::parsing::tm_from_str; +use header::HttpDate; -// Egh, replace as soon as something better than time::Tm exists. /// The `Date` header field. #[derive(Copy, PartialEq, Clone, Debug)] -pub struct Date(pub Tm); +pub struct Date(pub HttpDate); -deref!(Date => Tm); - -impl Header for Date { - fn header_name() -> &'static str { - "Date" - } - - fn parse_header(raw: &[Vec]) -> Option { - from_one_raw_str(raw) - } -} - - -impl HeaderFormat for Date { - fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let tm = self.0; - let tm = match tm.tm_utcoff { - 0 => tm, - _ => tm.to_utc(), - }; - fmt::Display::fmt(&tm.rfc822(), fmt) - } -} - -impl FromStr for Date { - type Err = (); - fn from_str(s: &str) -> Result { - tm_from_str(s).map(Date).ok_or(()) - } -} +impl_header!(Date, "Date", HttpDate); bench_header!(imf_fixdate, Date, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, Date, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/expires.rs b/src/header/common/expires.rs index 8969bfdb..57819180 100644 --- a/src/header/common/expires.rs +++ b/src/header/common/expires.rs @@ -1,44 +1,9 @@ -use std::fmt; -use std::str::FromStr; -use time::Tm; -use header::{Header, HeaderFormat}; -use header::parsing::from_one_raw_str; -use header::parsing::tm_from_str; +use header::HttpDate; /// The `Expires` header field. #[derive(Copy, PartialEq, Clone, Debug)] -pub struct Expires(pub Tm); - -deref!(Expires => Tm); - -impl Header for Expires { - fn header_name() -> &'static str { - "Expires" - } - - fn parse_header(raw: &[Vec]) -> Option { - from_one_raw_str(raw) - } -} - - -impl HeaderFormat for Expires { - fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let tm = self.0; - let tm = match tm.tm_utcoff { - 0 => tm, - _ => tm.to_utc(), - }; - fmt::Display::fmt(&tm.rfc822(), fmt) - } -} - -impl FromStr for Expires { - type Err = (); - fn from_str(s: &str) -> Result { - tm_from_str(s).map(Expires).ok_or(()) - } -} +pub struct Expires(pub HttpDate); +impl_header!(Expires, "Expires", HttpDate); bench_header!(imf_fixdate, Expires, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, Expires, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/if_modified_since.rs b/src/header/common/if_modified_since.rs index 11edfc9e..53dae88a 100644 --- a/src/header/common/if_modified_since.rs +++ b/src/header/common/if_modified_since.rs @@ -1,44 +1,9 @@ -use std::fmt; -use std::str::FromStr; -use time::Tm; -use header::{Header, HeaderFormat}; -use header::parsing::from_one_raw_str; -use header::parsing::tm_from_str; +use header::HttpDate; /// The `If-Modified-Since` header field. #[derive(Copy, PartialEq, Clone, Debug)] -pub struct IfModifiedSince(pub Tm); - -deref!(IfModifiedSince => Tm); - -impl Header for IfModifiedSince { - fn header_name() -> &'static str { - "If-Modified-Since" - } - - fn parse_header(raw: &[Vec]) -> Option { - from_one_raw_str(raw) - } -} - - -impl HeaderFormat for IfModifiedSince { - fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let tm = self.0; - let tm = match tm.tm_utcoff { - 0 => tm, - _ => tm.to_utc(), - }; - fmt::Display::fmt(&tm.rfc822(), fmt) - } -} - -impl FromStr for IfModifiedSince { - type Err = (); - fn from_str(s: &str) -> Result { - tm_from_str(s).map(IfModifiedSince).ok_or(()) - } -} +pub struct IfModifiedSince(pub HttpDate); +impl_header!(IfModifiedSince, "If-Modified-Since", HttpDate); bench_header!(imf_fixdate, IfModifiedSince, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, IfModifiedSince, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/if_unmodified_since.rs b/src/header/common/if_unmodified_since.rs index 92039301..88d0f481 100644 --- a/src/header/common/if_unmodified_since.rs +++ b/src/header/common/if_unmodified_since.rs @@ -1,44 +1,10 @@ -use std::fmt; -use std::str::FromStr; -use time::Tm; -use header::{Header, HeaderFormat}; -use header::parsing::from_one_raw_str; -use header::parsing::tm_from_str; +use header::HttpDate; /// The `If-Unmodified-Since` header field. #[derive(Copy, PartialEq, Clone, Debug)] -pub struct IfUnmodifiedSince(pub Tm); +pub struct IfUnmodifiedSince(pub HttpDate); -deref!(IfUnmodifiedSince => Tm); - -impl Header for IfUnmodifiedSince { - fn header_name() -> &'static str { - "If-Unmodified-Since" - } - - fn parse_header(raw: &[Vec]) -> Option { - from_one_raw_str(raw) - } -} - - -impl HeaderFormat for IfUnmodifiedSince { - fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let tm = self.0; - let tm = match tm.tm_utcoff { - 0 => tm, - _ => tm.to_utc(), - }; - fmt::Display::fmt(&tm.rfc822(), fmt) - } -} - -impl FromStr for IfUnmodifiedSince { - type Err = (); - fn from_str(s: &str) -> Result { - tm_from_str(s).map(IfUnmodifiedSince).ok_or(()) - } -} +impl_header!(IfUnmodifiedSince, "If-Unmodified-Since", HttpDate); bench_header!(imf_fixdate, IfUnmodifiedSince, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, IfUnmodifiedSince, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/last_modified.rs b/src/header/common/last_modified.rs index ae0aafae..5b90dce6 100644 --- a/src/header/common/last_modified.rs +++ b/src/header/common/last_modified.rs @@ -1,44 +1,10 @@ -use std::fmt; -use std::str::FromStr; -use time::Tm; -use header::{Header, HeaderFormat}; -use header::parsing::from_one_raw_str; -use header::parsing::tm_from_str; +use header::HttpDate; /// The `LastModified` header field. #[derive(Copy, PartialEq, Clone, Debug)] -pub struct LastModified(pub Tm); +pub struct LastModified(pub HttpDate); -deref!(LastModified => Tm); - -impl Header for LastModified { - fn header_name() -> &'static str { - "Last-Modified" - } - - fn parse_header(raw: &[Vec]) -> Option { - from_one_raw_str(raw) - } -} - - -impl HeaderFormat for LastModified { - fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let tm = self.0; - let tm = match tm.tm_utcoff { - 0 => tm, - _ => tm.to_utc(), - }; - fmt::Display::fmt(&tm.rfc822(), fmt) - } -} - -impl FromStr for LastModified { - type Err = (); - fn from_str(s: &str) -> Result { - tm_from_str(s).map(LastModified).ok_or(()) - } -} +impl_header!(LastModified, "Last-Modified", HttpDate); bench_header!(imf_fixdate, LastModified, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, LastModified, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/mod.rs b/src/header/mod.rs index 49a25dd8..1f694795 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -21,7 +21,7 @@ use unicase::UniCase; use self::internals::Item; use error::HttpResult; -pub use self::shared::{Charset, Encoding, EntityTag, Quality, QualityItem, qitem, q}; +pub use self::shared::{Charset, Encoding, EntityTag, HttpDate, Quality, QualityItem, qitem, q}; pub use self::common::*; mod common; diff --git a/src/header/parsing.rs b/src/header/parsing.rs index 9337cda2..f1af8661 100644 --- a/src/header/parsing.rs +++ b/src/header/parsing.rs @@ -2,7 +2,6 @@ use std::str; use std::fmt; -use time; /// Reads a single raw string when parsing a header pub fn from_one_raw_str(raw: &[Vec]) -> Option { @@ -51,74 +50,3 @@ pub fn fmt_comma_delimited(fmt: &mut fmt::Formatter, parts: &[T } Ok(()) } - -/// Get a Tm from HTTP date formats. -// Prior to 1995, there were three different formats commonly used by -// servers to communicate timestamps. For compatibility with old -// implementations, all three are defined here. The preferred format is -// a fixed-length and single-zone subset of the date and time -// specification used by the Internet Message Format [RFC5322]. -// -// HTTP-date = IMF-fixdate / obs-date -// -// An example of the preferred format is -// -// Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate -// -// Examples of the two obsolete formats are -// -// Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format -// Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format -// -// A recipient that parses a timestamp value in an HTTP header field -// MUST accept all three HTTP-date formats. When a sender generates a -// header field that contains one or more timestamps defined as -// HTTP-date, the sender MUST generate those timestamps in the -// IMF-fixdate format. -pub fn tm_from_str(s: &str) -> Option { - time::strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| { - time::strptime(s, "%A, %d-%b-%y %T %Z") - }).or_else(|_| { - time::strptime(s, "%c") - }).ok() -} - -#[cfg(test)] -mod tests { - use time::Tm; - use super::tm_from_str; - - const NOV_07: Tm = Tm { - tm_nsec: 0, - tm_sec: 37, - tm_min: 48, - tm_hour: 8, - tm_mday: 7, - tm_mon: 10, - tm_year: 94, - tm_wday: 0, - tm_isdst: 0, - tm_yday: 0, - tm_utcoff: 0, - }; - - #[test] - fn test_imf_fixdate() { - assert_eq!(tm_from_str("Sun, 07 Nov 1994 08:48:37 GMT"), - Some(NOV_07)); - } - - #[test] - fn test_rfc_850() { - assert_eq!(tm_from_str("Sunday, 07-Nov-94 08:48:37 GMT"), - Some(NOV_07)); - } - - #[test] - fn test_asctime() { - assert_eq!(tm_from_str("Sun Nov 7 08:48:37 1994"), - Some(NOV_07)); - } - - -} diff --git a/src/header/shared/httpdate.rs b/src/header/shared/httpdate.rs new file mode 100644 index 00000000..a61cd329 --- /dev/null +++ b/src/header/shared/httpdate.rs @@ -0,0 +1,91 @@ +use std::str::FromStr; +use std::fmt::{self, Display}; + +use time; + +/// A `time::Time` with HTTP formatting and parsing +/// +// Prior to 1995, there were three different formats commonly used by +// servers to communicate timestamps. For compatibility with old +// implementations, all three are defined here. The preferred format is +// a fixed-length and single-zone subset of the date and time +// specification used by the Internet Message Format [RFC5322]. +// +// HTTP-date = IMF-fixdate / obs-date +// +// An example of the preferred format is +// +// Sun, 06 Nov 1994 08:49:37 GMT ; IMF-fixdate +// +// Examples of the two obsolete formats are +// +// Sunday, 06-Nov-94 08:49:37 GMT ; obsolete RFC 850 format +// Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format +// +// A recipient that parses a timestamp value in an HTTP header field +// MUST accept all three HTTP-date formats. When a sender generates a +// header field that contains one or more timestamps defined as +// HTTP-date, the sender MUST generate those timestamps in the +// IMF-fixdate format. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct HttpDate(pub time::Tm); + +impl FromStr for HttpDate { + type Err = (); + fn from_str(s: &str) -> Result { + match time::strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| { + time::strptime(s, "%A, %d-%b-%y %T %Z") + }).or_else(|_| { + time::strptime(s, "%c") + }) { + Ok(t) => Ok(HttpDate(t)), + Err(_) => Err(()), + } + } +} + +impl Display for HttpDate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let tm = self.0; + let tm = match tm.tm_utcoff { + 0 => tm, + _ => tm.to_utc(), + }; + fmt::Display::fmt(&tm.rfc822(), f) + } +} + +#[cfg(test)] +mod tests { + use time::Tm; + use super::HttpDate; + + const NOV_07: HttpDate = HttpDate(Tm { + tm_nsec: 0, + tm_sec: 37, + tm_min: 48, + tm_hour: 8, + tm_mday: 7, + tm_mon: 10, + tm_year: 94, + tm_wday: 0, + tm_isdst: 0, + tm_yday: 0, + tm_utcoff: 0, + }); + + #[test] + fn test_imf_fixdate() { + assert_eq!("Sun, 07 Nov 1994 08:48:37 GMT".parse(), Ok(NOV_07)); + } + + #[test] + fn test_rfc_850() { + assert_eq!("Sunday, 07-Nov-94 08:48:37 GMT".parse(), Ok(NOV_07)); + } + + #[test] + fn test_asctime() { + assert_eq!("Sun Nov 7 08:48:37 1994".parse(), Ok(NOV_07)); + } +} diff --git a/src/header/shared/mod.rs b/src/header/shared/mod.rs index 8af5f065..d2c2355b 100644 --- a/src/header/shared/mod.rs +++ b/src/header/shared/mod.rs @@ -1,9 +1,11 @@ pub use self::charset::Charset; pub use self::encoding::Encoding; pub use self::entity::EntityTag; +pub use self::httpdate::HttpDate; pub use self::quality_item::{Quality, QualityItem, qitem, q}; mod charset; mod encoding; mod entity; +mod httpdate; mod quality_item; diff --git a/src/server/response.rs b/src/server/response.rs index 91b9450c..c23efb32 100644 --- a/src/server/response.rs +++ b/src/server/response.rs @@ -75,7 +75,7 @@ impl<'a> Response<'a, Fresh> { try!(write!(&mut self.body, "{} {}{}{}", self.version, self.status, CR as char, LF as char)); if !self.headers.has::() { - self.headers.set(header::Date(now_utc())); + self.headers.set(header::Date(header::HttpDate(now_utc()))); }