Skip to main content

reth_ipc/
stream_codec.rs

1// Copyright (c) 2015-2017 Parity Technologies Limited
2//
3// Permission is hereby granted, free of charge, to any
4// person obtaining a copy of this software and associated
5// documentation files (the "Software"), to deal in the
6// Software without restriction, including without
7// limitation the rights to use, copy, modify, merge,
8// publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software
10// is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice
14// shall be included in all copies or substantial portions
15// of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25// DEALINGS IN THE SOFTWARE.
26
27// This basis of this file has been taken from the deprecated jsonrpc codebase:
28// https://github.com/paritytech/jsonrpc
29
30use bytes::{Buf, BytesMut};
31use std::{io, str};
32
33/// Separator for enveloping messages in streaming codecs
34#[derive(Debug, Clone)]
35pub enum Separator {
36    /// No envelope is expected between messages. Decoder will try to figure out
37    /// message boundaries by accumulating incoming bytes until valid JSON is formed.
38    /// Encoder will send messages without any boundaries between requests.
39    Empty,
40    /// Byte is used as a sentinel between messages
41    Byte(u8),
42}
43
44impl Default for Separator {
45    fn default() -> Self {
46        Self::Byte(b'\n')
47    }
48}
49
50/// Stream codec for streaming protocols (ipc, tcp)
51#[derive(Debug, Default)]
52pub struct StreamCodec {
53    incoming_separator: Separator,
54    outgoing_separator: Separator,
55    scan: ScanState,
56}
57
58impl StreamCodec {
59    /// Default codec with streaming input data. Input can be both enveloped and not.
60    pub fn stream_incoming() -> Self {
61        Self::new(Separator::Empty, Default::default())
62    }
63
64    /// New custom stream codec
65    pub const fn new(incoming_separator: Separator, outgoing_separator: Separator) -> Self {
66        Self { incoming_separator, outgoing_separator, scan: ScanState::new() }
67    }
68}
69
70/// Scan state of the [`Separator::Empty`] decoder, carried across `decode` calls so bytes
71/// scanned by a previous call are not scanned again.
72///
73/// Indices refer to the current read buffer and must be reset whenever bytes are consumed from
74/// it.
75#[derive(Debug)]
76struct ScanState {
77    depth: i32,
78    in_str: bool,
79    is_escaped: bool,
80    start_idx: usize,
81    whitespaces: usize,
82    cursor: usize,
83}
84
85impl ScanState {
86    const fn new() -> Self {
87        Self { depth: 0, in_str: false, is_escaped: false, start_idx: 0, whitespaces: 0, cursor: 0 }
88    }
89}
90
91impl Default for ScanState {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97#[inline]
98const fn is_whitespace(byte: u8) -> bool {
99    matches!(byte, 0x0D | 0x0A | 0x20 | 0x09)
100}
101
102impl tokio_util::codec::Decoder for StreamCodec {
103    type Item = String;
104    type Error = io::Error;
105
106    fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Self::Item>> {
107        if let Separator::Byte(separator) = self.incoming_separator {
108            if let Some(i) = buf.as_ref().iter().position(|&b| b == separator) {
109                let line = buf.split_to(i);
110                let _ = buf.split_to(1);
111
112                match str::from_utf8(line.as_ref()) {
113                    Ok(s) => Ok(Some(s.to_string())),
114                    Err(_) => Err(io::Error::other("invalid UTF-8")),
115                }
116            } else {
117                Ok(None)
118            }
119        } else {
120            // resume scanning at the byte the previous call stopped at
121            while self.scan.cursor < buf.len() {
122                let idx = self.scan.cursor;
123                let byte = buf[idx];
124
125                if (byte == b'{' || byte == b'[') && !self.scan.in_str {
126                    if self.scan.depth == 0 {
127                        self.scan.start_idx = idx;
128                    }
129                    self.scan.depth += 1;
130                } else if (byte == b'}' || byte == b']') && !self.scan.in_str {
131                    self.scan.depth -= 1;
132                } else if byte == b'"' && !self.scan.is_escaped {
133                    self.scan.in_str = !self.scan.in_str;
134                } else if is_whitespace(byte) {
135                    self.scan.whitespaces += 1;
136                }
137                self.scan.is_escaped = byte == b'\\' && !self.scan.is_escaped && self.scan.in_str;
138
139                if self.scan.depth == 0 &&
140                    idx != self.scan.start_idx &&
141                    idx - self.scan.start_idx + 1 > self.scan.whitespaces
142                {
143                    let start = self.scan.start_idx;
144                    let end = idx + 1;
145                    // reset before advancing the buffer because the stored indices go stale
146                    self.scan = ScanState::new();
147                    if start > 0 {
148                        buf.advance(start);
149                    }
150                    let bts = buf.split_to(end - start);
151                    return Ok(String::from_utf8(bts.into()).ok())
152                }
153
154                self.scan.cursor += 1;
155            }
156            Ok(None)
157        }
158    }
159}
160
161impl tokio_util::codec::Encoder<String> for StreamCodec {
162    type Error = io::Error;
163
164    fn encode(&mut self, msg: String, buf: &mut BytesMut) -> io::Result<()> {
165        let mut payload = msg.into_bytes();
166        if let Separator::Byte(separator) = self.outgoing_separator {
167            payload.push(separator);
168        }
169        buf.extend_from_slice(&payload);
170        Ok(())
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177    use bytes::BufMut;
178    use rand::{rngs::StdRng, RngCore, SeedableRng};
179    use tokio_util::codec::Decoder;
180
181    #[test]
182    fn simple_encode() {
183        let mut buf = BytesMut::with_capacity(2048);
184        buf.put_slice(b"{ test: 1 }{ test: 2 }{ test: 3 }");
185
186        let mut codec = StreamCodec::stream_incoming();
187
188        let request = codec
189            .decode(&mut buf)
190            .expect("There should be no error in simple test")
191            .expect("There should be at least one request in simple test");
192
193        assert_eq!(request, "{ test: 1 }");
194    }
195
196    #[test]
197    fn escape() {
198        let mut buf = BytesMut::with_capacity(2048);
199        buf.put_slice(br#"{ test: "\"\\" }{ test: "\ " }{ test: "\}" }[ test: "\]" ]"#);
200
201        let mut codec = StreamCodec::stream_incoming();
202
203        let request = codec
204            .decode(&mut buf)
205            .expect("There should be no error in first escape test")
206            .expect("There should be a request in first escape test");
207
208        assert_eq!(request, r#"{ test: "\"\\" }"#);
209
210        let request2 = codec
211            .decode(&mut buf)
212            .expect("There should be no error in 2nd escape test")
213            .expect("There should be a request in 2nd escape test");
214        assert_eq!(request2, r#"{ test: "\ " }"#);
215
216        let request3 = codec
217            .decode(&mut buf)
218            .expect("There should be no error in 3rd escape test")
219            .expect("There should be a request in 3rd escape test");
220        assert_eq!(request3, r#"{ test: "\}" }"#);
221
222        let request4 = codec
223            .decode(&mut buf)
224            .expect("There should be no error in 4th escape test")
225            .expect("There should be a request in 4th escape test");
226        assert_eq!(request4, r#"[ test: "\]" ]"#);
227    }
228
229    #[test]
230    fn whitespace() {
231        let mut buf = BytesMut::with_capacity(2048);
232        buf.put_slice(b"{ test: 1 }\n\n\n\n{ test: 2 }\n\r{\n test: 3 }  ");
233
234        let mut codec = StreamCodec::stream_incoming();
235
236        let request = codec
237            .decode(&mut buf)
238            .expect("There should be no error in first whitespace test")
239            .expect("There should be a request in first whitespace test");
240
241        assert_eq!(request, "{ test: 1 }");
242
243        let request2 = codec
244            .decode(&mut buf)
245            .expect("There should be no error in first 2nd test")
246            .expect("There should be a request in 2nd whitespace test");
247        assert_eq!(request2, "{ test: 2 }");
248
249        let request3 = codec
250            .decode(&mut buf)
251            .expect("There should be no error in first 3rd test")
252            .expect("There should be a request in 3rd whitespace test");
253        assert_eq!(request3, "{\n test: 3 }");
254
255        let request4 = codec.decode(&mut buf).expect("There should be no error in first 4th test");
256        assert!(
257            request4.is_none(),
258            "There should be no 4th request because it contains only whitespaces"
259        );
260    }
261
262    #[test]
263    fn fragmented_encode() {
264        let mut buf = BytesMut::with_capacity(2048);
265        buf.put_slice(b"{ test: 1 }{ test: 2 }{ tes");
266
267        let mut codec = StreamCodec::stream_incoming();
268
269        let request = codec
270            .decode(&mut buf)
271            .expect("There should be no error in first fragmented test")
272            .expect("There should be at least one request in first fragmented test");
273        assert_eq!(request, "{ test: 1 }");
274        codec
275            .decode(&mut buf)
276            .expect("There should be no error in second fragmented test")
277            .expect("There should be at least one request in second fragmented test");
278        assert_eq!(String::from_utf8(buf.as_ref().to_vec()).unwrap(), "{ tes");
279
280        buf.put_slice(b"t: 3 }");
281        let request = codec
282            .decode(&mut buf)
283            .expect("There should be no error in third fragmented test")
284            .expect("There should be at least one request in third fragmented test");
285        assert_eq!(request, "{ test: 3 }");
286    }
287
288    #[test]
289    fn huge() {
290        let request = r#"{
291			"jsonrpc":"2.0",
292			"method":"say_hello",
293			"params": [
294				42,
295				0,
296				{
297					"from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155",
298					"gas":"0x2dc6c0",
299					"data":"0x606060405260003411156010576002565b6001805433600160a060020a0319918216811790925560028054909116909117905561291f806100406000396000f3606060405236156100e55760e060020a600035046304029f2381146100ed5780630a1273621461015f57806317c1dd87146102335780631f9ea25d14610271578063266fa0e91461029357806349593f5314610429578063569aa0d8146104fc57806359a4669f14610673578063647a4d5f14610759578063656104f5146108095780636e9febfe1461082b57806370de8c6e1461090d57806371bde852146109ed5780638f30435d14610ab4578063916dbc1714610da35780639f5a7cd414610eef578063c91540f614610fe6578063eae99e1c146110b5578063fedc2a281461115a575b61122d610002565b61122d6004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750949650509335935050604435915050606435600154600090600160a060020a03908116339091161461233357610002565b61122f6004808035906020019082018035906020019191908080601f016020809104026020016040519081016040528093929190818152602001838380828437509496505093359350506044359150506064355b60006000600060005086604051808280519060200190808383829060006004602084601f0104600f02600301f1509050019150509081526020016040518091039020600050905042816005016000508560ff1660028110156100025760040201835060010154604060020a90046001604060020a0316116115df576115d6565b6112416004355b604080516001604060020a038316408152606060020a33600160a060020a031602602082015290519081900360340190205b919050565b61122d600435600254600160a060020a0390811633909116146128e357610002565b61125e6004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750949650509335935050505060006000600060006000600060005087604051808280519060200190808383829060006004602084601f0104600f02600301f1509050019150509081526020016040518091039020600050905080600001600050600087600160a060020a0316815260200190815260200160002060005060000160059054906101000a90046001604060020a03169450845080600001600050600087600160a060020a03168152602001908152602001600020600050600001600d9054906101000a90046001604060020a03169350835080600001600050600087600160a060020a0316815260200190815260200160002060005060000160009054906101000a900460ff169250825080600001600050600087600160a060020a0316815260200190815260200160002060005060000160019054906101000a900463ffffffff16915081505092959194509250565b61122d6004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750949650509335935050604435915050606435608435600060006000600060005088604051808280519060200190808383829060006004602084601f0104600f02600301f15090500191505090815260200160405180910390206000509250346000141515611c0e5760405133600160a060020a0316908290349082818181858883f193505050501515611c1a57610002565b6112996004808035906020019082018035906020019191908080601f01602080910402602001604051908101604052809392919081815260200183838082843750949650509335935050604435915050600060006000600060006000600060006000508a604051808280519060200190808383829060006004602084601f0104600f02600301f15090500191505090815260200160405180910390206000509050806001016000508960ff16600281101561000257600160a060020a038a168452828101600101602052604084205463ffffffff1698506002811015610002576040842054606060020a90046001604060020a031697506002811015610002576040842054640100000000900463ffffffff169650600281101561000257604084206001015495506002811015610002576040842054604060020a900463ffffffff169450600281101561000257505060409091205495999498509296509094509260a060020a90046001604060020a0316919050565b61122d6004808035906020019082018035906020019191908080601f016020809104026020016040519081016040528093929190818152602001838380828437509496505050505050506000600060005082604051808280519060200190808383829060006004602084601f0104600f02600301f15090500191505090815260200160405180910390206000509050348160050160005082600d0160009054906101000a900460ff1660ff16600281101561000257600402830160070180546001608060020a0381169093016001608060020a03199390931692909217909155505b5050565b6112e26004808035906020019082018035906020019191908080601f01602080910003423423094734987103498712093847102938740192387401349857109487501938475"
300				}
301			]
302		}"#;
303
304        let mut buf = BytesMut::with_capacity(65536);
305        buf.put_slice(request.as_bytes());
306
307        let mut codec = StreamCodec::stream_incoming();
308
309        let parsed_request = codec
310            .decode(&mut buf)
311            .expect("There should be no error in huge test")
312            .expect("There should be at least one request huge test");
313        assert_eq!(request, parsed_request);
314    }
315
316    #[test]
317    fn simple_line_codec() {
318        let mut buf = BytesMut::with_capacity(2048);
319        buf.put_slice(b"{ test: 1 }\n{ test: 2 }\n{ test: 3 }");
320
321        let mut codec = StreamCodec::default();
322
323        let request = codec
324            .decode(&mut buf)
325            .expect("There should be no error in simple test")
326            .expect("There should be at least one request in simple test");
327        let request2 = codec
328            .decode(&mut buf)
329            .expect("There should be no error in simple test")
330            .expect("There should be at least one request in simple test");
331
332        assert_eq!(request, "{ test: 1 }");
333        assert_eq!(request2, "{ test: 2 }");
334    }
335
336    #[test]
337    fn serde_json_accepts_whitespace_wrapped_json() {
338        let json = "   { \"key\": \"value\" }   ";
339
340        #[derive(serde::Deserialize, Debug, PartialEq)]
341        struct Obj {
342            key: String,
343        }
344
345        let parsed: Result<Obj, _> = serde_json::from_str(json);
346        assert!(parsed.is_ok(), "serde_json should accept whitespace-wrapped JSON");
347        assert_eq!(parsed.unwrap(), Obj { key: "value".into() });
348    }
349
350    /// Multiple messages fed one byte at a time.
351    #[test]
352    fn pipelined_messages_byte_by_byte() {
353        let payload = br#"{"a":1}{"b":2}{"c":3}"#;
354        let mut buf = BytesMut::with_capacity(256);
355        let mut codec = StreamCodec::stream_incoming();
356        let mut decoded = Vec::new();
357
358        for byte in payload {
359            buf.put_u8(*byte);
360            while let Some(m) = codec.decode(&mut buf).unwrap() {
361                decoded.push(m);
362            }
363        }
364
365        assert_eq!(decoded, vec![r#"{"a":1}"#, r#"{"b":2}"#, r#"{"c":3}"#]);
366        assert!(buf.is_empty());
367    }
368
369    /// Escape sequence split across two `decode` calls, `is_escaped` must carry over.
370    #[test]
371    fn escape_split_across_chunk_boundary() {
372        let mut codec = StreamCodec::stream_incoming();
373        let mut buf = BytesMut::with_capacity(64);
374
375        buf.put_slice(br#"{"a":"\"#);
376        assert!(codec.decode(&mut buf).unwrap().is_none());
377
378        buf.put_slice(br#"\"}"#);
379        let msg = codec.decode(&mut buf).unwrap().unwrap();
380        assert_eq!(msg, r#"{"a":"\\"}"#);
381    }
382
383    /// Opening and closing bracket arriving in separate `decode` calls.
384    #[test]
385    fn depth_split_across_chunk_boundary() {
386        let mut codec = StreamCodec::stream_incoming();
387        let mut buf = BytesMut::with_capacity(64);
388
389        buf.put_u8(b'{');
390        assert!(codec.decode(&mut buf).unwrap().is_none());
391        buf.put_u8(b'}');
392        assert_eq!(codec.decode(&mut buf).unwrap().unwrap(), "{}");
393    }
394
395    /// Input that drives the depth negative must not wedge the codec.
396    #[test]
397    fn leading_close_bracket_does_not_poison() {
398        let mut codec = StreamCodec::stream_incoming();
399        let mut buf = BytesMut::with_capacity(8);
400        buf.put_slice(b"]{");
401        assert_eq!(codec.decode(&mut buf).unwrap(), Some("]{".to_string()));
402    }
403
404    /// `Separator::Byte` decoding fed one byte at a time.
405    #[test]
406    fn byte_separator_split_at_every_byte() {
407        let payload = b"first line\nsecond line\nthird line\n";
408        let mut codec = StreamCodec::default();
409        let mut buf = BytesMut::with_capacity(64);
410        let mut got = Vec::new();
411
412        for byte in payload {
413            buf.put_u8(*byte);
414            while let Some(m) = codec.decode(&mut buf).unwrap() {
415                got.push(m);
416            }
417        }
418
419        assert_eq!(got, vec!["first line", "second line", "third line"]);
420    }
421
422    /// The stateless `Separator::Empty` decode loop that `ScanState` replaced, kept as the
423    /// reference implementation for the differential tests below.
424    struct ReferenceDecoder;
425
426    impl Decoder for ReferenceDecoder {
427        type Item = String;
428        type Error = io::Error;
429
430        fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Self::Item>> {
431            let mut depth = 0i32;
432            let mut in_str = false;
433            let mut is_escaped = false;
434            let mut start_idx = 0usize;
435            let mut whitespaces = 0usize;
436
437            for idx in 0..buf.as_ref().len() {
438                let byte = buf.as_ref()[idx];
439
440                if (byte == b'{' || byte == b'[') && !in_str {
441                    if depth == 0 {
442                        start_idx = idx;
443                    }
444                    depth += 1;
445                } else if (byte == b'}' || byte == b']') && !in_str {
446                    depth -= 1;
447                } else if byte == b'"' && !is_escaped {
448                    in_str = !in_str;
449                } else if is_whitespace(byte) {
450                    whitespaces += 1;
451                }
452                is_escaped = byte == b'\\' && !is_escaped && in_str;
453
454                if depth == 0 && idx != start_idx && idx - start_idx + 1 > whitespaces {
455                    if start_idx > 0 {
456                        buf.advance(start_idx);
457                    }
458                    let bts = buf.split_to(idx + 1 - start_idx);
459                    return Ok(String::from_utf8(bts.into()).ok())
460                }
461            }
462            Ok(None)
463        }
464    }
465
466    /// Feeds the same bytes with the same chunk boundaries to the codec and the reference
467    /// decoder and returns the decode results of both. Asserts that both consume the same bytes.
468    fn drain_both(bytes: &[u8], chunks: &[usize]) -> (Vec<Option<String>>, Vec<Option<String>>) {
469        let mut ours = StreamCodec::stream_incoming();
470        let mut theirs = ReferenceDecoder;
471
472        let mut buf_ours = BytesMut::with_capacity(bytes.len() + 16);
473        let mut buf_theirs = BytesMut::with_capacity(bytes.len() + 16);
474        let mut out_ours = Vec::new();
475        let mut out_theirs = Vec::new();
476
477        let mut cur = 0usize;
478        for &c in chunks {
479            let end = cur.saturating_add(c.max(1)).min(bytes.len());
480            if cur == end {
481                break;
482            }
483            buf_ours.put_slice(&bytes[cur..end]);
484            buf_theirs.put_slice(&bytes[cur..end]);
485            loop {
486                let a = ours.decode(&mut buf_ours).unwrap();
487                let b = theirs.decode(&mut buf_theirs).unwrap();
488                out_ours.push(a.clone());
489                out_theirs.push(b.clone());
490                if a.is_none() && b.is_none() {
491                    break;
492                }
493            }
494            cur = end;
495        }
496        if cur < bytes.len() {
497            buf_ours.put_slice(&bytes[cur..]);
498            buf_theirs.put_slice(&bytes[cur..]);
499            loop {
500                let a = ours.decode(&mut buf_ours).unwrap();
501                let b = theirs.decode(&mut buf_theirs).unwrap();
502                out_ours.push(a.clone());
503                out_theirs.push(b.clone());
504                if a.is_none() && b.is_none() {
505                    break;
506                }
507            }
508        }
509
510        assert_eq!(buf_ours, buf_theirs, "residual buffers diverged");
511
512        (out_ours, out_theirs)
513    }
514
515    /// Hand-picked adversarial inputs under several chunk schedules.
516    #[test]
517    fn differential_curated_corpus() {
518        let inputs: &[&[u8]] = &[
519            b"",
520            b"{}",
521            b"[]",
522            b"{}{}",
523            br#"{"a":1}"#,
524            br#"{"a":1}{"b":2}"#,
525            b"   {}   ",
526            b"   { }   ",
527            b"   {\n}\n   ",
528            br#"{"a":"\""}"#,
529            br#"{"a":"\\"}"#,
530            br#"{"a":"\\\""}"#,
531            b"{\xff}",
532            b"{\xff}{\"a\":1}",
533            b"]{}",
534            b"}{}",
535            b"]{}{}",
536            b"}{}{}",
537            b"][{}",
538            b"abc",
539            b"a",
540            b"\n\n\n",
541            br#"{"\":1}"#,
542            br#"{"escaped":"line\nline"}"#,
543            b"{[]}",
544            b"[{}]",
545            b"[{},{},{}]",
546        ];
547        let chunk_variants: &[&[usize]] =
548            &[&[usize::MAX], &[1], &[2], &[3], &[5], &[7], &[1, 3, 2, 7, 1, 4]];
549
550        for input in inputs {
551            for chunks in chunk_variants {
552                let (ours, theirs) = drain_both(input, chunks);
553                assert_eq!(
554                    ours,
555                    theirs,
556                    "differential mismatch on {:?} with chunks {:?}",
557                    std::str::from_utf8(input).unwrap_or("<non-utf8>"),
558                    chunks
559                );
560            }
561        }
562    }
563
564    /// Random byte sequences over a JSON-heavy alphabet under random chunk schedules.
565    #[test]
566    fn differential_random_bytes() {
567        const SEQUENCES: usize = 200;
568        const SCHEDULES: usize = 5;
569        const MAX_LEN: usize = 128;
570
571        let mut rng = StdRng::seed_from_u64(0x00CC_2259_C0DE_C0DE);
572        let alphabet: &[u8] = b"{}[]\":,\\ \n \t a1{}[]\"";
573
574        for seq in 0..SEQUENCES {
575            let len = 1 + (rng.next_u32() as usize % MAX_LEN);
576            let mut bytes = Vec::with_capacity(len);
577            for _ in 0..len {
578                let b = alphabet[rng.next_u32() as usize % alphabet.len()];
579                bytes.push(b);
580            }
581            for sched in 0..SCHEDULES {
582                let mut chunks = Vec::new();
583                let mut remaining = len;
584                while remaining > 0 {
585                    let c = 1 + (rng.next_u32() as usize % remaining.min(16));
586                    chunks.push(c);
587                    remaining -= c;
588                }
589                let (ours, theirs) = drain_both(&bytes, &chunks);
590                assert_eq!(
591                    ours, theirs,
592                    "differential mismatch on seq={} sched={} bytes={:?}",
593                    seq, sched, bytes
594                );
595            }
596        }
597    }
598
599    /// Randomly chunked well-formed JSON-RPC envelopes.
600    #[test]
601    fn differential_random_wellformed_json_rpc() {
602        let mut rng = StdRng::seed_from_u64(0x00DE_C0DE_C002_2259);
603        for _ in 0..20 {
604            let str_len = 500 + (rng.next_u32() as usize % 4096);
605            let filler: String = (0..str_len)
606                .map(|_| {
607                    let b = rng.next_u32() as u8 & 0x7F;
608                    // printable ASCII without quotes or backslashes
609                    if b < 0x20 || b == b'"' || b == b'\\' {
610                        b'a'
611                    } else {
612                        b
613                    }
614                })
615                .map(|b| b as char)
616                .collect();
617            let msg = format!(
618                r#"{{"jsonrpc":"2.0","method":"engine_test","params":["{}"],"id":1}}"#,
619                filler
620            );
621            let bytes = msg.as_bytes();
622
623            let mut chunks = Vec::new();
624            let mut remaining = bytes.len();
625            while remaining > 0 {
626                let c = 1 + (rng.next_u32() as usize % remaining.min(64));
627                chunks.push(c);
628                remaining -= c;
629            }
630
631            let (ours, theirs) = drain_both(bytes, &chunks);
632            assert_eq!(ours, theirs, "differential mismatch on well-formed JSON-RPC");
633            assert!(
634                ours.iter().any(|o| o.is_some()),
635                "well-formed message should decode to at least one frame"
636            );
637        }
638    }
639}