tungstenite/protocol/frame/coding.rs
1//! Various codes defined in RFC 6455.
2
3use std::{
4 convert::{From, Into},
5 fmt,
6};
7
8/// WebSocket message opcode as in RFC 6455.
9#[derive(Debug, PartialEq, Eq, Clone, Copy)]
10pub enum OpCode {
11 /// Data (text or binary).
12 Data(Data),
13 /// Control message (close, ping, pong).
14 Control(Control),
15}
16
17/// Data opcodes as in RFC 6455
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19pub enum Data {
20 /// 0x0 denotes a continuation frame
21 Continue,
22 /// 0x1 denotes a text frame
23 Text,
24 /// 0x2 denotes a binary frame
25 Binary,
26 /// 0x3-7 are reserved for further non-control frames
27 Reserved(u8),
28}
29
30/// Control opcodes as in RFC 6455
31#[derive(Debug, PartialEq, Eq, Clone, Copy)]
32pub enum Control {
33 /// 0x8 denotes a connection close
34 Close,
35 /// 0x9 denotes a ping
36 Ping,
37 /// 0xa denotes a pong
38 Pong,
39 /// 0xb-f are reserved for further control frames
40 Reserved(u8),
41}
42
43impl fmt::Display for Data {
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 match *self {
46 Data::Continue => write!(f, "CONTINUE"),
47 Data::Text => write!(f, "TEXT"),
48 Data::Binary => write!(f, "BINARY"),
49 Data::Reserved(x) => write!(f, "RESERVED_DATA_{}", x),
50 }
51 }
52}
53
54impl fmt::Display for Control {
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56 match *self {
57 Control::Close => write!(f, "CLOSE"),
58 Control::Ping => write!(f, "PING"),
59 Control::Pong => write!(f, "PONG"),
60 Control::Reserved(x) => write!(f, "RESERVED_CONTROL_{}", x),
61 }
62 }
63}
64
65impl fmt::Display for OpCode {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 match *self {
68 OpCode::Data(d) => d.fmt(f),
69 OpCode::Control(c) => c.fmt(f),
70 }
71 }
72}
73
74impl From<OpCode> for u8 {
75 fn from(code: OpCode) -> Self {
76 use self::{
77 Control::{Close, Ping, Pong},
78 Data::{Binary, Continue, Text},
79 OpCode::*,
80 };
81 match code {
82 Data(Continue) => 0,
83 Data(Text) => 1,
84 Data(Binary) => 2,
85 Data(self::Data::Reserved(i)) => i,
86
87 Control(Close) => 8,
88 Control(Ping) => 9,
89 Control(Pong) => 10,
90 Control(self::Control::Reserved(i)) => i,
91 }
92 }
93}
94
95impl From<u8> for OpCode {
96 fn from(byte: u8) -> OpCode {
97 use self::{
98 Control::{Close, Ping, Pong},
99 Data::{Binary, Continue, Text},
100 OpCode::*,
101 };
102 match byte {
103 0 => Data(Continue),
104 1 => Data(Text),
105 2 => Data(Binary),
106 i @ 3..=7 => Data(self::Data::Reserved(i)),
107 8 => Control(Close),
108 9 => Control(Ping),
109 10 => Control(Pong),
110 i @ 11..=15 => Control(self::Control::Reserved(i)),
111 _ => panic!("Bug: OpCode out of range"),
112 }
113 }
114}
115
116use self::CloseCode::*;
117/// Status code used to indicate why an endpoint is closing the WebSocket connection.
118#[derive(Debug, Eq, PartialEq, Clone, Copy)]
119pub enum CloseCode {
120 /// Indicates a normal closure, meaning that the purpose for
121 /// which the connection was established has been fulfilled.
122 Normal,
123 /// Indicates that an endpoint is "going away", such as a server
124 /// going down or a browser having navigated away from a page.
125 Away,
126 /// Indicates that an endpoint is terminating the connection due
127 /// to a protocol error.
128 Protocol,
129 /// Indicates that an endpoint is terminating the connection
130 /// because it has received a type of data it cannot accept (e.g., an
131 /// endpoint that understands only text data MAY send this if it
132 /// receives a binary message).
133 Unsupported,
134 /// Indicates that no status code was included in a closing frame. This
135 /// close code makes it possible to use a single method, `on_close` to
136 /// handle even cases where no close code was provided.
137 Status,
138 /// Indicates an abnormal closure. If the abnormal closure was due to an
139 /// error, this close code will not be used. Instead, the `on_error` method
140 /// of the handler will be called with the error. However, if the connection
141 /// is simply dropped, without an error, this close code will be sent to the
142 /// handler.
143 Abnormal,
144 /// Indicates that an endpoint is terminating the connection
145 /// because it has received data within a message that was not
146 /// consistent with the type of the message (e.g., non-UTF-8 \[RFC3629\]
147 /// data within a text message).
148 Invalid,
149 /// Indicates that an endpoint is terminating the connection
150 /// because it has received a message that violates its policy. This
151 /// is a generic status code that can be returned when there is no
152 /// other more suitable status code (e.g., Unsupported or Size) or if there
153 /// is a need to hide specific details about the policy.
154 Policy,
155 /// Indicates that an endpoint is terminating the connection
156 /// because it has received a message that is too big for it to
157 /// process.
158 Size,
159 /// Indicates that an endpoint (client) is terminating the
160 /// connection because it has expected the server to negotiate one or
161 /// more extension, but the server didn't return them in the response
162 /// message of the WebSocket handshake. The list of extensions that
163 /// are needed should be given as the reason for closing.
164 /// Note that this status code is not used by the server, because it
165 /// can fail the WebSocket handshake instead.
166 Extension,
167 /// Indicates that a server is terminating the connection because
168 /// it encountered an unexpected condition that prevented it from
169 /// fulfilling the request.
170 Error,
171 /// Indicates that the server is restarting. A client may choose to reconnect,
172 /// and if it does, it should use a randomized delay of 5-30 seconds between attempts.
173 Restart,
174 /// Indicates that the server is overloaded and the client should either connect
175 /// to a different IP (when multiple targets exist), or reconnect to the same IP
176 /// when a user has performed an action.
177 Again,
178 #[doc(hidden)]
179 Tls,
180 #[doc(hidden)]
181 Reserved(u16),
182 #[doc(hidden)]
183 Iana(u16),
184 #[doc(hidden)]
185 Library(u16),
186 #[doc(hidden)]
187 Bad(u16),
188}
189
190impl CloseCode {
191 /// Check if this CloseCode is allowed.
192 pub fn is_allowed(self) -> bool {
193 !matches!(self, Bad(_) | Reserved(_) | Status | Abnormal | Tls)
194 }
195}
196
197impl fmt::Display for CloseCode {
198 fn fmt(&self, f: &mut fmt::Formatter) -> 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) -> u16 {
206 match code {
207 Normal => 1000,
208 Away => 1001,
209 Protocol => 1002,
210 Unsupported => 1003,
211 Status => 1005,
212 Abnormal => 1006,
213 Invalid => 1007,
214 Policy => 1008,
215 Size => 1009,
216 Extension => 1010,
217 Error => 1011,
218 Restart => 1012,
219 Again => 1013,
220 Tls => 1015,
221 Reserved(code) => code,
222 Iana(code) => code,
223 Library(code) => code,
224 Bad(code) => code,
225 }
226 }
227}
228
229impl<'t> From<&'t CloseCode> for u16 {
230 fn from(code: &'t CloseCode) -> u16 {
231 (*code).into()
232 }
233}
234
235impl From<u16> for CloseCode {
236 fn from(code: u16) -> CloseCode {
237 match code {
238 1000 => Normal,
239 1001 => Away,
240 1002 => Protocol,
241 1003 => Unsupported,
242 1005 => Status,
243 1006 => Abnormal,
244 1007 => Invalid,
245 1008 => Policy,
246 1009 => Size,
247 1010 => Extension,
248 1011 => Error,
249 1012 => Restart,
250 1013 => Again,
251 1015 => Tls,
252 1..=999 => Bad(code),
253 1016..=2999 => Reserved(code),
254 3000..=3999 => Iana(code),
255 4000..=4999 => Library(code),
256 _ => Bad(code),
257 }
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn opcode_from_u8() {
267 let byte = 2u8;
268 assert_eq!(OpCode::from(byte), OpCode::Data(Data::Binary));
269 }
270
271 #[test]
272 fn opcode_into_u8() {
273 let text = OpCode::Data(Data::Text);
274 let byte: u8 = text.into();
275 assert_eq!(byte, 1u8);
276 }
277
278 #[test]
279 fn closecode_from_u16() {
280 let byte = 1008u16;
281 assert_eq!(CloseCode::from(byte), CloseCode::Policy);
282 }
283
284 #[test]
285 fn closecode_into_u16() {
286 let text = CloseCode::Away;
287 let byte: u16 = text.into();
288 assert_eq!(byte, 1001u16);
289 assert_eq!(u16::from(text), 1001u16);
290 }
291}