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}