feat(error): revamp hyper::Error type

**The `Error` is now an opaque struct**, which allows for more variants to
be added freely, and the internal representation to change without being
breaking changes.

For inspecting an `Error`, there are several `is_*` methods to check for
certain classes of errors, such as `Error::is_parse()`. The `cause` can
also be inspected, like before. This likely seems like a downgrade, but
more inspection can be added as needed!

The `Error` now knows about more states, which gives much more context
around when a certain error occurs. This is also expressed in the
description and `fmt` messages.

**Most places where a user would provide an error to hyper can now pass
any error type** (`E: Into<Box<std::error::Error>>`). This error is passed
back in relevant places, and can be useful for logging. This should make
it much clearer about what error a user should provide to hyper: any it
feels is relevant!

Closes #1128
Closes #1130
Closes #1431
Closes #1338

BREAKING CHANGE: `Error` is no longer an enum to pattern match over, or
  to construct. Code will need to be updated accordingly.

  For body streams or `Service`s, inference might be unable to determine
  what error type you mean to return. Starting in Rust 1.26, you could
  just label that as `!` if you never return an error.
This commit is contained in:
Sean McArthur
2018-04-10 14:29:34 -07:00
parent 33874f9a75
commit 5d3c472228
22 changed files with 519 additions and 407 deletions

View File

@@ -40,7 +40,7 @@ where
let mut headers = [httparse::EMPTY_HEADER; MAX_HEADERS];
trace!("Request.parse([Header; {}], [u8; {}])", headers.len(), buf.len());
let mut req = httparse::Request::new(&mut headers);
match try!(req.parse(&buf)) {
match req.parse(&buf)? {
httparse::Status::Complete(len) => {
trace!("Request.parse Complete({})", len);
let method = Method::from_bytes(req.method.unwrap().as_bytes())?;
@@ -104,18 +104,18 @@ where
// mal-formed. A server should respond with 400 Bad Request.
if head.version == Version::HTTP_10 {
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
Err(::Error::Header)
Err(::Error::new_header())
} else if headers::transfer_encoding_is_chunked(&head.headers) {
Ok(Decode::Normal(Decoder::chunked()))
} else {
debug!("request with transfer-encoding header, but not chunked, bad request");
Err(::Error::Header)
Err(::Error::new_header())
}
} else if let Some(len) = headers::content_length_parse(&head.headers) {
Ok(Decode::Normal(Decoder::length(len)))
} else if head.headers.contains_key(CONTENT_LENGTH) {
debug!("illegal Content-Length header");
Err(::Error::Header)
Err(::Error::new_header())
} else {
Ok(Decode::Normal(Decoder::length(0)))
}
@@ -146,7 +146,8 @@ where
head = MessageHead::default();
head.subject = StatusCode::INTERNAL_SERVER_ERROR;
headers::content_length_zero(&mut head.headers);
Err(::Error::Status)
//TODO: change this to a more descriptive error than just a parse error
Err(::Error::new_status())
} else {
Ok(Server::set_length(&mut head, body, method.as_ref()))
};
@@ -184,14 +185,15 @@ where
}
fn on_error(err: &::Error) -> Option<MessageHead<Self::Outgoing>> {
let status = match err {
&::Error::Method |
&::Error::Version |
&::Error::Header /*|
&::Error::Uri(_)*/ => {
use ::error::{Kind, Parse};
let status = match *err.kind() {
Kind::Parse(Parse::Method) |
Kind::Parse(Parse::Version) |
Kind::Parse(Parse::Header) |
Kind::Parse(Parse::Uri) => {
StatusCode::BAD_REQUEST
},
&::Error::TooLarge => {
Kind::Parse(Parse::TooLarge) => {
StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE
}
_ => return None,
@@ -271,7 +273,7 @@ where
match try!(res.parse(bytes)) {
httparse::Status::Complete(len) => {
trace!("Response.parse Complete({})", len);
let status = try!(StatusCode::from_u16(res.code.unwrap()).map_err(|_| ::Error::Status));
let status = StatusCode::from_u16(res.code.unwrap())?;
let version = if res.version.unwrap() == 1 {
Version::HTTP_11
} else {
@@ -343,7 +345,7 @@ where
// mal-formed. A server should respond with 400 Bad Request.
if inc.version == Version::HTTP_10 {
debug!("HTTP/1.0 cannot have Transfer-Encoding header");
Err(::Error::Header)
Err(::Error::new_header())
} else if headers::transfer_encoding_is_chunked(&inc.headers) {
Ok(Decode::Normal(Decoder::chunked()))
} else {
@@ -354,7 +356,7 @@ where
Ok(Decode::Normal(Decoder::length(len)))
} else if inc.headers.contains_key(CONTENT_LENGTH) {
debug!("illegal Content-Length header");
Err(::Error::Header)
Err(::Error::new_header())
} else {
trace!("neither Transfer-Encoding nor Content-Length");
Ok(Decode::Normal(Decoder::eof()))
@@ -577,12 +579,13 @@ impl OnUpgrade for NoUpgrades {
*head = MessageHead::default();
head.subject = ::StatusCode::INTERNAL_SERVER_ERROR;
headers::content_length_zero(&mut head.headers);
Err(::Error::Status)
//TODO: replace with more descriptive error
return Err(::Error::new_status());
}
fn on_decode_upgrade() -> ::Result<Decoder> {
debug!("received 101 upgrade response, not supported");
return Err(::Error::Upgrade);
return Err(::Error::new_upgrade());
}
}