Skip to main content

tungstenite/
error.rs

1//! Error handling.
2
3use std::{io, result, str, string};
4
5use crate::{
6    extensions::{compression::CompressionError, ExtensionsError},
7    protocol::{frame::coding::Data, Message},
8};
9#[cfg(feature = "handshake")]
10use http::{header::HeaderName, Response};
11use thiserror::Error;
12
13/// Result type of all Tungstenite library calls.
14pub type Result<T, E = Error> = result::Result<T, E>;
15
16/// Possible WebSocket errors.
17#[derive(Error, Debug)]
18pub enum Error {
19    /// WebSocket connection closed normally. This informs you of the close.
20    /// It's not an error as such and nothing wrong happened.
21    ///
22    /// This is returned as soon as the close handshake is finished (we have both sent and
23    /// received a close frame) on the server end and as soon as the server has closed the
24    /// underlying connection if this endpoint is a client.
25    ///
26    /// Thus when you receive this, it is safe to drop the underlying connection.
27    ///
28    /// Receiving this error means that the WebSocket object is not usable anymore and the
29    /// only meaningful action with it is dropping it.
30    #[error("Connection closed normally")]
31    ConnectionClosed,
32    /// Trying to work with already closed connection.
33    ///
34    /// Trying to read or write after receiving `ConnectionClosed` causes this.
35    ///
36    /// As opposed to `ConnectionClosed`, this indicates your code tries to operate on the
37    /// connection when it really shouldn't anymore, so this really indicates a programmer
38    /// error on your part.
39    #[error("Trying to work with closed connection")]
40    AlreadyClosed,
41    /// Input-output error. Apart from WouldBlock, these are generally errors with the
42    /// underlying connection and you should probably consider them fatal.
43    #[error("IO error: {0}")]
44    Io(#[from] io::Error),
45    /// TLS error.
46    ///
47    /// Note that this error variant is enabled unconditionally even if no TLS feature is enabled,
48    /// to provide a feature-agnostic API surface.
49    #[error("TLS error: {0}")]
50    Tls(#[from] TlsError),
51    /// - When reading: buffer capacity exhausted.
52    /// - When writing: your message is bigger than the configured max message size
53    ///   (64MB by default).
54    #[error("Space limit exceeded: {0}")]
55    Capacity(#[from] CapacityError),
56    /// Protocol violation.
57    #[error("WebSocket protocol error: {0}")]
58    Protocol(#[from] ProtocolError),
59    /// Message write buffer is full.
60    #[error("Write buffer is full")]
61    WriteBufferFull(Box<Message>),
62    /// UTF coding error.
63    #[error("UTF-8 encoding error: {0}")]
64    Utf8(String),
65    /// Attack attempt detected.
66    #[error("Attack attempt detected")]
67    AttackAttempt,
68    /// Invalid URL.
69    #[error("URL error: {0}")]
70    Url(#[from] UrlError),
71    /// HTTP error.
72    #[error("HTTP error: {}", .0.status())]
73    #[cfg(feature = "handshake")]
74    Http(Box<Response<Option<Vec<u8>>>>),
75    /// HTTP format error.
76    #[error("HTTP format error: {0}")]
77    #[cfg(feature = "handshake")]
78    HttpFormat(#[from] http::Error),
79}
80
81impl From<str::Utf8Error> for Error {
82    fn from(err: str::Utf8Error) -> Self {
83        Error::Utf8(err.to_string())
84    }
85}
86
87impl From<string::FromUtf8Error> for Error {
88    fn from(err: string::FromUtf8Error) -> Self {
89        Error::Utf8(err.to_string())
90    }
91}
92
93#[cfg(feature = "handshake")]
94impl From<http::header::InvalidHeaderValue> for Error {
95    fn from(err: http::header::InvalidHeaderValue) -> Self {
96        Error::HttpFormat(err.into())
97    }
98}
99
100#[cfg(feature = "handshake")]
101impl From<http::header::InvalidHeaderName> for Error {
102    fn from(err: http::header::InvalidHeaderName) -> Self {
103        Error::HttpFormat(err.into())
104    }
105}
106
107#[cfg(feature = "handshake")]
108impl From<http::header::ToStrError> for Error {
109    fn from(err: http::header::ToStrError) -> Self {
110        Error::Utf8(err.to_string())
111    }
112}
113
114#[cfg(feature = "handshake")]
115impl From<http::uri::InvalidUri> for Error {
116    fn from(err: http::uri::InvalidUri) -> Self {
117        Error::HttpFormat(err.into())
118    }
119}
120
121#[cfg(feature = "handshake")]
122impl From<http::status::InvalidStatusCode> for Error {
123    fn from(err: http::status::InvalidStatusCode) -> Self {
124        Error::HttpFormat(err.into())
125    }
126}
127
128#[cfg(feature = "handshake")]
129impl From<httparse::Error> for Error {
130    fn from(err: httparse::Error) -> Self {
131        match err {
132            httparse::Error::TooManyHeaders => Error::Capacity(CapacityError::TooManyHeaders),
133            e => Error::Protocol(ProtocolError::HttparseError(e)),
134        }
135    }
136}
137
138/// Indicates the specific type/cause of a capacity error.
139#[derive(Error, Debug, PartialEq, Eq, Clone, Copy)]
140pub enum CapacityError {
141    /// Too many headers provided (see [`httparse::Error::TooManyHeaders`]).
142    #[error("Too many headers")]
143    TooManyHeaders,
144    /// Received header is too long.
145    /// Message is bigger than the maximum allowed size.
146    #[error("Message too long: {size} > {max_size}")]
147    MessageTooLong {
148        /// The size of the message.
149        size: usize,
150        /// The maximum allowed message size.
151        max_size: usize,
152    },
153}
154
155/// Indicates the specific type/cause of a subprotocol header error.
156#[derive(Error, Clone, PartialEq, Eq, Debug, Copy)]
157pub enum SubProtocolError {
158    /// The server sent a subprotocol to a client handshake request but none was requested
159    #[error("Server sent a subprotocol but none was requested")]
160    ServerSentSubProtocolNoneRequested,
161
162    /// The server sent an invalid subprotocol to a client handhshake request
163    #[error("Server sent an invalid subprotocol")]
164    InvalidSubProtocol,
165
166    /// The server sent no subprotocol to a client handshake request that requested one or more
167    /// subprotocols
168    #[error("Server sent no subprotocol")]
169    NoSubProtocol,
170}
171
172/// Indicates the specific type/cause of a protocol error.
173#[allow(missing_copy_implementations)]
174#[derive(Error, Debug, PartialEq, Eq, Clone)]
175pub enum ProtocolError {
176    /// Use of the wrong HTTP method (the WebSocket protocol requires the GET method be used).
177    #[error("Unsupported HTTP method used - only GET is allowed")]
178    WrongHttpMethod,
179    /// Wrong HTTP version used (the WebSocket protocol requires version 1.1 or higher).
180    #[error("HTTP version must be 1.1 or higher")]
181    WrongHttpVersion,
182    /// Missing `Connection: upgrade` HTTP header.
183    #[error("No \"Connection: upgrade\" header")]
184    MissingConnectionUpgradeHeader,
185    /// Missing `Upgrade: websocket` HTTP header.
186    #[error("No \"Upgrade: websocket\" header")]
187    MissingUpgradeWebSocketHeader,
188    /// Missing `Sec-WebSocket-Version: 13` HTTP header.
189    #[error("No \"Sec-WebSocket-Version: 13\" header")]
190    MissingSecWebSocketVersionHeader,
191    /// Missing `Sec-WebSocket-Key` HTTP header.
192    #[error("No \"Sec-WebSocket-Key\" header")]
193    MissingSecWebSocketKey,
194    /// The `Sec-WebSocket-Accept` header is either not present or does not specify the correct key value.
195    #[error("Key mismatch in \"Sec-WebSocket-Accept\" header")]
196    SecWebSocketAcceptKeyMismatch,
197    /// The `Sec-WebSocket-Protocol` header was invalid
198    #[error("SubProtocol error: {0}")]
199    SecWebSocketSubProtocolError(SubProtocolError),
200    /// The `Sec-WebSocket-Extensions` header is invalid.
201    #[error("Invalid \"Sec-WebSocket-Extensions\" header: {0}")]
202    InvalidExtensionsHeader(#[from] ExtensionsError),
203    /// Garbage data encountered after client request.
204    #[error("Junk after client request")]
205    JunkAfterRequest,
206    /// Custom responses must be unsuccessful.
207    #[error("Custom response must not be successful")]
208    CustomResponseSuccessful,
209    /// Invalid header is passed. Or the header is missing in the request. Or not present at all. Check the request that you pass.
210    #[error("Missing, duplicated or incorrect header {0}")]
211    #[cfg(feature = "handshake")]
212    InvalidHeader(Box<HeaderName>),
213    /// No more data while still performing handshake.
214    #[error("Handshake not finished")]
215    HandshakeIncomplete,
216    /// Wrapper around a [`httparse::Error`] value.
217    #[error("httparse error: {0}")]
218    #[cfg(feature = "handshake")]
219    HttparseError(#[from] httparse::Error),
220    /// Not allowed to send after having sent a closing frame.
221    #[error("Sending after closing is not allowed")]
222    SendAfterClosing,
223    /// Remote sent data after sending a closing frame.
224    #[error("Remote sent after having closed")]
225    ReceivedAfterClosing,
226    /// Reserved bits in frame header are non-zero.
227    #[error("Reserved bits are non-zero")]
228    NonZeroReservedBits,
229    /// The server must close the connection when an unmasked frame is received.
230    #[error("Received an unmasked frame from client")]
231    UnmaskedFrameFromClient,
232    /// The client must close the connection when a masked frame is received.
233    #[error("Received a masked frame from server")]
234    MaskedFrameFromServer,
235    /// Control frames must not be fragmented.
236    #[error("Fragmented control frame")]
237    FragmentedControlFrame,
238    /// Control frames must not be compressed.
239    #[error("Compressed control frame")]
240    CompressedControlFrame,
241    /// Control frames must have a payload of 125 bytes or less.
242    #[error("Control frame too big (payload must be 125 bytes or less)")]
243    ControlFrameTooBig,
244    /// Type of control frame not recognised.
245    #[error("Unknown control frame type: {0}")]
246    UnknownControlFrameType(u8),
247    /// Type of data frame not recognised.
248    #[error("Unknown data frame type: {0}")]
249    UnknownDataFrameType(u8),
250    /// Received a continue frame despite there being nothing to continue.
251    #[error("Continue frame but nothing to continue")]
252    UnexpectedContinueFrame,
253    /// Received a compressed continue frame.
254    #[error("Continue frame must not have compress bit set")]
255    CompressedContinueFrame,
256    /// Received data while waiting for more fragments.
257    #[error("While waiting for more fragments received: {0}")]
258    ExpectedFragment(Data),
259    /// Connection closed without performing the closing handshake.
260    #[error("Connection reset without closing handshake")]
261    ResetWithoutClosingHandshake,
262    /// Encountered an invalid opcode.
263    #[error("Encountered invalid opcode: {0}")]
264    InvalidOpcode(u8),
265    /// The payload for the closing frame is invalid.
266    #[error("Invalid close sequence")]
267    InvalidCloseSequence,
268    /// Compression or decompression failure.
269    #[error("Compression/decompression failed: {0}")]
270    CompressionFailure(#[from] CompressionError),
271}
272
273/// Indicates the specific type/cause of URL error.
274#[derive(Error, Debug, PartialEq, Eq)]
275pub enum UrlError {
276    /// TLS is used despite not being compiled with the TLS feature enabled.
277    #[error("TLS support not compiled in")]
278    TlsFeatureNotEnabled,
279    /// The URL does not include a host name.
280    #[error("No host name in the URL")]
281    NoHostName,
282    /// Failed to connect with this URL.
283    #[error("Unable to connect to {0}")]
284    UnableToConnect(String),
285    /// Unsupported URL scheme used (only `ws://` or `wss://` may be used).
286    #[error("URL scheme not supported")]
287    UnsupportedUrlScheme,
288    /// The URL host name, though included, is empty.
289    #[error("URL contains empty host name")]
290    EmptyHostName,
291    /// The URL does not include a path/query.
292    #[error("No path/query in URL")]
293    NoPathOrQuery,
294    /// The proxy URL uses an unsupported scheme.
295    #[error("Proxy URL scheme not supported")]
296    UnsupportedProxyScheme,
297    /// The proxy configuration was invalid.
298    #[error("Invalid proxy configuration: {0}")]
299    InvalidProxyConfig(String),
300    /// The proxy connection failed.
301    #[error("Proxy connection failed: {0}")]
302    ProxyConnect(String),
303}
304
305/// TLS errors.
306///
307/// Note that even if you enable only the rustls-based TLS support, the error at runtime could still
308/// be `Native`, as another crate in the dependency graph may enable native TLS support.
309#[allow(missing_copy_implementations)]
310#[derive(Error, Debug)]
311#[non_exhaustive]
312pub enum TlsError {
313    /// Native TLS error.
314    #[cfg(feature = "native-tls")]
315    #[error("native-tls error: {0}")]
316    Native(Box<native_tls_crate::Error>),
317    /// Rustls error.
318    #[cfg(feature = "__rustls-tls")]
319    #[error("rustls error: {0}")]
320    Rustls(Box<rustls::Error>),
321    /// DNS name resolution error.
322    #[cfg(feature = "__rustls-tls")]
323    #[error("Invalid DNS name")]
324    InvalidDnsName,
325}
326
327#[cfg(feature = "native-tls")]
328impl From<native_tls_crate::Error> for TlsError {
329    fn from(e: native_tls_crate::Error) -> Self {
330        Self::Native(e.into())
331    }
332}
333
334#[cfg(feature = "__rustls-tls")]
335impl From<rustls::Error> for TlsError {
336    fn from(e: rustls::Error) -> Self {
337        Self::Rustls(e.into())
338    }
339}
340
341#[cfg(test)]
342mod test {
343    #[test]
344    fn error_size() {
345        let size = std::mem::size_of::<crate::Error>();
346        assert!(size <= 32, "Error is large: {size}");
347    }
348
349    #[test]
350    fn tls_error_size() {
351        let size = std::mem::size_of::<crate::error::TlsError>();
352        assert!(size <= 16, "TlsError is large: {size}");
353    }
354
355    #[test]
356    fn protocol_error_size() {
357        let size = std::mem::size_of::<crate::error::ProtocolError>();
358        assert!(size <= 24, "ProtocolError is large: {size}");
359    }
360}