diff --git a/examples/diesel/src/main.rs b/examples/diesel/src/main.rs index d85b53d88..b659e766a 100644 --- a/examples/diesel/src/main.rs +++ b/examples/diesel/src/main.rs @@ -10,7 +10,7 @@ extern crate diesel_migrations; use diesel::prelude::*; use diesel::sqlite::SqliteConnection; use futures::prelude::*; -use gotham::handler::{HandlerError, HandlerFuture, IntoHandlerError}; +use gotham::handler::{HandlerError, HandlerFuture, MapHandlerError, MapHandlerErrorFuture}; use gotham::helpers::http::response::create_response; use gotham::hyper::{body, Body, StatusCode}; use gotham::pipeline::{new_pipeline, single::single_pipeline}; @@ -62,7 +62,7 @@ fn create_product_handler(mut state: State) -> Pin> { let rows = match query_result { Ok(rows) => rows, - Err(e) => return Err((state, e.into_handler_error())), + Err(e) => return Err((state, e.into())), }; let body = @@ -85,7 +85,7 @@ fn get_products_handler(state: State) -> Pin> { let res = create_response(&state, StatusCode::OK, mime::APPLICATION_JSON, body); Ok((state, res)) } - Err(e) => Err((state, e.into_handler_error())), + Err(e) => Err((state, e.into())), } } .boxed() @@ -103,24 +103,17 @@ fn router(repo: Repo) -> Router { }) } -fn bad_request(e: E) -> HandlerError -where - E: std::error::Error + Send + 'static, -{ - e.into_handler_error().with_status(StatusCode::BAD_REQUEST) -} - async fn extract_json(state: &mut State) -> Result where T: serde::de::DeserializeOwned, { let body = body::to_bytes(Body::take_from(state)) - .map_err(bad_request) + .map_err_with_status(StatusCode::BAD_REQUEST) .await?; let b = body.to_vec(); from_utf8(&b) - .map_err(bad_request) - .and_then(|s| serde_json::from_str::(s).map_err(bad_request)) + .map_err_with_status(StatusCode::BAD_REQUEST) + .and_then(|s| serde_json::from_str::(s).map_err_with_status(StatusCode::BAD_REQUEST)) } /// Start a server and use a `Router` to dispatch requests diff --git a/examples/handlers/async_handlers/src/main.rs b/examples/handlers/async_handlers/src/main.rs index 9bb016709..a65fab64c 100644 --- a/examples/handlers/async_handlers/src/main.rs +++ b/examples/handlers/async_handlers/src/main.rs @@ -11,7 +11,7 @@ use gotham::hyper::StatusCode; #[cfg(not(test))] use gotham::hyper::{body, Client, Uri}; -use gotham::handler::{HandlerFuture, IntoHandlerError}; +use gotham::handler::HandlerFuture; use gotham::helpers::http::response::create_response; use gotham::router::builder::DefineSingleRoute; use gotham::router::builder::{build_simple_router, DrawRoutes}; @@ -108,7 +108,7 @@ fn series_handler(mut state: State) -> Pin> { println!("series length: {} finished", length); future::ok((state, res)) } - Err(err) => future::err((state, err.into_handler_error())), + Err(err) => future::err((state, err.into())), }) .boxed() } @@ -162,7 +162,7 @@ fn loop_handler(mut state: State) -> Pin> { println!("loop length: {} finished", length); future::ok((state, res)) } - Err(err) => future::err((state, err.into_handler_error())), + Err(err) => future::err((state, err.into())), }) .boxed() } @@ -230,7 +230,7 @@ fn parallel_handler(mut state: State) -> Pin> { println!("parallel length: {} finished", length); future::ok((state, res)) } - Err(err) => future::err((state, err.into_handler_error())), + Err(err) => future::err((state, err.into())), }) .boxed() } diff --git a/examples/handlers/form_urlencoded/src/main.rs b/examples/handlers/form_urlencoded/src/main.rs index 3e7087124..082f7729d 100644 --- a/examples/handlers/form_urlencoded/src/main.rs +++ b/examples/handlers/form_urlencoded/src/main.rs @@ -4,7 +4,7 @@ use gotham::hyper::{body, Body, StatusCode}; use std::pin::Pin; use url::form_urlencoded; -use gotham::handler::{HandlerFuture, IntoHandlerError}; +use gotham::handler::HandlerFuture; use gotham::helpers::http::response::create_response; use gotham::router::builder::{build_simple_router, DefineSingleRoute, DrawRoutes}; use gotham::router::Router; @@ -25,7 +25,7 @@ fn form_handler(mut state: State) -> Pin> { let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, res_body); future::ok((state, res)) } - Err(e) => future::err((state, e.into_handler_error())), + Err(e) => future::err((state, e.into())), }); f.boxed() diff --git a/examples/handlers/multipart/src/main.rs b/examples/handlers/multipart/src/main.rs index b568f554f..148cd1557 100644 --- a/examples/handlers/multipart/src/main.rs +++ b/examples/handlers/multipart/src/main.rs @@ -1,6 +1,6 @@ //! An example of decoding multipart form requests use futures::prelude::*; -use gotham::handler::{HandlerFuture, IntoHandlerError}; +use gotham::handler::HandlerFuture; use gotham::helpers::http::response::create_response; use gotham::hyper::header::CONTENT_TYPE; use gotham::hyper::{body, Body, HeaderMap, StatusCode}; @@ -63,7 +63,7 @@ fn form_handler(mut state: State) -> Pin> { } } } - Err(e) => future::err((state, e.into_handler_error())), + Err(e) => future::err((state, e.into())), }) .boxed() } diff --git a/examples/handlers/request_data/src/main.rs b/examples/handlers/request_data/src/main.rs index 56d2c7a7b..bfe5bc5da 100644 --- a/examples/handlers/request_data/src/main.rs +++ b/examples/handlers/request_data/src/main.rs @@ -3,7 +3,7 @@ use futures::prelude::*; use gotham::hyper::{body, Body, HeaderMap, Method, Response, StatusCode, Uri, Version}; use std::pin::Pin; -use gotham::handler::{HandlerFuture, IntoHandlerError}; +use gotham::handler::HandlerFuture; use gotham::helpers::http::response::create_empty_response; use gotham::router::builder::{build_simple_router, DefineSingleRoute, DrawRoutes}; use gotham::router::Router; @@ -31,7 +31,7 @@ fn post_handler(mut state: State) -> Pin> { let res = create_empty_response(&state, StatusCode::OK); future::ok((state, res)) } - Err(e) => future::err((state, e.into_handler_error())), + Err(e) => future::err((state, e.into())), }); f.boxed() diff --git a/examples/handlers/stateful/src/main.rs b/examples/handlers/stateful/src/main.rs index ea06de981..380ec7a7d 100644 --- a/examples/handlers/stateful/src/main.rs +++ b/examples/handlers/stateful/src/main.rs @@ -7,7 +7,7 @@ use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::time::SystemTime; -use gotham::error::Result; +use gotham::anyhow::Result; use gotham::handler::{Handler, HandlerFuture, IntoResponse, NewHandler}; use gotham::router::builder::*; use gotham::router::Router; diff --git a/gotham/Cargo.toml b/gotham/Cargo.toml index 2953ac998..a79414de8 100644 --- a/gotham/Cargo.toml +++ b/gotham/Cargo.toml @@ -26,8 +26,6 @@ serde = "1.0" serde_derive = "1.0" bincode = "1.0" mime = "0.3.15" -# Using alpha version of mime_guess until mime crate stabilizes (releases 1.0). -# see https://github.com/hyperium/mime/issues/52 mime_guess = "2.0.1" futures = "0.3.1" tokio = { version = "0.2.6", features = ["full"] } @@ -35,7 +33,7 @@ bytes = "0.5" mio = "0.7" borrow-bag = { path = "../misc/borrow_bag", version = "1.0" } percent-encoding = "2.1" -pin-project = "0.4.2" +pin-project = "0.4.20" uuid = { version = "0.8", features = ["v4"] } chrono = "0.4" base64 = "0.12" @@ -47,8 +45,8 @@ regex = "1.0" cookie = "0.14" http = "0.2" httpdate = "0.3" -failure = "0.1" itertools = "0.9.0" +anyhow = "1.0" tokio-rustls = { version = "0.14.0", optional = true } [dev-dependencies] diff --git a/gotham/src/error.rs b/gotham/src/error.rs deleted file mode 100644 index d4e52c88c..000000000 --- a/gotham/src/error.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! The error module is nascent. At present, it re-exports types from the `failure` crate and adds an alias for compatible errors. -//! Future directions for Gotham error types are an ongoing discussion. Feel free to chip in. -use failure::Compat; - -pub use failure::Error; - -/// An implementation of the single-parameter Result pattern, using our `pub use failure::Error` -pub type Result = ::std::result::Result; - -/// An alias for `failure::Error.compat()`, which exists to fulfill the std::error::Error trait. -pub type CompatError = Compat; diff --git a/gotham/src/handler/assets/mod.rs b/gotham/src/handler/assets/mod.rs index e6cb90e22..2eed61bc2 100644 --- a/gotham/src/handler/assets/mod.rs +++ b/gotham/src/handler/assets/mod.rs @@ -6,7 +6,6 @@ mod accepted_encoding; -use crate::error::Result; use bytes::{BufMut, Bytes, BytesMut}; use futures::prelude::*; use futures::ready; @@ -23,7 +22,7 @@ use tokio::fs::File; use tokio::io::AsyncRead; use self::accepted_encoding::accepted_encodings; -use crate::handler::{Handler, HandlerFuture, IntoHandlerError, NewHandler}; +use crate::handler::{Handler, HandlerError, HandlerFuture, NewHandler}; use crate::router::response::extender::StaticResponseExtender; use crate::state::{FromState, State, StateData}; @@ -165,7 +164,7 @@ impl DirHandler { impl NewHandler for FileHandler { type Instance = Self; - fn new_handler(&self) -> Result { + fn new_handler(&self) -> anyhow::Result { Ok(self.clone()) } } @@ -173,7 +172,7 @@ impl NewHandler for FileHandler { impl NewHandler for DirHandler { type Instance = Self; - fn new_handler(&self) -> Result { + fn new_handler(&self) -> anyhow::Result { Ok(self.clone()) } } @@ -247,7 +246,8 @@ fn create_file_response(options: FileOptions, state: State) -> Pin StatusCode::FORBIDDEN, _ => StatusCode::INTERNAL_SERVER_ERROR, }; - Err((state, err.into_handler_error().with_status(status))) + let err: HandlerError = err.into(); + Err((state, err.with_status(status))) } }) .boxed() diff --git a/gotham/src/handler/error.rs b/gotham/src/handler/error.rs index f4113ec0b..f842166bf 100644 --- a/gotham/src/handler/error.rs +++ b/gotham/src/handler/error.rs @@ -1,5 +1,8 @@ -use std::error::Error; -use std::fmt::{self, Debug, Display, Formatter}; +use futures::future::FusedFuture; +use std::fmt::{Debug, Display}; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; use hyper::{Body, Response, StatusCode}; use log::{debug, trace}; @@ -10,84 +13,34 @@ use crate::state::{request_id, State}; /// Describes an error which occurred during handler execution, and allows the creation of a HTTP /// `Response`. +#[derive(Debug)] pub struct HandlerError { status_code: StatusCode, - cause: Box, + cause: anyhow::Error, } -/// Allows conversion into a HandlerError from an implementing type. -/// -/// Futures returned from handlers can resolve to an error type with a value of `(State, -/// HandlerError)`. -/// -/// ```rust -/// # extern crate gotham; -/// # extern crate futures; -/// # -/// # use std::fs::File; -/// # use std::pin::Pin; -/// # use gotham::state::State; -/// # use gotham::handler::{IntoHandlerError, HandlerFuture}; -/// # use futures::prelude::*; -/// # -/// # #[allow(dead_code)] -/// fn my_handler(state: State) -> Pin> { -/// match File::open("config.toml") { -/// Err(e) => future::err((state, e.into_handler_error())).boxed(), -/// Ok(_) => // Create and return a response -/// # unimplemented!(), -/// } -/// } -/// # -/// # fn main() {} -pub trait IntoHandlerError { - /// Convert `self` into a `HandlerError`. - /// - /// The return value will have a `500 Internal Server Error` as the HTTP status code. See - /// `HandlerError::with_status` for an example of changing it. - fn into_handler_error(self) -> HandlerError; -} - -impl IntoHandlerError for E +/// Convert a generic `anyhow::Error` into a `HandlerError`, similar as you would a concrete error +/// type with `into_handler_error()`. +impl From for HandlerError where - E: Error + Send + 'static, + E: Into + Display, { - fn into_handler_error(self) -> HandlerError { - trace!(" converting Error to HandlerError: {}", self); + fn from(error: E) -> HandlerError { + trace!(" converting Error to HandlerError: {}", error); HandlerError { status_code: StatusCode::INTERNAL_SERVER_ERROR, - cause: Box::new(self), + cause: error.into(), } } } -impl Display for HandlerError { - fn fmt(&self, out: &mut Formatter) -> fmt::Result { - out.write_str("handler failed to process request") - } -} - -impl Debug for HandlerError { - fn fmt(&self, out: &mut Formatter) -> fmt::Result { - Display::fmt(self, out)?; - out.write_str(" (")?; - Debug::fmt(&*self.cause, out)?; - out.write_str(")") - } -} - -impl Error for HandlerError { - fn description(&self) -> &str { - "handler failed to process request" - } - - fn cause(&self) -> Option<&dyn Error> { - Some(&*self.cause) +impl HandlerError { + /// Returns the HTTP status code associated with this `HandlerError`. + pub fn status(&self) -> StatusCode { + self.status_code } -} -impl HandlerError { /// Sets the HTTP status code of the response which is generated by the `IntoResponse` /// implementation. /// @@ -101,15 +54,14 @@ impl HandlerError { /// # use futures::prelude::*; /// # use hyper::StatusCode; /// # use gotham::state::State; - /// # use gotham::handler::{IntoHandlerError, HandlerFuture}; + /// # use gotham::handler::{HandlerError, HandlerFuture}; /// # use gotham::test::TestServer; /// # /// fn handler(state: State) -> Pin> { /// // It's OK if this is bogus, we just need something to convert into a `HandlerError`. /// let io_error = std::io::Error::last_os_error(); /// - /// let handler_error = io_error - /// .into_handler_error() + /// let handler_error = HandlerError::from(io_error) /// .with_status(StatusCode::IM_A_TEAPOT); /// /// future::err((state, handler_error)).boxed() @@ -140,9 +92,152 @@ impl IntoResponse for HandlerError { self.status_code .canonical_reason() .unwrap_or("(unregistered)",), - self.source().map(Error::description).unwrap_or("(none)"), + self.cause ); create_empty_response(state, self.status_code) } } + +/// This trait allows you to convert a `Result`'s `Err` case into a handler error with the given +/// status code. This is handy if you want to specify the status code but still use the `?` +/// shorthand. +/// +/// ```rust +/// # extern crate gotham; +/// # use gotham::anyhow::anyhow; +/// # use gotham::handler::{HandlerError, MapHandlerError}; +/// # use gotham::hyper::StatusCode; +/// fn handler() -> Result<(), HandlerError> { +/// let result = Err(anyhow!("just a test")); +/// result.map_err_with_status(StatusCode::IM_A_TEAPOT)?; +/// unreachable!() +/// } +/// +/// # #[allow(non_snake_case)] +/// # fn Err(err: T) -> Result<(), T> { +/// # Result::Err(err) +/// # } +/// # fn main() { +/// let response = handler(); +/// assert_eq!(response.map_err(|err| err.status()), Err(StatusCode::IM_A_TEAPOT)); +/// # } +/// ``` +pub trait MapHandlerError { + /// Equivalent of `map_err(|err| HandlerError::from(err).with_status(status_code))`. + fn map_err_with_status(self, status_code: StatusCode) -> Result; +} + +impl MapHandlerError for Result +where + E: Into + Display, +{ + fn map_err_with_status(self, status_code: StatusCode) -> Result { + self.map_err(|err| { + trace!(" converting Error to HandlerError: {}", err); + HandlerError { + status_code, + cause: err.into(), + } + }) + } +} + +// The future for `map_err_with_status`. +#[pin_project::pin_project(project = MapErrWithStatusProj, project_replace = MapErrWithStatusProjOwn)] +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub enum MapErrWithStatus { + Incomplete { + #[pin] + future: F, + status: StatusCode, + }, + Complete, +} + +impl MapErrWithStatus { + fn new(future: F, status: StatusCode) -> Self { + Self::Incomplete { future, status } + } +} + +impl FusedFuture for MapErrWithStatus +where + F: Future>, + E: Into + Display, +{ + fn is_terminated(&self) -> bool { + matches!(self, Self::Complete) + } +} + +impl Future for MapErrWithStatus +where + F: Future>, + E: Into + Display, +{ + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.as_mut().project() { + MapErrWithStatusProj::Incomplete { future, .. } => { + let output = match future.poll(cx) { + Poll::Ready(output) => output, + Poll::Pending => return Poll::Pending, + }; + match self.project_replace(MapErrWithStatus::Complete) { + MapErrWithStatusProjOwn::Incomplete { status, .. } => { + Poll::Ready(output.map_err_with_status(status)) + } + MapErrWithStatusProjOwn::Complete => unreachable!(), + } + } + MapErrWithStatusProj::Complete => { + panic!("MapErrWithStatus must not be polled after it returned `Poll::Ready`") + } + } + } +} + +/// This trait allows you to convert a `Result`'s `Err` case into a handler error with the given +/// status code. This is handy if you want to specify the status code but still use the `?` +/// shorthand. +/// ```rust +/// # extern crate futures; +/// # extern crate gotham; +/// # use futures::executor::block_on; +/// # use gotham::anyhow::anyhow; +/// # use gotham::handler::{HandlerError, MapHandlerErrorFuture}; +/// # use gotham::hyper::StatusCode; +/// # use std::future::Future; +/// fn handler() -> impl Future> { +/// let result = async { Err(anyhow!("just a test")) }; +/// result.map_err_with_status(StatusCode::IM_A_TEAPOT) +/// } +/// +/// # #[allow(non_snake_case)] +/// # fn Err(err: T) -> Result<(), T> { +/// # Result::Err(err) +/// # } +/// # fn main() { +/// let response = block_on(handler()); +/// assert_eq!(response.map_err(|err| err.status()), Err(StatusCode::IM_A_TEAPOT)); +/// # } +/// ``` +pub trait MapHandlerErrorFuture { + /// Equivalent of `map_err(|err| HandlerError::from(err).with_status(status_code))`. + fn map_err_with_status(self, status_code: StatusCode) -> MapErrWithStatus + where + Self: Sized; +} + +impl MapHandlerErrorFuture for F +where + E: Into + Display, + F: Future>, +{ + fn map_err_with_status(self, status_code: StatusCode) -> MapErrWithStatus { + MapErrWithStatus::new(self, status_code) + } +} diff --git a/gotham/src/handler/mod.rs b/gotham/src/handler/mod.rs index 1a4f452a0..27a118e2f 100644 --- a/gotham/src/handler/mod.rs +++ b/gotham/src/handler/mod.rs @@ -4,8 +4,10 @@ //! `Handler`, but the traits can also be implemented directly for greater control. See the //! `Handler` trait for some examples of valid handlers. use std::borrow::Cow; +use std::ops::Deref; use std::panic::RefUnwindSafe; use std::pin::Pin; +use std::sync::Arc; use bytes::Bytes; use futures::prelude::*; @@ -16,12 +18,11 @@ use crate::helpers::http::response; use crate::state::State; mod error; -use crate::error::*; /// Defines handlers for serving static assets. pub mod assets; -pub use self::error::{HandlerError, IntoHandlerError}; +pub use self::error::{HandlerError, MapHandlerError, MapHandlerErrorFuture}; /// A type alias for the results returned by async fns that can be passed to to_async. pub type HandlerResult = std::result::Result<(State, Response), (State, HandlerError)>; @@ -133,7 +134,7 @@ pub type HandlerFuture = dyn Future + Send; /// # /// # use gotham::handler::{Handler, HandlerFuture, NewHandler}; /// # use gotham::state::State; -/// # use gotham::error::*; +/// # use gotham::anyhow; /// # /// # fn main() { /// #[derive(Copy, Clone)] @@ -142,7 +143,7 @@ pub type HandlerFuture = dyn Future + Send; /// impl NewHandler for MyCustomHandler { /// type Instance = Self; /// -/// fn new_handler(&self) -> Result { +/// fn new_handler(&self) -> anyhow::Result { /// Ok(*self) /// } /// } @@ -191,7 +192,7 @@ where /// # /// # use gotham::handler::{Handler, HandlerFuture, NewHandler}; /// # use gotham::state::State; -/// # use gotham::error::*; +/// # use gotham::anyhow; /// # /// # fn main() { /// #[derive(Copy, Clone)] @@ -200,7 +201,7 @@ where /// impl NewHandler for MyCustomHandler { /// type Instance = Self; /// -/// fn new_handler(&self) -> Result { +/// fn new_handler(&self) -> anyhow::Result { /// Ok(*self) /// } /// } @@ -227,7 +228,7 @@ where /// # /// # use gotham::handler::{Handler, HandlerFuture, NewHandler}; /// # use gotham::state::State; -/// # use gotham::error::*; +/// # use gotham::anyhow; /// # /// # fn main() { /// #[derive(Copy, Clone)] @@ -236,7 +237,7 @@ where /// impl NewHandler for MyValueInstantiatingHandler { /// type Instance = MyHandler; /// -/// fn new_handler(&self) -> Result { +/// fn new_handler(&self) -> anyhow::Result { /// Ok(MyHandler) /// } /// } @@ -259,21 +260,32 @@ pub trait NewHandler: Send + Sync + RefUnwindSafe { type Instance: Handler + Send; /// Create and return a new `Handler` value. - fn new_handler(&self) -> Result; + fn new_handler(&self) -> anyhow::Result; } impl NewHandler for F where - F: Fn() -> Result + Send + Sync + RefUnwindSafe, + F: Fn() -> anyhow::Result + Send + Sync + RefUnwindSafe, H: Handler + Send, { type Instance = H; - fn new_handler(&self) -> Result { + fn new_handler(&self) -> anyhow::Result { self() } } +impl NewHandler for Arc +where + H: NewHandler, +{ + type Instance = H::Instance; + + fn new_handler(&self) -> anyhow::Result { + self.deref().new_handler() + } +} + /// Represents a type which can be converted into the future type returned by a `Handler`. /// /// This is used to allow functions with different return types to satisfy the `Handler` trait diff --git a/gotham/src/lib.rs b/gotham/src/lib.rs index 5afe053b3..ee7a11769 100644 --- a/gotham/src/lib.rs +++ b/gotham/src/lib.rs @@ -25,7 +25,7 @@ // TODO: Remove this when it's a hard error by default (error E0446). // See Rust issue #34537 #![deny(private_in_public)] -pub mod error; + pub mod extractor; pub mod handler; pub mod helpers; @@ -45,6 +45,8 @@ pub mod plain; #[cfg(feature = "rustls")] pub mod tls; +/// Re-export anyhow +pub use anyhow; /// Re-export hyper pub use hyper; diff --git a/gotham/src/middleware/chain.rs b/gotham/src/middleware/chain.rs index 337b970c9..a30cdaa24 100644 --- a/gotham/src/middleware/chain.rs +++ b/gotham/src/middleware/chain.rs @@ -2,7 +2,6 @@ use log::trace; -use std::io; use std::panic::RefUnwindSafe; use std::pin::Pin; @@ -19,7 +18,7 @@ pub unsafe trait NewMiddlewareChain: RefUnwindSafe + Sized { type Instance: MiddlewareChain; /// Create and return a new `MiddlewareChain` value. - fn construct(&self) -> io::Result; + fn construct(&self) -> anyhow::Result; } unsafe impl NewMiddlewareChain for (T, U) @@ -30,7 +29,7 @@ where { type Instance = (T::Instance, U::Instance); - fn construct(&self) -> io::Result { + fn construct(&self) -> anyhow::Result { // This works as a recursive `map` over the "list" of `NewMiddleware`, and is used in // creating the `Middleware` instances for serving a single request. // @@ -44,7 +43,7 @@ where unsafe impl NewMiddlewareChain for () { type Instance = (); - fn construct(&self) -> io::Result { + fn construct(&self) -> anyhow::Result { // () marks the end of the list, so is returned as-is. trace!(" completed middleware pipeline construction"); Ok(()) diff --git a/gotham/src/middleware/cookie/mod.rs b/gotham/src/middleware/cookie/mod.rs index 6a43a53b2..6d21c6c4e 100644 --- a/gotham/src/middleware/cookie/mod.rs +++ b/gotham/src/middleware/cookie/mod.rs @@ -1,5 +1,4 @@ //! Defines a cookie parsing middleware to be attach cookies on requests. -use std::io; use std::pin::Pin; use cookie::{Cookie, CookieJar}; @@ -52,7 +51,7 @@ impl NewMiddleware for CookieParser { type Instance = Self; /// Clones the current middleware to a new instance. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(*self) } } diff --git a/gotham/src/middleware/logger/mod.rs b/gotham/src/middleware/logger/mod.rs index e96946441..b6076472c 100644 --- a/gotham/src/middleware/logger/mod.rs +++ b/gotham/src/middleware/logger/mod.rs @@ -9,7 +9,6 @@ use futures::prelude::*; use hyper::{header::CONTENT_LENGTH, Method, Uri, Version}; use log::Level; use log::{log, log_enabled}; -use std::io; use std::pin::Pin; use crate::handler::HandlerFuture; @@ -42,7 +41,7 @@ impl NewMiddleware for RequestLogger { type Instance = Self; /// Returns a new middleware to be used to serve a request. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(*self) } } @@ -132,7 +131,7 @@ impl NewMiddleware for SimpleLogger { type Instance = Self; /// Returns a new middleware to be used to serve a request. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(*self) } } diff --git a/gotham/src/middleware/mod.rs b/gotham/src/middleware/mod.rs index 78797b24c..17a54ad83 100644 --- a/gotham/src/middleware/mod.rs +++ b/gotham/src/middleware/mod.rs @@ -1,7 +1,6 @@ //! Defines types for `Middleware`, a reusable unit of logic that can apply to a group of requests //! by being added to the `Pipeline` in a `Router`. -use std::io; use std::panic::RefUnwindSafe; use std::pin::Pin; @@ -347,7 +346,6 @@ pub trait Middleware { /// ```rust /// # extern crate gotham; /// # -/// # use std::io; /// # use std::pin::Pin; /// # /// # use gotham::middleware::{NewMiddleware, Middleware}; @@ -362,7 +360,7 @@ pub trait Middleware { /// impl NewMiddleware for MyMiddleware { /// type Instance = Self; /// -/// fn new_middleware(&self) -> io::Result { +/// fn new_middleware(&self) -> anyhow::Result { /// Ok(self.clone()) /// } /// } @@ -384,5 +382,5 @@ pub trait NewMiddleware: Sync + RefUnwindSafe { type Instance: Middleware; /// Create and return a new `Middleware` value. - fn new_middleware(&self) -> io::Result; + fn new_middleware(&self) -> anyhow::Result; } diff --git a/gotham/src/middleware/security/mod.rs b/gotham/src/middleware/security/mod.rs index f898f532a..bf56af197 100644 --- a/gotham/src/middleware/security/mod.rs +++ b/gotham/src/middleware/security/mod.rs @@ -19,7 +19,6 @@ use futures::prelude::*; use std::pin::Pin; use hyper::header::{HeaderValue, X_CONTENT_TYPE_OPTIONS, X_FRAME_OPTIONS, X_XSS_PROTECTION}; -use std::io; // constant strings to be used as header values const XFO_VALUE: &str = "DENY"; @@ -60,7 +59,7 @@ impl NewMiddleware for SecurityMiddleware { type Instance = Self; /// Clones the current middleware to a new instance. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(self.clone()) } } diff --git a/gotham/src/middleware/session/backend/memory.rs b/gotham/src/middleware/session/backend/memory.rs index 6b51273c8..af0e72cc4 100644 --- a/gotham/src/middleware/session/backend/memory.rs +++ b/gotham/src/middleware/session/backend/memory.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use std::sync::{Arc, Mutex, PoisonError, Weak}; +use std::thread; use std::time::{Duration, Instant}; -use std::{io, thread}; use futures::prelude::*; use linked_hash_map::LinkedHashMap; @@ -72,7 +72,7 @@ impl Default for MemoryBackend { impl NewBackend for MemoryBackend { type Instance = MemoryBackend; - fn new_backend(&self) -> io::Result { + fn new_backend(&self) -> anyhow::Result { Ok(self.clone()) } } diff --git a/gotham/src/middleware/session/backend/mod.rs b/gotham/src/middleware/session/backend/mod.rs index 24b41e71e..a41f930b1 100644 --- a/gotham/src/middleware/session/backend/mod.rs +++ b/gotham/src/middleware/session/backend/mod.rs @@ -1,6 +1,5 @@ pub(super) mod memory; -use std::io; use std::panic::RefUnwindSafe; use std::pin::Pin; @@ -14,7 +13,7 @@ pub trait NewBackend: Sync + Clone + RefUnwindSafe { type Instance: Backend + Send + 'static; /// Create and return a new `Backend` value. - fn new_backend(&self) -> io::Result; + fn new_backend(&self) -> anyhow::Result; } /// Type alias for the trait objects returned by `Backend`. diff --git a/gotham/src/middleware/session/mod.rs b/gotham/src/middleware/session/mod.rs index ac81adb60..55580cfe7 100644 --- a/gotham/src/middleware/session/mod.rs +++ b/gotham/src/middleware/session/mod.rs @@ -19,7 +19,7 @@ use serde::{Deserialize, Serialize}; use super::cookie::CookieParser; use super::{Middleware, NewMiddleware}; -use crate::handler::{HandlerError, HandlerFuture, IntoHandlerError}; +use crate::handler::{HandlerError, HandlerFuture}; use crate::helpers::http::response::create_empty_response; use crate::state::{self, FromState, State, StateData}; @@ -488,7 +488,7 @@ where { type Instance = SessionMiddleware; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { self.new_backend .new_backend() .map(|backend| SessionMiddleware { @@ -1017,7 +1017,7 @@ where format!("backend failed to return session: {:?}", e), ); - future::err((state, e.into_handler_error())) + future::err((state, e.into())) } } } diff --git a/gotham/src/middleware/state/mod.rs b/gotham/src/middleware/state/mod.rs index d3cde454d..95ae1f78c 100644 --- a/gotham/src/middleware/state/mod.rs +++ b/gotham/src/middleware/state/mod.rs @@ -7,7 +7,6 @@ use crate::handler::HandlerFuture; use crate::middleware::{Middleware, NewMiddleware}; use crate::state::{State, StateData}; -use std::io; use std::panic::RefUnwindSafe; use std::pin::Pin; @@ -63,7 +62,7 @@ where type Instance = Self; /// Clones the current middleware to a new instance. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(self.clone()) } } diff --git a/gotham/src/middleware/timer/mod.rs b/gotham/src/middleware/timer/mod.rs index e47689f8d..6223854bf 100644 --- a/gotham/src/middleware/timer/mod.rs +++ b/gotham/src/middleware/timer/mod.rs @@ -7,8 +7,6 @@ use crate::state::State; use futures::prelude::*; use std::pin::Pin; -use std::io; - /// Middleware binding to attach request execution times inside headers. /// /// This can be used to easily measure request time from outside the @@ -46,7 +44,7 @@ impl NewMiddleware for RequestTimer { type Instance = Self; /// Clones the current middleware to a new instance. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(self.clone()) } } diff --git a/gotham/src/pipeline/chain.rs b/gotham/src/pipeline/chain.rs index 4dbb29929..fea12ebe9 100644 --- a/gotham/src/pipeline/chain.rs +++ b/gotham/src/pipeline/chain.rs @@ -7,7 +7,7 @@ use log::trace; use std::panic::RefUnwindSafe; use std::pin::Pin; -use crate::handler::{HandlerFuture, IntoHandlerError}; +use crate::handler::HandlerFuture; use crate::middleware::chain::NewMiddlewareChain; use crate::pipeline::set::PipelineSet; use crate::pipeline::Pipeline; @@ -52,7 +52,7 @@ where Ok(p) => chain.call(pipelines, state, move |state| p.call(state, f)), Err(e) => { trace!("[{}] error borrowing pipeline", request_id(&state)); - future::err((state, e.into_handler_error())).boxed() + future::err((state, e.into())).boxed() } } } diff --git a/gotham/src/pipeline/mod.rs b/gotham/src/pipeline/mod.rs index ff1df1d3d..71d883d1c 100644 --- a/gotham/src/pipeline/mod.rs +++ b/gotham/src/pipeline/mod.rs @@ -5,7 +5,6 @@ pub mod set; pub mod single; use log::trace; -use std::io; use std::pin::Pin; use crate::handler::HandlerFuture; @@ -142,7 +141,7 @@ where { /// Constructs an instance of this `Pipeline` by creating all `Middleware` instances required /// to serve a request. If any middleware fails creation, its error will be returned. - fn construct(&self) -> io::Result> { + fn construct(&self) -> anyhow::Result> { Ok(PipelineInstance { chain: self.chain.construct()?, }) @@ -295,7 +294,7 @@ mod tests { use futures::prelude::*; use hyper::{Body, Response, StatusCode}; - use crate::handler::{Handler, IntoHandlerError}; + use crate::handler::Handler; use crate::middleware::Middleware; use crate::state::StateData; use crate::test::TestServer; @@ -319,7 +318,7 @@ mod tests { impl NewMiddleware for Number { type Instance = Number; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(self.clone()) } } @@ -344,7 +343,7 @@ mod tests { impl NewMiddleware for Addition { type Instance = Addition; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(Addition { ..*self }) } } @@ -367,7 +366,7 @@ mod tests { impl NewMiddleware for Multiplication { type Instance = Multiplication; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(Multiplication { ..*self }) } } @@ -398,7 +397,7 @@ mod tests { Ok(move |state| match pipeline.construct() { Ok(p) => p.call(state, |state| handler.handle(state)), - Err(e) => future::err((state, e.into_handler_error())).boxed(), + Err(e) => future::err((state, e.into())).boxed(), }) }) .unwrap(); diff --git a/gotham/src/plain/test.rs b/gotham/src/plain/test.rs index c40bd299a..5ea1540bd 100644 --- a/gotham/src/plain/test.rs +++ b/gotham/src/plain/test.rs @@ -10,8 +10,6 @@ use std::sync::{Arc, RwLock}; use std::task::{Context, Poll}; use std::time::Duration; -use failure; -use failure::ResultExt; use log::info; use futures::prelude::*; @@ -23,7 +21,6 @@ use tokio::time::{delay_for, Delay}; use hyper::service::Service; use tokio::net::TcpStream; -use crate::error::*; use crate::handler::NewHandler; use crate::test::{self, TestClient}; @@ -96,7 +93,7 @@ impl TestServer { /// for each connection. /// /// Timeout will be set to 10 seconds. - pub fn new(new_handler: NH) -> Result + pub fn new(new_handler: NH) -> anyhow::Result where NH::Instance: UnwindSafe, { @@ -107,15 +104,13 @@ impl TestServer { pub fn with_timeout( new_handler: NH, timeout: u64, - ) -> Result + ) -> anyhow::Result where NH::Instance: UnwindSafe, { let mut runtime = Runtime::new()?; // TODO: Fix this into an async flow - let listener = runtime.block_on(TcpListener::bind( - "127.0.0.1:0".parse::().compat()?, - ))?; + let listener = runtime.block_on(TcpListener::bind("127.0.0.1:0".parse::()?))?; let addr = listener.local_addr()?; let service_stream = super::bind_server(listener, new_handler, future::ok); @@ -161,13 +156,12 @@ impl TestServer { client_addr: net::SocketAddr, ) -> TestClient { self.try_client_with_address(client_addr) - .expect("TestServer: unable to spawn client") } fn try_client_with_address( &self, _client_addr: net::SocketAddr, - ) -> Result> { + ) -> TestClient { // We're creating a private TCP-based pipe here. Bind to an ephemeral port, connect to // it and then immediately discard the listener. @@ -175,10 +169,10 @@ impl TestServer { addr: self.data.addr, }); - Ok(TestClient { + TestClient { client, test_server: self.clone(), - }) + } } } @@ -191,7 +185,7 @@ pub struct TestConnect { impl Service for TestConnect { type Response = TcpStream; - type Error = CompatError; + type Error = tokio::io::Error; type Future = Pin> + Send>>; @@ -202,7 +196,6 @@ impl Service for TestConnect { fn call(&mut self, _req: Uri) -> Self::Future { TcpStream::connect(self.addr) .inspect(|s| info!("Client TcpStream connected: {:?}", s)) - .map_err(|e| Error::from(e).compat()) .boxed() } } @@ -216,7 +209,7 @@ mod tests { use mime; use std::time::{SystemTime, UNIX_EPOCH}; - use crate::handler::{Handler, HandlerFuture, IntoHandlerError, NewHandler}; + use crate::handler::{Handler, HandlerFuture, NewHandler}; use crate::helpers::http::response::create_response; use crate::state::{client_addr, FromState, State}; use http::header::CONTENT_TYPE; @@ -267,7 +260,7 @@ mod tests { impl NewHandler for TestHandler { type Instance = Self; - fn new_handler(&self) -> Result { + fn new_handler(&self) -> anyhow::Result { Ok(self.clone()) } } @@ -363,7 +356,7 @@ mod tests { future::ok((state, res)) } - Err(e) => future::err((state, e.into_handler_error())), + Err(e) => future::err((state, e.into())), }) .boxed() } diff --git a/gotham/src/router/builder/draw.rs b/gotham/src/router/builder/draw.rs index c23106e0b..974ddc5e4 100644 --- a/gotham/src/router/builder/draw.rs +++ b/gotham/src/router/builder/draw.rs @@ -950,7 +950,6 @@ where #[cfg(test)] mod tests { - use std::io; use std::pin::Pin; use futures::prelude::*; @@ -971,7 +970,7 @@ mod tests { impl NewMiddleware for QuickExitMiddleware { type Instance = Self; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(*self) } } diff --git a/gotham/src/router/builder/single.rs b/gotham/src/router/builder/single.rs index 371fb1fb9..b241bd2af 100644 --- a/gotham/src/router/builder/single.rs +++ b/gotham/src/router/builder/single.rs @@ -179,7 +179,7 @@ pub trait DefineSingleRoute { /// # use gotham::pipeline::single::*; /// # use gotham::middleware::session::NewSessionMiddleware; /// # use gotham::test::TestServer; - /// # use gotham::error::*; + /// # use gotham::anyhow; /// # /// struct MyNewHandler; /// struct MyHandler; @@ -187,7 +187,7 @@ pub trait DefineSingleRoute { /// impl NewHandler for MyNewHandler { /// type Instance = MyHandler; /// - /// fn new_handler(&self) -> Result { + /// fn new_handler(&self) -> anyhow::Result { /// Ok(MyHandler) /// } /// } diff --git a/gotham/src/router/mod.rs b/gotham/src/router/mod.rs index a5fba0f01..3628aca39 100644 --- a/gotham/src/router/mod.rs +++ b/gotham/src/router/mod.rs @@ -17,7 +17,6 @@ use hyper::header::ALLOW; use hyper::{Body, Response, StatusCode}; use log::{error, trace}; -use crate::error::*; use crate::handler::{Handler, HandlerFuture, IntoResponse, NewHandler}; use crate::helpers::http::request::path::RequestPathSegments; use crate::helpers::http::response::create_empty_response; @@ -62,7 +61,7 @@ impl NewHandler for Router { type Instance = Router; // Creates a new Router instance to route new HTTP requests - fn new_handler(&self) -> Result { + fn new_handler(&self) -> anyhow::Result { trace!(" cloning instance"); Ok(self.clone()) } diff --git a/gotham/src/router/route/dispatch.rs b/gotham/src/router/route/dispatch.rs index fe213f315..de4e647f7 100644 --- a/gotham/src/router/route/dispatch.rs +++ b/gotham/src/router/route/dispatch.rs @@ -5,7 +5,7 @@ use log::trace; use std::panic::RefUnwindSafe; use std::pin::Pin; -use crate::handler::{Handler, HandlerFuture, IntoHandlerError, NewHandler}; +use crate::handler::{Handler, HandlerFuture, NewHandler}; use crate::pipeline::chain::PipelineHandleChain; use crate::pipeline::set::PipelineSet; use crate::state::{request_id, State}; @@ -67,7 +67,7 @@ where } Err(e) => { trace!("[{}] error cloning handler", request_id(&state)); - future::err((state, e.compat().into_handler_error())).boxed() + future::err((state, e.into())).boxed() } } } @@ -76,7 +76,6 @@ where #[cfg(test)] mod tests { use super::*; - use std::io; use std::sync::Arc; use hyper::{Body, Response, StatusCode}; @@ -106,7 +105,7 @@ mod tests { impl NewMiddleware for Number { type Instance = Number; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(self.clone()) } } @@ -131,7 +130,7 @@ mod tests { impl NewMiddleware for Addition { type Instance = Addition; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(Addition { ..*self }) } } @@ -154,7 +153,7 @@ mod tests { impl NewMiddleware for Multiplication { type Instance = Multiplication; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(Multiplication { ..*self }) } } diff --git a/gotham/src/router/route/matcher/accept.rs b/gotham/src/router/route/matcher/accept.rs index c6fadf1a8..be05bfd9c 100644 --- a/gotham/src/router/route/matcher/accept.rs +++ b/gotham/src/router/route/matcher/accept.rs @@ -7,7 +7,6 @@ use mime; use mime::Mime; use super::{LookupTable, LookupTableFromTypes}; -use crate::error; use crate::router::route::RouteMatcher; use crate::router::RouteNonMatch; use crate::state::{request_id, FromState, State}; @@ -28,9 +27,9 @@ impl QMime { } impl core::str::FromStr for QMime { - type Err = error::Error; + type Err = anyhow::Error; - fn from_str(str: &str) -> error::Result { + fn from_str(str: &str) -> anyhow::Result { match str.find(";q=") { None => Ok(Self::new(str.parse()?, None)), Some(index) => { diff --git a/gotham/src/router/route/matcher/access_control_request_method.rs b/gotham/src/router/route/matcher/access_control_request_method.rs index 5eab97a74..5bc87120f 100644 --- a/gotham/src/router/route/matcher/access_control_request_method.rs +++ b/gotham/src/router/route/matcher/access_control_request_method.rs @@ -1,3 +1,5 @@ +//! Defines the `AccessControlRequestMethodMatcher`. + use crate::{ router::{non_match::RouteNonMatch, route::matcher::RouteMatcher}, state::{FromState, State}, diff --git a/gotham/src/router/route/mod.rs b/gotham/src/router/route/mod.rs index 24d1e6fe9..11c59d843 100644 --- a/gotham/src/router/route/mod.rs +++ b/gotham/src/router/route/mod.rs @@ -255,7 +255,7 @@ mod tests { match route.dispatch(state).now_or_never() { Some(Ok((_state, response))) => assert_eq!(response.status(), StatusCode::ACCEPTED), - Some(Err((_state, e))) => panic!("error polling future: {}", e), + Some(Err((_state, e))) => panic!("error polling future: {:?}", e), None => panic!("expected future to be completed already"), } } @@ -287,7 +287,7 @@ mod tests { match route.dispatch(state).now_or_never() { Some(Ok((_state, response))) => assert_eq!(response.status(), StatusCode::ACCEPTED), - Some(Err((_state, e))) => panic!("error polling future: {}", e), + Some(Err((_state, e))) => panic!("error polling future: {:?}", e), None => panic!("expected future to be completed already"), } } diff --git a/gotham/src/service/mod.rs b/gotham/src/service/mod.rs index 03ffbb0c5..9ff99218f 100644 --- a/gotham/src/service/mod.rs +++ b/gotham/src/service/mod.rs @@ -7,8 +7,6 @@ use std::pin::Pin; use std::sync::Arc; use std::thread; -use failure; - use futures::prelude::*; use futures::task::{self, Poll}; use http::request; @@ -66,7 +64,7 @@ where T: NewHandler, { type Response = Response; - type Error = failure::Compat; // :Into> + type Error = anyhow::Error; type Future = Pin> + Send>>; fn poll_ready( @@ -76,7 +74,7 @@ where Poll::Ready(Ok(())) } - fn call(&mut self, req: Request) -> Self::Future { + fn call<'a>(&'a mut self, req: Request) -> Self::Future { let mut state = State::new(); put_client_addr(&mut state, self.client_addr); @@ -109,7 +107,7 @@ where ); }; - trap::call_handler(&*self.handler, AssertUnwindSafe(state)) + trap::call_handler(self.handler.clone(), AssertUnwindSafe(state)).boxed() } } diff --git a/gotham/src/service/trap.rs b/gotham/src/service/trap.rs index 776950a6f..b667a426c 100644 --- a/gotham/src/service/trap.rs +++ b/gotham/src/service/trap.rs @@ -1,12 +1,9 @@ //! Defines functionality for processing a request and trapping errors and panics in response //! generation. -use std::error::Error; use std::panic::catch_unwind; -use std::panic::AssertUnwindSafe; -use std::pin::Pin; +use std::panic::{AssertUnwindSafe, UnwindSafe}; -use failure; use futures::prelude::*; use hyper::{Body, Response, StatusCode}; @@ -15,7 +12,16 @@ use log::error; use crate::handler::{Handler, HandlerError, IntoResponse, NewHandler}; use crate::state::{request_id, State}; -type CompatError = failure::Compat; +async fn handle( + handler: H, + state: AssertUnwindSafe, +) -> Result<(State, Response), (State, HandlerError)> +where + H: Handler, +{ + let AssertUnwindSafe(state) = state; + handler.handle(state).await +} /// Instantiates a `Handler` from the given `NewHandler`, and invokes it with the request. If a /// panic occurs from `NewHandler::new_handler` or `Handler::handle`, it is trapped and will result @@ -23,90 +29,45 @@ type CompatError = failure::Compat; /// /// Timing information is recorded and logged, except in the case of a panic where the timer is /// moved and cannot be recovered. -pub(super) fn call_handler<'a, T>( - t: &T, +pub(super) async fn call_handler( + t: T, state: AssertUnwindSafe, -) -> Pin, CompatError>> + Send + 'a>> +) -> anyhow::Result> where - T: NewHandler + 'a, + T: NewHandler + Send + UnwindSafe, { - // Need to consume the NewHandler eagerly (vs lazy) since its borrowed - // The rest of the processing occurs in a future match catch_unwind(move || t.new_handler()) { Ok(handler) => { - let res = future::ready(handler) - .map_err(failure::Error::compat) - .and_then(move |handler| { - let AssertUnwindSafe(state) = state; - - handler.handle(state).then(move |result| match result { - Ok((_state, res)) => { - future::ok::<_, CompatError>(res).err_into().left_future() - } - Err((state, err)) => finalize_error_response(state, err) - .err_into() - .right_future(), - }) - }); - - AssertUnwindSafe(res) + let unwind_result = AssertUnwindSafe(handle(handler?, state)) .catch_unwind() - .then(|unwind_result| match unwind_result { - Ok(result) => finalize_catch_unwind_response(result).left_future(), - Err(_) => finalize_panic_response().right_future(), - }) - .left_future() + .await; + let result = match unwind_result { + Ok(result) => result.map(|(_, res)| res), + Err(_) => Ok(finalize_panic_response()), + }; + Ok(match result { + Ok(res) => res, + Err((state, err)) => finalize_error_response(state, err), + }) } - // Pannicked creating the handler from NewHandler - Err(_) => finalize_panic_response().right_future(), + // Error while creating the handler from NewHandler + Err(_) => Ok(finalize_panic_response()), } - .boxed() } -fn finalize_error_response( - state: State, - err: HandlerError, -) -> impl Future, CompatError>> { - { - // HandlerError::source() is far more interesting for logging, but the - // API doesn't guarantee its presence (even though it always is). - let err_description = err - .source() - .map(Error::description) - .unwrap_or_else(|| err.description()); - - error!( - "[ERROR][{}][Error: {}]", - request_id(&state), - err_description - ); - } - future::ok(err.into_response(&state)) +fn finalize_error_response(state: State, err: HandlerError) -> Response { + error!("[ERROR][{}][Error: {:?}]", request_id(&state), err); + + err.into_response(&state) } -fn finalize_panic_response() -> impl Future, CompatError>> { +fn finalize_panic_response() -> Response { error!("[PANIC][A panic occurred while invoking the handler]"); - future::ok( - Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::default()) - .unwrap(), - ) -} - -fn finalize_catch_unwind_response( - result: Result, CompatError>, -) -> impl Future, CompatError>> { - let response = result.unwrap_or_else(|_| { - error!("[PANIC][A panic occurred while polling the future]"); - Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Body::default()) - .unwrap() - }); - - future::ok(response) + Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .body(Body::default()) + .unwrap() } #[cfg(test)] @@ -114,11 +75,11 @@ mod tests { use super::*; use std::io; + use std::pin::Pin; use hyper::{HeaderMap, Method, StatusCode}; - use crate::error::Result; - use crate::handler::{HandlerFuture, IntoHandlerError}; + use crate::handler::HandlerFuture; use crate::helpers::http::response::create_empty_response; use crate::state::set_request_id; @@ -170,11 +131,8 @@ mod tests { #[test] fn error() { - let new_handler = || { - Ok(|state| { - future::err((state, io::Error::last_os_error().into_handler_error())).boxed() - }) - }; + let new_handler = + || Ok(|state| future::err((state, io::Error::last_os_error().into())).boxed()); let mut state = State::new(); state.put(HeaderMap::new()); @@ -249,7 +207,7 @@ mod tests { impl NewHandler for PanicNewHandler { type Instance = Self; - fn new_handler(&self) -> Result { + fn new_handler(&self) -> anyhow::Result { panic!("Pannicked creating a new handler"); } } @@ -266,7 +224,7 @@ mod tests { set_request_id(&mut state); let new_handler = PanicNewHandler {}; - let r = call_handler(&new_handler, AssertUnwindSafe(state)); + let r = call_handler(new_handler, AssertUnwindSafe(state)); let response = futures::executor::block_on(r).unwrap(); assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); } diff --git a/gotham/src/test.rs b/gotham/src/test.rs index 1ff305ad4..cb33413aa 100644 --- a/gotham/src/test.rs +++ b/gotham/src/test.rs @@ -5,9 +5,7 @@ use std::convert::TryFrom; use std::fmt; use std::ops::{Deref, DerefMut}; -use failure::format_err; -use failure::ResultExt; - +use anyhow::anyhow; use futures::prelude::*; use hyper::client::connect::Connect; use hyper::client::Client; @@ -17,8 +15,6 @@ use log::warn; use mime; use tokio::time::Delay; -use crate::error::*; - pub use crate::plain::test::TestServer; use futures::TryFutureExt; pub use request::TestRequest; @@ -26,7 +22,7 @@ pub use request::TestRequest; pub(crate) trait BodyReader { /// Runs the underlying event loop until the response body has been fully read. An `Ok(_)` /// response holds a buffer containing all bytes of the response body. - fn read_body(&mut self, response: Response) -> Result>; + fn read_body(&mut self, response: Response) -> Result, hyper::Error>; } /// An in memory server for testing purposes. @@ -43,11 +39,11 @@ pub trait Server: Clone { /// /// If the future came from a different instance of `Server`, the event loop will run until /// the timeout is triggered. - fn run_request(&self, f: F) -> Result + fn run_request(&self, f: F) -> anyhow::Result where F: TryFuture + Unpin + Send + 'static, - F::Error: failure::Fail + Sized, F::Ok: Send, + F::Error: Into + Send, { self.run_future( // Race the timeout against the request future @@ -58,18 +54,17 @@ pub trait Server: Clone { .and_then(|might_expire| { future::ready(match might_expire { future::Either::Left((item, _)) => Ok(item), - future::Either::Right(_) => Err(failure::err_msg("timed out")), + future::Either::Right(_) => Err(anyhow!("timed out")), }) }) .into_future() - // Finally, make the fail error compatible .map_err(|error| error.into()), ) } } impl BodyReader for T { - fn read_body(&mut self, response: Response) -> Result> { + fn read_body(&mut self, response: Response) -> Result, hyper::Error> { let f = body::to_bytes(response.into_body()).and_then(|b| future::ok(b.to_vec())); self.run_future(f).map_err(|error| error.into()) } @@ -183,10 +178,10 @@ impl TestClien } /// Send a constructed request using this `TestClient`, and await the response. - pub fn perform(&self, req: TestRequest) -> Result { + pub fn perform(&self, req: TestRequest) -> anyhow::Result { let req_future = self.client.request(req.request()).map_err(|e| { warn!("Error from test client request {:?}", e); - format_err!("request failed: {:?}", e).compat() + e }); self.test_server @@ -263,14 +258,14 @@ impl fmt::Debug for TestResponse { impl TestResponse { /// Awaits the body of the underlying `Response`, and returns it. This will cause the event /// loop to execute until the `Response` body has been fully read into the `Vec`. - pub fn read_body(mut self) -> Result> { + pub fn read_body(mut self) -> Result, hyper::Error> { self.reader.read_body(self.response) } /// Awaits the UTF-8 encoded body of the underlying `Response`, and returns the `String`. This /// will cause the event loop to execute until the `Response` body has been fully read and the /// `String` created. - pub fn read_utf8_body(self) -> Result { + pub fn read_utf8_body(self) -> anyhow::Result { let buf = self.read_body()?; let s = String::from_utf8(buf)?; Ok(s) diff --git a/gotham/src/test/request.rs b/gotham/src/test/request.rs index 87f241edd..180993c97 100644 --- a/gotham/src/test/request.rs +++ b/gotham/src/test/request.rs @@ -9,8 +9,6 @@ use hyper::{Body, Method, Request, Uri}; use super::Server; use super::{TestClient, TestResponse}; -use crate::error::*; - /// Builder API for constructing `Server` requests. When the request is built, /// `RequestBuilder::perform` will issue the request and provide access to the response. pub struct TestRequest<'a, S: Server, C: Connect> { @@ -49,7 +47,7 @@ impl<'a, S: Server + 'static, C: Connect + Clone + Send + Sync + 'static> TestRe } /// Send a constructed request using the `TestClient`, and await the response. - pub fn perform(self) -> Result { + pub fn perform(self) -> anyhow::Result { self.client.perform(self) } diff --git a/gotham/src/tls/test.rs b/gotham/src/tls/test.rs index 579ceabe2..51051fb19 100644 --- a/gotham/src/tls/test.rs +++ b/gotham/src/tls/test.rs @@ -12,7 +12,6 @@ use std::sync::{Arc, RwLock}; use std::task::{Context, Poll}; use std::time::Duration; -use failure::Fail; use log::info; use futures::prelude::*; @@ -38,8 +37,6 @@ use tokio_rustls::{ use crate::handler::NewHandler; -use crate::error::*; - use crate::test::{self, TestClient}; struct TestServerData { @@ -110,7 +107,7 @@ impl TestServer { /// for each connection. /// /// Timeout will be set to 10 seconds. - pub fn new(new_handler: NH) -> Result + pub fn new(new_handler: NH) -> anyhow::Result where NH::Instance: UnwindSafe, { @@ -121,17 +118,13 @@ impl TestServer { pub fn with_timeout( new_handler: NH, timeout: u64, - ) -> Result + ) -> anyhow::Result where NH::Instance: UnwindSafe, { let mut runtime = Runtime::new()?; // TODO: Fix this into an async flow - let listener = runtime.block_on(TcpListener::bind( - "127.0.0.1:0" - .parse::() - .map_err(|e| e.compat())?, - ))?; + let listener = runtime.block_on(TcpListener::bind("127.0.0.1:0".parse::()?))?; let addr = listener.local_addr()?; let mut cfg = rustls::ServerConfig::new(NoClientAuth::new()); @@ -190,7 +183,7 @@ impl TestServer { fn try_client_with_address( &self, _client_addr: net::SocketAddr, - ) -> Result> { + ) -> anyhow::Result> { // We're creating a private TCP-based pipe here. Bind to an ephemeral port, connect to // it and then immediately discard the listener. let mut config = rustls::ClientConfig::new(); @@ -275,7 +268,7 @@ pub struct TestConnect { impl Service for TestConnect { type Response = TlsConnectionStream; - type Error = CompatError; + type Error = tokio::io::Error; type Future = Pin> + Send>>; @@ -287,7 +280,6 @@ impl Service for TestConnect { let tls = TlsConnector::from(self.config.clone()); TcpStream::connect(self.addr) - .map_err(|e| Error::from(e).compat()) .and_then(move |stream| { let domain = DNSNameRef::try_from_ascii_str(req.host().unwrap()).unwrap(); tls.connect(domain, stream) @@ -295,7 +287,7 @@ impl Service for TestConnect { .map_ok(TlsConnectionStream) .map_err(|e| { info!("TLS TestClient error: {:?}", e); - Error::from(e).compat() + e }) }) .boxed() @@ -312,7 +304,7 @@ mod tests { use hyper::{body, Body, Response, StatusCode, Uri}; use mime; - use crate::handler::{Handler, HandlerFuture, IntoHandlerError, NewHandler}; + use crate::handler::{Handler, HandlerFuture, NewHandler}; use crate::helpers::http::response::create_response; use crate::state::{client_addr, FromState, State}; use http::header::CONTENT_TYPE; @@ -364,7 +356,7 @@ mod tests { impl NewHandler for TestHandler { type Instance = Self; - fn new_handler(&self) -> Result { + fn new_handler(&self) -> anyhow::Result { Ok(self.clone()) } } @@ -466,7 +458,7 @@ mod tests { future::ok((state, res)) } - Err(e) => future::err((state, e.into_handler_error())), + Err(e) => future::err((state, e.into())), }, ); diff --git a/gotham_derive/src/new_middleware.rs b/gotham_derive/src/new_middleware.rs index 8f4ce4df9..c2ee86524 100644 --- a/gotham_derive/src/new_middleware.rs +++ b/gotham_derive/src/new_middleware.rs @@ -12,7 +12,7 @@ pub(crate) fn new_middleware(ast: &syn::DeriveInput) -> proc_macro::TokenStream { type Instance = Self; - fn new_middleware(&self) -> ::std::io::Result { + fn new_middleware(&self) -> ::gotham::anyhow::Result { // Calling it this way makes the error look like this: // // | #[derive(NewMiddleware)] diff --git a/middleware/diesel/src/lib.rs b/middleware/diesel/src/lib.rs index 4b7748832..717e07486 100644 --- a/middleware/diesel/src/lib.rs +++ b/middleware/diesel/src/lib.rs @@ -15,7 +15,7 @@ //! # use gotham::pipeline::*; //! # use gotham::state::{FromState, State}; //! # use gotham::helpers::http::response::create_response; -//! # use gotham::handler::{HandlerFuture, IntoHandlerError}; +//! # use gotham::handler::HandlerFuture; //! # use gotham_middleware_diesel::{self, DieselMiddleware}; //! # use diesel::{RunQueryDsl, SqliteConnection}; //! # use gotham::hyper::StatusCode; @@ -54,7 +54,7 @@ //! let res = create_response(&state, StatusCode::OK, mime::TEXT_PLAIN, body); //! Ok((state, res)) //! }, -//! Err(e) => Err((state, e.into_handler_error())), +//! Err(e) => Err((state, e.into())), //! } //! }.boxed() //! } @@ -76,11 +76,11 @@ use diesel::Connection; use futures::prelude::*; use log::{error, trace}; -use std::io; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::pin::Pin; use std::process; +use gotham::anyhow; use gotham::handler::HandlerFuture; use gotham::middleware::{Middleware, NewMiddleware}; use gotham::state::{request_id, State}; @@ -133,7 +133,7 @@ where { type Instance = DieselMiddleware; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { match catch_unwind(|| self.repo.clone()) { Ok(repo) => Ok(DieselMiddleware { repo: AssertUnwindSafe(repo), diff --git a/middleware/jwt/src/middleware.rs b/middleware/jwt/src/middleware.rs index dacdbc28d..d231c2800 100644 --- a/middleware/jwt/src/middleware.rs +++ b/middleware/jwt/src/middleware.rs @@ -5,6 +5,7 @@ use gotham::hyper::{ StatusCode, }; use gotham::{ + anyhow, handler::HandlerFuture, helpers::http::response::create_empty_response, middleware::{Middleware, NewMiddleware}, @@ -13,7 +14,7 @@ use gotham::{ use jsonwebtoken::{decode, DecodingKey, Validation}; use serde::de::Deserialize; use std::pin::Pin; -use std::{io, marker::PhantomData, panic::RefUnwindSafe}; +use std::{marker::PhantomData, panic::RefUnwindSafe}; const DEFAULT_SCHEME: &str = "Bearer"; @@ -173,7 +174,7 @@ where { type Instance = JWTMiddleware; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(JWTMiddleware { secret: self.secret.clone(), validation: self.validation.clone(), diff --git a/middleware/template/src/lib.rs b/middleware/template/src/lib.rs index ffe3ba140..d863c9d51 100644 --- a/middleware/template/src/lib.rs +++ b/middleware/template/src/lib.rs @@ -13,11 +13,11 @@ extern crate log; //#[macro_use] //extern crate gotham_derive; -use std::io; use std::pin::Pin; use futures::prelude::*; +use gotham::anyhow; use gotham::handler::HandlerFuture; use gotham::middleware::{Middleware, NewMiddleware}; use gotham::state::{request_id, State}; @@ -38,7 +38,7 @@ pub struct MyMiddleware {} impl NewMiddleware for MyMiddleware { type Instance = MyMiddleware; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> anyhow::Result { Ok(MyMiddleware {}) } }