blitz_ws/
error.rs

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