diff --git a/src/header/common/date.rs b/src/header/common/date.rs index ffa0a548..7a283c0e 100644 --- a/src/header/common/date.rs +++ b/src/header/common/date.rs @@ -1,8 +1,8 @@ -use header::{Header, HeaderFormat}; use std::fmt::{mod, Show}; -use super::util::from_one_raw_str; use std::str::FromStr; -use time::{Tm, strptime}; +use time::Tm; +use header::{Header, HeaderFormat}; +use super::util::{from_one_raw_str, tm_from_str}; // Egh, replace as soon as something better than time::Tm exists. /// The `Date` header field. @@ -21,17 +21,10 @@ impl Header for Date { } } + impl HeaderFormat for Date { fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - self.fmt(fmt) - } -} - -impl fmt::Show for Date { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let Date(ref tm) = *self; - // bummer that tm.strftime allocates a string. It would nice if it - // returned a Show instead, since I don't need the String here + let tm = **self; match tm.tm_utcoff { 0 => tm.rfc822().fmt(fmt), _ => tm.to_utc().rfc822().fmt(fmt) @@ -40,34 +33,8 @@ impl fmt::Show for Date { } impl FromStr for Date { - // 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. fn from_str(s: &str) -> Option { - strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| { - strptime(s, "%A, %d-%b-%y %T %Z") - }).or_else(|_| { - strptime(s, "%c") - }).ok().map(|tm| Date(tm)) + tm_from_str(s).map(Date) } } diff --git a/src/header/common/expires.rs b/src/header/common/expires.rs new file mode 100644 index 00000000..b11e08fe --- /dev/null +++ b/src/header/common/expires.rs @@ -0,0 +1,42 @@ +use std::fmt::{mod, Show}; +use std::str::FromStr; +use time::Tm; +use header::{Header, HeaderFormat}; +use super::util::{from_one_raw_str, tm_from_str}; + +/// The `Expires` header field. +#[deriving(PartialEq, Clone)] +pub struct Expires(pub Tm); + +deref!(Expires -> Tm) + +impl Header for Expires { + fn header_name(_: Option) -> &'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; + match tm.tm_utcoff { + 0 => tm.rfc822().fmt(fmt), + _ => tm.to_utc().rfc822().fmt(fmt) + } + } +} + +impl FromStr for Expires { + fn from_str(s: &str) -> Option { + tm_from_str(s).map(Expires) + } +} + +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()] }) +bench_header!(asctime, Expires, { vec![b"Sun Nov 6 08:49:37 1994".to_vec()] }) diff --git a/src/header/common/mod.rs b/src/header/common/mod.rs index 664966d1..dd3a0d9f 100644 --- a/src/header/common/mod.rs +++ b/src/header/common/mod.rs @@ -96,6 +96,9 @@ pub mod content_type; /// Exposes the Date header. pub mod date; +/// Exposes the Expires header. +pub mod expires; + /// Exposes the Host header. pub mod host; diff --git a/src/header/common/util.rs b/src/header/common/util.rs index a6063975..0d4f60f9 100644 --- a/src/header/common/util.rs +++ b/src/header/common/util.rs @@ -2,6 +2,7 @@ use std::str::{FromStr, from_utf8}; use std::fmt::{mod, Show}; +use time::{Tm, strptime}; /// Reads a single raw string when parsing a header pub fn from_one_raw_str(raw: &[Vec]) -> Option { @@ -43,3 +44,34 @@ pub fn fmt_comma_delimited(fmt: &mut fmt::Formatter, parts: &[T]) -> fm } 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 { + strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| { + strptime(s, "%A, %d-%b-%y %T %Z") + }).or_else(|_| { + strptime(s, "%c") + }).ok() +}