reqwest_websocket/protocol.rs
1use bytes::Bytes;
2
3/// A `WebSocket` message, which can be a text string or binary data.
4#[derive(Clone, Debug)]
5pub enum Message {
6 /// A text `WebSocket` message.
7 // note: we can't use `tungstenite::Utf8String` here, since we don't have tungstenite on wasm.
8 Text(String),
9
10 /// A binary `WebSocket` message.
11 Binary(Bytes),
12
13 /// A ping message with the specified payload.
14 ///
15 /// The payload here must have a length less than 125 bytes.
16 ///
17 /// # WASM
18 ///
19 /// This variant is ignored for WASM targets.
20 #[cfg_attr(
21 target_arch = "wasm32",
22 deprecated(note = "This variant is ignored for WASM targets")
23 )]
24 Ping(Bytes),
25
26 /// A pong message with the specified payload.
27 ///
28 /// The payload here must have a length less than 125 bytes.
29 ///
30 /// # WASM
31 ///
32 /// This variant is ignored for WASM targets.
33 #[cfg_attr(
34 target_arch = "wasm32",
35 deprecated(note = "This variant is ignored for WASM targets")
36 )]
37 Pong(Bytes),
38
39 /// A close message.
40 ///
41 /// Sending this will not close the connection. Use [`WebSocket::close`] for this.
42 /// Though the remote peer will likely close the connection after receiving this.
43 ///
44 /// [`WebSocket::close`]: crate::WebSocket::close
45 Close { code: CloseCode, reason: String },
46}
47
48impl From<String> for Message {
49 #[inline]
50 fn from(value: String) -> Self {
51 Self::Text(value)
52 }
53}
54
55impl From<&str> for Message {
56 #[inline]
57 fn from(value: &str) -> Self {
58 Self::from(value.to_owned())
59 }
60}
61
62impl From<Bytes> for Message {
63 #[inline]
64 fn from(value: Bytes) -> Self {
65 Self::Binary(value)
66 }
67}
68
69impl From<Vec<u8>> for Message {
70 #[inline]
71 fn from(value: Vec<u8>) -> Self {
72 Self::from(Bytes::from(value))
73 }
74}
75
76impl From<&[u8]> for Message {
77 #[inline]
78 fn from(value: &[u8]) -> Self {
79 Self::from(Bytes::copy_from_slice(value))
80 }
81}
82
83/// Status code used to indicate why an endpoint is closing the `WebSocket`
84/// connection.[1]
85///
86/// [1]: https://datatracker.ietf.org/doc/html/rfc6455
87#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
88#[non_exhaustive]
89pub enum CloseCode {
90 /// Indicates a normal closure, meaning that the purpose for
91 /// which the connection was established has been fulfilled.
92 #[default]
93 Normal,
94
95 /// Indicates that an endpoint is "going away", such as a server
96 /// going down or a browser having navigated away from a page.
97 Away,
98
99 /// Indicates that an endpoint is terminating the connection due
100 /// to a protocol error.
101 Protocol,
102
103 /// Indicates that an endpoint is terminating the connection
104 /// because it has received a type of data it cannot accept (e.g., an
105 /// endpoint that understands only text data MAY send this if it
106 /// receives a binary message).
107 Unsupported,
108
109 /// Indicates that no status code was included in a closing frame. This
110 /// close code makes it possible to use a single method, `on_close` to
111 /// handle even cases where no close code was provided.
112 Status,
113
114 /// Indicates an abnormal closure. If the abnormal closure was due to an
115 /// error, this close code will not be used. Instead, the `on_error` method
116 /// of the handler will be called with the error. However, if the connection
117 /// is simply dropped, without an error, this close code will be sent to the
118 /// handler.
119 Abnormal,
120
121 /// Indicates that an endpoint is terminating the connection
122 /// because it has received data within a message that was not
123 /// consistent with the type of the message (e.g., non-UTF-8 \[RFC3629\]
124 /// data within a text message).
125 Invalid,
126
127 /// Indicates that an endpoint is terminating the connection
128 /// because it has received a message that violates its policy. This
129 /// is a generic status code that can be returned when there is no
130 /// other more suitable status code (e.g., Unsupported or Size) or if there
131 /// is a need to hide specific details about the policy.
132 Policy,
133
134 /// Indicates that an endpoint is terminating the connection
135 /// because it has received a message that is too big for it to
136 /// process.
137 Size,
138
139 /// Indicates that an endpoint (client) is terminating the
140 /// connection because it has expected the server to negotiate one or
141 /// more extension, but the server didn't return them in the response
142 /// message of the `WebSocket` handshake. The list of extensions that
143 /// are needed should be given as the reason for closing.
144 /// Note that this status code is not used by the server, because it
145 /// can fail the `WebSocket` handshake instead.
146 Extension,
147
148 /// Indicates that a server is terminating the connection because
149 /// it encountered an unexpected condition that prevented it from
150 /// fulfilling the request.
151 Error,
152
153 /// Indicates that the server is restarting. A client may choose to
154 /// reconnect, and if it does, it should use a randomized delay of 5-30
155 /// seconds between attempts.
156 Restart,
157
158 /// Indicates that the server is overloaded and the client should either
159 /// connect to a different IP (when multiple targets exist), or
160 /// reconnect to the same IP when a user has performed an action.
161 Again,
162
163 /// Indicates that the connection was closed due to a failure to perform a
164 /// TLS handshake (e.g., the server certificate can't be verified). This
165 /// is a reserved value and MUST NOT be set as a status code in a Close
166 /// control frame by an endpoint.
167 Tls,
168
169 /// Reserved status codes.
170 Reserved(u16),
171
172 /// Reserved for use by libraries, frameworks, and applications. These
173 /// status codes are registered directly with IANA. The interpretation of
174 /// these codes is undefined by the `WebSocket` protocol.
175 Iana(u16),
176
177 /// Reserved for private use. These can't be registered and can be used by
178 /// prior agreements between `WebSocket` applications. The interpretation of
179 /// these codes is undefined by the `WebSocket` protocol.
180 Library(u16),
181
182 /// Unused / invalid status codes.
183 Bad(u16),
184}
185
186impl CloseCode {
187 /// Check if this `CloseCode` is allowed.
188 #[must_use]
189 pub const fn is_allowed(self) -> bool {
190 !matches!(
191 self,
192 Self::Bad(_) | Self::Reserved(_) | Self::Status | Self::Abnormal | Self::Tls
193 )
194 }
195}
196
197impl std::fmt::Display for CloseCode {
198 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
199 let code: u16 = (*self).into();
200 write!(f, "{code}")
201 }
202}
203
204impl From<CloseCode> for u16 {
205 fn from(code: CloseCode) -> Self {
206 match code {
207 CloseCode::Normal => 1000,
208 CloseCode::Away => 1001,
209 CloseCode::Protocol => 1002,
210 CloseCode::Unsupported => 1003,
211 CloseCode::Status => 1005,
212 CloseCode::Abnormal => 1006,
213 CloseCode::Invalid => 1007,
214 CloseCode::Policy => 1008,
215 CloseCode::Size => 1009,
216 CloseCode::Extension => 1010,
217 CloseCode::Error => 1011,
218 CloseCode::Restart => 1012,
219 CloseCode::Again => 1013,
220 CloseCode::Tls => 1015,
221 CloseCode::Reserved(code)
222 | CloseCode::Iana(code)
223 | CloseCode::Library(code)
224 | CloseCode::Bad(code) => code,
225 }
226 }
227}
228
229impl From<u16> for CloseCode {
230 fn from(code: u16) -> Self {
231 match code {
232 1000 => Self::Normal,
233 1001 => Self::Away,
234 1002 => Self::Protocol,
235 1003 => Self::Unsupported,
236 1005 => Self::Status,
237 1006 => Self::Abnormal,
238 1007 => Self::Invalid,
239 1008 => Self::Policy,
240 1009 => Self::Size,
241 1010 => Self::Extension,
242 1011 => Self::Error,
243 1012 => Self::Restart,
244 1013 => Self::Again,
245 1015 => Self::Tls,
246 1016..=2999 => Self::Reserved(code),
247 3000..=3999 => Self::Iana(code),
248 4000..=4999 => Self::Library(code),
249 _ => Self::Bad(code),
250 }
251 }
252}