Skip to content

Commit 195a89f

Browse files
committed
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
1 parent 7637461 commit 195a89f

19 files changed

+140
-132
lines changed

benches/client.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ impl hyper::header::Header for Foo {
5858
fn header_name() -> &'static str {
5959
"x-foo"
6060
}
61-
fn parse_header(_: &[Vec<u8>]) -> Option<Foo> {
62-
None
61+
fn parse_header(_: &[Vec<u8>]) -> hyper::Result<Foo> {
62+
Err(hyper::Error::Header)
6363
}
6464
}
6565

@@ -104,4 +104,3 @@ fn bench_mock_hyper(b: &mut test::Bencher) {
104104
.read_to_string(&mut s).unwrap()
105105
});
106106
}
107-

src/error.rs

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use std::error::Error as StdError;
33
use std::fmt;
44
use std::io::Error as IoError;
5+
use std::str::Utf8Error;
56

67
use httparse;
78
use openssl::ssl::error::SslError;
@@ -18,6 +19,7 @@ use self::Error::{
1819
Ssl,
1920
TooLarge,
2021
Http2,
22+
Utf8
2123
};
2224

2325

@@ -45,6 +47,8 @@ pub enum Error {
4547
Ssl(SslError),
4648
/// An HTTP/2-specific error, coming from the `solicit` library.
4749
Http2(Http2Error),
50+
/// Parsing a field as string failed
51+
Utf8(Utf8Error),
4852

4953
#[doc(hidden)]
5054
__Nonexhaustive(Void)
@@ -77,6 +81,7 @@ impl StdError for Error {
7781
Io(ref e) => e.description(),
7882
Ssl(ref e) => e.description(),
7983
Http2(ref e) => e.description(),
84+
Utf8(ref e) => e.description(),
8085
Error::__Nonexhaustive(ref void) => match *void {}
8186
}
8287
}
@@ -113,6 +118,12 @@ impl From<SslError> for Error {
113118
}
114119
}
115120

