Skip to main content

micro_h2/
frame.rs

1//! HTTP/2 framing (RFC 7540 section 4).
2//!
3//! ```text
4//! 3B length (24-bit BE) ‖ 1B type ‖ 1B flags ‖ 4B stream id (31-bit BE)
5//! ```
6//!
7//! The stream identifier's top bit is reserved and must be ignored on receipt,
8//! not treated as part of the number — a detail that only bites when a peer
9//! happens to set it.
10
11use crate::Error;
12
13/// Every frame begins with nine bytes. That this is also the length of the
14/// ts2021 early-payload probe is what makes the two distinguishable; see
15/// `tailfeather::noise::early`.
16pub const HEADER_LEN: usize = 9;
17
18/// The largest frame every endpoint must accept, and the default until
19/// `SETTINGS` raises it.
20pub const DEFAULT_MAX_FRAME: usize = 16_384;
21
22/// The connection preface a client sends before anything else (RFC 7540
23/// section 3.5). Exact bytes, including the deliberately odd `PRI` method — its
24/// purpose is to make an HTTP/1.1 server fail immediately rather than
25/// misinterpret what follows.
26pub const CLIENT_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum FrameType {
30    Data,
31    Headers,
32    Priority,
33    RstStream,
34    Settings,
35    PushPromise,
36    Ping,
37    GoAway,
38    WindowUpdate,
39    Continuation,
40    /// A type this client does not know. RFC 7540 requires unknown frame types
41    /// to be discarded, not treated as an error — that is what lets the protocol
42    /// be extended without breaking older peers.
43    Unknown(u8),
44}
45
46impl FrameType {
47    pub fn from_byte(byte: u8) -> Self {
48        match byte {
49            0x0 => Self::Data,
50            0x1 => Self::Headers,
51            0x2 => Self::Priority,
52            0x3 => Self::RstStream,
53            0x4 => Self::Settings,
54            0x5 => Self::PushPromise,
55            0x6 => Self::Ping,
56            0x7 => Self::GoAway,
57            0x8 => Self::WindowUpdate,
58            0x9 => Self::Continuation,
59            other => Self::Unknown(other),
60        }
61    }
62
63    pub fn to_byte(self) -> u8 {
64        match self {
65            Self::Data => 0x0,
66            Self::Headers => 0x1,
67            Self::Priority => 0x2,
68            Self::RstStream => 0x3,
69            Self::Settings => 0x4,
70            Self::PushPromise => 0x5,
71            Self::Ping => 0x6,
72            Self::GoAway => 0x7,
73            Self::WindowUpdate => 0x8,
74            Self::Continuation => 0x9,
75            Self::Unknown(other) => other,
76        }
77    }
78}
79
80pub mod flags {
81    pub const END_STREAM: u8 = 0x1;
82    pub const ACK: u8 = 0x1;
83    pub const END_HEADERS: u8 = 0x4;
84    pub const PADDED: u8 = 0x8;
85    pub const PRIORITY: u8 = 0x20;
86}
87
88pub mod settings {
89    pub const HEADER_TABLE_SIZE: u16 = 0x1;
90    pub const ENABLE_PUSH: u16 = 0x2;
91    pub const MAX_CONCURRENT_STREAMS: u16 = 0x3;
92    pub const INITIAL_WINDOW_SIZE: u16 = 0x4;
93    pub const MAX_FRAME_SIZE: u16 = 0x5;
94    pub const MAX_HEADER_LIST_SIZE: u16 = 0x6;
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub struct FrameHeader {
99    pub length: usize,
100    pub kind: FrameType,
101    pub flags: u8,
102    pub stream: u32,
103}
104
105impl FrameHeader {
106    /// Parse the nine-byte header, without the payload.
107    pub fn parse(bytes: &[u8]) -> Result<Self, Error> {
108        let bytes = bytes.get(..HEADER_LEN).ok_or(Error::Incomplete)?;
109        Ok(Self {
110            length: u32::from_be_bytes([0, bytes[0], bytes[1], bytes[2]]) as usize,
111            kind: FrameType::from_byte(bytes[3]),
112            flags: bytes[4],
113            // The top bit is reserved and must be ignored, not read as part of
114            // the identifier.
115            stream: u32::from_be_bytes([bytes[5], bytes[6], bytes[7], bytes[8]]) & 0x7fff_ffff,
116        })
117    }
118
119    pub fn write(&self, out: &mut [u8]) -> Result<usize, Error> {
120        let out = out.get_mut(..HEADER_LEN).ok_or(Error::BufferTooSmall)?;
121        let length = (self.length as u32).to_be_bytes();
122        out[0..3].copy_from_slice(&length[1..4]);
123        out[3] = self.kind.to_byte();
124        out[4] = self.flags;
125        out[5..9].copy_from_slice(&(self.stream & 0x7fff_ffff).to_be_bytes());
126        Ok(HEADER_LEN)
127    }
128
129    pub fn has(&self, flag: u8) -> bool {
130        self.flags & flag != 0
131    }
132}
133
134/// Write a complete frame: header then payload.
135pub fn write_frame(
136    kind: FrameType,
137    flags: u8,
138    stream: u32,
139    payload: &[u8],
140    out: &mut [u8],
141) -> Result<usize, Error> {
142    let header = FrameHeader {
143        length: payload.len(),
144        kind,
145        flags,
146        stream,
147    };
148    header.write(out)?;
149    let end = HEADER_LEN + payload.len();
150    out.get_mut(HEADER_LEN..end)
151        .ok_or(Error::BufferTooSmall)?
152        .copy_from_slice(payload);
153    Ok(end)
154}
155
156/// Strip the padding a DATA or HEADERS frame may carry.
157///
158/// The pad length is one byte at the front, and the padding itself is at the
159/// back. Forgetting it feeds padding bytes to the HPACK decoder, which then
160/// fails on a frame that was perfectly valid.
161pub fn strip_padding(payload: &[u8], flags: u8) -> Result<&[u8], Error> {
162    if flags & flags::PADDED == 0 {
163        return Ok(payload);
164    }
165    let pad_length = *payload.first().ok_or(Error::Protocol)? as usize;
166    let body = payload.get(1..).ok_or(Error::Protocol)?;
167    let end = body.len().checked_sub(pad_length).ok_or(Error::Protocol)?;
168    Ok(&body[..end])
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174
175    #[test]
176    fn parses_the_settings_frame_a_server_opens_with() {
177        // Length 18, type SETTINGS, no flags, stream 0.
178        let bytes = [0x00, 0x00, 0x12, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00];
179        let header = FrameHeader::parse(&bytes).unwrap();
180        assert_eq!(header.length, 18);
181        assert_eq!(header.kind, FrameType::Settings);
182        assert_eq!(header.stream, 0);
183        assert!(!header.has(flags::ACK));
184    }
185
186    #[test]
187    fn the_reserved_bit_of_a_stream_id_is_ignored() {
188        // A peer that sets it must not make us read stream 1 as 2147483649.
189        let bytes = [0x00, 0x00, 0x00, 0x01, 0x04, 0x80, 0x00, 0x00, 0x01];
190        assert_eq!(FrameHeader::parse(&bytes).unwrap().stream, 1);
191    }
192
193    #[test]
194    fn a_header_round_trips() {
195        let header = FrameHeader {
196            length: 16_383,
197            kind: FrameType::Data,
198            flags: flags::END_STREAM,
199            stream: 3,
200        };
201        let mut out = [0u8; HEADER_LEN];
202        header.write(&mut out).unwrap();
203        assert_eq!(FrameHeader::parse(&out).unwrap(), header);
204    }
205
206    #[test]
207    fn an_unknown_frame_type_is_carried_rather_than_rejected() {
208        // RFC 7540 requires discarding, not failing: that is what lets the
209        // protocol gain frame types without breaking older clients.
210        let bytes = [0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00];
211        assert_eq!(
212            FrameHeader::parse(&bytes).unwrap().kind,
213            FrameType::Unknown(0x63)
214        );
215    }
216
217    #[test]
218    fn a_short_buffer_is_incomplete_rather_than_a_guess() {
219        assert_eq!(FrameHeader::parse(&[0x00, 0x00]), Err(Error::Incomplete));
220    }
221
222    #[test]
223    fn padding_is_stripped_from_both_ends() {
224        // One byte of pad length at the front, that many bytes at the back.
225        let payload = [0x02, b'h', b'i', 0x00, 0x00];
226        assert_eq!(strip_padding(&payload, flags::PADDED).unwrap(), b"hi");
227        // Without the flag the first byte is data, not a length.
228        assert_eq!(strip_padding(&payload, 0).unwrap(), &payload);
229        // Padding longer than the frame is a protocol error, not a wrap-around.
230        assert_eq!(
231            strip_padding(&[0x09, b'h'], flags::PADDED),
232            Err(Error::Protocol)
233        );
234    }
235}