use std::borrow::Cow;
use std::convert::{From, Into};
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::result::Result as StdResult;
use std::str::Utf8Error;
use httparse;
use mio;
#[cfg(feature = "ssl")]
use openssl::ssl::{Error as SslError, HandshakeError as SslHandshakeError};
#[cfg(feature = "nativetls")]
use native_tls::{Error as SslError, HandshakeError as SslHandshakeError};
#[cfg(any(feature = "ssl", feature = "nativetls"))]
type HandshakeError = SslHandshakeError<mio::tcp::TcpStream>;
use communication::Command;
pub type Result<T> = StdResult<T, Error>;
#[derive(Debug)]
pub enum Kind {
Internal,
Capacity,
Protocol,
Encoding(Utf8Error),
Io(io::Error),
Http(httparse::Error),
Queue(mio::channel::SendError<Command>),
#[cfg(any(feature = "ssl", feature = "nativetls"))]
Ssl(SslError),
#[cfg(any(feature = "ssl", feature = "nativetls"))]
SslHandshake(HandshakeError),
Custom(Box<dyn StdError + Send + Sync>),
}
pub struct Error {
pub kind: Kind,
pub details: Cow<'static, str>,
}
impl Error {
pub fn new<I>(kind: Kind, details: I) -> Error
where
I: Into<Cow<'static, str>>,
{
Error {
kind,
details: details.into(),
}
}
pub fn into_box(self) -> Box<dyn StdError> {
match self.kind {
Kind::Custom(err) => err,
_ => Box::new(self),
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.details.len() > 0 {
write!(f, "WS Error <{:?}>: {}", self.kind, self.details)
} else {
write!(f, "WS Error <{:?}>", self.kind)
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.details.len() > 0 {
write!(f, "{}: {}", self.description(), self.details)
} else {
write!(f, "{}", self.description())
}
}
}
impl StdError for Error {
fn description(&self) -> &str {
match self.kind {
Kind::Internal => "Internal Application Error",
Kind::Capacity => "WebSocket at Capacity",
Kind::Protocol => "WebSocket Protocol Error",
Kind::Encoding(ref err) => err.description(),
Kind::Io(ref err) => err.description(),
Kind::Http(_) => "Unable to parse HTTP",
#[cfg(any(feature = "ssl", feature = "nativetls"))]
Kind::Ssl(ref err) => err.description(),
#[cfg(any(feature = "ssl", feature = "nativetls"))]
Kind::SslHandshake(ref err) => err.description(),
Kind::Queue(_) => "Unable to send signal on event loop",
Kind::Custom(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&dyn StdError> {
match self.kind {
Kind::Encoding(ref err) => Some(err),
Kind::Io(ref err) => Some(err),
#[cfg(any(feature = "ssl", feature = "nativetls"))]
Kind::Ssl(ref err) => Some(err),
#[cfg(any(feature = "ssl", feature = "nativetls"))]
Kind::SslHandshake(ref err) => err.cause(),
Kind::Custom(ref err) => Some(err.as_ref()),
_ => None,
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::new(Kind::Io(err), "")
}
}
impl From<httparse::Error> for Error {
fn from(err: httparse::Error) -> Error {
let details = match err {
httparse::Error::HeaderName => "Invalid byte in header name.",
httparse::Error::HeaderValue => "Invalid byte in header value.",
httparse::Error::NewLine => "Invalid byte in new line.",
httparse::Error::Status => "Invalid byte in Response status.",
httparse::Error::Token => "Invalid byte where token is required.",
httparse::Error::TooManyHeaders => {
"Parsed more headers than provided buffer can contain."
}
httparse::Error::Version => "Invalid byte in HTTP version.",
};
Error::new(Kind::Http(err), details)
}
}
impl From<mio::channel::SendError<Command>> for Error {
fn from(err: mio::channel::SendError<Command>) -> Error {
match err {
mio::channel::SendError::Io(err) => Error::from(err),
_ => Error::new(Kind::Queue(err), ""),
}
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Error {
Error::new(Kind::Encoding(err), "")
}
}
#[cfg(any(feature = "ssl", feature = "nativetls"))]
impl From<SslError> for Error {
fn from(err: SslError) -> Error {
Error::new(Kind::Ssl(err), "")
}
}
#[cfg(any(feature = "ssl", feature = "nativetls"))]
impl From<HandshakeError> for Error {
fn from(err: HandshakeError) -> Error {
Error::new(Kind::SslHandshake(err), "")
}
}
impl<B> From<Box<B>> for Error
where
B: StdError + Send + Sync + 'static,
{
fn from(err: Box<B>) -> Error {
Error::new(Kind::Custom(err), "")
}
}