Skip to main content

simoom_h3/
frame.rs

1//! HTTP/3 frame types and SETTINGS (RFC 9114 §7).
2
3use crate::{code, H3Error};
4use simoom_quic::coding::{put_varint, Reader};
5
6pub const DATA: u64 = 0x00;
7pub const HEADERS: u64 = 0x01;
8pub const CANCEL_PUSH: u64 = 0x03;
9pub const SETTINGS: u64 = 0x04;
10pub const PUSH_PROMISE: u64 = 0x05;
11pub const GOAWAY: u64 = 0x07;
12pub const MAX_PUSH_ID: u64 = 0x0d;
13
14/// HTTP/2 frame types that have no HTTP/3 meaning (RFC 9114 §11.2.1).
15pub fn is_reserved_h2(t: u64) -> bool {
16    matches!(t, 0x02 | 0x06 | 0x08 | 0x09)
17}
18
19/// A frame payload that must be exactly one varint.
20pub fn single_varint(p: &[u8]) -> Result<u64, H3Error> {
21    let mut r = Reader::new(p);
22    match r.varint() {
23        Some(v) if r.is_empty() => Ok(v),
24        _ => Err(H3Error::new(code::FRAME_ERROR, "malformed frame")),
25    }
26}
27
28const QPACK_MAX_TABLE_CAPACITY: u64 = 0x01;
29const MAX_FIELD_SECTION_SIZE: u64 = 0x06;
30const QPACK_BLOCKED_STREAMS: u64 = 0x07;
31/// SETTINGS_ENABLE_CONNECT_PROTOCOL (RFC 9220 §3, the same identifier as in HTTP/2).
32const ENABLE_CONNECT_PROTOCOL: u64 = 0x08;
33
34/// The settings simoom understands; others are ignored.
35#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct Settings {
37    pub qpack_max_table_capacity: Option<u64>,
38    pub max_field_section_size: Option<u64>,
39    pub qpack_blocked_streams: Option<u64>,
40    /// 1: the sender (a server) accepts extended CONNECT (RFC 9220).
41    pub enable_connect_protocol: Option<u64>,
42}
43
44impl Settings {
45    /// Appends a SETTINGS frame, with one reserved (grease) setting.
46    pub fn encode(&self, out: &mut Vec<u8>) {
47        let mut p = Vec::with_capacity(24);
48        for (id, v) in [
49            (QPACK_MAX_TABLE_CAPACITY, self.qpack_max_table_capacity),
50            (MAX_FIELD_SECTION_SIZE, self.max_field_section_size),
51            (QPACK_BLOCKED_STREAMS, self.qpack_blocked_streams),
52            (ENABLE_CONNECT_PROTOCOL, self.enable_connect_protocol),
53        ] {
54            if let Some(v) = v {
55                put_varint(&mut p, id);
56                put_varint(&mut p, v);
57            }
58        }
59        // 0x1f * N + 0x21 is reserved for greasing (RFC 9114 §7.2.4.1).
60        put_varint(&mut p, 0x1f * 3 + 0x21);
61        put_varint(&mut p, 0);
62        put_varint(out, SETTINGS);
63        put_varint(out, p.len() as u64);
64        out.extend_from_slice(&p);
65    }
66
67    pub fn decode(p: &[u8]) -> Result<Self, H3Error> {
68        let err = |r| H3Error::new(code::SETTINGS_ERROR, r);
69        let mut s = Settings::default();
70        let mut r = Reader::new(p);
71        let mut seen: Vec<u64> = Vec::new();
72        while !r.is_empty() {
73            let (Some(id), Some(v)) = (r.varint(), r.varint()) else {
74                return Err(H3Error::new(code::FRAME_ERROR, "malformed SETTINGS"));
75            };
76            if seen.contains(&id) {
77                return Err(err("duplicate setting"));
78            }
79            if seen.len() >= 256 {
80                return Err(H3Error::new(code::EXCESSIVE_LOAD, "too many settings"));
81            }
82            seen.push(id);
83            match id {
84                QPACK_MAX_TABLE_CAPACITY => s.qpack_max_table_capacity = Some(v),
85                MAX_FIELD_SECTION_SIZE => s.max_field_section_size = Some(v),
86                QPACK_BLOCKED_STREAMS => s.qpack_blocked_streams = Some(v),
87                ENABLE_CONNECT_PROTOCOL => {
88                    if v > 1 {
89                        return Err(err("invalid SETTINGS_ENABLE_CONNECT_PROTOCOL"));
90                    }
91                    s.enable_connect_protocol = Some(v);
92                }
93                // HTTP/2 settings are an error in HTTP/3 (RFC 9114 §7.2.4.1).
94                0x00 | 0x02 | 0x03 | 0x04 | 0x05 => return Err(err("HTTP/2 setting")),
95                _ => {}
96            }
97        }
98        Ok(s)
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn settings_roundtrip_and_errors() {
108        let s = Settings { qpack_max_table_capacity: Some(4096), max_field_section_size: Some(16384), qpack_blocked_streams: Some(16), enable_connect_protocol: Some(1) };
109        let mut b = Vec::new();
110        s.encode(&mut b);
111        let mut r = Reader::new(&b);
112        assert_eq!(r.varint(), Some(SETTINGS));
113        let len = r.varint().unwrap() as usize;
114        assert_eq!(Settings::decode(r.bytes(len).unwrap()).unwrap(), s);
115        assert_eq!(Settings::decode(&[0x06, 0x01, 0x06, 0x02]).unwrap_err().code, code::SETTINGS_ERROR);
116        assert_eq!(Settings::decode(&[0x02, 0x01]).unwrap_err().code, code::SETTINGS_ERROR);
117        assert_eq!(Settings::decode(&[0x06]).unwrap_err().code, code::FRAME_ERROR);
118        assert!(Settings::decode(&[0x21, 0x05]).is_ok());
119        assert_eq!(Settings::decode(&[0x08, 0x02]).unwrap_err().code, code::SETTINGS_ERROR);
120        assert_eq!(single_varint(&[0x05, 0x00]).unwrap_err().code, code::FRAME_ERROR);
121    }
122}