From d2259b57dceccf5fdd6b4fd1b4f307776116906a Mon Sep 17 00:00:00 2001 From: Otavio Salvador Date: Wed, 11 Mar 2020 23:00:55 -0300 Subject: [PATCH] http: Convert from `derive_more` to `thiserror` The `thiserror` has the advantage of implementing `std::error::Error` and it integrates better with the Rust ecosystem. Signed-off-by: Otavio Salvador --- actix-http/Cargo.toml | 2 +- actix-http/src/client/error.rs | 94 ++++++++++----------- actix-http/src/error.rs | 147 +++++++++++---------------------- actix-http/src/ws/mod.rs | 40 ++++----- 4 files changed, 112 insertions(+), 171 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index e78c74624..1b95babc2 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -56,7 +56,6 @@ base64 = "0.11" bitflags = "1.2" bytes = "0.5.3" copyless = "0.1.4" -derive_more = "0.99.2" either = "1.5.3" encoding_rs = "0.8" futures-core = "0.3.1" @@ -80,6 +79,7 @@ serde_json = "1.0" sha-1 = "0.8" slab = "0.4" serde_urlencoded = "0.6.1" +thiserror = "1.0.11" time = { version = "0.2.7", default-features = false, features = ["std"] } # for secure cookie diff --git a/actix-http/src/client/error.rs b/actix-http/src/client/error.rs index 42ea47ee8..4a17a7e88 100644 --- a/actix-http/src/client/error.rs +++ b/actix-http/src/client/error.rs @@ -1,7 +1,7 @@ use std::io; use actix_connect::resolver::ResolveError; -use derive_more::{Display, From}; +use thiserror::Error; #[cfg(feature = "openssl")] use actix_connect::ssl::openssl::{HandshakeError, SslError}; @@ -10,49 +10,49 @@ use crate::error::{Error, ParseError, ResponseError}; use crate::http::{Error as HttpError, StatusCode}; /// A set of errors that can occur while connecting to an HTTP host -#[derive(Debug, Display, From)] +#[derive(Debug, Error)] pub enum ConnectError { /// SSL feature is not enabled - #[display(fmt = "SSL is not supported")] + #[error("SSL is not supported")] SslIsNotSupported, /// SSL error #[cfg(feature = "openssl")] - #[display(fmt = "{}", _0)] - SslError(SslError), + #[error(transparent)] + SslError(#[from] SslError), /// SSL Handshake error #[cfg(feature = "openssl")] - #[display(fmt = "{}", _0)] + #[error("SSL handshake error: {0}")] SslHandshakeError(String), /// Failed to resolve the hostname - #[display(fmt = "Failed resolving hostname: {}", _0)] - Resolver(ResolveError), + #[error("Failed resolving hostname: {0}")] + Resolver(#[from] ResolveError), /// No dns records - #[display(fmt = "No dns records found for the input")] + #[error("No dns records found for the input")] NoRecords, /// Http2 error - #[display(fmt = "{}", _0)] - H2(h2::Error), + #[error(transparent)] + H2(#[from] h2::Error), /// Connecting took too long - #[display(fmt = "Timeout out while establishing connection")] + #[error("Timeout out while establishing connection")] Timeout, /// Connector has been disconnected - #[display(fmt = "Internal error: connector has been disconnected")] + #[error("Internal error: connector has been disconnected")] Disconnected, /// Unresolved host name - #[display(fmt = "Connector received `Connect` method with unresolved host")] + #[error("Connector received `Connect` method with unresolved host")] Unresolverd, /// Connection io error - #[display(fmt = "{}", _0)] - Io(io::Error), + #[error(transparent)] + Io(#[from] io::Error), } impl From for ConnectError { @@ -74,45 +74,48 @@ impl From> for ConnectError { } } -#[derive(Debug, Display, From)] +#[derive(Debug, Error)] pub enum InvalidUrl { - #[display(fmt = "Missing url scheme")] + #[error("Missing url scheme")] MissingScheme, - #[display(fmt = "Unknown url scheme")] + #[error("Unknown url scheme")] UnknownScheme, - #[display(fmt = "Missing host name")] + #[error("Missing host name")] MissingHost, - #[display(fmt = "Url parse error: {}", _0)] - HttpError(http::Error), + #[error("Url parse error: {0}")] + HttpError(#[from] http::Error), } /// A set of errors that can occur during request sending and response reading -#[derive(Debug, Display, From)] +#[derive(Debug, Error)] pub enum SendRequestError { /// Invalid URL - #[display(fmt = "Invalid URL: {}", _0)] - Url(InvalidUrl), + #[error("Invalid URL: {0}")] + Url(#[from] InvalidUrl), /// Failed to connect to host - #[display(fmt = "Failed to connect to host: {}", _0)] - Connect(ConnectError), + #[error("Failed to connect to host: {0}")] + Connect(#[from] ConnectError), /// Error sending request - Send(io::Error), + #[error(transparent)] + Send(#[from] io::Error), /// Error parsing response - Response(ParseError), + #[error(transparent)] + Response(#[from] ParseError), /// Http error - #[display(fmt = "{}", _0)] - Http(HttpError), + #[error(transparent)] + Http(#[from] HttpError), /// Http2 error - #[display(fmt = "{}", _0)] - H2(h2::Error), + #[error(transparent)] + H2(#[from] h2::Error), /// Response took too long - #[display(fmt = "Timeout out while waiting for response")] + #[error("Timeout out while waiting for response")] Timeout, /// Tunnels are not supported for http2 connection - #[display(fmt = "Tunnels are not supported for http2 connection")] + #[error("Tunnels are not supported for http2 connection")] TunnelNotSupported, /// Error sending request body - Body(Error), + #[error(transparent)] + Body(#[from] Error), } /// Convert `SendRequestError` to a server `Response` @@ -129,21 +132,12 @@ impl ResponseError for SendRequestError { } /// A set of errors that can occur during freezing a request -#[derive(Debug, Display, From)] +#[derive(Debug, Error)] pub enum FreezeRequestError { /// Invalid URL - #[display(fmt = "Invalid URL: {}", _0)] - Url(InvalidUrl), + #[error("Invalid URL: {0}")] + Url(#[from] InvalidUrl), /// Http error - #[display(fmt = "{}", _0)] - Http(HttpError), -} - -impl From for SendRequestError { - fn from(e: FreezeRequestError) -> Self { - match e { - FreezeRequestError::Url(e) => e.into(), - FreezeRequestError::Http(e) => e.into(), - } - } + #[error(transparent)] + Http(#[from] HttpError), } diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 0850e18ff..0c05c947d 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -2,7 +2,6 @@ use std::cell::RefCell; use std::io::Write; use std::str::Utf8Error; -use std::string::FromUtf8Error; use std::{fmt, io, result}; use actix_codec::{Decoder, Encoder}; @@ -10,13 +9,13 @@ pub use actix_threadpool::BlockingError; use actix_utils::framed::DispatcherError as FramedDispatcherError; use actix_utils::timeout::TimeoutError; use bytes::BytesMut; -use derive_more::{Display, From}; pub use futures_channel::oneshot::Canceled; use http::uri::InvalidUri; use http::{header, Error as HttpError, StatusCode}; use serde::de::value::Error as DeError; use serde_json::error::Error as JsonError; use serde_urlencoded::ser::Error as FormError; +use thiserror::Error; // re-export for convinience use crate::body::Body; @@ -42,6 +41,7 @@ pub type Result = result::Result; /// 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. +#[derive(Error)] pub struct Error { cause: Box, } @@ -98,16 +98,6 @@ impl fmt::Debug for Error { } } -impl std::error::Error for Error { - fn cause(&self) -> Option<&dyn std::error::Error> { - None - } - - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } -} - impl From<()> for Error { fn from(_: ()) -> Self { Error::from(UnitError) @@ -162,8 +152,8 @@ impl ResponseError for TimeoutError { } } -#[derive(Debug, Display)] -#[display(fmt = "UnknownError")] +#[derive(Debug, Error)] +#[error("UnknownError")] struct UnitError; /// `InternalServerError` for `UnitError` @@ -226,40 +216,40 @@ impl ResponseError for header::InvalidHeaderValue { } /// A set of errors that can occur during parsing HTTP streams -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum ParseError { /// An invalid `Method`, such as `GE.T`. - #[display(fmt = "Invalid Method specified")] + #[error("Invalid Method specified")] Method, /// An invalid `Uri`, such as `exam ple.domain`. - #[display(fmt = "Uri error: {}", _0)] - Uri(InvalidUri), + #[error("Uri error: {0}")] + Uri(#[from] InvalidUri), /// An invalid `HttpVersion`, such as `HTP/1.1` - #[display(fmt = "Invalid HTTP version specified")] + #[error("Invalid HTTP version specified")] Version, /// An invalid `Header`. - #[display(fmt = "Invalid Header provided")] + #[error("Invalid Header provided")] Header, /// A message head is too large to be reasonable. - #[display(fmt = "Message head is too large")] + #[error("Message head is too large")] TooLarge, /// A message reached EOF, but is not complete. - #[display(fmt = "Message is incomplete")] + #[error("Message is incomplete")] Incomplete, /// An invalid `Status`, such as `1337 ELITE`. - #[display(fmt = "Invalid Status provided")] + #[error("Invalid Status provided")] Status, /// A timeout occurred waiting for an IO event. #[allow(dead_code)] - #[display(fmt = "Timeout")] + #[error("Timeout")] Timeout, /// An `io::Error` that occurred while trying to read or write to a network /// stream. - #[display(fmt = "IO error: {}", _0)] - Io(io::Error), + #[error("IO error: {0}")] + Io(#[from] io::Error), /// Parsing a field as string failed - #[display(fmt = "UTF8 error: {}", _0)] - Utf8(Utf8Error), + #[error("UTF8 error: {0}")] + Utf8(#[from] Utf8Error), } /// Return `BadRequest` for `ParseError` @@ -269,30 +259,6 @@ impl ResponseError for ParseError { } } -impl From for ParseError { - fn from(err: io::Error) -> 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 { @@ -307,48 +273,27 @@ impl From for ParseError { } } -#[derive(Display, Debug)] +#[derive(Error, Debug)] /// A set of errors that can occur during payload parsing pub enum PayloadError { /// A payload reached EOF, but is not complete. - #[display( - fmt = "A payload reached EOF, but is not complete. With error: {:?}", - _0 - )] - Incomplete(Option), + #[error("A payload reached EOF, but is not complete. With error: {0:?}")] + Incomplete(#[from] Option), /// Content encoding stream corruption - #[display(fmt = "Can not decode content-encoding.")] + #[error("Can not decode content-encoding.")] EncodingCorrupted, /// A payload reached size limit. - #[display(fmt = "A payload reached size limit.")] + #[error("A payload reached size limit.")] Overflow, /// A payload length is unknown. - #[display(fmt = "A payload length is unknown.")] + #[error("A payload length is unknown.")] UnknownLength, /// Http2 payload error - #[display(fmt = "{}", _0)] - Http2Payload(h2::Error), + #[error(transparent)] + Http2Payload(#[from] h2::Error), /// Io error - #[display(fmt = "{}", _0)] - Io(io::Error), -} - -impl From for PayloadError { - fn from(err: h2::Error) -> Self { - PayloadError::Http2Payload(err) - } -} - -impl From> for PayloadError { - fn from(err: Option) -> Self { - PayloadError::Incomplete(err) - } -} - -impl From for PayloadError { - fn from(err: io::Error) -> Self { - PayloadError::Incomplete(Some(err)) - } + #[error(transparent)] + Io(#[from] io::Error), } impl From> for PayloadError { @@ -383,61 +328,63 @@ impl ResponseError for crate::cookie::ParseError { } } -#[derive(Debug, Display, From)] +#[derive(Debug, Error)] /// A set of errors that can occur during dispatching http requests pub enum DispatchError { /// Service error - Service(Error), + #[error(transparent)] + Service(#[from] Error), /// Upgrade service error + #[error("Upgrade service error")] Upgrade, /// An `io::Error` that occurred while trying to read or write to a network /// stream. - #[display(fmt = "IO error: {}", _0)] - Io(io::Error), + #[error("IO error: {0}")] + Io(#[from] io::Error), /// Http request parse error. - #[display(fmt = "Parse error: {}", _0)] - Parse(ParseError), + #[error("Parse error: {0}")] + Parse(#[from] ParseError), /// Http/2 error - #[display(fmt = "{}", _0)] - H2(h2::Error), + #[error(transparent)] + H2(#[from] h2::Error), /// The first request did not complete within the specified timeout. - #[display(fmt = "The first request did not complete within the specified timeout")] + #[error("The first request did not complete within the specified timeout")] SlowRequestTimeout, /// Disconnect timeout. Makes sense for ssl streams. - #[display(fmt = "Connection shutdown timeout")] + #[error("Connection shutdown timeout")] DisconnectTimeout, /// Payload is not consumed - #[display(fmt = "Task is completed but request's payload is not consumed")] + #[error("Task is completed but request's payload is not consumed")] PayloadIsNotConsumed, /// Malformed request - #[display(fmt = "Malformed request")] + #[error("Malformed request")] MalformedRequest, /// Internal error - #[display(fmt = "Internal error")] + #[error("Internal error")] InternalError, /// Unknown error - #[display(fmt = "Unknown error")] + #[error("Unknown error")] Unknown, } /// A set of error that can occure during parsing content type -#[derive(PartialEq, Debug, Display)] +#[derive(Error, PartialEq, Debug)] pub enum ContentTypeError { /// Can not parse content type - #[display(fmt = "Can not parse content type")] + #[error("Can not parse content type")] ParseError, /// Unknown content encoding - #[display(fmt = "Unknown content encoding")] + #[error("Unknown content encoding")] UnknownEncoding, } diff --git a/actix-http/src/ws/mod.rs b/actix-http/src/ws/mod.rs index ffa397979..4eacb4438 100644 --- a/actix-http/src/ws/mod.rs +++ b/actix-http/src/ws/mod.rs @@ -5,8 +5,8 @@ //! communicate with the peer. use std::io; -use derive_more::{Display, From}; use http::{header, Method, StatusCode}; +use thiserror::Error; use crate::error::ResponseError; use crate::message::RequestHead; @@ -24,62 +24,62 @@ pub use self::frame::Parser; pub use self::proto::{hash_key, CloseCode, CloseReason, OpCode}; /// Websocket protocol errors -#[derive(Debug, Display, From)] +#[derive(Debug, Error)] pub enum ProtocolError { /// Received an unmasked frame from client - #[display(fmt = "Received an unmasked frame from client")] + #[error("Received an unmasked frame from client")] UnmaskedFrame, /// Received a masked frame from server - #[display(fmt = "Received a masked frame from server")] + #[error("Received a masked frame from server")] MaskedFrame, /// Encountered invalid opcode - #[display(fmt = "Invalid opcode: {}", _0)] + #[error("Invalid opcode: {0}")] InvalidOpcode(u8), /// Invalid control frame length - #[display(fmt = "Invalid control frame length: {}", _0)] + #[error("Invalid control frame length: {0}")] InvalidLength(usize), /// Bad web socket op code - #[display(fmt = "Bad web socket op code")] + #[error("Bad web socket op code")] BadOpCode, /// A payload reached size limit. - #[display(fmt = "A payload reached size limit.")] + #[error("A payload reached size limit.")] Overflow, /// Continuation is not started - #[display(fmt = "Continuation is not started.")] + #[error("Continuation is not started.")] ContinuationNotStarted, /// Received new continuation but it is already started - #[display(fmt = "Received new continuation but it is already started")] + #[error("Received new continuation but it is already started")] ContinuationStarted, /// Unknown continuation fragment - #[display(fmt = "Unknown continuation fragment.")] + #[error("Unknown continuation fragment.")] ContinuationFragment(OpCode), /// Io error - #[display(fmt = "io error: {}", _0)] - Io(io::Error), + #[error("io error: {0}")] + Io(#[from] io::Error), } impl ResponseError for ProtocolError {} /// Websocket handshake errors -#[derive(PartialEq, Debug, Display)] +#[derive(PartialEq, Debug, Error)] pub enum HandshakeError { /// Only get method is allowed - #[display(fmt = "Method not allowed")] + #[error("Method not allowed")] GetMethodRequired, /// Upgrade header if not set to websocket - #[display(fmt = "Websocket upgrade is expected")] + #[error("Websocket upgrade is expected")] NoWebsocketUpgrade, /// Connection header is not set to upgrade - #[display(fmt = "Connection upgrade is expected")] + #[error("Connection upgrade is expected")] NoConnectionUpgrade, /// Websocket version header is not set - #[display(fmt = "Websocket version header is required")] + #[error("Websocket version header is required")] NoVersionHeader, /// Unsupported websocket version - #[display(fmt = "Unsupported version")] + #[error("Unsupported version")] UnsupportedVersion, /// Websocket key is not set or wrong - #[display(fmt = "Unknown websocket key")] + #[error("Unknown websocket key")] BadWebsocketKey, }