Merge pull request #165 from hyperium/expires
Adds CacheControl, Expires, and LastModified headers
This commit is contained in:
		
							
								
								
									
										165
									
								
								src/header/common/cache_control.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								src/header/common/cache_control.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | |||||||
|  | use std::fmt; | ||||||
|  | use std::str::FromStr; | ||||||
|  | use header::{Header, HeaderFormat}; | ||||||
|  | use super::util::{from_one_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
|  | /// The Cache-Control header. | ||||||
|  | #[deriving(PartialEq, Clone, Show)] | ||||||
|  | pub struct CacheControl(pub Vec<CacheDirective>); | ||||||
|  |  | ||||||
|  | deref!(CacheControl -> Vec<CacheDirective>) | ||||||
|  |  | ||||||
|  | impl Header for CacheControl { | ||||||
|  |     fn header_name(_: Option<CacheControl>) -> &'static str { | ||||||
|  |         "Cache-Control" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn parse_header(raw: &[Vec<u8>]) -> Option<CacheControl> { | ||||||
|  |         let directives = raw.iter() | ||||||
|  |             .filter_map(|line| from_one_comma_delimited(line[])) | ||||||
|  |             .collect::<Vec<Vec<CacheDirective>>>() | ||||||
|  |             .concat_vec(); | ||||||
|  |         if directives.len() > 0 { | ||||||
|  |             Some(CacheControl(directives)) | ||||||
|  |         } else { | ||||||
|  |             None | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl HeaderFormat for CacheControl { | ||||||
|  |     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         fmt_comma_delimited(fmt, self[]) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// CacheControl contains a list of these directives. | ||||||
|  | #[deriving(PartialEq, Clone)] | ||||||
|  | pub enum CacheDirective { | ||||||
|  |     /// "no-cache" | ||||||
|  |     NoCache, | ||||||
|  |     /// "no-store" | ||||||
|  |     NoStore, | ||||||
|  |     /// "no-transform" | ||||||
|  |     NoTransform, | ||||||
|  |     /// "only-if-cached" | ||||||
|  |     OnlyIfCached, | ||||||
|  |  | ||||||
|  |     // request directives | ||||||
|  |     /// "max-age=delta" | ||||||
|  |     MaxAge(uint), | ||||||
|  |     /// "max-stale=delta" | ||||||
|  |     MaxStale(uint), | ||||||
|  |     /// "min-fresh=delta" | ||||||
|  |     MinFresh(uint), | ||||||
|  |  | ||||||
|  |     // response directives | ||||||
|  |     /// "must-revalidate" | ||||||
|  |     MustRevalidate, | ||||||
|  |     /// "public" | ||||||
|  |     Public, | ||||||
|  |     /// "private" | ||||||
|  |     Private, | ||||||
|  |     /// "proxy-revalidate" | ||||||
|  |     ProxyRevalidate, | ||||||
|  |     /// "s-maxage=delta" | ||||||
|  |     SMaxAge(uint), | ||||||
|  |  | ||||||
|  |     /// Extension directives. Optionally include an argument. | ||||||
|  |     Extension(String, Option<String>) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl fmt::Show for CacheDirective { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         use self::CacheDirective::*; | ||||||
|  |         match *self { | ||||||
|  |             NoCache => "no-cache", | ||||||
|  |             NoStore => "no-store", | ||||||
|  |             NoTransform => "no-transform", | ||||||
|  |             OnlyIfCached => "only-if-cached", | ||||||
|  |  | ||||||
|  |             MaxAge(secs) => return write!(f, "max-age={}", secs), | ||||||
|  |             MaxStale(secs) => return write!(f, "max-stale={}", secs), | ||||||
|  |             MinFresh(secs) => return write!(f, "min-fresh={}", secs), | ||||||
|  |  | ||||||
|  |             MustRevalidate => "must-revalidate", | ||||||
|  |             Public => "public", | ||||||
|  |             Private => "private", | ||||||
|  |             ProxyRevalidate => "proxy-revalidate", | ||||||
|  |             SMaxAge(secs) => return write!(f, "s-maxage={}", secs), | ||||||
|  |  | ||||||
|  |             Extension(ref name, None) => name[], | ||||||
|  |             Extension(ref name, Some(ref arg)) => return write!(f, "{}={}", name, arg), | ||||||
|  |  | ||||||
|  |         }.fmt(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl FromStr for CacheDirective { | ||||||
|  |     fn from_str(s: &str) -> Option<CacheDirective> { | ||||||
|  |         use self::CacheDirective::*; | ||||||
|  |         match s { | ||||||
|  |             "no-cache" => Some(NoCache), | ||||||
|  |             "no-store" => Some(NoStore), | ||||||
|  |             "no-transform" => Some(NoTransform), | ||||||
|  |             "only-if-cached" => Some(OnlyIfCached), | ||||||
|  |             "must-revalidate" => Some(MustRevalidate), | ||||||
|  |             "public" => Some(Public), | ||||||
|  |             "private" => Some(Private), | ||||||
|  |             "proxy-revalidate" => Some(ProxyRevalidate), | ||||||
|  |             "" => None, | ||||||
|  |             _ => match s.find('=') { | ||||||
|  |                 Some(idx) if idx+1 < s.len() => match (s[..idx], s[idx+1..].trim_chars('"')) { | ||||||
|  |                     ("max-age" , secs) => from_str::<uint>(secs).map(MaxAge), | ||||||
|  |                     ("max-stale", secs) => from_str::<uint>(secs).map(MaxStale), | ||||||
|  |                     ("min-fresh", secs) => from_str::<uint>(secs).map(MinFresh), | ||||||
|  |                     ("s-maxage", secs) => from_str::<uint>(secs).map(SMaxAge), | ||||||
|  |                     (left, right) => Some(Extension(left.into_string(), Some(right.into_string()))) | ||||||
|  |                 }, | ||||||
|  |                 Some(_) => None, | ||||||
|  |                 None => Some(Extension(s.into_string(), None)) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use header::Header; | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_parse_multiple_headers() { | ||||||
|  |         let cache = Header::parse_header(&[b"no-cache".to_vec(), b"private".to_vec()]); | ||||||
|  |         assert_eq!(cache, Some(CacheControl(vec![CacheDirective::NoCache, | ||||||
|  |                                                  CacheDirective::Private]))) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_parse_argument() { | ||||||
|  |         let cache = Header::parse_header(&[b"max-age=100, private".to_vec()]); | ||||||
|  |         assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(100), | ||||||
|  |                                                  CacheDirective::Private]))) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_parse_quote_form() { | ||||||
|  |         let cache = Header::parse_header(&[b"max-age=\"200\"".to_vec()]); | ||||||
|  |         assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(200)]))) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_parse_extension() { | ||||||
|  |         let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]); | ||||||
|  |         assert_eq!(cache, Some(CacheControl(vec![CacheDirective::Extension("foo".to_string(), None), | ||||||
|  |                                                  CacheDirective::Extension("bar".to_string(), Some("baz".to_string()))]))) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_parse_bad_syntax() { | ||||||
|  |         let cache: Option<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]); | ||||||
|  |         assert_eq!(cache, None) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bench_header!(normal, CacheControl, { vec![b"no-cache, private".to_vec(), b"max-age=100".to_vec()] }) | ||||||
| @@ -1,8 +1,8 @@ | |||||||
| use header::{Header, HeaderFormat}; |  | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
| use super::util::from_one_raw_str; |  | ||||||
| use std::str::FromStr; | 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. | // Egh, replace as soon as something better than time::Tm exists. | ||||||
| /// The `Date` header field. | /// The `Date` header field. | ||||||
| @@ -21,17 +21,10 @@ impl Header for Date { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl HeaderFormat for Date { | impl HeaderFormat for Date { | ||||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         self.fmt(fmt) |         let tm = **self; | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
|         match tm.tm_utcoff { |         match tm.tm_utcoff { | ||||||
|             0 => tm.rfc822().fmt(fmt), |             0 => tm.rfc822().fmt(fmt), | ||||||
|             _ => tm.to_utc().rfc822().fmt(fmt) |             _ => tm.to_utc().rfc822().fmt(fmt) | ||||||
| @@ -40,34 +33,8 @@ impl fmt::Show for Date { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl FromStr 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<Date> { |     fn from_str(s: &str) -> Option<Date> { | ||||||
|         strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| { |         tm_from_str(s).map(Date) | ||||||
|             strptime(s, "%A, %d-%b-%y %T %Z") |  | ||||||
|         }).or_else(|_| { |  | ||||||
|             strptime(s, "%c") |  | ||||||
|         }).ok().map(|tm| Date(tm)) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								src/header/common/expires.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/header/common/expires.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Expires>) -> &'static str { | ||||||
|  |         "Expires" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn parse_header(raw: &[Vec<u8>]) -> Option<Expires> { | ||||||
|  |         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<Expires> { | ||||||
|  |         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()] }) | ||||||
							
								
								
									
										42
									
								
								src/header/common/last_modified.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/header/common/last_modified.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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 `LastModified` header field. | ||||||
|  | #[deriving(PartialEq, Clone)] | ||||||
|  | pub struct LastModified(pub Tm); | ||||||
|  |  | ||||||
|  | deref!(LastModified -> Tm) | ||||||
|  |  | ||||||
|  | impl Header for LastModified { | ||||||
|  |     fn header_name(_: Option<LastModified>) -> &'static str { | ||||||
|  |         "Last-Modified" | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn parse_header(raw: &[Vec<u8>]) -> Option<LastModified> { | ||||||
|  |         from_one_raw_str(raw) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | impl HeaderFormat for LastModified { | ||||||
|  |     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 LastModified { | ||||||
|  |     fn from_str(s: &str) -> Option<LastModified> { | ||||||
|  |         tm_from_str(s).map(LastModified) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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()] }) | ||||||
|  | bench_header!(asctime, LastModified, { vec![b"Sun Nov  6 08:49:37 1994".to_vec()] }) | ||||||
| @@ -8,12 +8,15 @@ | |||||||
|  |  | ||||||
| pub use self::accept::Accept; | pub use self::accept::Accept; | ||||||
| pub use self::authorization::Authorization; | pub use self::authorization::Authorization; | ||||||
|  | pub use self::cache_control::CacheControl; | ||||||
| pub use self::cookie::Cookies; | pub use self::cookie::Cookies; | ||||||
| pub use self::connection::Connection; | pub use self::connection::Connection; | ||||||
| pub use self::content_length::ContentLength; | pub use self::content_length::ContentLength; | ||||||
| pub use self::content_type::ContentType; | pub use self::content_type::ContentType; | ||||||
| pub use self::date::Date; | pub use self::date::Date; | ||||||
|  | pub use self::expires::Expires; | ||||||
| pub use self::host::Host; | pub use self::host::Host; | ||||||
|  | pub use self::last_modified::LastModified; | ||||||
| pub use self::location::Location; | pub use self::location::Location; | ||||||
| pub use self::transfer_encoding::TransferEncoding; | pub use self::transfer_encoding::TransferEncoding; | ||||||
| pub use self::upgrade::Upgrade; | pub use self::upgrade::Upgrade; | ||||||
| @@ -72,6 +75,9 @@ pub mod accept; | |||||||
| /// Exposes the Authorization header. | /// Exposes the Authorization header. | ||||||
| pub mod authorization; | pub mod authorization; | ||||||
|  |  | ||||||
|  | /// Exposes the CacheControl header. | ||||||
|  | pub mod cache_control; | ||||||
|  |  | ||||||
| /// Exposes the Cookie header. | /// Exposes the Cookie header. | ||||||
| pub mod cookie; | pub mod cookie; | ||||||
|  |  | ||||||
| @@ -87,9 +93,15 @@ pub mod content_type; | |||||||
| /// Exposes the Date header. | /// Exposes the Date header. | ||||||
| pub mod date; | pub mod date; | ||||||
|  |  | ||||||
|  | /// Exposes the Expires header. | ||||||
|  | pub mod expires; | ||||||
|  |  | ||||||
| /// Exposes the Host header. | /// Exposes the Host header. | ||||||
| pub mod host; | pub mod host; | ||||||
|  |  | ||||||
|  | /// Exposes the LastModified header. | ||||||
|  | pub mod last_modified; | ||||||
|  |  | ||||||
| /// Exposes the Location header. | /// Exposes the Location header. | ||||||
| pub mod location; | pub mod location; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -77,14 +77,13 @@ impl Header for TransferEncoding { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<TransferEncoding> { |     fn parse_header(raw: &[Vec<u8>]) -> Option<TransferEncoding> { | ||||||
|         from_comma_delimited(raw).map(|vec| TransferEncoding(vec)) |         from_comma_delimited(raw).map(TransferEncoding) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl HeaderFormat for TransferEncoding { | impl HeaderFormat for TransferEncoding { | ||||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         let TransferEncoding(ref parts) = *self; |         fmt_comma_delimited(fmt, self[]) | ||||||
|         fmt_comma_delimited(fmt, parts[]) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| use std::str::{FromStr, from_utf8}; | use std::str::{FromStr, from_utf8}; | ||||||
| use std::fmt::{mod, Show}; | use std::fmt::{mod, Show}; | ||||||
|  | use time::{Tm, strptime}; | ||||||
|  |  | ||||||
| /// Reads a single raw string when parsing a header | /// Reads a single raw string when parsing a header | ||||||
| pub fn from_one_raw_str<T: FromStr>(raw: &[Vec<u8>]) -> Option<T> { | pub fn from_one_raw_str<T: FromStr>(raw: &[Vec<u8>]) -> Option<T> { | ||||||
| @@ -15,13 +16,19 @@ pub fn from_one_raw_str<T: FromStr>(raw: &[Vec<u8>]) -> Option<T> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Reads a comma-delimited raw string into a Vec. | /// Reads a comma-delimited raw header into a Vec. | ||||||
|  | #[inline] | ||||||
| pub fn from_comma_delimited<T: FromStr>(raw: &[Vec<u8>]) -> Option<Vec<T>> { | pub fn from_comma_delimited<T: FromStr>(raw: &[Vec<u8>]) -> Option<Vec<T>> { | ||||||
|     if raw.len() != 1 { |     if raw.len() != 1 { | ||||||
|         return None; |         return None; | ||||||
|     } |     } | ||||||
|     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. |     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. | ||||||
|     match from_utf8(unsafe { raw.as_slice().unsafe_get(0).as_slice() }) { |     from_one_comma_delimited(unsafe { raw.as_slice().unsafe_get(0).as_slice() }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Reads a comma-delimited raw string into a Vec. | ||||||
|  | pub fn from_one_comma_delimited<T: FromStr>(raw: &[u8]) -> Option<Vec<T>> { | ||||||
|  |     match from_utf8(raw) { | ||||||
|         Some(s) => { |         Some(s) => { | ||||||
|             Some(s.as_slice() |             Some(s.as_slice() | ||||||
|                  .split([',', ' '].as_slice()) |                  .split([',', ' '].as_slice()) | ||||||
| @@ -43,3 +50,34 @@ pub fn fmt_comma_delimited<T: Show>(fmt: &mut fmt::Formatter, parts: &[T]) -> fm | |||||||
|     } |     } | ||||||
|     Ok(()) |     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<Tm> { | ||||||
|  |     strptime(s, "%a, %d %b %Y %T %Z").or_else(|_| { | ||||||
|  |         strptime(s, "%A, %d-%b-%y %T %Z") | ||||||
|  |     }).or_else(|_| { | ||||||
|  |         strptime(s, "%c") | ||||||
|  |     }).ok() | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user