refactor(headers): errors for parse_header
Header::parse_header() returns now a hyper Result instead of an option this will enable more precise Error messages in the future, currently most failures are reported as ::Error::Header. BREAKING CHANGE: parse_header returns Result instead of Option, related code did also change
This commit is contained in:
@@ -58,8 +58,8 @@ impl hyper::header::Header for Foo {
|
||||
fn header_name() -> &'static str {
|
||||
"x-foo"
|
||||
}
|
||||
fn parse_header(_: &[Vec<u8>]) -> Option<Foo> {
|
||||
None
|
||||
fn parse_header(_: &[Vec<u8>]) -> hyper::Result<Foo> {
|
||||
Err(hyper::Error::Header)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,4 +104,3 @@ fn bench_mock_hyper(b: &mut test::Bencher) {
|
||||
.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::fmt;
|
||||
use std::io::Error as IoError;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use httparse;
|
||||
use openssl::ssl::error::SslError;
|
||||
@@ -18,6 +19,7 @@ use self::Error::{
|
||||
Ssl,
|
||||
TooLarge,
|
||||
Http2,
|
||||
Utf8
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +47,8 @@ pub enum Error {
|
||||
Ssl(SslError),
|
||||
/// An HTTP/2-specific error, coming from the `solicit` library.
|
||||
Http2(Http2Error),
|
||||
/// Parsing a field as string failed
|
||||
Utf8(Utf8Error),
|
||||
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive(Void)
|
||||
@@ -77,6 +81,7 @@ impl StdError for Error {
|
||||
Io(ref e) => e.description(),
|
||||
Ssl(ref e) => e.description(),
|
||||
Http2(ref e) => e.description(),
|
||||
Utf8(ref e) => e.description(),
|
||||
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 {
|
||||
fn from(err: httparse::Error) -> Error {
|
||||
match err {
|
||||
|
||||
@@ -52,8 +52,8 @@ pub enum RangeUnit {
|
||||
|
||||
|
||||
impl FromStr for RangeUnit {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, ()> {
|
||||
type Err = ::Error;
|
||||
fn from_str(s: &str) -> ::Result<Self> {
|
||||
match s {
|
||||
"bytes" => Ok(RangeUnit::Bytes),
|
||||
"none" => Ok(RangeUnit::None),
|
||||
|
||||
@@ -35,16 +35,14 @@ impl Header for AccessControlAllowOrigin {
|
||||
"Access-Control-Allow-Origin"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<AccessControlAllowOrigin> {
|
||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowOrigin> {
|
||||
if raw.len() == 1 {
|
||||
match unsafe { &raw.get_unchecked(0)[..] } {
|
||||
b"*" => Some(AccessControlAllowOrigin::Any),
|
||||
b"null" => Some(AccessControlAllowOrigin::Null),
|
||||
r => if let Ok(s) = str::from_utf8(r) {
|
||||
Url::parse(s).ok().map(AccessControlAllowOrigin::Value)
|
||||
} else { None }
|
||||
b"*" => Ok(AccessControlAllowOrigin::Any),
|
||||
b"null" => Ok(AccessControlAllowOrigin::Null),
|
||||
r => Ok(AccessControlAllowOrigin::Value(try!(Url::parse(try!(str::from_utf8(r))))))
|
||||
}
|
||||
} 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"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<Authorization<S>> {
|
||||
if raw.len() == 1 {
|
||||
match (from_utf8(unsafe { &raw.get_unchecked(0)[..] }), <S as Scheme>::scheme()) {
|
||||
(Ok(header), Some(scheme))
|
||||
if header.starts_with(scheme) && header.len() > scheme.len() + 1 => {
|
||||
header[scheme.len() + 1..].parse::<S>().map(Authorization).ok()
|
||||
},
|
||||
(Ok(header), None) => header.parse::<S>().map(Authorization).ok(),
|
||||
_ => None
|
||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> {
|
||||
if raw.len() != 1 {
|
||||
return Err(::Error::Header);
|
||||
}
|
||||
let header = try!(from_utf8(unsafe { &raw.get_unchecked(0)[..] }));
|
||||
return if let Some(scheme) = <S as Scheme>::scheme() {
|
||||
if header.starts_with(scheme) && header.len() > scheme.len() + 1 {
|
||||
match header[scheme.len() + 1..].parse::<S>().map(Authorization) {
|
||||
Ok(h) => Ok(h),
|
||||
Err(_) => Err(::Error::Header)
|
||||
}
|
||||
} else {
|
||||
Err(::Error::Header)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
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 {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Basic, ()> {
|
||||
type Err = ::Error;
|
||||
fn from_str(s: &str) -> ::Result<Basic> {
|
||||
match s.from_base64() {
|
||||
Ok(decoded) => match String::from_utf8(decoded) {
|
||||
Ok(text) => {
|
||||
let mut parts = &mut text.split(':');
|
||||
let user = match parts.next() {
|
||||
Some(part) => part.to_owned(),
|
||||
None => return Err(())
|
||||
None => return Err(::Error::Header)
|
||||
};
|
||||
let password = match parts.next() {
|
||||
Some(part) => Some(part.to_owned()),
|
||||
@@ -142,12 +149,12 @@ impl FromStr for Basic {
|
||||
},
|
||||
Err(e) => {
|
||||
debug!("Basic::from_utf8 error={:?}", e);
|
||||
Err(())
|
||||
Err(::Error::Header)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
debug!("Basic::from_base64 error={:?}", e);
|
||||
Err(())
|
||||
Err(::Error::Header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,15 +30,15 @@ impl Header for CacheControl {
|
||||
"Cache-Control"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<CacheControl> {
|
||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> {
|
||||
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>>>()
|
||||
.concat();
|
||||
if !directives.is_empty() {
|
||||
Some(CacheControl(directives))
|
||||
Ok(CacheControl(directives))
|
||||
} else {
|
||||
None
|
||||
Err(::Error::Header)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,35 +148,35 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse_multiple_headers() {
|
||||
let cache = Header::parse_header(&[b"no-cache".to_vec(), b"private".to_vec()]);
|
||||
assert_eq!(cache, Some(CacheControl(vec![CacheDirective::NoCache,
|
||||
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::NoCache,
|
||||
CacheDirective::Private])))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_argument() {
|
||||
let cache = Header::parse_header(&[b"max-age=100, private".to_vec()]);
|
||||
assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(100),
|
||||
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(100),
|
||||
CacheDirective::Private])))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_quote_form() {
|
||||
let cache = Header::parse_header(&[b"max-age=\"200\"".to_vec()]);
|
||||
assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(200)])))
|
||||
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(200)])))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_extension() {
|
||||
let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]);
|
||||
assert_eq!(cache, Some(CacheControl(vec![
|
||||
assert_eq!(cache.ok(), Some(CacheControl(vec![
|
||||
CacheDirective::Extension("foo".to_owned(), None),
|
||||
CacheDirective::Extension("bar".to_owned(), Some("baz".to_owned()))])))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_bad_syntax() {
|
||||
let cache: Option<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]);
|
||||
assert_eq!(cache, None)
|
||||
let cache: ::Result<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]);
|
||||
assert_eq!(cache.ok(), None)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,26 +27,23 @@ impl Header for 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());
|
||||
for cookies_raw in raw.iter() {
|
||||
match from_utf8(&cookies_raw[..]) {
|
||||
Ok(cookies_str) => {
|
||||
for cookie_str in cookies_str.split(';') {
|
||||
match cookie_str.trim().parse() {
|
||||
Ok(cookie) => cookies.push(cookie),
|
||||
Err(_) => return None
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => return None
|
||||
};
|
||||
let cookies_str = try!(from_utf8(&cookies_raw[..]));
|
||||
for cookie_str in cookies_str.split(';') {
|
||||
if let Ok(cookie) = cookie_str.trim().parse() {
|
||||
cookies.push(cookie);
|
||||
} else {
|
||||
return Err(::Error::Header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !cookies.is_empty() {
|
||||
Some(Cookie(cookies))
|
||||
Ok(Cookie(cookies))
|
||||
} 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 c1 = CookiePair::new("foo".to_owned(), "bar".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]
|
||||
|
||||
@@ -26,7 +26,7 @@ impl Header for Expect {
|
||||
"Expect"
|
||||
}
|
||||
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<Expect> {
|
||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> {
|
||||
if raw.len() == 1 {
|
||||
let text = unsafe {
|
||||
// safe because:
|
||||
@@ -38,12 +38,12 @@ impl Header for Expect {
|
||||
str::from_utf8_unchecked(raw.get_unchecked(0))
|
||||
};
|
||||
if UniCase(text) == EXPECT_CONTINUE {
|
||||
Some(Expect::Continue)
|
||||
Ok(Expect::Continue)
|
||||
} else {
|
||||
None
|
||||
Err(::Error::Header)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
Err(::Error::Header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl Header for 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| {
|
||||
// FIXME: use rust-url to parse this
|
||||
// https://github.com/servo/rust-url/issues/42
|
||||
@@ -39,7 +39,7 @@ impl Header for Host {
|
||||
None
|
||||
}
|
||||
}
|
||||
None => return None // this is a bad ipv6 address...
|
||||
None => return Err(::Error::Header) // this is a bad ipv6 address...
|
||||
}
|
||||
} else {
|
||||
slice.rfind(':')
|
||||
@@ -56,7 +56,7 @@ impl Header for Host {
|
||||
None => ()
|
||||
}
|
||||
|
||||
Some(Host {
|
||||
Ok(Host {
|
||||
hostname: s,
|
||||
port: port
|
||||
})
|
||||
@@ -82,14 +82,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_host() {
|
||||
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(),
|
||||
port: None
|
||||
}));
|
||||
|
||||
|
||||
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(),
|
||||
port: Some(8080)
|
||||
}));
|
||||
|
||||
@@ -45,10 +45,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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());
|
||||
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());
|
||||
let mut entities: Vec<EntityTag> = Vec::new();
|
||||
@@ -56,7 +56,7 @@ mod tests {
|
||||
let weak_etag = EntityTag::new(true, "weak-etag".to_owned());
|
||||
entities.push(foobar_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 {
|
||||
"If-Range"
|
||||
}
|
||||
fn parse_header(raw: &[Vec<u8>]) -> Option<IfRange> {
|
||||
let etag: Option<EntityTag> = header::parsing::from_one_raw_str(raw);
|
||||
if etag != None {
|
||||
return Some(IfRange::EntityTag(etag.unwrap()));
|
||||
fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> {
|
||||
let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw);
|
||||
if etag.is_ok() {
|
||||
return Ok(IfRange::EntityTag(etag.unwrap()));
|
||||
}
|
||||
let date: Option<HttpDate> = header::parsing::from_one_raw_str(raw);
|
||||
if date != None {
|
||||
return Some(IfRange::Date(date.unwrap()));
|
||||
let date: ::Result<HttpDate> = header::parsing::from_one_raw_str(raw);
|
||||
if date.is_ok() {
|
||||
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 val = HeaderField::parse_header(&a[..]);
|
||||
// Test parsing
|
||||
assert_eq!(val, $typed);
|
||||
assert_eq!(val.ok(), $typed);
|
||||
// Test formatting
|
||||
if $typed != None {
|
||||
let res: &str = str::from_utf8($raw[0]).unwrap();
|
||||
@@ -171,7 +171,7 @@ macro_rules! header {
|
||||
fn header_name() -> &'static str {
|
||||
$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)
|
||||
}
|
||||
}
|
||||
@@ -197,7 +197,7 @@ macro_rules! header {
|
||||
fn header_name() -> &'static str {
|
||||
$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)
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,7 @@ macro_rules! header {
|
||||
fn header_name() -> &'static str {
|
||||
$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)
|
||||
}
|
||||
}
|
||||
@@ -252,11 +252,11 @@ macro_rules! header {
|
||||
fn header_name() -> &'static str {
|
||||
$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
|
||||
if raw.len() == 1 {
|
||||
if raw[0] == b"*" {
|
||||
return Some($id::Any)
|
||||
return Ok($id::Any)
|
||||
}
|
||||
}
|
||||
$crate::header::parsing::from_comma_delimited(raw).map(|vec| $id::Items(vec))
|
||||
|
||||
@@ -29,12 +29,12 @@ impl Header for 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| {
|
||||
let slice = &s.to_ascii_lowercase()[..];
|
||||
match slice {
|
||||
"no-cache" => Some(Pragma::NoCache),
|
||||
_ => Some(Pragma::Ext(s)),
|
||||
"no-cache" => Ok(Pragma::NoCache),
|
||||
_ => 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 d = Pragma::Ext("FoObar".to_owned());
|
||||
assert_eq!(c, d);
|
||||
let e: Option<Pragma> = Header::parse_header([b"".to_vec()].as_ref());
|
||||
assert_eq!(e, None);
|
||||
let e: ::Result<Pragma> = Header::parse_header([b"".to_vec()].as_ref());
|
||||
assert_eq!(e.ok(), None);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ impl Header for SetCookie {
|
||||
"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());
|
||||
for set_cookies_raw in raw {
|
||||
if let Ok(s) = from_utf8(&set_cookies_raw[..]) {
|
||||
@@ -75,9 +75,9 @@ impl Header for SetCookie {
|
||||
}
|
||||
|
||||
if !set_cookies.is_empty() {
|
||||
Some(SetCookie(set_cookies))
|
||||
Ok(SetCookie(set_cookies))
|
||||
} else {
|
||||
None
|
||||
Err(::Error::Header)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ fn test_parse() {
|
||||
let mut c1 = Cookie::new("foo".to_owned(), "bar".to_owned());
|
||||
c1.httponly = true;
|
||||
|
||||
assert_eq!(h, Some(SetCookie(vec![c1])));
|
||||
assert_eq!(h.ok(), Some(SetCookie(vec![c1])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -45,8 +45,8 @@ header! {
|
||||
Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
||||
#[test]
|
||||
fn test3() {
|
||||
let x: Option<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]);
|
||||
assert_eq!(x, Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
||||
let x: ::Result<Upgrade> = Header::parse_header(&[b"WEbSOCKet".to_vec()]);
|
||||
assert_eq!(x.ok(), Some(Upgrade(vec![Protocol::new(ProtocolName::WebSocket, None)])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,15 +24,15 @@ header! {
|
||||
|
||||
#[test]
|
||||
fn test2() {
|
||||
let mut vary: Option<Vary>;
|
||||
let mut vary: ::Result<Vary>;
|
||||
|
||||
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());
|
||||
assert_eq!(vary, Some(Vary::Items(vec!["eTag".parse().unwrap(),
|
||||
"cookIE".parse().unwrap(),
|
||||
"AlLOw".parse().unwrap(),])));
|
||||
assert_eq!(vary.ok(), Some(Vary::Items(vec!["eTag".parse().unwrap(),
|
||||
"cookIE".parse().unwrap(),
|
||||
"AlLOw".parse().unwrap(),])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ impl Item {
|
||||
Some(val) => Some(val),
|
||||
None => {
|
||||
match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) {
|
||||
Some(typed) => {
|
||||
Ok(typed) => {
|
||||
unsafe { self.typed.insert(tid, typed); }
|
||||
self.typed.get(tid)
|
||||
},
|
||||
None => None
|
||||
Err(_) => None
|
||||
}
|
||||
}
|
||||
}.map(|typed| unsafe { typed.downcast_ref_unchecked() })
|
||||
@@ -74,10 +74,10 @@ impl Item {
|
||||
let tid = TypeId::of::<H>();
|
||||
if self.typed.get_mut(tid).is_none() {
|
||||
match parse::<H>(self.raw.as_ref().expect("item.raw must exist")) {
|
||||
Some(typed) => {
|
||||
Ok(typed) => {
|
||||
unsafe { self.typed.insert(tid, typed); }
|
||||
},
|
||||
None => ()
|
||||
Err(_) => ()
|
||||
}
|
||||
}
|
||||
self.typed.get_mut(tid).map(|typed| unsafe { typed.downcast_mut_unchecked() })
|
||||
@@ -85,7 +85,7 @@ impl Item {
|
||||
}
|
||||
|
||||
#[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| {
|
||||
// FIXME: Use Type ascription
|
||||
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
|
||||
/// than one field value. If that's the case, you **should** return `None`
|
||||
/// 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]
|
||||
fn test_content_type() {
|
||||
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]
|
||||
@@ -429,10 +429,10 @@ mod tests {
|
||||
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());
|
||||
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());
|
||||
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)]
|
||||
@@ -442,18 +442,21 @@ mod tests {
|
||||
fn header_name() -> &'static str {
|
||||
"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::FromStr;
|
||||
|
||||
if raw.len() != 1 {
|
||||
return None;
|
||||
return Err(::Error::Header);
|
||||
}
|
||||
// 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(),
|
||||
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};
|
||||
|
||||
/// Reads a single raw string when parsing a header
|
||||
pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> Option<T> {
|
||||
if raw.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
pub fn from_one_raw_str<T: str::FromStr>(raw: &[Vec<u8>]) -> ::Result<T> {
|
||||
if raw.len() != 1 || unsafe { raw.get_unchecked(0) } == b"" { return Err(::Error::Header) }
|
||||
// we JUST checked that raw.len() == 1, so raw[0] WILL exist.
|
||||
if let Ok(s) = str::from_utf8(& unsafe { raw.get_unchecked(0) }[..]) {
|
||||
if s != "" {
|
||||
return str::FromStr::from_str(s).ok();
|
||||
}
|
||||
let s: &str = try!(str::from_utf8(& unsafe { raw.get_unchecked(0) }[..]));
|
||||
if let Ok(x) = str::FromStr::from_str(s) {
|
||||
Ok(x)
|
||||
} else {
|
||||
Err(::Error::Header)
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Reads a comma-delimited raw header into a Vec.
|
||||
#[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 {
|
||||
return None;
|
||||
return Err(::Error::Header);
|
||||
}
|
||||
// we JUST checked that raw.len() == 1, so raw[0] WILL exist.
|
||||
from_one_comma_delimited(& unsafe { raw.get_unchecked(0) }[..])
|
||||
}
|
||||
|
||||
/// Reads a comma-delimited raw string into a Vec.
|
||||
pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> Option<Vec<T>> {
|
||||
match str::from_utf8(raw) {
|
||||
Ok(s) => {
|
||||
Some(s
|
||||
.split(',')
|
||||
.filter_map(|x| match x.trim() {
|
||||
"" => None,
|
||||
y => Some(y)
|
||||
})
|
||||
.filter_map(|x| x.parse().ok())
|
||||
.collect())
|
||||
}
|
||||
Err(_) => None
|
||||
}
|
||||
pub fn from_one_comma_delimited<T: str::FromStr>(raw: &[u8]) -> ::Result<Vec<T>> {
|
||||
let s = try!(str::from_utf8(raw));
|
||||
Ok(s.split(',')
|
||||
.filter_map(|x| match x.trim() {
|
||||
"" => None,
|
||||
y => Some(y)
|
||||
})
|
||||
.filter_map(|x| x.parse().ok())
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Format an array into a comma-delimited string.
|
||||
|
||||
Reference in New Issue
Block a user