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:
@@ -25,7 +25,7 @@ use super::{HyperService, Request, Response, Service};
|
||||
pub struct Connection<I, S>
|
||||
where
|
||||
S: HyperService,
|
||||
S::ResponseBody: Entity<Error=::Error>,
|
||||
S::ResponseBody: Entity,
|
||||
{
|
||||
pub(super) conn: proto::dispatch::Dispatcher<
|
||||
proto::dispatch::Server<S>,
|
||||
@@ -59,9 +59,11 @@ pub struct Parts<T> {
|
||||
// ===== impl Connection =====
|
||||
|
||||
impl<I, B, S> Connection<I, S>
|
||||
where S: Service<Request = Request<Body>, Response = Response<B>, Error = ::Error> + 'static,
|
||||
I: AsyncRead + AsyncWrite + 'static,
|
||||
B: Entity<Error=::Error> + 'static,
|
||||
where
|
||||
S: Service<Request=Request<Body>, Response=Response<B>> + 'static,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
I: AsyncRead + AsyncWrite + 'static,
|
||||
B: Entity + 'static,
|
||||
{
|
||||
/// Disables keep-alive for this connection.
|
||||
pub fn disable_keep_alive(&mut self) {
|
||||
@@ -96,9 +98,11 @@ where S: Service<Request = Request<Body>, Response = Response<B>, Error = ::Erro
|
||||
}
|
||||
|
||||
impl<I, B, S> Future for Connection<I, S>
|
||||
where S: Service<Request = Request<Body>, Response = Response<B>, Error = ::Error> + 'static,
|
||||
I: AsyncRead + AsyncWrite + 'static,
|
||||
B: Entity<Error=::Error> + 'static,
|
||||
where
|
||||
S: Service<Request=Request<Body>, Response=Response<B>> + 'static,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
I: AsyncRead + AsyncWrite + 'static,
|
||||
B: Entity + 'static,
|
||||
{
|
||||
type Item = ();
|
||||
type Error = ::Error;
|
||||
@@ -111,7 +115,7 @@ where S: Service<Request = Request<Body>, Response = Response<B>, Error = ::Erro
|
||||
impl<I, S> fmt::Debug for Connection<I, S>
|
||||
where
|
||||
S: HyperService,
|
||||
S::ResponseBody: Entity<Error=::Error>,
|
||||
S::ResponseBody: Entity,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Connection")
|
||||
|
||||
@@ -51,7 +51,7 @@ pub struct Http<B = ::Chunk> {
|
||||
/// address and then serving TCP connections accepted with the service provided.
|
||||
pub struct Server<S, B>
|
||||
where
|
||||
B: Entity<Error=::Error>,
|
||||
B: Entity,
|
||||
{
|
||||
protocol: Http<B::Data>,
|
||||
new_service: S,
|
||||
@@ -165,12 +165,14 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
|
||||
/// The returned `Server` contains one method, `run`, which is used to
|
||||
/// actually run the server.
|
||||
pub fn bind<S, Bd>(&self, addr: &SocketAddr, new_service: S) -> ::Result<Server<S, Bd>>
|
||||
where S: NewService<Request = Request<Body>, Response = Response<Bd>, Error = ::Error> + 'static,
|
||||
Bd: Entity<Data=B, Error=::Error>,
|
||||
where
|
||||
S: NewService<Request=Request<Body>, Response=Response<Bd>> + 'static,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
Bd: Entity<Data=B>,
|
||||
{
|
||||
let handle = Handle::current();
|
||||
let std_listener = StdTcpListener::bind(addr)?;
|
||||
let listener = try!(TcpListener::from_std(std_listener, &handle));
|
||||
let std_listener = StdTcpListener::bind(addr).map_err(::Error::new_listen)?;
|
||||
let listener = TcpListener::from_std(std_listener, &handle).map_err(::Error::new_listen)?;
|
||||
|
||||
Ok(Server {
|
||||
new_service: new_service,
|
||||
@@ -188,13 +190,15 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
|
||||
/// `new_service` object provided as well, creating a new service per
|
||||
/// connection.
|
||||
pub fn serve_addr<S, Bd>(&self, addr: &SocketAddr, new_service: S) -> ::Result<Serve<AddrIncoming, S>>
|
||||
where S: NewService<Request = Request<Body>, Response = Response<Bd>, Error = ::Error>,
|
||||
Bd: Entity<Data=B, Error=::Error>,
|
||||
where
|
||||
S: NewService<Request=Request<Body>, Response=Response<Bd>>,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
Bd: Entity<Data=B>,
|
||||
{
|
||||
let handle = Handle::current();
|
||||
let std_listener = StdTcpListener::bind(addr)?;
|
||||
let listener = TcpListener::from_std(std_listener, &handle)?;
|
||||
let mut incoming = AddrIncoming::new(listener, handle.clone(), self.sleep_on_errors)?;
|
||||
let std_listener = StdTcpListener::bind(addr).map_err(::Error::new_listen)?;
|
||||
let listener = TcpListener::from_std(std_listener, &handle).map_err(::Error::new_listen)?;
|
||||
let mut incoming = AddrIncoming::new(listener, handle.clone(), self.sleep_on_errors).map_err(::Error::new_listen)?;
|
||||
if self.keep_alive {
|
||||
incoming.set_keepalive(Some(Duration::from_secs(90)));
|
||||
}
|
||||
@@ -210,12 +214,15 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
|
||||
/// `new_service` object provided as well, creating a new service per
|
||||
/// connection.
|
||||
pub fn serve_addr_handle<S, Bd>(&self, addr: &SocketAddr, handle: &Handle, new_service: S) -> ::Result<Serve<AddrIncoming, S>>
|
||||
where S: NewService<Request = Request<Body>, Response = Response<Bd>, Error = ::Error>,
|
||||
Bd: Entity<Data=B, Error=::Error>,
|
||||
where
|
||||
S: NewService<Request = Request<Body>, Response = Response<Bd>>,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
Bd: Entity<Data=B>,
|
||||
{
|
||||
let std_listener = StdTcpListener::bind(addr)?;
|
||||
let listener = TcpListener::from_std(std_listener, &handle)?;
|
||||
let mut incoming = AddrIncoming::new(listener, handle.clone(), self.sleep_on_errors)?;
|
||||
let std_listener = StdTcpListener::bind(addr).map_err(::Error::new_listen)?;
|
||||
let listener = TcpListener::from_std(std_listener, &handle).map_err(::Error::new_listen)?;
|
||||
let mut incoming = AddrIncoming::new(listener, handle.clone(), self.sleep_on_errors).map_err(::Error::new_listen)?;
|
||||
|
||||
if self.keep_alive {
|
||||
incoming.set_keepalive(Some(Duration::from_secs(90)));
|
||||
}
|
||||
@@ -226,10 +233,12 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
|
||||
///
|
||||
/// This method allows the ability to share a `Core` with multiple servers.
|
||||
pub fn serve_incoming<I, S, Bd>(&self, incoming: I, new_service: S) -> Serve<I, S>
|
||||
where I: Stream<Error=::std::io::Error>,
|
||||
I::Item: AsyncRead + AsyncWrite,
|
||||
S: NewService<Request = Request<Body>, Response = Response<Bd>, Error = ::Error>,
|
||||
Bd: Entity<Data=B, Error=::Error>,
|
||||
where
|
||||
I: Stream<Error=::std::io::Error>,
|
||||
I::Item: AsyncRead + AsyncWrite,
|
||||
S: NewService<Request = Request<Body>, Response = Response<Bd>>,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
Bd: Entity<Data=B>,
|
||||
{
|
||||
Serve {
|
||||
incoming: incoming,
|
||||
@@ -279,9 +288,11 @@ impl<B: AsRef<[u8]> + 'static> Http<B> {
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub fn serve_connection<S, I, Bd>(&self, io: I, service: S) -> Connection<I, S>
|
||||
where S: Service<Request = Request<Body>, Response = Response<Bd>, Error = ::Error>,
|
||||
Bd: Entity<Error=::Error>,
|
||||
I: AsyncRead + AsyncWrite,
|
||||
where
|
||||
S: Service<Request = Request<Body>, Response = Response<Bd>>,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
Bd: Entity,
|
||||
I: AsyncRead + AsyncWrite,
|
||||
{
|
||||
let mut conn = proto::Conn::new(io);
|
||||
if !self.keep_alive {
|
||||
@@ -341,15 +352,19 @@ impl Future for Run {
|
||||
|
||||
|
||||
impl<S, B> Server<S, B>
|
||||
where S: NewService<Request = Request<Body>, Response = Response<B>, Error = ::Error> + Send + 'static,
|
||||
<S as NewService>::Instance: Send,
|
||||
<<S as NewService>::Instance as Service>::Future: Send,
|
||||
B: Entity<Error=::Error> + Send + 'static,
|
||||
B::Data: Send,
|
||||
where
|
||||
S: NewService<Request = Request<Body>, Response = Response<B>> + Send + 'static,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
<S as NewService>::Instance: Send,
|
||||
<<S as NewService>::Instance as Service>::Future: Send,
|
||||
B: Entity + Send + 'static,
|
||||
B::Data: Send,
|
||||
{
|
||||
/// Returns the local address that this server is bound to.
|
||||
pub fn local_addr(&self) -> ::Result<SocketAddr> {
|
||||
Ok(try!(self.listener.local_addr()))
|
||||
//TODO: this shouldn't return an error at all, but should get the
|
||||
//local_addr at construction
|
||||
self.listener.local_addr().map_err(::Error::new_io)
|
||||
}
|
||||
|
||||
/// Configure the amount of time this server will wait for a "graceful
|
||||
@@ -393,7 +408,7 @@ impl<S, B> Server<S, B>
|
||||
|
||||
let mut incoming = match AddrIncoming::new(listener, handle.clone(), protocol.sleep_on_errors) {
|
||||
Ok(incoming) => incoming,
|
||||
Err(err) => return Run(Box::new(future::err(err.into()))),
|
||||
Err(err) => return Run(Box::new(future::err(::Error::new_listen(err)))),
|
||||
};
|
||||
|
||||
if protocol.keep_alive {
|
||||
@@ -439,7 +454,7 @@ impl<S, B> Server<S, B>
|
||||
let main_execution = shutdown_signal.select(srv).then(move |result| {
|
||||
match result {
|
||||
Ok(((), _incoming)) => {},
|
||||
Err((e, _other)) => return future::Either::A(future::err(e.into()))
|
||||
Err((e, _other)) => return future::Either::A(future::err(::Error::new_accept(e))),
|
||||
}
|
||||
|
||||
// Ok we've stopped accepting new connections at this point, but we want
|
||||
@@ -454,7 +469,8 @@ impl<S, B> Server<S, B>
|
||||
future::Either::B(wait.select(timeout).then(|result| {
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err((e, _)) => Err(e.into())
|
||||
//TODO: error variant should be "timed out waiting for graceful shutdown"
|
||||
Err((e, _)) => Err(::Error::new_io(e))
|
||||
}
|
||||
}))
|
||||
});
|
||||
@@ -463,11 +479,10 @@ impl<S, B> Server<S, B>
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: fmt::Debug, B: Entity<Error=::Error>> fmt::Debug for Server<S, B>
|
||||
impl<S: fmt::Debug, B: Entity> fmt::Debug for Server<S, B>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Server")
|
||||
.field("reactor", &"...")
|
||||
.field("listener", &self.listener)
|
||||
.field("new_service", &self.new_service)
|
||||
.field("protocol", &self.protocol)
|
||||
@@ -499,15 +514,16 @@ impl<I, S, B> Stream for Serve<I, S>
|
||||
where
|
||||
I: Stream<Error=io::Error>,
|
||||
I::Item: AsyncRead + AsyncWrite,
|
||||
S: NewService<Request=Request<Body>, Response=Response<B>, Error=::Error>,
|
||||
B: Entity<Error=::Error>,
|
||||
S: NewService<Request=Request<Body>, Response=Response<B>>,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
B: Entity,
|
||||
{
|
||||
type Item = Connection<I::Item, S::Instance>;
|
||||
type Error = ::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
if let Some(io) = try_ready!(self.incoming.poll()) {
|
||||
let service = self.new_service.new_service()?;
|
||||
if let Some(io) = try_ready!(self.incoming.poll().map_err(::Error::new_accept)) {
|
||||
let service = self.new_service.new_service().map_err(::Error::new_user_new_service)?;
|
||||
Ok(Async::Ready(Some(self.protocol.serve_connection(io, service))))
|
||||
} else {
|
||||
Ok(Async::Ready(None))
|
||||
@@ -579,6 +595,12 @@ impl AddrIncoming {
|
||||
fn set_keepalive(&mut self, dur: Option<Duration>) {
|
||||
self.keep_alive_timeout = dur;
|
||||
}
|
||||
|
||||
/*
|
||||
fn set_sleep_on_errors(&mut self, val: bool) {
|
||||
self.sleep_on_errors = val;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl Stream for AddrIncoming {
|
||||
@@ -802,9 +824,9 @@ mod hyper_service {
|
||||
S: Service<
|
||||
Request=Request<Body>,
|
||||
Response=Response<B>,
|
||||
Error=::Error,
|
||||
>,
|
||||
B: Entity<Error=::Error>,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
B: Entity,
|
||||
{}
|
||||
|
||||
impl<S, B> HyperService for S
|
||||
@@ -812,10 +834,10 @@ mod hyper_service {
|
||||
S: Service<
|
||||
Request=Request<Body>,
|
||||
Response=Response<B>,
|
||||
Error=::Error,
|
||||
>,
|
||||
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
|
||||
S: Sealed,
|
||||
B: Entity<Error=::Error>,
|
||||
B: Entity,
|
||||
{
|
||||
type ResponseBody = B;
|
||||
type Sealed = Opaque;
|
||||
|
||||
Reference in New Issue
Block a user