Skip to main content

o_sfu_rfc/
tcp_framing.rs

1//! RFC 4571 framing for RTP/RTCP over TCP.
2//! - Framing RTP and RTCP packets over connection-oriented transport: <https://www.rfc-editor.org/rfc/rfc4571>
3
4use std::io::IoSlice;
5
6use crate::stream_decoder::{Decoded, FrameCodec, StreamDecoder};
7
8/// The payload doesn't fit in the 2 bytes length header of a frame or exceeds [`MAX_FRAME_SIZE`].
9#[derive(Debug, PartialEq, Eq, thiserror::Error)]
10#[error("frame exceeds the maximum size")]
11pub struct FrameTooLargeError;
12
13#[derive(Debug, PartialEq)]
14pub struct TcpFrame {
15    prefix: [u8; 2],
16    payload: Vec<u8>,
17}
18
19impl TcpFrame {
20    /// Frames `payload` with its RFC 4571 length prefix.
21    ///
22    /// # Errors
23    ///
24    /// Returns [`FrameTooLargeError`] when the payload doesn't fit in the 2 bytes length
25    /// header.
26    pub fn try_new(payload: Vec<u8>) -> Result<Self, FrameTooLargeError> {
27        let length = u16::try_from(payload.len()).map_err(|_e| FrameTooLargeError)?;
28        Ok(Self {
29            prefix: u16::to_be_bytes(length),
30            payload,
31        })
32    }
33
34    /// Whether this frame is NULL according to RFC 4571 (i.e. prefix length of 0).
35    #[must_use]
36    pub fn is_null(&self) -> bool {
37        self.payload.is_empty()
38    }
39
40    #[must_use]
41    pub fn io_slices(&self) -> [IoSlice<'_>; 2] {
42        [IoSlice::new(&self.prefix), IoSlice::new(&self.payload)]
43    }
44    #[must_use]
45    pub fn into_payload(self) -> Vec<u8> {
46        self.payload
47    }
48
49    #[must_use]
50    pub fn payload(&self) -> &[u8] {
51        &self.payload
52    }
53}
54
55/// Largest frame payload the decoder accepts, in bytes.
56///
57/// Stricter than RFC 4571, which allows up to 65535 bytes. The decoder buffers a whole
58/// frame before returning it, so this is the most a peer can make us hold.
59///
60/// STUN, DTLS and RTCP should keep their messages within the path MTU (RFC 8489 §6.1, RFC
61/// 9147 §4.3, RFC 3550 §6.1), which is usually 1500 bytes.
62pub const MAX_FRAME_SIZE: u16 = 4096;
63
64/// Incremental decoder of RFC 4571 frames, see [`StreamDecoder`].
65pub type TcpFrameDecoder = StreamDecoder<TcpCodec>;
66
67/// Finds RFC 4571 frames: a 2 bytes length prefix, then the payload.
68#[derive(Debug)]
69pub struct TcpCodec;
70
71impl FrameCodec for TcpCodec {
72    type Frame = TcpFrame;
73    type Error = FrameTooLargeError;
74
75    fn try_decode(unread: &[u8]) -> Decoded<TcpFrame, FrameTooLargeError> {
76        let &[first, second] = unread.first_chunk::<2>()?;
77        let length = u16::from_be_bytes([first, second]);
78        if length > MAX_FRAME_SIZE {
79            return Some(Err(FrameTooLargeError));
80        }
81        let frame_end = 2 + usize::from(length);
82        let payload = unread.get(2..frame_end)?;
83        Some(TcpFrame::try_new(payload.to_vec()).map(|frame| (frame, frame_end)))
84    }
85}
86
87#[cfg(test)]
88#[path = "TESTS/tcp_framing.rs"]
89mod tests;