Merge pull request #562 from pyfisch/parseerror
refactor(headers): errors for parse_header
This commit is contained in:
		| @@ -58,8 +58,8 @@ impl hyper::header::Header for Foo { | |||||||
|     fn header_name() -> &'static str { |     fn header_name() -> &'static str { | ||||||
|         "x-foo" |         "x-foo" | ||||||
|     } |     } | ||||||
|     fn parse_header(_: &[Vec<u8>]) -> Option<Foo> { |     fn parse_header(_: &[Vec<u8>]) -> hyper::Result<Foo> { | ||||||
|         None |         Err(hyper::Error::Header) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -104,4 +104,3 @@ fn bench_mock_hyper(b: &mut test::Bencher) { | |||||||
|             .read_to_string(&mut s).unwrap() |             .read_to_string(&mut s).unwrap() | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -2,6 +2,7 @@ | |||||||
| use std::error::Error as StdError; | use std::error::Error as StdError; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::io::Error as IoError; | use std::io::Error as IoError; | ||||||
|  | use std::str::Utf8Error; | ||||||
|  |  | ||||||
| use httparse; | use httparse; | ||||||
| use openssl::ssl::error::SslError; | use openssl::ssl::error::SslError; | ||||||
| @@ -18,6 +19,7 @@ use self::Error::{ | |||||||
|     Ssl, |     Ssl, | ||||||
|     TooLarge, |     TooLarge, | ||||||
|     Http2, |     Http2, | ||||||
|  |     Utf8 | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -45,6 +47,8 @@ pub enum Error { | |||||||
|     Ssl(SslError), |     Ssl(SslError), | ||||||
|     /// An HTTP/2-specific error, coming from the `solicit` library. |     /// An HTTP/2-specific error, coming from the `solicit` library. | ||||||
|     Http2(Http2Error), |     Http2(Http2Error), | ||||||
|  |     /// Parsing a field as string failed | ||||||
|  |     Utf8(Utf8Error), | ||||||
|  |  | ||||||
|     #[doc(hidden)] |     #[doc(hidden)] | ||||||
|     __Nonexhaustive(Void) |     __Nonexhaustive(Void) | ||||||
| @@ -77,6 +81,7 @@ impl StdError for Error { | |||||||
|             Io(ref e) => e.description(), |             Io(ref e) => e.description(), | ||||||
|             Ssl(ref e) => e.description(), |             Ssl(ref e) => e.description(), | ||||||
|             Http2(ref e) => e.description(), |             Http2(ref e) => e.description(), | ||||||
|  |             Utf8(ref e) => e.description(), | ||||||
|             Error::__Nonexhaustive(ref void) =>  match *void {} |             Error::__Nonexhaustive(ref void) =>  match *void {} | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -113,6 +118,12 @@ impl From<SslError> for Error { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl From<Utf8Error> for Error { | ||||||
|  |     fn from(err: Utf8Error) -> Error { | ||||||
|  |         Utf8(err) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| impl From<httparse::Error> for Error { | impl From<httparse::Error> for Error { | ||||||
|     fn from(err: httparse::Error) -> Error { |     fn from(err: httparse::Error) -> Error { | ||||||
|         match err { |         match err { | ||||||
|   | |||||||
| @@ -52,8 +52,8 @@ pub enum RangeUnit { | |||||||
|  |  | ||||||
|  |  | ||||||
| impl FromStr for RangeUnit { | impl FromStr for RangeUnit { | ||||||
|     type Err = (); |     type Err = ::Error; | ||||||
|     fn from_str(s: &str) -> Result<Self, ()> { |     fn from_str(s: &str) -> ::Result<Self> { | ||||||
|         match s { |         match s { | ||||||
|             "bytes" => Ok(RangeUnit::Bytes), |             "bytes" => Ok(RangeUnit::Bytes), | ||||||
|             "none" => Ok(RangeUnit::None), |             "none" => Ok(RangeUnit::None), | ||||||
|   | |||||||
| @@ -35,16 +35,14 @@ impl Header for AccessControlAllowOrigin { | |||||||
|         "Access-Control-Allow-Origin" |         "Access-Control-Allow-Origin" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<AccessControlAllowOrigin> { |     fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowOrigin> { | ||||||
|         if raw.len() == 1 { |         if raw.len() == 1 { | ||||||
|             match unsafe { &raw.get_unchecked(0)[..] } { |             match unsafe { &raw.get_unchecked(0)[..] } { | ||||||
|                 b"*" => Some(AccessControlAllowOrigin::Any), |                 b"*" => Ok(AccessControlAllowOrigin::Any), | ||||||
|                 b"null" => Some(AccessControlAllowOrigin::Null), |                 b"null" => Ok(AccessControlAllowOrigin::Null), | ||||||
|                 r => if let Ok(s) = str::from_utf8(r) { |                 r => Ok(AccessControlAllowOrigin::Value(try!(Url::parse(try!(str::from_utf8(r)))))) | ||||||
|                     Url::parse(s).ok().map(AccessControlAllowOrigin::Value) |  | ||||||
|                 } else { None } |  | ||||||
|             } |             } | ||||||
|         } else { None } |         } else { Err(::Error::Header) } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -42,18 +42,25 @@ impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'st | |||||||
|         "Authorization" |         "Authorization" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<Authorization<S>> { |     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> { | ||||||
|         if raw.len() == 1 { |         if raw.len() != 1 { | ||||||
|             match (from_utf8(unsafe { &raw.get_unchecked(0)[..] }), <S as Scheme>::scheme()) { |             return Err(::Error::Header); | ||||||
|                 (Ok(header), Some(scheme)) |         } | ||||||
|                     if header.starts_with(scheme) && header.len() > scheme.len() + 1 => { |         let header = try!(from_utf8(unsafe { &raw.get_unchecked(0)[..] })); | ||||||
|                     header[scheme.len() + 1..].parse::<S>().map(Authorization).ok() |         return if let Some(scheme) = <S as Scheme>::scheme() { | ||||||
|                 }, |             if header.starts_with(scheme) && header.len() > scheme.len() + 1 { | ||||||
|                 (Ok(header), None) => header.parse::<S>().map(Authorization).ok(), |                 match header[scheme.len() + 1..].parse::<S>().map(Authorization) { | ||||||
|                 _ => None |                     Ok(h) => Ok(h), | ||||||
|  |                     Err(_) => Err(::Error::Header) | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|             None |                 Err(::Error::Header) | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             match header.parse::<S>().map(Authorization) { | ||||||
|  |                 Ok(h) => Ok(h), | ||||||
|  |                 Err(_) => Err(::Error::Header) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -121,15 +128,15 @@ impl Scheme for Basic { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl FromStr for Basic { | impl FromStr for Basic { | ||||||
|     type Err = (); |     type Err = ::Error; | ||||||
|     fn from_str(s: &str) -> Result<Basic, ()> { |     fn from_str(s: &str) -> ::Result<Basic> { | ||||||
|         match s.from_base64() { |         match s.from_base64() { | ||||||
|             Ok(decoded) => match String::from_utf8(decoded) { |             Ok(decoded) => match String::from_utf8(decoded) { | ||||||
|                 Ok(text) => { |                 Ok(text) => { | ||||||
|                     let mut parts = &mut text.split(':'); |                     let mut parts = &mut text.split(':'); | ||||||
|                     let user = match parts.next() { |                     let user = match parts.next() { | ||||||
|                         Some(part) => part.to_owned(), |                         Some(part) => part.to_owned(), | ||||||
|                         None => return Err(()) |                         None => return Err(::Error::Header) | ||||||
|                     }; |                     }; | ||||||
|                     let password = match parts.next() { |                     let password = match parts.next() { | ||||||
|                         Some(part) => Some(part.to_owned()), |                         Some(part) => Some(part.to_owned()), | ||||||
| @@ -142,12 +149,12 @@ impl FromStr for Basic { | |||||||
|                 }, |                 }, | ||||||
|                 Err(e) => { |                 Err(e) => { | ||||||
|                     debug!("Basic::from_utf8 error={:?}", e); |                     debug!("Basic::from_utf8 error={:?}", e); | ||||||
|                     Err(()) |                     Err(::Error::Header) | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             Err(e) => { |             Err(e) => { | ||||||
|                 debug!("Basic::from_base64 error={:?}", e); |                 debug!("Basic::from_base64 error={:?}", e); | ||||||
|                 Err(()) |                 Err(::Error::Header) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -30,15 +30,15 @@ impl Header for CacheControl { | |||||||
|         "Cache-Control" |         "Cache-Control" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<CacheControl> { |     fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> { | ||||||
|         let directives = raw.iter() |         let directives = raw.iter() | ||||||
|             .filter_map(|line| from_one_comma_delimited(&line[..])) |             .filter_map(|line| from_one_comma_delimited(&line[..]).ok()) | ||||||
|             .collect::<Vec<Vec<CacheDirective>>>() |             .collect::<Vec<Vec<CacheDirective>>>() | ||||||
|             .concat(); |             .concat(); | ||||||
|         if !directives.is_empty() { |         if !directives.is_empty() { | ||||||
|             Some(CacheControl(directives)) |             Ok(CacheControl(directives)) | ||||||
|         } else { |         } else { | ||||||
|             None |             Err(::Error::Header) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -148,35 +148,35 @@ 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(&[b"no-cache".to_vec(), b"private".to_vec()]); | ||||||
|         assert_eq!(cache, 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(&[b"max-age=100, private".to_vec()]); | ||||||
|         assert_eq!(cache, 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(&[b"max-age=\"200\"".to_vec()]); | ||||||
|         assert_eq!(cache, 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(&[b"foo, bar=baz".to_vec()]); | ||||||
|         assert_eq!(cache, 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()))]))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_parse_bad_syntax() { |     fn test_parse_bad_syntax() { | ||||||
|         let cache: Option<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]); |         let cache: ::Result<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]); | ||||||
|         assert_eq!(cache, None) |         assert_eq!(cache.ok(), None) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -27,26 +27,23 @@ impl Header for Cookie { | |||||||
|         "Cookie" |         "Cookie" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<Cookie> { |     fn parse_header(raw: &[Vec<u8>]) -> ::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() { | ||||||
|             match from_utf8(&cookies_raw[..]) { |             let cookies_str = try!(from_utf8(&cookies_raw[..])); | ||||||
|                 Ok(cookies_str) => { |  | ||||||
|             for cookie_str in cookies_str.split(';') { |             for cookie_str in cookies_str.split(';') { | ||||||
|                         match cookie_str.trim().parse() { |                 if let Ok(cookie) = cookie_str.trim().parse() { | ||||||
|                             Ok(cookie) => cookies.push(cookie), |                     cookies.push(cookie); | ||||||
|                             Err(_) => return None |                 } else { | ||||||
|  |                     return Err(::Error::Header); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|                 }, |  | ||||||
|                 Err(_) => return None |  | ||||||
|             }; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if !cookies.is_empty() { |         if !cookies.is_empty() { | ||||||
|             Some(Cookie(cookies)) |             Ok(Cookie(cookies)) | ||||||
|         } else { |         } else { | ||||||
|             None |             Err(::Error::Header) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -88,7 +85,7 @@ 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".to_vec()][..]); | ||||||
|     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, Some(Cookie(vec![c1, c2]))); |     assert_eq!(h.ok(), Some(Cookie(vec![c1, c2]))); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ impl Header for Expect { | |||||||
|         "Expect" |         "Expect" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<Expect> { |     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> { | ||||||
|         if raw.len() == 1 { |         if raw.len() == 1 { | ||||||
|             let text = unsafe { |             let text = unsafe { | ||||||
|                 // safe because: |                 // safe because: | ||||||
| @@ -38,12 +38,12 @@ impl Header for Expect { | |||||||
|                 str::from_utf8_unchecked(raw.get_unchecked(0)) |                 str::from_utf8_unchecked(raw.get_unchecked(0)) | ||||||
|             }; |             }; | ||||||
|             if UniCase(text) == EXPECT_CONTINUE { |             if UniCase(text) == EXPECT_CONTINUE { | ||||||
|                 Some(Expect::Continue) |                 Ok(Expect::Continue) | ||||||
|             } else { |             } else { | ||||||
|                 None |                 Err(::Error::Header) | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             None |             Err(::Error::Header) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ impl Header for Host { | |||||||
|         "Host" |         "Host" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<Host> { |     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> { | ||||||
|         from_one_raw_str(raw).and_then(|mut s: String| { |         from_one_raw_str(raw).and_then(|mut s: String| { | ||||||
|             // FIXME: use rust-url to parse this |             // FIXME: use rust-url to parse this | ||||||
|             // https://github.com/servo/rust-url/issues/42 |             // https://github.com/servo/rust-url/issues/42 | ||||||
| @@ -39,7 +39,7 @@ impl Header for Host { | |||||||
|                                 None |                                 None | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         None => return None // this is a bad ipv6 address... |                         None => return Err(::Error::Header) // this is a bad ipv6 address... | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     slice.rfind(':') |                     slice.rfind(':') | ||||||
| @@ -56,7 +56,7 @@ impl Header for Host { | |||||||
|                 None => () |                 None => () | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             Some(Host { |             Ok(Host { | ||||||
|                 hostname: s, |                 hostname: s, | ||||||
|                 port: port |                 port: port | ||||||
|             }) |             }) | ||||||
| @@ -82,14 +82,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([b"foo.com".to_vec()].as_ref()); | ||||||
|         assert_eq!(host, 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([b"foo.com:8080".to_vec()].as_ref()); | ||||||
|         assert_eq!(host, Some(Host { |         assert_eq!(host.ok(), Some(Host { | ||||||
|             hostname: "foo.com".to_owned(), |             hostname: "foo.com".to_owned(), | ||||||
|             port: Some(8080) |             port: Some(8080) | ||||||
|         })); |         })); | ||||||
|   | |||||||
| @@ -45,10 +45,10 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_if_none_match() { |     fn test_if_none_match() { | ||||||
|         let mut if_none_match: Option<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"*".to_vec()].as_ref()); | ||||||
|         assert_eq!(if_none_match, 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\"".to_vec()].as_ref()); | ||||||
|         let mut entities: Vec<EntityTag> = Vec::new(); |         let mut entities: Vec<EntityTag> = Vec::new(); | ||||||
| @@ -56,7 +56,7 @@ mod tests { | |||||||
|         let weak_etag = EntityTag::new(true, "weak-etag".to_owned()); |         let weak_etag = EntityTag::new(true, "weak-etag".to_owned()); | ||||||
|         entities.push(foobar_etag); |         entities.push(foobar_etag); | ||||||
|         entities.push(weak_etag); |         entities.push(weak_etag); | ||||||
|         assert_eq!(if_none_match, Some(IfNoneMatch::Items(entities))); |         assert_eq!(if_none_match.ok(), Some(IfNoneMatch::Items(entities))); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -36,16 +36,16 @@ impl Header for IfRange { | |||||||
|     fn header_name() -> &'static str { |     fn header_name() -> &'static str { | ||||||
|         "If-Range" |         "If-Range" | ||||||
|     } |     } | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<IfRange> { |     fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> { | ||||||
|         let etag: Option<EntityTag> = header::parsing::from_one_raw_str(raw); |         let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw); | ||||||
|         if etag != None { |         if etag.is_ok() { | ||||||
|             return Some(IfRange::EntityTag(etag.unwrap())); |             return Ok(IfRange::EntityTag(etag.unwrap())); | ||||||
|         } |         } | ||||||
|         let date: Option<HttpDate> = header::parsing::from_one_raw_str(raw); |         let date: ::Result<HttpDate> = header::parsing::from_one_raw_str(raw); | ||||||
|         if date != None { |         if date.is_ok() { | ||||||
|             return Some(IfRange::Date(date.unwrap())); |             return Ok(IfRange::Date(date.unwrap())); | ||||||
|         } |         } | ||||||
|         None |         Err(::Error::Header) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -144,7 +144,7 @@ macro_rules! test_header { | |||||||
|             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 val = HeaderField::parse_header(&a[..]); | ||||||
|             // Test parsing |             // Test parsing | ||||||
|             assert_eq!(val, $typed); |             assert_eq!(val.ok(), $typed); | ||||||
|             // Test formatting |             // Test formatting | ||||||
|             if $typed != None { |             if $typed != None { | ||||||
|                 let res: &str = str::from_utf8($raw[0]).unwrap(); |                 let res: &str = str::from_utf8($raw[0]).unwrap(); | ||||||
| @@ -171,7 +171,7 @@ macro_rules! header { | |||||||
|             fn header_name() -> &'static str { |             fn header_name() -> &'static str { | ||||||
|                 $n |                 $n | ||||||
|             } |             } | ||||||
|             fn parse_header(raw: &[Vec<u8>]) -> Option<Self> { |             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id) |                 $crate::header::parsing::from_comma_delimited(raw).map($id) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -197,7 +197,7 @@ macro_rules! header { | |||||||
|             fn header_name() -> &'static str { |             fn header_name() -> &'static str { | ||||||
|                 $n |                 $n | ||||||
|             } |             } | ||||||
|             fn parse_header(raw: &[Vec<u8>]) -> Option<Self> { |             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||||
|                 $crate::header::parsing::from_comma_delimited(raw).map($id) |                 $crate::header::parsing::from_comma_delimited(raw).map($id) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -223,7 +223,7 @@ macro_rules! header { | |||||||
|             fn header_name() -> &'static str { |             fn header_name() -> &'static str { | ||||||
|                 $n |                 $n | ||||||
|             } |             } | ||||||
|             fn parse_header(raw: &[Vec<u8>]) -> Option<Self> { |             fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { | ||||||
|                 $crate::header::parsing::from_one_raw_str(raw).map($id) |                 $crate::header::parsing::from_one_raw_str(raw).map($id) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -252,11 +252,11 @@ macro_rules! header { | |||||||
|             fn header_name() -> &'static str { |             fn header_name() -> &'static str { | ||||||
|                 $n |                 $n | ||||||
|             } |             } | ||||||
|             fn parse_header(raw: &[Vec<u8>]) -> Option<Self> { |             fn parse_header(raw: &[Vec<u8>]) -> $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 Some($id::Any) |                         return Ok($id::Any) | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 $crate::header::parsing::from_comma_delimited(raw).map(|vec| $id::Items(vec)) |                 $crate::header::parsing::from_comma_delimited(raw).map(|vec| $id::Items(vec)) | ||||||
|   | |||||||
| @@ -29,12 +29,12 @@ impl Header for Pragma { | |||||||
|         "Pragma" |         "Pragma" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<Pragma> { |     fn parse_header(raw: &[Vec<u8>]) -> ::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 { | ||||||
|                 "no-cache" => Some(Pragma::NoCache), |                 "no-cache" => Ok(Pragma::NoCache), | ||||||
|                 _ => Some(Pragma::Ext(s)), |                 _ => Ok(Pragma::Ext(s)), | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| @@ -57,6 +57,6 @@ fn test_parse_header() { | |||||||
|     let c: Pragma = Header::parse_header([b"FoObar".to_vec()].as_ref()).unwrap(); |     let c: Pragma = Header::parse_header([b"FoObar".to_vec()].as_ref()).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: Option<Pragma> = Header::parse_header([b"".to_vec()].as_ref()); |     let e: ::Result<Pragma> = Header::parse_header([b"".to_vec()].as_ref()); | ||||||
|     assert_eq!(e, None); |     assert_eq!(e.ok(), None); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -64,7 +64,7 @@ impl Header for SetCookie { | |||||||
|         "Set-Cookie" |         "Set-Cookie" | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn parse_header(raw: &[Vec<u8>]) -> Option<SetCookie> { |     fn parse_header(raw: &[Vec<u8>]) -> ::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[..]) { | ||||||
| @@ -75,9 +75,9 @@ impl Header for SetCookie { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if !set_cookies.is_empty() { |         if !set_cookies.is_empty() { | ||||||
|             Some(SetCookie(set_cookies)) |             Ok(SetCookie(set_cookies)) | ||||||
|         } else { |         } else { | ||||||
|             None |             Err(::Error::Header) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -120,7 +120,7 @@ fn test_parse() { | |||||||
|     let mut c1 = Cookie::new("foo".to_owned(), "bar".to_owned()); |     let mut c1 = Cookie::new("foo".to_owned(), "bar".to_owned()); | ||||||
|     c1.httponly = true; |     c1.httponly = true; | ||||||
|  |  | ||||||
|     assert_eq!(h, Some(SetCookie(vec![c1]))); |     assert_eq!(h.ok(), Some(SetCookie(vec![c1]))); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
|   | |||||||
| @@ -45,8 +45,8 @@ 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: Option<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]); |             let x: ::Result<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]); | ||||||
|             assert_eq!(x, Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); |             assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)]))); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,13 +24,13 @@ header! { | |||||||
|  |  | ||||||
|         #[test] |         #[test] | ||||||
|         fn test2() { |         fn test2() { | ||||||
|             let mut vary: Option<Vary>; |             let mut vary: ::Result<Vary>; | ||||||
|  |  | ||||||
|             vary = Header::parse_header([b"*".to_vec()].as_ref()); |             vary = Header::parse_header([b"*".to_vec()].as_ref()); | ||||||
|             assert_eq!(vary, 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([b"etag,cookie,allow".to_vec()].as_ref()); | ||||||
|             assert_eq!(vary, 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(),]))); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -60,11 +60,11 @@ impl Item { | |||||||
|             Some(val) => Some(val), |             Some(val) => Some(val), | ||||||
|             None => { |             None => { | ||||||
|                 match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) { |                 match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) { | ||||||
|                     Some(typed) => { |                     Ok(typed) => { | ||||||
|                         unsafe { self.typed.insert(tid, typed); } |                         unsafe { self.typed.insert(tid, typed); } | ||||||
|                         self.typed.get(tid) |                         self.typed.get(tid) | ||||||
|                     }, |                     }, | ||||||
|                     None => None |                     Err(_) => None | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }.map(|typed| unsafe { typed.downcast_ref_unchecked() }) |         }.map(|typed| unsafe { typed.downcast_ref_unchecked() }) | ||||||
| @@ -74,10 +74,10 @@ impl Item { | |||||||
|         let tid = TypeId::of::<H>(); |         let tid = TypeId::of::<H>(); | ||||||
|         if self.typed.get_mut(tid).is_none() { |         if self.typed.get_mut(tid).is_none() { | ||||||
|             match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) { |             match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) { | ||||||
|                 Some(typed) => { |                 Ok(typed) => { | ||||||
|                     unsafe { self.typed.insert(tid, typed); } |                     unsafe { self.typed.insert(tid, typed); } | ||||||
|                 }, |                 }, | ||||||
|                 None => () |                 Err(_) => () | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         self.typed.get_mut(tid).map(|typed| unsafe { typed.downcast_mut_unchecked() }) |         self.typed.get_mut(tid).map(|typed| unsafe { typed.downcast_mut_unchecked() }) | ||||||
| @@ -85,7 +85,7 @@ impl Item { | |||||||
| } | } | ||||||
|  |  | ||||||
| #[inline] | #[inline] | ||||||
| fn parse<H: Header + HeaderFormat>(raw: &Vec<Vec<u8>>) -> Option<Box<HeaderFormat + Send + Sync>> { | fn parse<H: Header + HeaderFormat>(raw: &Vec<Vec<u8>>) -> ::Result<Box<HeaderFormat + Send + Sync>> { | ||||||
|     Header::parse_header(&raw[..]).map(|h: H| { |     Header::parse_header(&raw[..]).map(|h: H| { | ||||||
|         // FIXME: Use Type ascription |         // FIXME: Use Type ascription | ||||||
|         let h: Box<HeaderFormat + Send + Sync> = Box::new(h); |         let h: Box<HeaderFormat + Send + Sync> = Box::new(h); | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ pub trait Header: Clone + Any + 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>]) -> Option<Self>; |     fn parse_header(raw: &[Vec<u8>]) -> ::Result<Self>; | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -420,7 +420,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".to_vec()].as_ref()); | ||||||
|         assert_eq!(content_type, Some(ContentType(Mime(Text, Plain, vec![])))); |         assert_eq!(content_type.ok(), Some(ContentType(Mime(Text, Plain, vec![])))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
| @@ -429,10 +429,10 @@ mod tests { | |||||||
|         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".to_vec()].as_ref()); | ||||||
|         assert_eq!(accept, Some(Accept(vec![text_plain.clone()]))); |         assert_eq!(accept.ok(), Some(Accept(vec![text_plain.clone()]))); | ||||||
|  |  | ||||||
|         let accept = Header::parse_header([b"application/vnd.github.v3.full+json; q=0.5, text/plain".to_vec()].as_ref()); |         let accept = Header::parse_header([b"application/vnd.github.v3.full+json; q=0.5, text/plain".to_vec()].as_ref()); | ||||||
|         assert_eq!(accept, Some(Accept(vec![application_vendor, text_plain]))); |         assert_eq!(accept.ok(), Some(Accept(vec![application_vendor, text_plain]))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[derive(Clone, PartialEq, Debug)] |     #[derive(Clone, PartialEq, Debug)] | ||||||
| @@ -442,18 +442,21 @@ mod tests { | |||||||
|         fn header_name() -> &'static str { |         fn header_name() -> &'static str { | ||||||
|             "content-length" |             "content-length" | ||||||
|         } |         } | ||||||
|         fn parse_header(raw: &[Vec<u8>]) -> Option<CrazyLength> { |         fn parse_header(raw: &[Vec<u8>]) -> ::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 raw.len() != 1 { | ||||||
|                 return None; |                 return Err(::Error::Header); | ||||||
|             } |             } | ||||||
|             // we JUST checked that raw.len() == 1, so raw[0] WILL exist. |             // we JUST checked that raw.len() == 1, so raw[0] WILL exist. | ||||||
|             match from_utf8(unsafe { &raw.get_unchecked(0)[..] }) { |             match match from_utf8(unsafe { &raw.get_unchecked(0)[..] }) { | ||||||
|                 Ok(s) => FromStr::from_str(s).ok(), |                 Ok(s) => FromStr::from_str(s).ok(), | ||||||
|                 Err(_) => None |                 Err(_) => None | ||||||
|             }.map(|u| CrazyLength(Some(false), u)) |             }.map(|u| CrazyLength(Some(false), u)) { | ||||||
|  |                 Some(x) => Ok(x), | ||||||
|  |                 None => Err(::Error::Header), | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,44 +4,37 @@ use std::str; | |||||||
| use std::fmt::{self, Display}; | use std::fmt::{self, Display}; | ||||||
|  |  | ||||||
| /// Reads a single raw string when parsing a header | /// Reads a single raw string when parsing a header | ||||||
| pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> Option<T> { | pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> ::Result<T> { | ||||||
|     if raw.len() != 1 { |     if raw.len() != 1 || unsafe { raw.get_unchecked(0) } == b"" { return Err(::Error::Header) } | ||||||
|         return None; |  | ||||||
|     } |  | ||||||
|     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. |     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. | ||||||
|     if let Ok(s) = str::from_utf8(& unsafe { raw.get_unchecked(0) }[..]) { |     let s: &str = try!(str::from_utf8(& unsafe { raw.get_unchecked(0) }[..])); | ||||||
|         if s != "" { |     if let Ok(x) = str::FromStr::from_str(s) { | ||||||
|             return str::FromStr::from_str(s).ok(); |         Ok(x) | ||||||
|  |     } else { | ||||||
|  |         Err(::Error::Header) | ||||||
|     } |     } | ||||||
|     } |  | ||||||
|     None |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// 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>(raw: &[Vec<u8>]) -> Option<Vec<T>> { | pub fn from_comma_delimited<T: str::FromStr>(raw: &[Vec<u8>]) -> ::Result<Vec<T>> { | ||||||
|     if raw.len() != 1 { |     if raw.len() != 1 { | ||||||
|         return None; |         return Err(::Error::Header); | ||||||
|     } |     } | ||||||
|     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. |     // we JUST checked that raw.len() == 1, so raw[0] WILL exist. | ||||||
|     from_one_comma_delimited(& unsafe { raw.get_unchecked(0) }[..]) |     from_one_comma_delimited(& unsafe { raw.get_unchecked(0) }[..]) | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Reads a comma-delimited raw string into a Vec. | /// Reads a comma-delimited raw string into a Vec. | ||||||
| pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> Option<Vec<T>> { | pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> ::Result<Vec<T>> { | ||||||
|     match str::from_utf8(raw) { |     let s = try!(str::from_utf8(raw)); | ||||||
|         Ok(s) => { |     Ok(s.split(',') | ||||||
|             Some(s |  | ||||||
|                  .split(',') |  | ||||||
|         .filter_map(|x| match x.trim() { |         .filter_map(|x| match x.trim() { | ||||||
|             "" => None, |             "" => None, | ||||||
|             y => Some(y) |             y => Some(y) | ||||||
|         }) |         }) | ||||||
|         .filter_map(|x| x.parse().ok()) |         .filter_map(|x| x.parse().ok()) | ||||||
|         .collect()) |         .collect()) | ||||||
|         } |  | ||||||
|         Err(_) => None |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Format an array into a comma-delimited string. | /// Format an array into a comma-delimited string. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user