Skip to main content

barnabas_core/
frame.rs

1//! Kafka's framing: a 4-byte big-endian length, then that many bytes.
2//!
3//! The only place in the crate that deals with partial reads. A socket hands
4//! back whatever arrived — half a header, three frames and a fragment — and
5//! this turns that into whole frames or nothing.
6
7use bytes::{Buf, Bytes, BytesMut};
8
9use crate::{Error, Result};
10
11/// 100 MiB. Larger than any sane `fetch.max.bytes`, small enough that a corrupt
12/// or hostile length prefix cannot steer us into an unbounded allocation.
13pub const DEFAULT_MAX_FRAME: usize = 100 * 1024 * 1024;
14
15/// Reassembles length-prefixed frames from a byte stream.
16#[derive(Debug)]
17pub struct FrameDecoder {
18    buf: BytesMut,
19    max_frame: usize,
20}
21
22impl Default for FrameDecoder {
23    fn default() -> Self {
24        Self::new(DEFAULT_MAX_FRAME)
25    }
26}
27
28impl FrameDecoder {
29    #[must_use]
30    pub fn new(max_frame: usize) -> Self {
31        Self {
32            buf: BytesMut::new(),
33            max_frame,
34        }
35    }
36
37    /// Feed bytes straight off the socket.
38    pub fn push(&mut self, bytes: &[u8]) {
39        self.buf.extend_from_slice(bytes);
40    }
41
42    /// Bytes held pending a complete frame.
43    #[must_use]
44    pub fn buffered(&self) -> usize {
45        self.buf.len()
46    }
47
48    /// How many more bytes are needed before [`Self::next_frame`] can return.
49    ///
50    /// **This is what lets a reader size its read to the frame.** A caller that
51    /// reads into a small fixed buffer issues one syscall per chunk — for a
52    /// 10 MiB fetch response and a 16 KiB buffer, more than six hundred of them
53    /// — and copies every byte twice. Knowing the length prefix turns that into
54    /// a handful of large reads.
55    ///
56    /// Returns 4 while the prefix itself is incomplete, since that is what must
57    /// be read before anything else can be known.
58    #[must_use]
59    pub fn needed(&self) -> usize {
60        if self.buf.len() < 4 {
61            return 4 - self.buf.len();
62        }
63        let len = i32::from_be_bytes([self.buf[0], self.buf[1], self.buf[2], self.buf[3]]);
64        let len = usize::try_from(len).unwrap_or(0).min(self.max_frame);
65        (4 + len).saturating_sub(self.buf.len())
66    }
67
68    /// Take the next complete frame, if one has arrived.
69    ///
70    /// The length prefix is consumed and not returned: every caller wants the
71    /// body, and handing back the prefix invites double-counting it.
72    ///
73    /// # Errors
74    /// [`Error::FrameTooLarge`] if the prefix exceeds the configured limit. The
75    /// check happens *before* reserving, which is the entire point of it.
76    pub fn next_frame(&mut self) -> Result<Option<Bytes>> {
77        if self.buf.len() < 4 {
78            return Ok(None);
79        }
80        let len = i32::from_be_bytes([self.buf[0], self.buf[1], self.buf[2], self.buf[3]]);
81        let len = usize::try_from(len).map_err(|_| Error::FrameTooLarge {
82            len: usize::MAX,
83            limit: self.max_frame,
84        })?;
85        if len > self.max_frame {
86            return Err(Error::FrameTooLarge {
87                len,
88                limit: self.max_frame,
89            });
90        }
91        if self.buf.len() < 4 + len {
92            return Ok(None);
93        }
94        self.buf.advance(4);
95        Ok(Some(self.buf.split_to(len).freeze()))
96    }
97}
98
99/// Prefix `body` with its length, ready to write.
100///
101/// # Errors
102/// [`Error::FrameTooLarge`] if the body does not fit in an `i32`.
103pub fn frame(body: &[u8]) -> Result<Bytes> {
104    let len = i32::try_from(body.len()).map_err(|_| Error::FrameTooLarge {
105        len: body.len(),
106        limit: i32::MAX as usize,
107    })?;
108    let mut out = BytesMut::with_capacity(body.len() + 4);
109    out.extend_from_slice(&len.to_be_bytes());
110    out.extend_from_slice(body);
111    Ok(out.freeze())
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn a_whole_frame_round_trips() {
120        let mut dec = FrameDecoder::default();
121        dec.push(&frame(b"hello").unwrap());
122        assert_eq!(dec.next_frame().unwrap().as_deref(), Some(&b"hello"[..]));
123        assert!(dec.next_frame().unwrap().is_none());
124    }
125
126    /// A socket splits wherever it likes. Feeding one byte at a time is the
127    /// harshest version of that, and must yield exactly one frame at the end.
128    #[test]
129    fn a_frame_split_byte_by_byte_still_arrives() {
130        let bytes = frame(b"a longer payload").unwrap();
131        let mut dec = FrameDecoder::default();
132        for (i, b) in bytes.iter().enumerate() {
133            dec.push(&[*b]);
134            if i + 1 < bytes.len() {
135                assert!(
136                    dec.next_frame().unwrap().is_none(),
137                    "yielded a frame after {} of {} bytes",
138                    i + 1,
139                    bytes.len()
140                );
141            }
142        }
143        assert_eq!(
144            dec.next_frame().unwrap().as_deref(),
145            Some(&b"a longer payload"[..])
146        );
147    }
148
149    /// Several frames in one read, plus a fragment of the next: all the whole
150    /// ones come out, the fragment waits.
151    #[test]
152    fn many_frames_and_a_fragment_in_one_read() {
153        let mut wire = Vec::new();
154        wire.extend_from_slice(&frame(b"one").unwrap());
155        wire.extend_from_slice(&frame(b"two").unwrap());
156        let third = frame(b"three").unwrap();
157        wire.extend_from_slice(&third[..4]); // header only
158
159        let mut dec = FrameDecoder::default();
160        dec.push(&wire);
161        assert_eq!(dec.next_frame().unwrap().as_deref(), Some(&b"one"[..]));
162        assert_eq!(dec.next_frame().unwrap().as_deref(), Some(&b"two"[..]));
163        assert!(dec.next_frame().unwrap().is_none());
164
165        dec.push(&third[4..]);
166        assert_eq!(dec.next_frame().unwrap().as_deref(), Some(&b"three"[..]));
167    }
168
169    /// An empty frame is legal framing and must not be confused with "nothing
170    /// yet" — the difference between `Some(empty)` and `None` is whether the
171    /// caller advances.
172    #[test]
173    fn an_empty_frame_is_a_frame() {
174        let mut dec = FrameDecoder::default();
175        dec.push(&frame(b"").unwrap());
176        assert_eq!(dec.next_frame().unwrap().as_deref(), Some(&b""[..]));
177    }
178
179    /// The allocation guard fires on the prefix, before any reservation.
180    #[test]
181    fn an_absurd_length_prefix_is_rejected_before_allocating() {
182        let mut dec = FrameDecoder::new(1024);
183        dec.push(&i32::MAX.to_be_bytes());
184        assert!(matches!(
185            dec.next_frame(),
186            Err(Error::FrameTooLarge { limit: 1024, .. })
187        ));
188        assert_eq!(
189            dec.buffered(),
190            4,
191            "a rejected frame must not consume the buffer; the caller drops the connection"
192        );
193    }
194
195    /// A negative prefix is a corrupt peer, not a small frame.
196    #[test]
197    fn a_negative_length_prefix_is_rejected() {
198        let mut dec = FrameDecoder::default();
199        dec.push(&(-1i32).to_be_bytes());
200        assert!(matches!(dec.next_frame(), Err(Error::FrameTooLarge { .. })));
201    }
202}