/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 | | } |