1 //! Various codes defined in RFC 6455.
2 
3 use std::{
4     convert::{From, Into},
5     fmt,
6 };
7 
8 /// WebSocket message opcode as in RFC 6455.
9 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
10 pub 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)]
19 pub 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)]
32 pub 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 
43 impl fmt::Display for Data {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result44     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 
54 impl fmt::Display for Control {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result55     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 
65 impl fmt::Display for OpCode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result66     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 
74 impl From<OpCode> for u8 {
from(code: OpCode) -> Self75     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 
95 impl From<u8> for OpCode {
from(byte: u8) -> OpCode96     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 
116 use self::CloseCode::*;
117 /// Status code used to indicate why an endpoint is closing the WebSocket connection.
118 #[derive(Debug, Eq, PartialEq, Clone, Copy)]
119 pub 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 
190 impl CloseCode {
191     /// Check if this CloseCode is allowed.
is_allowed(self) -> bool192     pub fn is_allowed(self) -> bool {
193         !matches!(self, Bad(_) | Reserved(_) | Status | Abnormal | Tls)
194     }
195 }
196 
197 impl fmt::Display for CloseCode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result198     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199         let code: u16 = self.into();
200         write!(f, "{}", code)
201     }
202 }
203 
204 impl From<CloseCode> for u16 {
from(code: CloseCode) -> u16205     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 
229 impl<'t> From<&'t CloseCode> for u16 {
from(code: &'t CloseCode) -> u16230     fn from(code: &'t CloseCode) -> u16 {
231         (*code).into()
232     }
233 }
234 
235 impl From<u16> for CloseCode {
from(code: u16) -> CloseCode236     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)]
262 mod tests {
263     use super::*;
264 
265     #[test]
opcode_from_u8()266     fn opcode_from_u8() {
267         let byte = 2u8;
268         assert_eq!(OpCode::from(byte), OpCode::Data(Data::Binary));
269     }
270 
271     #[test]
opcode_into_u8()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]
closecode_from_u16()279     fn closecode_from_u16() {
280         let byte = 1008u16;
281         assert_eq!(CloseCode::from(byte), CloseCode::Policy);
282     }
283 
284     #[test]
closecode_into_u16()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 }
292