1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
//! Error handling.
use std::{io, result, str, string};
use crate::protocol::{frame::coding::Data, Message};
#[cfg(feature = "handshake")]
use http::{header::HeaderName, Response};
use thiserror::Error;
/// Result type of all Tungstenite library calls.
pub type Result<T, E = Error> = result::Result<T, E>;
/// 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),
/// 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),
/// - 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),
/// Protocol violation.
#[error("WebSocket protocol error: {0}")]
Protocol(#[from] ProtocolError),
/// Message write buffer is full.
#[error("Write buffer is full")]
WriteBufferFull(Message),
/// UTF coding error.
#[error("UTF-8 encoding error")]
Utf8,
/// Attack attempt detected.
#[error("Attack attempt detected")]
AttackAttempt,
/// Invalid URL.
#[error("URL error: {0}")]
Url(#[from] UrlError),
/// HTTP error.
#[error("HTTP error: {}", .0.status())]
#[cfg(feature = "handshake")]
Http(Response<Option<Vec<u8>>>),
/// HTTP format error.
#[error("HTTP format error: {0}")]
#[cfg(feature = "handshake")]
HttpFormat(#[from] http::Error),
}
impl From<str::Utf8Error> for Error {
fn from(_: str::Utf8Error) -> Self {
Error::Utf8
}
}
impl From<string::FromUtf8Error> for Error {
fn from(_: string::FromUtf8Error) -> Self {
Error::Utf8
}
}
#[cfg(feature = "handshake")]
impl From<http::header::InvalidHeaderValue> for Error {
fn from(err: http::header::InvalidHeaderValue) -> Self {
Error::HttpFormat(err.into())
}
}
#[cfg(feature = "handshake")]
impl From<http::header::InvalidHeaderName> for Error {
fn from(err: http::header::InvalidHeaderName) -> Self {
Error::HttpFormat(err.into())
}
}
#[cfg(feature = "handshake")]
impl From<http::header::ToStrError> for Error {
fn from(_: http::header::ToStrError) -> Self {
Error::Utf8
}
}
#[cfg(feature = "handshake")]
impl From<http::uri::InvalidUri> for Error {
fn from(err: http::uri::InvalidUri) -> Self {
Error::HttpFormat(err.into())
}
}
#[cfg(feature = "handshake")]
impl From<http::status::InvalidStatusCode> for Error {
fn from(err: http::status::InvalidStatusCode) -> Self {
Error::HttpFormat(err.into())
}
}
#[cfg(feature = "handshake")]
impl From<httparse::Error> 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.
/// 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,
},
}
/// Indicates the specific type/cause of a protocol error.
#[allow(missing_copy_implementations)]
#[derive(Error, Debug, PartialEq, Eq, Clone)]
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,
/// Invalid header is passed. Or the header is missing in the request. Or not present at all. Check the request that you pass.
#[error("Missing, duplicated or incorrect header {0}")]
#[cfg(feature = "handshake")]
InvalidHeader(HeaderName),
/// No more data while still performing handshake.
#[error("Handshake not finished")]
HandshakeIncomplete,
/// Wrapper around a [`httparse::Error`] value.
#[error("httparse error: {0}")]
#[cfg(feature = "handshake")]
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(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,
}
/// 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::Error),
/// DNS name resolution error.
#[cfg(feature = "__rustls-tls")]
#[error("Invalid DNS name")]
InvalidDnsName,
}