Coverage Report

Created: 2025-09-05 06:17

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