-
Notifications
You must be signed in to change notification settings - Fork 0
Fix decoder example to propagate errors #192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ee23219
dcdce6b
929e723
347e9a9
597302a
2134dc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1120,65 +1120,62 @@ 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`; invalid input or oversized frames return `io::Error` from both | ||
| decode and encode): | ||
|
|
||
| ```rust | ||
| // Crate: my_frame_processor.rs | ||
| use bytes::{BytesMut, Buf, BufMut}; | ||
| use tokio_util::codec::{Decoder, Encoder}; | ||
| use std::io; | ||
| ```rust | ||
| // 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 | ||
| 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<Option<Self::Item>, Self::Error> { | ||
| if src.len() < 4 { return Ok(None); } // Not enough data for length prefix | ||
| fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { | ||
| if src.len() < 4 { return Ok(None); } // Not enough data for length prefix | ||
|
|
||
| let length = u32::from_be_bytes( | ||
| src[..4].try_into().expect("slice length checked"), | ||
| ) 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 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))) | ||
| } | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (review_instructions): Advance the buffer by 4 bytes to consume the length prefix before splitting out the frame payload. Currently, you split out the payload without first advancing past the 4-byte length prefix, so the returned BytesMut will include the length prefix. You should call src.advance(4) before split_to(length). Review instructions:Path patterns: Instructions:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (review_instructions): Advance the buffer by 4 bytes to consume the length prefix before splitting the payload. You must call src.advance(4) before src.split_to(length) to remove the length prefix from the buffer. Otherwise, the returned frame will include the length prefix, which is incorrect. Review instructions:Path patterns: Instructions: |
||
| src.advance(4); // Consume length prefix | ||
| Ok(Some(src.split_to(length))) | ||
| } | ||
| } | ||
| impl<T: AsRef<[u8]>> Encoder<T> for LengthPrefixedCodec { | ||
| type Error = io::Error; | ||
|
|
||
| impl<T: AsRef<[u8]>> Encoder<T> for LengthPrefixedCodec { | ||
| type Error = io::Error; | ||
|
|
||
| fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> { | ||
| let data = item.as_ref(); | ||
| let length = u32::try_from(data.len()).map_err(|_| { | ||
| io::Error::new( | ||
| io::ErrorKind::InvalidInput, | ||
| "payload exceeds 4 GiB limit", | ||
| ) | ||
| })?; | ||
| dst.reserve(4 + data.len()); | ||
| dst.put_u32(length); | ||
| dst.put_slice(data); | ||
| Ok(()) | ||
| 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")); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| (Note: "wireframe" would abstract the direct use of `Encoder`/`Decoder` behind | ||
| its own `FrameProcessor` trait or provide helpers.) | ||
| dst.reserve(4 + data.len()); | ||
| dst.put_u32(data.len() as u32); | ||
| dst.put_slice(data); | ||
| Ok(()) | ||
| } | ||
| } | ||
|
Comment on lines
+1128
to
+1174
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Introduce a The literal @@
- if src.len() < 4 { return Ok(None); } // Not enough data for length prefix
+ const LEN_PREFIX: usize = 4;
+ if src.len() < LEN_PREFIX {
+ return Ok(None); // Not enough data for length prefix
+ }
@@
- if src.len() < 4 + length {
+ if src.len() < LEN_PREFIX + length {
src.reserve(LEN_PREFIX + length - src.len());
return Ok(None); // Not enough data for full frame
}
- src.advance(4); // Consume length prefix
+ src.advance(LEN_PREFIX); // Consume length prefix
@@
- dst.reserve(4 + data.len());
- dst.put_u32(data.len() as u32);
+ dst.reserve(LEN_PREFIX + data.len());
+ dst.put_u32(data.len() as u32);
🤖 Prompt for AI Agents |
||
| ``` | ||
|
|
||
| (Note: "wireframe" would abstract the direct use of `Encoder`/`Decoder` behind | ||
| its own `FrameProcessor` trait or provide helpers.) | ||
|
Comment on lines
+1177
to
+1178
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Line length exceeds documentation guidelines
🤖 Prompt for AI Agents |
||
|
|
||
| 1. **Server Setup and Handler**: | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Wrap prose at 80 columns to satisfy documentation linting
The bullet text introducing the codec example runs far beyond the 80-column limit mandated for
docs/**/*.md. This will failmarkdownlint/CI.🤖 Prompt for AI Agents