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 <otavio@ossystems.com.br>
This commit is contained in:
Otavio Salvador 2020-03-11 23:00:55 -03:00
parent 9f196fe5a5
commit d2259b57dc
4 changed files with 112 additions and 171 deletions

View File

@ -56,7 +56,6 @@ base64 = "0.11"
bitflags = "1.2" bitflags = "1.2"
bytes = "0.5.3" bytes = "0.5.3"
copyless = "0.1.4" copyless = "0.1.4"
derive_more = "0.99.2"
either = "1.5.3" either = "1.5.3"
encoding_rs = "0.8" encoding_rs = "0.8"
futures-core = "0.3.1" futures-core = "0.3.1"
@ -80,6 +79,7 @@ serde_json = "1.0"
sha-1 = "0.8" sha-1 = "0.8"
slab = "0.4" slab = "0.4"
serde_urlencoded = "0.6.1" serde_urlencoded = "0.6.1"
thiserror = "1.0.11"
time = { version = "0.2.7", default-features = false, features = ["std"] } time = { version = "0.2.7", default-features = false, features = ["std"] }
# for secure cookie # for secure cookie

View File

@ -1,7 +1,7 @@
use std::io; use std::io;
use actix_connect::resolver::ResolveError; use actix_connect::resolver::ResolveError;
use derive_more::{Display, From}; use thiserror::Error;
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
use actix_connect::ssl::openssl::{HandshakeError, SslError}; use actix_connect::ssl::openssl::{HandshakeError, SslError};
@ -10,49 +10,49 @@ use crate::error::{Error, ParseError, ResponseError};
use crate::http::{Error as HttpError, StatusCode}; use crate::http::{Error as HttpError, StatusCode};
/// A set of errors that can occur while connecting to an HTTP host /// A set of errors that can occur while connecting to an HTTP host
#[derive(Debug, Display, From)] #[derive(Debug, Error)]
pub enum ConnectError { pub enum ConnectError {
/// SSL feature is not enabled /// SSL feature is not enabled
#[display(fmt = "SSL is not supported")] #[error("SSL is not supported")]
SslIsNotSupported, SslIsNotSupported,
/// SSL error /// SSL error
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
#[display(fmt = "{}", _0)] #[error(transparent)]
SslError(SslError), SslError(#[from] SslError),
/// SSL Handshake error /// SSL Handshake error
#[cfg(feature = "openssl")] #[cfg(feature = "openssl")]
#[display(fmt = "{}", _0)] #[error("SSL handshake error: {0}")]
SslHandshakeError(String), SslHandshakeError(String),
/// Failed to resolve the hostname /// Failed to resolve the hostname
#[display(fmt = "Failed resolving hostname: {}", _0)] #[error("Failed resolving hostname: {0}")]
Resolver(ResolveError), Resolver(#[from] ResolveError),
/// No dns records /// No dns records
#[display(fmt = "No dns records found for the input")] #[error("No dns records found for the input")]
NoRecords, NoRecords,
/// Http2 error /// Http2 error
#[display(fmt = "{}", _0)] #[error(transparent)]
H2(h2::Error), H2(#[from] h2::Error),
/// Connecting took too long /// Connecting took too long
#[display(fmt = "Timeout out while establishing connection")] #[error("Timeout out while establishing connection")]
Timeout, Timeout,
/// Connector has been disconnected /// Connector has been disconnected
#[display(fmt = "Internal error: connector has been disconnected")] #[error("Internal error: connector has been disconnected")]
Disconnected, Disconnected,
/// Unresolved host name /// Unresolved host name
#[display(fmt = "Connector received `Connect` method with unresolved host")] #[error("Connector received `Connect` method with unresolved host")]
Unresolverd, Unresolverd,
/// Connection io error /// Connection io error
#[display(fmt = "{}", _0)] #[error(transparent)]
Io(io::Error), Io(#[from] io::Error),
} }
impl From<actix_connect::ConnectError> for ConnectError { impl From<actix_connect::ConnectError> for ConnectError {
@ -74,45 +74,48 @@ impl<T: std::fmt::Debug> From<HandshakeError<T>> for ConnectError {
} }
} }
#[derive(Debug, Display, From)] #[derive(Debug, Error)]
pub enum InvalidUrl { pub enum InvalidUrl {
#[display(fmt = "Missing url scheme")] #[error("Missing url scheme")]
MissingScheme, MissingScheme,
#[display(fmt = "Unknown url scheme")] #[error("Unknown url scheme")]
UnknownScheme, UnknownScheme,
#[display(fmt = "Missing host name")] #[error("Missing host name")]
MissingHost, MissingHost,
#[display(fmt = "Url parse error: {}", _0)] #[error("Url parse error: {0}")]
HttpError(http::Error), HttpError(#[from] http::Error),
} }
/// A set of errors that can occur during request sending and response reading /// A set of errors that can occur during request sending and response reading
#[derive(Debug, Display, From)] #[derive(Debug, Error)]
pub enum SendRequestError { pub enum SendRequestError {
/// Invalid URL /// Invalid URL
#[display(fmt = "Invalid URL: {}", _0)] #[error("Invalid URL: {0}")]
Url(InvalidUrl), Url(#[from] InvalidUrl),
/// Failed to connect to host /// Failed to connect to host
#[display(fmt = "Failed to connect to host: {}", _0)] #[error("Failed to connect to host: {0}")]
Connect(ConnectError), Connect(#[from] ConnectError),
/// Error sending request /// Error sending request
Send(io::Error), #[error(transparent)]
Send(#[from] io::Error),
/// Error parsing response /// Error parsing response
Response(ParseError), #[error(transparent)]
Response(#[from] ParseError),
/// Http error /// Http error
#[display(fmt = "{}", _0)] #[error(transparent)]
Http(HttpError), Http(#[from] HttpError),
/// Http2 error /// Http2 error
#[display(fmt = "{}", _0)] #[error(transparent)]
H2(h2::Error), H2(#[from] h2::Error),
/// Response took too long /// Response took too long
#[display(fmt = "Timeout out while waiting for response")] #[error("Timeout out while waiting for response")]
Timeout, Timeout,
/// Tunnels are not supported for http2 connection /// 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, TunnelNotSupported,
/// Error sending request body /// Error sending request body
Body(Error), #[error(transparent)]
Body(#[from] Error),
} }
/// Convert `SendRequestError` to a server `Response` /// 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 /// A set of errors that can occur during freezing a request
#[derive(Debug, Display, From)] #[derive(Debug, Error)]
pub enum FreezeRequestError { pub enum FreezeRequestError {
/// Invalid URL /// Invalid URL
#[display(fmt = "Invalid URL: {}", _0)] #[error("Invalid URL: {0}")]
Url(InvalidUrl), Url(#[from] InvalidUrl),
/// Http error /// Http error
#[display(fmt = "{}", _0)] #[error(transparent)]
Http(HttpError), Http(#[from] HttpError),
}
impl From<FreezeRequestError> for SendRequestError {
fn from(e: FreezeRequestError) -> Self {
match e {
FreezeRequestError::Url(e) => e.into(),
FreezeRequestError::Http(e) => e.into(),
}
}
} }

View File

@ -2,7 +2,6 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::io::Write; use std::io::Write;
use std::str::Utf8Error; use std::str::Utf8Error;
use std::string::FromUtf8Error;
use std::{fmt, io, result}; use std::{fmt, io, result};
use actix_codec::{Decoder, Encoder}; use actix_codec::{Decoder, Encoder};
@ -10,13 +9,13 @@ pub use actix_threadpool::BlockingError;
use actix_utils::framed::DispatcherError as FramedDispatcherError; use actix_utils::framed::DispatcherError as FramedDispatcherError;
use actix_utils::timeout::TimeoutError; use actix_utils::timeout::TimeoutError;
use bytes::BytesMut; use bytes::BytesMut;
use derive_more::{Display, From};
pub use futures_channel::oneshot::Canceled; pub use futures_channel::oneshot::Canceled;
use http::uri::InvalidUri; use http::uri::InvalidUri;
use http::{header, Error as HttpError, StatusCode}; use http::{header, Error as HttpError, StatusCode};
use serde::de::value::Error as DeError; use serde::de::value::Error as DeError;
use serde_json::error::Error as JsonError; use serde_json::error::Error as JsonError;
use serde_urlencoded::ser::Error as FormError; use serde_urlencoded::ser::Error as FormError;
use thiserror::Error;
// re-export for convinience // re-export for convinience
use crate::body::Body; use crate::body::Body;
@ -42,6 +41,7 @@ pub type Result<T, E = Error> = result::Result<T, E>;
/// for it that can be used to create an http response from it this means that /// 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 /// if you have access to an actix `Error` you can always get a
/// `ResponseError` reference from it. /// `ResponseError` reference from it.
#[derive(Error)]
pub struct Error { pub struct Error {
cause: Box<dyn ResponseError>, cause: Box<dyn ResponseError>,
} }
@ -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 { impl From<()> for Error {
fn from(_: ()) -> Self { fn from(_: ()) -> Self {
Error::from(UnitError) Error::from(UnitError)
@ -162,8 +152,8 @@ impl<E: ResponseError> ResponseError for TimeoutError<E> {
} }
} }
#[derive(Debug, Display)] #[derive(Debug, Error)]
#[display(fmt = "UnknownError")] #[error("UnknownError")]
struct UnitError; struct UnitError;
/// `InternalServerError` for `UnitError` /// `InternalServerError` for `UnitError`
@ -226,40 +216,40 @@ impl ResponseError for header::InvalidHeaderValue {
} }
/// A set of errors that can occur during parsing HTTP streams /// A set of errors that can occur during parsing HTTP streams
#[derive(Debug, Display)] #[derive(Debug, Error)]
pub enum ParseError { pub enum ParseError {
/// An invalid `Method`, such as `GE.T`. /// An invalid `Method`, such as `GE.T`.
#[display(fmt = "Invalid Method specified")] #[error("Invalid Method specified")]
Method, Method,
/// An invalid `Uri`, such as `exam ple.domain`. /// An invalid `Uri`, such as `exam ple.domain`.
#[display(fmt = "Uri error: {}", _0)] #[error("Uri error: {0}")]
Uri(InvalidUri), Uri(#[from] InvalidUri),
/// An invalid `HttpVersion`, such as `HTP/1.1` /// An invalid `HttpVersion`, such as `HTP/1.1`
#[display(fmt = "Invalid HTTP version specified")] #[error("Invalid HTTP version specified")]
Version, Version,
/// An invalid `Header`. /// An invalid `Header`.
#[display(fmt = "Invalid Header provided")] #[error("Invalid Header provided")]
Header, Header,
/// A message head is too large to be reasonable. /// A message head is too large to be reasonable.
#[display(fmt = "Message head is too large")] #[error("Message head is too large")]
TooLarge, TooLarge,
/// A message reached EOF, but is not complete. /// A message reached EOF, but is not complete.
#[display(fmt = "Message is incomplete")] #[error("Message is incomplete")]
Incomplete, Incomplete,
/// An invalid `Status`, such as `1337 ELITE`. /// An invalid `Status`, such as `1337 ELITE`.
#[display(fmt = "Invalid Status provided")] #[error("Invalid Status provided")]
Status, Status,
/// A timeout occurred waiting for an IO event. /// A timeout occurred waiting for an IO event.
#[allow(dead_code)] #[allow(dead_code)]
#[display(fmt = "Timeout")] #[error("Timeout")]
Timeout, Timeout,
/// An `io::Error` that occurred while trying to read or write to a network /// An `io::Error` that occurred while trying to read or write to a network
/// stream. /// stream.
#[display(fmt = "IO error: {}", _0)] #[error("IO error: {0}")]
Io(io::Error), Io(#[from] io::Error),
/// Parsing a field as string failed /// Parsing a field as string failed
#[display(fmt = "UTF8 error: {}", _0)] #[error("UTF8 error: {0}")]
Utf8(Utf8Error), Utf8(#[from] Utf8Error),
} }
/// Return `BadRequest` for `ParseError` /// Return `BadRequest` for `ParseError`
@ -269,30 +259,6 @@ impl ResponseError for ParseError {
} }
} }
impl From<io::Error> for ParseError {
fn from(err: io::Error) -> ParseError {
ParseError::Io(err)
}
}
impl From<InvalidUri> for ParseError {
fn from(err: InvalidUri) -> ParseError {
ParseError::Uri(err)
}
}
impl From<Utf8Error> for ParseError {
fn from(err: Utf8Error) -> ParseError {
ParseError::Utf8(err)
}
}
impl From<FromUtf8Error> for ParseError {
fn from(err: FromUtf8Error) -> ParseError {
ParseError::Utf8(err.utf8_error())
}
}
impl From<httparse::Error> for ParseError { impl From<httparse::Error> for ParseError {
fn from(err: httparse::Error) -> ParseError { fn from(err: httparse::Error) -> ParseError {
match err { match err {
@ -307,48 +273,27 @@ impl From<httparse::Error> for ParseError {
} }
} }
#[derive(Display, Debug)] #[derive(Error, Debug)]
/// A set of errors that can occur during payload parsing /// A set of errors that can occur during payload parsing
pub enum PayloadError { pub enum PayloadError {
/// A payload reached EOF, but is not complete. /// A payload reached EOF, but is not complete.
#[display( #[error("A payload reached EOF, but is not complete. With error: {0:?}")]
fmt = "A payload reached EOF, but is not complete. With error: {:?}", Incomplete(#[from] Option<io::Error>),
_0
)]
Incomplete(Option<io::Error>),
/// Content encoding stream corruption /// Content encoding stream corruption
#[display(fmt = "Can not decode content-encoding.")] #[error("Can not decode content-encoding.")]
EncodingCorrupted, EncodingCorrupted,
/// A payload reached size limit. /// A payload reached size limit.
#[display(fmt = "A payload reached size limit.")] #[error("A payload reached size limit.")]
Overflow, Overflow,
/// A payload length is unknown. /// A payload length is unknown.
#[display(fmt = "A payload length is unknown.")] #[error("A payload length is unknown.")]
UnknownLength, UnknownLength,
/// Http2 payload error /// Http2 payload error
#[display(fmt = "{}", _0)] #[error(transparent)]
Http2Payload(h2::Error), Http2Payload(#[from] h2::Error),
/// Io error /// Io error
#[display(fmt = "{}", _0)] #[error(transparent)]
Io(io::Error), Io(#[from] io::Error),
}
impl From<h2::Error> for PayloadError {
fn from(err: h2::Error) -> Self {
PayloadError::Http2Payload(err)
}
}
impl From<Option<io::Error>> for PayloadError {
fn from(err: Option<io::Error>) -> Self {
PayloadError::Incomplete(err)
}
}
impl From<io::Error> for PayloadError {
fn from(err: io::Error) -> Self {
PayloadError::Incomplete(Some(err))
}
} }
impl From<BlockingError<io::Error>> for PayloadError { impl From<BlockingError<io::Error>> 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 /// A set of errors that can occur during dispatching http requests
pub enum DispatchError { pub enum DispatchError {
/// Service error /// Service error
Service(Error), #[error(transparent)]
Service(#[from] Error),
/// Upgrade service error /// Upgrade service error
#[error("Upgrade service error")]
Upgrade, Upgrade,
/// An `io::Error` that occurred while trying to read or write to a network /// An `io::Error` that occurred while trying to read or write to a network
/// stream. /// stream.
#[display(fmt = "IO error: {}", _0)] #[error("IO error: {0}")]
Io(io::Error), Io(#[from] io::Error),
/// Http request parse error. /// Http request parse error.
#[display(fmt = "Parse error: {}", _0)] #[error("Parse error: {0}")]
Parse(ParseError), Parse(#[from] ParseError),
/// Http/2 error /// Http/2 error
#[display(fmt = "{}", _0)] #[error(transparent)]
H2(h2::Error), H2(#[from] h2::Error),
/// The first request did not complete within the specified timeout. /// 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, SlowRequestTimeout,
/// Disconnect timeout. Makes sense for ssl streams. /// Disconnect timeout. Makes sense for ssl streams.
#[display(fmt = "Connection shutdown timeout")] #[error("Connection shutdown timeout")]
DisconnectTimeout, DisconnectTimeout,
/// Payload is not consumed /// 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, PayloadIsNotConsumed,
/// Malformed request /// Malformed request
#[display(fmt = "Malformed request")] #[error("Malformed request")]
MalformedRequest, MalformedRequest,
/// Internal error /// Internal error
#[display(fmt = "Internal error")] #[error("Internal error")]
InternalError, InternalError,
/// Unknown error /// Unknown error
#[display(fmt = "Unknown error")] #[error("Unknown error")]
Unknown, Unknown,
} }
/// A set of error that can occure during parsing content type /// A set of error that can occure during parsing content type
#[derive(PartialEq, Debug, Display)] #[derive(Error, PartialEq, Debug)]
pub enum ContentTypeError { pub enum ContentTypeError {
/// Can not parse content type /// Can not parse content type
#[display(fmt = "Can not parse content type")] #[error("Can not parse content type")]
ParseError, ParseError,
/// Unknown content encoding /// Unknown content encoding
#[display(fmt = "Unknown content encoding")] #[error("Unknown content encoding")]
UnknownEncoding, UnknownEncoding,
} }

View File

@ -5,8 +5,8 @@
//! communicate with the peer. //! communicate with the peer.
use std::io; use std::io;
use derive_more::{Display, From};
use http::{header, Method, StatusCode}; use http::{header, Method, StatusCode};
use thiserror::Error;
use crate::error::ResponseError; use crate::error::ResponseError;
use crate::message::RequestHead; use crate::message::RequestHead;
@ -24,62 +24,62 @@ pub use self::frame::Parser;
pub use self::proto::{hash_key, CloseCode, CloseReason, OpCode}; pub use self::proto::{hash_key, CloseCode, CloseReason, OpCode};
/// Websocket protocol errors /// Websocket protocol errors
#[derive(Debug, Display, From)] #[derive(Debug, Error)]
pub enum ProtocolError { pub enum ProtocolError {
/// Received an unmasked frame from client /// Received an unmasked frame from client
#[display(fmt = "Received an unmasked frame from client")] #[error("Received an unmasked frame from client")]
UnmaskedFrame, UnmaskedFrame,
/// Received a masked frame from server /// Received a masked frame from server
#[display(fmt = "Received a masked frame from server")] #[error("Received a masked frame from server")]
MaskedFrame, MaskedFrame,
/// Encountered invalid opcode /// Encountered invalid opcode
#[display(fmt = "Invalid opcode: {}", _0)] #[error("Invalid opcode: {0}")]
InvalidOpcode(u8), InvalidOpcode(u8),
/// Invalid control frame length /// Invalid control frame length
#[display(fmt = "Invalid control frame length: {}", _0)] #[error("Invalid control frame length: {0}")]
InvalidLength(usize), InvalidLength(usize),
/// Bad web socket op code /// Bad web socket op code
#[display(fmt = "Bad web socket op code")] #[error("Bad web socket op code")]
BadOpCode, BadOpCode,
/// A payload reached size limit. /// A payload reached size limit.
#[display(fmt = "A payload reached size limit.")] #[error("A payload reached size limit.")]
Overflow, Overflow,
/// Continuation is not started /// Continuation is not started
#[display(fmt = "Continuation is not started.")] #[error("Continuation is not started.")]
ContinuationNotStarted, ContinuationNotStarted,
/// Received new continuation but it is already started /// 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, ContinuationStarted,
/// Unknown continuation fragment /// Unknown continuation fragment
#[display(fmt = "Unknown continuation fragment.")] #[error("Unknown continuation fragment.")]
ContinuationFragment(OpCode), ContinuationFragment(OpCode),
/// Io error /// Io error
#[display(fmt = "io error: {}", _0)] #[error("io error: {0}")]
Io(io::Error), Io(#[from] io::Error),
} }
impl ResponseError for ProtocolError {} impl ResponseError for ProtocolError {}
/// Websocket handshake errors /// Websocket handshake errors
#[derive(PartialEq, Debug, Display)] #[derive(PartialEq, Debug, Error)]
pub enum HandshakeError { pub enum HandshakeError {
/// Only get method is allowed /// Only get method is allowed
#[display(fmt = "Method not allowed")] #[error("Method not allowed")]
GetMethodRequired, GetMethodRequired,
/// Upgrade header if not set to websocket /// Upgrade header if not set to websocket
#[display(fmt = "Websocket upgrade is expected")] #[error("Websocket upgrade is expected")]
NoWebsocketUpgrade, NoWebsocketUpgrade,
/// Connection header is not set to upgrade /// Connection header is not set to upgrade
#[display(fmt = "Connection upgrade is expected")] #[error("Connection upgrade is expected")]
NoConnectionUpgrade, NoConnectionUpgrade,
/// Websocket version header is not set /// Websocket version header is not set
#[display(fmt = "Websocket version header is required")] #[error("Websocket version header is required")]
NoVersionHeader, NoVersionHeader,
/// Unsupported websocket version /// Unsupported websocket version
#[display(fmt = "Unsupported version")] #[error("Unsupported version")]
UnsupportedVersion, UnsupportedVersion,
/// Websocket key is not set or wrong /// Websocket key is not set or wrong
#[display(fmt = "Unknown websocket key")] #[error("Unknown websocket key")]
BadWebsocketKey, BadWebsocketKey,
} }