wtransport_proto/
settings.rs1use crate::bytes::BufferReader;
2use crate::bytes::BufferWriter;
3use crate::bytes::BytesReader;
4use crate::bytes::BytesWriter;
5use crate::bytes::EndOfBuffer;
6use crate::error::ErrorCode;
7use crate::frame::Frame;
8use crate::frame::FrameKind;
9use crate::varint::VarInt;
10use std::borrow::Cow;
11use std::collections::hash_map;
12use std::collections::HashMap;
13
14enum ParseError {
15 ReservedSetting,
16 UnknownSetting,
17}
18
19#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
21pub enum SettingId {
22 QPackMaxTableCapacity,
24
25 MaxFieldSectionSize,
27
28 QPackBlockedStreams,
30
31 EnableConnectProtocol,
33
34 H3Datagram,
36
37 EnableWebTransport,
39
40 WebTransportMaxSessions,
42
43 Exercise(VarInt),
45}
46
47impl SettingId {
48 fn parse(id: VarInt) -> Result<Self, ParseError> {
49 if Self::is_reserved(id) {
50 return Err(ParseError::ReservedSetting);
51 }
52
53 if Self::is_exercise(id) {
54 Ok(Self::Exercise(id))
55 } else {
56 match id {
57 setting_ids::SETTINGS_QPACK_MAX_TABLE_CAPACITY => Ok(Self::QPackMaxTableCapacity),
58 setting_ids::SETTINGS_MAX_FIELD_SECTION_SIZE => Ok(Self::MaxFieldSectionSize),
59 setting_ids::SETTINGS_QPACK_BLOCKED_STREAMS => Ok(Self::QPackBlockedStreams),
60 setting_ids::SETTINGS_ENABLE_CONNECT_PROTOCOL => Ok(Self::EnableConnectProtocol),
61 setting_ids::SETTINGS_H3_DATAGRAM => Ok(Self::H3Datagram),
62 setting_ids::SETTINGS_ENABLE_WEBTRANSPORT => Ok(Self::EnableWebTransport),
63 setting_ids::SETTINGS_WEBTRANSPORT_MAX_SESSIONS => {
64 Ok(Self::WebTransportMaxSessions)
65 }
66 _ => Err(ParseError::UnknownSetting),
67 }
68 }
69 }
70
71 const fn id(self) -> VarInt {
72 match self {
73 Self::QPackMaxTableCapacity => setting_ids::SETTINGS_QPACK_MAX_TABLE_CAPACITY,
74 Self::MaxFieldSectionSize => setting_ids::SETTINGS_MAX_FIELD_SECTION_SIZE,
75 Self::QPackBlockedStreams => setting_ids::SETTINGS_QPACK_BLOCKED_STREAMS,
76 Self::EnableConnectProtocol => setting_ids::SETTINGS_ENABLE_CONNECT_PROTOCOL,
77 Self::H3Datagram => setting_ids::SETTINGS_H3_DATAGRAM,
78 Self::EnableWebTransport => setting_ids::SETTINGS_ENABLE_WEBTRANSPORT,
79 Self::WebTransportMaxSessions => setting_ids::SETTINGS_WEBTRANSPORT_MAX_SESSIONS,
80 Self::Exercise(id) => id,
81 }
82 }
83
84 #[inline(always)]
85 const fn is_reserved(id: VarInt) -> bool {
86 matches!(id.into_inner(), 0x0 | 0x2 | 0x3 | 0x4 | 0x5)
87 }
88
89 #[inline(always)]
90 const fn is_exercise(id: VarInt) -> bool {
91 id.into_inner() >= 0x21 && ((id.into_inner() - 0x21) % 0x1f == 0)
92 }
93}
94
95#[derive(Clone, Debug)]
97pub struct Settings(HashMap<SettingId, VarInt>);
98
99impl Settings {
100 pub fn builder() -> SettingsBuilder {
102 SettingsBuilder(Settings::new())
103 }
104
105 pub fn with_frame(frame: &Frame) -> Result<Self, ErrorCode> {
115 assert!(matches!(frame.kind(), FrameKind::Settings));
116
117 let mut settings = Settings::new();
118 let mut buffer_reader = BufferReader::new(frame.payload());
119
120 while buffer_reader.capacity() > 0 {
121 let id = buffer_reader.get_varint().ok_or(ErrorCode::Frame)?;
122 let value = buffer_reader.get_varint().ok_or(ErrorCode::Frame)?;
123
124 match SettingId::parse(id) {
127 Ok(setting_id) => match settings.0.entry(setting_id) {
128 hash_map::Entry::Vacant(slot) => {
129 slot.insert(value);
130 }
131 hash_map::Entry::Occupied(_) => {
132 return Err(ErrorCode::Settings);
133 }
134 },
135 Err(ParseError::UnknownSetting) => {}
136 Err(ParseError::ReservedSetting) => return Err(ErrorCode::Settings),
137 }
138 }
139
140 Ok(settings)
141 }
142
143 pub fn generate_frame(&self) -> Frame {
148 let mut payload = Vec::new();
149
150 for (id, value) in &self.0 {
151 payload.put_varint(id.id()).expect("Vec does not have EOF");
152
153 payload.put_varint(*value).expect("Vec does not have EOF");
154 }
155
156 payload.shrink_to_fit();
157
158 Frame::new_settings(Cow::Owned(payload))
159 }
160
161 pub fn generate_frame_ref<'a>(&self, buffer: &'a mut [u8]) -> Result<Frame<'a>, EndOfBuffer> {
167 let mut bytes_writer = BufferWriter::new(buffer);
168
169 for (id, value) in &self.0 {
170 bytes_writer.put_varint(id.id())?;
171 bytes_writer.put_varint(*value)?;
172 }
173
174 let offset = bytes_writer.offset();
175
176 Ok(Frame::new_settings(Cow::Borrowed(&buffer[..offset])))
177 }
178
179 pub fn get(&self, id: SettingId) -> Option<VarInt> {
181 self.0.get(&id).copied()
182 }
183
184 fn new() -> Self {
185 Self(HashMap::new())
186 }
187}
188
189pub struct SettingsBuilder(Settings);
191
192impl SettingsBuilder {
193 pub fn qpack_max_table_capacity(mut self, value: VarInt) -> Self {
195 self.0 .0.insert(SettingId::QPackMaxTableCapacity, value);
196 self
197 }
198
199 pub fn qpack_blocked_streams(mut self, value: VarInt) -> Self {
201 self.0 .0.insert(SettingId::QPackBlockedStreams, value);
202 self
203 }
204
205 pub fn enable_connect_protocol(mut self) -> Self {
207 self.0
208 .0
209 .insert(SettingId::EnableConnectProtocol, VarInt::from_u32(1));
210 self
211 }
212
213 pub fn enable_webtransport(mut self) -> Self {
215 self.0
216 .0
217 .insert(SettingId::EnableWebTransport, VarInt::from_u32(1));
218 self
219 }
220
221 pub fn enable_h3_datagrams(mut self) -> Self {
223 self.0 .0.insert(SettingId::H3Datagram, VarInt::from_u32(1));
224 self
225 }
226
227 pub fn webtransport_max_sessions(mut self, value: VarInt) -> Self {
229 self.0 .0.insert(SettingId::WebTransportMaxSessions, value);
230 self
231 }
232
233 pub fn build(self) -> Settings {
235 self.0
236 }
237}
238
239mod setting_ids {
240 use crate::varint::VarInt;
241
242 pub const SETTINGS_QPACK_MAX_TABLE_CAPACITY: VarInt = VarInt::from_u32(0x01);
243 pub const SETTINGS_MAX_FIELD_SECTION_SIZE: VarInt = VarInt::from_u32(0x06);
244 pub const SETTINGS_QPACK_BLOCKED_STREAMS: VarInt = VarInt::from_u32(0x07);
245 pub const SETTINGS_ENABLE_CONNECT_PROTOCOL: VarInt = VarInt::from_u32(0x08);
246 pub const SETTINGS_H3_DATAGRAM: VarInt = VarInt::from_u32(0x33);
247 pub const SETTINGS_ENABLE_WEBTRANSPORT: VarInt = VarInt::from_u32(0x2b60_3742);
248 pub const SETTINGS_WEBTRANSPORT_MAX_SESSIONS: VarInt = VarInt::from_u32(0xc671_706a);
249}