1use std::fmt;
2
3use base64::prelude::*;
4use tracing::error;
5
6#[derive(Debug, Eq, PartialEq, Clone, Copy)]
10pub enum OpCode {
11 Continue,
13
14 Text,
16
17 Binary,
19
20 Close,
22
23 Ping,
25
26 Pong,
28
29 Bad,
31}
32
33impl fmt::Display for OpCode {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 use OpCode::*;
36
37 match self {
38 Continue => write!(f, "CONTINUE"),
39 Text => write!(f, "TEXT"),
40 Binary => write!(f, "BINARY"),
41 Close => write!(f, "CLOSE"),
42 Ping => write!(f, "PING"),
43 Pong => write!(f, "PONG"),
44 Bad => write!(f, "BAD"),
45 }
46 }
47}
48
49impl From<OpCode> for u8 {
50 fn from(op: OpCode) -> u8 {
51 use self::OpCode::*;
52
53 match op {
54 Continue => 0,
55 Text => 1,
56 Binary => 2,
57 Close => 8,
58 Ping => 9,
59 Pong => 10,
60 Bad => {
61 error!("Attempted to convert invalid opcode to u8. This is a bug.");
62 8 }
64 }
65 }
66}
67
68impl From<u8> for OpCode {
69 fn from(byte: u8) -> OpCode {
70 use self::OpCode::*;
71
72 match byte {
73 0 => Continue,
74 1 => Text,
75 2 => Binary,
76 8 => Close,
77 9 => Ping,
78 10 => Pong,
79 _ => Bad,
80 }
81 }
82}
83
84#[derive(Debug, Eq, PartialEq, Clone, Copy)]
86pub enum CloseCode {
87 Normal,
90
91 Away,
94
95 Protocol,
97
98 Unsupported,
102
103 Abnormal,
108
109 Invalid,
113
114 Policy,
119
120 Size,
123
124 Extension,
130
131 Error,
134
135 Restart,
138
139 Again,
143
144 #[doc(hidden)]
145 Tls,
146
147 #[doc(hidden)]
148 Other(u16),
149}
150
151impl From<CloseCode> for u16 {
152 fn from(code: CloseCode) -> u16 {
153 use self::CloseCode::*;
154
155 match code {
156 Normal => 1000,
157 Away => 1001,
158 Protocol => 1002,
159 Unsupported => 1003,
160 Abnormal => 1006,
161 Invalid => 1007,
162 Policy => 1008,
163 Size => 1009,
164 Extension => 1010,
165 Error => 1011,
166 Restart => 1012,
167 Again => 1013,
168 Tls => 1015,
169 Other(code) => code,
170 }
171 }
172}
173
174impl From<u16> for CloseCode {
175 fn from(code: u16) -> CloseCode {
176 use self::CloseCode::*;
177
178 match code {
179 1000 => Normal,
180 1001 => Away,
181 1002 => Protocol,
182 1003 => Unsupported,
183 1006 => Abnormal,
184 1007 => Invalid,
185 1008 => Policy,
186 1009 => Size,
187 1010 => Extension,
188 1011 => Error,
189 1012 => Restart,
190 1013 => Again,
191 1015 => Tls,
192 _ => Other(code),
193 }
194 }
195}
196
197#[derive(Debug, Eq, PartialEq, Clone)]
198pub struct CloseReason {
200 pub code: CloseCode,
202
203 pub description: Option<String>,
205}
206
207impl From<CloseCode> for CloseReason {
208 fn from(code: CloseCode) -> Self {
209 CloseReason {
210 code,
211 description: None,
212 }
213 }
214}
215
216impl<T: Into<String>> From<(CloseCode, T)> for CloseReason {
217 fn from(info: (CloseCode, T)) -> Self {
218 CloseReason {
219 code: info.0,
220 description: Some(info.1.into()),
221 }
222 }
223}
224
225static WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
228
229pub fn hash_key(key: &[u8]) -> [u8; 28] {
233 let hash = {
234 use sha1::Digest as _;
235
236 let mut hasher = sha1::Sha1::new();
237
238 hasher.update(key);
239 hasher.update(WS_GUID);
240
241 hasher.finalize()
242 };
243
244 let mut hash_b64 = [0; 28];
245 let n = BASE64_STANDARD.encode_slice(hash, &mut hash_b64).unwrap();
246 assert_eq!(n, 28);
247
248 hash_b64
249}
250
251#[cfg(test)]
252mod test {
253 #![allow(unused_imports, unused_variables, dead_code)]
254 use super::*;
255
256 macro_rules! opcode_into {
257 ($from:expr => $opcode:pat) => {
258 match OpCode::from($from) {
259 e @ $opcode => {}
260 e => unreachable!("{:?}", e),
261 }
262 };
263 }
264
265 macro_rules! opcode_from {
266 ($from:expr => $opcode:pat) => {
267 let res: u8 = $from.into();
268 match res {
269 e @ $opcode => {}
270 e => unreachable!("{:?}", e),
271 }
272 };
273 }
274
275 #[test]
276 fn test_to_opcode() {
277 opcode_into!(0 => OpCode::Continue);
278 opcode_into!(1 => OpCode::Text);
279 opcode_into!(2 => OpCode::Binary);
280 opcode_into!(8 => OpCode::Close);
281 opcode_into!(9 => OpCode::Ping);
282 opcode_into!(10 => OpCode::Pong);
283 opcode_into!(99 => OpCode::Bad);
284 }
285
286 #[test]
287 fn test_from_opcode() {
288 opcode_from!(OpCode::Continue => 0);
289 opcode_from!(OpCode::Text => 1);
290 opcode_from!(OpCode::Binary => 2);
291 opcode_from!(OpCode::Close => 8);
292 opcode_from!(OpCode::Ping => 9);
293 opcode_from!(OpCode::Pong => 10);
294 }
295
296 #[test]
297 #[should_panic]
298 fn test_from_opcode_debug() {
299 opcode_from!(OpCode::Bad => 99);
300 }
301
302 #[test]
303 fn test_from_opcode_display() {
304 assert_eq!(format!("{}", OpCode::Continue), "CONTINUE");
305 assert_eq!(format!("{}", OpCode::Text), "TEXT");
306 assert_eq!(format!("{}", OpCode::Binary), "BINARY");
307 assert_eq!(format!("{}", OpCode::Close), "CLOSE");
308 assert_eq!(format!("{}", OpCode::Ping), "PING");
309 assert_eq!(format!("{}", OpCode::Pong), "PONG");
310 assert_eq!(format!("{}", OpCode::Bad), "BAD");
311 }
312
313 #[test]
314 fn test_hash_key() {
315 let hash = hash_key(b"hello actix-web");
316 assert_eq!(&hash, b"cR1dlyUUJKp0s/Bel25u5TgvC3E=");
317 }
318
319 #[test]
320 fn close_code_from_u16() {
321 assert_eq!(CloseCode::from(1000u16), CloseCode::Normal);
322 assert_eq!(CloseCode::from(1001u16), CloseCode::Away);
323 assert_eq!(CloseCode::from(1002u16), CloseCode::Protocol);
324 assert_eq!(CloseCode::from(1003u16), CloseCode::Unsupported);
325 assert_eq!(CloseCode::from(1006u16), CloseCode::Abnormal);
326 assert_eq!(CloseCode::from(1007u16), CloseCode::Invalid);
327 assert_eq!(CloseCode::from(1008u16), CloseCode::Policy);
328 assert_eq!(CloseCode::from(1009u16), CloseCode::Size);
329 assert_eq!(CloseCode::from(1010u16), CloseCode::Extension);
330 assert_eq!(CloseCode::from(1011u16), CloseCode::Error);
331 assert_eq!(CloseCode::from(1012u16), CloseCode::Restart);
332 assert_eq!(CloseCode::from(1013u16), CloseCode::Again);
333 assert_eq!(CloseCode::from(1015u16), CloseCode::Tls);
334 assert_eq!(CloseCode::from(2000u16), CloseCode::Other(2000));
335 }
336
337 #[test]
338 fn close_code_into_u16() {
339 assert_eq!(1000u16, Into::<u16>::into(CloseCode::Normal));
340 assert_eq!(1001u16, Into::<u16>::into(CloseCode::Away));
341 assert_eq!(1002u16, Into::<u16>::into(CloseCode::Protocol));
342 assert_eq!(1003u16, Into::<u16>::into(CloseCode::Unsupported));
343 assert_eq!(1006u16, Into::<u16>::into(CloseCode::Abnormal));
344 assert_eq!(1007u16, Into::<u16>::into(CloseCode::Invalid));
345 assert_eq!(1008u16, Into::<u16>::into(CloseCode::Policy));
346 assert_eq!(1009u16, Into::<u16>::into(CloseCode::Size));
347 assert_eq!(1010u16, Into::<u16>::into(CloseCode::Extension));
348 assert_eq!(1011u16, Into::<u16>::into(CloseCode::Error));
349 assert_eq!(1012u16, Into::<u16>::into(CloseCode::Restart));
350 assert_eq!(1013u16, Into::<u16>::into(CloseCode::Again));
351 assert_eq!(1015u16, Into::<u16>::into(CloseCode::Tls));
352 assert_eq!(2000u16, Into::<u16>::into(CloseCode::Other(2000)));
353 }
354}