Merge pull request #503 from pyfisch/nice2
refactor(headers): Improve docs, fix nits, make formatting faster
This commit is contained in:
		| @@ -48,7 +48,9 @@ header! { | |||||||
|             Some(HeaderField(vec![ |             Some(HeaderField(vec![ | ||||||
|                 QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![]), Quality(500)), |                 QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![]), Quality(500)), | ||||||
|                 qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])), |                 qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])), | ||||||
|                 QualityItem::new(Mime(TopLevel::Text, SubLevel::Ext("x-dvi".to_string()), vec![]), Quality(800)), |                 QualityItem::new( | ||||||
|  |                     Mime(TopLevel::Text, SubLevel::Ext("x-dvi".to_string()), vec![]), | ||||||
|  |                     Quality(800)), | ||||||
|                 qitem(Mime(TopLevel::Text, SubLevel::Ext("x-c".to_string()), vec![])), |                 qitem(Mime(TopLevel::Text, SubLevel::Ext("x-c".to_string()), vec![])), | ||||||
|                 ]))); |                 ]))); | ||||||
|         // Custom tests |         // Custom tests | ||||||
| @@ -62,7 +64,9 @@ header! { | |||||||
|             test4, |             test4, | ||||||
|             vec![b"text/plain; charset=utf-8; q=0.5"], |             vec![b"text/plain; charset=utf-8; q=0.5"], | ||||||
|             Some(Accept(vec![ |             Some(Accept(vec![ | ||||||
|                 QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]), Quality(500)), |                 QualityItem::new(Mime(TopLevel::Text, | ||||||
|  |                     SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]), | ||||||
|  |                     Quality(500)), | ||||||
|             ]))); |             ]))); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ use unicase::UniCase; | |||||||
|  |  | ||||||
| header! { | header! { | ||||||
|     #[doc="`Access-Control-Allow-Headers` header, part of"] |     #[doc="`Access-Control-Allow-Headers` header, part of"] | ||||||
|     #[doc="[CORS](www.w3.org/TR/cors/#access-control-allow-headers-response-header)"] |     #[doc="[CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header)"] | ||||||
|     #[doc=""] |     #[doc=""] | ||||||
|     #[doc="The `Access-Control-Allow-Headers` header indicates, as part of the"] |     #[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="response to a preflight request, which header field names can be used"] | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ use method::Method; | |||||||
|  |  | ||||||
| header! { | header! { | ||||||
|     #[doc="`Access-Control-Allow-Methods` header, part of"] |     #[doc="`Access-Control-Allow-Methods` header, part of"] | ||||||
|     #[doc="[CORS](www.w3.org/TR/cors/#access-control-allow-methods-response-header)"] |     #[doc="[CORS](http://www.w3.org/TR/cors/#access-control-allow-methods-response-header)"] | ||||||
|     #[doc=""] |     #[doc=""] | ||||||
|     #[doc="The `Access-Control-Allow-Methods` header indicates, as part of the"] |     #[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="response to a preflight request, which methods can be used during the"] | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| header! { | header! { | ||||||
|     #[doc="`Access-Control-Max-Age` header, part of"] |     #[doc="`Access-Control-Max-Age` header, part of"] | ||||||
|     #[doc="[CORS](www.w3.org/TR/cors/#access-control-max-age-response-header)"] |     #[doc="[CORS](http://www.w3.org/TR/cors/#access-control-max-age-response-header)"] | ||||||
|     #[doc=""] |     #[doc=""] | ||||||
|     #[doc="The `Access-Control-Max-Age` header indicates how long the results of a"] |     #[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="preflight request can be cached in a preflight result cache."] | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ use unicase::UniCase; | |||||||
|  |  | ||||||
| header! { | header! { | ||||||
|     #[doc="`Access-Control-Request-Headers` header, part of"] |     #[doc="`Access-Control-Request-Headers` header, part of"] | ||||||
|     #[doc="[CORS](www.w3.org/TR/cors/#access-control-request-headers-request-header)"] |     #[doc="[CORS](http://www.w3.org/TR/cors/#access-control-request-headers-request-header)"] | ||||||
|     #[doc=""] |     #[doc=""] | ||||||
|     #[doc="The `Access-Control-Request-Headers` header indicates which headers will"] |     #[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="be used in the actual request as part of the preflight request."] | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ use method::Method; | |||||||
|  |  | ||||||
| header! { | header! { | ||||||
|     #[doc="`Access-Control-Request-Method` header, part of"] |     #[doc="`Access-Control-Request-Method` header, part of"] | ||||||
|     #[doc="[CORS](www.w3.org/TR/cors/#access-control-request-method-request-header)"] |     #[doc="[CORS](http://www.w3.org/TR/cors/#access-control-request-method-request-header)"] | ||||||
|     #[doc=""] |     #[doc=""] | ||||||
|     #[doc="The `Access-Control-Request-Method` header indicates which method will be"] |     #[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="used in the actual request as part of the preflight request."] | ||||||
|   | |||||||
| @@ -47,4 +47,5 @@ header! { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(bench, Allow, { vec![b"OPTIONS,GET,PUT,POST,DELETE,HEAD,TRACE,CONNECT,PATCH,fOObAr".to_vec()] }); | bench_header!(bench, | ||||||
|  |     Allow, { vec![b"OPTIONS,GET,PUT,POST,DELETE,HEAD,TRACE,CONNECT,PATCH,fOObAr".to_vec()] }); | ||||||
|   | |||||||
| @@ -1,11 +1,25 @@ | |||||||
| use std::any::Any; | use std::any::Any; | ||||||
| use std::fmt; | use std::fmt::{self, Display}; | ||||||
| use std::str::{FromStr, from_utf8}; | use std::str::{FromStr, from_utf8}; | ||||||
| use std::ops::{Deref, DerefMut}; | use std::ops::{Deref, DerefMut}; | ||||||
| use serialize::base64::{ToBase64, FromBase64, Standard, Config, Newline}; | use serialize::base64::{ToBase64, FromBase64, Standard, Config, Newline}; | ||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
|  |  | ||||||
| /// The `Authorization` header field. | /// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2) | ||||||
|  | /// | ||||||
|  | /// The `Authorization` header field allows a user agent to authenticate | ||||||
|  | /// itself with an origin server -- usually, but not necessarily, after | ||||||
|  | /// receiving a 401 (Unauthorized) response.  Its value consists of | ||||||
|  | /// credentials containing the authentication information of the user | ||||||
|  | /// agent for the realm of the resource being requested. | ||||||
|  | /// | ||||||
|  | /// # ABNF | ||||||
|  | /// ```plain | ||||||
|  | /// Authorization = credentials | ||||||
|  | /// ``` | ||||||
|  | /// | ||||||
|  | /// # Example values | ||||||
|  | /// * `Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==` | ||||||
| #[derive(Clone, PartialEq, Debug)] | #[derive(Clone, PartialEq, Debug)] | ||||||
| pub struct Authorization<S: Scheme>(pub S); | pub struct Authorization<S: Scheme>(pub S); | ||||||
|  |  | ||||||
| @@ -69,7 +83,7 @@ impl Scheme for String { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         fmt::Display::fmt(self, f) |         Display::fmt(self, f) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -97,12 +111,12 @@ impl Scheme for Basic { | |||||||
|         if let Some(ref pass) = self.password { |         if let Some(ref pass) = self.password { | ||||||
|             text.push_str(&pass[..]); |             text.push_str(&pass[..]); | ||||||
|         } |         } | ||||||
|         write!(f, "{}", text.as_bytes().to_base64(Config { |         f.write_str(&text.as_bytes().to_base64(Config { | ||||||
|             char_set: Standard, |             char_set: Standard, | ||||||
|             newline: Newline::CRLF, |             newline: Newline::CRLF, | ||||||
|             pad: true, |             pad: true, | ||||||
|             line_length: None |             line_length: None | ||||||
|         })) |         })[..]) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -153,15 +167,19 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_raw_auth_parse() { |     fn test_raw_auth_parse() { | ||||||
|         let header: Authorization<String> = Header::parse_header(&[b"foo bar baz".to_vec()]).unwrap(); |         let header: Authorization<String> = Header::parse_header( | ||||||
|  |             &[b"foo bar baz".to_vec()]).unwrap(); | ||||||
|         assert_eq!(header.0, "foo bar baz"); |         assert_eq!(header.0, "foo bar baz"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_basic_auth() { |     fn test_basic_auth() { | ||||||
|         let mut headers = Headers::new(); |         let mut headers = Headers::new(); | ||||||
|         headers.set(Authorization(Basic { username: "Aladdin".to_string(), password: Some("open sesame".to_string()) })); |         headers.set(Authorization( | ||||||
|         assert_eq!(headers.to_string(), "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n".to_string()); |             Basic { username: "Aladdin".to_string(), password: Some("open sesame".to_string()) })); | ||||||
|  |         assert_eq!( | ||||||
|  |             headers.to_string(), | ||||||
|  |             "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n".to_string()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
| @@ -173,14 +191,16 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_basic_auth_parse() { |     fn test_basic_auth_parse() { | ||||||
|         let auth: Authorization<Basic> = Header::parse_header(&[b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()]).unwrap(); |         let auth: Authorization<Basic> = Header::parse_header( | ||||||
|  |             &[b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()]).unwrap(); | ||||||
|         assert_eq!(auth.0.username, "Aladdin"); |         assert_eq!(auth.0.username, "Aladdin"); | ||||||
|         assert_eq!(auth.0.password, Some("open sesame".to_string())); |         assert_eq!(auth.0.password, Some("open sesame".to_string())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_basic_auth_parse_no_password() { |     fn test_basic_auth_parse_no_password() { | ||||||
|         let auth: Authorization<Basic> = Header::parse_header(&[b"Basic QWxhZGRpbjo=".to_vec()]).unwrap(); |         let auth: Authorization<Basic> = Header::parse_header( | ||||||
|  |             &[b"Basic QWxhZGRpbjo=".to_vec()]).unwrap(); | ||||||
|         assert_eq!(auth.0.username, "Aladdin"); |         assert_eq!(auth.0.username, "Aladdin"); | ||||||
|         assert_eq!(auth.0.password, Some("".to_string())); |         assert_eq!(auth.0.password, Some("".to_string())); | ||||||
|     } |     } | ||||||
| @@ -188,4 +208,4 @@ mod tests { | |||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] }); | bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] }); | ||||||
| bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()] }); | bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] }); | ||||||
|   | |||||||
| @@ -3,7 +3,23 @@ use std::str::FromStr; | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use header::parsing::{from_one_comma_delimited, fmt_comma_delimited}; | use header::parsing::{from_one_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| /// The Cache-Control header. | /// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2) | ||||||
|  | /// | ||||||
|  | /// The `Cache-Control` header field is used to specify directives for | ||||||
|  | /// caches along the request/response chain.  Such cache directives are | ||||||
|  | /// unidirectional in that the presence of a directive in a request does | ||||||
|  | /// not imply that the same directive is to be given in the response. | ||||||
|  | /// | ||||||
|  | /// # ABNF | ||||||
|  | /// ```plain | ||||||
|  | /// Cache-Control   = 1#cache-directive | ||||||
|  | /// cache-directive = token [ "=" ( token / quoted-string ) ] | ||||||
|  | /// ``` | ||||||
|  | /// | ||||||
|  | /// # Example values | ||||||
|  | /// * `no-cache` | ||||||
|  | /// * `private, community="UCI"` | ||||||
|  | /// * `max-age=30` | ||||||
| #[derive(PartialEq, Clone, Debug)] | #[derive(PartialEq, Clone, Debug)] | ||||||
| pub struct CacheControl(pub Vec<CacheDirective>); | pub struct CacheControl(pub Vec<CacheDirective>); | ||||||
|  |  | ||||||
| @@ -28,8 +44,8 @@ impl Header for CacheControl { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl HeaderFormat for CacheControl { | impl HeaderFormat for CacheControl { | ||||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         fmt_comma_delimited(fmt, &self[..]) |         fmt_comma_delimited(f, &self[..]) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -152,8 +168,9 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_extension() { |     fn test_parse_extension() { | ||||||
|         let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]); |         let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]); | ||||||
|         assert_eq!(cache, Some(CacheControl(vec![CacheDirective::Extension("foo".to_string(), None), |         assert_eq!(cache, Some(CacheControl(vec![ | ||||||
|                                                  CacheDirective::Extension("bar".to_string(), Some("baz".to_string()))]))) |             CacheDirective::Extension("foo".to_string(), None), | ||||||
|  |             CacheDirective::Extension("bar".to_string(), Some("baz".to_string()))]))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
| @@ -163,4 +180,5 @@ mod tests { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(normal, CacheControl, { vec![b"no-cache, private".to_vec(), b"max-age=100".to_vec()] }); | bench_header!(normal, | ||||||
|  |     CacheControl, { vec![b"no-cache, private".to_vec(), b"max-age=100".to_vec()] }); | ||||||
|   | |||||||
| @@ -1,12 +1,29 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt; | use std::fmt::{self, Display}; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | ||||||
| use unicase::UniCase; | use unicase::UniCase; | ||||||
|  |  | ||||||
| pub use self::ConnectionOption::{KeepAlive, Close, ConnectionHeader}; | pub use self::ConnectionOption::{KeepAlive, Close, ConnectionHeader}; | ||||||
|  |  | ||||||
| /// The `Connection` header. | /// `Connection` header, defined in [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.1) | ||||||
|  | /// | ||||||
|  | /// The `Connection` header field allows the sender to indicate desired | ||||||
|  | /// control options for the current connection.  In order to avoid | ||||||
|  | /// confusing downstream recipients, a proxy or gateway MUST remove or | ||||||
|  | /// replace any received connection options before forwarding the | ||||||
|  | /// message. | ||||||
|  | /// | ||||||
|  | /// # ABNF | ||||||
|  | /// ```plain | ||||||
|  | /// Connection        = 1#connection-option | ||||||
|  | /// connection-option = token | ||||||
|  | /// ``` | ||||||
|  | /// | ||||||
|  | /// # Example values | ||||||
|  | /// * `close` | ||||||
|  | /// * `upgrade` | ||||||
|  | /// * `keep-alive` | ||||||
| #[derive(Clone, PartialEq, Debug)] | #[derive(Clone, PartialEq, Debug)] | ||||||
| pub struct Connection(pub Vec<ConnectionOption>); | pub struct Connection(pub Vec<ConnectionOption>); | ||||||
|  |  | ||||||
| @@ -33,20 +50,20 @@ pub enum ConnectionOption { | |||||||
| impl FromStr for ConnectionOption { | impl FromStr for ConnectionOption { | ||||||
|     type Err = (); |     type Err = (); | ||||||
|     fn from_str(s: &str) -> Result<ConnectionOption, ()> { |     fn from_str(s: &str) -> Result<ConnectionOption, ()> { | ||||||
|         match s { |         Ok(match s { | ||||||
|             "keep-alive" => Ok(KeepAlive), |             "keep-alive" => KeepAlive, | ||||||
|             "close" => Ok(Close), |             "close" => Close, | ||||||
|             s => Ok(ConnectionHeader(UniCase(s.to_string()))) |             s => ConnectionHeader(UniCase(s.to_string())), | ||||||
|         } |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Display for ConnectionOption { | impl Display for ConnectionOption { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         write!(fmt, "{}", match *self { |         f.write_str(match *self { | ||||||
|             KeepAlive => "keep-alive", |             KeepAlive => "keep-alive", | ||||||
|             Close => "close", |             Close => "close", | ||||||
|             ConnectionHeader(UniCase(ref s)) => s.as_ref() |             ConnectionHeader(UniCase(ref s)) => s, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -62,13 +79,12 @@ impl Header for Connection { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl HeaderFormat for Connection { | impl HeaderFormat for Connection { | ||||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         let Connection(ref parts) = *self; |         let Connection(ref parts) = *self; | ||||||
|         fmt_comma_delimited(fmt, &parts[..]) |         fmt_comma_delimited(f, &parts[..]) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(close, Connection, { vec![b"close".to_vec()] }); | bench_header!(close, Connection, { vec![b"close".to_vec()] }); | ||||||
| bench_header!(keep_alive, Connection, { vec![b"keep-alive".to_vec()] }); | bench_header!(keep_alive, Connection, { vec![b"keep-alive".to_vec()] }); | ||||||
| bench_header!(header, Connection, { vec![b"authorization".to_vec()] }); | bench_header!(header, Connection, { vec![b"authorization".to_vec()] }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,7 +27,10 @@ header! { | |||||||
|             // FIXME: Should be b"text/html; charset=ISO-8859-4" but mime crate lowercases |             // FIXME: Should be b"text/html; charset=ISO-8859-4" but mime crate lowercases | ||||||
|             // the whole value so parsing and formatting the value gives a different result |             // the whole value so parsing and formatting the value gives a different result | ||||||
|             vec![b"text/html; charset=iso-8859-4"], |             vec![b"text/html; charset=iso-8859-4"], | ||||||
|             Some(HeaderField(Mime(TopLevel::Text, SubLevel::Html, vec![(Attr::Charset, Value::Ext("iso-8859-4".to_string()))])))); |             Some(HeaderField(Mime( | ||||||
|  |                 TopLevel::Text, | ||||||
|  |                 SubLevel::Html, | ||||||
|  |                 vec![(Attr::Charset, Value::Ext("iso-8859-4".to_string()))])))); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,18 +1,22 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt; | use std::fmt::{self, Display}; | ||||||
| use std::str::from_utf8; | use std::str::from_utf8; | ||||||
|  |  | ||||||
| use cookie::Cookie as CookiePair; | use cookie::Cookie as CookiePair; | ||||||
| use cookie::CookieJar; | use cookie::CookieJar; | ||||||
|  |  | ||||||
| /// The `Cookie` header. Defined in [RFC6265](tools.ietf.org/html/rfc6265#section-5.4): | /// `Cookie` header, defined in [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.4) | ||||||
| /// | /// | ||||||
| /// > If the user agent does attach a Cookie header field to an HTTP | /// If the user agent does attach a Cookie header field to an HTTP | ||||||
| /// > request, the user agent must send the cookie-string | /// request, the user agent must send the cookie-string | ||||||
| /// > as the value of the header field. | /// as the value of the header field. | ||||||
| /// | /// | ||||||
| /// > When the user agent generates an HTTP request, the user agent MUST NOT | /// When the user agent generates an HTTP request, the user agent MUST NOT | ||||||
| /// > attach more than one Cookie header field. | /// attach more than one Cookie header field. | ||||||
|  | /// | ||||||
|  | /// # Example values | ||||||
|  | /// * `SID=31d4d96e407aad42` | ||||||
|  | /// * `SID=31d4d96e407aad42; lang=en-US` | ||||||
| #[derive(Clone, PartialEq, Debug)] | #[derive(Clone, PartialEq, Debug)] | ||||||
| pub struct Cookie(pub Vec<CookiePair>); | pub struct Cookie(pub Vec<CookiePair>); | ||||||
|  |  | ||||||
| @@ -48,13 +52,13 @@ impl Header for Cookie { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl HeaderFormat for Cookie { | impl HeaderFormat for Cookie { | ||||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         let cookies = &self.0; |         let cookies = &self.0; | ||||||
|         for (i, cookie) in cookies.iter().enumerate() { |         for (i, cookie) in cookies.iter().enumerate() { | ||||||
|             if i != 0 { |             if i != 0 { | ||||||
|                 try!(fmt.write_str("; ")); |                 try!(f.write_str("; ")); | ||||||
|             } |             } | ||||||
|             try!(write!(fmt, "{}", cookie.pair())); |             try!(Display::fmt(&cookie.pair(), f)); | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -94,7 +98,9 @@ fn test_fmt() { | |||||||
|     let mut cookie_pair = CookiePair::new("foo".to_string(), "bar".to_string()); |     let mut cookie_pair = CookiePair::new("foo".to_string(), "bar".to_string()); | ||||||
|     cookie_pair.httponly = true; |     cookie_pair.httponly = true; | ||||||
|     cookie_pair.path = Some("/p".to_string()); |     cookie_pair.path = Some("/p".to_string()); | ||||||
|     let cookie_header = Cookie(vec![cookie_pair, CookiePair::new("baz".to_string(), "quux".to_string())]); |     let cookie_header = Cookie(vec![ | ||||||
|  |         cookie_pair, | ||||||
|  |         CookiePair::new("baz".to_string(),"quux".to_string())]); | ||||||
|     let mut headers = Headers::new(); |     let mut headers = Headers::new(); | ||||||
|     headers.set(cookie_header); |     headers.set(cookie_header); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,21 +26,49 @@ header! { | |||||||
|  |  | ||||||
|     test_etag { |     test_etag { | ||||||
|         // From the RFC |         // From the RFC | ||||||
|         test_header!(test1, vec![b"\"xyzzy\""], Some(ETag(EntityTag::new(false, "xyzzy".to_string())))); |         test_header!(test1, | ||||||
|         test_header!(test2, vec![b"W/\"xyzzy\""], Some(ETag(EntityTag::new(true, "xyzzy".to_string())))); |             vec![b"\"xyzzy\""], | ||||||
|         test_header!(test3, vec![b"\"\""], Some(ETag(EntityTag::new(false, "".to_string())))); |             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 |         // Own tests | ||||||
|         test_header!(test4, vec![b"\"foobar\""], Some(ETag(EntityTag::new(false, "foobar".to_string())))); |         test_header!(test4, | ||||||
|         test_header!(test5, vec![b"\"\""], Some(ETag(EntityTag::new(false, "".to_string())))); |             vec![b"\"foobar\""], | ||||||
|         test_header!(test6, vec![b"W/\"weak-etag\""], Some(ETag(EntityTag::new(true, "weak-etag".to_string())))); |             Some(ETag(EntityTag::new(false, "foobar".to_string())))); | ||||||
|         test_header!(test7, vec![b"W/\"\x65\x62\""], Some(ETag(EntityTag::new(true, "\u{0065}\u{0062}".to_string())))); |         test_header!(test5, | ||||||
|         test_header!(test8, vec![b"W/\"\""], Some(ETag(EntityTag::new(true, "".to_string())))); |             vec![b"\"\""], | ||||||
|         test_header!(test9, vec![b"no-dquotes"], None::<ETag>); |             Some(ETag(EntityTag::new(false, "".to_string())))); | ||||||
|         test_header!(test10, vec![b"w/\"the-first-w-is-case-sensitive\""], None::<ETag>); |         test_header!(test6, | ||||||
|         test_header!(test11, vec![b""], None::<ETag>); |             vec![b"W/\"weak-etag\""], | ||||||
|         test_header!(test12, vec![b"\"unmatched-dquotes1"], None::<ETag>); |             Some(ETag(EntityTag::new(true, "weak-etag".to_string())))); | ||||||
|         test_header!(test13, vec![b"unmatched-dquotes2\""], None::<ETag>); |         test_header!(test7, | ||||||
|         test_header!(test14, vec![b"matched-\"dquotes\""], None::<ETag>); |             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>); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -65,10 +65,10 @@ impl Header for Host { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl HeaderFormat for Host { | impl HeaderFormat for Host { | ||||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         match self.port { |         match self.port { | ||||||
|             None | Some(80) | Some(443) => write!(fmt, "{}", self.hostname), |             None | Some(80) | Some(443) => f.write_str(&self.hostname[..]), | ||||||
|             Some(port) => write!(fmt, "{}:{}", self.hostname, port) |             Some(port) => write!(f, "{}:{}", self.hostname, port) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -97,4 +97,3 @@ mod tests { | |||||||
| } | } | ||||||
|  |  | ||||||
| bench_header!(bench, Host, { vec![b"foo.com:3000".to_vec()] }); | bench_header!(bench, Host, { vec![b"foo.com:3000".to_vec()] }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| use header::{self, EntityTag, HttpDate}; | use std::fmt::{self, Display}; | ||||||
|  | use header::{self, Header, HeaderFormat, EntityTag, HttpDate}; | ||||||
|  |  | ||||||
| /// `If-Range` header, defined in [RFC7233](http://tools.ietf.org/html/rfc7233#section-3.2) | /// `If-Range` header, defined in [RFC7233](http://tools.ietf.org/html/rfc7233#section-3.2) | ||||||
| /// | /// | ||||||
| @@ -31,7 +32,7 @@ pub enum IfRange { | |||||||
|     Date(HttpDate), |     Date(HttpDate), | ||||||
| } | } | ||||||
|  |  | ||||||
| impl header::Header for IfRange { | impl Header for IfRange { | ||||||
|     fn header_name() -> &'static str { |     fn header_name() -> &'static str { | ||||||
|         "If-Range" |         "If-Range" | ||||||
|     } |     } | ||||||
| @@ -48,18 +49,17 @@ impl header::Header for IfRange { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl header::HeaderFormat for IfRange { | impl HeaderFormat for IfRange { | ||||||
|     fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { |     fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||||
|         match self { |         match self { | ||||||
|             &IfRange::EntityTag(ref x) => write!(f, "{}", x), |             &IfRange::EntityTag(ref x) => Display::fmt(x, f), | ||||||
|             &IfRange::Date(ref x) => write!(f, "{}", x), |             &IfRange::Date(ref x) => Display::fmt(x, f), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl ::std::fmt::Display for IfRange { | impl Display for IfRange { | ||||||
|     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         use header::HeaderFormat; |  | ||||||
|         self.fmt_header(f) |         self.fmt_header(f) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,8 @@ | |||||||
| use header::HttpDate; | use header::HttpDate; | ||||||
|  |  | ||||||
| header! { | header! { | ||||||
|     #[doc="`Last-Modified` header, defined in [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2)"] |     #[doc="`Last-Modified` header, defined in"] | ||||||
|  |     #[doc="[RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2)"] | ||||||
|     #[doc=""] |     #[doc=""] | ||||||
|     #[doc="The `Last-Modified` header field in a response provides a timestamp"] |     #[doc="The `Last-Modified` header field in a response provides a timestamp"] | ||||||
|     #[doc="indicating the date and time at which the origin server believes the"] |     #[doc="indicating the date and time at which the origin server believes the"] | ||||||
|   | |||||||
| @@ -124,8 +124,16 @@ macro_rules! test_header { | |||||||
|             let value = HeaderField::parse_header(&a[..]); |             let value = HeaderField::parse_header(&a[..]); | ||||||
|             let result = format!("{}", value.unwrap()); |             let result = format!("{}", value.unwrap()); | ||||||
|             let expected = String::from_utf8(raw[0].to_vec()).unwrap(); |             let expected = String::from_utf8(raw[0].to_vec()).unwrap(); | ||||||
|             let result_cmp: Vec<String> = result.to_ascii_lowercase().split(' ').map(|x| x.to_string()).collect(); |             let result_cmp: Vec<String> = result | ||||||
|             let expected_cmp: Vec<String> = expected.to_ascii_lowercase().split(' ').map(|x| x.to_string()).collect(); |                 .to_ascii_lowercase() | ||||||
|  |                 .split(' ') | ||||||
|  |                 .map(|x| x.to_string()) | ||||||
|  |                 .collect(); | ||||||
|  |             let expected_cmp: Vec<String> = expected | ||||||
|  |                 .to_ascii_lowercase() | ||||||
|  |                 .split(' ') | ||||||
|  |                 .map(|x| x.to_string()) | ||||||
|  |                 .collect(); | ||||||
|             assert_eq!(result_cmp.concat(), expected_cmp.concat()); |             assert_eq!(result_cmp.concat(), expected_cmp.concat()); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| @@ -258,8 +266,9 @@ macro_rules! header { | |||||||
|         impl $crate::header::HeaderFormat for $id { |         impl $crate::header::HeaderFormat for $id { | ||||||
|             fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { |             fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||||||
|                 match *self { |                 match *self { | ||||||
|                     $id::Any => write!(f, "*"), |                     $id::Any => f.write_str("*"), | ||||||
|                     $id::Items(ref fields) => $crate::header::parsing::fmt_comma_delimited(f, &fields[..]) |                     $id::Items(ref fields) => $crate::header::parsing::fmt_comma_delimited( | ||||||
|  |                         f, &fields[..]) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -42,10 +42,10 @@ impl Header for Pragma { | |||||||
|  |  | ||||||
| impl HeaderFormat for Pragma { | impl HeaderFormat for Pragma { | ||||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         match *self { |         f.write_str(match *self { | ||||||
|             Pragma::NoCache => write!(f, "no-cache"), |             Pragma::NoCache => "no-cache", | ||||||
|             Pragma::Ext(ref string) => write!(f, "{}", string), |             Pragma::Ext(ref string) => &string[..], | ||||||
|         } |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,15 +1,59 @@ | |||||||
| use header::{Header, HeaderFormat}; | use header::{Header, HeaderFormat}; | ||||||
| use std::fmt; | use std::fmt::{self, Display}; | ||||||
| use std::str::from_utf8; | use std::str::from_utf8; | ||||||
|  |  | ||||||
| use cookie::Cookie; | use cookie::Cookie; | ||||||
| use cookie::CookieJar; | use cookie::CookieJar; | ||||||
|  |  | ||||||
| /// The `Set-Cookie` header | /// `Set-Cookie` header, defined [RFC6265](http://tools.ietf.org/html/rfc6265#section-4.1) | ||||||
|  | /// | ||||||
|  | /// The Set-Cookie HTTP response header is used to send cookies from the | ||||||
|  | /// server to the user agent. | ||||||
| /// | /// | ||||||
| /// Informally, the Set-Cookie response header contains the header name | /// Informally, the Set-Cookie response header contains the header name | ||||||
| /// "Set-Cookie" followed by a ":" and a cookie.  Each cookie begins with | /// "Set-Cookie" followed by a ":" and a cookie.  Each cookie begins with | ||||||
| /// a name-value-pair, followed by zero or more attribute-value pairs. | /// a name-value-pair, followed by zero or more attribute-value pairs. | ||||||
|  | /// | ||||||
|  | /// # ABNF | ||||||
|  | /// ```plain | ||||||
|  | ///  set-cookie-header = "Set-Cookie:" SP set-cookie-string | ||||||
|  | /// set-cookie-string = cookie-pair *( ";" SP cookie-av ) | ||||||
|  | /// cookie-pair       = cookie-name "=" cookie-value | ||||||
|  | /// cookie-name       = token | ||||||
|  | /// cookie-value      = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) | ||||||
|  | /// cookie-octet      = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E | ||||||
|  | ///                       ; US-ASCII characters excluding CTLs, | ||||||
|  | ///                       ; whitespace DQUOTE, comma, semicolon, | ||||||
|  | ///                       ; and backslash | ||||||
|  | /// token             = <token, defined in [RFC2616], Section 2.2> | ||||||
|  | /// | ||||||
|  | /// cookie-av         = expires-av / max-age-av / domain-av / | ||||||
|  | ///                    path-av / secure-av / httponly-av / | ||||||
|  | ///                     extension-av | ||||||
|  | /// expires-av        = "Expires=" sane-cookie-date | ||||||
|  | /// sane-cookie-date  = <rfc1123-date, defined in [RFC2616], Section 3.3.1> | ||||||
|  | /// max-age-av        = "Max-Age=" non-zero-digit *DIGIT | ||||||
|  | ///                       ; In practice, both expires-av and max-age-av | ||||||
|  | ///                       ; are limited to dates representable by the | ||||||
|  | ///                       ; user agent. | ||||||
|  | /// non-zero-digit    = %x31-39 | ||||||
|  | ///                       ; digits 1 through 9 | ||||||
|  | /// domain-av         = "Domain=" domain-value | ||||||
|  | /// domain-value      = <subdomain> | ||||||
|  | ///                       ; defined in [RFC1034], Section 3.5, as | ||||||
|  | ///                       ; enhanced by [RFC1123], Section 2.1 | ||||||
|  | /// path-av           = "Path=" path-value | ||||||
|  | /// path-value        = <any CHAR except CTLs or ";"> | ||||||
|  | /// secure-av         = "Secure" | ||||||
|  | /// httponly-av       = "HttpOnly" | ||||||
|  | /// extension-av      = <any CHAR except CTLs or ";"> | ||||||
|  | /// ``` | ||||||
|  | /// | ||||||
|  | /// # Example values | ||||||
|  | /// * `SID=31d4d96e407aad42` | ||||||
|  | /// * `lang=en-US; Expires=Wed, 09 Jun 2021 10:18:14 GMT` | ||||||
|  | /// * `lang=; Expires=Sun, 06 Nov 1994 08:49:37 GMT` | ||||||
|  | /// * `lang=en-US; Path=/; Domain=example.com` | ||||||
| #[derive(Clone, PartialEq, Debug)] | #[derive(Clone, PartialEq, Debug)] | ||||||
| pub struct SetCookie(pub Vec<Cookie>); | pub struct SetCookie(pub Vec<Cookie>); | ||||||
|  |  | ||||||
| @@ -46,7 +90,7 @@ impl HeaderFormat for SetCookie { | |||||||
|             if i != 0 { |             if i != 0 { | ||||||
|                 try!(f.write_str("\r\nSet-Cookie: ")); |                 try!(f.write_str("\r\nSet-Cookie: ")); | ||||||
|             } |             } | ||||||
|             try!(write!(f, "{}", cookie)); |             try!(Display::fmt(cookie, f)); | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| @@ -90,7 +134,9 @@ fn test_fmt() { | |||||||
|     let mut headers = Headers::new(); |     let mut headers = Headers::new(); | ||||||
|     headers.set(cookies); |     headers.set(cookies); | ||||||
|  |  | ||||||
|     assert_eq!(&headers.to_string()[..], "Set-Cookie: foo=bar; HttpOnly; Path=/p\r\nSet-Cookie: baz=quux; Path=/\r\n"); |     assert_eq!( | ||||||
|  |         &headers.to_string()[..], | ||||||
|  |         "Set-Cookie: foo=bar; HttpOnly; Path=/p\r\nSet-Cookie: baz=quux; Path=/\r\n"); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
|   | |||||||
| @@ -94,12 +94,12 @@ fn parse<H: Header + HeaderFormat>(raw: &Vec<Vec<u8>>) -> Option<Box<HeaderForma | |||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Display for Item { | impl fmt::Display for Item { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         match *self.raw { |         match *self.raw { | ||||||
|             Some(ref raw) => { |             Some(ref raw) => { | ||||||
|                 for part in raw.iter() { |                 for part in raw.iter() { | ||||||
|                     match from_utf8(&part[..]) { |                     match from_utf8(&part[..]) { | ||||||
|                         Ok(s) => try!(fmt.write_str(s)), |                         Ok(s) => try!(f.write_str(s)), | ||||||
|                         Err(e) => { |                         Err(e) => { | ||||||
|                             error!("raw header value is not utf8. header={:?}, error={:?}", part, e); |                             error!("raw header value is not utf8. header={:?}, error={:?}", part, e); | ||||||
|                             return Err(fmt::Error); |                             return Err(fmt::Error); | ||||||
| @@ -108,7 +108,7 @@ impl fmt::Display for Item { | |||||||
|                 } |                 } | ||||||
|                 Ok(()) |                 Ok(()) | ||||||
|             }, |             }, | ||||||
|             None => fmt::Display::fmt(&unsafe { self.typed.one() }, fmt) |             None => fmt::Display::fmt(&unsafe { self.typed.one() }, f) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -57,7 +57,7 @@ pub trait HeaderFormat: fmt::Debug + HeaderClone + Any + Typeable + Send + Sync | |||||||
|     /// |     /// | ||||||
|     /// This method is not allowed to introduce an Err not produced |     /// This method is not allowed to introduce an Err not produced | ||||||
|     /// by the passed-in Formatter. |     /// by the passed-in Formatter. | ||||||
|     fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result; |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -94,8 +94,7 @@ impl Clone for Box<HeaderFormat + Send + Sync> { | |||||||
|  |  | ||||||
| #[inline] | #[inline] | ||||||
| fn header_name<T: Header>() -> &'static str { | fn header_name<T: Header>() -> &'static str { | ||||||
|     let name = <T as Header>::header_name(); |     <T as Header>::header_name() | ||||||
|     name |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// A map of header fields on requests and responses. | /// A map of header fields on requests and responses. | ||||||
| @@ -224,21 +223,21 @@ impl Headers { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Display for Headers { | impl fmt::Display for Headers { | ||||||
|    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         for header in self.iter() { |         for header in self.iter() { | ||||||
|             try!(write!(fmt, "{}\r\n", header)); |             try!(write!(f, "{}\r\n", header)); | ||||||
|         } |         } | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for Headers { | impl fmt::Debug for Headers { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         try!(fmt.write_str("Headers { ")); |         try!(f.write_str("Headers { ")); | ||||||
|         for header in self.iter() { |         for header in self.iter() { | ||||||
|             try!(write!(fmt, "{:?}, ", header)); |             try!(write!(f, "{:?}, ", header)); | ||||||
|         } |         } | ||||||
|         try!(fmt.write_str("}")); |         try!(f.write_str("}")); | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -292,8 +291,8 @@ impl<'a> fmt::Display for HeaderView<'a> { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl<'a> fmt::Debug for HeaderView<'a> { | impl<'a> fmt::Debug for HeaderView<'a> { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         fmt::Display::fmt(self, fmt) |         fmt::Display::fmt(self, f) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -315,8 +314,8 @@ impl<'a> FromIterator<HeaderView<'a>> for Headers { | |||||||
|  |  | ||||||
| impl<'a> fmt::Display for &'a (HeaderFormat + Send + Sync) { | impl<'a> fmt::Display for &'a (HeaderFormat + Send + Sync) { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         (**self).fmt_header(fmt) |         (**self).fmt_header(f) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -353,14 +352,14 @@ impl Deref for CowStr { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for CowStr { | impl fmt::Debug for CowStr { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         fmt::Debug::fmt(&self.0, fmt) |         fmt::Debug::fmt(&self.0, f) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Display for CowStr { | impl fmt::Display for CowStr { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         fmt::Display::fmt(&self.0, fmt) |         fmt::Display::fmt(&self.0, f) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -460,9 +459,9 @@ mod tests { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     impl HeaderFormat for CrazyLength { |     impl HeaderFormat for CrazyLength { | ||||||
|         fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |         fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|             let CrazyLength(ref opt, ref value) = *self; |             let CrazyLength(ref opt, ref value) = *self; | ||||||
|             write!(fmt, "{:?}, {:?}", opt, value) |             write!(f, "{:?}, {:?}", opt, value) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| //! Utility functions for Header implementations. | //! Utility functions for Header implementations. | ||||||
|  |  | ||||||
| use std::str; | use std::str; | ||||||
| use std::fmt; | use std::fmt::{self, Display}; | ||||||
|  |  | ||||||
| /// 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: str::FromStr>(raw: &[Vec<u8>]) -> Option<T> { | pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> Option<T> { | ||||||
| @@ -9,7 +9,7 @@ pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> Option<T> { | |||||||
|         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. | ||||||
|     if let Ok(s) = str::from_utf8(&raw[0][..]) { |     if let Ok(s) = str::from_utf8(& unsafe { raw.get_unchecked(0) }[..]) { | ||||||
|         if s != "" { |         if s != "" { | ||||||
|             return str::FromStr::from_str(s).ok(); |             return str::FromStr::from_str(s).ok(); | ||||||
|         } |         } | ||||||
| @@ -24,7 +24,7 @@ pub fn from_comma_delimited<T: str::FromStr>(raw: &[Vec<u8>]) -> Option<Vec<T>> | |||||||
|         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. | ||||||
|     from_one_comma_delimited(&raw[0][..]) |     from_one_comma_delimited(& unsafe { raw.get_unchecked(0) }[..]) | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Reads a comma-delimited raw string into a Vec. | /// Reads a comma-delimited raw string into a Vec. | ||||||
| @@ -45,12 +45,12 @@ pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> Option<Vec<T>> { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Format an array into a comma-delimited string. | /// Format an array into a comma-delimited string. | ||||||
| pub fn fmt_comma_delimited<T: fmt::Display>(f: &mut fmt::Formatter, parts: &[T]) -> fmt::Result { | pub fn fmt_comma_delimited<T: Display>(f: &mut fmt::Formatter, parts: &[T]) -> fmt::Result { | ||||||
|     for (i, part) in parts.iter().enumerate() { |     for (i, part) in parts.iter().enumerate() { | ||||||
|         if i > 0 { |         if i != 0 { | ||||||
|             try!(write!(f, ", ")); |             try!(f.write_str(", ")); | ||||||
|         } |         } | ||||||
|         try!(write!(f, "{}", part)); |         try!(Display::fmt(part, f)); | ||||||
|     } |     } | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -97,8 +97,8 @@ impl Charset { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl Display for Charset { | impl Display for Charset { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         write!(fmt, "{}", self.name()) |         f.write_str(self.name()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,8 +24,8 @@ pub enum Encoding { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Display for Encoding { | impl fmt::Display for Encoding { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         fmt.write_str(match *self { |         f.write_str(match *self { | ||||||
|             Chunked => "chunked", |             Chunked => "chunked", | ||||||
|             Gzip => "gzip", |             Gzip => "gzip", | ||||||
|             Deflate => "deflate", |             Deflate => "deflate", | ||||||
|   | |||||||
| @@ -103,10 +103,10 @@ impl EntityTag { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl Display for EntityTag { | impl Display for EntityTag { | ||||||
|     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         match self.weak { |         match self.weak { | ||||||
|             true => write!(fmt, "W/\"{}\"", self.tag), |             true => write!(f, "W/\"{}\"", self.tag), | ||||||
|             false => write!(fmt, "\"{}\"", self.tag), |             false => write!(f, "\"{}\"", self.tag), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -116,10 +116,8 @@ impl FromStr for EntityTag { | |||||||
|     fn from_str(s: &str) -> Result<EntityTag, ()> { |     fn from_str(s: &str) -> Result<EntityTag, ()> { | ||||||
|         let length: usize = s.len(); |         let length: usize = s.len(); | ||||||
|         let slice = &s[..]; |         let slice = &s[..]; | ||||||
|         // Early exits: |         // Early exits if it doesn't terminate in a DQUOTE. | ||||||
|         // 1. The string is empty, or, |         if !slice.ends_with('"') { | ||||||
|         // 2. it doesn't terminate in a DQUOTE. |  | ||||||
|         if slice.is_empty() || !slice.ends_with('"') { |  | ||||||
|             return Err(()); |             return Err(()); | ||||||
|         } |         } | ||||||
|         // The etag is weak if its first char is not a DQUOTE. |         // The etag is weak if its first char is not a DQUOTE. | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ impl FromStr for Language { | |||||||
|  |  | ||||||
| impl fmt::Display for Language { | impl fmt::Display for Language { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         try!(write!(f, "{}", self.primary)); |         try!(f.write_str(&self.primary[..])); | ||||||
|         match self.sub { |         match self.sub { | ||||||
|             Some(ref s) => write!(f, "-{}", s), |             Some(ref s) => write!(f, "-{}", s), | ||||||
|             None => Ok(()) |             None => Ok(()) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user