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