1use crate::stream::{BoxStream, Flow, NotUsed};
2use crate::{StreamError, StreamResult};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum FramingByteOrder {
7 BigEndian,
9 LittleEndian,
11}
12
13#[derive(Clone)]
14enum Terminal {
15 Complete,
16 Error(StreamError),
17}
18
19fn sticky_terminal<T>(terminal: &Terminal) -> Option<StreamResult<T>> {
20 match terminal {
21 Terminal::Complete => None,
22 Terminal::Error(error) => Some(Err(error.clone())),
23 }
24}
25
26pub struct Framing;
29
30impl Framing {
31 #[must_use]
38 pub fn delimiter(
39 delimiter: Vec<u8>,
40 maximum_frame_length: usize,
41 allow_truncation: bool,
42 ) -> Flow<Vec<u8>, Vec<u8>> {
43 assert!(
44 !delimiter.is_empty(),
45 "delimiter must contain at least one byte"
46 );
47 assert!(
48 maximum_frame_length > 0,
49 "maximum frame length must be greater than zero"
50 );
51 Flow::from_transform(move |input| {
52 Box::new(DelimiterFramingStream::new(
53 input,
54 delimiter.clone(),
55 maximum_frame_length,
56 allow_truncation,
57 )) as BoxStream<Vec<u8>>
58 })
59 }
60
61 #[must_use]
69 pub fn length_field(
70 field_length: usize,
71 field_offset: usize,
72 maximum_frame_length: usize,
73 byte_order: FramingByteOrder,
74 ) -> Flow<Vec<u8>, Vec<u8>> {
75 assert!(
76 (1..=4).contains(&field_length),
77 "Length field length must be 1, 2, 3 or 4."
78 );
79 assert!(
80 maximum_frame_length > 0,
81 "maximum frame length must be greater than zero"
82 );
83 Flow::from_transform(move |input| {
84 Box::new(LengthFieldFramingStream::new(
85 input,
86 field_length,
87 field_offset,
88 maximum_frame_length,
89 byte_order,
90 )) as BoxStream<Vec<u8>>
91 })
92 }
93
94 #[must_use]
101 pub fn json(maximum_object_length: usize) -> Flow<Vec<u8>, Vec<u8>, NotUsed> {
102 assert!(
103 maximum_object_length > 0,
104 "maximum object length must be greater than zero"
105 );
106 Flow::from_transform(move |input| {
107 Box::new(JsonFramingStream::new(input, maximum_object_length)) as BoxStream<Vec<u8>>
108 })
109 }
110}
111
112struct DelimiterFramingStream {
113 input: BoxStream<Vec<u8>>,
114 delimiter: Vec<u8>,
115 maximum_frame_length: usize,
116 allow_truncation: bool,
117 buffer: Vec<u8>,
118 terminal: Option<Terminal>,
119}
120
121impl DelimiterFramingStream {
122 fn new(
123 input: BoxStream<Vec<u8>>,
124 delimiter: Vec<u8>,
125 maximum_frame_length: usize,
126 allow_truncation: bool,
127 ) -> Self {
128 Self {
129 input,
130 delimiter,
131 maximum_frame_length,
132 allow_truncation,
133 buffer: Vec::new(),
134 terminal: None,
135 }
136 }
137
138 fn fail<T>(&mut self, error: StreamError) -> Option<StreamResult<T>> {
139 self.terminal = Some(Terminal::Error(error.clone()));
140 Some(Err(error))
141 }
142
143 fn delimiter_position(&self) -> Option<usize> {
144 self.buffer
145 .windows(self.delimiter.len())
146 .position(|window| window == self.delimiter.as_slice())
147 }
148
149 fn trailing_delimiter_prefix_len(&self) -> usize {
150 let max_prefix = self
151 .delimiter
152 .len()
153 .saturating_sub(1)
154 .min(self.buffer.len());
155 (1..=max_prefix)
156 .rev()
157 .find(|&prefix_len| self.buffer.ends_with(&self.delimiter[..prefix_len]))
158 .unwrap_or(0)
159 }
160}
161
162impl Iterator for DelimiterFramingStream {
163 type Item = StreamResult<Vec<u8>>;
164
165 fn next(&mut self) -> Option<Self::Item> {
166 if let Some(terminal) = &self.terminal {
167 return sticky_terminal(terminal);
168 }
169
170 loop {
171 if let Some(position) = self.delimiter_position() {
172 if position > self.maximum_frame_length {
173 return self.fail(StreamError::Failed(format!(
174 "Read {position} bytes which is more than {} without seeing a line terminator",
175 self.maximum_frame_length
176 )));
177 }
178 let frame = self.buffer[..position].to_vec();
179 self.buffer.drain(..position + self.delimiter.len());
180 return Some(Ok(frame));
181 }
182
183 let trailing_partial = self.trailing_delimiter_prefix_len();
187 let unmatched = self.buffer.len() - trailing_partial;
188 if unmatched > self.maximum_frame_length {
189 return self.fail(StreamError::Failed(format!(
190 "Read {} bytes which is more than {} without seeing a line terminator",
191 unmatched, self.maximum_frame_length
192 )));
193 }
194
195 match self.input.next() {
196 Some(Ok(chunk)) => self.buffer.extend_from_slice(&chunk),
197 Some(Err(error)) => {
198 self.terminal = Some(Terminal::Error(error.clone()));
199 return Some(Err(error));
200 }
201 None => {
202 if self.buffer.is_empty() {
203 self.terminal = Some(Terminal::Complete);
204 return None;
205 }
206 if self.allow_truncation {
207 let frame = std::mem::take(&mut self.buffer);
208 self.terminal = Some(Terminal::Complete);
209 return Some(Ok(frame));
210 }
211 return self.fail(StreamError::Failed(
212 "Stream finished but there was a truncated final frame in the buffer"
213 .to_owned(),
214 ));
215 }
216 }
217 }
218 }
219}
220
221struct LengthFieldFramingStream {
222 input: BoxStream<Vec<u8>>,
223 field_length: usize,
224 field_offset: usize,
225 minimum_chunk_size: usize,
226 maximum_frame_length: usize,
227 byte_order: FramingByteOrder,
228 buffer: Vec<u8>,
229 frame_size: Option<usize>,
230 terminal: Option<Terminal>,
231}
232
233impl LengthFieldFramingStream {
234 fn new(
235 input: BoxStream<Vec<u8>>,
236 field_length: usize,
237 field_offset: usize,
238 maximum_frame_length: usize,
239 byte_order: FramingByteOrder,
240 ) -> Self {
241 let minimum_chunk_size = field_offset + field_length;
242 Self {
243 input,
244 field_length,
245 field_offset,
246 minimum_chunk_size,
247 maximum_frame_length,
248 byte_order,
249 buffer: Vec::new(),
250 frame_size: None,
251 terminal: None,
252 }
253 }
254
255 fn fail<T>(&mut self, error: StreamError) -> Option<StreamResult<T>> {
256 self.terminal = Some(Terminal::Error(error.clone()));
257 Some(Err(error))
258 }
259
260 fn parse_length(&self) -> i32 {
261 let bytes = &self.buffer[self.field_offset..self.field_offset + self.field_length];
262 match (self.byte_order, self.field_length) {
263 (FramingByteOrder::BigEndian, 1) => i32::from(bytes[0]),
264 (FramingByteOrder::BigEndian, 2) => i32::from(u16::from_be_bytes([bytes[0], bytes[1]])),
265 (FramingByteOrder::BigEndian, 3) => {
266 ((i32::from(bytes[0])) << 16) | ((i32::from(bytes[1])) << 8) | i32::from(bytes[2])
267 }
268 (FramingByteOrder::BigEndian, 4) => {
269 i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
270 }
271 (FramingByteOrder::LittleEndian, 1) => i32::from(bytes[0]),
272 (FramingByteOrder::LittleEndian, 2) => {
273 i32::from(u16::from_le_bytes([bytes[0], bytes[1]]))
274 }
275 (FramingByteOrder::LittleEndian, 3) => {
276 ((i32::from(bytes[2])) << 16) | ((i32::from(bytes[1])) << 8) | i32::from(bytes[0])
277 }
278 (FramingByteOrder::LittleEndian, 4) => {
279 i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
280 }
281 _ => unreachable!("field length validated at construction"),
282 }
283 }
284}
285
286impl Iterator for LengthFieldFramingStream {
287 type Item = StreamResult<Vec<u8>>;
288
289 fn next(&mut self) -> Option<Self::Item> {
290 if let Some(terminal) = &self.terminal {
291 return sticky_terminal(terminal);
292 }
293
294 loop {
295 if let Some(frame_size) = self.frame_size {
296 if self.buffer.len() >= frame_size {
297 let frame = self.buffer[..frame_size].to_vec();
298 self.buffer.drain(..frame_size);
299 self.frame_size = None;
300 return Some(Ok(frame));
301 }
302 } else if self.buffer.len() >= self.minimum_chunk_size {
303 let parsed_length = self.parse_length();
304 if parsed_length < 0 {
305 return self.fail(StreamError::Failed(format!(
306 "Decoded frame header reported negative size {parsed_length}"
307 )));
308 }
309 let frame_size = parsed_length as usize + self.minimum_chunk_size;
310 if frame_size > self.maximum_frame_length {
311 return self.fail(StreamError::Failed(format!(
312 "Maximum allowed frame size is {} but decoded frame header reported size {frame_size}",
313 self.maximum_frame_length
314 )));
315 }
316 if frame_size < self.minimum_chunk_size {
317 return self.fail(StreamError::Failed(format!(
318 "Computed frame size {frame_size} is less than minimum chunk size {}",
319 self.minimum_chunk_size
320 )));
321 }
322 self.frame_size = Some(frame_size);
323 continue;
324 }
325
326 match self.input.next() {
327 Some(Ok(chunk)) => self.buffer.extend_from_slice(&chunk),
328 Some(Err(error)) => {
329 self.terminal = Some(Terminal::Error(error.clone()));
330 return Some(Err(error));
331 }
332 None => {
333 if self.buffer.is_empty() {
334 self.terminal = Some(Terminal::Complete);
335 return None;
336 }
337 return self.fail(StreamError::Failed(
338 "Stream finished but there was a truncated final frame in the buffer"
339 .to_owned(),
340 ));
341 }
342 }
343 }
344 }
345}
346
347struct JsonFramingStream {
348 input: BoxStream<Vec<u8>>,
349 maximum_object_length: usize,
350 buffer: Vec<u8>,
351 pos: usize,
352 start: usize,
353 depth: usize,
354 completed_object: bool,
355 in_string_expression: bool,
356 in_backslash_escape: bool,
357 terminal: Option<Terminal>,
358}
359
360const OUTER_OBJECT: u8 = 2; const OUTER_SKIP: u8 = 1; const OUTER_ERROR: u8 = 0; static OUTER_CHARS: [u8; 256] = {
366 let mut table = [OUTER_ERROR; 256];
367 table[b'{' as usize] = OUTER_OBJECT;
368 table[b'[' as usize] = OUTER_SKIP;
369 table[b']' as usize] = OUTER_SKIP;
370 table[b',' as usize] = OUTER_SKIP;
371 table[b' ' as usize] = OUTER_SKIP;
372 table[b'\n' as usize] = OUTER_SKIP;
373 table[b'\r' as usize] = OUTER_SKIP;
374 table[b'\t' as usize] = OUTER_SKIP;
375 table
376};
377
378static INNER_INTERESTING: [u8; 256] = {
383 let mut table = [0u8; 256];
384 table[b'"' as usize] = 1;
385 table[b'{' as usize] = 1;
386 table[b'}' as usize] = 1;
387 table
388};
389
390static STRING_INTERESTING: [u8; 256] = {
395 let mut table = [0u8; 256];
396 table[b'"' as usize] = 1;
397 table[b'\\' as usize] = 1;
398 table
399};
400
401impl JsonFramingStream {
402 fn new(input: BoxStream<Vec<u8>>, maximum_object_length: usize) -> Self {
403 Self {
404 input,
405 maximum_object_length,
406 buffer: Vec::with_capacity(maximum_object_length.min(4096)),
407 pos: 0,
408 start: 0,
409 depth: 0,
410 completed_object: false,
411 in_string_expression: false,
412 in_backslash_escape: false,
413 terminal: None,
414 }
415 }
416
417 fn fail<T>(&mut self, error: StreamError) -> Option<StreamResult<T>> {
418 self.terminal = Some(Terminal::Error(error.clone()));
419 Some(Err(error))
420 }
421
422 fn can_complete(&self) -> bool {
423 self.depth == 0
424 }
425
426 fn compact(&mut self) {
430 if self.start == 0 {
431 return;
432 }
433 if self.start >= self.buffer.len() {
434 self.buffer.clear();
435 } else {
436 self.buffer.drain(..self.start);
437 }
438 self.pos -= self.start;
439 self.start = 0;
440 }
441
442 fn skip_to_next_object(&mut self) -> StreamResult<()> {
443 if self.depth > 0 {
444 return Ok(());
445 }
446 let max = self.buffer.len();
447 let limit = self.maximum_object_length;
448 let mut pos = self.pos;
449 let mut start = self.start;
450 while pos < max && pos - start < limit {
451 let outer = OUTER_CHARS[self.buffer[pos] as usize];
452 if outer == OUTER_SKIP {
453 start += 1;
454 pos += 1;
455 } else if outer == OUTER_OBJECT {
456 self.start = start;
457 self.pos = pos + 1;
458 self.depth = 1;
459 return Ok(());
460 } else {
461 return Err(StreamError::Failed(format!(
462 "Invalid JSON encountered at position [{}] of [{}]",
463 self.start,
464 String::from_utf8_lossy(&self.buffer)
465 )));
466 }
467 }
468 self.start = start;
469 self.pos = pos;
470 Ok(())
471 }
472
473 fn scan_object(&mut self) -> StreamResult<()> {
474 let max = self.buffer.len();
475 let limit = self.maximum_object_length;
476 let mut pos = self.pos;
477 let mut depth = self.depth;
478 let mut in_string = self.in_string_expression;
479 let mut in_escape = self.in_backslash_escape;
480 let start = self.start;
481 let mut completed = false;
482
483 while pos < max && pos - start < limit {
484 let byte = self.buffer[pos];
485 if in_string {
486 if in_escape {
487 in_escape = false;
490 } else if STRING_INTERESTING[byte as usize] != 0 {
491 if byte == b'"' {
493 in_string = false;
494 } else {
495 in_escape = true;
496 }
497 }
498 } else {
503 if INNER_INTERESTING[byte as usize] == 0 {
506 pos += 1;
507 continue;
508 }
509 match byte {
510 b'"' => in_string = true,
511 b'{' => depth += 1,
512 b'}' => {
513 depth -= 1;
514 if depth == 0 {
515 pos += 1;
516 completed = true;
517 break;
518 }
519 }
520 _ => {}
521 }
522 }
523 pos += 1;
524 }
525
526 self.pos = pos;
527 self.depth = depth;
528 self.in_string_expression = in_string;
529 self.in_backslash_escape = in_escape;
530 self.completed_object = completed;
531 Ok(())
532 }
533
534 fn poll_object(&mut self) -> StreamResult<Option<Vec<u8>>> {
535 self.completed_object = false;
536 self.skip_to_next_object()?;
537 self.scan_object()?;
538
539 if !self.completed_object
540 && self.pos.saturating_sub(self.start) >= self.maximum_object_length
541 {
542 return Err(StreamError::Failed(format!(
543 "JSON element exceeded maximumObjectLength ({} bytes)!",
544 self.maximum_object_length
545 )));
546 }
547
548 if self.completed_object && self.start < self.pos {
549 let frame = self.buffer[self.start..self.pos].to_vec();
550 self.start = self.pos;
553 return Ok(Some(frame));
554 }
555
556 Ok(None)
557 }
558}
559
560impl Iterator for JsonFramingStream {
561 type Item = StreamResult<Vec<u8>>;
562
563 fn next(&mut self) -> Option<Self::Item> {
564 if let Some(terminal) = &self.terminal {
565 return sticky_terminal(terminal);
566 }
567
568 loop {
569 match self.poll_object() {
570 Ok(Some(frame)) => return Some(Ok(frame)),
571 Ok(None) => {}
572 Err(error) => return self.fail(error),
573 }
574
575 match self.input.next() {
576 Some(Ok(chunk)) => {
577 if self.start > 0 && self.buffer.capacity() - self.buffer.len() < chunk.len() {
582 let tail = self.buffer.len() - self.start;
583 if tail + chunk.len() <= self.buffer.capacity() {
584 self.compact();
585 }
586 }
587 self.buffer.extend_from_slice(&chunk);
588 }
589 Some(Err(error)) => {
590 self.terminal = Some(Terminal::Error(error.clone()));
591 return Some(Err(error));
592 }
593 None => {
594 if self.start >= self.buffer.len() {
595 self.terminal = Some(Terminal::Complete);
596 return None;
597 }
598 if self.can_complete() {
599 self.start = 0;
600 self.terminal = Some(Terminal::Complete);
601 return None;
602 }
603 return self.fail(StreamError::Failed(
604 "Stream finished but there was a truncated final frame in the buffer"
605 .to_owned(),
606 ));
607 }
608 }
609 }
610 }
611}
612
613#[cfg(test)]
614mod tests {
615 use super::*;
616 use crate::testkit::{TestSink, TestSource};
617 use crate::{Keep, Source};
618
619 #[test]
620 fn delimiter_framing_handles_split_frames() {
621 let sink = Source::from_iter([b"ab|c".to_vec(), b"d|".to_vec()])
622 .via(Framing::delimiter(b"|".to_vec(), 16, false))
623 .run_with(TestSink::probe())
624 .expect("delimiter framing materializes");
625
626 sink.request(3);
627 sink.assert_next_n([b"ab".to_vec(), b"cd".to_vec()]);
628 sink.expect_complete();
629 }
630
631 #[test]
632 fn delimiter_framing_fails_on_max_length_exceeded() {
633 let sink = Source::single(b"abcdef".to_vec())
634 .via(Framing::delimiter(b"|".to_vec(), 3, false))
635 .run_with(TestSink::probe())
636 .expect("delimiter framing materializes");
637
638 sink.request(1);
639 assert_eq!(
640 sink.expect_error(),
641 StreamError::Failed(
642 "Read 6 bytes which is more than 3 without seeing a line terminator".to_owned()
643 )
644 );
645 }
646
647 #[test]
648 fn delimiter_framing_accepts_max_length_frame_with_split_partial_delimiter() {
649 let frame = vec![b'a'; 64];
650 let first = [frame.clone(), vec![b'\r']].concat();
651 let second = vec![b'\n'];
652 let sink = Source::from_iter([first, second])
653 .via(Framing::delimiter(b"\r\n".to_vec(), 64, false))
654 .run_with(TestSink::probe())
655 .expect("delimiter framing materializes");
656
657 sink.request(2);
658 sink.assert_next(frame);
659 sink.expect_complete();
660 }
661
662 #[test]
663 fn delimiter_framing_still_fails_when_max_length_is_exceeded_without_partial_match() {
664 let sink = Source::single(vec![b'a'; 65])
665 .via(Framing::delimiter(b"\r\n".to_vec(), 64, false))
666 .run_with(TestSink::probe())
667 .expect("delimiter framing materializes");
668
669 sink.request(1);
670 assert_eq!(
671 sink.expect_error(),
672 StreamError::Failed(
673 "Read 65 bytes which is more than 64 without seeing a line terminator".to_owned()
674 )
675 );
676 }
677
678 #[test]
679 fn delimiter_framing_fails_on_truncated_eof() {
680 let sink = Source::single(b"abc".to_vec())
681 .via(Framing::delimiter(b"|".to_vec(), 8, false))
682 .run_with(TestSink::probe())
683 .expect("delimiter framing materializes");
684
685 sink.request(1);
686 assert_eq!(
687 sink.expect_error(),
688 StreamError::Failed(
689 "Stream finished but there was a truncated final frame in the buffer".to_owned()
690 )
691 );
692 }
693
694 #[test]
695 fn delimiter_framing_allows_truncation_when_enabled() {
696 let sink = Source::single(b"abc".to_vec())
697 .via(Framing::delimiter(b"|".to_vec(), 8, true))
698 .run_with(TestSink::probe())
699 .expect("delimiter framing materializes");
700
701 sink.request(2);
702 sink.assert_next(b"abc".to_vec());
703 sink.expect_complete();
704 }
705
706 #[test]
707 fn length_field_framing_handles_split_headers_and_payloads() {
708 let frame = [0_u8, 0_u8, 0_u8, 2_u8, b'o', b'k'];
709 let sink = Source::from_iter([frame[..3].to_vec(), frame[3..].to_vec()])
710 .via(Framing::length_field(4, 0, 16, FramingByteOrder::BigEndian))
711 .run_with(TestSink::probe())
712 .expect("length field framing materializes");
713
714 sink.request(2);
715 sink.assert_next(frame.to_vec());
716 sink.expect_complete();
717 }
718
719 #[test]
720 fn length_field_framing_reads_header_at_non_zero_offset() {
721 let frame = [b'i', b'd', 0_u8, 3_u8, b'h', b'e', b'y'];
722 let sink = Source::from_iter([
723 frame[..1].to_vec(),
724 frame[1..3].to_vec(),
725 frame[3..].to_vec(),
726 ])
727 .via(Framing::length_field(2, 2, 16, FramingByteOrder::BigEndian))
728 .run_with(TestSink::probe())
729 .expect("length field framing materializes");
730
731 sink.request(2);
732 sink.assert_next(frame.to_vec());
733 sink.expect_complete();
734 }
735
736 #[test]
737 fn length_field_framing_fails_on_max_length_exceeded() {
738 let sink = Source::single(vec![0_u8, 0_u8, 0_u8, 8_u8])
739 .via(Framing::length_field(4, 0, 6, FramingByteOrder::BigEndian))
740 .run_with(TestSink::probe())
741 .expect("length field framing materializes");
742
743 sink.request(1);
744 assert_eq!(
745 sink.expect_error(),
746 StreamError::Failed(
747 "Maximum allowed frame size is 6 but decoded frame header reported size 12"
748 .to_owned()
749 )
750 );
751 }
752
753 #[test]
754 fn length_field_framing_fails_on_truncated_eof() {
755 let sink = Source::single(vec![0_u8, 0_u8, 0_u8, 2_u8, b'o'])
756 .via(Framing::length_field(4, 0, 16, FramingByteOrder::BigEndian))
757 .run_with(TestSink::probe())
758 .expect("length field framing materializes");
759
760 sink.request(1);
761 assert_eq!(
762 sink.expect_error(),
763 StreamError::Failed(
764 "Stream finished but there was a truncated final frame in the buffer".to_owned()
765 )
766 );
767 }
768
769 #[test]
770 fn length_field_framing_treats_two_byte_big_endian_lengths_as_unsigned() {
771 let payload = vec![b'x'; 0x8000];
772 let mut frame = vec![0x80, 0x00];
773 frame.extend_from_slice(&payload);
774 let sink = Source::single(frame.clone())
775 .via(Framing::length_field(
776 2,
777 0,
778 frame.len(),
779 FramingByteOrder::BigEndian,
780 ))
781 .run_with(TestSink::probe())
782 .expect("length field framing materializes");
783
784 sink.request(2);
785 sink.assert_next(frame);
786 sink.expect_complete();
787 }
788
789 #[test]
790 fn length_field_framing_treats_two_byte_little_endian_lengths_as_unsigned() {
791 let payload = vec![b'y'; 0x8000];
792 let mut frame = vec![0x00, 0x80];
793 frame.extend_from_slice(&payload);
794 let sink = Source::single(frame.clone())
795 .via(Framing::length_field(
796 2,
797 0,
798 frame.len(),
799 FramingByteOrder::LittleEndian,
800 ))
801 .run_with(TestSink::probe())
802 .expect("length field framing materializes");
803
804 sink.request(2);
805 sink.assert_next(frame);
806 sink.expect_complete();
807 }
808
809 #[test]
810 fn length_field_framing_reads_three_byte_big_endian_lengths() {
811 let frame = [0_u8, 0_u8, 2_u8, b'o', b'k'];
812 let sink = Source::single(frame.to_vec())
813 .via(Framing::length_field(3, 0, 8, FramingByteOrder::BigEndian))
814 .run_with(TestSink::probe())
815 .expect("length field framing materializes");
816
817 sink.request(2);
818 sink.assert_next(frame.to_vec());
819 sink.expect_complete();
820 }
821
822 #[test]
823 fn length_field_framing_reads_three_byte_little_endian_lengths() {
824 let frame = [2_u8, 0_u8, 0_u8, b'o', b'k'];
825 let sink = Source::single(frame.to_vec())
826 .via(Framing::length_field(
827 3,
828 0,
829 8,
830 FramingByteOrder::LittleEndian,
831 ))
832 .run_with(TestSink::probe())
833 .expect("length field framing materializes");
834
835 sink.request(2);
836 sink.assert_next(frame.to_vec());
837 sink.expect_complete();
838 }
839
840 #[test]
841 fn length_field_framing_keeps_four_byte_signed_overflow_behavior() {
842 let sink = Source::single(vec![0x80, 0x00, 0x00, 0x00])
843 .via(Framing::length_field(
844 4,
845 0,
846 usize::MAX,
847 FramingByteOrder::BigEndian,
848 ))
849 .run_with(TestSink::probe())
850 .expect("length field framing materializes");
851
852 sink.request(1);
853 assert_eq!(
854 sink.expect_error(),
855 StreamError::Failed(
856 "Decoded frame header reported negative size -2147483648".to_owned()
857 )
858 );
859 }
860
861 #[test]
862 fn json_framing_extracts_objects_split_across_chunks() {
863 let sink = Source::from_iter([b"[{\"a\":1},".to_vec(), b"{\"b\":2}]".to_vec()])
864 .via(Framing::json(64))
865 .run_with(TestSink::probe())
866 .expect("json framing materializes");
867
868 sink.request(3);
869 sink.assert_next_n([b"{\"a\":1}".to_vec(), b"{\"b\":2}".to_vec()]);
870 sink.expect_complete();
871 }
872
873 #[test]
874 fn json_framing_fails_on_max_length_exceeded() {
875 let sink = Source::single(b"{\"abcdef\":1}".to_vec())
876 .via(Framing::json(4))
877 .run_with(TestSink::probe())
878 .expect("json framing materializes");
879
880 sink.request(1);
881 assert_eq!(
882 sink.expect_error(),
883 StreamError::Failed("JSON element exceeded maximumObjectLength (4 bytes)!".to_owned())
884 );
885 }
886
887 #[test]
888 fn json_framing_accepts_object_one_byte_under_max_length() {
889 let sink = Source::single(br#"{"":1}"#.to_vec())
890 .via(Framing::json(7))
891 .run_with(TestSink::probe())
892 .expect("json framing materializes");
893
894 sink.request(2);
895 sink.assert_next(br#"{"":1}"#.to_vec());
896 sink.expect_complete();
897 }
898
899 #[test]
900 fn json_framing_accepts_object_at_max_length() {
901 let sink = Source::single(br#"{"a":1}"#.to_vec())
902 .via(Framing::json(7))
903 .run_with(TestSink::probe())
904 .expect("json framing materializes");
905
906 sink.request(2);
907 sink.assert_next(br#"{"a":1}"#.to_vec());
908 sink.expect_complete();
909 }
910
911 #[test]
912 fn json_framing_rejects_object_one_byte_over_max_length() {
913 let sink = Source::single(br#"{"aa":1}"#.to_vec())
914 .via(Framing::json(7))
915 .run_with(TestSink::probe())
916 .expect("json framing materializes");
917
918 sink.request(1);
919 assert_eq!(
920 sink.expect_error(),
921 StreamError::Failed("JSON element exceeded maximumObjectLength (7 bytes)!".to_owned())
922 );
923 }
924
925 #[test]
926 fn json_framing_fails_on_truncated_eof() {
927 let sink = Source::single(b"{\"a\":".to_vec())
928 .via(Framing::json(32))
929 .run_with(TestSink::probe())
930 .expect("json framing materializes");
931
932 sink.request(1);
933 assert_eq!(
934 sink.expect_error(),
935 StreamError::Failed(
936 "Stream finished but there was a truncated final frame in the buffer".to_owned()
937 )
938 );
939 }
940
941 #[test]
942 fn framing_preserves_upstream_errors() {
943 let (source, sink) = TestSource::probe::<Vec<u8>>()
944 .via(Framing::delimiter(b"|".to_vec(), 32, false))
945 .to_mat(TestSink::probe(), Keep::both)
946 .run()
947 .expect("probe framing materializes");
948
949 sink.request(1);
950 source.send_error(StreamError::Failed("boom".to_owned()));
951 assert_eq!(sink.expect_error(), StreamError::Failed("boom".to_owned()));
952 }
953
954 #[test]
955 fn json_framing_no_bleed_across_buffer_reuse() {
956 use crate::Sink;
957 let mut payload = Vec::new();
958 payload.push(b'[');
959 for index in 0..128_usize {
960 if index > 0 {
961 payload.push(b',');
962 }
963 payload
964 .extend_from_slice(format!("{{\"id\":{index},\"pad\":\"xxxxxxxx\"}}").as_bytes());
965 }
966 payload.push(b']');
967
968 let chunks: Vec<Vec<u8>> = payload.chunks(193).map(|c| c.to_vec()).collect();
969 assert!(
970 chunks.len() > 4,
971 "chunks must split objects across boundaries"
972 );
973
974 let frames = Source::from_iter(chunks)
975 .via(Framing::json(256))
976 .run_with(Sink::collect())
977 .expect("json framing materializes")
978 .wait()
979 .expect("json framing completes");
980
981 assert_eq!(frames.len(), 128);
982 for (index, frame) in frames.iter().enumerate() {
983 let text = std::str::from_utf8(frame).expect("frame is valid utf-8");
984 let needle = format!("\"id\":{index}");
985 assert!(
986 text.contains(&needle),
987 "frame {index} missing {needle}: {text}"
988 );
989 assert_eq!(text.as_bytes()[0], b'{');
990 assert_eq!(text.as_bytes()[text.len() - 1], b'}');
991 }
992 }
993
994 #[test]
995 fn json_framing_compacts_buffer_at_chunk_boundaries() {
996 use crate::Sink;
997 let object = br#"{"id":1,"name":"x"}"#;
998 let mut payload = Vec::new();
999 payload.push(b'[');
1000 for _ in 0..32 {
1001 payload.push(b',');
1002 payload.extend_from_slice(object);
1003 }
1004 payload.push(b']');
1005 let chunks: Vec<Vec<u8>> = payload.iter().copied().map(|b| vec![b]).collect();
1006
1007 let frames = Source::from_iter(chunks)
1008 .via(Framing::json(64))
1009 .run_with(Sink::collect())
1010 .expect("json framing materializes")
1011 .wait()
1012 .expect("json framing completes");
1013
1014 assert_eq!(frames.len(), 32);
1015 for frame in &frames {
1016 assert_eq!(frame.as_slice(), object);
1017 }
1018 }
1019}