requiem_http/ws/
proto.rs

1use base64;
2use sha1;
3use std::convert::{From, Into};
4use std::fmt;
5
6use self::OpCode::*;
7/// Operation codes as part of rfc6455.
8#[derive(Debug, Eq, PartialEq, Clone, Copy)]
9pub enum OpCode {
10    /// Indicates a continuation frame of a fragmented message.
11    Continue,
12    /// Indicates a text data frame.
13    Text,
14    /// Indicates a binary data frame.
15    Binary,
16    /// Indicates a close control frame.
17    Close,
18    /// Indicates a ping control frame.
19    Ping,
20    /// Indicates a pong control frame.
21    Pong,
22    /// Indicates an invalid opcode was received.
23    Bad,
24}
25
26impl fmt::Display for OpCode {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        match *self {
29            Continue => write!(f, "CONTINUE"),
30            Text => write!(f, "TEXT"),
31            Binary => write!(f, "BINARY"),
32            Close => write!(f, "CLOSE"),
33            Ping => write!(f, "PING"),
34            Pong => write!(f, "PONG"),
35            Bad => write!(f, "BAD"),
36        }
37    }
38}
39
40impl Into<u8> for OpCode {
41    fn into(self) -> u8 {
42        match self {
43            Continue => 0,
44            Text => 1,
45            Binary => 2,
46            Close => 8,
47            Ping => 9,
48            Pong => 10,
49            Bad => {
50                log::error!("Attempted to convert invalid opcode to u8. This is a bug.");
51                8 // if this somehow happens, a close frame will help us tear down quickly
52            }
53        }
54    }
55}
56
57impl From<u8> for OpCode {
58    fn from(byte: u8) -> OpCode {
59        match byte {
60            0 => Continue,
61            1 => Text,
62            2 => Binary,
63            8 => Close,
64            9 => Ping,
65            10 => Pong,
66            _ => Bad,
67        }
68    }
69}
70
71use self::CloseCode::*;
72/// Status code used to indicate why an endpoint is closing the `WebSocket`
73/// connection.
74#[derive(Debug, Eq, PartialEq, Clone, Copy)]
75pub enum CloseCode {
76    /// Indicates a normal closure, meaning that the purpose for
77    /// which the connection was established has been fulfilled.
78    Normal,
79    /// Indicates that an endpoint is "going away", such as a server
80    /// going down or a browser having navigated away from a page.
81    Away,
82    /// Indicates that an endpoint is terminating the connection due
83    /// to a protocol error.
84    Protocol,
85    /// Indicates that an endpoint is terminating the connection
86    /// because it has received a type of data it cannot accept (e.g., an
87    /// endpoint that understands only text data MAY send this if it
88    /// receives a binary message).
89    Unsupported,
90    /// Indicates an abnormal closure. If the abnormal closure was due to an
91    /// error, this close code will not be used. Instead, the `on_error` method
92    /// of the handler will be called with the error. However, if the connection
93    /// is simply dropped, without an error, this close code will be sent to the
94    /// handler.
95    Abnormal,
96    /// Indicates that an endpoint is terminating the connection
97    /// because it has received data within a message that was not
98    /// consistent with the type of the message (e.g., non-UTF-8 \[RFC3629\]
99    /// data within a text message).
100    Invalid,
101    /// Indicates that an endpoint is terminating the connection
102    /// because it has received a message that violates its policy.  This
103    /// is a generic status code that can be returned when there is no
104    /// other more suitable status code (e.g., Unsupported or Size) or if there
105    /// is a need to hide specific details about the policy.
106    Policy,
107    /// Indicates that an endpoint is terminating the connection
108    /// because it has received a message that is too big for it to
109    /// process.
110    Size,
111    /// Indicates that an endpoint (client) is terminating the
112    /// connection because it has expected the server to negotiate one or
113    /// more extension, but the server didn't return them in the response
114    /// message of the WebSocket handshake.  The list of extensions that
115    /// are needed should be given as the reason for closing.
116    /// Note that this status code is not used by the server, because it
117    /// can fail the WebSocket handshake instead.
118    Extension,
119    /// Indicates that a server is terminating the connection because
120    /// it encountered an unexpected condition that prevented it from
121    /// fulfilling the request.
122    Error,
123    /// Indicates that the server is restarting. A client may choose to
124    /// reconnect, and if it does, it should use a randomized delay of 5-30
125    /// seconds between attempts.
126    Restart,
127    /// Indicates that the server is overloaded and the client should either
128    /// connect to a different IP (when multiple targets exist), or
129    /// reconnect to the same IP when a user has performed an action.
130    Again,
131    #[doc(hidden)]
132    Tls,
133    #[doc(hidden)]
134    Other(u16),
135}
136
137impl Into<u16> for CloseCode {
138    fn into(self) -> u16 {
139        match self {
140            Normal => 1000,
141            Away => 1001,
142            Protocol => 1002,
143            Unsupported => 1003,
144            Abnormal => 1006,
145            Invalid => 1007,
146            Policy => 1008,
147            Size => 1009,
148            Extension => 1010,
149            Error => 1011,
150            Restart => 1012,
151            Again => 1013,
152            Tls => 1015,
153            Other(code) => code,
154        }
155    }
156}
157
158impl From<u16> for CloseCode {
159    fn from(code: u16) -> CloseCode {
160        match code {
161            1000 => Normal,
162            1001 => Away,
163            1002 => Protocol,
164            1003 => Unsupported,
165            1006 => Abnormal,
166            1007 => Invalid,
167            1008 => Policy,
168            1009 => Size,
169            1010 => Extension,
170            1011 => Error,
171            1012 => Restart,
172            1013 => Again,
173            1015 => Tls,
174            _ => Other(code),
175        }
176    }
177}
178
179#[derive(Debug, Eq, PartialEq, Clone)]
180/// Reason for closing the connection
181pub struct CloseReason {
182    /// Exit code
183    pub code: CloseCode,
184    /// Optional description of the exit code
185    pub description: Option<String>,
186}
187
188impl From<CloseCode> for CloseReason {
189    fn from(code: CloseCode) -> Self {
190        CloseReason {
191            code,
192            description: None,
193        }
194    }
195}
196
197impl<T: Into<String>> From<(CloseCode, T)> for CloseReason {
198    fn from(info: (CloseCode, T)) -> Self {
199        CloseReason {
200            code: info.0,
201            description: Some(info.1.into()),
202        }
203    }
204}
205
206static WS_GUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
207
208// TODO: hash is always same size, we dont need String
209pub fn hash_key(key: &[u8]) -> String {
210    use sha1::Digest;
211    let mut hasher = sha1::Sha1::new();
212
213    hasher.input(key);
214    hasher.input(WS_GUID.as_bytes());
215
216    base64::encode(hasher.result().as_ref())
217}
218
219#[cfg(test)]
220mod test {
221    #![allow(unused_imports, unused_variables, dead_code)]
222    use super::*;
223
224    macro_rules! opcode_into {
225        ($from:expr => $opcode:pat) => {
226            match OpCode::from($from) {
227                e @ $opcode => (),
228                e => unreachable!("{:?}", e),
229            }
230        };
231    }
232
233    macro_rules! opcode_from {
234        ($from:expr => $opcode:pat) => {
235            let res: u8 = $from.into();
236            match res {
237                e @ $opcode => (),
238                e => unreachable!("{:?}", e),
239            }
240        };
241    }
242
243    #[test]
244    fn test_to_opcode() {
245        opcode_into!(0 => OpCode::Continue);
246        opcode_into!(1 => OpCode::Text);
247        opcode_into!(2 => OpCode::Binary);
248        opcode_into!(8 => OpCode::Close);
249        opcode_into!(9 => OpCode::Ping);
250        opcode_into!(10 => OpCode::Pong);
251        opcode_into!(99 => OpCode::Bad);
252    }
253
254    #[test]
255    fn test_from_opcode() {
256        opcode_from!(OpCode::Continue => 0);
257        opcode_from!(OpCode::Text => 1);
258        opcode_from!(OpCode::Binary => 2);
259        opcode_from!(OpCode::Close => 8);
260        opcode_from!(OpCode::Ping => 9);
261        opcode_from!(OpCode::Pong => 10);
262    }
263
264    #[test]
265    #[should_panic]
266    fn test_from_opcode_debug() {
267        opcode_from!(OpCode::Bad => 99);
268    }
269
270    #[test]
271    fn test_from_opcode_display() {
272        assert_eq!(format!("{}", OpCode::Continue), "CONTINUE");
273        assert_eq!(format!("{}", OpCode::Text), "TEXT");
274        assert_eq!(format!("{}", OpCode::Binary), "BINARY");
275        assert_eq!(format!("{}", OpCode::Close), "CLOSE");
276        assert_eq!(format!("{}", OpCode::Ping), "PING");
277        assert_eq!(format!("{}", OpCode::Pong), "PONG");
278        assert_eq!(format!("{}", OpCode::Bad), "BAD");
279    }
280
281    #[test]
282    fn test_hash_key() {
283        let hash = hash_key(b"hello actix-web");
284        assert_eq!(&hash, "cR1dlyUUJKp0s/Bel25u5TgvC3E=");
285    }
286
287    #[test]
288    fn closecode_from_u16() {
289        assert_eq!(CloseCode::from(1000u16), CloseCode::Normal);
290        assert_eq!(CloseCode::from(1001u16), CloseCode::Away);
291        assert_eq!(CloseCode::from(1002u16), CloseCode::Protocol);
292        assert_eq!(CloseCode::from(1003u16), CloseCode::Unsupported);
293        assert_eq!(CloseCode::from(1006u16), CloseCode::Abnormal);
294        assert_eq!(CloseCode::from(1007u16), CloseCode::Invalid);
295        assert_eq!(CloseCode::from(1008u16), CloseCode::Policy);
296        assert_eq!(CloseCode::from(1009u16), CloseCode::Size);
297        assert_eq!(CloseCode::from(1010u16), CloseCode::Extension);
298        assert_eq!(CloseCode::from(1011u16), CloseCode::Error);
299        assert_eq!(CloseCode::from(1012u16), CloseCode::Restart);
300        assert_eq!(CloseCode::from(1013u16), CloseCode::Again);
301        assert_eq!(CloseCode::from(1015u16), CloseCode::Tls);
302        assert_eq!(CloseCode::from(2000u16), CloseCode::Other(2000));
303    }
304
305    #[test]
306    fn closecode_into_u16() {
307        assert_eq!(1000u16, Into::<u16>::into(CloseCode::Normal));
308        assert_eq!(1001u16, Into::<u16>::into(CloseCode::Away));
309        assert_eq!(1002u16, Into::<u16>::into(CloseCode::Protocol));
310        assert_eq!(1003u16, Into::<u16>::into(CloseCode::Unsupported));
311        assert_eq!(1006u16, Into::<u16>::into(CloseCode::Abnormal));
312        assert_eq!(1007u16, Into::<u16>::into(CloseCode::Invalid));
313        assert_eq!(1008u16, Into::<u16>::into(CloseCode::Policy));
314        assert_eq!(1009u16, Into::<u16>::into(CloseCode::Size));
315        assert_eq!(1010u16, Into::<u16>::into(CloseCode::Extension));
316        assert_eq!(1011u16, Into::<u16>::into(CloseCode::Error));
317        assert_eq!(1012u16, Into::<u16>::into(CloseCode::Restart));
318        assert_eq!(1013u16, Into::<u16>::into(CloseCode::Again));
319        assert_eq!(1015u16, Into::<u16>::into(CloseCode::Tls));
320        assert_eq!(2000u16, Into::<u16>::into(CloseCode::Other(2000)));
321    }
322}