feat(header): introduce header::Raw (#869)
The Raw type repesents the raw bytes of a header-value. Having a special type allows a couple of benefits: - The exact representation has become private, allowing "uglier" internals. Specifically, since the common case is for a header to only have 1 line of bytes, an enum is used to skip allocating a Vec for only 1 line. Additionally, a Cow<'static, [u8]> is used, so static bytes don't require a copy. Finally, since we can use static bytes, when parsing, we can compare the incoming bytes against a couple of the most common header-values, and possibly remove another copy. - As its own type, the `Headers.set_raw` method can be generic over `Into<Raw>`, which allows for more ergnomic method calls. BREAKING CHANGE: `Header::parse_header` now receives `&Raw`, instead of a `&[Vec<u8>]`. `Raw` provides several methods to ease using it, but may require some changes to existing code.
This commit is contained in:
		| @@ -1,7 +1,7 @@ | |||||||
| use std::fmt::{self, Display}; | use std::fmt::{self, Display}; | ||||||
| use std::str; | use std::str; | ||||||
| use unicase::UniCase; | use unicase::UniCase; | ||||||
| use header::{Header}; | use header::{Header, Raw}; | ||||||
|  |  | ||||||
| /// `Access-Control-Allow-Credentials` header, part of | /// `Access-Control-Allow-Credentials` header, part of | ||||||
| /// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header) | /// [CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header) | ||||||
| @@ -46,16 +46,15 @@ impl Header for AccessControlAllowCredentials { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowCredentials> { |     fn parse_header(raw: &Raw) -> ::Result<AccessControlAllowCredentials> { | ||||||
|         if raw.len() == 1 { |         if let Some(line) = raw.one() { | ||||||
|             let text = unsafe { |             let text = unsafe { | ||||||
|                 // safe because: |                 // safe because: | ||||||
|                 // 1. we just checked raw.len == 1 |                 // 1. we don't actually care if it's utf8, we just want to | ||||||
|                 // 2. we don't actually care if it's utf8, we just want to |  | ||||||
|                 //    compare the bytes with the "case" normalized. If it's not |                 //    compare the bytes with the "case" normalized. If it's not | ||||||
|                 //    utf8, then the byte comparison will fail, and we'll return |                 //    utf8, then the byte comparison will fail, and we'll return | ||||||
|                 //    None. No big deal. |                 //    None. No big deal. | ||||||
|                 str::from_utf8_unchecked(raw.get_unchecked(0)) |                 str::from_utf8_unchecked(line) | ||||||
|             }; |             }; | ||||||
|             if UniCase(text) == ACCESS_CONTROL_ALLOW_CREDENTIALS_TRUE { |             if UniCase(text) == ACCESS_CONTROL_ALLOW_CREDENTIALS_TRUE { | ||||||
|                 return Ok(AccessControlAllowCredentials); |                 return Ok(AccessControlAllowCredentials); | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| use std::fmt::{self, Display}; | use std::fmt::{self, Display}; | ||||||
|  | use std::str; | ||||||
|  |  | ||||||
| use header::{Header}; | use header::{Header, Raw}; | ||||||
|  |  | ||||||
| /// The `Access-Control-Allow-Origin` response header, | /// The `Access-Control-Allow-Origin` response header, | ||||||
| /// part of [CORS](http://www.w3.org/TR/cors/#access-control-allow-origin-response-header) | /// part of [CORS](http://www.w3.org/TR/cors/#access-control-allow-origin-response-header) | ||||||
| @@ -59,16 +60,16 @@ impl Header for AccessControlAllowOrigin { | |||||||
|         "Access-Control-Allow-Origin" |         "Access-Control-Allow-Origin" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowOrigin> { |     fn parse_header(raw: &Raw) -> ::Result<AccessControlAllowOrigin> { | ||||||
|         if raw.len() != 1 { |         if let Some(line) = raw.one() { | ||||||
|             return Err(::Error::Header) |             Ok(match line { | ||||||
|         } |  | ||||||
|         let value = unsafe { raw.get_unchecked(0) }; |  | ||||||
|         Ok(match &value[..] { |  | ||||||
|                 b"*" => AccessControlAllowOrigin::Any, |                 b"*" => AccessControlAllowOrigin::Any, | ||||||
|                 b"null" => AccessControlAllowOrigin::Null, |                 b"null" => AccessControlAllowOrigin::Null, | ||||||
|             _ => AccessControlAllowOrigin::Value(try!(String::from_utf8(value.clone()))) |                 _ => AccessControlAllowOrigin::Value(try!(str::from_utf8(line)).into()) | ||||||
|             }) |             }) | ||||||
|  |         } else { | ||||||
|  |             Err(::Error::Header) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ 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}; | use header::{Header, Raw}; | ||||||
|  |  | ||||||
| /// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2) | /// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2) | ||||||
| /// | /// | ||||||
| @@ -77,11 +77,9 @@ impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'st | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> { |     fn parse_header(raw: &Raw) -> ::Result<Authorization<S>> { | ||||||
|         if raw.len() != 1 { |         if let Some(line) = raw.one() { | ||||||
|             return Err(::Error::Header); |             let header = try!(from_utf8(line)); | ||||||
|         } |  | ||||||
|         let header = try!(from_utf8(unsafe { &raw.get_unchecked(0)[..] })); |  | ||||||
|             if let Some(scheme) = <S as Scheme>::scheme() { |             if let Some(scheme) = <S as Scheme>::scheme() { | ||||||
|                 if header.starts_with(scheme) && header.len() > scheme.len() + 1 { |                 if header.starts_with(scheme) && header.len() > scheme.len() + 1 { | ||||||
|                     match header[scheme.len() + 1..].parse::<S>().map(Authorization) { |                     match header[scheme.len() + 1..].parse::<S>().map(Authorization) { | ||||||
| @@ -97,6 +95,9 @@ impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'st | |||||||
|                     Err(_) => Err(::Error::Header) |                     Err(_) => Err(::Error::Header) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         } else { | ||||||
|  |             Err(::Error::Header) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
| @@ -231,8 +232,7 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_raw_auth_parse() { |     fn test_raw_auth_parse() { | ||||||
|         let header: Authorization<String> = Header::parse_header( |         let header: Authorization<String> = Header::parse_header(&b"foo bar baz".as_ref().into()).unwrap(); | ||||||
|             &[b"foo bar baz".to_vec()]).unwrap(); |  | ||||||
|         assert_eq!(header.0, "foo bar baz"); |         assert_eq!(header.0, "foo bar baz"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -256,7 +256,7 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn test_basic_auth_parse() { |     fn test_basic_auth_parse() { | ||||||
|         let auth: Authorization<Basic> = Header::parse_header( |         let auth: Authorization<Basic> = Header::parse_header( | ||||||
|             &[b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()]).unwrap(); |             &b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".as_ref().into()).unwrap(); | ||||||
|         assert_eq!(auth.0.username, "Aladdin"); |         assert_eq!(auth.0.username, "Aladdin"); | ||||||
|         assert_eq!(auth.0.password, Some("open sesame".to_owned())); |         assert_eq!(auth.0.password, Some("open sesame".to_owned())); | ||||||
|     } |     } | ||||||
| @@ -264,7 +264,7 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn test_basic_auth_parse_no_password() { |     fn test_basic_auth_parse_no_password() { | ||||||
|         let auth: Authorization<Basic> = Header::parse_header( |         let auth: Authorization<Basic> = Header::parse_header( | ||||||
|             &[b"Basic QWxhZGRpbjo=".to_vec()]).unwrap(); |             &b"Basic QWxhZGRpbjo=".as_ref().into()).unwrap(); | ||||||
|         assert_eq!(auth.0.username, "Aladdin"); |         assert_eq!(auth.0.username, "Aladdin"); | ||||||
|         assert_eq!(auth.0.password, Some("".to_owned())); |         assert_eq!(auth.0.password, Some("".to_owned())); | ||||||
|     } |     } | ||||||
| @@ -282,7 +282,7 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn test_bearer_auth_parse() { |     fn test_bearer_auth_parse() { | ||||||
|         let auth: Authorization<Bearer> = Header::parse_header( |         let auth: Authorization<Bearer> = Header::parse_header( | ||||||
|             &[b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()]).unwrap(); |             &b"Bearer fpKL54jvWmEGVoRdCNjG".as_ref().into()).unwrap(); | ||||||
|         assert_eq!(auth.0.token, "fpKL54jvWmEGVoRdCNjG"); |         assert_eq!(auth.0.token, "fpKL54jvWmEGVoRdCNjG"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use header::Header; | use header::{Header, Raw}; | ||||||
| use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| /// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2) | /// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2) | ||||||
| @@ -55,7 +55,7 @@ impl Header for CacheControl { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> { |     fn parse_header(raw: &Raw) -> ::Result<CacheControl> { | ||||||
|         let directives = try!(from_comma_delimited(raw)); |         let directives = try!(from_comma_delimited(raw)); | ||||||
|         if !directives.is_empty() { |         if !directives.is_empty() { | ||||||
|             Ok(CacheControl(directives)) |             Ok(CacheControl(directives)) | ||||||
| @@ -167,27 +167,27 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_multiple_headers() { |     fn test_parse_multiple_headers() { | ||||||
|         let cache = Header::parse_header(&[b"no-cache".to_vec(), b"private".to_vec()]); |         let cache = Header::parse_header(&vec![b"no-cache".to_vec(), b"private".to_vec()].into()); | ||||||
|         assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::NoCache, |         assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::NoCache, | ||||||
|                                                  CacheDirective::Private]))) |                                                  CacheDirective::Private]))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_argument() { |     fn test_parse_argument() { | ||||||
|         let cache = Header::parse_header(&[b"max-age=100, private".to_vec()]); |         let cache = Header::parse_header(&vec![b"max-age=100, private".to_vec()].into()); | ||||||
|         assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(100), |         assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(100), | ||||||
|                                                  CacheDirective::Private]))) |                                                  CacheDirective::Private]))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_quote_form() { |     fn test_parse_quote_form() { | ||||||
|         let cache = Header::parse_header(&[b"max-age=\"200\"".to_vec()]); |         let cache = Header::parse_header(&vec![b"max-age=\"200\"".to_vec()].into()); | ||||||
|         assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(200)]))) |         assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(200)]))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[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(&vec![b"foo, bar=baz".to_vec()].into()); | ||||||
|         assert_eq!(cache.ok(), Some(CacheControl(vec![ |         assert_eq!(cache.ok(), Some(CacheControl(vec![ | ||||||
|             CacheDirective::Extension("foo".to_owned(), None), |             CacheDirective::Extension("foo".to_owned(), None), | ||||||
|             CacheDirective::Extension("bar".to_owned(), Some("baz".to_owned()))]))) |             CacheDirective::Extension("bar".to_owned(), Some("baz".to_owned()))]))) | ||||||
| @@ -195,7 +195,7 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_bad_syntax() { |     fn test_parse_bad_syntax() { | ||||||
|         let cache: ::Result<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]); |         let cache: ::Result<CacheControl> = Header::parse_header(&vec![b"foo=".to_vec()].into()); | ||||||
|         assert_eq!(cache.ok(), None) |         assert_eq!(cache.ok(), None) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -127,8 +127,8 @@ mod tests { | |||||||
|     use unicase::UniCase; |     use unicase::UniCase; | ||||||
|  |  | ||||||
|     fn parse_option(header: Vec<u8>) -> Connection { |     fn parse_option(header: Vec<u8>) -> Connection { | ||||||
|         let val = vec![header]; |         let val = header.into(); | ||||||
|         let connection: Connection = Header::parse_header(&val[..]).unwrap(); |         let connection: Connection = Header::parse_header(&val).unwrap(); | ||||||
|         connection |         connection | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ use std::fmt; | |||||||
| use unicase::UniCase; | use unicase::UniCase; | ||||||
| use url::percent_encoding; | use url::percent_encoding; | ||||||
|  |  | ||||||
| use header::{Header, parsing}; | use header::{Header, Raw, parsing}; | ||||||
| use header::parsing::{parse_extended_value, HTTP_VALUE}; | use header::parsing::{parse_extended_value, HTTP_VALUE}; | ||||||
| use header::shared::Charset; | use header::shared::Charset; | ||||||
|  |  | ||||||
| @@ -94,7 +94,7 @@ impl Header for ContentDisposition { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentDisposition> { |     fn parse_header(raw: &Raw) -> ::Result<ContentDisposition> { | ||||||
|         parsing::from_one_raw_str(raw).and_then(|s: String| { |         parsing::from_one_raw_str(raw).and_then(|s: String| { | ||||||
|             let mut sections = s.split(';'); |             let mut sections = s.split(';'); | ||||||
|             let disposition = match sections.next() { |             let disposition = match sections.next() { | ||||||
| @@ -201,10 +201,10 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_header() { |     fn test_parse_header() { | ||||||
|         assert!(ContentDisposition::parse_header([b"".to_vec()].as_ref()).is_err()); |         assert!(ContentDisposition::parse_header(&"".into()).is_err()); | ||||||
|  |  | ||||||
|         let a = [b"form-data; dummy=3; name=upload;\r\n filename=\"sample.png\"".to_vec()]; |         let a = "form-data; dummy=3; name=upload;\r\n filename=\"sample.png\"".into(); | ||||||
|         let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap(); |         let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap(); | ||||||
|         let b = ContentDisposition { |         let b = ContentDisposition { | ||||||
|             disposition: DispositionType::Ext("form-data".to_owned()), |             disposition: DispositionType::Ext("form-data".to_owned()), | ||||||
|             parameters: vec![ |             parameters: vec![ | ||||||
| @@ -217,8 +217,8 @@ mod tests { | |||||||
|         }; |         }; | ||||||
|         assert_eq!(a, b); |         assert_eq!(a, b); | ||||||
|  |  | ||||||
|         let a = [b"attachment; filename=\"image.jpg\"".to_vec()]; |         let a = "attachment; filename=\"image.jpg\"".into(); | ||||||
|         let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap(); |         let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap(); | ||||||
|         let b = ContentDisposition { |         let b = ContentDisposition { | ||||||
|             disposition: DispositionType::Attachment, |             disposition: DispositionType::Attachment, | ||||||
|             parameters: vec![ |             parameters: vec![ | ||||||
| @@ -229,8 +229,8 @@ mod tests { | |||||||
|         }; |         }; | ||||||
|         assert_eq!(a, b); |         assert_eq!(a, b); | ||||||
|  |  | ||||||
|         let a = [b"attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates".to_vec()]; |         let a = "attachment; filename*=UTF-8''%c2%a3%20and%20%e2%82%ac%20rates".into(); | ||||||
|         let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap(); |         let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap(); | ||||||
|         let b = ContentDisposition { |         let b = ContentDisposition { | ||||||
|             disposition: DispositionType::Attachment, |             disposition: DispositionType::Attachment, | ||||||
|             parameters: vec![ |             parameters: vec![ | ||||||
| @@ -245,19 +245,19 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_display() { |     fn test_display() { | ||||||
|         let a = [b"attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates".to_vec()]; |         let as_string = "attachment; filename*=UTF-8'en'%C2%A3%20and%20%E2%82%AC%20rates"; | ||||||
|         let as_string = ::std::str::from_utf8(&(a[0])).unwrap(); |         let a = as_string.into(); | ||||||
|         let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap(); |         let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap(); | ||||||
|         let display_rendered = format!("{}",a); |         let display_rendered = format!("{}",a); | ||||||
|         assert_eq!(as_string, display_rendered); |         assert_eq!(as_string, display_rendered); | ||||||
|  |  | ||||||
|         let a = [b"attachment; filename*=UTF-8''black%20and%20white.csv".to_vec()]; |         let a = "attachment; filename*=UTF-8''black%20and%20white.csv".into(); | ||||||
|         let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap(); |         let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap(); | ||||||
|         let display_rendered = format!("{}",a); |         let display_rendered = format!("{}",a); | ||||||
|         assert_eq!("attachment; filename=\"black and white.csv\"".to_owned(), display_rendered); |         assert_eq!("attachment; filename=\"black and white.csv\"".to_owned(), display_rendered); | ||||||
|  |  | ||||||
|         let a = [b"attachment; filename=colourful.csv".to_vec()]; |         let a = "attachment; filename=colourful.csv".into(); | ||||||
|         let a: ContentDisposition = ContentDisposition::parse_header(a.as_ref()).unwrap(); |         let a: ContentDisposition = ContentDisposition::parse_header(&a).unwrap(); | ||||||
|         let display_rendered = format!("{}",a); |         let display_rendered = format!("{}",a); | ||||||
|         assert_eq!("attachment; filename=\"colourful.csv\"".to_owned(), display_rendered); |         assert_eq!("attachment; filename=\"colourful.csv\"".to_owned(), display_rendered); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
|  |  | ||||||
| use header::{Header, parsing}; | use header::{Header, Raw, parsing}; | ||||||
|  |  | ||||||
| /// `Content-Length` header, defined in | /// `Content-Length` header, defined in | ||||||
| /// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2) | /// [RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2) | ||||||
| @@ -40,12 +40,11 @@ impl Header for ContentLength { | |||||||
|         static NAME: &'static str = "Content-Length"; |         static NAME: &'static str = "Content-Length"; | ||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentLength> { |     fn parse_header(raw: &Raw) -> ::Result<ContentLength> { | ||||||
|         // If multiple Content-Length headers were sent, everything can still |         // If multiple Content-Length headers were sent, everything can still | ||||||
|         // be alright if they all contain the same value, and all parse |         // be alright if they all contain the same value, and all parse | ||||||
|         // correctly. If not, then it's an error. |         // correctly. If not, then it's an error. | ||||||
|         raw.iter() |         raw.iter() | ||||||
|             .map(::std::ops::Deref::deref) |  | ||||||
|             .map(parsing::from_raw_str) |             .map(parsing::from_raw_str) | ||||||
|             .fold(None, |prev, x| { |             .fold(None, |prev, x| { | ||||||
|                 match (prev, x) { |                 match (prev, x) { | ||||||
| @@ -84,8 +83,8 @@ __hyper__tm!(ContentLength, tests { | |||||||
|     // Can't use the test_header macro because "5, 5" gets cleaned to "5". |     // Can't use the test_header macro because "5, 5" gets cleaned to "5". | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_duplicates() { |     fn test_duplicates() { | ||||||
|         let parsed = HeaderField::parse_header(&[b"5"[..].into(), |         let parsed = HeaderField::parse_header(&vec![b"5".to_vec(), | ||||||
|                                                  b"5"[..].into()]).unwrap(); |                                                  b"5".to_vec()].into()).unwrap(); | ||||||
|         assert_eq!(parsed, HeaderField(5)); |         assert_eq!(parsed, HeaderField(5)); | ||||||
|         assert_eq!(format!("{}", parsed), "5"); |         assert_eq!(format!("{}", parsed), "5"); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| use header::{Header, CookiePair, CookieJar}; | use header::{Header, Raw, CookiePair, CookieJar}; | ||||||
| use std::fmt::{self, Display}; | use std::fmt::{self, Display}; | ||||||
| use std::str::from_utf8; | use std::str::from_utf8; | ||||||
|  |  | ||||||
| @@ -43,7 +43,7 @@ impl Header for Cookie { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Cookie> { |     fn parse_header(raw: &Raw) -> ::Result<Cookie> { | ||||||
|         let mut cookies = Vec::with_capacity(raw.len()); |         let mut cookies = Vec::with_capacity(raw.len()); | ||||||
|         for cookies_raw in raw.iter() { |         for cookies_raw in raw.iter() { | ||||||
|             let cookies_str = try!(from_utf8(&cookies_raw[..])); |             let cookies_str = try!(from_utf8(&cookies_raw[..])); | ||||||
| @@ -96,7 +96,7 @@ impl Cookie { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_parse() { | fn test_parse() { | ||||||
|     let h = Header::parse_header(&[b"foo=bar; baz=quux".to_vec()][..]); |     let h = Header::parse_header(&b"foo=bar; baz=quux".as_ref().into()); | ||||||
|     let c1 = CookiePair::new("foo".to_owned(), "bar".to_owned()); |     let c1 = CookiePair::new("foo".to_owned(), "bar".to_owned()); | ||||||
|     let c2 = CookiePair::new("baz".to_owned(), "quux".to_owned()); |     let c2 = CookiePair::new("baz".to_owned(), "quux".to_owned()); | ||||||
|     assert_eq!(h.ok(), Some(Cookie(vec![c1, c2]))); |     assert_eq!(h.ok(), Some(Cookie(vec![c1, c2]))); | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ use std::str; | |||||||
|  |  | ||||||
| use unicase::UniCase; | use unicase::UniCase; | ||||||
|  |  | ||||||
| use header::{Header}; | use header::{Header, Raw}; | ||||||
|  |  | ||||||
| /// The `Expect` header. | /// The `Expect` header. | ||||||
| /// | /// | ||||||
| @@ -34,16 +34,15 @@ impl Header for Expect { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> { |     fn parse_header(raw: &Raw) -> ::Result<Expect> { | ||||||
|         if raw.len() == 1 { |         if let Some(line) = raw.one() { | ||||||
|             let text = unsafe { |             let text = unsafe { | ||||||
|                 // safe because: |                 // safe because: | ||||||
|                 // 1. we just checked raw.len == 1 |                 // 1. we don't actually care if it's utf8, we just want to | ||||||
|                 // 2. we don't actually care if it's utf8, we just want to |  | ||||||
|                 //    compare the bytes with the "case" normalized. If it's not |                 //    compare the bytes with the "case" normalized. If it's not | ||||||
|                 //    utf8, then the byte comparison will fail, and we'll return |                 //    utf8, then the byte comparison will fail, and we'll return | ||||||
|                 //    None. No big deal. |                 //    None. No big deal. | ||||||
|                 str::from_utf8_unchecked(raw.get_unchecked(0)) |                 str::from_utf8_unchecked(line) | ||||||
|             }; |             }; | ||||||
|             if UniCase(text) == EXPECT_CONTINUE { |             if UniCase(text) == EXPECT_CONTINUE { | ||||||
|                 Ok(Expect::Continue) |                 Ok(Expect::Continue) | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| use header::{Header}; | use header::{Header, Raw}; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use header::parsing::from_one_raw_str; | use header::parsing::from_one_raw_str; | ||||||
| @@ -49,7 +49,7 @@ impl Header for Host { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> { |     fn parse_header(raw: &Raw) -> ::Result<Host> { | ||||||
|        from_one_raw_str(raw) |        from_one_raw_str(raw) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -98,14 +98,14 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_host() { |     fn test_host() { | ||||||
|         let host = Header::parse_header([b"foo.com".to_vec()].as_ref()); |         let host = Header::parse_header(&vec![b"foo.com".to_vec()].into()); | ||||||
|         assert_eq!(host.ok(), Some(Host { |         assert_eq!(host.ok(), Some(Host { | ||||||
|             hostname: "foo.com".to_owned(), |             hostname: "foo.com".to_owned(), | ||||||
|             port: None |             port: None | ||||||
|         })); |         })); | ||||||
|  |  | ||||||
|  |  | ||||||
|         let host = Header::parse_header([b"foo.com:8080".to_vec()].as_ref()); |         let host = Header::parse_header(&vec![b"foo.com:8080".to_vec()].into()); | ||||||
|         assert_eq!(host.ok(), Some(Host { |         assert_eq!(host.ok(), Some(Host { | ||||||
|             hostname: "foo.com".to_owned(), |             hostname: "foo.com".to_owned(), | ||||||
|             port: Some(8080) |             port: Some(8080) | ||||||
|   | |||||||
| @@ -67,10 +67,10 @@ mod tests { | |||||||
|     fn test_if_none_match() { |     fn test_if_none_match() { | ||||||
|         let mut if_none_match: ::Result<IfNoneMatch>; |         let mut if_none_match: ::Result<IfNoneMatch>; | ||||||
|  |  | ||||||
|         if_none_match = Header::parse_header([b"*".to_vec()].as_ref()); |         if_none_match = Header::parse_header(&b"*".as_ref().into()); | ||||||
|         assert_eq!(if_none_match.ok(), Some(IfNoneMatch::Any)); |         assert_eq!(if_none_match.ok(), Some(IfNoneMatch::Any)); | ||||||
|  |  | ||||||
|         if_none_match = Header::parse_header([b"\"foobar\", W/\"weak-etag\"".to_vec()].as_ref()); |         if_none_match = Header::parse_header(&b"\"foobar\", W/\"weak-etag\"".as_ref().into()); | ||||||
|         let mut entities: Vec<EntityTag> = Vec::new(); |         let mut entities: Vec<EntityTag> = Vec::new(); | ||||||
|         let foobar_etag = EntityTag::new(false, "foobar".to_owned()); |         let foobar_etag = EntityTag::new(false, "foobar".to_owned()); | ||||||
|         let weak_etag = EntityTag::new(true, "weak-etag".to_owned()); |         let weak_etag = EntityTag::new(true, "weak-etag".to_owned()); | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| use std::fmt::{self, Display}; | use std::fmt::{self, Display}; | ||||||
| use header::{self, Header, EntityTag, HttpDate}; | use header::{self, Header, Raw, 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) | ||||||
| /// | /// | ||||||
| @@ -58,7 +58,7 @@ impl Header for IfRange { | |||||||
|         static NAME: &'static str = "If-Range"; |         static NAME: &'static str = "If-Range"; | ||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> { |     fn parse_header(raw: &Raw) -> ::Result<IfRange> { | ||||||
|         let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw); |         let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw); | ||||||
|         if let Ok(etag) = etag { |         if let Ok(etag) = etag { | ||||||
|             return Ok(IfRange::EntityTag(etag)); |             return Ok(IfRange::EntityTag(etag)); | ||||||
|   | |||||||
| @@ -72,15 +72,16 @@ macro_rules! bench_header( | |||||||
|  |  | ||||||
|             #[bench] |             #[bench] | ||||||
|             fn bench_parse(b: &mut Bencher) { |             fn bench_parse(b: &mut Bencher) { | ||||||
|                 let val = $value; |                 let val = $value.into(); | ||||||
|                 b.iter(|| { |                 b.iter(|| { | ||||||
|                     let _: $ty = Header::parse_header(&val[..]).unwrap(); |                     let _: $ty = Header::parse_header(&val).unwrap(); | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             #[bench] |             #[bench] | ||||||
|             fn bench_format(b: &mut Bencher) { |             fn bench_format(b: &mut Bencher) { | ||||||
|                 let val: $ty = Header::parse_header(&$value[..]).unwrap(); |                 let raw = $value.into(); | ||||||
|  |                 let val: $ty = Header::parse_header(&raw).unwrap(); | ||||||
|                 let fmt = ::header::HeaderFormatter(&val); |                 let fmt = ::header::HeaderFormatter(&val); | ||||||
|                 b.iter(|| { |                 b.iter(|| { | ||||||
|                     format!("{}", fmt); |                     format!("{}", fmt); | ||||||
| @@ -137,7 +138,8 @@ macro_rules! test_header { | |||||||
|             use std::ascii::AsciiExt; |             use std::ascii::AsciiExt; | ||||||
|             let raw = $raw; |             let raw = $raw; | ||||||
|             let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect(); |             let a: Vec<Vec<u8>> = raw.iter().map(|x| x.to_vec()).collect(); | ||||||
|             let value = HeaderField::parse_header(&a[..]); |             let a = a.into(); | ||||||
|  |             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 |             let result_cmp: Vec<String> = result | ||||||
| @@ -157,7 +159,8 @@ macro_rules! test_header { | |||||||
|         #[test] |         #[test] | ||||||
|         fn $id() { |         fn $id() { | ||||||
|             let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect(); |             let a: Vec<Vec<u8>> = $raw.iter().map(|x| x.to_vec()).collect(); | ||||||
|             let val = HeaderField::parse_header(&a[..]); |             let a = a.into(); | ||||||
|  |             let val = HeaderField::parse_header(&a); | ||||||
|             let typed: Option<HeaderField> = $typed; |             let typed: Option<HeaderField> = $typed; | ||||||
|             // Test parsing |             // Test parsing | ||||||
|             assert_eq!(val.ok(), typed); |             assert_eq!(val.ok(), typed); | ||||||
| @@ -195,9 +198,8 @@ macro_rules! __hyper_generate_header_serialization { | |||||||
|                               where D: ::serde::Deserializer { |                               where D: ::serde::Deserializer { | ||||||
|                 let string_representation: String = |                 let string_representation: String = | ||||||
|                     try!(::serde::Deserialize::deserialize(deserializer)); |                     try!(::serde::Deserialize::deserialize(deserializer)); | ||||||
|                 Ok($crate::header::Header::parse_header(&[ |                 let raw = string_representation.into_bytes().into(); | ||||||
|                     string_representation.into_bytes() |                 Ok($crate::header::Header::parse_header(&raw).unwrap()) | ||||||
|                 ]).unwrap()) |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -221,7 +223,7 @@ macro_rules! header { | |||||||
|                 static NAME: &'static str = $n; |                 static NAME: &'static str = $n; | ||||||
|                 NAME |                 NAME | ||||||
|             } |             } | ||||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { |             fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> { | ||||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id) |                 $crate::header::parsing::from_comma_delimited(raw).map($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 { | ||||||
| @@ -248,7 +250,7 @@ macro_rules! header { | |||||||
|                 static NAME: &'static str = $n; |                 static NAME: &'static str = $n; | ||||||
|                 NAME |                 NAME | ||||||
|             } |             } | ||||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { |             fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> { | ||||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id) |                 $crate::header::parsing::from_comma_delimited(raw).map($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 { | ||||||
| @@ -274,7 +276,7 @@ macro_rules! header { | |||||||
|                 static NAME: &'static str = $n; |                 static NAME: &'static str = $n; | ||||||
|                 NAME |                 NAME | ||||||
|             } |             } | ||||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { |             fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> { | ||||||
|                 $crate::header::parsing::from_one_raw_str(raw).map($id) |                 $crate::header::parsing::from_one_raw_str(raw).map($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 { | ||||||
| @@ -303,10 +305,10 @@ macro_rules! header { | |||||||
|                 static NAME: &'static str = $n; |                 static NAME: &'static str = $n; | ||||||
|                 NAME |                 NAME | ||||||
|             } |             } | ||||||
|             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { |             fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> { | ||||||
|                 // FIXME: Return None if no item is in $id::Only |                 // FIXME: Return None if no item is in $id::Only | ||||||
|                 if raw.len() == 1 { |                 if raw.len() == 1 { | ||||||
|                     if raw[0] == b"*" { |                     if &raw[0] == b"*" { | ||||||
|                         return Ok($id::Any) |                         return Ok($id::Any) | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| use header::{Header, Host}; | use header::{Header, Raw, Host}; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use header::parsing::from_one_raw_str; | use header::parsing::from_one_raw_str; | ||||||
| @@ -57,7 +57,7 @@ impl Header for Origin { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Origin> { |     fn parse_header(raw: &Raw) -> ::Result<Origin> { | ||||||
|         from_one_raw_str(raw) |         from_one_raw_str(raw) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -100,10 +100,10 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_origin() { |     fn test_origin() { | ||||||
|         let origin = Header::parse_header([b"http://foo.com".to_vec()].as_ref()); |         let origin = Header::parse_header(&vec![b"http://foo.com".to_vec()].into()); | ||||||
|         assert_eq!(origin.ok(), Some(Origin::new("http", "foo.com", None))); |         assert_eq!(origin.ok(), Some(Origin::new("http", "foo.com", None))); | ||||||
|  |  | ||||||
|         let origin = Header::parse_header([b"https://foo.com:443".to_vec()].as_ref()); |         let origin = Header::parse_header(&vec![b"https://foo.com:443".to_vec()].into()); | ||||||
|         assert_eq!(origin.ok(), Some(Origin::new("https", "foo.com", Some(443)))); |         assert_eq!(origin.ok(), Some(Origin::new("https", "foo.com", Some(443)))); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::ascii::AsciiExt; | use std::ascii::AsciiExt; | ||||||
|  |  | ||||||
| use header::{Header, parsing}; | use header::{Header, Raw, parsing}; | ||||||
|  |  | ||||||
| /// The `Pragma` header defined by HTTP/1.0. | /// The `Pragma` header defined by HTTP/1.0. | ||||||
| /// | /// | ||||||
| @@ -44,7 +44,7 @@ impl Header for Pragma { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Pragma> { |     fn parse_header(raw: &Raw) -> ::Result<Pragma> { | ||||||
|         parsing::from_one_raw_str(raw).and_then(|s: String| { |         parsing::from_one_raw_str(raw).and_then(|s: String| { | ||||||
|             let slice = &s.to_ascii_lowercase()[..]; |             let slice = &s.to_ascii_lowercase()[..]; | ||||||
|             match slice { |             match slice { | ||||||
| @@ -64,12 +64,12 @@ impl Header for Pragma { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_parse_header() { | fn test_parse_header() { | ||||||
|     let a: Pragma = Header::parse_header([b"no-cache".to_vec()].as_ref()).unwrap(); |     let a: Pragma = Header::parse_header(&"no-cache".into()).unwrap(); | ||||||
|     let b = Pragma::NoCache; |     let b = Pragma::NoCache; | ||||||
|     assert_eq!(a, b); |     assert_eq!(a, b); | ||||||
|     let c: Pragma = Header::parse_header([b"FoObar".to_vec()].as_ref()).unwrap(); |     let c: Pragma = Header::parse_header(&"FoObar".into()).unwrap(); | ||||||
|     let d = Pragma::Ext("FoObar".to_owned()); |     let d = Pragma::Ext("FoObar".to_owned()); | ||||||
|     assert_eq!(c, d); |     assert_eq!(c, d); | ||||||
|     let e: ::Result<Pragma> = Header::parse_header([b"".to_vec()].as_ref()); |     let e: ::Result<Pragma> = Header::parse_header(&"".into()); | ||||||
|     assert_eq!(e.ok(), None); |     assert_eq!(e.ok(), None); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use header::{Header}; | use header::{Header, Raw}; | ||||||
| use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| /// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240) | /// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240) | ||||||
| @@ -56,7 +56,7 @@ impl Header for Prefer { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Prefer> { |     fn parse_header(raw: &Raw) -> ::Result<Prefer> { | ||||||
|         let preferences = try!(from_comma_delimited(raw)); |         let preferences = try!(from_comma_delimited(raw)); | ||||||
|         if !preferences.is_empty() { |         if !preferences.is_empty() { | ||||||
|             Ok(Prefer(preferences)) |             Ok(Prefer(preferences)) | ||||||
| @@ -159,14 +159,14 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_multiple_headers() { |     fn test_parse_multiple_headers() { | ||||||
|         let prefer = Header::parse_header(&[b"respond-async, return=representation".to_vec()]); |         let prefer = Header::parse_header(&"respond-async, return=representation".into()); | ||||||
|         assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::RespondAsync, |         assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::RespondAsync, | ||||||
|                                            Preference::ReturnRepresentation]))) |                                            Preference::ReturnRepresentation]))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_argument() { |     fn test_parse_argument() { | ||||||
|         let prefer = Header::parse_header(&[b"wait=100, handling=leniant, respond-async".to_vec()]); |         let prefer = Header::parse_header(&"wait=100, handling=leniant, respond-async".into()); | ||||||
|         assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(100), |         assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(100), | ||||||
|                                            Preference::HandlingLeniant, |                                            Preference::HandlingLeniant, | ||||||
|                                            Preference::RespondAsync]))) |                                            Preference::RespondAsync]))) | ||||||
| @@ -174,14 +174,14 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_quote_form() { |     fn test_parse_quote_form() { | ||||||
|         let prefer = Header::parse_header(&[b"wait=\"200\", handling=\"strict\"".to_vec()]); |         let prefer = Header::parse_header(&"wait=\"200\", handling=\"strict\"".into()); | ||||||
|         assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(200), |         assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(200), | ||||||
|                                            Preference::HandlingStrict]))) |                                            Preference::HandlingStrict]))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_extension() { |     fn test_parse_extension() { | ||||||
|         let prefer = Header::parse_header(&[b"foo, bar=baz, baz; foo; bar=baz, bux=\"\"; foo=\"\", buz=\"some parameter\"".to_vec()]); |         let prefer = Header::parse_header(&"foo, bar=baz, baz; foo; bar=baz, bux=\"\"; foo=\"\", buz=\"some parameter\"".into()); | ||||||
|         assert_eq!(prefer.ok(), Some(Prefer(vec![ |         assert_eq!(prefer.ok(), Some(Prefer(vec![ | ||||||
|             Preference::Extension("foo".to_owned(), "".to_owned(), vec![]), |             Preference::Extension("foo".to_owned(), "".to_owned(), vec![]), | ||||||
|             Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]), |             Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]), | ||||||
| @@ -192,7 +192,7 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_fail_with_args() { |     fn test_fail_with_args() { | ||||||
|         let prefer: ::Result<Prefer> = Header::parse_header(&[b"respond-async; foo=bar".to_vec()]); |         let prefer: ::Result<Prefer> = Header::parse_header(&"respond-async; foo=bar".into()); | ||||||
|         assert_eq!(prefer.ok(), None); |         assert_eq!(prefer.ok(), None); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use header::{Header, Preference}; | use header::{Header, Raw, Preference}; | ||||||
| use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | use header::parsing::{from_comma_delimited, fmt_comma_delimited}; | ||||||
|  |  | ||||||
| /// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240) | /// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240) | ||||||
| @@ -54,7 +54,7 @@ impl Header for PreferenceApplied { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<PreferenceApplied> { |     fn parse_header(raw: &Raw) -> ::Result<PreferenceApplied> { | ||||||
|         let preferences = try!(from_comma_delimited(raw)); |         let preferences = try!(from_comma_delimited(raw)); | ||||||
|         if !preferences.is_empty() { |         if !preferences.is_empty() { | ||||||
|             Ok(PreferenceApplied(preferences)) |             Ok(PreferenceApplied(preferences)) | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| use std::fmt::{self, Display}; | use std::fmt::{self, Display}; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
|  |  | ||||||
| use header::Header; | use header::{Header, Raw}; | ||||||
| use header::parsing::{from_one_raw_str, from_comma_delimited}; | use header::parsing::{from_one_raw_str}; | ||||||
|  |  | ||||||
| /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) | /// `Range` header, defined in [RFC7233](https://tools.ietf.org/html/rfc7233#section-3.1) | ||||||
| /// | /// | ||||||
| @@ -130,15 +130,11 @@ impl FromStr for Range { | |||||||
|  |  | ||||||
|         match (iter.next(), iter.next()) { |         match (iter.next(), iter.next()) { | ||||||
|             (Some("bytes"), Some(ranges)) => { |             (Some("bytes"), Some(ranges)) => { | ||||||
|                 match from_comma_delimited(&[ranges]) { |                 let ranges = from_comma_delimited(ranges); | ||||||
|                     Ok(ranges) => { |  | ||||||
|                 if ranges.is_empty() { |                 if ranges.is_empty() { | ||||||
|                     return Err(::Error::Header); |                     return Err(::Error::Header); | ||||||
|                 } |                 } | ||||||
|                 Ok(Range::Bytes(ranges)) |                 Ok(Range::Bytes(ranges)) | ||||||
|                     }, |  | ||||||
|                     Err(_) => Err(::Error::Header) |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|             (Some(unit), Some(range_str)) if unit != "" && range_str != "" => { |             (Some(unit), Some(range_str)) if unit != "" && range_str != "" => { | ||||||
|                 Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned())) |                 Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned())) | ||||||
| @@ -173,6 +169,16 @@ impl FromStr for ByteRangeSpec { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | fn from_comma_delimited<T: FromStr>(s: &str) -> Vec<T> { | ||||||
|  |     s.split(',') | ||||||
|  |         .filter_map(|x| match x.trim() { | ||||||
|  |             "" => None, | ||||||
|  |             y => Some(y) | ||||||
|  |         }) | ||||||
|  |         .filter_map(|x| x.parse().ok()) | ||||||
|  |         .collect() | ||||||
|  | } | ||||||
|  |  | ||||||
| impl Header for Range { | impl Header for Range { | ||||||
|  |  | ||||||
|     fn header_name() -> &'static str { |     fn header_name() -> &'static str { | ||||||
| @@ -180,7 +186,7 @@ impl Header for Range { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> { |     fn parse_header(raw: &Raw) -> ::Result<Range> { | ||||||
|         from_one_raw_str(raw) |         from_one_raw_str(raw) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -192,29 +198,29 @@ impl Header for Range { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_parse_bytes_range_valid() { | fn test_parse_bytes_range_valid() { | ||||||
|     let r: Range = Header::parse_header(&[b"bytes=1-100".to_vec()]).unwrap(); |     let r: Range = Header::parse_header(&"bytes=1-100".into()).unwrap(); | ||||||
|     let r2: Range = Header::parse_header(&[b"bytes=1-100,-".to_vec()]).unwrap(); |     let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap(); | ||||||
|     let r3 =  Range::bytes(1, 100); |     let r3 =  Range::bytes(1, 100); | ||||||
|     assert_eq!(r, r2); |     assert_eq!(r, r2); | ||||||
|     assert_eq!(r2, r3); |     assert_eq!(r2, r3); | ||||||
|  |  | ||||||
|     let r: Range = Header::parse_header(&[b"bytes=1-100,200-".to_vec()]).unwrap(); |     let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap(); | ||||||
|     let r2: Range = Header::parse_header(&[b"bytes= 1-100 , 101-xxx,  200- ".to_vec()]).unwrap(); |     let r2: Range = Header::parse_header(&"bytes= 1-100 , 101-xxx,  200- ".into()).unwrap(); | ||||||
|     let r3 =  Range::Bytes( |     let r3 =  Range::Bytes( | ||||||
|         vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::AllFrom(200)] |         vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::AllFrom(200)] | ||||||
|     ); |     ); | ||||||
|     assert_eq!(r, r2); |     assert_eq!(r, r2); | ||||||
|     assert_eq!(r2, r3); |     assert_eq!(r2, r3); | ||||||
|  |  | ||||||
|     let r: Range = Header::parse_header(&[b"bytes=1-100,-100".to_vec()]).unwrap(); |     let r: Range = Header::parse_header(&"bytes=1-100,-100".into()).unwrap(); | ||||||
|     let r2: Range = Header::parse_header(&[b"bytes=1-100, ,,-100".to_vec()]).unwrap(); |     let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap(); | ||||||
|     let r3 =  Range::Bytes( |     let r3 =  Range::Bytes( | ||||||
|         vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::Last(100)] |         vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::Last(100)] | ||||||
|     ); |     ); | ||||||
|     assert_eq!(r, r2); |     assert_eq!(r, r2); | ||||||
|     assert_eq!(r2, r3); |     assert_eq!(r2, r3); | ||||||
|  |  | ||||||
|     let r: Range = Header::parse_header(&[b"custom=1-100,-100".to_vec()]).unwrap(); |     let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); | ||||||
|     let r2 =  Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); |     let r2 =  Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); | ||||||
|     assert_eq!(r, r2); |     assert_eq!(r, r2); | ||||||
|  |  | ||||||
| @@ -222,40 +228,40 @@ fn test_parse_bytes_range_valid() { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_parse_unregistered_range_valid() { | fn test_parse_unregistered_range_valid() { | ||||||
|     let r: Range = Header::parse_header(&[b"custom=1-100,-100".to_vec()]).unwrap(); |     let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); | ||||||
|     let r2 =  Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); |     let r2 =  Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); | ||||||
|     assert_eq!(r, r2); |     assert_eq!(r, r2); | ||||||
|  |  | ||||||
|     let r: Range = Header::parse_header(&[b"custom=abcd".to_vec()]).unwrap(); |     let r: Range = Header::parse_header(&"custom=abcd".into()).unwrap(); | ||||||
|     let r2 =  Range::Unregistered("custom".to_owned(), "abcd".to_owned()); |     let r2 =  Range::Unregistered("custom".to_owned(), "abcd".to_owned()); | ||||||
|     assert_eq!(r, r2); |     assert_eq!(r, r2); | ||||||
|  |  | ||||||
|     let r: Range = Header::parse_header(&[b"custom=xxx-yyy".to_vec()]).unwrap(); |     let r: Range = Header::parse_header(&"custom=xxx-yyy".into()).unwrap(); | ||||||
|     let r2 =  Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned()); |     let r2 =  Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned()); | ||||||
|     assert_eq!(r, r2); |     assert_eq!(r, r2); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_parse_invalid() { | fn test_parse_invalid() { | ||||||
|     let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-a,-".to_vec()]); |     let r: ::Result<Range> = Header::parse_header(&"bytes=1-a,-".into()); | ||||||
|     assert_eq!(r.ok(), None); |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|     let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-2-3".to_vec()]); |     let r: ::Result<Range> = Header::parse_header(&"bytes=1-2-3".into()); | ||||||
|     assert_eq!(r.ok(), None); |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|     let r: ::Result<Range> = Header::parse_header(&[b"abc".to_vec()]); |     let r: ::Result<Range> = Header::parse_header(&"abc".into()); | ||||||
|     assert_eq!(r.ok(), None); |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|     let r: ::Result<Range> = Header::parse_header(&[b"bytes=1-100=".to_vec()]); |     let r: ::Result<Range> = Header::parse_header(&"bytes=1-100=".into()); | ||||||
|     assert_eq!(r.ok(), None); |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|     let r: ::Result<Range> = Header::parse_header(&[b"bytes=".to_vec()]); |     let r: ::Result<Range> = Header::parse_header(&"bytes=".into()); | ||||||
|     assert_eq!(r.ok(), None); |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|     let r: ::Result<Range> = Header::parse_header(&[b"custom=".to_vec()]); |     let r: ::Result<Range> = Header::parse_header(&"custom=".into()); | ||||||
|     assert_eq!(r.ok(), None); |     assert_eq!(r.ok(), None); | ||||||
|  |  | ||||||
|     let r: ::Result<Range> = Header::parse_header(&[b"=1-100".to_vec()]); |     let r: ::Result<Range> = Header::parse_header(&"=1-100".into()); | ||||||
|     assert_eq!(r.ok(), None); |     assert_eq!(r.ok(), None); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::ascii::AsciiExt; | use std::ascii::AsciiExt; | ||||||
|  |  | ||||||
| use header::{Header, parsing}; | use header::{Header, Raw, parsing}; | ||||||
|  |  | ||||||
| /// `Referrer-Policy` header, part of | /// `Referrer-Policy` header, part of | ||||||
| /// [Referrer Policy](https://www.w3.org/TR/referrer-policy/#referrer-policy-header) | /// [Referrer Policy](https://www.w3.org/TR/referrer-policy/#referrer-policy-header) | ||||||
| @@ -52,7 +52,7 @@ impl Header for ReferrerPolicy { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<ReferrerPolicy> { |     fn parse_header(raw: &Raw) -> ::Result<ReferrerPolicy> { | ||||||
|         use self::ReferrerPolicy::*; |         use self::ReferrerPolicy::*; | ||||||
|         parsing::from_one_raw_str(raw).and_then(|s: String| { |         parsing::from_one_raw_str(raw).and_then(|s: String| { | ||||||
|             let slice = &s.to_ascii_lowercase()[..]; |             let slice = &s.to_ascii_lowercase()[..]; | ||||||
| @@ -84,9 +84,9 @@ impl Header for ReferrerPolicy { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_parse_header() { | fn test_parse_header() { | ||||||
|     let a: ReferrerPolicy = Header::parse_header([b"origin".to_vec()].as_ref()).unwrap(); |     let a: ReferrerPolicy = Header::parse_header(&"origin".into()).unwrap(); | ||||||
|     let b = ReferrerPolicy::Origin; |     let b = ReferrerPolicy::Origin; | ||||||
|     assert_eq!(a, b); |     assert_eq!(a, b); | ||||||
|     let e: ::Result<ReferrerPolicy> = Header::parse_header([b"foobar".to_vec()].as_ref()); |     let e: ::Result<ReferrerPolicy> = Header::parse_header(&"foobar".into()); | ||||||
|     assert!(e.is_err()); |     assert!(e.is_err()); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| use header::{Header, CookiePair, CookieJar}; | use header::{Header, Raw, CookiePair, CookieJar}; | ||||||
| use std::fmt::{self, Display}; | use std::fmt::{self, Display}; | ||||||
| use std::str::from_utf8; | use std::str::from_utf8; | ||||||
|  |  | ||||||
| @@ -88,7 +88,7 @@ impl Header for SetCookie { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<SetCookie> { |     fn parse_header(raw: &Raw) -> ::Result<SetCookie> { | ||||||
|         let mut set_cookies = Vec::with_capacity(raw.len()); |         let mut set_cookies = Vec::with_capacity(raw.len()); | ||||||
|         for set_cookies_raw in raw { |         for set_cookies_raw in raw { | ||||||
|             if let Ok(s) = from_utf8(&set_cookies_raw[..]) { |             if let Ok(s) = from_utf8(&set_cookies_raw[..]) { | ||||||
| @@ -136,7 +136,7 @@ impl SetCookie { | |||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_parse() { | fn test_parse() { | ||||||
|     let h = Header::parse_header(&[b"foo=bar; HttpOnly".to_vec()][..]); |     let h = Header::parse_header(&"foo=bar; HttpOnly".into()); | ||||||
|     let mut c1 = CookiePair::new("foo".to_owned(), "bar".to_owned()); |     let mut c1 = CookiePair::new("foo".to_owned(), "bar".to_owned()); | ||||||
|     c1.httponly = true; |     c1.httponly = true; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ use std::str::{self, FromStr}; | |||||||
|  |  | ||||||
| use unicase::UniCase; | use unicase::UniCase; | ||||||
|  |  | ||||||
| use header::{Header, parsing}; | use header::{Header, Raw, parsing}; | ||||||
|  |  | ||||||
| /// `StrictTransportSecurity` header, defined in [RFC6797](https://tools.ietf.org/html/rfc6797) | /// `StrictTransportSecurity` header, defined in [RFC6797](https://tools.ietf.org/html/rfc6797) | ||||||
| /// | /// | ||||||
| @@ -125,7 +125,7 @@ impl Header for StrictTransportSecurity { | |||||||
|         NAME |         NAME | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<StrictTransportSecurity> { |     fn parse_header(raw: &Raw) -> ::Result<StrictTransportSecurity> { | ||||||
|         parsing::from_one_raw_str(raw) |         parsing::from_one_raw_str(raw) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -145,49 +145,49 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_max_age() { |     fn test_parse_max_age() { | ||||||
|         let h = Header::parse_header(&[b"max-age=31536000".to_vec()][..]); |         let h = Header::parse_header(&"max-age=31536000".into()); | ||||||
|         assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 })); |         assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 })); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_max_age_no_value() { |     fn test_parse_max_age_no_value() { | ||||||
|         let h: ::Result<StrictTransportSecurity> = Header::parse_header(&[b"max-age".to_vec()][..]); |         let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"max-age".into()); | ||||||
|         assert!(h.is_err()); |         assert!(h.is_err()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_quoted_max_age() { |     fn test_parse_quoted_max_age() { | ||||||
|         let h = Header::parse_header(&[b"max-age=\"31536000\"".to_vec()][..]); |         let h = Header::parse_header(&"max-age=\"31536000\"".into()); | ||||||
|         assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 })); |         assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 })); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_spaces_max_age() { |     fn test_parse_spaces_max_age() { | ||||||
|         let h = Header::parse_header(&[b"max-age = 31536000".to_vec()][..]); |         let h = Header::parse_header(&"max-age = 31536000".into()); | ||||||
|         assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 })); |         assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: false, max_age: 31536000u64 })); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_include_subdomains() { |     fn test_parse_include_subdomains() { | ||||||
|         let h = Header::parse_header(&[b"max-age=15768000 ; includeSubDomains".to_vec()][..]); |         let h = Header::parse_header(&"max-age=15768000 ; includeSubDomains".into()); | ||||||
|         assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: true, max_age: 15768000u64 })); |         assert_eq!(h.ok(), Some(StrictTransportSecurity { include_subdomains: true, max_age: 15768000u64 })); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_no_max_age() { |     fn test_parse_no_max_age() { | ||||||
|         let h: ::Result<StrictTransportSecurity> = Header::parse_header(&[b"includeSubDomains".to_vec()][..]); |         let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"includeSubDomains".into()); | ||||||
|         assert!(h.is_err()); |         assert!(h.is_err()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_max_age_nan() { |     fn test_parse_max_age_nan() { | ||||||
|         let h: ::Result<StrictTransportSecurity> = Header::parse_header(&[b"max-age = derp".to_vec()][..]); |         let h: ::Result<StrictTransportSecurity> = Header::parse_header(&"max-age = derp".into()); | ||||||
|         assert!(h.is_err()); |         assert!(h.is_err()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_duplicate_directives() { |     fn test_parse_duplicate_directives() { | ||||||
|         assert!(StrictTransportSecurity::parse_header(&[b"max-age=100; max-age=5; max-age=0".to_vec()][..]).is_err()); |         assert!(StrictTransportSecurity::parse_header(&"max-age=100; max-age=5; max-age=0".into()).is_err()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -68,7 +68,7 @@ header! { | |||||||
|             Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); |             Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); | ||||||
|         #[test] |         #[test] | ||||||
|         fn test3() { |         fn test3() { | ||||||
|             let x: ::Result<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]); |             let x: ::Result<Upgrade> = Header::parse_header(&"WEbSOCKet".into()); | ||||||
|             assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); |             assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -54,10 +54,10 @@ header! { | |||||||
|         fn test2() { |         fn test2() { | ||||||
|             let mut vary: ::Result<Vary>; |             let mut vary: ::Result<Vary>; | ||||||
|  |  | ||||||
|             vary = Header::parse_header([b"*".to_vec()].as_ref()); |             vary = Header::parse_header(&"*".into()); | ||||||
|             assert_eq!(vary.ok(), Some(Vary::Any)); |             assert_eq!(vary.ok(), Some(Vary::Any)); | ||||||
|  |  | ||||||
|             vary = Header::parse_header([b"etag,cookie,allow".to_vec()].as_ref()); |             vary = Header::parse_header(&"etag,cookie,allow".into()); | ||||||
|             assert_eq!(vary.ok(), Some(Vary::Items(vec!["eTag".parse().unwrap(), |             assert_eq!(vary.ok(), Some(Vary::Items(vec!["eTag".parse().unwrap(), | ||||||
|                                                         "cookIE".parse().unwrap(), |                                                         "cookIE".parse().unwrap(), | ||||||
|                                                         "AlLOw".parse().unwrap(),]))); |                                                         "AlLOw".parse().unwrap(),]))); | ||||||
|   | |||||||
| @@ -4,18 +4,18 @@ use std::fmt; | |||||||
| use std::str::from_utf8; | use std::str::from_utf8; | ||||||
|  |  | ||||||
| use super::cell::{OptCell, PtrMapCell}; | use super::cell::{OptCell, PtrMapCell}; | ||||||
| use header::{Header}; | use header::{Header, Raw}; | ||||||
|  |  | ||||||
|  |  | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub struct Item { | pub struct Item { | ||||||
|     raw: OptCell<Vec<Vec<u8>>>, |     raw: OptCell<Raw>, | ||||||
|     typed: PtrMapCell<Header + Send + Sync> |     typed: PtrMapCell<Header + Send + Sync> | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Item { | impl Item { | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn new_raw(data: Vec<Vec<u8>>) -> Item { |     pub fn new_raw(data: Raw) -> Item { | ||||||
|         Item { |         Item { | ||||||
|             raw: OptCell::new(Some(data)), |             raw: OptCell::new(Some(data)), | ||||||
|             typed: PtrMapCell::new(), |             typed: PtrMapCell::new(), | ||||||
| @@ -33,23 +33,22 @@ impl Item { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn mut_raw(&mut self) -> &mut Vec<Vec<u8>> { |     pub fn mut_raw(&mut self) -> &mut Raw { | ||||||
|         self.typed = PtrMapCell::new(); |         self.typed = PtrMapCell::new(); | ||||||
|         unsafe { |         unsafe { | ||||||
|             self.raw.get_mut() |             self.raw.get_mut() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn raw(&self) -> &[Vec<u8>] { |     pub fn raw(&self) -> &Raw { | ||||||
|         if let Some(ref raw) = *self.raw { |         if let Some(ref raw) = *self.raw { | ||||||
|             return &raw[..]; |             return raw; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let raw = vec![unsafe { self.typed.one() }.to_string().into_bytes()]; |         let raw = unsafe { self.typed.one() }.to_string().into_bytes().into(); | ||||||
|         self.raw.set(raw); |         self.raw.set(raw); | ||||||
|  |  | ||||||
|         let raw = self.raw.as_ref().unwrap(); |         self.raw.as_ref().unwrap() | ||||||
|         &raw[..] |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn typed<H: Header + Any>(&self) -> Option<&H> { |     pub fn typed<H: Header + Any>(&self) -> Option<&H> { | ||||||
| @@ -86,10 +85,8 @@ impl Item { | |||||||
| } | } | ||||||
|  |  | ||||||
| #[inline] | #[inline] | ||||||
| fn parse<H: Header>(raw: &Vec<Vec<u8>>) -> | fn parse<H: Header>(raw: &Raw) -> ::Result<Box<Header + Send + Sync>> { | ||||||
|         ::Result<Box<Header + Send + Sync>> { |     H::parse_header(raw).map(|h| { | ||||||
|     Header::parse_header(&raw[..]).map(|h: H| { |  | ||||||
|         // FIXME: Use Type ascription |  | ||||||
|         let h: Box<Header + Send + Sync> = Box::new(h); |         let h: Box<Header + Send + Sync> = Box::new(h); | ||||||
|         h |         h | ||||||
|     }) |     }) | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ | |||||||
| //! | //! | ||||||
| //! ``` | //! ``` | ||||||
| //! use std::fmt; | //! use std::fmt; | ||||||
| //! use hyper::header::Header; | //! use hyper::header::{Header, Raw}; | ||||||
| //! | //! | ||||||
| //! #[derive(Debug, Clone, Copy)] | //! #[derive(Debug, Clone, Copy)] | ||||||
| //! struct Dnt(bool); | //! struct Dnt(bool); | ||||||
| @@ -51,7 +51,7 @@ | |||||||
| //!         "DNT" | //!         "DNT" | ||||||
| //!     } | //!     } | ||||||
| //! | //! | ||||||
| //!     fn parse_header(raw: &[Vec<u8>]) -> hyper::Result<Dnt> { | //!     fn parse_header(raw: &Raw) -> hyper::Result<Dnt> { | ||||||
| //!         if raw.len() == 1 { | //!         if raw.len() == 1 { | ||||||
| //!             let line = &raw[0]; | //!             let line = &raw[0]; | ||||||
| //!             if line.len() == 1 { | //!             if line.len() == 1 { | ||||||
| @@ -94,9 +94,11 @@ use serde::ser; | |||||||
|  |  | ||||||
| pub use self::shared::*; | pub use self::shared::*; | ||||||
| pub use self::common::*; | pub use self::common::*; | ||||||
|  | pub use self::raw::Raw; | ||||||
|  |  | ||||||
| mod common; | mod common; | ||||||
| mod internals; | mod internals; | ||||||
|  | mod raw; | ||||||
| mod shared; | mod shared; | ||||||
| pub mod parsing; | pub mod parsing; | ||||||
|  |  | ||||||
| @@ -117,7 +119,7 @@ pub trait Header: HeaderClone + Any + GetType + Send + Sync { | |||||||
|     /// it's not necessarily the case that a Header is *allowed* to have more |     /// it's not necessarily the case that a Header is *allowed* to have more | ||||||
|     /// than one field value. If that's the case, you **should** return `None` |     /// than one field value. If that's the case, you **should** return `None` | ||||||
|     /// if `raw.len() > 1`. |     /// if `raw.len() > 1`. | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self> where Self: Sized; |     fn parse_header(raw: &Raw) -> ::Result<Self> where Self: Sized; | ||||||
|     /// Format a header to be output into a TcpStream. |     /// Format a header to be output into a TcpStream. | ||||||
|     /// |     /// | ||||||
|     /// This method is not allowed to introduce an Err not produced |     /// This method is not allowed to introduce an Err not produced | ||||||
| @@ -199,7 +201,6 @@ fn header_name<T: Header>() -> &'static str { | |||||||
| /// A map of header fields on requests and responses. | /// A map of header fields on requests and responses. | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub struct Headers { | pub struct Headers { | ||||||
|     //data: HashMap<HeaderName, Item> |  | ||||||
|     data: VecMap<HeaderName, Item>, |     data: VecMap<HeaderName, Item>, | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -270,13 +271,17 @@ impl Headers { | |||||||
|         for header in raw { |         for header in raw { | ||||||
|             trace!("raw header: {:?}={:?}", header.name, &header.value[..]); |             trace!("raw header: {:?}={:?}", header.name, &header.value[..]); | ||||||
|             let name = HeaderName(UniCase(maybe_literal(header.name))); |             let name = HeaderName(UniCase(maybe_literal(header.name))); | ||||||
|             let mut item = match headers.data.entry(name) { |  | ||||||
|                 Entry::Vacant(entry) => entry.insert(Item::new_raw(vec![])), |  | ||||||
|                 Entry::Occupied(entry) => entry.into_mut() |  | ||||||
|             }; |  | ||||||
|             let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count(); |             let trim = header.value.iter().rev().take_while(|&&x| x == b' ').count(); | ||||||
|             let value = &header.value[.. header.value.len() - trim]; |             let value = &header.value[.. header.value.len() - trim]; | ||||||
|             item.mut_raw().push(value.to_vec()); |  | ||||||
|  |             match headers.data.entry(name) { | ||||||
|  |                 Entry::Vacant(entry) => { | ||||||
|  |                     entry.insert(Item::new_raw(self::raw::parsed(value))); | ||||||
|  |                 } | ||||||
|  |                 Entry::Occupied(entry) => { | ||||||
|  |                     entry.into_mut().mut_raw().push(value); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|         } |         } | ||||||
|         Ok(headers) |         Ok(headers) | ||||||
|     } |     } | ||||||
| @@ -290,46 +295,6 @@ impl Headers { | |||||||
|                          Item::new_typed(Box::new(value))); |                          Item::new_typed(Box::new(value))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Access the raw value of a header. |  | ||||||
|     /// |  | ||||||
|     /// Prefer to use the typed getters instead. |  | ||||||
|     /// |  | ||||||
|     /// Example: |  | ||||||
|     /// |  | ||||||
|     /// ``` |  | ||||||
|     /// # use hyper::header::Headers; |  | ||||||
|     /// # let mut headers = Headers::new(); |  | ||||||
|     /// let raw_content_type = headers.get_raw("content-type"); |  | ||||||
|     /// ``` |  | ||||||
|     pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> { |  | ||||||
|         self.data |  | ||||||
|             .get(&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))) |  | ||||||
|             .map(Item::raw) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Set the raw value of a header, bypassing any typed headers. |  | ||||||
|     /// |  | ||||||
|     /// Example: |  | ||||||
|     /// |  | ||||||
|     /// ``` |  | ||||||
|     /// # use hyper::header::Headers; |  | ||||||
|     /// # let mut headers = Headers::new(); |  | ||||||
|     /// headers.set_raw("content-length", vec![b"5".to_vec()]); |  | ||||||
|     /// ``` |  | ||||||
|     pub fn set_raw<K: Into<Cow<'static, str>> + fmt::Debug>(&mut self, name: K, |  | ||||||
|             value: Vec<Vec<u8>>) { |  | ||||||
|         trace!("Headers.set_raw( {:?}, {:?} )", name, value); |  | ||||||
|         self.data.insert(HeaderName(UniCase(name.into())), Item::new_raw(value)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Remove a header set by set_raw |  | ||||||
|     pub fn remove_raw(&mut self, name: &str) { |  | ||||||
|         trace!("Headers.remove_raw( {:?} )", name); |  | ||||||
|         self.data.remove( |  | ||||||
|             &HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))) |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Get a reference to the header field's value, if it exists. |     /// Get a reference to the header field's value, if it exists. | ||||||
|     pub fn get<H: Header>(&self) -> Option<&H> { |     pub fn get<H: Header>(&self) -> Option<&H> { | ||||||
|         self.data.get(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>())))) |         self.data.get(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>())))) | ||||||
| @@ -379,6 +344,54 @@ impl Headers { | |||||||
|     pub fn clear(&mut self) { |     pub fn clear(&mut self) { | ||||||
|         self.data.clear() |         self.data.clear() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Access the raw value of a header. | ||||||
|  |     /// | ||||||
|  |     /// Prefer to use the typed getters instead. | ||||||
|  |     /// | ||||||
|  |     /// Example: | ||||||
|  |     /// | ||||||
|  |     /// ``` | ||||||
|  |     /// # use hyper::header::Headers; | ||||||
|  |     /// # let mut headers = Headers::new(); | ||||||
|  |     /// # headers.set_raw("content-type", "text/plain"); | ||||||
|  |     /// let raw = headers.get_raw("content-type").unwrap(); | ||||||
|  |     /// assert_eq!(raw, "text/plain"); | ||||||
|  |     /// ``` | ||||||
|  |     pub fn get_raw(&self, name: &str) -> Option<&Raw> { | ||||||
|  |         self.data | ||||||
|  |             .get(&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))) | ||||||
|  |             .map(Item::raw) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Set the raw value of a header, bypassing any typed headers. | ||||||
|  |     /// | ||||||
|  |     /// Example: | ||||||
|  |     /// | ||||||
|  |     /// ``` | ||||||
|  |     /// # use hyper::header::Headers; | ||||||
|  |     /// # let mut headers = Headers::new(); | ||||||
|  |     /// headers.set_raw("content-length", b"1".as_ref()); | ||||||
|  |     /// headers.set_raw("content-length", "2"); | ||||||
|  |     /// headers.set_raw("content-length", "3".to_string()); | ||||||
|  |     /// headers.set_raw("content-length", vec![vec![b'4']]); | ||||||
|  |     /// ``` | ||||||
|  |     pub fn set_raw<K: Into<Cow<'static, str>>, V: Into<Raw>>(&mut self, name: K, value: V) { | ||||||
|  |         let name = name.into(); | ||||||
|  |         let value = value.into(); | ||||||
|  |         trace!("Headers.set_raw( {:?}, {:?} )", name, value); | ||||||
|  |         self.data.insert(HeaderName(UniCase(name)), Item::new_raw(value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Remove a header by name. | ||||||
|  |     pub fn remove_raw(&mut self, name: &str) { | ||||||
|  |         trace!("Headers.remove_raw( {:?} )", name); | ||||||
|  |         self.data.remove( | ||||||
|  |             &HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| impl PartialEq for Headers { | impl PartialEq for Headers { | ||||||
| @@ -584,7 +597,7 @@ mod tests { | |||||||
|     use mime::Mime; |     use mime::Mime; | ||||||
|     use mime::TopLevel::Text; |     use mime::TopLevel::Text; | ||||||
|     use mime::SubLevel::Plain; |     use mime::SubLevel::Plain; | ||||||
|     use super::{Headers, Header, ContentLength, ContentType, |     use super::{Headers, Header, Raw, ContentLength, ContentType, | ||||||
|                 Accept, Host, qitem}; |                 Accept, Host, qitem}; | ||||||
|     use httparse; |     use httparse; | ||||||
|  |  | ||||||
| @@ -622,7 +635,7 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_content_type() { |     fn test_content_type() { | ||||||
|         let content_type = Header::parse_header([b"text/plain".to_vec()].as_ref()); |         let content_type = Header::parse_header(&b"text/plain".as_ref().into()); | ||||||
|         assert_eq!(content_type.ok(), Some(ContentType(Mime(Text, Plain, vec![])))); |         assert_eq!(content_type.ok(), Some(ContentType(Mime(Text, Plain, vec![])))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -631,11 +644,11 @@ mod tests { | |||||||
|         let text_plain = qitem(Mime(Text, Plain, vec![])); |         let text_plain = qitem(Mime(Text, Plain, vec![])); | ||||||
|         let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap(); |         let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap(); | ||||||
|  |  | ||||||
|         let accept = Header::parse_header([b"text/plain".to_vec()].as_ref()); |         let accept = Header::parse_header(&b"text/plain".as_ref().into()); | ||||||
|         assert_eq!(accept.ok(), Some(Accept(vec![text_plain.clone()]))); |         assert_eq!(accept.ok(), Some(Accept(vec![text_plain.clone()]))); | ||||||
|  |  | ||||||
|         let bytevec = [b"application/vnd.github.v3.full+json; q=0.5, text/plain".to_vec()]; |         let bytevec = b"application/vnd.github.v3.full+json; q=0.5, text/plain".as_ref().into(); | ||||||
|         let accept = Header::parse_header(bytevec.as_ref()); |         let accept = Header::parse_header(&bytevec); | ||||||
|         assert_eq!(accept.ok(), Some(Accept(vec![application_vendor, text_plain]))); |         assert_eq!(accept.ok(), Some(Accept(vec![application_vendor, text_plain]))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -646,20 +659,15 @@ mod tests { | |||||||
|         fn header_name() -> &'static str { |         fn header_name() -> &'static str { | ||||||
|             "content-length" |             "content-length" | ||||||
|         } |         } | ||||||
|         fn parse_header(raw: &[Vec<u8>]) -> ::Result<CrazyLength> { |         fn parse_header(raw: &Raw) -> ::Result<CrazyLength> { | ||||||
|             use std::str::from_utf8; |             use std::str::from_utf8; | ||||||
|             use std::str::FromStr; |             use std::str::FromStr; | ||||||
|  |  | ||||||
|             if raw.len() != 1 { |             if let Some(line) = raw.one() { | ||||||
|                 return Err(::Error::Header); |                 let s = try!(from_utf8(line).map(|s| FromStr::from_str(s).map_err(|_| ::Error::Header))); | ||||||
|             } |                 s.map(|u| CrazyLength(Some(false), u)) | ||||||
|             // we JUST checked that raw.len() == 1, so raw[0] WILL exist. |             } else { | ||||||
|             match match from_utf8(unsafe { &raw.get_unchecked(0)[..] }) { |                 Err(::Error::Header) | ||||||
|                 Ok(s) => FromStr::from_str(s).ok(), |  | ||||||
|                 Err(_) => None |  | ||||||
|             }.map(|u| CrazyLength(Some(false), u)) { |  | ||||||
|                 Some(x) => Ok(x), |  | ||||||
|                 None => Err(::Error::Header), |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,13 +6,17 @@ use std::str::FromStr; | |||||||
| use std::fmt::{self, Display}; | use std::fmt::{self, Display}; | ||||||
| use url::percent_encoding; | use url::percent_encoding; | ||||||
|  |  | ||||||
|  | use header::Raw; | ||||||
| use header::shared::Charset; | use header::shared::Charset; | ||||||
|  |  | ||||||
| /// 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>]) -> ::Result<T> { | pub fn from_one_raw_str<T: str::FromStr>(raw: &Raw) -> ::Result<T> { | ||||||
|     if raw.len() != 1 || unsafe { raw.get_unchecked(0) } == b"" { return Err(::Error::Header) } |     if let Some(line) = raw.one() { | ||||||
|     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. |         if !line.is_empty() { | ||||||
|     from_raw_str( unsafe { raw.get_unchecked(0) }) |             return from_raw_str(line) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     Err(::Error::Header) | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Reads a raw string into a value. | /// Reads a raw string into a value. | ||||||
| @@ -23,7 +27,7 @@ pub fn from_raw_str<T: str::FromStr>(raw: &[u8]) -> ::Result<T> { | |||||||
|  |  | ||||||
| /// Reads a comma-delimited raw header into a Vec. | /// Reads a comma-delimited raw header into a Vec. | ||||||
| #[inline] | #[inline] | ||||||
| pub fn from_comma_delimited<T: str::FromStr, S: AsRef<[u8]>>(raw: &[S]) -> ::Result<Vec<T>> { | pub fn from_comma_delimited<T: str::FromStr>(raw: &Raw) -> ::Result<Vec<T>> { | ||||||
|     let mut result = Vec::new(); |     let mut result = Vec::new(); | ||||||
|     for s in raw { |     for s in raw { | ||||||
|         let s = try!(str::from_utf8(s.as_ref())); |         let s = try!(str::from_utf8(s.as_ref())); | ||||||
|   | |||||||
							
								
								
									
										229
									
								
								src/header/raw.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								src/header/raw.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,229 @@ | |||||||
|  | use std::borrow::Cow; | ||||||
|  | use std::fmt; | ||||||
|  |  | ||||||
|  | /// A raw header value. | ||||||
|  | #[derive(Clone, PartialEq, Eq)] | ||||||
|  | pub struct Raw(Lines); | ||||||
|  |  | ||||||
|  | impl Raw { | ||||||
|  |     /// Returns the amount of lines. | ||||||
|  |     #[inline] | ||||||
|  |     pub fn len(&self) -> usize { | ||||||
|  |         match self.0 { | ||||||
|  |             Lines::One(..) => 1, | ||||||
|  |             Lines::Many(ref lines) => lines.len() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns the line if there is only 1. | ||||||
|  |     #[inline] | ||||||
|  |     pub fn one(&self) -> Option<&[u8]> { | ||||||
|  |         match self.0 { | ||||||
|  |             Lines::One(ref line) => Some(line), | ||||||
|  |             Lines::Many(ref lines) if lines.len() == 1 => Some(&lines[0]), | ||||||
|  |             _ => None | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Iterate the lines of raw bytes. | ||||||
|  |     #[inline] | ||||||
|  |     pub fn iter(&self) -> RawLines { | ||||||
|  |         RawLines { | ||||||
|  |             inner: match self.0 { | ||||||
|  |                 Lines::One(ref line) => unsafe { | ||||||
|  |                     ::std::slice::from_raw_parts(line, 1) | ||||||
|  |                 }.iter(), | ||||||
|  |                 Lines::Many(ref lines) => lines.iter() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Append a line to this `Raw` header value. | ||||||
|  |     pub fn push(&mut self, val: &[u8]) { | ||||||
|  |         let lines = ::std::mem::replace(&mut self.0, Lines::Many(Vec::new())); | ||||||
|  |         match lines { | ||||||
|  |             Lines::One(line) => { | ||||||
|  |                 self.0 = Lines::Many(vec![line, maybe_literal(val.into())]); | ||||||
|  |             } | ||||||
|  |             Lines::Many(mut lines) => { | ||||||
|  |                 lines.push(maybe_literal(val.into())); | ||||||
|  |                 self.0 = Lines::Many(lines); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Clone, PartialEq, Eq)] | ||||||
|  | enum Lines { | ||||||
|  |     One(Line), | ||||||
|  |     Many(Vec<Line>) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Line = Cow<'static, [u8]>; | ||||||
|  |  | ||||||
|  | fn eq<A: AsRef<[u8]>, B: AsRef<[u8]>>(a: &[A], b: &[B]) -> bool { | ||||||
|  |     if a.len() != b.len() { | ||||||
|  |         false | ||||||
|  |     } else { | ||||||
|  |         for (a, b) in a.iter().zip(b.iter()) { | ||||||
|  |             if a.as_ref() != b.as_ref() { | ||||||
|  |                 return false | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         true | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl PartialEq<[Vec<u8>]> for Raw { | ||||||
|  |     fn eq(&self, bytes: &[Vec<u8>]) -> bool { | ||||||
|  |         match self.0 { | ||||||
|  |             Lines::One(ref line) => eq(&[line], bytes), | ||||||
|  |             Lines::Many(ref lines) => eq(lines, bytes) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl PartialEq<[u8]> for Raw { | ||||||
|  |     fn eq(&self, bytes: &[u8]) -> bool { | ||||||
|  |         match self.0 { | ||||||
|  |             Lines::One(ref line) => line.as_ref() == bytes, | ||||||
|  |             Lines::Many(..) => false | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl PartialEq<str> for Raw { | ||||||
|  |     fn eq(&self, s: &str) -> bool { | ||||||
|  |         match self.0 { | ||||||
|  |             Lines::One(ref line) => line.as_ref() == s.as_bytes(), | ||||||
|  |             Lines::Many(..) => false | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<Vec<Vec<u8>>> for Raw { | ||||||
|  |     #[inline] | ||||||
|  |     fn from(val: Vec<Vec<u8>>) -> Raw { | ||||||
|  |         Raw(Lines::Many( | ||||||
|  |             val.into_iter() | ||||||
|  |                 .map(|vec| maybe_literal(vec.into())) | ||||||
|  |                 .collect() | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<String> for Raw { | ||||||
|  |     #[inline] | ||||||
|  |     fn from(val: String) -> Raw { | ||||||
|  |         let vec: Vec<u8> = val.into(); | ||||||
|  |         vec.into() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<Vec<u8>> for Raw { | ||||||
|  |     #[inline] | ||||||
|  |     fn from(val: Vec<u8>) -> Raw { | ||||||
|  |         Raw(Lines::One(Cow::Owned(val))) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<&'static str> for Raw { | ||||||
|  |     fn from(val: &'static str) -> Raw { | ||||||
|  |         Raw(Lines::One(Cow::Borrowed(val.as_bytes()))) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl From<&'static [u8]> for Raw { | ||||||
|  |     fn from(val: &'static [u8]) -> Raw { | ||||||
|  |         Raw(Lines::One(Cow::Borrowed(val))) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub fn parsed(val: &[u8]) -> Raw { | ||||||
|  |     Raw(Lines::One(maybe_literal(val.into()))) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl fmt::Debug for Raw { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|  |         match self.0 { | ||||||
|  |             Lines::One(ref line) => fmt::Debug::fmt(&[line], f), | ||||||
|  |             Lines::Many(ref lines) => fmt::Debug::fmt(lines, f) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl ::std::ops::Index<usize> for Raw { | ||||||
|  |     type Output = [u8]; | ||||||
|  |     fn index(&self, idx: usize) -> &[u8] { | ||||||
|  |         match self.0 { | ||||||
|  |             Lines::One(ref line) => if idx == 0 { | ||||||
|  |                 line.as_ref() | ||||||
|  |             } else { | ||||||
|  |                 panic!("index out of bounds: {}", idx) | ||||||
|  |             }, | ||||||
|  |             Lines::Many(ref lines) => lines[idx].as_ref() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | macro_rules! literals { | ||||||
|  |     ($($len:expr => $($value:expr),+;)+) => ( | ||||||
|  |         fn maybe_literal<'a>(s: Cow<'a, [u8]>) -> Cow<'static, [u8]> { | ||||||
|  |             match s.len() { | ||||||
|  |                 $($len => { | ||||||
|  |                     $( | ||||||
|  |                     if s.as_ref() == $value { | ||||||
|  |                         return Cow::Borrowed($value); | ||||||
|  |                     } | ||||||
|  |                     )+ | ||||||
|  |                 })+ | ||||||
|  |  | ||||||
|  |                 _ => () | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             Cow::Owned(s.into_owned()) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         #[test] | ||||||
|  |         fn test_literal_lens() { | ||||||
|  |             $( | ||||||
|  |             $({ | ||||||
|  |                 let s = $value; | ||||||
|  |                 assert!(s.len() == $len, "{:?} has len of {}, listed as {}", s, s.len(), $len); | ||||||
|  |             })+ | ||||||
|  |             )+ | ||||||
|  |         } | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | literals! { | ||||||
|  |     1  => b"*", b"0"; | ||||||
|  |     3  => b"*/*"; | ||||||
|  |     4  => b"gzip"; | ||||||
|  |     5  => b"close"; | ||||||
|  |     7  => b"chunked"; | ||||||
|  |     10 => b"keep-alive"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'a> IntoIterator for &'a Raw { | ||||||
|  |     type IntoIter = RawLines<'a>; | ||||||
|  |     type Item = &'a [u8]; | ||||||
|  |  | ||||||
|  |     fn into_iter(self) -> RawLines<'a> { | ||||||
|  |         self.iter() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct RawLines<'a> { | ||||||
|  |     inner: ::std::slice::Iter<'a, Cow<'static, [u8]>> | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'a> Iterator for RawLines<'a> { | ||||||
|  |     type Item = &'a [u8]; | ||||||
|  |  | ||||||
|  |     #[inline] | ||||||
|  |     fn next(&mut self) -> Option<&'a [u8]> { | ||||||
|  |         self.inner.next().map(AsRef::as_ref) | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user