rtc_shared/
tcp_framing.rs

1//! TCP framing utilities for ICE over TCP (RFC 4571/RFC 6544).
2//!
3//! When using ICE over TCP, all messages must be framed with a 2-byte
4//! big-endian length prefix. This module provides helper functions for
5//! encoding and decoding framed packets without performing any I/O.
6//!
7//! # Protocol Format (RFC 4571 Section 2)
8//!
9//! ```text
10//!  0                   1                   2                   3
11//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
12//! -----------------------------------------------------------------
13//! |             LENGTH            |  STUN/DTLS/RTP packet ...     |
14//! -----------------------------------------------------------------
15//! ```
16//!
17//! # Example Usage
18//!
19//! ```rust
20//! use rtc_shared::tcp_framing::{frame_packet, TcpFrameDecoder};
21//!
22//! // Encoding: add framing header to outbound packet
23//! let packet = b"STUN message data";
24//! let framed = frame_packet(packet);
25//! // Send `framed` over TCP...
26//!
27//! // Decoding: parse framed packets from inbound TCP data
28//! let mut decoder = TcpFrameDecoder::new();
29//! // Feed data as it arrives from TCP...
30//! decoder.extend_from_slice(&framed);
31//! while let Some(packet) = decoder.next_packet() {
32//!     // Process complete packet
33//!     assert_eq!(packet, b"STUN message data");
34//! }
35//! ```
36
37/// Length of the framing header (2 bytes for length prefix).
38pub const FRAMING_HEADER_LEN: usize = 2;
39
40/// Maximum packet size that can be framed (u16::MAX = 65535 bytes).
41pub const MAX_FRAMED_PACKET_SIZE: usize = u16::MAX as usize;
42
43/// Adds RFC 4571 framing header to a packet.
44///
45/// Returns a new `Vec<u8>` containing the 2-byte big-endian length prefix
46/// followed by the packet data.
47///
48/// # Panics
49///
50/// Panics if `buf.len() > 65535` (maximum u16 value).
51///
52/// # Example
53///
54/// ```rust
55/// use rtc_shared::tcp_framing::frame_packet;
56///
57/// let packet = b"Hello, WebRTC!";
58/// let framed = frame_packet(packet);
59///
60/// assert_eq!(framed.len(), 2 + packet.len());
61/// assert_eq!(&framed[0..2], &[0, 14]); // Length = 14 in big-endian
62/// assert_eq!(&framed[2..], packet);
63/// ```
64pub fn frame_packet(buf: &[u8]) -> Vec<u8> {
65    assert!(
66        buf.len() <= MAX_FRAMED_PACKET_SIZE,
67        "packet length {} exceeds maximum {}",
68        buf.len(),
69        MAX_FRAMED_PACKET_SIZE
70    );
71
72    let mut framed = Vec::with_capacity(FRAMING_HEADER_LEN + buf.len());
73    let header = (buf.len() as u16).to_be_bytes();
74    framed.extend_from_slice(&header);
75    framed.extend_from_slice(buf);
76    framed
77}
78
79/// Adds RFC 4571 framing header to a packet, writing into a provided buffer.
80///
81/// Returns the total number of bytes written (header + payload).
82///
83/// # Arguments
84///
85/// * `buf` - The packet data to frame
86/// * `out` - Output buffer (must be at least `buf.len() + 2` bytes)
87///
88/// # Returns
89///
90/// * `Some(n)` - Total bytes written to `out`
91/// * `None` - If `out` is too small or `buf` exceeds max size
92///
93/// # Example
94///
95/// ```rust
96/// use rtc_shared::tcp_framing::frame_packet_to;
97///
98/// let packet = b"Hello";
99/// let mut out = [0u8; 100];
100///
101/// let n = frame_packet_to(packet, &mut out).unwrap();
102/// assert_eq!(n, 7); // 2 byte header + 5 byte payload
103/// ```
104pub fn frame_packet_to(buf: &[u8], out: &mut [u8]) -> Option<usize> {
105    if buf.len() > MAX_FRAMED_PACKET_SIZE {
106        return None;
107    }
108
109    let total_len = FRAMING_HEADER_LEN + buf.len();
110    if out.len() < total_len {
111        return None;
112    }
113
114    let header = (buf.len() as u16).to_be_bytes();
115    out[..FRAMING_HEADER_LEN].copy_from_slice(&header);
116    out[FRAMING_HEADER_LEN..total_len].copy_from_slice(buf);
117
118    Some(total_len)
119}
120
121/// A stateful decoder for RFC 4571 framed TCP packets.
122///
123/// This decoder buffers incoming TCP data and extracts complete framed packets.
124/// It handles partial reads gracefully - you can feed it data in any chunk size.
125///
126/// # Example
127///
128/// ```rust
129/// use rtc_shared::tcp_framing::TcpFrameDecoder;
130///
131/// let mut decoder = TcpFrameDecoder::new();
132///
133/// // Simulate receiving data in chunks
134/// let chunk1 = &[0, 5, b'H', b'e']; // Partial: header + 2 bytes
135/// let chunk2 = &[b'l', b'l', b'o']; // Remaining 3 bytes
136///
137/// decoder.extend_from_slice(chunk1);
138/// assert!(decoder.next_packet().is_none()); // Not complete yet
139///
140/// decoder.extend_from_slice(chunk2);
141/// assert_eq!(decoder.next_packet(), Some(b"Hello".to_vec()));
142/// ```
143#[derive(Debug, Default)]
144pub struct TcpFrameDecoder {
145    buffer: Vec<u8>,
146}
147
148impl TcpFrameDecoder {
149    /// Creates a new decoder with an empty buffer.
150    pub fn new() -> Self {
151        Self { buffer: Vec::new() }
152    }
153
154    /// Creates a new decoder with pre-allocated buffer capacity.
155    pub fn with_capacity(capacity: usize) -> Self {
156        Self {
157            buffer: Vec::with_capacity(capacity),
158        }
159    }
160
161    /// Appends data to the internal buffer.
162    pub fn extend_from_slice(&mut self, data: &[u8]) {
163        self.buffer.extend_from_slice(data);
164    }
165
166    /// Attempts to extract the next complete packet from the buffer.
167    ///
168    /// Returns `Some(packet)` if a complete packet is available,
169    /// or `None` if more data is needed.
170    ///
171    /// The returned packet does not include the 2-byte length header.
172    pub fn next_packet(&mut self) -> Option<Vec<u8>> {
173        // Need at least the header
174        if self.buffer.len() < FRAMING_HEADER_LEN {
175            return None;
176        }
177
178        // Parse the length
179        let length = u16::from_be_bytes([self.buffer[0], self.buffer[1]]) as usize;
180        let total_len = FRAMING_HEADER_LEN + length;
181
182        // Check if we have the complete packet
183        if self.buffer.len() < total_len {
184            return None;
185        }
186
187        // Extract the packet (skip header)
188        let packet = self.buffer[FRAMING_HEADER_LEN..total_len].to_vec();
189
190        // Remove processed data from buffer
191        self.buffer.drain(..total_len);
192
193        Some(packet)
194    }
195
196    /// Returns the number of buffered bytes.
197    pub fn buffered_len(&self) -> usize {
198        self.buffer.len()
199    }
200
201    /// Returns `true` if the buffer is empty.
202    pub fn is_empty(&self) -> bool {
203        self.buffer.is_empty()
204    }
205
206    /// Clears the internal buffer.
207    pub fn clear(&mut self) {
208        self.buffer.clear();
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215
216    #[test]
217    fn test_frame_packet() {
218        let packet = b"Hello, WebRTC!";
219        let framed = frame_packet(packet);
220
221        assert_eq!(framed.len(), FRAMING_HEADER_LEN + packet.len());
222
223        // Check header (big-endian length)
224        let length = u16::from_be_bytes([framed[0], framed[1]]) as usize;
225        assert_eq!(length, packet.len());
226
227        // Check payload
228        assert_eq!(&framed[FRAMING_HEADER_LEN..], packet);
229    }
230
231    #[test]
232    fn test_frame_packet_to() {
233        let packet = b"Hello";
234        let mut out = [0u8; 100];
235
236        let n = frame_packet_to(packet, &mut out).unwrap();
237        assert_eq!(n, 7);
238        assert_eq!(&out[..n], &frame_packet(packet)[..]);
239    }
240
241    #[test]
242    fn test_frame_packet_to_buffer_too_small() {
243        let packet = b"Hello";
244        let mut out = [0u8; 3]; // Too small
245
246        assert!(frame_packet_to(packet, &mut out).is_none());
247    }
248
249    #[test]
250    fn test_decoder_complete_packet() {
251        let mut decoder = TcpFrameDecoder::new();
252        let framed = frame_packet(b"Test");
253
254        decoder.extend_from_slice(&framed);
255
256        let packet = decoder.next_packet().unwrap();
257        assert_eq!(packet, b"Test");
258        assert!(decoder.is_empty());
259    }
260
261    #[test]
262    fn test_decoder_partial_header() {
263        let mut decoder = TcpFrameDecoder::new();
264
265        // Only first byte of header
266        decoder.extend_from_slice(&[0]);
267        assert!(decoder.next_packet().is_none());
268
269        // Complete header + payload
270        decoder.extend_from_slice(&[5, b'H', b'e', b'l', b'l', b'o']);
271        assert_eq!(decoder.next_packet(), Some(b"Hello".to_vec()));
272    }
273
274    #[test]
275    fn test_decoder_partial_payload() {
276        let mut decoder = TcpFrameDecoder::new();
277
278        // Header says 5 bytes, but only 2 bytes of payload
279        decoder.extend_from_slice(&[0, 5, b'H', b'e']);
280        assert!(decoder.next_packet().is_none());
281        assert_eq!(decoder.buffered_len(), 4);
282
283        // Rest of payload
284        decoder.extend_from_slice(&[b'l', b'l', b'o']);
285        assert_eq!(decoder.next_packet(), Some(b"Hello".to_vec()));
286    }
287
288    #[test]
289    fn test_decoder_multiple_packets() {
290        let mut decoder = TcpFrameDecoder::new();
291
292        let framed1 = frame_packet(b"First");
293        let framed2 = frame_packet(b"Second");
294        let framed3 = frame_packet(b"Third");
295
296        // Feed all at once
297        decoder.extend_from_slice(&framed1);
298        decoder.extend_from_slice(&framed2);
299        decoder.extend_from_slice(&framed3);
300
301        assert_eq!(decoder.next_packet(), Some(b"First".to_vec()));
302        assert_eq!(decoder.next_packet(), Some(b"Second".to_vec()));
303        assert_eq!(decoder.next_packet(), Some(b"Third".to_vec()));
304        assert!(decoder.next_packet().is_none());
305    }
306
307    #[test]
308    fn test_decoder_multiple_packets_interleaved() {
309        let mut decoder = TcpFrameDecoder::new();
310
311        let mut combined = frame_packet(b"First");
312        combined.extend_from_slice(&frame_packet(b"Second"));
313
314        // Feed first 5 bytes (partial first packet)
315        decoder.extend_from_slice(&combined[..5]);
316        assert!(decoder.next_packet().is_none());
317
318        // Feed rest
319        decoder.extend_from_slice(&combined[5..]);
320        assert_eq!(decoder.next_packet(), Some(b"First".to_vec()));
321        assert_eq!(decoder.next_packet(), Some(b"Second".to_vec()));
322    }
323
324    #[test]
325    fn test_empty_packet() {
326        let framed = frame_packet(b"");
327        assert_eq!(framed.len(), FRAMING_HEADER_LEN);
328        assert_eq!(framed, vec![0, 0]);
329
330        let mut decoder = TcpFrameDecoder::new();
331        decoder.extend_from_slice(&framed);
332        assert_eq!(decoder.next_packet(), Some(vec![]));
333    }
334
335    #[test]
336    #[should_panic(expected = "packet length")]
337    fn test_frame_packet_too_large() {
338        let huge = vec![0u8; MAX_FRAMED_PACKET_SIZE + 1];
339        frame_packet(&huge);
340    }
341
342    #[test]
343    fn test_decoder_clear() {
344        let mut decoder = TcpFrameDecoder::new();
345        decoder.extend_from_slice(&[0, 5, b'H']);
346
347        assert_eq!(decoder.buffered_len(), 3);
348        decoder.clear();
349        assert!(decoder.is_empty());
350    }
351}