1mod websocket_opcode;
2
3use std::convert::TryInto;
4use colored::Colorize;
5use super::color::Color;
6use websocket_opcode::WebSocketOpCode;
7
8const BITS_IN_BYTE: usize = 8;
9const BYTES_IN_DWORD: usize = 4;
10
11pub struct FormatStyle {
12 pub border_color: Color,
13 pub tick_mark_color: Color,
14 pub title_color: Color,
15 pub column_title_color: Color,
16 pub dword_title_color: Color,
17 pub notes_color: Color,
18 pub bit_color: Color,
19 pub unmasked_payload_bit_color: Color,
20 pub byte_value_color: Color,
21 pub data_value_color: Color,
22 pub summary_title_color: Color,
23 pub summary_value_color: Color,
24}
25
26impl FormatStyle {
27 pub fn new() -> FormatStyle {
28 FormatStyle {
29 border_color: Color::Cyan,
30 tick_mark_color: Color::Green,
31 title_color: Color::White,
32 column_title_color: Color::Green,
33 dword_title_color: Color::Green,
34 notes_color: Color::Magenta,
35 bit_color: Color::White,
36 unmasked_payload_bit_color: Color::Yellow,
37 byte_value_color: Color::Blue,
38 data_value_color: Color::Red,
39 summary_title_color: Color::Magenta,
40 summary_value_color: Color::Red,
41 }
42 }
43}
44
45#[derive(Debug)]
47#[derive(PartialEq)]
48pub enum PayloadLength {
49 Short(u8),
50 Medium(u16),
51 Long(u64)
52}
53
54impl std::fmt::Display for PayloadLength {
55 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
56 let out = match *self {
57 PayloadLength::Short(length) => format!("Short ({0} bytes)", length),
58 PayloadLength::Medium(length) => format!("Medium ({0} bytes)", length),
59 PayloadLength::Long(length) => format!("Long ({0} bytes)", length)
60 };
61 write!(f, "{}", out)
62 }
63}
64
65pub struct WebSocketFrame<'a> {
66 pub frame_len: u8,
67 pub is_payload_masked: bool,
68 pub payload_length: PayloadLength,
69 pub format_style: FormatStyle,
70 fin_bit: bool,
71 rsv1: bool,
72 rsv2: bool,
73 rsv3: bool,
74 opcode_bits: u8,
75 opcode: WebSocketOpCode,
76 mask_bit: bool,
77 payload_length_code: u8,
78 masking_key: [u8; 4],
79 masked_payload: &'a [u8],
80 unmasked_payload: Vec<u8>,
81 payload_chars: Vec<char>,
82}
83
84impl<'a> WebSocketFrame<'a> {
85 pub fn from_bytes(data: &Vec<u8>) -> WebSocketFrame {
91 const NUM_MASK_BYTES: usize = 4;
92
93 let frame_length: usize = data.len();
95
96 let opcode_bits = get_bits_from_byte(data[0], 0b00001111);
98
99 let is_payload_masked: bool = get_bit(data[1], 0);
101
102 let payload_length_code: u8 = get_bits_from_byte(data[1], 0b01111111);
104
105 let mut extension_data: Vec<u8> = Vec::new();
107 for ix in 0..8 {
108 if data.len() > ix + 2 {
109 extension_data.push(data[ix + 2]);
110 }
111 }
112
113 let payload_start_index = 6;
115
116 let num_payload_bytes: usize = frame_length - payload_start_index;
117
118 let masking_key: [u8; 4] = [data[2], data[3], data[4], data[5]];
120
121 let mut unmasked_payload: Vec<u8> = Vec::new();
123 let mut payload_chars: Vec<char> = Vec::new();
124 for i in 0..num_payload_bytes {
125 let byte: u8 = data[payload_start_index + i] ^ masking_key[i % NUM_MASK_BYTES];
126 unmasked_payload.push(byte); payload_chars.push(byte as char);
129 }
130
131 WebSocketFrame {
132 frame_len: data.len() as u8,
134 is_payload_masked,
136 payload_length: WebSocketFrame::get_payload_length(payload_length_code, extension_data),
138 format_style: FormatStyle::new(),
140 fin_bit: get_bit(data[0], 0),
142 rsv1: get_bit(data[0], 1),
144 rsv2: get_bit(data[0], 2),
146 rsv3: get_bit(data[0], 3),
148 opcode_bits,
150 opcode: WebSocketOpCode::from_bit_value(opcode_bits),
152 mask_bit: is_payload_masked,
154 payload_length_code,
156 masking_key,
158 masked_payload: &data[payload_start_index..data.len()],
160 unmasked_payload,
162 payload_chars,
164 }
165 }
166
167 pub fn format(self: &WebSocketFrame<'a>) -> String {
173 let mut result = self.format_header();
174
175 result.push_str(&self.format_first_two_dwords());
176
177 let payload_length: usize =
178 match self.payload_length {
179 PayloadLength::Short(length) => length.into(),
180 PayloadLength::Medium(length) => length.into(),
181 PayloadLength::Long(length) => length.try_into().unwrap()
182 };
183
184 let dword_from =
185 match self.payload_length {
186 PayloadLength::Short(_) => 3,
187 PayloadLength::Medium(_) => 3,
188 PayloadLength::Long(_) => 4
189 };
190
191 let remaining_payload_dwords = (payload_length - 2).div_euclid(BYTES_IN_DWORD.into());
193 for i in 0..remaining_payload_dwords {
194 let from_byte_ix = (i * BYTES_IN_DWORD) + 2;
195 let to_byte_ix = BYTES_IN_DWORD * from_byte_ix;
196 result.push_str(&self.format_payload_dword_row(
197 from_byte_ix,
198 to_byte_ix,
199 i + dword_from,
200 i + 2
201 ));
202 }
203 let remaining_bytes: usize = (payload_length - 2).rem_euclid(BYTES_IN_DWORD);
205 if remaining_bytes > 0 {
206 let from_byte_ix: usize = (remaining_payload_dwords * BYTES_IN_DWORD) + 2;
207 let to_byte_ix: usize = from_byte_ix + remaining_bytes;
208 result.push_str(&self.format_payload_dword_row(
209 from_byte_ix,
210 to_byte_ix,
211 remaining_payload_dwords + 3,
212 (remaining_payload_dwords * 2) + 2,
213 ));
214 }
215
216 result
217 }
218
219 fn format_header(self: &WebSocketFrame<'a>) -> String {
225 let border_color = |s: &str| s.color(self.format_style.border_color.to_string());
227 let tick_color = |s: &str| s.color(self.format_style.tick_mark_color.to_string());
228 let title_color = |s: &str| s.color(self.format_style.title_color.to_string());
229 let column_title_color = |s: &str| s.color(self.format_style.column_title_color.to_string());
230
231 let mut result: String =
233 format!(
234 "{0:15}{1}\n",
235 "",
236 border_color("+---------------+---------------+---------------+---------------+")
237 );
238
239 result.push_str(
241 &format!(
242 "{0:2}{2}{0:3}{1}{3:^15}{1}{4:^15}{1}{5:^15}{1}{6:^15}{1}\n",
243 "",
244 border_color("|"),
245 title_color("Frame Data"),
246 column_title_color("Byte 1"),
247 column_title_color("Byte 2"),
248 column_title_color("Byte 3"),
249 column_title_color("Byte 4")
250 )
251 );
252 result.push_str(
254 &format!(
255 "{0:2}{1}\n",
256 " ",
257 format!(
258 "{0:^10}{1:3}{2}",
259 if self.is_payload_masked { title_color("(Masked)") } else { title_color("(Unmasked)") },
260 "",
261 border_color("+---------------+---------------+---------------+---------------+"),
262 )
263 )
264 );
265 result.push_str(
267 &format!(
268 "{0:2}{1:^10}{0:3}{2}{3}{0:14}{2}{0:4}{4}{0:10}{2}{0:8}{5}{0:6}{2}{0:12}{6}{0:2}{2}\n",
269 "",
270 format!("{:?}", self.payload_length),
271 border_color("|"),
272 tick_color("0"),
273 tick_color("1"),
274 tick_color("2"),
275 tick_color("3")
276 )
277 );
278 result.push_str(
280 &format!(
281 "{0:15}{1}{2}{1}{3}{1}{4}{1}{5}{1}\n",
282 "",
283 border_color("|"),
284 tick_color("0 1 2 3 4 5 6 7"),
285 tick_color("8 9 0 1 2 3 4 5"),
286 tick_color("6 7 8 9 0 1 2 3"),
287 tick_color("4 5 6 7 8 9 0 1")
288 )
289 );
290
291 result
292 }
293
294 fn format_first_two_dwords(
295 self: &WebSocketFrame<'a>
296 ) -> String {
297 let border_color = |s: &str| s.color(self.format_style.border_color.to_string());
299 let dword_title_color = |s: &str| s.color(self.format_style.dword_title_color.to_string());
300 let notes_color = |s: &str| s.color(self.format_style.notes_color.to_string());
301 let bit_color = |s: &str| s.color(self.format_style.bit_color.to_string());
302 let unmasked_payload_bit_color = |s: &str| s.color(self.format_style.unmasked_payload_bit_color.to_string());
303 let byte_value_color = |s: &str| s.color(self.format_style.byte_value_color.to_string());
304 let data_value_color = |s: &str| s.color(self.format_style.data_value_color.to_string());
305
306 let mut result: String =
308 format!(
309 "{0:7}{1}\n",
310 "",
311 border_color("+-------+---------------+---------------+---------------+---------------+")
312 );
313 result.push_str(
315 &format!(
316 "{0:7}{1}{2:^7}{1}{3}{1}{4}{1}{5}{1}{6}{1}{7}{1}{8}{1}{9}{1}{10}{1}{11}{1}\n",
317 "",
318 border_color("|"),
319 dword_title_color("DWORD"),
320 bit_color(bit_str(self.fin_bit)),
321 bit_color(bit_str(self.rsv1)),
322 bit_color(bit_str(self.rsv2)),
323 bit_color(bit_str(self.rsv3)),
324 bit_color(&byte_str(self.opcode_bits, 4)),
325 bit_color(bit_str(self.mask_bit)),
326 bit_color(&byte_str(self.payload_length_code, 7)),
327 bit_color(&byte_str(self.masking_key[0], 8)),
328 bit_color(&byte_str(self.masking_key[1], 8)),
329 )
330 );
331 result.push_str(
333 &format!(
334 "{0:7}{1}{2:^7}{1}{3}{1}{4}{1}{4}{1}{4}{1}{6:^7}{1}{5}{1}{7:^13}{1}{0:31}{1}\n",
335 "",
336 border_color("|"),
337 dword_title_color("1"),
338 notes_color("F"),
339 notes_color("R"),
340 notes_color("M"),
341 data_value_color(&format!("{:?}",self.opcode)),
342 data_value_color(&format!("{:?}", self.payload_length)),
343 )
344 );
345 result.push_str(
347 &format!(
348 "{0:7}{1}{0:7}{1}{2}{1}{3}{1}{3}{1}{3}{1}{4:7}{1}{5}{1}{6:^13}{1}{7:^31}{1}\n",
349 "",
350 border_color("|"),
351 notes_color("I"),
352 notes_color("S"),
353 notes_color("op code"),
354 notes_color("A"),
355 notes_color("Payload len"),
356 notes_color("Masking-key (part 1)"),
357 )
358 );
359 result.push_str(
361 &format!(
362 "{0:7}{1}{0:7}{1}{2}{1}{3}{1}{3}{1}{3}{1}{4:^7}{1}{5}{1}{6:^13}{1}{0:31}{1}\n",
363 "",
364 border_color("|"),
365 notes_color("N"),
366 notes_color("V"),
367 notes_color("(4 b)"),
368 notes_color("S"),
369 notes_color("(7 bits)"),
370 )
371 );
372 result.push_str(
374 &format!(
375 "{0:7}{1}{0:7}{1}{0:1}{1}{2}{1}{3}{1}{4}{1}{0:7}{1}{5}{1}{0:13}{1}{0:31}{1}\n",
376 "",
377 border_color("|"),
378 notes_color("1"),
379 notes_color("2"),
380 notes_color("3"),
381 notes_color("K"),
382 )
383 );
384 result.push_str(
386 &format!(
387 "{0:7}{1}\n",
388 "",
389 border_color("+-------+-+-+-+-+-------+-+-------------+-------------------------------+")
390 )
391 );
392 result.push_str(
394 &format!(
395 "{0:7}{1}{2:^7}{1}{3:^15}{1}{4:^15}{1}{5:^15}{1}{6:^15}{1}\n",
396 "",
397 border_color("|"),
398 dword_title_color("DWORD"),
399 bit_color(&byte_str(self.masking_key[2], 8)),
400 bit_color(&byte_str(self.masking_key[3], 8)),
401 bit_color(&byte_str(self.masked_payload[0], 8)),
402 bit_color(&byte_str(self.masked_payload[1], 8)),
403 )
404 );
405 result.push_str(
407 &format!(
408 "{0:7}{1}{2:^7}{1}{0:^31}{1}{0:1}{4:>5}{0:6}{3}{0:2}{5:>5}{0:6}{1}\n",
409 "",
410 border_color("|"),
411 dword_title_color("2"),
412 notes_color("MASKED"),
413 byte_value_color(&format!("({})", self.masked_payload[0])),
414 byte_value_color(&format!("({})", self.masked_payload[1])),
415 )
416 );
417 result.push_str(
419 &format!(
420 "{0:7}{1}{0:7}{1}{2:^31}{1}{3:^15}{1}{4:^15}{1}\n",
421 "",
422 border_color("|"),
423 notes_color("Masking-key (part 2)"),
424 unmasked_payload_bit_color(&byte_str(self.unmasked_payload[0], 8)),
425 unmasked_payload_bit_color(&byte_str(self.unmasked_payload[1], 8)),
426 )
427 );
428 result.push_str(
430 &format!(
431 "{0:7}{1}{0:7}{1}{2:^31}{1}{0:1}{4:>5}{0:1}{5:3}{0:1}{3}{0:1}{6:>5}{0:1}{7:3}{0:2}{1}\n",
432 "",
433 border_color("|"),
434 notes_color("(16 bits)"),
435 notes_color("UNMASKED"),
436 byte_value_color(&format!("({})", self.unmasked_payload[0])),
437 data_value_color(&format!("'{0}'", &self.payload_chars[0])),
438 byte_value_color(&format!("({})", self.unmasked_payload[1])),
439 data_value_color(&format!("'{0}'", &self.payload_chars[1]))
440 )
441 );
442 result.push_str(
444 &format!(
445 "{0:7}{1}{0:7}{1}{0:^31}{1}{2:^31}{1}\n",
446 "",
447 border_color("|"),
448 notes_color("Payload Data (part 1)")
449 )
450 );
451 result.push_str(
453 &format!(
454 "{0:7}{1}\n",
455 "",
456 border_color("+-------+-------------------------------+-------------------------------+"),
457 )
458 );
459
460 result
461 }
462
463 fn format_payload_dword_row(
468 self: &WebSocketFrame<'a>,
469 from_byte_ix: usize,
470 to_byte_ix: usize,
471 dword_number: usize,
472 part_number: usize,
473 ) -> String {
474 let border_color = |s: &str| s.color(self.format_style.border_color.to_string());
476 let dword_title_color = |s: &str| s.color(self.format_style.dword_title_color.to_string());
477 let notes_color = |s: &str| s.color(self.format_style.notes_color.to_string());
478 let bit_color = |s: &str| s.color(self.format_style.bit_color.to_string());
479 let unmasked_payload_bit_color = |s: &str| s.color(self.format_style.unmasked_payload_bit_color.to_string());
480 let data_value_color = |s: &str| s.color(self.format_style.data_value_color.to_string());
481 let byte_value_color = |s: &str| s.color(self.format_style.byte_value_color.to_string());
482
483 let mut result: String = String::from("");
484
485 let num_bytes = to_byte_ix - from_byte_ix;
487
488 let masked_bits: &[u8] = &self.masked_payload[from_byte_ix..to_byte_ix];
489 let unmasked_bits: &[u8] = &self.unmasked_payload[from_byte_ix..to_byte_ix];
490 let payload_data: &[char] = &self.payload_chars[from_byte_ix..to_byte_ix];
491
492 if num_bytes < 1 || num_bytes > 4 {
494 return String::from(
495 format!("ERROR: Cannot print dword row. Illegal byte indexes provided. from_byte_ix: {} to_byte_ix: {}",
496 from_byte_ix,
497 to_byte_ix));
498 }
499
500 result.push_str(
502 &format!(
503 "{0:7}{1}{2:^7}{1}",
504 "",
505 border_color("|"),
506 dword_title_color("DWORD"),
507 )
508 );
509 result.push_str(
510 &(0..num_bytes)
511 .map(|i| format!(
512 "{1}{0}",
513 border_color("|"),
514 bit_color(&byte_str(masked_bits[i], BITS_IN_BYTE as u8))))
515 .collect::<String>(),
516 );
517 result.push_str("\n");
518
519 result.push_str(
521 &format!(
522 "{0:7}{1}{2:^7}{1}",
523 "",
524 border_color("|"),
525 dword_title_color(&dword_number.to_string())
526 )
527 );
528 match num_bytes {
529 1 => result.push_str(&format!(
530 "{0:1}{3:>5}{0:5}{2}{0:1}{1}",
531 "",
532 border_color("|"),
533 notes_color("MSK"),
534 byte_value_color(&format!("({})", masked_bits[0]))
535 )),
536 2 => result.push_str(&format!(
537 "{0:1}{3:>5}{0:6}{2}{0:2}{4:>5}{0:6}{1}",
538 "",
539 border_color("|"),
540 notes_color("MASKED"),
541 byte_value_color(&format!("({})", masked_bits[0])),
542 byte_value_color(&format!("({})", masked_bits[1]))
543 )),
544 3 => result.push_str(&format!(
545 "{0:1}{4:>5}{0:6}{2}{0:2}{5:>5}{0:6}{1}{0:1}{6:>5}{0:5}{3}{0:1}{1}",
546 "",
547 border_color("|"),
548 notes_color("MASKED"),
549 notes_color("MSK"),
550 byte_value_color(&format!("({})", masked_bits[0])),
551 byte_value_color(&format!("({})", masked_bits[1])),
552 byte_value_color(&format!("({})", masked_bits[2]))
553 )),
554 4 => result.push_str(&format!(
555 "{0:1}{4:>5}{0:6}{2}{0:2}{5:>5}{0:6}{1}{0:1}{6:>5}{0:6}{3}{0:2}{7:>5}{0:6}{1}",
556 "",
557 border_color("|"),
558 notes_color("MASKED"),
559 notes_color("MASKED"),
560 byte_value_color(&format!("({})", masked_bits[0])),
561 byte_value_color(&format!("({})", masked_bits[1])),
562 byte_value_color(&format!("({})", masked_bits[2])),
563 byte_value_color(&format!("({})", masked_bits[3]))
564 )),
565 _ => {}
566 }
567 result.push_str("\n");
568
569 result.push_str(
571 &format!(
572 "{0:7}{1}{0:7}{1}",
573 "",
574 border_color("|")
575 )
576 );
577 result.push_str(
578 &(0..num_bytes)
579 .map(|i| format!("{1}{0}", border_color("|"), unmasked_payload_bit_color(&byte_str(unmasked_bits[i], BITS_IN_BYTE as u8))))
580 .collect::<String>(),
581 );
582 result.push_str("\n");
583
584 result.push_str(&format!("{0:7}{1}{0:7}{1}", "", border_color("|")));
586 match num_bytes {
587 1 => result.push_str(&format!(
588 "{0:1}{3:>5}{0:1}{4}{0:1}{2}{0:1}{1}",
589 "",
590 border_color("|"),
591 notes_color("UNM"),
592 byte_value_color(&format!("({})", unmasked_bits[0])),
593 data_value_color(&format!("'{0}'", payload_data[0]))
594 )),
595 2 => result.push_str(&format!(
596 "{0:1}{3:>5}{0:1}{4:3}{0:1}{2}{0:1}{5:>5}{0:1}{6:3}{0:2}{1}",
597 "",
598 border_color("|"),
599 notes_color("UNMASKED"),
600 byte_value_color(&format!("({})", unmasked_bits[0])),
601 data_value_color(&format!("'{}'", payload_data[0])),
602 byte_value_color(&format!("({})", unmasked_bits[1])),
603 data_value_color(&format!("'{}'", payload_data[1]))
604 )),
605 3 => result.push_str(&format!(
606 "{0:1}{4:>5}{0:1}{5:3}{0:1}{2}{0:1}{6:>5}{0:1}{7:3}{0:2}{1}{0:1}{8:>5}{0:1}{9:3}{0:1}{3}{0:1}{1}",
607 "",
608 border_color("|"),
609 notes_color("UNMASKED"),
610 notes_color("UNM"),
611 byte_value_color(&format!("({})", unmasked_bits[0])),
612 data_value_color(&format!("'{}'", payload_data[0])),
613 byte_value_color(&format!("({})", unmasked_bits[1])),
614 data_value_color(&format!("'{}'", payload_data[1])),
615 byte_value_color(&format!("({})", unmasked_bits[2])),
616 data_value_color(&format!("'{}'", payload_data[2])),
617 )),
618 4 => result.push_str(&format!(
619 "{0:1}{3:>5}{0:1}{4:3}{0:1}{2}{0:1}{5:>5}{0:1}{6:3}{0:2}{1}{0:1}{7:>5}{0:1}{8:3}{0:1}{2}{0:1}{9:>5}{0:1}{10:3}{0:2}{1}",
620 "",
621 border_color("|"),
622 notes_color("UNMASKED"),
623 byte_value_color(&format!("({})", unmasked_bits[0])),
624 data_value_color(&format!("'{}'", payload_data[0])),
625 byte_value_color(&format!("({})", unmasked_bits[1])),
626 data_value_color(&format!("'{}'", payload_data[1])),
627 byte_value_color(&format!("({})", unmasked_bits[2])),
628 data_value_color(&format!("'{}'", payload_data[2])),
629 byte_value_color(&format!("({})", unmasked_bits[3])),
630 data_value_color(&format!("'{}'", payload_data[3])),
631 )),
632 _ => {}
633 }
634 result.push_str("\n");
635
636 result.push_str(&format!("{0:7}{1}{0:7}{1}", "", border_color("|")));
638 match num_bytes {
639 1 => result.push_str(&format!(
640 "{1:^15}{0}",
641 border_color("|"),
642 notes_color(&format!("Payload pt {}", part_number))
643 )),
644 2 => result.push_str(&format!(
645 "{1:^31}{0}",
646 border_color("|"),
647 notes_color(&format!("Payload Data (part {})", part_number))
648 )),
649 3 => result.push_str(&format!(
650 "{1:^47}{0}",
651 border_color("|"),
652 notes_color(&format!("Payload Data (part {})", part_number)),
653 )),
654 4 => result.push_str(&format!(
655 "{1:^63}{0}",
656 border_color("|"),
657 notes_color(&format!("Payload Data (part {})", part_number)),
658 )),
659 _ => {}
660 }
661 result.push_str("\n");
662
663 result.push_str(&format!("{0:7}{1}", "", border_color("+-------+")));
665 result.push_str(
666 &(0..num_bytes)
667 .map(|_| border_color("---------------+").to_string())
668 .collect::<String>(),
669 );
670 result.push_str("\n");
671
672 result
673 }
674
675 fn get_payload_length(code: u8, ext_bytes: Vec<u8>) -> PayloadLength {
684 if code <= 125 {
686 return PayloadLength::Short(code);
687 }
688 if code == 126 {
690 return PayloadLength::Medium(u16::from_le_bytes([ext_bytes[0], ext_bytes[1]]));
691 }
692 if code == 127 {
694 return PayloadLength::Long(u64::from_le_bytes([ext_bytes[0], ext_bytes[1], ext_bytes[2], ext_bytes[3], ext_bytes[4], ext_bytes[5], ext_bytes[6], ext_bytes[7]]));
695 }
696 panic!("ERROR: Unable to determine payload length from code: {}", code);
698 }
699}
700
701fn get_bits_from_byte(byte: u8, mask: u8) -> u8 {
702 byte & mask
703}
704
705fn byte_str<'a>(byte: u8, num_bits: u8) -> String {
712 let mut result: String = String::from("");
713 result.push_str(
714 &(8 - num_bits..8)
715 .map(|i| format!("{} ", bit_str(get_bit(byte, i))))
716 .collect::<String>(),
717 );
718 result.trim().to_string()
719}
720
721fn bit_str<'a>(bit: bool) -> &'a str {
722 if bit == true {
723 "1"
724 } else {
725 "0"
726 }
727}
728
729fn get_bit(byte: u8, bit_position: u8) -> bool {
730 match bit_position {
731 0 => byte & 0b10000000 != 0,
732 1 => byte & 0b01000000 != 0,
733 2 => byte & 0b00100000 != 0,
734 3 => byte & 0b00010000 != 0,
735 4 => byte & 0b00001000 != 0,
736 5 => byte & 0b00000100 != 0,
737 6 => byte & 0b00000010 != 0,
738 7 => byte & 0b00000001 != 0,
739 _ => false,
740 }
741}
742
743#[cfg(test)]
746mod tests {
747 use super::*;
748
749 #[test]
750 fn test_short_masked_text_frame() {
751 let bytes = base64::decode("gYR7q0rdD845qQ==").unwrap();
752
753 let frame = WebSocketFrame::from_bytes(&bytes);
754 let expected = " \u{1b}[36m+---------------+---------------+---------------+---------------+\u{1b}[0m\n \u{1b}[37mFrame Data\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[32m Byte 1 \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[32m Byte 2 \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[32m Byte 3 \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[32m Byte 4 \u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[37m (Masked) \u{1b}[0m \u{1b}[36m+---------------+---------------+---------------+---------------+\u{1b}[0m\n Short(4) \u{1b}[36m|\u{1b}[0m\u{1b}[32m0\u{1b}[0m \u{1b}[36m|\u{1b}[0m \u{1b}[32m1\u{1b}[0m \u{1b}[36m|\u{1b}[0m \u{1b}[32m2\u{1b}[0m \u{1b}[36m|\u{1b}[0m \u{1b}[32m3\u{1b}[0m \u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m\u{1b}[32m0 1 2 3 4 5 6 7\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[32m8 9 0 1 2 3 4 5\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[32m6 7 8 9 0 1 2 3\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[32m4 5 6 7 8 9 0 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m+-------+---------------+---------------+---------------+---------------+\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m\u{1b}[32m DWORD \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0 0 0 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0 0 0 0 1 0 0\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0 1 1 1 1 0 1 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m1 0 1 0 1 0 1 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m\u{1b}[32m 1 \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mF\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mR\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mR\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mR\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[31m Text \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mM\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[31m Short(4) \u{1b}[0m\u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[35mI\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mS\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mS\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mS\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mop code\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mA\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35m Payload len \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35m Masking-key (part 1) \u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[35mN\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mV\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mV\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mV\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35m (4 b) \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35mS\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35m (7 bits) \u{1b}[0m\u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[35m1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35m2\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[35m3\u{1b}[0m\u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[35mK\u{1b}[0m\u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\n \u{1b}[36m+-------+-+-+-+-+-------+-+-------------+-------------------------------+\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m\u{1b}[32m DWORD \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0 1 0 0 1 0 1 0\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m1 1 0 1 1 1 0 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0 0 0 0 1 1 1 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m1 1 0 0 1 1 1 0\u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m\u{1b}[32m 2 \u{1b}[0m\u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m \u{1b}[34m (15)\u{1b}[0m \u{1b}[35mMASKED\u{1b}[0m \u{1b}[34m(206)\u{1b}[0m \u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[35m Masking-key (part 2) \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[33m0 1 1 1 0 1 0 0\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[33m0 1 1 0 0 1 0 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[35m (16 bits) \u{1b}[0m\u{1b}[36m|\u{1b}[0m \u{1b}[34m(116)\u{1b}[0m \u{1b}[31m\'t\'\u{1b}[0m \u{1b}[35mUNMASKED\u{1b}[0m \u{1b}[34m(101)\u{1b}[0m \u{1b}[31m\'e\'\u{1b}[0m \u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[35m Payload Data (part 1) \u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m+-------+-------------------------------+-------------------------------+\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m\u{1b}[32m DWORD \u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m0 0 1 1 1 0 0 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[37m1 0 1 0 1 0 0 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m\u{1b}[32m 3 \u{1b}[0m\u{1b}[36m|\u{1b}[0m \u{1b}[34m (57)\u{1b}[0m \u{1b}[35mMASKED\u{1b}[0m \u{1b}[34m(169)\u{1b}[0m \u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[33m0 1 1 1 0 0 1 1\u{1b}[0m\u{1b}[36m|\u{1b}[0m\u{1b}[33m0 1 1 1 0 1 0 0\u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m \u{1b}[34m(115)\u{1b}[0m \u{1b}[31m\'s\'\u{1b}[0m \u{1b}[35mUNMASKED\u{1b}[0m \u{1b}[34m(116)\u{1b}[0m \u{1b}[31m\'t\'\u{1b}[0m \u{1b}[36m|\u{1b}[0m\n \u{1b}[36m|\u{1b}[0m \u{1b}[36m|\u{1b}[0m\u{1b}[35m Payload Data (part 2) \u{1b}[0m\u{1b}[36m|\u{1b}[0m\n \u{1b}[36m+-------+\u{1b}[0m\u{1b}[36m---------------+\u{1b}[0m\u{1b}[36m---------------+\u{1b}[0m\n";
755
756 assert_eq!(frame.format(), expected);
759 }
760}
761
762