From be0a8c325f2fbe0c07298b87e8c868e3832d7515 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 4 Apr 2021 18:09:12 -0300 Subject: [PATCH] Remove thiserror dependency --- Cargo.toml | 1 - src/error.rs | 258 +++++++++++++----------------------- src/error/capacity_error.rs | 42 ++++++ src/error/protocol_error.rs | 113 ++++++++++++++++ src/error/tls_error.rs | 66 +++++++++ src/error/url_error.rs | 41 ++++++ src/handshake/mod.rs | 5 +- 7 files changed, 358 insertions(+), 168 deletions(-) create mode 100644 src/error/capacity_error.rs create mode 100644 src/error/protocol_error.rs create mode 100644 src/error/tls_error.rs create mode 100644 src/error/url_error.rs diff --git a/Cargo.toml b/Cargo.toml index 705c5d2..dabf0c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ input_buffer = "0.4.0" log = "0.4.8" rand = "0.8.0" sha-1 = "0.9" -thiserror = "1.0.23" url = "2.1.0" utf-8 = "0.7.5" diff --git a/src/error.rs b/src/error.rs index 406e64f..a6019d7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,16 +1,22 @@ //! Error handling. -use std::{io, result, str, string}; +mod capacity_error; +mod protocol_error; +mod tls_error; +mod url_error; -use crate::protocol::{frame::coding::Data, Message}; +use crate::protocol::Message; +pub use capacity_error::CapacityError; use http::Response; -use thiserror::Error; +pub use protocol_error::ProtocolError; +use std::{fmt, io, result, str, string}; +pub use tls_error::TlsError; +pub use url_error::UrlError; /// Result type of all Tungstenite library calls. pub type Result = result::Result; /// Possible WebSocket errors. -#[derive(Error, Debug)] pub enum Error { /// WebSocket connection closed normally. This informs you of the close. /// It's not an error as such and nothing wrong happened. @@ -23,7 +29,6 @@ pub enum Error { /// /// Receiving this error means that the WebSocket object is not usable anymore and the /// only meaningful action with it is dropping it. - #[error("Connection closed normally")] ConnectionClosed, /// Trying to work with already closed connection. /// @@ -32,41 +37,87 @@ pub enum Error { /// As opposed to `ConnectionClosed`, this indicates your code tries to operate on the /// connection when it really shouldn't anymore, so this really indicates a programmer /// error on your part. - #[error("Trying to work with closed connection")] AlreadyClosed, /// Input-output error. Apart from WouldBlock, these are generally errors with the /// underlying connection and you should probably consider them fatal. - #[error("IO error: {0}")] - Io(#[from] io::Error), + Io(io::Error), /// TLS error. /// /// Note that this error variant is enabled unconditionally even if no TLS feature is enabled, /// to provide a feature-agnostic API surface. - #[error("TLS error: {0}")] - Tls(#[from] TlsError), + Tls(TlsError), /// - When reading: buffer capacity exhausted. /// - When writing: your message is bigger than the configured max message size /// (64MB by default). - #[error("Space limit exceeded: {0}")] - Capacity(#[from] CapacityError), + Capacity(CapacityError), /// Protocol violation. - #[error("WebSocket protocol error: {0}")] - Protocol(#[from] ProtocolError), + Protocol(ProtocolError), /// Message send queue full. - #[error("Send queue is full")] SendQueueFull(Message), /// UTF coding error. - #[error("UTF-8 encoding error")] Utf8, /// Invalid URL. - #[error("URL error: {0}")] - Url(#[from] UrlError), + Url(UrlError), /// HTTP error. - #[error("HTTP error: {}", .0.status())] Http(Response>), /// HTTP format error. - #[error("HTTP format error: {0}")] - HttpFormat(#[from] http::Error), + HttpFormat(http::Error), +} + +impl From for Error { + #[inline] + fn from(from: io::Error) -> Self { + Self::Io(from) + } +} + +impl From for Error { + #[inline] + fn from(from: TlsError) -> Self { + Self::Tls(from) + } +} + +impl From for Error { + #[inline] + fn from(from: CapacityError) -> Self { + Self::Capacity(from) + } +} + +impl From for Error { + #[inline] + fn from(from: ProtocolError) -> Self { + Self::Protocol(from) + } +} + +impl From for Error { + #[inline] + fn from(from: Message) -> Self { + Self::SendQueueFull(from) + } +} + +impl From for Error { + #[inline] + fn from(from: UrlError) -> Self { + Self::Url(from) + } +} + +impl From>> for Error { + #[inline] + fn from(from: Response>) -> Self { + Self::Http(from) + } +} + +impl From for Error { + #[inline] + fn from(from: http::Error) -> Self { + Self::HttpFormat(from) + } } impl From for Error { @@ -120,149 +171,30 @@ impl From for Error { } } -/// Indicates the specific type/cause of a capacity error. -#[derive(Error, Debug, PartialEq, Eq, Clone, Copy)] -pub enum CapacityError { - /// Too many headers provided (see [`httparse::Error::TooManyHeaders`]). - #[error("Too many headers")] - TooManyHeaders, - /// Received header is too long. - #[error("Header too long")] - HeaderTooLong, - /// Message is bigger than the maximum allowed size. - #[error("Message too long: {size} > {max_size}")] - MessageTooLong { - /// The size of the message. - size: usize, - /// The maximum allowed message size. - max_size: usize, - }, - /// TCP buffer is full. - #[error("Incoming TCP buffer is full")] - TcpBufferFull, -} - -/// Indicates the specific type/cause of a protocol error. -#[derive(Error, Debug, PartialEq, Eq, Clone, Copy)] -pub enum ProtocolError { - /// Use of the wrong HTTP method (the WebSocket protocol requires the GET method be used). - #[error("Unsupported HTTP method used - only GET is allowed")] - WrongHttpMethod, - /// Wrong HTTP version used (the WebSocket protocol requires version 1.1 or higher). - #[error("HTTP version must be 1.1 or higher")] - WrongHttpVersion, - /// Missing `Connection: upgrade` HTTP header. - #[error("No \"Connection: upgrade\" header")] - MissingConnectionUpgradeHeader, - /// Missing `Upgrade: websocket` HTTP header. - #[error("No \"Upgrade: websocket\" header")] - MissingUpgradeWebSocketHeader, - /// Missing `Sec-WebSocket-Version: 13` HTTP header. - #[error("No \"Sec-WebSocket-Version: 13\" header")] - MissingSecWebSocketVersionHeader, - /// Missing `Sec-WebSocket-Key` HTTP header. - #[error("No \"Sec-WebSocket-Key\" header")] - MissingSecWebSocketKey, - /// The `Sec-WebSocket-Accept` header is either not present or does not specify the correct key value. - #[error("Key mismatch in \"Sec-WebSocket-Accept\" header")] - SecWebSocketAcceptKeyMismatch, - /// Garbage data encountered after client request. - #[error("Junk after client request")] - JunkAfterRequest, - /// Custom responses must be unsuccessful. - #[error("Custom response must not be successful")] - CustomResponseSuccessful, - /// No more data while still performing handshake. - #[error("Handshake not finished")] - HandshakeIncomplete, - /// Wrapper around a [`httparse::Error`] value. - #[error("httparse error: {0}")] - HttparseError(#[from] httparse::Error), - /// Not allowed to send after having sent a closing frame. - #[error("Sending after closing is not allowed")] - SendAfterClosing, - /// Remote sent data after sending a closing frame. - #[error("Remote sent after having closed")] - ReceivedAfterClosing, - /// Reserved bits in frame header are non-zero. - #[error("Reserved bits are non-zero")] - NonZeroReservedBits, - /// The server must close the connection when an unmasked frame is received. - #[error("Received an unmasked frame from client")] - UnmaskedFrameFromClient, - /// The client must close the connection when a masked frame is received. - #[error("Received a masked frame from server")] - MaskedFrameFromServer, - /// Control frames must not be fragmented. - #[error("Fragmented control frame")] - FragmentedControlFrame, - /// Control frames must have a payload of 125 bytes or less. - #[error("Control frame too big (payload must be 125 bytes or less)")] - ControlFrameTooBig, - /// Type of control frame not recognised. - #[error("Unknown control frame type: {0}")] - UnknownControlFrameType(u8), - /// Type of data frame not recognised. - #[error("Unknown data frame type: {0}")] - UnknownDataFrameType(u8), - /// Received a continue frame despite there being nothing to continue. - #[error("Continue frame but nothing to continue")] - UnexpectedContinueFrame, - /// Received data while waiting for more fragments. - #[error("While waiting for more fragments received: {0}")] - ExpectedFragment(Data), - /// Connection closed without performing the closing handshake. - #[error("Connection reset without closing handshake")] - ResetWithoutClosingHandshake, - /// Encountered an invalid opcode. - #[error("Encountered invalid opcode: {0}")] - InvalidOpcode(u8), - /// The payload for the closing frame is invalid. - #[error("Invalid close sequence")] - InvalidCloseSequence, +impl fmt::Debug for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::ConnectionClosed => write!(f, "Connection closed normally"), + Self::AlreadyClosed => write!(f, "Trying to work with closed connection"), + Self::Io(ref elem) => write!(f, "IO error: {}", elem), + Self::Tls(ref elem) => write!(f, "TLS error: {}", elem), + Self::Capacity(ref elem) => write!(f, "Space limit exceeded: {}", elem), + Self::Protocol(ref elem) => write!(f, "WebSocket protocol error: {}", elem), + Self::SendQueueFull(ref elem) => write!(f, "Send queue is full: {}", elem), + Self::Utf8 => write!(f, "UTF-8 encoding error"), + Self::Url(ref elem) => write!(f, "URL error: {}", elem), + Self::Http(ref elem) => write!(f, "HTTP error: {:?}", elem), + Self::HttpFormat(ref elem) => write!(f, "HTTP format error: {}", elem), + } + } } -/// Indicates the specific type/cause of URL error. -#[derive(Error, Debug, PartialEq, Eq)] -pub enum UrlError { - /// TLS is used despite not being compiled with the TLS feature enabled. - #[error("TLS support not compiled in")] - TlsFeatureNotEnabled, - /// The URL does not include a host name. - #[error("No host name in the URL")] - NoHostName, - /// Failed to connect with this URL. - #[error("Unable to connect to {0}")] - UnableToConnect(String), - /// Unsupported URL scheme used (only `ws://` or `wss://` may be used). - #[error("URL scheme not supported")] - UnsupportedUrlScheme, - /// The URL host name, though included, is empty. - #[error("URL contains empty host name")] - EmptyHostName, - /// The URL does not include a path/query. - #[error("No path/query in URL")] - NoPathOrQuery, +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } } -/// TLS errors. -/// -/// Note that even if you enable only the rustls-based TLS support, the error at runtime could still -/// be `Native`, as another crate in the dependency graph may enable native TLS support. -#[allow(missing_copy_implementations)] -#[derive(Error, Debug)] -#[non_exhaustive] -pub enum TlsError { - /// Native TLS error. - #[cfg(feature = "native-tls")] - #[error("native-tls error: {0}")] - Native(#[from] native_tls_crate::Error), - /// Rustls error. - #[cfg(feature = "rustls-tls")] - #[error("rustls error: {0}")] - Rustls(#[from] rustls::TLSError), - /// DNS name resolution error. - #[cfg(feature = "rustls-tls")] - #[error("Invalid DNS name: {0}")] - Dns(#[from] webpki::InvalidDNSNameError), -} +impl std::error::Error for Error {} diff --git a/src/error/capacity_error.rs b/src/error/capacity_error.rs new file mode 100644 index 0000000..10235e6 --- /dev/null +++ b/src/error/capacity_error.rs @@ -0,0 +1,42 @@ +use std::fmt; + +/// Indicates the specific type/cause of a capacity error. +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum CapacityError { + /// Too many headers provided (see [`httparse::Error::TooManyHeaders`]). + TooManyHeaders, + /// Received header is too long. + HeaderTooLong, + /// Message is bigger than the maximum allowed size. + MessageTooLong { + /// The size of the message. + size: usize, + /// The maximum allowed message size. + max_size: usize, + }, + /// TCP buffer is full. + TcpBufferFull, +} + +impl fmt::Debug for CapacityError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::TooManyHeaders => write!(f, "Too many headers"), + Self::HeaderTooLong => write!(f, "Header too long"), + Self::MessageTooLong { size, max_size } => { + write!(f, "Message too long: {} > {}", size, max_size) + } + Self::TcpBufferFull => write!(f, "Incoming TCP buffer is full"), + } + } +} + +impl fmt::Display for CapacityError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl std::error::Error for CapacityError {} diff --git a/src/error/protocol_error.rs b/src/error/protocol_error.rs new file mode 100644 index 0000000..6f4a98e --- /dev/null +++ b/src/error/protocol_error.rs @@ -0,0 +1,113 @@ +use crate::protocol::frame::coding::Data; +use std::fmt; + +/// Indicates the specific type/cause of a protocol error. +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum ProtocolError { + /// Use of the wrong HTTP method (the WebSocket protocol requires the GET method be used). + WrongHttpMethod, + /// Wrong HTTP version used (the WebSocket protocol requires version 1.1 or higher). + WrongHttpVersion, + /// Missing `Connection: upgrade` HTTP header. + MissingConnectionUpgradeHeader, + /// Missing `Upgrade: websocket` HTTP header. + MissingUpgradeWebSocketHeader, + /// Missing `Sec-WebSocket-Version: 13` HTTP header. + MissingSecWebSocketVersionHeader, + /// Missing `Sec-WebSocket-Key` HTTP header. + MissingSecWebSocketKey, + /// The `Sec-WebSocket-Accept` header is either not present or does not specify the correct key value. + SecWebSocketAcceptKeyMismatch, + /// Garbage data encountered after client request. + JunkAfterRequest, + /// Custom responses must be unsuccessful. + CustomResponseSuccessful, + /// No more data while still performing handshake. + HandshakeIncomplete, + /// Wrapper around a [`httparse::Error`] value. + HttparseError(httparse::Error), + /// Not allowed to send after having sent a closing frame. + SendAfterClosing, + /// Remote sent data after sending a closing frame. + ReceivedAfterClosing, + /// Reserved bits in frame header are non-zero. + NonZeroReservedBits, + /// The server must close the connection when an unmasked frame is received. + UnmaskedFrameFromClient, + /// The client must close the connection when a masked frame is received. + MaskedFrameFromServer, + /// Control frames must not be fragmented. + FragmentedControlFrame, + /// Control frames must have a payload of 125 bytes or less. + ControlFrameTooBig, + /// Type of control frame not recognised. + UnknownControlFrameType(u8), + /// Type of data frame not recognised. + UnknownDataFrameType(u8), + /// Received a continue frame despite there being nothing to continue. + UnexpectedContinueFrame, + /// Received data while waiting for more fragments. + ExpectedFragment(Data), + /// Connection closed without performing the closing handshake. + ResetWithoutClosingHandshake, + /// Encountered an invalid opcode. + InvalidOpcode(u8), + /// The payload for the closing frame is invalid. + InvalidCloseSequence, +} + +impl fmt::Debug for ProtocolError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::WrongHttpMethod => { + write!(f, "Unsupported HTTP method used - only GET is allowed") + } + Self::WrongHttpVersion => write!(f, "HTTP version must be 1.1 or higher"), + Self::MissingConnectionUpgradeHeader => write!(f, "No \"Connection: upgrade\" header"), + Self::MissingUpgradeWebSocketHeader => write!(f, "No \"Upgrade: websocket\" header"), + Self::MissingSecWebSocketVersionHeader => { + write!(f, "No \"Sec-WebSocket-Version: 13\" header") + } + Self::MissingSecWebSocketKey => write!(f, "No \"Sec-WebSocket-Key\" header"), + Self::SecWebSocketAcceptKeyMismatch => { + write!(f, "Key mismatch in \"Sec-WebSocket-Accept\" header") + } + Self::JunkAfterRequest => write!(f, "Junk after client request"), + Self::CustomResponseSuccessful => write!(f, "Custom response must not be successful"), + Self::HandshakeIncomplete => write!(f, "Handshake not finished"), + Self::HttparseError(elem) => write!(f, "httparse error: {}", elem), + Self::SendAfterClosing => write!(f, "Sending after closing is not allowed"), + Self::ReceivedAfterClosing => write!(f, "Remote sent after having closed"), + Self::NonZeroReservedBits => write!(f, "Reserved bits are non-zero"), + Self::UnmaskedFrameFromClient => write!(f, "Received an unmasked frame from client"), + Self::MaskedFrameFromServer => write!(f, "Received a masked frame from server"), + Self::FragmentedControlFrame => write!(f, "Fragmented control frame"), + Self::ControlFrameTooBig => { + write!(f, "Control frame too big (payload must be 125 bytes or less)") + } + Self::UnknownControlFrameType(elem) => { + write!(f, "Unknown control frame type: {}", elem) + } + Self::UnknownDataFrameType(elem) => write!(f, "Unknown data frame type: {}", elem), + Self::UnexpectedContinueFrame => write!(f, "Continue frame but nothing to continue"), + Self::ExpectedFragment(elem) => { + write!(f, "While waiting for more fragments received: {}", elem) + } + Self::ResetWithoutClosingHandshake => { + write!(f, "Connection reset without closing handshake") + } + Self::InvalidOpcode(elem) => write!(f, "Encountered invalid opcode: {}", elem), + Self::InvalidCloseSequence => write!(f, "Invalid close sequence"), + } + } +} + +impl fmt::Display for ProtocolError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl std::error::Error for ProtocolError {} diff --git a/src/error/tls_error.rs b/src/error/tls_error.rs new file mode 100644 index 0000000..ff74ef4 --- /dev/null +++ b/src/error/tls_error.rs @@ -0,0 +1,66 @@ +use std::fmt; + +/// TLS errors. +/// +/// Note that even if you enable only the rustls-based TLS support, the error at runtime could still +/// be `Native`, as another crate in the dependency graph may enable native TLS support. +#[allow(missing_copy_implementations)] +#[non_exhaustive] +pub enum TlsError { + /// Native TLS error. + #[cfg(feature = "native-tls")] + Native(native_tls_crate::Error), + /// Rustls error. + #[cfg(feature = "rustls-tls")] + Rustls(rustls::TLSError), + /// DNS name resolution error. + #[cfg(feature = "rustls-tls")] + Dns(webpki::InvalidDNSNameError), +} + +impl fmt::Debug for TlsError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + #[cfg(feature = "native-tls")] + Self::Native(ref elem) => write!(f, "native-tls error: {}", elem), + #[cfg(feature = "rustls-tls")] + Self::Rustls(ref elem) => write!(f, "rustls error: {}", elem), + #[cfg(feature = "rustls-tls")] + Self::Dns(ref elem) => write!(f, "Invalid DNS name: {}", elem), + } + } +} + +#[cfg(feature = "native-tls")] +impl From for TlsError { + #[inline] + fn from(from: native_tls_crate::Error) -> Self { + Self::Native(from) + } +} + +#[cfg(feature = "rustls-tls")] +impl From for TlsError { + #[inline] + fn from(from: rustls::TLSError) -> Self { + Self::Rustls(from) + } +} + +#[cfg(feature = "rustls-tls")] +impl From for TlsError { + #[inline] + fn from(from: webpki::InvalidDNSNameError) -> Self { + Self::Dns(from) + } +} + +impl fmt::Display for TlsError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl std::error::Error for TlsError {} diff --git a/src/error/url_error.rs b/src/error/url_error.rs new file mode 100644 index 0000000..64d1989 --- /dev/null +++ b/src/error/url_error.rs @@ -0,0 +1,41 @@ +use std::fmt; + +/// Indicates the specific type/cause of URL error. +#[derive(PartialEq, Eq)] +pub enum UrlError { + /// TLS is used despite not being compiled with the TLS feature enabled. + TlsFeatureNotEnabled, + /// The URL does not include a host name. + NoHostName, + /// Failed to connect with this URL. + UnableToConnect(String), + /// Unsupported URL scheme used (only `ws://` or `wss://` may be used). + UnsupportedUrlScheme, + /// The URL host name, though included, is empty. + EmptyHostName, + /// The URL does not include a path/query. + NoPathOrQuery, +} + +impl fmt::Debug for UrlError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::TlsFeatureNotEnabled => write!(f, "TLS support not compiled in"), + Self::NoHostName => write!(f, "No host name in the URL"), + Self::UnableToConnect(ref elem) => write!(f, "Unable to connect to {}", elem), + Self::UnsupportedUrlScheme => write!(f, "URL scheme not supported"), + Self::EmptyHostName => write!(f, "URL contains empty host name"), + Self::NoPathOrQuery => write!(f, "No path/query in URL"), + } + } +} + +impl fmt::Display for UrlError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +impl std::error::Error for UrlError {} diff --git a/src/handshake/mod.rs b/src/handshake/mod.rs index c2c63de..e063d4a 100644 --- a/src/handshake/mod.rs +++ b/src/handshake/mod.rs @@ -131,9 +131,6 @@ mod tests { #[test] fn key_conversion() { // example from RFC 6455 - assert_eq!( - derive_accept_key(b"dGhlIHNhbXBsZSBub25jZQ=="), - "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=" - ); + assert_eq!(derive_accept_key(b"dGhlIHNhbXBsZSBub25jZQ=="), "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="); } }