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:
@@ -1,5 +1,3 @@
|
||||
use std::io;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use http::{Request, Response, StatusCode};
|
||||
@@ -9,7 +7,7 @@ use tokio_service::Service;
|
||||
use proto::body::Entity;
|
||||
use proto::{Body, BodyLength, Conn, Http1Transaction, MessageHead, RequestHead, RequestLine, ResponseHead};
|
||||
|
||||
pub struct Dispatcher<D, Bs, I, B, T> {
|
||||
pub(crate) struct Dispatcher<D, Bs, I, B, T> {
|
||||
conn: Conn<I, B, T>,
|
||||
dispatch: D,
|
||||
body_tx: Option<::proto::body::Sender>,
|
||||
@@ -17,7 +15,7 @@ pub struct Dispatcher<D, Bs, I, B, T> {
|
||||
is_closing: bool,
|
||||
}
|
||||
|
||||
pub trait Dispatch {
|
||||
pub(crate) trait Dispatch {
|
||||
type PollItem;
|
||||
type PollBody;
|
||||
type RecvItem;
|
||||
@@ -47,7 +45,7 @@ where
|
||||
I: AsyncRead + AsyncWrite,
|
||||
B: AsRef<[u8]>,
|
||||
T: Http1Transaction,
|
||||
Bs: Entity<Data=B, Error=::Error>,
|
||||
Bs: Entity<Data=B>,
|
||||
{
|
||||
pub fn new(dispatch: D, conn: Conn<I, B, T>) -> Self {
|
||||
Dispatcher {
|
||||
@@ -98,7 +96,7 @@ where
|
||||
|
||||
if self.is_done() {
|
||||
if should_shutdown {
|
||||
try_ready!(self.conn.shutdown());
|
||||
try_ready!(self.conn.shutdown().map_err(::Error::new_shutdown));
|
||||
}
|
||||
self.conn.take_error()?;
|
||||
Ok(Async::Ready(()))
|
||||
@@ -152,7 +150,7 @@ where
|
||||
return Ok(Async::NotReady);
|
||||
}
|
||||
Err(e) => {
|
||||
body.send_error(::Error::Io(e));
|
||||
body.send_error(::Error::new_body(e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -225,14 +223,14 @@ where
|
||||
} else if !self.conn.can_buffer_body() {
|
||||
try_ready!(self.poll_flush());
|
||||
} else if let Some(mut body) = self.body_rx.take() {
|
||||
let chunk = match body.poll_data()? {
|
||||
let chunk = match body.poll_data().map_err(::Error::new_user_body)? {
|
||||
Async::Ready(Some(chunk)) => {
|
||||
self.body_rx = Some(body);
|
||||
chunk
|
||||
},
|
||||
Async::Ready(None) => {
|
||||
if self.conn.can_write_body() {
|
||||
self.conn.write_body(None)?;
|
||||
self.conn.write_body(None).map_err(::Error::new_body_write)?;
|
||||
}
|
||||
continue;
|
||||
},
|
||||
@@ -243,7 +241,7 @@ where
|
||||
};
|
||||
|
||||
if self.conn.can_write_body() {
|
||||
assert!(self.conn.write_body(Some(chunk))?.is_ready());
|
||||
self.conn.write_body(Some(chunk)).map_err(::Error::new_body_write)?;
|
||||
// This allows when chunk is `None`, or `Some([])`.
|
||||
} else if chunk.as_ref().len() == 0 {
|
||||
// ok
|
||||
@@ -259,7 +257,7 @@ where
|
||||
fn poll_flush(&mut self) -> Poll<(), ::Error> {
|
||||
self.conn.flush().map_err(|err| {
|
||||
debug!("error writing: {}", err);
|
||||
err.into()
|
||||
::Error::new_body_write(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -294,7 +292,7 @@ where
|
||||
I: AsyncRead + AsyncWrite,
|
||||
B: AsRef<[u8]>,
|
||||
T: Http1Transaction,
|
||||
Bs: Entity<Data=B, Error=::Error>,
|
||||
Bs: Entity<Data=B>,
|
||||
{
|
||||
type Item = ();
|
||||
type Error = ::Error;
|
||||
@@ -318,8 +316,9 @@ impl<S> Server<S> where S: Service {
|
||||
|
||||
impl<S, Bs> Dispatch for Server<S>
|
||||
where
|
||||
S: Service<Request=Request<Body>, Response=Response<Bs>, Error=::Error>,
|
||||
Bs: Entity<Error=::Error>,
|
||||
S: Service<Request=Request<Body>, Response=Response<Bs>>,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
Bs: Entity,
|
||||
{
|
||||
type PollItem = MessageHead<StatusCode>;
|
||||
type PollBody = Bs;
|
||||
@@ -327,7 +326,7 @@ where
|
||||
|
||||
fn poll_msg(&mut self) -> Poll<Option<(Self::PollItem, Option<Self::PollBody>)>, ::Error> {
|
||||
if let Some(mut fut) = self.in_flight.take() {
|
||||
let resp = match fut.poll()? {
|
||||
let resp = match fut.poll().map_err(::Error::new_user_service)? {
|
||||
Async::Ready(res) => res,
|
||||
Async::NotReady => {
|
||||
self.in_flight = Some(fut);
|
||||
@@ -389,7 +388,7 @@ impl<B> Client<B> {
|
||||
|
||||
impl<B> Dispatch for Client<B>
|
||||
where
|
||||
B: Entity<Error=::Error>,
|
||||
B: Entity,
|
||||
{
|
||||
type PollItem = RequestHead;
|
||||
type PollBody = B;
|
||||
@@ -443,7 +442,7 @@ where
|
||||
let _ = cb.send(Ok(res));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(::Error::Io(io::Error::new(io::ErrorKind::InvalidData, "response received without matching request")))
|
||||
Err(::Error::new_mismatched_response())
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
@@ -507,8 +506,15 @@ mod tests {
|
||||
.expect("callback poll")
|
||||
.expect_err("callback response");
|
||||
|
||||
match err {
|
||||
(::Error::Cancel(_), Some(_)) => (),
|
||||
/*
|
||||
let err = match async {
|
||||
Async::Ready(result) => result.unwrap_err(),
|
||||
Async::Pending => panic!("callback should be ready"),
|
||||
};
|
||||
*/
|
||||
|
||||
match (err.0.kind(), err.1) {
|
||||
(&::error::Kind::Canceled, Some(_)) => (),
|
||||
other => panic!("expected Canceled, got {:?}", other),
|
||||
}
|
||||
Ok::<(), ()>(())
|
||||
|
||||
Reference in New Issue
Block a user