Skip to main content

simoom_h2/
frame.rs

1//! HTTP/2 frame layout (RFC 9113 §4, §6): constants, the 9-byte header, and writers that append
2//! frames to an output buffer.
3
4use std::fmt;
5
6/// The client connection preface (RFC 9113 §3.4).
7pub const PREFACE: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
8
9/// Frame header length.
10pub const HEADER_LEN: usize = 9;
11
12/// Frame types.
13pub mod kind {
14    pub const DATA: u8 = 0x0;
15    pub const HEADERS: u8 = 0x1;
16    pub const PRIORITY: u8 = 0x2;
17    pub const RST_STREAM: u8 = 0x3;
18    pub const SETTINGS: u8 = 0x4;
19    pub const PUSH_PROMISE: u8 = 0x5;
20    pub const PING: u8 = 0x6;
21    pub const GOAWAY: u8 = 0x7;
22    pub const WINDOW_UPDATE: u8 = 0x8;
23    pub const CONTINUATION: u8 = 0x9;
24}
25
26/// Frame flags.
27pub mod flag {
28    pub const END_STREAM: u8 = 0x1;
29    pub const ACK: u8 = 0x1;
30    pub const END_HEADERS: u8 = 0x4;
31    pub const PADDED: u8 = 0x8;
32    pub const PRIORITY: u8 = 0x20;
33}
34
35/// SETTINGS identifiers.
36pub mod setting {
37    pub const HEADER_TABLE_SIZE: u16 = 0x1;
38    pub const ENABLE_PUSH: u16 = 0x2;
39    pub const MAX_CONCURRENT_STREAMS: u16 = 0x3;
40    pub const INITIAL_WINDOW_SIZE: u16 = 0x4;
41    pub const MAX_FRAME_SIZE: u16 = 0x5;
42    pub const MAX_HEADER_LIST_SIZE: u16 = 0x6;
43    pub const ENABLE_CONNECT_PROTOCOL: u16 = 0x8;
44}
45
46/// Largest flow-control window (RFC 9113 §6.9.1).
47pub const MAX_WINDOW: u32 = (1 << 31) - 1;
48/// The protocol's initial window (RFC 9113 §6.9.2).
49pub const DEFAULT_WINDOW: u32 = 65_535;
50/// Smallest and largest SETTINGS_MAX_FRAME_SIZE.
51pub const MIN_FRAME_SIZE: u32 = 16_384;
52pub const MAX_FRAME_SIZE: u32 = (1 << 24) - 1;
53
54/// HTTP/2 error codes (RFC 9113 §7).
55#[derive(Clone, Copy, PartialEq, Eq, Hash)]
56pub struct ErrorCode(pub u32);
57
58impl ErrorCode {
59    pub const NO_ERROR: ErrorCode = ErrorCode(0x0);
60    pub const PROTOCOL_ERROR: ErrorCode = ErrorCode(0x1);
61    pub const INTERNAL_ERROR: ErrorCode = ErrorCode(0x2);
62    pub const FLOW_CONTROL_ERROR: ErrorCode = ErrorCode(0x3);
63    pub const SETTINGS_TIMEOUT: ErrorCode = ErrorCode(0x4);
64    pub const STREAM_CLOSED: ErrorCode = ErrorCode(0x5);
65    pub const FRAME_SIZE_ERROR: ErrorCode = ErrorCode(0x6);
66    pub const REFUSED_STREAM: ErrorCode = ErrorCode(0x7);
67    pub const CANCEL: ErrorCode = ErrorCode(0x8);
68    pub const COMPRESSION_ERROR: ErrorCode = ErrorCode(0x9);
69    pub const CONNECT_ERROR: ErrorCode = ErrorCode(0xa);
70    pub const ENHANCE_YOUR_CALM: ErrorCode = ErrorCode(0xb);
71    pub const INADEQUATE_SECURITY: ErrorCode = ErrorCode(0xc);
72    pub const HTTP_1_1_REQUIRED: ErrorCode = ErrorCode(0xd);
73
74    /// The RFC name of the code.
75    pub fn name(self) -> &'static str {
76        const NAMES: [&str; 14] = [
77            "NO_ERROR",
78            "PROTOCOL_ERROR",
79            "INTERNAL_ERROR",
80            "FLOW_CONTROL_ERROR",
81            "SETTINGS_TIMEOUT",
82            "STREAM_CLOSED",
83            "FRAME_SIZE_ERROR",
84            "REFUSED_STREAM",
85            "CANCEL",
86            "COMPRESSION_ERROR",
87            "CONNECT_ERROR",
88            "ENHANCE_YOUR_CALM",
89            "INADEQUATE_SECURITY",
90            "HTTP_1_1_REQUIRED",
91        ];
92        NAMES.get(self.0 as usize).copied().unwrap_or("UNKNOWN")
93    }
94}
95
96impl fmt::Debug for ErrorCode {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        write!(f, "{}", self.name())
99    }
100}
101
102impl fmt::Display for ErrorCode {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(f, "{} (0x{:x})", self.name(), self.0)
105    }
106}
107
108/// A parsed frame header.
109#[derive(Clone, Copy, Debug, PartialEq, Eq)]
110pub struct FrameHeader {
111    pub len: usize,
112    pub kind: u8,
113    pub flags: u8,
114    pub stream: u32,
115}
116
117impl FrameHeader {
118    /// Parses the first 9 bytes of `b` (the reserved bit is ignored, RFC 9113 §4.1).
119    #[inline]
120    pub fn parse(b: &[u8]) -> FrameHeader {
121        FrameHeader {
122            len: ((b[0] as usize) << 16) | ((b[1] as usize) << 8) | b[2] as usize,
123            kind: b[3],
124            flags: b[4],
125            stream: u32::from_be_bytes([b[5], b[6], b[7], b[8]]) & 0x7fff_ffff,
126        }
127    }
128
129    #[inline]
130    pub fn has(&self, f: u8) -> bool {
131        self.flags & f != 0
132    }
133}
134
135/// Appends a frame header.
136#[inline]
137pub fn write_header(out: &mut Vec<u8>, len: usize, kind: u8, flags: u8, stream: u32) {
138    let s = (stream & 0x7fff_ffff).to_be_bytes();
139    out.extend_from_slice(&[(len >> 16) as u8, (len >> 8) as u8, len as u8, kind, flags, s[0], s[1], s[2], s[3]]);
140}
141
142/// Rewrites the length and flags of a header written earlier at `at`.
143#[inline]
144pub(crate) fn patch_header(out: &mut [u8], at: usize, len: usize, flags: u8) {
145    out[at] = (len >> 16) as u8;
146    out[at + 1] = (len >> 8) as u8;
147    out[at + 2] = len as u8;
148    out[at + 4] = flags;
149}
150
151/// Appends a DATA frame.
152pub fn write_data(out: &mut Vec<u8>, stream: u32, data: &[u8], end_stream: bool) {
153    write_header(out, data.len(), kind::DATA, if end_stream { flag::END_STREAM } else { 0 }, stream);
154    out.extend_from_slice(data);
155}
156
157/// Appends a SETTINGS frame.
158pub fn write_settings(out: &mut Vec<u8>, values: &[(u16, u32)]) {
159    write_header(out, values.len() * 6, kind::SETTINGS, 0, 0);
160    for (id, v) in values {
161        out.extend_from_slice(&id.to_be_bytes());
162        out.extend_from_slice(&v.to_be_bytes());
163    }
164}
165
166/// Appends a SETTINGS ACK.
167pub fn write_settings_ack(out: &mut Vec<u8>) {
168    write_header(out, 0, kind::SETTINGS, flag::ACK, 0);
169}
170
171/// Appends a PING.
172pub fn write_ping(out: &mut Vec<u8>, data: [u8; 8], ack: bool) {
173    write_header(out, 8, kind::PING, if ack { flag::ACK } else { 0 }, 0);
174    out.extend_from_slice(&data);
175}
176
177/// Appends a GOAWAY.
178pub fn write_goaway(out: &mut Vec<u8>, last_stream: u32, code: ErrorCode, debug: &[u8]) {
179    write_header(out, 8 + debug.len(), kind::GOAWAY, 0, 0);
180    out.extend_from_slice(&(last_stream & 0x7fff_ffff).to_be_bytes());
181    out.extend_from_slice(&code.0.to_be_bytes());
182    out.extend_from_slice(debug);
183}
184
185/// Appends a RST_STREAM.
186pub fn write_rst(out: &mut Vec<u8>, stream: u32, code: ErrorCode) {
187    write_header(out, 4, kind::RST_STREAM, 0, stream);
188    out.extend_from_slice(&code.0.to_be_bytes());
189}
190
191/// Appends a WINDOW_UPDATE.
192pub fn write_window_update(out: &mut Vec<u8>, stream: u32, increment: u32) {
193    write_header(out, 4, kind::WINDOW_UPDATE, 0, stream);
194    out.extend_from_slice(&(increment & 0x7fff_ffff).to_be_bytes());
195}
196
197/// Appends a HEADERS frame and as many CONTINUATION frames as `block` needs at `max_frame`.
198pub fn write_headers(out: &mut Vec<u8>, stream: u32, block: &[u8], end_stream: bool, max_frame: usize) {
199    let mut chunks = block.chunks(max_frame.max(1));
200    let first = chunks.next().unwrap_or(&[]);
201    let mut rest = chunks.peekable();
202    let mut fl = if end_stream { flag::END_STREAM } else { 0 };
203    if rest.peek().is_none() {
204        fl |= flag::END_HEADERS;
205    }
206    write_header(out, first.len(), kind::HEADERS, fl, stream);
207    out.extend_from_slice(first);
208    while let Some(c) = rest.next() {
209        let fl = if rest.peek().is_none() { flag::END_HEADERS } else { 0 };
210        write_header(out, c.len(), kind::CONTINUATION, fl, stream);
211        out.extend_from_slice(c);
212    }
213}
214
215/// Reads a big-endian u32.
216#[inline]
217pub(crate) fn be32(b: &[u8]) -> u32 {
218    u32::from_be_bytes([b[0], b[1], b[2], b[3]])
219}
220
221/// Splits `buf` into complete frames (header, payload); the rest is ignored. For tests and
222/// tools.
223pub fn split_frames(buf: &[u8]) -> Vec<(FrameHeader, &[u8])> {
224    let mut v = Vec::new();
225    let mut pos = 0;
226    while buf.len() - pos >= HEADER_LEN {
227        let h = FrameHeader::parse(&buf[pos..]);
228        if buf.len() - pos - HEADER_LEN < h.len {
229            break;
230        }
231        v.push((h, &buf[pos + HEADER_LEN..pos + HEADER_LEN + h.len]));
232        pos += HEADER_LEN + h.len;
233    }
234    v
235}
236
237#[cfg(test)]
238mod tests {
239    use super::*;
240
241    #[test]
242    fn layouts() {
243        let mut o = Vec::new();
244        write_data(&mut o, 3, b"hello", true);
245        assert_eq!(o, [0, 0, 5, 0, 1, 0, 0, 0, 3, b'h', b'e', b'l', b'l', b'o']);
246        let h = FrameHeader::parse(&o);
247        assert_eq!(h, FrameHeader { len: 5, kind: kind::DATA, flags: 1, stream: 3 });
248        assert!(h.has(flag::END_STREAM));
249        o.clear();
250        write_window_update(&mut o, 5, 1000);
251        assert_eq!(o, [0, 0, 4, 8, 0, 0, 0, 0, 5, 0, 0, 0x03, 0xe8]);
252        o.clear();
253        write_settings_ack(&mut o);
254        assert_eq!(o, [0, 0, 0, 4, 1, 0, 0, 0, 0]);
255        o.clear();
256        write_goaway(&mut o, 7, ErrorCode::PROTOCOL_ERROR, b"bad");
257        assert_eq!(&o[9..], &[0, 0, 0, 7, 0, 0, 0, 1, b'b', b'a', b'd']);
258        assert_eq!(ErrorCode::CANCEL.to_string(), "CANCEL (0x8)");
259        assert_eq!(format!("{:?}", ErrorCode(99)), "UNKNOWN");
260        // the reserved bit is masked
261        assert_eq!(FrameHeader::parse(&[0, 0, 0, 0, 0, 0x80, 0, 0, 1]).stream, 1);
262    }
263
264    #[test]
265    fn headers_split_into_continuations() {
266        let block: Vec<u8> = (0..25u8).collect();
267        let mut o = Vec::new();
268        write_headers(&mut o, 1, &block, true, 10);
269        let f = split_frames(&o);
270        assert_eq!(f.len(), 3);
271        assert_eq!((f[0].0.kind, f[0].0.flags), (kind::HEADERS, flag::END_STREAM));
272        assert_eq!((f[1].0.kind, f[1].0.flags), (kind::CONTINUATION, 0));
273        assert_eq!((f[2].0.kind, f[2].0.flags), (kind::CONTINUATION, flag::END_HEADERS));
274        let joined: Vec<u8> = f.iter().flat_map(|x| x.1.to_vec()).collect();
275        assert_eq!(joined, block);
276        o.clear();
277        write_headers(&mut o, 1, &[1, 2], false, 10);
278        assert_eq!(split_frames(&o)[0].0.flags, flag::END_HEADERS);
279    }
280}