From ee232196dbbfd9298d61c8d24618d8b9020284ee Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 6 Jul 2025 15:55:46 +0100 Subject: [PATCH 1/5] Refine length-prefixed decoding example --- docs/rust-binary-router-library-design.md | 84 +++++++++++------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/rust-binary-router-library-design.md b/docs/rust-binary-router-library-design.md index 3599733d..115fa9e7 100644 --- a/docs/rust-binary-router-library-design.md +++ b/docs/rust-binary-router-library-design.md @@ -1120,60 +1120,60 @@ examples are invaluable. They make the abstract design tangible and showcase how } ``` - 2. **Frame Processor Implementation** (Simple Length-Prefixed Framing using - `tokio-util`): +1. **Frame Processor Implementation** (Simple length-prefixed framing using + `tokio-util`; demonstrates error propagation): - ```rust - // Crate: my_frame_processor.rs - use bytes::{BytesMut, Buf, BufMut}; - use tokio_util::codec::{Decoder, Encoder}; - use byteorder::{BigEndian, ReadBytesExt}; - use std::io; +```rust +// Crate: my_frame_processor.rs +use bytes::{BytesMut, Buf, BufMut}; +use tokio_util::codec::{Decoder, Encoder}; +use byteorder::{BigEndian, ReadBytesExt}; +use std::io; - const MAX_FRAME_LEN: usize = 16 * 1024 * 1024; // 16 MiB upper limit +const MAX_FRAME_LEN: usize = 16 * 1024 * 1024; // 16 MiB upper limit - pub struct LengthPrefixedCodec; +pub struct LengthPrefixedCodec; - impl Decoder for LengthPrefixedCodec { - type Item = BytesMut; // Raw frame payload - type Error = io::Error; +impl Decoder for LengthPrefixedCodec { + type Item = BytesMut; // Raw frame payload + type Error = io::Error; - fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { - if src.len() < 4 { return Ok(None); } // Not enough data for length prefix + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + if src.len() < 4 { return Ok(None); } // Not enough data for length prefix - let length = (&src[..4]) - .read_u32::() - .expect("slice length checked") as usize; + let length = (&src[..4]) + .read_u32::()? + as usize; - if length > MAX_FRAME_LEN { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame too large")); - } + if length > MAX_FRAME_LEN { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame too large")); + } - if src.len() < 4 + length { - src.reserve(4 + length - src.len()); - return Ok(None); // Not enough data for full frame - } + if src.len() < 4 + length { + src.reserve(4 + length - src.len()); + return Ok(None); // Not enough data for full frame + } - src.advance(4); // Consume length prefix - Ok(Some(src.split_to(length))) - } - } + src.advance(4); // Consume length prefix + Ok(Some(src.split_to(length))) + } +} - impl> Encoder for LengthPrefixedCodec { - type Error = io::Error; +impl> Encoder for LengthPrefixedCodec { + type Error = io::Error; - fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> { - let data = item.as_ref(); - dst.reserve(4 + data.len()); - dst.put_u32(data.len() as u32); - dst.put_slice(data); - Ok(()) - } - } - ``` + fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> { + let data = item.as_ref(); + dst.reserve(4 + data.len()); + dst.put_u32(data.len() as u32); + dst.put_slice(data); + Ok(()) + } +} +``` - (Note: "wireframe" would abstract the direct use of `Encoder`/`Decoder` behind - its own `FrameProcessor` trait or provide helpers.) +(Note: "wireframe" would abstract the direct use of `Encoder`/`Decoder` behind +its own `FrameProcessor` trait or provide helpers.) 1. **Server Setup and Handler**: From dcdce6b8e9c8a51d670db2396ee3f0f4412fd5cb Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 6 Jul 2025 16:41:16 +0100 Subject: [PATCH 2/5] Handle invalid frame sizes --- docs/rust-binary-router-library-design.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/rust-binary-router-library-design.md b/docs/rust-binary-router-library-design.md index 115fa9e7..83fa46be 100644 --- a/docs/rust-binary-router-library-design.md +++ b/docs/rust-binary-router-library-design.md @@ -1128,7 +1128,7 @@ examples are invaluable. They make the abstract design tangible and showcase how use bytes::{BytesMut, Buf, BufMut}; use tokio_util::codec::{Decoder, Encoder}; use byteorder::{BigEndian, ReadBytesExt}; -use std::io; +use std::io::{self, Cursor}; const MAX_FRAME_LEN: usize = 16 * 1024 * 1024; // 16 MiB upper limit @@ -1141,9 +1141,8 @@ impl Decoder for LengthPrefixedCodec { fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { if src.len() < 4 { return Ok(None); } // Not enough data for length prefix - let length = (&src[..4]) - .read_u32::()? - as usize; + let mut length_cursor = Cursor::new(&src[..4]); + let length = length_cursor.read_u32::()? as usize; if length > MAX_FRAME_LEN { return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame too large")); @@ -1164,6 +1163,10 @@ impl> Encoder for LengthPrefixedCodec { fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> { let data = item.as_ref(); + if data.len() > MAX_FRAME_LEN { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame too large")); + } + dst.reserve(4 + data.len()); dst.put_u32(data.len() as u32); dst.put_slice(data); From 929e723238d272cd855c1d2e588c46c66e829488 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 6 Jul 2025 21:15:16 +0100 Subject: [PATCH 3/5] Improve frame processor example --- docs/rust-binary-router-library-design.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/rust-binary-router-library-design.md b/docs/rust-binary-router-library-design.md index 83fa46be..ca5c823b 100644 --- a/docs/rust-binary-router-library-design.md +++ b/docs/rust-binary-router-library-design.md @@ -1121,14 +1121,13 @@ examples are invaluable. They make the abstract design tangible and showcase how ``` 1. **Frame Processor Implementation** (Simple length-prefixed framing using - `tokio-util`; demonstrates error propagation): + `tokio-util`; invalid input or oversized frames return errors): ```rust // Crate: my_frame_processor.rs use bytes::{BytesMut, Buf, BufMut}; use tokio_util::codec::{Decoder, Encoder}; -use byteorder::{BigEndian, ReadBytesExt}; -use std::io::{self, Cursor}; +use std::io; const MAX_FRAME_LEN: usize = 16 * 1024 * 1024; // 16 MiB upper limit @@ -1141,8 +1140,7 @@ impl Decoder for LengthPrefixedCodec { fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { if src.len() < 4 { return Ok(None); } // Not enough data for length prefix - let mut length_cursor = Cursor::new(&src[..4]); - let length = length_cursor.read_u32::()? as usize; + let length = (&src[..4]).get_u32() as usize; if length > MAX_FRAME_LEN { return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame too large")); From 347e9a9e1515adbe84292a10a8e16c63328d11f0 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 6 Jul 2025 21:48:59 +0100 Subject: [PATCH 4/5] Explain error propagation --- docs/rust-binary-router-library-design.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/rust-binary-router-library-design.md b/docs/rust-binary-router-library-design.md index ca5c823b..20b3245c 100644 --- a/docs/rust-binary-router-library-design.md +++ b/docs/rust-binary-router-library-design.md @@ -1121,7 +1121,8 @@ examples are invaluable. They make the abstract design tangible and showcase how ``` 1. **Frame Processor Implementation** (Simple length-prefixed framing using - `tokio-util`; invalid input or oversized frames return errors): + `tokio-util`; invalid input or oversized frames return `io::Error` from both + decode and encode): ```rust // Crate: my_frame_processor.rs @@ -1140,18 +1141,17 @@ impl Decoder for LengthPrefixedCodec { fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { if src.len() < 4 { return Ok(None); } // Not enough data for length prefix - let length = (&src[..4]).get_u32() as usize; + let length = src.get_u32() as usize; if length > MAX_FRAME_LEN { return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame too large")); } - if src.len() < 4 + length { - src.reserve(4 + length - src.len()); + if src.len() < length { + src.reserve(length - src.len()); return Ok(None); // Not enough data for full frame } - src.advance(4); // Consume length prefix Ok(Some(src.split_to(length))) } } From 597302ab726bbbaf9fea3248399cf8962bbec916 Mon Sep 17 00:00:00 2001 From: Leynos Date: Mon, 7 Jul 2025 19:59:16 +0100 Subject: [PATCH 5/5] Fix decoder buffer checks --- docs/rust-binary-router-library-design.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/rust-binary-router-library-design.md b/docs/rust-binary-router-library-design.md index 20b3245c..0f22aad4 100644 --- a/docs/rust-binary-router-library-design.md +++ b/docs/rust-binary-router-library-design.md @@ -1128,6 +1128,7 @@ examples are invaluable. They make the abstract design tangible and showcase how // Crate: my_frame_processor.rs use bytes::{BytesMut, Buf, BufMut}; use tokio_util::codec::{Decoder, Encoder}; +use byteorder::{BigEndian, ByteOrder}; use std::io; const MAX_FRAME_LEN: usize = 16 * 1024 * 1024; // 16 MiB upper limit @@ -1141,17 +1142,17 @@ impl Decoder for LengthPrefixedCodec { fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { if src.len() < 4 { return Ok(None); } // Not enough data for length prefix - let length = src.get_u32() as usize; + let length = BigEndian::read_u32(&src[..4]) as usize; if length > MAX_FRAME_LEN { return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame too large")); } - if src.len() < length { - src.reserve(length - src.len()); + if src.len() < 4 + length { + src.reserve(4 + length - src.len()); return Ok(None); // Not enough data for full frame } - + src.advance(4); // Consume length prefix Ok(Some(src.split_to(length))) } }