diff --git a/gotham/Cargo.toml b/gotham/Cargo.toml index 9d5a83899..64767d974 100644 --- a/gotham/Cargo.toml +++ b/gotham/Cargo.toml @@ -48,6 +48,7 @@ cookie = "0.13" http = "0.2" httpdate = "0.3" failure = "0.1" +thiserror = "1.0" itertools = "0.9.0" tokio-rustls = { version = "0.12.1", optional = true } diff --git a/gotham/src/middleware/chain.rs b/gotham/src/middleware/chain.rs index 337b970c9..8d08e7e3d 100644 --- a/gotham/src/middleware/chain.rs +++ b/gotham/src/middleware/chain.rs @@ -2,14 +2,32 @@ use log::trace; -use std::io; +use std::convert::Infallible; +use std::error::Error; use std::panic::RefUnwindSafe; use std::pin::Pin; +use thiserror::Error; use crate::handler::HandlerFuture; use crate::middleware::{Middleware, NewMiddleware}; use crate::state::{request_id, State}; +/// This error type is used by `NewMiddlewareChain` to wrap the two errors that can occur, one from +/// the middleware, and one from the chain, in one error type. +#[derive(Debug, Error)] +pub enum MiddlewareChainError +where + E: Error + 'static, + F: Error + 'static, +{ + /// Wrap an error returned by `NewMiddleware`. + #[error("{0}")] + MiddlewareError(#[source] E), + /// Wrap an error returned by `NewMiddlewareChain`. + #[error("{0}")] + ChainError(#[source] F), +} + /// A recursive type representing a pipeline, which is used to spawn a `MiddlewareChain`. /// /// This type should never be implemented outside of Gotham, does not form part of the public API, @@ -17,9 +35,10 @@ use crate::state::{request_id, State}; #[doc(hidden)] pub unsafe trait NewMiddlewareChain: RefUnwindSafe + Sized { type Instance: MiddlewareChain; + type Err: Error + Send + 'static; /// Create and return a new `MiddlewareChain` value. - fn construct(&self) -> io::Result; + fn construct(&self) -> Result; } unsafe impl NewMiddlewareChain for (T, U) @@ -29,22 +48,29 @@ where U: NewMiddlewareChain, { type Instance = (T::Instance, U::Instance); + type Err = MiddlewareChainError; - fn construct(&self) -> io::Result { + fn construct(&self) -> 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. // // The reversed order is preserved in the return value. trace!(" adding middleware instance to pipeline"); let (ref nm, ref tail) = *self; - Ok((nm.new_middleware()?, tail.construct()?)) + Ok(( + nm.new_middleware() + .map_err(|err| MiddlewareChainError::MiddlewareError(err))?, + tail.construct() + .map_err(|err| MiddlewareChainError::ChainError(err))?, + )) } } unsafe impl NewMiddlewareChain for () { type Instance = (); + type Err = Infallible; - fn construct(&self) -> io::Result { + fn construct(&self) -> Result<(), Infallible> { // () 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..4c7b2074b 100644 --- a/gotham/src/middleware/cookie/mod.rs +++ b/gotham/src/middleware/cookie/mod.rs @@ -1,5 +1,5 @@ //! Defines a cookie parsing middleware to be attach cookies on requests. -use std::io; +use std::convert::Infallible; use std::pin::Pin; use cookie::{Cookie, CookieJar}; @@ -50,9 +50,10 @@ impl Middleware for CookieParser { /// `NewMiddleware` trait implementation. impl NewMiddleware for CookieParser { type Instance = Self; + type Err = Infallible; /// Clones the current middleware to a new instance. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(*self) } } diff --git a/gotham/src/middleware/logger/mod.rs b/gotham/src/middleware/logger/mod.rs index e96946441..1bb6ffa34 100644 --- a/gotham/src/middleware/logger/mod.rs +++ b/gotham/src/middleware/logger/mod.rs @@ -9,7 +9,7 @@ use futures::prelude::*; use hyper::{header::CONTENT_LENGTH, Method, Uri, Version}; use log::Level; use log::{log, log_enabled}; -use std::io; +use std::convert::Infallible; use std::pin::Pin; use crate::handler::HandlerFuture; @@ -40,9 +40,10 @@ impl RequestLogger { /// which will clone the structure - should be cheaper for repeated calls. impl NewMiddleware for RequestLogger { type Instance = Self; + type Err = Infallible; /// Returns a new middleware to be used to serve a request. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(*self) } } @@ -130,9 +131,10 @@ impl SimpleLogger { /// which will clone the structure - should be cheaper for repeated calls. impl NewMiddleware for SimpleLogger { type Instance = Self; + type Err = Infallible; /// Returns a new middleware to be used to serve a request. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(*self) } } diff --git a/gotham/src/middleware/mod.rs b/gotham/src/middleware/mod.rs index 78797b24c..4a4039167 100644 --- a/gotham/src/middleware/mod.rs +++ b/gotham/src/middleware/mod.rs @@ -1,7 +1,7 @@ //! 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::error::Error; use std::panic::RefUnwindSafe; use std::pin::Pin; @@ -347,7 +347,7 @@ pub trait Middleware { /// ```rust /// # extern crate gotham; /// # -/// # use std::io; +/// # use std::convert::Infallible; /// # use std::pin::Pin; /// # /// # use gotham::middleware::{NewMiddleware, Middleware}; @@ -361,8 +361,9 @@ pub trait Middleware { /// /// impl NewMiddleware for MyMiddleware { /// type Instance = Self; +/// type Err = Infallible; /// -/// fn new_middleware(&self) -> io::Result { +/// fn new_middleware(&self) -> Result { /// Ok(self.clone()) /// } /// } @@ -382,7 +383,9 @@ pub trait Middleware { pub trait NewMiddleware: Sync + RefUnwindSafe { /// The type of `Middleware` created by the `NewMiddleware`. type Instance: Middleware; + /// The error that can occur when creating a new middleware. + type Err: Error + Send + 'static; /// Create and return a new `Middleware` value. - fn new_middleware(&self) -> io::Result; + fn new_middleware(&self) -> Result; } diff --git a/gotham/src/middleware/security/mod.rs b/gotham/src/middleware/security/mod.rs index f898f532a..e8a699ea8 100644 --- a/gotham/src/middleware/security/mod.rs +++ b/gotham/src/middleware/security/mod.rs @@ -16,10 +16,10 @@ use crate::handler::HandlerFuture; use crate::middleware::{Middleware, NewMiddleware}; use crate::state::State; use futures::prelude::*; +use std::convert::Infallible; 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"; @@ -58,9 +58,10 @@ impl Middleware for SecurityMiddleware { /// `NewMiddleware` trait implementation. impl NewMiddleware for SecurityMiddleware { type Instance = Self; + type Err = Infallible; /// Clones the current middleware to a new instance. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(self.clone()) } } diff --git a/gotham/src/middleware/session/backend/memory.rs b/gotham/src/middleware/session/backend/memory.rs index 6b51273c8..0adbbfdda 100644 --- a/gotham/src/middleware/session/backend/memory.rs +++ b/gotham/src/middleware/session/backend/memory.rs @@ -1,7 +1,8 @@ +use std::convert::Infallible; 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; @@ -71,8 +72,9 @@ impl Default for MemoryBackend { impl NewBackend for MemoryBackend { type Instance = MemoryBackend; + type Err = Infallible; - fn new_backend(&self) -> io::Result { + fn new_backend(&self) -> Result { Ok(self.clone()) } } diff --git a/gotham/src/middleware/session/backend/mod.rs b/gotham/src/middleware/session/backend/mod.rs index 24b41e71e..0ecad6c1f 100644 --- a/gotham/src/middleware/session/backend/mod.rs +++ b/gotham/src/middleware/session/backend/mod.rs @@ -1,6 +1,6 @@ pub(super) mod memory; -use std::io; +use std::error::Error; use std::panic::RefUnwindSafe; use std::pin::Pin; @@ -12,9 +12,11 @@ use crate::middleware::session::{SessionError, SessionIdentifier}; pub trait NewBackend: Sync + Clone + RefUnwindSafe { /// The type of `Backend` created by the `NewBackend`. type Instance: Backend + Send + 'static; + /// The type of error that might occur creating a new backend. + type Err: Error + Send + 'static; /// Create and return a new `Backend` value. - fn new_backend(&self) -> io::Result; + fn new_backend(&self) -> 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..50d1db91f 100644 --- a/gotham/src/middleware/session/mod.rs +++ b/gotham/src/middleware/session/mod.rs @@ -1,6 +1,5 @@ //! Defines a session middleware with a pluggable backend. -use std::io; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::panic::RefUnwindSafe; @@ -16,6 +15,7 @@ use hyper::{Body, Response, StatusCode}; use log::{error, trace, warn}; use rand::RngCore; use serde::{Deserialize, Serialize}; +use thiserror::Error; use super::cookie::CookieParser; use super::{Middleware, NewMiddleware}; @@ -40,14 +40,17 @@ pub struct SessionIdentifier { } /// The kind of failure which occurred trying to perform a session operation. -#[derive(Debug)] +#[derive(Debug, Error)] pub enum SessionError { /// The backend failed, and the included message describes the problem. + #[error("The backend failed to return a session: {0}")] Backend(String), /// The session was unable to be deserialized. + #[error("The backend failed to deserialize the session")] Deserialize, /// Exhaustive match against this enum is unsupported. #[doc(hidden)] + #[error("__NonExhaustive")] __NonExhaustive, } @@ -487,8 +490,9 @@ where T: Default + Serialize + for<'de> Deserialize<'de> + Send + 'static, { type Instance = SessionMiddleware; + type Err = B::Err; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { self.new_backend .new_backend() .map(|backend| SessionMiddleware { @@ -1006,17 +1010,12 @@ where } Err(e) => { error!( - "[{}] failed to retrieve session ({}) from backend: {:?}", + "[{}] failed to retrieve session ({}) from backend: {}", state::request_id(&state), identifier.value, e ); - let e = io::Error::new( - io::ErrorKind::Other, - format!("backend failed to return session: {:?}", e), - ); - future::err((state, e.into_handler_error())) } } diff --git a/gotham/src/middleware/state/mod.rs b/gotham/src/middleware/state/mod.rs index d3cde454d..cbcfa085f 100644 --- a/gotham/src/middleware/state/mod.rs +++ b/gotham/src/middleware/state/mod.rs @@ -7,7 +7,7 @@ use crate::handler::HandlerFuture; use crate::middleware::{Middleware, NewMiddleware}; use crate::state::{State, StateData}; -use std::io; +use std::convert::Infallible; use std::panic::RefUnwindSafe; use std::pin::Pin; @@ -61,9 +61,10 @@ where T: Clone + RefUnwindSafe + StateData + Sync, { type Instance = Self; + type Err = Infallible; /// Clones the current middleware to a new instance. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(self.clone()) } } diff --git a/gotham/src/middleware/timer/mod.rs b/gotham/src/middleware/timer/mod.rs index e47689f8d..b5300b4c4 100644 --- a/gotham/src/middleware/timer/mod.rs +++ b/gotham/src/middleware/timer/mod.rs @@ -5,10 +5,9 @@ use crate::helpers::timing::Timer; use crate::middleware::{Middleware, NewMiddleware}; use crate::state::State; use futures::prelude::*; +use std::convert::Infallible; 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 @@ -44,9 +43,10 @@ impl Middleware for RequestTimer { /// `NewMiddleware` trait implementation. impl NewMiddleware for RequestTimer { type Instance = Self; + type Err = Infallible; /// Clones the current middleware to a new instance. - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(self.clone()) } } diff --git a/gotham/src/pipeline/mod.rs b/gotham/src/pipeline/mod.rs index ff1df1d3d..8e3eb697a 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) -> Result, T::Err> { Ok(PipelineInstance { chain: self.chain.construct()?, }) @@ -292,6 +291,8 @@ where mod tests { use super::*; + use std::convert::Infallible; + use futures::prelude::*; use hyper::{Body, Response, StatusCode}; @@ -318,8 +319,9 @@ mod tests { impl NewMiddleware for Number { type Instance = Number; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(self.clone()) } } @@ -343,8 +345,9 @@ mod tests { impl NewMiddleware for Addition { type Instance = Addition; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(Addition { ..*self }) } } @@ -366,8 +369,9 @@ mod tests { impl NewMiddleware for Multiplication { type Instance = Multiplication; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(Multiplication { ..*self }) } } diff --git a/gotham/src/router/builder/draw.rs b/gotham/src/router/builder/draw.rs index e86745e0d..6c176f707 100644 --- a/gotham/src/router/builder/draw.rs +++ b/gotham/src/router/builder/draw.rs @@ -949,7 +949,7 @@ where #[cfg(test)] mod tests { - use std::io; + use std::convert::Infallible; use std::pin::Pin; use futures::prelude::*; @@ -969,8 +969,9 @@ mod tests { impl NewMiddleware for QuickExitMiddleware { type Instance = Self; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(*self) } } diff --git a/gotham/src/router/route/dispatch.rs b/gotham/src/router/route/dispatch.rs index fe213f315..e37db27a9 100644 --- a/gotham/src/router/route/dispatch.rs +++ b/gotham/src/router/route/dispatch.rs @@ -76,7 +76,7 @@ where #[cfg(test)] mod tests { use super::*; - use std::io; + use std::convert::Infallible; use std::sync::Arc; use hyper::{Body, Response, StatusCode}; @@ -105,8 +105,9 @@ mod tests { impl NewMiddleware for Number { type Instance = Number; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(self.clone()) } } @@ -130,8 +131,9 @@ mod tests { impl NewMiddleware for Addition { type Instance = Addition; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(Addition { ..*self }) } } @@ -153,8 +155,9 @@ mod tests { impl NewMiddleware for Multiplication { type Instance = Multiplication; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(Multiplication { ..*self }) } } diff --git a/gotham_derive/src/new_middleware.rs b/gotham_derive/src/new_middleware.rs index 8f4ce4df9..ba2f7f094 100644 --- a/gotham_derive/src/new_middleware.rs +++ b/gotham_derive/src/new_middleware.rs @@ -11,8 +11,9 @@ pub(crate) fn new_middleware(ast: &syn::DeriveInput) -> proc_macro::TokenStream #where_clause { type Instance = Self; + type Err = ::std::convert::Infallible; - fn new_middleware(&self) -> ::std::io::Result { + fn new_middleware(&self) -> 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..6302e5730 100644 --- a/middleware/diesel/src/lib.rs +++ b/middleware/diesel/src/lib.rs @@ -76,7 +76,7 @@ use diesel::Connection; use futures::prelude::*; use log::{error, trace}; -use std::io; +use std::convert::Infallible; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::pin::Pin; use std::process; @@ -132,8 +132,9 @@ where T: Connection + 'static, { type Instance = DieselMiddleware; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> 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..3bafbfd95 100644 --- a/middleware/jwt/src/middleware.rs +++ b/middleware/jwt/src/middleware.rs @@ -12,8 +12,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::{convert::Infallible, marker::PhantomData, panic::RefUnwindSafe, pin::Pin}; const DEFAULT_SCHEME: &str = "Bearer"; @@ -172,8 +171,9 @@ where T: for<'de> Deserialize<'de> + RefUnwindSafe + Send + Sync + 'static, { type Instance = JWTMiddleware; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> 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..36c29a591 100644 --- a/middleware/template/src/lib.rs +++ b/middleware/template/src/lib.rs @@ -13,7 +13,7 @@ extern crate log; //#[macro_use] //extern crate gotham_derive; -use std::io; +use std::convert::Infallible; use std::pin::Pin; use futures::prelude::*; @@ -37,8 +37,9 @@ pub struct MyMiddleware {} impl NewMiddleware for MyMiddleware { type Instance = MyMiddleware; + type Err = Infallible; - fn new_middleware(&self) -> io::Result { + fn new_middleware(&self) -> Result { Ok(MyMiddleware {}) } }