//! Error and Result module use std::io::Error as IoError; use std::str::Utf8Error; use std::string::FromUtf8Error; use std::sync::Mutex; use std::{fmt, io, result}; use actix::MailboxError; use cookie; use failure::{self, Backtrace, Fail}; use futures::Canceled; use http::uri::InvalidUri; use http::{header, Error as HttpError, StatusCode}; use http2::Error as Http2Error; use httparse; use serde::de::value::Error as DeError; use serde_json::error::Error as JsonError; use serde_urlencoded::ser::Error as FormError; use tokio_timer::Error as TimerError; pub use url::ParseError as UrlParseError; // re-exports pub use cookie::ParseError as CookieParseError; use handler::Responder; use httprequest::HttpRequest; use httpresponse::{HttpResponse, HttpResponseParts}; /// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html) /// for actix web operations /// /// This typedef is generally used to avoid writing out /// `actix_web::error::Error` directly and is otherwise a direct mapping to /// `Result`. pub type Result = result::Result; /// General purpose actix web error. /// /// An actix web error is used to carry errors from `failure` or `std::error` /// through actix in a convenient way. It can be created through through /// converting errors with `into()`. /// /// Whenever it is created from an external object a response error is created /// for it that can be used to create an http response from it this means that /// if you have access to an actix `Error` you can always get a /// `ResponseError` reference from it. pub struct Error { cause: Box, backtrace: Option, } impl Error { /// Deprecated way to reference the underlying response error. #[deprecated( since = "0.6.0", note = "please use `Error::as_response_error()` instead" )] pub fn cause(&self) -> &ResponseError { self.cause.as_ref() } /// Returns a reference to the underlying cause of this `Error` as `Fail` pub fn as_fail(&self) -> &Fail { self.cause.as_fail() } /// Returns the reference to the underlying `ResponseError`. pub fn as_response_error(&self) -> &ResponseError { self.cause.as_ref() } /// Returns a reference to the Backtrace carried by this error, if it /// carries one. /// /// This uses the same `Backtrace` type that `failure` uses. pub fn backtrace(&self) -> &Backtrace { if let Some(bt) = self.cause.backtrace() { bt } else { self.backtrace.as_ref().unwrap() } } /// Attempts to downcast this `Error` to a particular `Fail` type by /// reference. /// /// If the underlying error is not of type `T`, this will return `None`. pub fn downcast_ref(&self) -> Option<&T> { // in the most trivial way the cause is directly of the requested type. if let Some(rv) = Fail::downcast_ref(self.cause.as_fail()) { return Some(rv); } // in the more complex case the error has been constructed from a failure // error. This happens because we implement From by // calling compat() and then storing it here. In failure this is // represented by a failure::Error being wrapped in a failure::Compat. // // So we first downcast into that compat, to then further downcast through // the failure's Error downcasting system into the original failure. // // This currently requires a transmute. This could be avoided if failure // provides a deref: https://github.com/rust-lang-nursery/failure/pull/213 let compat: Option<&failure::Compat> = Fail::downcast_ref(self.cause.as_fail()); if let Some(compat) = compat { pub struct CompatWrappedError { error: failure::Error, } let compat: &CompatWrappedError = unsafe { &*(compat as *const _ as *const CompatWrappedError) }; compat.error.downcast_ref() } else { None } } } /// Helper trait to downcast a response error into a fail. /// /// This is currently not exposed because it's unclear if this is the best way /// to achieve the downcasting on `Error` for which this is needed. #[doc(hidden)] pub trait InternalResponseErrorAsFail { #[doc(hidden)] fn as_fail(&self) -> &Fail; #[doc(hidden)] fn as_mut_fail(&mut self) -> &mut Fail; } #[doc(hidden)] impl InternalResponseErrorAsFail for T { fn as_fail(&self) -> &Fail { self } fn as_mut_fail(&mut self) -> &mut Fail { self } } /// Error that can be converted to `HttpResponse` pub trait ResponseError: Fail + InternalResponseErrorAsFail { /// Create response for error /// /// Internal server error is generated by default. fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.cause, f) } } impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(bt) = self.cause.backtrace() { write!(f, "{:?}\n\n{:?}", &self.cause, bt) } else { write!( f, "{:?}\n\n{:?}", &self.cause, self.backtrace.as_ref().unwrap() ) } } } /// Convert `Error` to a `HttpResponse` instance impl From for HttpResponse { fn from(err: Error) -> Self { HttpResponse::from_error(err) } } /// `Error` for any error that implements `ResponseError` impl From for Error { fn from(err: T) -> Error { let backtrace = if err.backtrace().is_none() { Some(Backtrace::new()) } else { None }; Error { cause: Box::new(err), backtrace, } } } /// Compatibility for `failure::Error` impl ResponseError for failure::Compat where T: fmt::Display + fmt::Debug + Sync + Send + 'static {} impl From for Error { fn from(err: failure::Error) -> Error { err.compat().into() } } /// `InternalServerError` for `JsonError` impl ResponseError for JsonError {} /// `InternalServerError` for `FormError` impl ResponseError for FormError {} /// `InternalServerError` for `TimerError` impl ResponseError for TimerError {} /// `InternalServerError` for `UrlParseError` impl ResponseError for UrlParseError {} /// Return `BAD_REQUEST` for `de::value::Error` impl ResponseError for DeError { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } /// Return `BAD_REQUEST` for `Utf8Error` impl ResponseError for Utf8Error { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } /// Return `InternalServerError` for `HttpError`, /// Response generation can return `HttpError`, so it is internal error impl ResponseError for HttpError {} /// Return `InternalServerError` for `io::Error` impl ResponseError for io::Error { fn error_response(&self) -> HttpResponse { match self.kind() { io::ErrorKind::NotFound => HttpResponse::new(StatusCode::NOT_FOUND), io::ErrorKind::PermissionDenied => HttpResponse::new(StatusCode::FORBIDDEN), _ => HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR), } } } /// `BadRequest` for `InvalidHeaderValue` impl ResponseError for header::InvalidHeaderValue { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } /// `BadRequest` for `InvalidHeaderValue` impl ResponseError for header::InvalidHeaderValueBytes { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } /// `InternalServerError` for `futures::Canceled` impl ResponseError for Canceled {} /// `InternalServerError` for `actix::MailboxError` impl ResponseError for MailboxError {} /// A set of errors that can occur during parsing HTTP streams #[derive(Fail, Debug)] pub enum ParseError { /// An invalid `Method`, such as `GE.T`. #[fail(display = "Invalid Method specified")] Method, /// An invalid `Uri`, such as `exam ple.domain`. #[fail(display = "Uri error: {}", _0)] Uri(InvalidUri), /// An invalid `HttpVersion`, such as `HTP/1.1` #[fail(display = "Invalid HTTP version specified")] Version, /// An invalid `Header`. #[fail(display = "Invalid Header provided")] Header, /// A message head is too large to be reasonable. #[fail(display = "Message head is too large")] TooLarge, /// A message reached EOF, but is not complete. #[fail(display = "Message is incomplete")] Incomplete, /// An invalid `Status`, such as `1337 ELITE`. #[fail(display = "Invalid Status provided")] Status, /// A timeout occurred waiting for an IO event. #[allow(dead_code)] #[fail(display = "Timeout")] Timeout, /// An `io::Error` that occurred while trying to read or write to a network /// stream. #[fail(display = "IO error: {}", _0)] Io(#[cause] IoError), /// Parsing a field as string failed #[fail(display = "UTF8 error: {}", _0)] Utf8(#[cause] Utf8Error), } /// Return `BadRequest` for `ParseError` impl ResponseError for ParseError { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } impl From for ParseError { fn from(err: IoError) -> ParseError { ParseError::Io(err) } } impl From for ParseError { fn from(err: InvalidUri) -> ParseError { ParseError::Uri(err) } } impl From for ParseError { fn from(err: Utf8Error) -> ParseError { ParseError::Utf8(err) } } impl From for ParseError { fn from(err: FromUtf8Error) -> ParseError { ParseError::Utf8(err.utf8_error()) } } impl From for ParseError { fn from(err: httparse::Error) -> ParseError { match err { httparse::Error::HeaderName | httparse::Error::HeaderValue | httparse::Error::NewLine | httparse::Error::Token => ParseError::Header, httparse::Error::Status => ParseError::Status, httparse::Error::TooManyHeaders => ParseError::TooLarge, httparse::Error::Version => ParseError::Version, } } } #[derive(Fail, Debug)] /// A set of errors that can occur during payload parsing pub enum PayloadError { /// A payload reached EOF, but is not complete. #[fail(display = "A payload reached EOF, but is not complete.")] Incomplete, /// Content encoding stream corruption #[fail(display = "Can not decode content-encoding.")] EncodingCorrupted, /// A payload reached size limit. #[fail(display = "A payload reached size limit.")] Overflow, /// A payload length is unknown. #[fail(display = "A payload length is unknown.")] UnknownLength, /// Io error #[fail(display = "{}", _0)] Io(#[cause] IoError), /// Http2 error #[fail(display = "{}", _0)] Http2(#[cause] Http2Error), } impl From for PayloadError { fn from(err: IoError) -> PayloadError { PayloadError::Io(err) } } /// `PayloadError` returns two possible results: /// /// - `Overflow` returns `PayloadTooLarge` /// - Other errors returns `BadRequest` impl ResponseError for PayloadError { fn error_response(&self) -> HttpResponse { match *self { PayloadError::Overflow => HttpResponse::new(StatusCode::PAYLOAD_TOO_LARGE), _ => HttpResponse::new(StatusCode::BAD_REQUEST), } } } /// Return `BadRequest` for `cookie::ParseError` impl ResponseError for cookie::ParseError { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } /// A set of errors that can occur during parsing multipart streams #[derive(Fail, Debug)] pub enum MultipartError { /// Content-Type header is not found #[fail(display = "No Content-type header found")] NoContentType, /// Can not parse Content-Type header #[fail(display = "Can not parse Content-Type header")] ParseContentType, /// Multipart boundary is not found #[fail(display = "Multipart boundary is not found")] Boundary, /// Multipart stream is incomplete #[fail(display = "Multipart stream is incomplete")] Incomplete, /// Error during field parsing #[fail(display = "{}", _0)] Parse(#[cause] ParseError), /// Payload error #[fail(display = "{}", _0)] Payload(#[cause] PayloadError), } impl From for MultipartError { fn from(err: ParseError) -> MultipartError { MultipartError::Parse(err) } } impl From for MultipartError { fn from(err: PayloadError) -> MultipartError { MultipartError::Payload(err) } } /// Return `BadRequest` for `MultipartError` impl ResponseError for MultipartError { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } /// Error during handling `Expect` header #[derive(Fail, PartialEq, Debug)] pub enum ExpectError { /// Expect header value can not be converted to utf8 #[fail(display = "Expect header value can not be converted to utf8")] Encoding, /// Unknown expect value #[fail(display = "Unknown expect value")] UnknownExpect, } impl ResponseError for ExpectError { fn error_response(&self) -> HttpResponse { HttpResponse::with_body(StatusCode::EXPECTATION_FAILED, "Unknown Expect") } } /// A set of error that can occure during parsing content type #[derive(Fail, PartialEq, Debug)] pub enum ContentTypeError { /// Can not parse content type #[fail(display = "Can not parse content type")] ParseError, /// Unknown content encoding #[fail(display = "Unknown content encoding")] UnknownEncoding, } /// Return `BadRequest` for `ContentTypeError` impl ResponseError for ContentTypeError { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } /// A set of errors that can occur during parsing urlencoded payloads #[derive(Fail, Debug)] pub enum UrlencodedError { /// Can not decode chunked transfer encoding #[fail(display = "Can not decode chunked transfer encoding")] Chunked, /// Payload size is bigger than allowed. (default: 256kB) #[fail( display = "Urlencoded payload size is bigger than allowed. (default: 256kB)" )] Overflow, /// Payload size is now known #[fail(display = "Payload size is now known")] UnknownLength, /// Content type error #[fail(display = "Content type error")] ContentType, /// Parse error #[fail(display = "Parse error")] Parse, /// Payload error #[fail(display = "Error that occur during reading payload: {}", _0)] Payload(#[cause] PayloadError), } /// Return `BadRequest` for `UrlencodedError` impl ResponseError for UrlencodedError { fn error_response(&self) -> HttpResponse { match *self { UrlencodedError::Overflow => { HttpResponse::new(StatusCode::PAYLOAD_TOO_LARGE) } UrlencodedError::UnknownLength => { HttpResponse::new(StatusCode::LENGTH_REQUIRED) } _ => HttpResponse::new(StatusCode::BAD_REQUEST), } } } impl From for UrlencodedError { fn from(err: PayloadError) -> UrlencodedError { UrlencodedError::Payload(err) } } /// A set of errors that can occur during parsing json payloads #[derive(Fail, Debug)] pub enum JsonPayloadError { /// Payload size is bigger than allowed. (default: 256kB) #[fail(display = "Json payload size is bigger than allowed. (default: 256kB)")] Overflow, /// Content type error #[fail(display = "Content type error")] ContentType, /// Deserialize error #[fail(display = "Json deserialize error: {}", _0)] Deserialize(#[cause] JsonError), /// Payload error #[fail(display = "Error that occur during reading payload: {}", _0)] Payload(#[cause] PayloadError), } /// Return `BadRequest` for `UrlencodedError` impl ResponseError for JsonPayloadError { fn error_response(&self) -> HttpResponse { match *self { JsonPayloadError::Overflow => { HttpResponse::new(StatusCode::PAYLOAD_TOO_LARGE) } _ => HttpResponse::new(StatusCode::BAD_REQUEST), } } } impl From for JsonPayloadError { fn from(err: PayloadError) -> JsonPayloadError { JsonPayloadError::Payload(err) } } impl From for JsonPayloadError { fn from(err: JsonError) -> JsonPayloadError { JsonPayloadError::Deserialize(err) } } /// Error type returned when reading body as lines. pub enum ReadlinesError { /// Error when decoding a line. EncodingError, /// Payload error. PayloadError(PayloadError), /// Line limit exceeded. LimitOverflow, /// ContentType error. ContentTypeError(ContentTypeError), } impl From for ReadlinesError { fn from(err: PayloadError) -> Self { ReadlinesError::PayloadError(err) } } impl From for ReadlinesError { fn from(err: ContentTypeError) -> Self { ReadlinesError::ContentTypeError(err) } } /// Errors which can occur when attempting to interpret a segment string as a /// valid path segment. #[derive(Fail, Debug, PartialEq)] pub enum UriSegmentError { /// The segment started with the wrapped invalid character. #[fail(display = "The segment started with the wrapped invalid character")] BadStart(char), /// The segment contained the wrapped invalid character. #[fail(display = "The segment contained the wrapped invalid character")] BadChar(char), /// The segment ended with the wrapped invalid character. #[fail(display = "The segment ended with the wrapped invalid character")] BadEnd(char), } /// Return `BadRequest` for `UriSegmentError` impl ResponseError for UriSegmentError { fn error_response(&self) -> HttpResponse { HttpResponse::new(StatusCode::BAD_REQUEST) } } /// Errors which can occur when attempting to generate resource uri. #[derive(Fail, Debug, PartialEq)] pub enum UrlGenerationError { /// Resource not found #[fail(display = "Resource not found")] ResourceNotFound, /// Not all path pattern covered #[fail(display = "Not all path pattern covered")] NotEnoughElements, /// Router is not available #[fail(display = "Router is not available")] RouterNotAvailable, /// URL parse error #[fail(display = "{}", _0)] ParseError(#[cause] UrlParseError), } /// `InternalServerError` for `UrlGeneratorError` impl ResponseError for UrlGenerationError {} impl From for UrlGenerationError { fn from(err: UrlParseError) -> Self { UrlGenerationError::ParseError(err) } } /// Helper type that can wrap any error and generate custom response. /// /// In following example any `io::Error` will be converted into "BAD REQUEST" /// response as opposite to *INTERNAL SERVER ERROR* which is defined by /// default. /// /// ```rust /// # extern crate actix_web; /// # use actix_web::*; /// use actix_web::fs::NamedFile; /// /// fn index(req: HttpRequest) -> Result { /// let f = NamedFile::open("test.txt").map_err(error::ErrorBadRequest)?; /// Ok(f) /// } /// # fn main() {} /// ``` pub struct InternalError { cause: T, status: InternalErrorType, backtrace: Backtrace, } enum InternalErrorType { Status(StatusCode), Response(Mutex>), } impl InternalError { /// Create `InternalError` instance pub fn new(cause: T, status: StatusCode) -> Self { InternalError { cause, status: InternalErrorType::Status(status), backtrace: Backtrace::new(), } } /// Create `InternalError` with predefined `HttpResponse`. pub fn from_response(cause: T, response: HttpResponse) -> Self { let resp = response.into_parts(); InternalError { cause, status: InternalErrorType::Response(Mutex::new(Some(resp))), backtrace: Backtrace::new(), } } } impl Fail for InternalError where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { fn backtrace(&self) -> Option<&Backtrace> { Some(&self.backtrace) } } impl fmt::Debug for InternalError where T: Send + Sync + fmt::Debug + 'static, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.cause, f) } } impl fmt::Display for InternalError where T: Send + Sync + fmt::Display + 'static, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.cause, f) } } impl ResponseError for InternalError where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { fn error_response(&self) -> HttpResponse { match self.status { InternalErrorType::Status(st) => HttpResponse::new(st), InternalErrorType::Response(ref resp) => { if let Some(resp) = resp.lock().unwrap().take() { HttpResponse::from_parts(resp) } else { HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR) } } } } } impl Responder for InternalError where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { type Item = HttpResponse; type Error = Error; fn respond_to(self, _: &HttpRequest) -> Result { Err(self.into()) } } /// Helper function that creates wrapper of any error and generate *BAD /// REQUEST* response. #[allow(non_snake_case)] pub fn ErrorBadRequest(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::BAD_REQUEST).into() } /// Helper function that creates wrapper of any error and generate /// *UNAUTHORIZED* response. #[allow(non_snake_case)] pub fn ErrorUnauthorized(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::UNAUTHORIZED).into() } /// Helper function that creates wrapper of any error and generate *FORBIDDEN* /// response. #[allow(non_snake_case)] pub fn ErrorForbidden(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::FORBIDDEN).into() } /// Helper function that creates wrapper of any error and generate *NOT FOUND* /// response. #[allow(non_snake_case)] pub fn ErrorNotFound(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::NOT_FOUND).into() } /// Helper function that creates wrapper of any error and generate *METHOD NOT /// ALLOWED* response. #[allow(non_snake_case)] pub fn ErrorMethodNotAllowed(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::METHOD_NOT_ALLOWED).into() } /// Helper function that creates wrapper of any error and generate *REQUEST /// TIMEOUT* response. #[allow(non_snake_case)] pub fn ErrorRequestTimeout(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::REQUEST_TIMEOUT).into() } /// Helper function that creates wrapper of any error and generate *CONFLICT* /// response. #[allow(non_snake_case)] pub fn ErrorConflict(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::CONFLICT).into() } /// Helper function that creates wrapper of any error and generate *GONE* /// response. #[allow(non_snake_case)] pub fn ErrorGone(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::GONE).into() } /// Helper function that creates wrapper of any error and generate /// *PRECONDITION FAILED* response. #[allow(non_snake_case)] pub fn ErrorPreconditionFailed(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::PRECONDITION_FAILED).into() } /// Helper function that creates wrapper of any error and generate /// *EXPECTATION FAILED* response. #[allow(non_snake_case)] pub fn ErrorExpectationFailed(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::EXPECTATION_FAILED).into() } /// Helper function that creates wrapper of any error and /// generate *INTERNAL SERVER ERROR* response. #[allow(non_snake_case)] pub fn ErrorInternalServerError(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::INTERNAL_SERVER_ERROR).into() } /// Helper function that creates wrapper of any error and /// generate *NOT IMPLEMENTED* response. #[allow(non_snake_case)] pub fn ErrorNotImplemented(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::NOT_IMPLEMENTED).into() } /// Helper function that creates wrapper of any error and /// generate *BAD GATEWAY* response. #[allow(non_snake_case)] pub fn ErrorBadGateway(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::BAD_GATEWAY).into() } /// Helper function that creates wrapper of any error and /// generate *SERVICE UNAVAILABLE* response. #[allow(non_snake_case)] pub fn ErrorServiceUnavailable(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::SERVICE_UNAVAILABLE).into() } /// Helper function that creates wrapper of any error and /// generate *GATEWAY TIMEOUT* response. #[allow(non_snake_case)] pub fn ErrorGatewayTimeout(err: T) -> Error where T: Send + Sync + fmt::Debug + fmt::Display + 'static, { InternalError::new(err, StatusCode::GATEWAY_TIMEOUT).into() } #[cfg(test)] mod tests { use super::*; use cookie::ParseError as CookieParseError; use failure; use http::{Error as HttpError, StatusCode}; use httparse; use std::env; use std::error::Error as StdError; use std::io; #[test] #[cfg(actix_nightly)] fn test_nightly() { let resp: HttpResponse = IoError::new(io::ErrorKind::Other, "test").error_response(); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); } #[test] fn test_into_response() { let resp: HttpResponse = ParseError::Incomplete.error_response(); assert_eq!(resp.status(), StatusCode::BAD_REQUEST); let resp: HttpResponse = CookieParseError::EmptyName.error_response(); assert_eq!(resp.status(), StatusCode::BAD_REQUEST); let resp: HttpResponse = MultipartError::Boundary.error_response(); assert_eq!(resp.status(), StatusCode::BAD_REQUEST); let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into(); let resp: HttpResponse = err.error_response(); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); } #[test] fn test_as_fail() { let orig = io::Error::new(io::ErrorKind::Other, "other"); let desc = orig.description().to_owned(); let e = ParseError::Io(orig); assert_eq!(format!("{}", e.cause().unwrap()), desc); } #[test] fn test_backtrace() { let e = ErrorBadRequest("err"); let _ = e.backtrace(); } #[test] fn test_error_cause() { let orig = io::Error::new(io::ErrorKind::Other, "other"); let desc = orig.description().to_owned(); let e = Error::from(orig); assert_eq!(format!("{}", e.as_fail()), desc); } #[test] fn test_error_display() { let orig = io::Error::new(io::ErrorKind::Other, "other"); let desc = orig.description().to_owned(); let e = Error::from(orig); assert_eq!(format!("{}", e), desc); } #[test] fn test_error_http_response() { let orig = io::Error::new(io::ErrorKind::Other, "other"); let e = Error::from(orig); let resp: HttpResponse = e.into(); assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR); } #[test] fn test_expect_error() { let resp: HttpResponse = ExpectError::Encoding.error_response(); assert_eq!(resp.status(), StatusCode::EXPECTATION_FAILED); let resp: HttpResponse = ExpectError::UnknownExpect.error_response(); assert_eq!(resp.status(), StatusCode::EXPECTATION_FAILED); } macro_rules! from { ($from:expr => $error:pat) => { match ParseError::from($from) { e @ $error => { assert!(format!("{}", e).len() >= 5); } e => unreachable!("{:?}", e), } }; } macro_rules! from_and_cause { ($from:expr => $error:pat) => { match ParseError::from($from) { e @ $error => { let desc = format!("{}", e.cause().unwrap()); assert_eq!(desc, $from.description().to_owned()); } _ => unreachable!("{:?}", $from), } }; } #[test] fn test_from() { from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => ParseError::Io(..)); from!(httparse::Error::HeaderName => ParseError::Header); from!(httparse::Error::HeaderName => ParseError::Header); from!(httparse::Error::HeaderValue => ParseError::Header); from!(httparse::Error::NewLine => ParseError::Header); from!(httparse::Error::Status => ParseError::Status); from!(httparse::Error::Token => ParseError::Header); from!(httparse::Error::TooManyHeaders => ParseError::TooLarge); from!(httparse::Error::Version => ParseError::Version); } #[test] fn failure_error() { const NAME: &str = "RUST_BACKTRACE"; let old_tb = env::var(NAME); env::set_var(NAME, "0"); let error = failure::err_msg("Hello!"); let resp: Error = error.into(); assert_eq!( format!("{:?}", resp), "Compat { error: ErrorMessage { msg: \"Hello!\" } }\n\n" ); match old_tb { Ok(x) => env::set_var(NAME, x), _ => env::remove_var(NAME), } } #[test] fn test_internal_error() { let err = InternalError::from_response( ExpectError::Encoding, HttpResponse::Ok().into(), ); let resp: HttpResponse = err.error_response(); assert_eq!(resp.status(), StatusCode::OK); } #[test] fn test_error_downcasting_direct() { #[derive(Debug, Fail)] #[fail(display = "demo error")] struct DemoError; impl ResponseError for DemoError {} let err: Error = DemoError.into(); let err_ref: &DemoError = err.downcast_ref().unwrap(); assert_eq!(err_ref.to_string(), "demo error"); } #[test] fn test_error_downcasting_compat() { #[derive(Debug, Fail)] #[fail(display = "demo error")] struct DemoError; impl ResponseError for DemoError {} let err: Error = failure::Error::from(DemoError).into(); let err_ref: &DemoError = err.downcast_ref().unwrap(); assert_eq!(err_ref.to_string(), "demo error"); } #[test] fn test_error_helpers() { let r: HttpResponse = ErrorBadRequest("err").into(); assert_eq!(r.status(), StatusCode::BAD_REQUEST); let r: HttpResponse = ErrorUnauthorized("err").into(); assert_eq!(r.status(), StatusCode::UNAUTHORIZED); let r: HttpResponse = ErrorForbidden("err").into(); assert_eq!(r.status(), StatusCode::FORBIDDEN); let r: HttpResponse = ErrorNotFound("err").into(); assert_eq!(r.status(), StatusCode::NOT_FOUND); let r: HttpResponse = ErrorMethodNotAllowed("err").into(); assert_eq!(r.status(), StatusCode::METHOD_NOT_ALLOWED); let r: HttpResponse = ErrorRequestTimeout("err").into(); assert_eq!(r.status(), StatusCode::REQUEST_TIMEOUT); let r: HttpResponse = ErrorConflict("err").into(); assert_eq!(r.status(), StatusCode::CONFLICT); let r: HttpResponse = ErrorGone("err").into(); assert_eq!(r.status(), StatusCode::GONE); let r: HttpResponse = ErrorPreconditionFailed("err").into(); assert_eq!(r.status(), StatusCode::PRECONDITION_FAILED); let r: HttpResponse = ErrorExpectationFailed("err").into(); assert_eq!(r.status(), StatusCode::EXPECTATION_FAILED); let r: HttpResponse = ErrorInternalServerError("err").into(); assert_eq!(r.status(), StatusCode::INTERNAL_SERVER_ERROR); let r: HttpResponse = ErrorNotImplemented("err").into(); assert_eq!(r.status(), StatusCode::NOT_IMPLEMENTED); let r: HttpResponse = ErrorBadGateway("err").into(); assert_eq!(r.status(), StatusCode::BAD_GATEWAY); let r: HttpResponse = ErrorServiceUnavailable("err").into(); assert_eq!(r.status(), StatusCode::SERVICE_UNAVAILABLE); let r: HttpResponse = ErrorGatewayTimeout("err").into(); assert_eq!(r.status(), StatusCode::GATEWAY_TIMEOUT); } }