1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
use std::error::Error;
use std::fmt::{self, Debug, Display, Formatter};
use hyper::{Body, Response, StatusCode};
use log::{debug, trace};
use crate::handler::IntoResponse;
use crate::helpers::http::response::create_empty_response;
use crate::state::{request_id, State};
/// Describes an error which occurred during handler execution, and allows the creation of a HTTP
/// `Response`.
pub struct HandlerError {
status_code: StatusCode,
cause: Box<dyn Error + Send>,
}
/// 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 gotham::state::State;
/// # use gotham::handler::{IntoHandlerError, HandlerFuture};
/// # use futures::future;
/// #
/// # #[allow(dead_code)]
/// fn my_handler(state: State) -> Box<HandlerFuture> {
/// match File::open("config.toml") {
/// Err(e) => Box::new(future::err((state, e.into_handler_error()))),
/// 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<E> IntoHandlerError for E
where
E: Error + Send + 'static,
{
fn into_handler_error(self) -> HandlerError {
trace!(" converting Error to HandlerError: {}", self);
HandlerError {
status_code: StatusCode::INTERNAL_SERVER_ERROR,
cause: Box::new(self),
}
}
}
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 {
/// Sets the HTTP status code of the response which is generated by the `IntoResponse`
/// implementation.
///
/// ```rust
/// # extern crate gotham;
/// # extern crate hyper;
/// # extern crate futures;
/// #
/// # use futures::future;
/// # use hyper::StatusCode;
/// # use gotham::state::State;
/// # use gotham::handler::{IntoHandlerError, HandlerFuture};
/// # use gotham::test::TestServer;
/// #
/// fn handler(state: State) -> Box<HandlerFuture> {
/// // 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()
/// .with_status(StatusCode::IM_A_TEAPOT);
///
/// Box::new(future::err((state, handler_error)))
/// }
///
/// # fn main() {
/// #
/// let test_server = TestServer::new(|| Ok(handler)).unwrap();
/// let response = test_server.client().get("http://example.com/").perform().unwrap();
/// assert_eq!(response.status(), StatusCode::IM_A_TEAPOT);
/// #
/// # }
/// ```
pub fn with_status(self, status_code: StatusCode) -> HandlerError {
HandlerError {
status_code,
..self
}
}
}
impl IntoResponse for HandlerError {
fn into_response(self, state: &State) -> Response<Body> {
debug!(
"[{}] HandlerError generating {} {} response: {}",
request_id(state),
self.status_code.as_u16(),
self.status_code
.canonical_reason()
.unwrap_or("(unregistered)",),
self.source().map(Error::description).unwrap_or("(none)"),
);
create_empty_response(state, self.status_code)
}
}