121+
impl From<Utf8Error> for Error {
122+
fn from(err: Utf8Error) -> Error {
123+
Utf8(err)
124+
}
125+
}
126+
116127
impl From<httparse::Error> for Error {
117128
fn from(err: httparse::Error) -> Error {
118129
match err {

src/header/common/accept_ranges.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ pub enum RangeUnit {
5252

5353

5454
impl FromStr for RangeUnit {
55-
type Err = ();
56-
fn from_str(s: &str) -> Result<Self, ()> {
55+
type Err = ::Error;
56+
fn from_str(s: &str) -> ::Result<Self> {
5757
match s {
5858
"bytes" => Ok(RangeUnit::Bytes),
5959
"none" => Ok(RangeUnit::None),

src/header/common/access_control_allow_origin.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,14 @@ impl Header for AccessControlAllowOrigin {
3535
"Access-Control-Allow-Origin"
3636
}
3737

38-
fn parse_header(raw: &[Vec<u8>]) -> Option<AccessControlAllowOrigin> {
38+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowOrigin> {
3939
if raw.len() == 1 {
4040
match unsafe { &raw.get_unchecked(0)[..] } {
41-
b"*" => Some(AccessControlAllowOrigin::Any),
42-
b"null" => Some(AccessControlAllowOrigin::Null),
43-
r => if let Ok(s) = str::from_utf8(r) {
44-
Url::parse(s).ok().map(AccessControlAllowOrigin::Value)
45-
} else { None }
41+
b"*" => Ok(AccessControlAllowOrigin::Any),
42+
b"null" => Ok(AccessControlAllowOrigin::Null),
43+
r => Ok(AccessControlAllowOrigin::Value(try!(Url::parse(try!(str::from_utf8(r))))))
4644
}
47-
} else { None }
45+
} else { Err(::Error::Header) }
4846
}
4947
}
5048

src/header/common/authorization.rs

+22-15
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,25 @@ impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'st
4242
"Authorization"
4343
}
4444

45-
fn parse_header(raw: &[Vec<u8>]) -> Option<Authorization<S>> {
46-
if raw.len() == 1 {
47-
match (from_utf8(unsafe { &raw.get_unchecked(0)[..] }), <S as Scheme>::scheme()) {
48-
(Ok(header), Some(scheme))
49-
if header.starts_with(scheme) && header.len() > scheme.len() + 1 => {
50-
header[scheme.len() + 1..].parse::<S>().map(Authorization).ok()
51-
},
52-
(Ok(header), None) => header.parse::<S>().map(Authorization).ok(),
53-
_ => None
45+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> {
46+
if raw.len() != 1 {
47+
return Err(::Error::Header);
48+
}
49+
let header = try!(from_utf8(unsafe { &raw.get_unchecked(0)[..] }));
50+
return if let Some(scheme) = <S as Scheme>::scheme() {
51+
if header.starts_with(scheme) && header.len() > scheme.len() + 1 {
52+
match header[scheme.len() + 1..].parse::<S>().map(Authorization) {
53+
Ok(h) => Ok(h),
54+
Err(_) => Err(::Error::Header)
55+
}
56+
} else {
57+
Err(::Error::Header)
5458
}
5559
} else {
56-
None
60+
match header.parse::<S>().map(Authorization) {
61+
Ok(h) => Ok(h),
62+
Err(_) => Err(::Error::Header)
63+
}
5764
}
5865
}
5966
}
@@ -121,15 +128,15 @@ impl Scheme for Basic {
121128
}
122129

123130
impl FromStr for Basic {
124-
type Err = ();
125-
fn from_str(s: &str) -> Result<Basic, ()> {
131+
type Err = ::Error;
132+
fn from_str(s: &str) -> ::Result<Basic> {
126133
match s.from_base64() {
127134
Ok(decoded) => match String::from_utf8(decoded) {
128135
Ok(text) => {
129136
let mut parts = &mut text.split(':');
130137
let user = match parts.next() {
131138
Some(part) => part.to_owned(),
132-
None => return Err(())
139+
None => return Err(::Error::Header)
133140
};
134141
let password = match parts.next() {
135142
Some(part) => Some(part.to_owned()),
@@ -142,12 +149,12 @@ impl FromStr for Basic {
142149
},
143150
Err(e) => {
144151
debug!("Basic::from_utf8 error={:?}", e);
145-
Err(())
152+
Err(::Error::Header)
146153
}
147154
},
148155
Err(e) => {
149156
debug!("Basic::from_base64 error={:?}", e);
150-
Err(())
157+
Err(::Error::Header)
151158
}
152159
}
153160
}

src/header/common/cache_control.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ impl Header for CacheControl {
3030
"Cache-Control"
3131
}
3232

33-
fn parse_header(raw: &[Vec<u8>]) -> Option<CacheControl> {
33+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> {
3434
let directives = raw.iter()
35-
.filter_map(|line| from_one_comma_delimited(&line[..]))
35+
.filter_map(|line| from_one_comma_delimited(&line[..]).ok())
3636
.collect::<Vec<Vec<CacheDirective>>>()
3737
.concat();
3838
if !directives.is_empty() {
39-
Some(CacheControl(directives))
39+
Ok(CacheControl(directives))
4040
} else {
41-
None
41+
Err(::Error::Header)
4242
}
4343
}
4444
}
@@ -148,35 +148,35 @@ mod tests {
148148
#[test]
149149
fn test_parse_multiple_headers() {
150150
let cache = Header::parse_header(&[b"no-cache".to_vec(), b"private".to_vec()]);
151-
assert_eq!(cache, Some(CacheControl(vec![CacheDirective::NoCache,
151+
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::NoCache,
152152
CacheDirective::Private])))
153153
}
154154

155155
#[test]
156156
fn test_parse_argument() {
157157
let cache = Header::parse_header(&[b"max-age=100, private".to_vec()]);
158-
assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(100),
158+
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(100),
159159
CacheDirective::Private])))
160160
}
161161

162162
#[test]
163163
fn test_parse_quote_form() {
164164
let cache = Header::parse_header(&[b"max-age=\"200\"".to_vec()]);
165-
assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(200)])))
165+
assert_eq!(cache.ok(), Some(CacheControl(vec![CacheDirective::MaxAge(200)])))
166166
}
167167

168168
#[test]
169169
fn test_parse_extension() {
170170
let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]);
171-
assert_eq!(cache, Some(CacheControl(vec![
171+
assert_eq!(cache.ok(), Some(CacheControl(vec![
172172
CacheDirective::Extension("foo".to_owned(), None),
173173
CacheDirective::Extension("bar".to_owned(), Some("baz".to_owned()))])))
174174
}
175175

176176
#[test]
177177
fn test_parse_bad_syntax() {
178-
let cache: Option<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]);
179-
assert_eq!(cache, None)
178+
let cache: ::Result<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]);
179+
assert_eq!(cache.ok(), None)
180180
}
181181
}
182182

src/header/common/cookie.rs

+12-15
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,23 @@ impl Header for Cookie {
2727
"Cookie"
2828
}
2929

30-
fn parse_header(raw: &[Vec<u8>]) -> Option<Cookie> {
30+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Cookie> {
3131
let mut cookies = Vec::with_capacity(raw.len());
3232
for cookies_raw in raw.iter() {
33-
match from_utf8(&cookies_raw[..]) {
34-
Ok(cookies_str) => {
35-
for cookie_str in cookies_str.split(';') {
36-
match cookie_str.trim().parse() {
37-
Ok(cookie) => cookies.push(cookie),
38-
Err(_) => return None
39-
}
40-
}
41-
},
42-
Err(_) => return None
43-
};
33+
let cookies_str = try!(from_utf8(&cookies_raw[..]));
34+
for cookie_str in cookies_str.split(';') {
35+
if let Ok(cookie) = cookie_str.trim().parse() {
36+
cookies.push(cookie);
37+
} else {
38+
return Err(::Error::Header);
39+
}
40+
}
4441
}
4542

4643
if !cookies.is_empty() {
47-
Some(Cookie(cookies))
44+
Ok(Cookie(cookies))
4845
} else {
49-
None
46+
Err(::Error::Header)
5047
}
5148
}
5249
}
@@ -88,7 +85,7 @@ fn test_parse() {
8885
let h = Header::parse_header(&[b"foo=bar; baz=quux".to_vec()][..]);
8986
let c1 = CookiePair::new("foo".to_owned(), "bar".to_owned());
9087
let c2 = CookiePair::new("baz".to_owned(), "quux".to_owned());
91-
assert_eq!(h, Some(Cookie(vec![c1, c2])));
88+
assert_eq!(h.ok(), Some(Cookie(vec![c1, c2])));
9289
}
9390

9491
#[test]

src/header/common/expect.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl Header for Expect {
2626
"Expect"
2727
}
2828

29-
fn parse_header(raw: &[Vec<u8>]) -> Option<Expect> {
29+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> {
3030
if raw.len() == 1 {
3131
let text = unsafe {
3232
// safe because:
@@ -38,12 +38,12 @@ impl Header for Expect {
3838
str::from_utf8_unchecked(raw.get_unchecked(0))
3939
};
4040
if UniCase(text) == EXPECT_CONTINUE {
41-
Some(Expect::Continue)
41+
Ok(Expect::Continue)
4242
} else {
43-
None
43+
Err(::Error::Header)
4444
}
4545
} else {
46-
None
46+
Err(::Error::Header)
4747
}
4848
}
4949
}

src/header/common/host.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl Header for Host {
2222
"Host"
2323
}
2424

25-
fn parse_header(raw: &[Vec<u8>]) -> Option<Host> {
25+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> {
2626
from_one_raw_str(raw).and_then(|mut s: String| {
2727
// FIXME: use rust-url to parse this
2828
// https://github.com/servo/rust-url/issues/42
@@ -39,7 +39,7 @@ impl Header for Host {
3939
None
4040
}
4141
}
42-
None => return None // this is a bad ipv6 address...
42+
None => return Err(::Error::Header) // this is a bad ipv6 address...
4343
}
4444
} else {
4545
slice.rfind(':')
@@ -56,7 +56,7 @@ impl Header for Host {
5656
None => ()
5757
}
5858

59-
Some(Host {
59+
Ok(Host {
6060
hostname: s,
6161
port: port
6262
})
@@ -82,14 +82,14 @@ mod tests {
8282
#[test]
8383
fn test_host() {
8484
let host = Header::parse_header([b"foo.com".to_vec()].as_ref());
85-
assert_eq!(host, Some(Host {
85+
assert_eq!(host.ok(), Some(Host {
8686
hostname: "foo.com".to_owned(),
8787
port: None
8888
}));
8989

9090

9191
let host = Header::parse_header([b"foo.com:8080".to_vec()].as_ref());
92-
assert_eq!(host, Some(Host {
92+
assert_eq!(host.ok(), Some(Host {
9393
hostname: "foo.com".to_owned(),
9494
port: Some(8080)
9595
}));

src/header/common/if_none_match.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,18 @@ mod tests {
4545

4646
#[test]
4747
fn test_if_none_match() {
48-
let mut if_none_match: Option<IfNoneMatch>;
48+
let mut if_none_match: ::Result<IfNoneMatch>;
4949

5050
if_none_match = Header::parse_header([b"*".to_vec()].as_ref());
51-
assert_eq!(if_none_match, Some(IfNoneMatch::Any));
51+
assert_eq!(if_none_match.ok(), Some(IfNoneMatch::Any));
5252

5353
if_none_match = Header::parse_header([b"\"foobar\", W/\"weak-etag\"".to_vec()].as_ref());
5454
let mut entities: Vec<EntityTag> = Vec::new();
5555
let foobar_etag = EntityTag::new(false, "foobar".to_owned());
5656
let weak_etag = EntityTag::new(true, "weak-etag".to_owned());
5757
entities.push(foobar_etag);
5858
entities.push(weak_etag);
59-
assert_eq!(if_none_match, Some(IfNoneMatch::Items(entities)));
59+
assert_eq!(if_none_match.ok(), Some(IfNoneMatch::Items(entities)));
6060
}
6161
}
6262

src/header/common/if_range.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@ impl Header for IfRange {
3636
fn header_name() -> &'static str {
3737
"If-Range"
3838
}
39-
fn parse_header(raw: &[Vec<u8>]) -> Option<IfRange> {
40-
let etag: Option<EntityTag> = header::parsing::from_one_raw_str(raw);
41-
if etag != None {
42-
return Some(IfRange::EntityTag(etag.unwrap()));
39+
fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> {
40+
let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw);
41+
if etag.is_ok() {
42+
return Ok(IfRange::EntityTag(etag.unwrap()));
4343
}
44-
let date: Option<HttpDate> = header::parsing::from_one_raw_str(raw);
45-
if date != None {
46-
return Some(IfRange::Date(date.unwrap()));
44+
let date: ::Result<HttpDate> = header::parsing::from_one_raw_str(raw);
45+
if date.is_ok() {
46+
return Ok(IfRange::Date(date.unwrap()));
4747
}
48-
None
48+
Err(::Error::Header)
4949
}
5050
}
5151

0 commit comments

Comments
 (0)