//! Error handling. use std::{fmt, io, result, str, string}; use crate::protocol::{frame::coding::Data, Message}; use http::Response; use thiserror::Error; #[cfg(feature = "tls")] pub mod tls { //! TLS error wrapper module, feature-gated. pub use native_tls::Error; } /// 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. /// /// This is returned as soon as the close handshake is finished (we have both sent and /// received a close frame) on the server end and as soon as the server has closed the /// underlying connection if this endpoint is a client. /// /// Thus when you receive this, it is safe to drop the underlying connection. /// /// 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. /// /// Trying to read or write after receiving `ConnectionClosed` causes this. /// /// 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), #[cfg(feature = "tls")] /// TLS error. #[error("TLS error: {0}")] Tls(#[from] tls::Error), /// - 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(CapacityError), /// Protocol violation. #[error("WebSocket protocol error: {0}")] 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(UrlErrorType), /// HTTP error. #[error("HTTP error: {}", .0.status())] Http(Response>), /// HTTP format error. #[error("HTTP format error: {0}")] HttpFormat(#[from] http::Error), } impl From for Error { fn from(_: str::Utf8Error) -> Self { Error::Utf8 } } impl From for Error { fn from(_: string::FromUtf8Error) -> Self { Error::Utf8 } } impl From for Error { fn from(err: http::header::InvalidHeaderValue) -> Self { Error::HttpFormat(err.into()) } } impl From for Error { fn from(err: http::header::InvalidHeaderName) -> Self { Error::HttpFormat(err.into()) } } impl From for Error { fn from(_: http::header::ToStrError) -> Self { Error::Utf8 } } impl From for Error { fn from(err: http::uri::InvalidUri) -> Self { Error::HttpFormat(err.into()) } } impl From for Error { fn from(err: http::status::InvalidStatusCode) -> Self { Error::HttpFormat(err.into()) } } impl From for Error { fn from(err: httparse::Error) -> Self { match err { httparse::Error::TooManyHeaders => Error::Capacity(CapacityError::TooManyHeaders), e => Error::Protocol(ProtocolError::HttparseError(e)), } } } /// 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, } /// Indicates the specific type/cause of URL error. #[derive(Debug, PartialEq, Eq)] pub enum UrlErrorType { /// 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::Display for UrlErrorType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { UrlErrorType::TlsFeatureNotEnabled => write!(f, "TLS support not compiled in"), UrlErrorType::NoHostName => write!(f, "No host name in the URL"), UrlErrorType::UnableToConnect(uri) => write!(f, "Unable to connect to {}", uri), UrlErrorType::UnsupportedUrlScheme => write!(f, "URL scheme not supported"), UrlErrorType::EmptyHostName => write!(f, "URL contains empty host name"), UrlErrorType::NoPathOrQuery => write!(f, "No path/query in URL"), } } }