Merge pull request #487 from pyfisch/headertests2
Bugfixes, tests and documentation for headers
This commit is contained in:
		| @@ -23,6 +23,10 @@ header! { | ||||
|     #[doc="accept-ext = OWS \";\" OWS token [ \"=\" ( token / quoted-string ) ]"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `audio/*; q=0.2, audio/basic` (`*` value won't parse correctly)"] | ||||
|     #[doc="* `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c`"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Notes"] | ||||
|     #[doc="* Using always Mime types to represent `media-range` differs from the ABNF."] | ||||
|     #[doc="* **FIXME**: `accept-ext` is not supported."] | ||||
|   | ||||
| @@ -15,9 +15,13 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Accept-Charset = 1#( ( charset / \"*\" ) [ weight ] )"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `iso-8859-5, unicode-1-1;q=0.8`"] | ||||
|     (AcceptCharset, "Accept-Charset") => (QualityItem<Charset>)+ | ||||
|  | ||||
|     test_accept_charset { | ||||
|         /// Testcase from RFC | ||||
|         test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -15,33 +15,23 @@ header! { | ||||
|     #[doc="Accept-Encoding  = #( codings [ weight ] )"] | ||||
|     #[doc="codings          = content-coding / \"identity\" / \"*\""] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `compress, gzip`"] | ||||
|     #[doc="* ``"] | ||||
|     #[doc="* `*`"] | ||||
|     #[doc="* `compress;q=0.5, gzip;q=1`"] | ||||
|     #[doc="* `gzip;q=1.0, identity; q=0.5, *;q=0`"] | ||||
|     (AcceptEncoding, "Accept-Encoding") => (QualityItem<Encoding>)* | ||||
|  | ||||
|     test_accept_encoding { | ||||
|         // From the RFC | ||||
|         test_header!(test1, vec![b"compress, gzip"]); | ||||
|         test_header!(test2, vec![b""]); | ||||
|         test_header!(test2, vec![b""], Some(AcceptEncoding(vec![]))); | ||||
|         test_header!(test3, vec![b"*"]); | ||||
|         // Note: Removed quality 1 from gzip | ||||
|         test_header!(test4, vec![b"compress;q=0.5, gzip"]); | ||||
|         // FIXME: Formatting of 0 as quality value | ||||
|         // test_header!(test5, vec![b"gzip;q=1.0, identity; q=0.5, *;q=0"]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use header::{Encoding, Header, qitem, Quality, QualityItem}; | ||||
|  | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_header() { | ||||
|         let a: AcceptEncoding = Header::parse_header([b"gzip;q=1.0, identity; q=0.5".to_vec()].as_ref()).unwrap(); | ||||
|         let b = AcceptEncoding(vec![ | ||||
|             qitem(Encoding::Gzip), | ||||
|             QualityItem::new(Encoding::Identity, Quality(500)), | ||||
|         ]); | ||||
|         assert_eq!(a, b); | ||||
|         // Note: Removed quality 1 from gzip | ||||
|         test_header!(test5, vec![b"gzip, identity; q=0.5, *;q=0"]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -13,49 +13,23 @@ header! { | ||||
|     #[doc="Accept-Language = 1#( language-range [ weight ] )"] | ||||
|     #[doc="language-range  = <language-range, see [RFC4647], Section 2.1>"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `da, en-gb;q=0.8, en;q=0.7`"] | ||||
|     #[doc="* `en-us;q=1.0, en;q=0.5, fr`"] | ||||
|     (AcceptLanguage, "Accept-Language") => (QualityItem<Language>)+ | ||||
|  | ||||
|     test_accept_language { | ||||
|         // From the RFC | ||||
|         test_header!(test1, vec![b"da, en-gb;q=0.8, en;q=0.7"]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use header::{Header, Language, qitem, Quality, QualityItem}; | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_parse_header() { | ||||
|         let a: AcceptLanguage = Header::parse_header( | ||||
|             [b"en-us;q=1.0, en;q=0.5, fr".to_vec()].as_ref()).unwrap(); | ||||
|         let b = AcceptLanguage(vec![ | ||||
|         // Own test | ||||
|         test_header!( | ||||
|             test2, vec![b"en-us, en; q=0.5, fr"], | ||||
|             Some(AcceptLanguage(vec![ | ||||
|                 qitem(Language {primary: "en".to_string(), sub: Some("us".to_string())}), | ||||
|             QualityItem::new(Language{primary: "en".to_string(), sub: None}, | ||||
|                              Quality(500)), | ||||
|                 QualityItem::new(Language{primary: "en".to_string(), sub: None}, Quality(500)), | ||||
|                 qitem(Language {primary: "fr".to_string(), sub: None}), | ||||
|         ]); | ||||
|         assert_eq!(format!("{}", a), format!("{}", b)); | ||||
|         assert_eq!(a, b); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_display() { | ||||
|         assert_eq!("en".to_string(), | ||||
|                    format!("{}", Language{primary: "en".to_string(), | ||||
|                                           sub: None})); | ||||
|         assert_eq!("en-us".to_string(), | ||||
|                    format!("{}", Language{primary: "en".to_string(), | ||||
|                                           sub: Some("us".to_string())})); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_from_str() { | ||||
|         assert_eq!(Language { primary: "en".to_string(), sub: None }, | ||||
|                    "en".parse().unwrap()); | ||||
|         assert_eq!(Language { primary: "en".to_string(), | ||||
|                               sub: Some("us".to_string()) }, | ||||
|                    "en-us".parse().unwrap()); | ||||
|         ]))); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,17 @@ header! { | ||||
|     #[doc="The `Access-Control-Allow-Headers` header indicates, as part of the"] | ||||
|     #[doc="response to a preflight request, which header field names can be used"] | ||||
|     #[doc="during the actual request."] | ||||
|     #[doc=""] | ||||
|     #[doc="# ABNF"] | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Access-Control-Allow-Headers: \"Access-Control-Allow-Headers\" \":\" #field-name"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `accept-language, date`"] | ||||
|     (AccessControlAllowHeaders, "Access-Control-Allow-Headers") => (UniCase<String>)* | ||||
|  | ||||
|     test_access_control_allow_headers {} | ||||
|     test_access_control_allow_headers { | ||||
|         test_header!(test1, vec![b"accept-language, date"]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,17 @@ header! { | ||||
|     #[doc="The `Access-Control-Allow-Methods` header indicates, as part of the"] | ||||
|     #[doc="response to a preflight request, which methods can be used during the"] | ||||
|     #[doc="actual request."] | ||||
|     #[doc=""] | ||||
|     #[doc="# ABNF"] | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Access-Control-Allow-Methods: \"Access-Control-Allow-Methods\" \":\" #Method"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `PUT, DELETE, XMODIFY`"] | ||||
|     (AccessControlAllowMethods, "Access-Control-Allow-Methods") => (Method)* | ||||
|  | ||||
|     test_access_control_allow_methods {} | ||||
|     test_access_control_allow_methods { | ||||
|         test_header!(test1, vec![b"PUT, DELETE, XMODIFY"]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,17 +1,22 @@ | ||||
| use std::fmt::{self}; | ||||
| use std::fmt; | ||||
| use std::str; | ||||
|  | ||||
| use url::Url; | ||||
| use header; | ||||
|  | ||||
| /// The `Access-Control-Allow-Origin` response header, | ||||
| /// part of [CORS](http://www.w3.org/TR/cors/). | ||||
| /// part of [CORS](www.w3.org/TR/cors/#access-control-allow-origin-response-header) | ||||
| /// | ||||
| /// > The `Access-Control-Allow-Origin` header indicates whether a resource | ||||
| /// > can be shared based by returning the value of the Origin request header, | ||||
| /// > "*", or "null" in the response. | ||||
| /// The `Access-Control-Allow-Origin` header indicates whether a resource | ||||
| /// can be shared based by returning the value of the Origin request header, | ||||
| /// "*", or "null" in the response. | ||||
| /// | ||||
| /// Spec: www.w3.org/TR/cors/#access-control-allow-origin-response-header | ||||
| /// # ABNF | ||||
| /// ```plain | ||||
| /// Access-Control-Allow-Origin = "Access-Control-Allow-Origin" ":" origin-list-or-null | "*" | ||||
| /// ``` | ||||
| // FIXME: The documentation says differently (missing "null" value, "*" not used in practice, | ||||
| // orgin list no list but single value) | ||||
| #[derive(Clone, PartialEq, Debug)] | ||||
| pub enum AccessControlAllowOrigin { | ||||
|     /// Allow all origins | ||||
|   | ||||
| @@ -4,7 +4,17 @@ header! { | ||||
|     #[doc=""] | ||||
|     #[doc="The `Access-Control-Max-Age` header indicates how long the results of a"] | ||||
|     #[doc="preflight request can be cached in a preflight result cache."] | ||||
|     #[doc=""] | ||||
|     #[doc="# ABNF"] | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Access-Control-Max-Age = \"Access-Control-Max-Age\" \":\" delta-seconds"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `531`"] | ||||
|     (AccessControlMaxAge, "Access-Control-Max-Age") => [u32] | ||||
|  | ||||
|     test_access_control_max_age {} | ||||
|     test_access_control_max_age { | ||||
|         test_header!(test1, vec![b"531"]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,17 @@ header! { | ||||
|     #[doc="The `Access-Control-Request-Headers` header indicates which headers will"] | ||||
|     #[doc="be used in the actual request as part of the preflight request."] | ||||
|     #[doc="during the actual request."] | ||||
|     #[doc=""] | ||||
|     #[doc="# ABNF"] | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Access-Control-Allow-Headers: \"Access-Control-Allow-Headers\" \":\" #field-name"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `accept-language, date`"] | ||||
|     (AccessControlRequestHeaders, "Access-Control-Request-Headers") => (UniCase<String>)* | ||||
|  | ||||
|     test_access_control_request_headers {} | ||||
|     test_access_control_request_headers { | ||||
|         test_header!(test1, vec![b"accept-language, date"]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,16 @@ header! { | ||||
|     #[doc=""] | ||||
|     #[doc="The `Access-Control-Request-Method` header indicates which method will be"] | ||||
|     #[doc="used in the actual request as part of the preflight request."] | ||||
|     #[doc="# ABNF"] | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Access-Control-Request-Method: \"Access-Control-Request-Method\" \":\" Method"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `GET`"] | ||||
|     (AccessControlRequestMethod, "Access-Control-Request-Method") => [Method] | ||||
|  | ||||
|     test_access_control_request_method {} | ||||
|     test_access_control_request_method { | ||||
|         test_header!(test1, vec![b"GET"]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,6 +12,11 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Allow = #method"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `GET, HEAD, PUT`"] | ||||
|     #[doc="* `OPTIONS, GET, PUT, POST, DELETE, HEAD, TRACE, CONNECT, PATCH, fOObAr`"] | ||||
|     #[doc="* ``"] | ||||
|     (Allow, "Allow") => (Method)* | ||||
|  | ||||
|     test_allow { | ||||
| @@ -35,11 +40,10 @@ header! { | ||||
|                 Method::Connect, | ||||
|                 Method::Patch, | ||||
|                 Method::Extension("fOObAr".to_string())]))); | ||||
|         // FIXME: Formatting fails | ||||
|         // test_header!( | ||||
|         //    test3, | ||||
|         //    vec![b""], | ||||
|         //    Some(HeaderField(Vec::<Method>::new()))); | ||||
|         test_header!( | ||||
|             test3, | ||||
|             vec![b""], | ||||
|             Some(HeaderField(Vec::<Method>::new()))); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -16,9 +16,15 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Content-Encoding = 1#content-coding"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `gzip`"] | ||||
|     (ContentEncoding, "Content-Encoding") => (Encoding)+ | ||||
|  | ||||
|     test_content_encoding {} | ||||
|     test_content_encoding { | ||||
|         /// Testcase from the RFC | ||||
|         test_header!(test1, vec![b"gzip"], Some(ContentEncoding(vec![Encoding::Gzip]))); | ||||
|     } | ||||
| } | ||||
|  | ||||
| bench_header!(single, ContentEncoding, { vec![b"gzip".to_vec()] }); | ||||
|   | ||||
| @@ -13,6 +13,10 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Content-Language = 1#language-tag"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `da`"] | ||||
|     #[doc="* `mi, en`"] | ||||
|     (ContentLanguage, "Content-Language") => (QualityItem<Language>)+ | ||||
|  | ||||
|     test_content_language { | ||||
|   | ||||
| @@ -15,6 +15,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Content-Length = 1*DIGIT"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `3495`"] | ||||
|     (ContentLength, "Content-Length") => [u64] | ||||
|  | ||||
|     test_content_length { | ||||
|   | ||||
| @@ -16,6 +16,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Content-Type = media-type"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `text/html; charset=ISO-8859-4`"] | ||||
|     (ContentType, "Content-Type") => [Mime] | ||||
|  | ||||
|     test_content_type { | ||||
|   | ||||
| @@ -10,6 +10,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Date = HTTP-date"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `Tue, 15 Nov 1994 08:12:31 GMT`"] | ||||
|     (Date, "Date") => [HttpDate] | ||||
|  | ||||
|     test_date { | ||||
|   | ||||
| @@ -17,63 +17,30 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="ETag       = entity-tag"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `\"xyzzy\"`"] | ||||
|     #[doc="* `W/\"xyzzy\"`"] | ||||
|     #[doc="* `\"\"`"] | ||||
|     (ETag, "ETag") => [EntityTag] | ||||
|  | ||||
|     test_etag { | ||||
|         test_header!(test1, vec![b"\"xyzzy\""], Some(HeaderField(EntityTag::new(false, "xyzzy".to_string())))); | ||||
|         test_header!(test2, vec![b"W/\"xyzzy\""], Some(HeaderField(EntityTag::new(true, "xyzzy".to_string())))); | ||||
|         test_header!(test3, vec![b"\"\""], Some(HeaderField(EntityTag::new(false, "".to_string())))); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::ETag; | ||||
|     use header::{Header,EntityTag}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_etag_successes() { | ||||
|         // Expected successes | ||||
|         let mut etag: Option<ETag>; | ||||
|  | ||||
|         etag = Header::parse_header([b"\"foobar\"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, Some(ETag(EntityTag::new(false, "foobar".to_string())))); | ||||
|  | ||||
|         etag = Header::parse_header([b"\"\"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, Some(ETag(EntityTag::new(false, "".to_string())))); | ||||
|  | ||||
|         etag = Header::parse_header([b"W/\"weak-etag\"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, Some(ETag(EntityTag::new(true, "weak-etag".to_string())))); | ||||
|  | ||||
|         etag = Header::parse_header([b"W/\"\x65\x62\"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, Some(ETag(EntityTag::new(true, "\u{0065}\u{0062}".to_string())))); | ||||
|  | ||||
|         etag = Header::parse_header([b"W/\"\"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, Some(ETag(EntityTag::new(true, "".to_string())))); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_etag_failures() { | ||||
|         // Expected failures | ||||
|         let mut etag: Option<ETag>; | ||||
|  | ||||
|         etag = Header::parse_header([b"no-dquotes".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, None); | ||||
|  | ||||
|         etag = Header::parse_header([b"w/\"the-first-w-is-case-sensitive\"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, None); | ||||
|  | ||||
|         etag = Header::parse_header([b"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, None); | ||||
|  | ||||
|         etag = Header::parse_header([b"\"unmatched-dquotes1".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, None); | ||||
|  | ||||
|         etag = Header::parse_header([b"unmatched-dquotes2\"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, None); | ||||
|  | ||||
|         etag = Header::parse_header([b"matched-\"dquotes\"".to_vec()].as_ref()); | ||||
|         assert_eq!(etag, None); | ||||
|         // From the RFC | ||||
|         test_header!(test1, vec![b"\"xyzzy\""], Some(ETag(EntityTag::new(false, "xyzzy".to_string())))); | ||||
|         test_header!(test2, vec![b"W/\"xyzzy\""], Some(ETag(EntityTag::new(true, "xyzzy".to_string())))); | ||||
|         test_header!(test3, vec![b"\"\""], Some(ETag(EntityTag::new(false, "".to_string())))); | ||||
|         // Own tests | ||||
|         test_header!(test4, vec![b"\"foobar\""], Some(ETag(EntityTag::new(false, "foobar".to_string())))); | ||||
|         test_header!(test5, vec![b"\"\""], Some(ETag(EntityTag::new(false, "".to_string())))); | ||||
|         test_header!(test6, vec![b"W/\"weak-etag\""], Some(ETag(EntityTag::new(true, "weak-etag".to_string())))); | ||||
|         test_header!(test7, vec![b"W/\"\x65\x62\""], Some(ETag(EntityTag::new(true, "\u{0065}\u{0062}".to_string())))); | ||||
|         test_header!(test8, vec![b"W/\"\""], Some(ETag(EntityTag::new(true, "".to_string())))); | ||||
|         test_header!(test9, vec![b"no-dquotes"], None::<ETag>); | ||||
|         test_header!(test10, vec![b"w/\"the-first-w-is-case-sensitive\""], None::<ETag>); | ||||
|         test_header!(test11, vec![b""], None::<ETag>); | ||||
|         test_header!(test12, vec![b"\"unmatched-dquotes1"], None::<ETag>); | ||||
|         test_header!(test13, vec![b"unmatched-dquotes2\""], None::<ETag>); | ||||
|         test_header!(test14, vec![b"matched-\"dquotes\""], None::<ETag>); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Expires = HTTP-date"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `Thu, 01 Dec 1994 16:00:00 GMT`"] | ||||
|     (Expires, "Expires") => [HttpDate] | ||||
|  | ||||
|     test_expires { | ||||
|   | ||||
| @@ -20,6 +20,10 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="If-Match = \"*\" / 1#entity-tag"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `\"xyzzy\"`"] | ||||
|     #[doc="* \"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\""] | ||||
|     (IfMatch, "If-Match") => {Any / (EntityTag)+} | ||||
|  | ||||
|     test_if_match { | ||||
|   | ||||
| @@ -14,6 +14,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="If-Unmodified-Since = HTTP-date"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `Sat, 29 Oct 1994 19:43:31 GMT`"] | ||||
|     (IfModifiedSince, "If-Modified-Since") => [HttpDate] | ||||
|  | ||||
|     test_if_modified_since { | ||||
|   | ||||
| @@ -19,6 +19,13 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="If-None-Match = \"*\" / 1#entity-tag"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `\"xyzzy\"`"] | ||||
|     #[doc="* `W/\"xyzzy\"`"] | ||||
|     #[doc="* `\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"`"] | ||||
|     #[doc="* `W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"`"] | ||||
|     #[doc="* `*`"] | ||||
|     (IfNoneMatch, "If-None-Match") => {Any / (EntityTag)+} | ||||
|  | ||||
|     test_if_none_match { | ||||
|   | ||||
| @@ -14,6 +14,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="If-Unmodified-Since = HTTP-date"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `Sat, 29 Oct 1994 19:43:31 GMT`"] | ||||
|     (IfUnmodifiedSince, "If-Unmodified-Since") => [HttpDate] | ||||
|  | ||||
|     test_if_unmodified_since { | ||||
|   | ||||
| @@ -12,6 +12,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Expires = HTTP-date"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `Sat, 29 Oct 1994 19:43:31 GMT`"] | ||||
|     (LastModified, "Last-Modified") => [HttpDate] | ||||
|  | ||||
|     test_last_modified { | ||||
|   | ||||
| @@ -11,6 +11,10 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Location = URI-reference"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `/People.html#tim`"] | ||||
|     #[doc="* `http://www.example.net/index.html`"] | ||||
|     // TODO: Use URL | ||||
|     (Location, "Location") => [String] | ||||
|  | ||||
|   | ||||
| @@ -118,11 +118,13 @@ macro_rules! test_header { | ||||
|             // Test parsing | ||||
|             assert_eq!(val, $typed); | ||||
|             // Test formatting | ||||
|             if $typed != None { | ||||
|                 let res: &str = str::from_utf8($raw[0]).unwrap(); | ||||
|                 assert_eq!(format!("{}", $typed.unwrap()), res); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! header { | ||||
|   | ||||
| @@ -12,6 +12,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Referer = absolute-URI / partial-URI"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `http://www.example.org/hypertext/Overview.html`"] | ||||
|     // TODO: Use URL | ||||
|     (Referer, "Referer") => [String] | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Server = product *( RWS ( product / comment ) )"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `CERN/3.0 libwww/2.17`"] | ||||
|     // TODO: Maybe parse as defined in the spec? | ||||
|     (Server, "Server") => [String] | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,9 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Transfer-Encoding = 1#transfer-coding"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `gzip, chunked`"] | ||||
|     (TransferEncoding, "Transfer-Encoding") => (Encoding)+ | ||||
|  | ||||
|     transfer_encoding { | ||||
|   | ||||
| @@ -25,6 +25,9 @@ header! { | ||||
|     #[doc="protocol-name    = token"] | ||||
|     #[doc="protocol-version = token"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11`"] | ||||
|     (Upgrade, "Upgrade") => (Protocol)+ | ||||
|  | ||||
|     test_upgrade { | ||||
| @@ -41,6 +44,7 @@ header! { | ||||
| } | ||||
|  | ||||
| /// Protocol values that can appear in the Upgrade header. | ||||
| // TODO: Parse version part seperately | ||||
| #[derive(Clone, PartialEq, Debug)] | ||||
| pub enum Protocol { | ||||
|     /// The websocket protocol. | ||||
|   | ||||
| @@ -16,19 +16,19 @@ header! { | ||||
|     #[doc="product         = token [\"/\" product-version]"] | ||||
|     #[doc="product-version = token"] | ||||
|     #[doc="```"] | ||||
|     // TODO: Maybe write parsing according to the spec? (Split the String) | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `CERN-LineMode/2.15 libwww/2.17b3`"] | ||||
|     #[doc="* `Bunnies`"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Notes"] | ||||
|     #[doc="* The parser does not split the value"] | ||||
|     (UserAgent, "User-Agent") => [String] | ||||
|  | ||||
|     test_user_agent { | ||||
|         // Testcase from RFC | ||||
|         test_header!(test1, vec![b"CERN-LineMode/2.15 libwww/2.17b3"]); | ||||
|         // Own testcase | ||||
|         test_header!(test2, vec![b"Bunnies"], Some(UserAgent("Bunnies".to_string()))); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[test] fn test_format() { | ||||
|     use std::borrow::ToOwned; | ||||
|     use header::Headers; | ||||
|     let mut head = Headers::new(); | ||||
|     head.set(UserAgent("Bunnies".to_owned())); | ||||
|     assert!(head.to_string() == "User-Agent: Bunnies\r\n".to_owned()); | ||||
| } | ||||
|   | ||||
| @@ -14,20 +14,16 @@ header! { | ||||
|     #[doc="```plain"] | ||||
|     #[doc="Vary = \"*\" / 1#field-name"] | ||||
|     #[doc="```"] | ||||
|     #[doc=""] | ||||
|     #[doc="# Example values"] | ||||
|     #[doc="* `accept-encoding, accept-language`"] | ||||
|     (Vary, "Vary") => {Any / (UniCase<String>)+} | ||||
|  | ||||
|     test_vary { | ||||
|         test_header!(test1, vec![b"accept-encoding, accept-language"]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::Vary; | ||||
|     use header::Header; | ||||
|  | ||||
|         #[test] | ||||
|     fn test_vary() { | ||||
|         fn test2() { | ||||
|             let mut vary: Option<Vary>; | ||||
|  | ||||
|             vary = Header::parse_header([b"*".to_vec()].as_ref()); | ||||
| @@ -39,3 +35,4 @@ mod tests { | ||||
|                                                     "AlLOw".parse().unwrap(),]))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,7 +31,10 @@ pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> Option<Vec<T>> { | ||||
|         Ok(s) => { | ||||
|             Some(s | ||||
|                  .split(',') | ||||
|                  .map(|x| x.trim()) | ||||
|                  .filter_map(|x| match x.trim() { | ||||
|                      "" => None, | ||||
|                      y => Some(y) | ||||
|                      }) | ||||
|                  .filter_map(|x| x.parse().ok()) | ||||
|                  .collect()) | ||||
|         } | ||||
| @@ -40,13 +43,12 @@ pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> Option<Vec<T>> { | ||||
| } | ||||
|  | ||||
| /// Format an array into a comma-delimited string. | ||||
| pub fn fmt_comma_delimited<T: fmt::Display>(fmt: &mut fmt::Formatter, parts: &[T]) -> fmt::Result { | ||||
|     let last = parts.len() - 1; | ||||
| pub fn fmt_comma_delimited<T: fmt::Display>(f: &mut fmt::Formatter, parts: &[T]) -> fmt::Result { | ||||
|     for (i, part) in parts.iter().enumerate() { | ||||
|         try!(write!(fmt, "{}", part)); | ||||
|         if i < last { | ||||
|             try!(write!(fmt, ", ")); | ||||
|         if i > 0 { | ||||
|             try!(write!(f, ", ")); | ||||
|         } | ||||
|         try!(write!(f, "{}", part)); | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|   | ||||
| @@ -24,10 +24,10 @@ pub struct Quality(pub u16); | ||||
|  | ||||
| impl fmt::Display for Quality { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         if self.0 == 1000 { | ||||
|             write!(f, "") | ||||
|         } else { | ||||
|             write!(f, "; q=0.{}", format!("{:03}", self.0).trim_right_matches('0')) | ||||
|         match self.0 { | ||||
|             1000 => Ok(()), | ||||
|             0 => f.write_str("; q=0"), | ||||
|             x => write!(f, "; q=0.{}", format!("{:03}", x).trim_right_matches('0')) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -196,6 +196,11 @@ mod tests { | ||||
|         assert_eq!(q(0.5), Quality(500)); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_quality2() { | ||||
|         assert_eq!(format!("{}", q(0.0)), "; q=0"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     #[should_panic] | ||||
|     fn test_quality_invalid() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user