1use std::{cmp::min, io, str};
2
3use bytes::{Buf, BufMut, BytesMut};
4
5use super::{
6 mask::apply_mask,
7 proto::{CloseCode, CloseReason, OpCode},
8 ProtocolError,
9};
10
11#[derive(Debug)]
13pub struct Parser;
14
15const FIN_MASK: u8 = 0x80;
17
18const RSV_MASK: u8 = 0b0111_0000;
20
21const OPCODE_MASK: u8 = 0x0F;
23
24const MASK_BIT: u8 = 0x80;
26
27const PAYLOAD_LEN_MASK: u8 = 0x7F;
29
30const EXT_LEN_U16: u8 = 126;
32
33const EXT_LEN_U64: u8 = 127;
35
36const MAX_CONTROL_FRAME_PAYLOAD: usize = 125;
38
39const U16_PAYLOAD_MAX: usize = u16::MAX as usize;
41
42const U64_PAYLOAD_MAX: u64 = i64::MAX as u64;
44
45const MASK_LEN: usize = 4;
47
48fn payload_len_to_usize(len: u64) -> Result<usize, ProtocolError> {
54 usize::try_from(len).map_err(|_| ProtocolError::Overflow)
55}
56
57fn payload_len_msb_set(len: u64) -> bool {
62 len > U64_PAYLOAD_MAX
63}
64
65impl Parser {
66 fn parse_metadata(
67 src: &[u8],
68 server: bool,
69 ) -> Result<Option<(usize, bool, OpCode, usize, Option<[u8; 4]>)>, ProtocolError> {
70 let chunk_len = src.len();
71
72 let mut idx = 2;
73 if chunk_len < 2 {
74 return Ok(None);
75 }
76
77 let first = src[0];
78 let second = src[1];
79 let finished = first & FIN_MASK != 0;
80
81 if first & RSV_MASK != 0 {
83 return Err(ProtocolError::Io(io::Error::new(
85 io::ErrorKind::InvalidData,
86 "Received a frame with non-zero reserved bits",
87 )));
88 }
89
90 let masked = second & MASK_BIT != 0;
92 if !masked && server {
93 return Err(ProtocolError::UnmaskedFrame);
94 } else if masked && !server {
95 return Err(ProtocolError::MaskedFrame);
96 }
97
98 let opcode = OpCode::from(first & OPCODE_MASK);
100
101 if let OpCode::Bad = opcode {
102 return Err(ProtocolError::InvalidOpcode(first & OPCODE_MASK));
103 }
104
105 let len = second & PAYLOAD_LEN_MASK;
106 let length = if len == EXT_LEN_U16 {
107 if chunk_len < 4 {
108 return Ok(None);
109 }
110 let len = usize::from(u16::from_be_bytes(
111 TryFrom::try_from(&src[idx..idx + 2]).unwrap(),
112 ));
113 idx += 2;
114 len
115 } else if len == EXT_LEN_U64 {
116 if chunk_len < 10 {
117 return Ok(None);
118 }
119 let len = u64::from_be_bytes(TryFrom::try_from(&src[idx..idx + 8]).unwrap());
120 idx += 8;
121
122 if payload_len_msb_set(len) {
123 return Err(ProtocolError::Overflow);
126 }
127
128 payload_len_to_usize(len)?
129 } else {
130 len as usize
131 };
132
133 let mask = if server {
134 if chunk_len < idx + MASK_LEN {
135 return Ok(None);
136 }
137
138 let mask = TryFrom::try_from(&src[idx..idx + MASK_LEN]).unwrap();
139
140 idx += MASK_LEN;
141
142 Some(mask)
143 } else {
144 None
145 };
146
147 Ok(Some((idx, finished, opcode, length, mask)))
148 }
149
150 pub fn parse(
152 src: &mut BytesMut,
153 server: bool,
154 max_size: usize,
155 ) -> Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError> {
156 let (idx, finished, opcode, length, mask) = match Parser::parse_metadata(src, server)? {
158 None => return Ok(None),
159 Some(res) => res,
160 };
161
162 let frame_len = match idx.checked_add(length) {
166 Some(len) => len,
167 None => return Err(ProtocolError::Overflow),
168 };
169
170 if src.len() < frame_len {
172 let min_length = min(length, max_size);
173 let required_cap = match idx.checked_add(min_length) {
174 Some(cap) => cap,
175 None => return Err(ProtocolError::Overflow),
176 };
177
178 if src.capacity() < required_cap {
179 src.reserve(required_cap - src.capacity());
180 }
181 return Ok(None);
182 }
183
184 src.advance(idx);
186
187 if length > max_size {
189 src.advance(length);
191 return Err(ProtocolError::Overflow);
192 }
193
194 if length == 0 {
196 return Ok(Some((finished, opcode, None)));
197 }
198
199 let mut data = src.split_to(length);
200
201 match opcode {
203 OpCode::Ping | OpCode::Pong | OpCode::Close if length > MAX_CONTROL_FRAME_PAYLOAD => {
204 return Err(ProtocolError::InvalidLength(length));
205 }
206 _ => {}
207 }
208
209 if let Some(mask) = mask {
211 apply_mask(&mut data, mask);
212 }
213
214 Ok(Some((finished, opcode, Some(data))))
215 }
216
217 #[deprecated(
223 since = "3.13.5",
224 note = "Use `Parser::try_parse_close_payload` instead."
225 )]
226 pub fn parse_close_payload(payload: &[u8]) -> Option<CloseReason> {
227 if payload.len() >= 2 {
228 let raw_code = u16::from_be_bytes(TryFrom::try_from(&payload[..2]).unwrap());
229 let code = CloseCode::from(raw_code);
230 let description = if payload.len() > 2 {
231 Some(String::from_utf8_lossy(&payload[2..]).into())
232 } else {
233 None
234 };
235 Some(CloseReason { code, description })
236 } else {
237 None
238 }
239 }
240
241 pub fn try_parse_close_payload(payload: &[u8]) -> Result<Option<CloseReason>, ProtocolError> {
253 if payload.len() == 1 {
255 return Err(ProtocolError::InvalidLength(payload.len()));
256 }
257
258 if payload.len() >= 2 {
259 let raw_code = u16::from_be_bytes(
260 payload[..2]
261 .try_into()
262 .expect("Payload length should be checked before parsing"),
263 );
264
265 if !matches!(raw_code, 1000..=1003 | 1007..=1014 | 3000..=4999) {
269 return Err(ProtocolError::BadOpCode);
272 }
273
274 if payload.len() > 2 {
275 str::from_utf8(&payload[2..]).map_err(|_| {
278 ProtocolError::BadOpCode
281 })?;
282 }
283 }
284
285 #[expect(deprecated)]
286 Ok(Self::parse_close_payload(payload))
287 }
288
289 pub fn write_message<B: AsRef<[u8]>>(
291 dst: &mut BytesMut,
292 pl: B,
293 op: OpCode,
294 fin: bool,
295 mask: bool,
296 ) {
297 let payload = pl.as_ref();
298 let one = if fin {
299 FIN_MASK | u8::from(op)
300 } else {
301 u8::from(op)
302 };
303 let payload_len = payload.len();
304 let (two, p_len) = if mask {
305 (MASK_BIT, payload_len + MASK_LEN)
306 } else {
307 (0, payload_len)
308 };
309
310 if payload_len < EXT_LEN_U16 as usize {
311 dst.reserve(p_len + 2);
312 dst.put_slice(&[one, two | payload_len as u8]);
313 } else if payload_len <= U16_PAYLOAD_MAX {
314 dst.reserve(p_len + 4);
315 dst.put_slice(&[one, two | EXT_LEN_U16]);
316 dst.put_u16(payload_len as u16);
317 } else {
318 dst.reserve(p_len + 10);
319 dst.put_slice(&[one, two | EXT_LEN_U64]);
320 dst.put_u64(payload_len as u64);
321 };
322
323 if mask {
324 let mask = rand::random::<[u8; 4]>();
325 dst.put_slice(mask.as_ref());
326 dst.put_slice(payload.as_ref());
327 let pos = dst.len() - payload_len;
328 apply_mask(&mut dst[pos..], mask);
329 } else {
330 dst.put_slice(payload.as_ref());
331 }
332 }
333
334 #[inline]
336 pub fn write_close(dst: &mut BytesMut, reason: Option<CloseReason>, mask: bool) {
337 let payload = match reason {
338 None => Vec::new(),
339 Some(reason) => {
340 let mut payload = Into::<u16>::into(reason.code).to_be_bytes().to_vec();
341 if let Some(description) = reason.description {
342 payload.extend(description.as_bytes());
343 }
344 payload
345 }
346 };
347
348 Parser::write_message(dst, payload, OpCode::Close, true, mask)
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use bytes::Bytes;
355
356 use super::*;
357
358 struct F {
359 finished: bool,
360 opcode: OpCode,
361 payload: Bytes,
362 }
363
364 fn is_none(frm: &Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError>) -> bool {
365 matches!(*frm, Ok(None))
366 }
367
368 fn extract(frm: Result<Option<(bool, OpCode, Option<BytesMut>)>, ProtocolError>) -> F {
369 match frm {
370 Ok(Some((finished, opcode, payload))) => F {
371 finished,
372 opcode,
373 payload: payload
374 .map(|b| b.freeze())
375 .unwrap_or_else(|| Bytes::from("")),
376 },
377 _ => unreachable!("error"),
378 }
379 }
380
381 #[test]
383 fn test_parse_rejects_64_bit_length_with_msb_set() {
384 let mut buf = BytesMut::from(&[0x81u8, 127][..]);
385 buf.extend_from_slice(&(U64_PAYLOAD_MAX + 1).to_be_bytes());
386
387 assert!(
388 matches!(
389 Parser::parse(&mut buf, false, 1024),
390 Err(ProtocolError::Overflow)
391 ),
392 "a 64-bit payload length with the MSB set must be rejected, not treated as incomplete"
393 );
394 assert_eq!(buf.len(), 10);
396 }
397
398 #[cfg(target_pointer_width = "64")]
401 #[test]
402 fn test_parse_accepts_largest_legal_64_bit_length() {
403 let mut buf = BytesMut::from(&[0x81u8, 127][..]);
404 buf.extend_from_slice(&U64_PAYLOAD_MAX.to_be_bytes());
405
406 assert!(
410 is_none(&Parser::parse(&mut buf, false, 1024)),
411 "the largest legal 64-bit length must be incomplete, not rejected"
412 );
413 assert_eq!(buf.len(), 10);
415 }
416
417 #[test]
420 fn test_payload_len_msb_set() {
421 assert!(!payload_len_msb_set(0));
422 assert!(!payload_len_msb_set(U64_PAYLOAD_MAX));
423 assert!(payload_len_msb_set(1 << 63));
424 assert!(payload_len_msb_set(u64::MAX));
425 }
426
427 #[cfg(target_pointer_width = "32")]
430 #[test]
431 fn test_parse_rejects_64_bit_length_beyond_addressable_range() {
432 let mut buf = BytesMut::from(&[0x81u8, 127][..]);
433 buf.extend_from_slice(&0x0000_0001_0000_0000u64.to_be_bytes());
434 buf.extend_from_slice(b"ABCD");
435
436 assert!(
437 matches!(
438 Parser::parse(&mut buf, false, 1024),
439 Err(ProtocolError::Overflow)
440 ),
441 "a length that does not fit in usize must be rejected, not truncated to 0"
442 );
443 assert_eq!(buf.len(), 14);
444 }
445
446 #[cfg(target_pointer_width = "32")]
450 #[test]
451 fn test_parse_rejects_length_that_overflows_frame_len() {
452 let mut buf = BytesMut::from(&[0x81u8, 127][..]);
453 buf.extend_from_slice(&(u32::MAX as u64).to_be_bytes());
454
455 assert!(matches!(
456 Parser::parse(&mut buf, false, 1024),
457 Err(ProtocolError::Overflow)
458 ));
459 }
460
461 #[test]
462 fn test_parse() {
463 let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);
464 assert!(is_none(&Parser::parse(&mut buf, false, 1024)));
465
466 let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);
467 buf.extend(b"1");
468
469 let frame = extract(Parser::parse(&mut buf, false, 1024));
470 assert!(!frame.finished);
471 assert_eq!(frame.opcode, OpCode::Text);
472 assert_eq!(frame.payload.as_ref(), &b"1"[..]);
473 }
474
475 #[test]
476 fn test_parse_length0() {
477 let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0000u8][..]);
478 let frame = extract(Parser::parse(&mut buf, false, 1024));
479 assert!(!frame.finished);
480 assert_eq!(frame.opcode, OpCode::Text);
481 assert!(frame.payload.is_empty());
482 }
483
484 #[test]
485 fn test_parse_length2() {
486 let mut buf = BytesMut::from(&[0b0000_0001u8, 126u8][..]);
487 assert!(is_none(&Parser::parse(&mut buf, false, 1024)));
488
489 let mut buf = BytesMut::from(&[0b0000_0001u8, 126u8][..]);
490 buf.extend(&[0u8, 4u8][..]);
491 buf.extend(b"1234");
492
493 let frame = extract(Parser::parse(&mut buf, false, 1024));
494 assert!(!frame.finished);
495 assert_eq!(frame.opcode, OpCode::Text);
496 assert_eq!(frame.payload.as_ref(), &b"1234"[..]);
497 }
498
499 #[test]
500 fn test_parse_length4() {
501 let mut buf = BytesMut::from(&[0b0000_0001u8, 127u8][..]);
502 assert!(is_none(&Parser::parse(&mut buf, false, 1024)));
503
504 let mut buf = BytesMut::from(&[0b0000_0001u8, 127u8][..]);
505 buf.extend(&[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 4u8][..]);
506 buf.extend(b"1234");
507
508 let frame = extract(Parser::parse(&mut buf, false, 1024));
509 assert!(!frame.finished);
510 assert_eq!(frame.opcode, OpCode::Text);
511 assert_eq!(frame.payload.as_ref(), &b"1234"[..]);
512 }
513
514 #[test]
515 fn test_parse_frame_mask() {
516 let mut buf = BytesMut::from(&[0b0000_0001u8, 0b1000_0001u8][..]);
517 buf.extend(b"0001");
518 buf.extend(b"1");
519
520 assert!(Parser::parse(&mut buf, false, 1024).is_err());
521
522 let frame = extract(Parser::parse(&mut buf, true, 1024));
523 assert!(!frame.finished);
524 assert_eq!(frame.opcode, OpCode::Text);
525 assert_eq!(frame.payload, Bytes::from(vec![1u8]));
526 }
527
528 #[test]
529 fn test_parse_frame_no_mask() {
530 let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0001u8][..]);
531 buf.extend([1u8]);
532
533 assert!(Parser::parse(&mut buf, true, 1024).is_err());
534
535 let frame = extract(Parser::parse(&mut buf, false, 1024));
536 assert!(!frame.finished);
537 assert_eq!(frame.opcode, OpCode::Text);
538 assert_eq!(frame.payload, Bytes::from(vec![1u8]));
539 }
540
541 #[test]
542 fn test_parse_frame_with_rsv1_set() {
543 let mut buf = BytesMut::from(
547 &[
548 0b1100_0001u8, 0b1000_0001u8, 0, 0, 0, 0, b'a', ][..],
556 );
557
558 Parser::parse(&mut buf, true, 1024)
559 .expect_err("Should reject set RSV1 bit when no extension is negotiated");
560 }
561
562 #[test]
563 fn test_parse_frame_max_size() {
564 let mut buf = BytesMut::from(&[0b0000_0001u8, 0b0000_0010u8][..]);
565 buf.extend([1u8, 1u8]);
566
567 assert!(Parser::parse(&mut buf, true, 1).is_err());
568
569 if let Err(ProtocolError::Overflow) = Parser::parse(&mut buf, false, 0) {
570 } else {
571 unreachable!("error");
572 }
573 }
574
575 #[test]
576 fn test_parse_frame_max_size_recoverability() {
577 let mut buf = BytesMut::new();
578 buf.extend([0b0000_0001u8, 0b0000_0010u8, 0b0000_0000u8, 0b0000_0000u8]);
580 buf.extend([0b0000_0010u8, 0b0000_0010u8, 0b1111_1111u8, 0b1111_1111u8]);
582
583 assert_eq!(buf.len(), 8);
584 assert!(matches!(
585 Parser::parse(&mut buf, false, 1),
586 Err(ProtocolError::Overflow)
587 ));
588 assert_eq!(buf.len(), 4);
589 let frame = extract(Parser::parse(&mut buf, false, 2));
590 assert!(!frame.finished);
591 assert_eq!(frame.opcode, OpCode::Binary);
592 assert_eq!(
593 frame.payload,
594 Bytes::from(vec![0b1111_1111u8, 0b1111_1111u8])
595 );
596 assert_eq!(buf.len(), 0);
597 }
598
599 #[test]
600 fn test_ping_frame() {
601 let mut buf = BytesMut::new();
602 Parser::write_message(&mut buf, Vec::from("data"), OpCode::Ping, true, false);
603
604 let mut v = vec![137u8, 4u8];
605 v.extend(b"data");
606 assert_eq!(&buf[..], &v[..]);
607 }
608
609 #[test]
610 fn test_pong_frame() {
611 let mut buf = BytesMut::new();
612 Parser::write_message(&mut buf, Vec::from("data"), OpCode::Pong, true, false);
613
614 let mut v = vec![138u8, 4u8];
615 v.extend(b"data");
616 assert_eq!(&buf[..], &v[..]);
617 }
618
619 #[test]
620 fn test_close_frame() {
621 let mut buf = BytesMut::new();
622 let reason = (CloseCode::Normal, "data");
623 Parser::write_close(&mut buf, Some(reason.into()), false);
624
625 let mut v = vec![136u8, 6u8, 3u8, 232u8];
626 v.extend(b"data");
627 assert_eq!(&buf[..], &v[..]);
628 }
629
630 #[test]
631 fn test_empty_close_frame() {
632 let mut buf = BytesMut::new();
633 Parser::write_close(&mut buf, None, false);
634 assert_eq!(&buf[..], &vec![0x88, 0x00][..]);
635 }
636
637 #[test]
638 fn try_parse_close_payload_validates_payload() {
639 assert!(matches!(
640 Parser::try_parse_close_payload(&[0x03, 0xe8, 0xff]).unwrap_err(),
641 ProtocolError::BadOpCode
642 ));
643 assert!(matches!(
644 Parser::try_parse_close_payload(&[0, 0]).unwrap_err(),
645 ProtocolError::BadOpCode
646 ));
647 }
648
649 #[test]
650 fn test_oversized_control_frames() {
651 let mut buf = BytesMut::from(&[0x89u8, 126u8, 0x00, 126][..]);
653 buf.extend(vec![0u8; 126]);
654 assert!(matches!(
655 Parser::parse(&mut buf, false, 65536),
656 Err(ProtocolError::InvalidLength(126))
657 ));
658
659 let mut buf = BytesMut::from(&[0x88u8, 126u8, 0x00, 126][..]);
661 buf.extend(vec![0u8; 126]);
662 assert!(matches!(
663 Parser::parse(&mut buf, false, 65536),
664 Err(ProtocolError::InvalidLength(126))
665 ));
666 }
667
668 #[test]
673 fn test_parse_rejects_msb_length_from_3723() {
674 let buf: [u8; 14] = [
675 0x0a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0x0e, 0x8f,
676 ];
677 let mut buf = BytesMut::from(&buf[..]);
678 let result = Parser::parse(&mut buf, true, 65536);
679 assert!(
682 matches!(result, Err(ProtocolError::Overflow)),
683 "a declared length above i64::MAX must be rejected outright"
684 );
685 }
686}