1use core::fmt;
4
5use tracing::error;
6
7#[derive(Debug, Eq, PartialEq, Copy, Clone)]
9pub enum OpCode {
10 Continue,
12 Text,
14 Binary,
16 Close,
18 Ping,
20 Pong,
22 Bad,
24}
25
26impl fmt::Display for OpCode {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 use OpCode::*;
29
30 match self {
31 Continue => write!(f, "CONTINUE"),
32 Text => write!(f, "TEXT"),
33 Binary => write!(f, "BINARY"),
34 Close => write!(f, "CLOSE"),
35 Ping => write!(f, "PING"),
36 Pong => write!(f, "PONG"),
37 Bad => write!(f, "BAD"),
38 }
39 }
40}
41
42impl From<OpCode> for u8 {
43 fn from(op: OpCode) -> u8 {
44 match op {
45 OpCode::Continue => 0,
46 OpCode::Text => 1,
47 OpCode::Binary => 2,
48 OpCode::Close => 8,
49 OpCode::Ping => 9,
50 OpCode::Pong => 10,
51 OpCode::Bad => {
52 error!("Attempted to convert invalid opcode to u8. This is a bug.");
53 8 }
55 }
56 }
57}
58
59impl From<u8> for OpCode {
60 fn from(byte: u8) -> OpCode {
61 match byte {
62 0 => OpCode::Continue,
63 1 => OpCode::Text,
64 2 => OpCode::Binary,
65 8 => OpCode::Close,
66 9 => OpCode::Ping,
67 10 => OpCode::Pong,
68 _ => OpCode::Bad,
69 }
70 }
71}
72
73#[derive(Debug, Eq, PartialEq, Copy, Clone)]
75pub enum CloseCode {
76 Normal,
79 Away,
82 Protocol,
84 Unsupported,
88 Abnormal,
93 Invalid,
97 Policy,
102 Size,
105 Extension,
111 Error,
114 Restart,
117 Again,
121 #[doc(hidden)]
122 Tls,
123 #[doc(hidden)]
124 Other(u16),
125}
126
127impl From<CloseCode> for u16 {
128 fn from(code: CloseCode) -> u16 {
129 match code {
130 CloseCode::Normal => 1000,
131 CloseCode::Away => 1001,
132 CloseCode::Protocol => 1002,
133 CloseCode::Unsupported => 1003,
134 CloseCode::Abnormal => 1006,
135 CloseCode::Invalid => 1007,
136 CloseCode::Policy => 1008,
137 CloseCode::Size => 1009,
138 CloseCode::Extension => 1010,
139 CloseCode::Error => 1011,
140 CloseCode::Restart => 1012,
141 CloseCode::Again => 1013,
142 CloseCode::Tls => 1015,
143 CloseCode::Other(code) => code,
144 }
145 }
146}
147
148impl From<u16> for CloseCode {
149 fn from(code: u16) -> CloseCode {
150 match code {
151 1000 => CloseCode::Normal,
152 1001 => CloseCode::Away,
153 1002 => CloseCode::Protocol,
154 1003 => CloseCode::Unsupported,
155 1006 => CloseCode::Abnormal,
156 1007 => CloseCode::Invalid,
157 1008 => CloseCode::Policy,
158 1009 => CloseCode::Size,
159 1010 => CloseCode::Extension,
160 1011 => CloseCode::Error,
161 1012 => CloseCode::Restart,
162 1013 => CloseCode::Again,
163 1015 => CloseCode::Tls,
164 _ => CloseCode::Other(code),
165 }
166 }
167}
168
169#[derive(Debug, Eq, PartialEq, Clone)]
170pub struct CloseReason {
172 pub code: CloseCode,
174 pub description: Option<String>,
176}
177
178impl From<CloseCode> for CloseReason {
179 fn from(code: CloseCode) -> Self {
180 CloseReason {
181 code,
182 description: None,
183 }
184 }
185}
186
187impl<T: Into<String>> From<(CloseCode, T)> for CloseReason {
188 fn from(info: (CloseCode, T)) -> Self {
189 CloseReason {
190 code: info.0,
191 description: Some(info.1.into()),
192 }
193 }
194}
195
196const WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
198
199pub fn hash_key(key: &[u8]) -> [u8; 28] {
203 let hash = {
204 use sha1::Digest as _;
205
206 let mut hasher = sha1::Sha1::new();
207
208 hasher.update(key);
209 hasher.update(WS_GUID);
210
211 hasher.finalize()
212 };
213
214 let mut hash_b64 = [0; 28];
215 #[allow(clippy::needless_borrow)] let n =
217 base64::engine::Engine::encode_slice(&base64::engine::general_purpose::STANDARD, hash, &mut hash_b64).unwrap();
218 assert_eq!(n, 28);
219
220 hash_b64
221}
222
223#[cfg(test)]
224mod test {
225 #![allow(unused_imports, unused_variables, dead_code)]
226 use super::*;
227
228 macro_rules! opcode_into {
229 ($from:expr => $opcode:pat) => {
230 match OpCode::from($from) {
231 e @ $opcode => {}
232 e => unreachable!("{:?}", e),
233 }
234 };
235 }
236
237 macro_rules! opcode_from {
238 ($from:expr => $opcode:pat) => {
239 let res: u8 = $from.into();
240 match res {
241 e @ $opcode => {}
242 e => unreachable!("{:?}", e),
243 }
244 };
245 }
246
247 #[test]
248 fn test_to_opcode() {
249 opcode_into!(0 => OpCode::Continue);
250 opcode_into!(1 => OpCode::Text);
251 opcode_into!(2 => OpCode::Binary);
252 opcode_into!(8 => OpCode::Close);
253 opcode_into!(9 => OpCode::Ping);
254 opcode_into!(10 => OpCode::Pong);
255 opcode_into!(99 => OpCode::Bad);
256 }
257
258 #[test]
259 fn test_from_opcode() {
260 opcode_from!(OpCode::Continue => 0);
261 opcode_from!(OpCode::Text => 1);
262 opcode_from!(OpCode::Binary => 2);
263 opcode_from!(OpCode::Close => 8);
264 opcode_from!(OpCode::Ping => 9);
265 opcode_from!(OpCode::Pong => 10);
266 }
267
268 #[test]
269 #[should_panic]
270 fn test_from_opcode_debug() {
271 opcode_from!(OpCode::Bad => 99);
272 }
273
274 #[test]
275 fn test_from_opcode_display() {
276 assert_eq!(format!("{}", OpCode::Continue), "CONTINUE");
277 assert_eq!(format!("{}", OpCode::Text), "TEXT");
278 assert_eq!(format!("{}", OpCode::Binary), "BINARY");
279 assert_eq!(format!("{}", OpCode::Close), "CLOSE");
280 assert_eq!(format!("{}", OpCode::Ping), "PING");
281 assert_eq!(format!("{}", OpCode::Pong), "PONG");
282 assert_eq!(format!("{}", OpCode::Bad), "BAD");
283 }
284
285 #[test]
286 fn test_hash_key() {
287 let hash = hash_key(b"hello xitca-web");
288 assert_eq!(&hash, b"z1coNb4wFSWTJ6aS4TQIOo6b9DA=");
289 }
290
291 #[test]
292 fn close_code_from_u16() {
293 assert_eq!(CloseCode::from(1000u16), CloseCode::Normal);
294 assert_eq!(CloseCode::from(1001u16), CloseCode::Away);
295 assert_eq!(CloseCode::from(1002u16), CloseCode::Protocol);
296 assert_eq!(CloseCode::from(1003u16), CloseCode::Unsupported);
297 assert_eq!(CloseCode::from(1006u16), CloseCode::Abnormal);
298 assert_eq!(CloseCode::from(1007u16), CloseCode::Invalid);
299 assert_eq!(CloseCode::from(1008u16), CloseCode::Policy);
300 assert_eq!(CloseCode::from(1009u16), CloseCode::Size);
301 assert_eq!(CloseCode::from(1010u16), CloseCode::Extension);
302 assert_eq!(CloseCode::from(1011u16), CloseCode::Error);
303 assert_eq!(CloseCode::from(1012u16), CloseCode::Restart);
304 assert_eq!(CloseCode::from(1013u16), CloseCode::Again);
305 assert_eq!(CloseCode::from(1015u16), CloseCode::Tls);
306 assert_eq!(CloseCode::from(2000u16), CloseCode::Other(2000));
307 }
308
309 #[test]
310 fn close_code_into_u16() {
311 assert_eq!(1000u16, Into::<u16>::into(CloseCode::Normal));
312 assert_eq!(1001u16, Into::<u16>::into(CloseCode::Away));
313 assert_eq!(1002u16, Into::<u16>::into(CloseCode::Protocol));
314 assert_eq!(1003u16, Into::<u16>::into(CloseCode::Unsupported));
315 assert_eq!(1006u16, Into::<u16>::into(CloseCode::Abnormal));
316 assert_eq!(1007u16, Into::<u16>::into(CloseCode::Invalid));
317 assert_eq!(1008u16, Into::<u16>::into(CloseCode::Policy));
318 assert_eq!(1009u16, Into::<u16>::into(CloseCode::Size));
319 assert_eq!(1010u16, Into::<u16>::into(CloseCode::Extension));
320 assert_eq!(1011u16, Into::<u16>::into(CloseCode::Error));
321 assert_eq!(1012u16, Into::<u16>::into(CloseCode::Restart));
322 assert_eq!(1013u16, Into::<u16>::into(CloseCode::Again));
323 assert_eq!(1015u16, Into::<u16>::into(CloseCode::Tls));
324 assert_eq!(2000u16, Into::<u16>::into(CloseCode::Other(2000)));
325 }
326}