1use core::fmt;
4use core::iter::FusedIterator;
5
6use crate::quic::varint::{QuicVarInt, QuicVarIntBuildError};
7
8use super::codepoints::{Http3FrameType, Http3SettingId};
9use super::frame::{
10 Http3Frame, Http3FrameBuildError, Http3FrameBuilder, Http3FramePayloadField,
11 Http3FramePayloadParseError, encode_varint, parse_payload_varint, validate_type,
12};
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum Http3SettingsBuildError {
17 Frame(Http3FrameBuildError),
19 SettingVarInt {
21 field: Http3FramePayloadField,
23 error: QuicVarIntBuildError,
25 },
26 PayloadLengthOverflow {
28 index: usize,
30 current: usize,
32 addition: usize,
34 },
35 ScratchTooShort {
37 required: usize,
39 available: usize,
41 },
42 DuplicateSetting {
44 id: Http3SettingId,
46 },
47 ProhibitedSetting {
49 id: Http3SettingId,
51 },
52}
53
54impl fmt::Display for Http3SettingsBuildError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 Self::Frame(error) => write!(f, "HTTP/3 frame: {error}"),
58 Self::SettingVarInt { field, error } => {
59 write!(f, "HTTP/3 {field} variable-length integer: {error}")
60 }
61 Self::PayloadLengthOverflow {
62 index,
63 current,
64 addition,
65 } => write!(
66 f,
67 "HTTP/3 SETTINGS payload length overflows at entry {index}: {current} plus {addition} bytes"
68 ),
69 Self::ScratchTooShort {
70 required,
71 available,
72 } => write!(
73 f,
74 "HTTP/3 SETTINGS workspace is too short: need {required} bytes, have {available}"
75 ),
76 Self::DuplicateSetting { id } => {
77 write!(
78 f,
79 "HTTP/3 SETTINGS contains duplicate identifier {}",
80 id.value()
81 )
82 }
83 Self::ProhibitedSetting { id } => write!(
84 f,
85 "HTTP/3 SETTINGS identifier {} is reserved for HTTP/2",
86 id.value()
87 ),
88 }
89 }
90}
91
92impl core::error::Error for Http3SettingsBuildError {
93 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
94 match self {
95 Self::Frame(error) => Some(error),
96 Self::SettingVarInt { .. }
97 | Self::PayloadLengthOverflow { .. }
98 | Self::ScratchTooShort { .. }
99 | Self::DuplicateSetting { .. }
100 | Self::ProhibitedSetting { .. } => None,
101 }
102 }
103}
104#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106pub struct Http3Setting<'a> {
107 bytes: &'a [u8],
108 id: QuicVarInt<'a>,
109 value: QuicVarInt<'a>,
110}
111
112impl<'a> Http3Setting<'a> {
113 pub const fn as_bytes(&self) -> &'a [u8] {
115 self.bytes
116 }
117
118 pub const fn id(&self) -> Http3SettingId {
120 Http3SettingId::new(self.id.value())
121 }
122
123 pub const fn id_varint(&self) -> QuicVarInt<'a> {
125 self.id
126 }
127
128 pub const fn value(&self) -> u64 {
130 self.value.value()
131 }
132
133 pub const fn value_varint(&self) -> QuicVarInt<'a> {
135 self.value
136 }
137}
138
139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
141pub struct Http3Settings<'a> {
142 frame: Http3Frame<'a>,
143}
144
145impl<'a> Http3Settings<'a> {
146 const fn from_validated(frame: Http3Frame<'a>) -> Self {
148 Self { frame }
149 }
150
151 pub fn parse(
153 bytes: &'a [u8],
154 maximum_payload: usize,
155 ) -> Result<Self, Http3FramePayloadParseError> {
156 Self::from_frame(
157 Http3Frame::parse(bytes, maximum_payload)
158 .map_err(Http3FramePayloadParseError::Frame)?,
159 )
160 }
161
162 pub fn from_frame(frame: Http3Frame<'a>) -> Result<Self, Http3FramePayloadParseError> {
164 validate_type(frame, Http3FrameType::SETTINGS)?;
165 validate_settings(frame.payload())?;
166 Ok(Self { frame })
167 }
168
169 pub const fn frame(&self) -> Http3Frame<'a> {
171 self.frame
172 }
173
174 pub const fn as_bytes(&self) -> &'a [u8] {
176 self.frame.as_bytes()
177 }
178
179 pub fn settings(&self) -> Http3SettingsIter<'a> {
181 Http3SettingsIter {
182 bytes: self.frame.payload(),
183 }
184 }
185
186 pub fn validate_semantics(self) -> Result<Http3PeerSettings, Http3SettingsSemanticError> {
191 validate(self)
192 }
193}
194
195#[derive(Clone, Debug)]
197pub struct Http3SettingsIter<'a> {
198 bytes: &'a [u8],
199}
200
201impl<'a> Iterator for Http3SettingsIter<'a> {
202 type Item = Http3Setting<'a>;
203
204 fn next(&mut self) -> Option<Self::Item> {
205 let (setting, remaining) = parse_setting(self.bytes)?;
206 self.bytes = remaining;
207 Some(setting)
208 }
209}
210
211impl FusedIterator for Http3SettingsIter<'_> {}
212
213fn validate_settings(payload: &[u8]) -> Result<(), Http3FramePayloadParseError> {
214 let mut remaining = payload;
215 while !remaining.is_empty() {
216 let identifier_offset = payload.len() - remaining.len();
217 let identifier = parse_payload_varint(
218 remaining,
219 Http3FramePayloadField::SettingIdentifier,
220 identifier_offset,
221 )?;
222 remaining = &remaining[identifier.byte_len()..];
223
224 let value_offset = payload.len() - remaining.len();
225 let value = parse_payload_varint(
226 remaining,
227 Http3FramePayloadField::SettingValue,
228 value_offset,
229 )?;
230 remaining = &remaining[value.byte_len()..];
231 }
232 Ok(())
233}
234
235fn parse_setting<'a>(bytes: &'a [u8]) -> Option<(Http3Setting<'a>, &'a [u8])> {
236 let id = QuicVarInt::parse(bytes).ok()?;
237 let after_id = &bytes[id.byte_len()..];
238 let value = QuicVarInt::parse(after_id).ok()?;
239 let remaining = &after_id[value.byte_len()..];
240 let setting = Http3Setting {
241 bytes: &bytes[..bytes.len() - remaining.len()],
242 id,
243 value,
244 };
245 Some((setting, remaining))
246}
247
248#[derive(Clone, Copy, Debug, Eq, PartialEq)]
250pub struct Http3SettingValue {
251 id: Http3SettingId,
252 value: u64,
253}
254
255impl Http3SettingValue {
256 pub const fn new(id: Http3SettingId, value: u64) -> Self {
258 Self { id, value }
259 }
260
261 pub const fn id(self) -> Http3SettingId {
263 self.id
264 }
265
266 pub const fn value(self) -> u64 {
268 self.value
269 }
270}
271
272pub struct Http3SettingsBuilder<'output, 'scratch, 'settings> {
274 destination: &'output mut [u8],
275 payload_scratch: &'scratch mut [u8],
276 settings: &'settings [Http3SettingValue],
277}
278
279impl<'output, 'scratch, 'settings> Http3SettingsBuilder<'output, 'scratch, 'settings> {
280 pub fn new(
285 destination: &'output mut [u8],
286 payload_scratch: &'scratch mut [u8],
287 settings: &'settings [Http3SettingValue],
288 ) -> Self {
289 Self {
290 destination,
291 payload_scratch,
292 settings,
293 }
294 }
295
296 pub fn build(self) -> Result<Http3Settings<'output>, Http3SettingsBuildError> {
298 let payload_length = validate_outbound_settings(self.settings)?;
299 if self.payload_scratch.len() < payload_length {
300 return Err(Http3SettingsBuildError::ScratchTooShort {
301 required: payload_length,
302 available: self.payload_scratch.len(),
303 });
304 }
305
306 let mut offset = 0;
307 for setting in self.settings {
308 let id = encode_varint(setting.id.value()).map_err(|error| {
309 Http3SettingsBuildError::SettingVarInt {
310 field: Http3FramePayloadField::SettingIdentifier,
311 error,
312 }
313 })?;
314 let value = encode_varint(setting.value).map_err(|error| {
315 Http3SettingsBuildError::SettingVarInt {
316 field: Http3FramePayloadField::SettingValue,
317 error,
318 }
319 })?;
320 let id_end = offset + id.byte_len();
321 self.payload_scratch[offset..id_end].copy_from_slice(id.bytes());
322 let value_end = id_end + value.byte_len();
323 self.payload_scratch[id_end..value_end].copy_from_slice(value.bytes());
324 offset = value_end;
325 }
326
327 let frame = Http3FrameBuilder::new(
328 self.destination,
329 Http3FrameType::SETTINGS,
330 &self.payload_scratch[..payload_length],
331 )
332 .build()
333 .map_err(Http3SettingsBuildError::Frame)?;
334 Ok(Http3Settings::from_validated(frame))
335 }
336}
337
338fn validate_outbound_settings(
339 settings: &[Http3SettingValue],
340) -> Result<usize, Http3SettingsBuildError> {
341 let mut payload_length = 0;
342 for (index, setting) in settings.iter().copied().enumerate() {
343 if prohibited_setting(setting.id) {
344 return Err(Http3SettingsBuildError::ProhibitedSetting { id: setting.id });
345 }
346 for previous in &settings[..index] {
347 if previous.id == setting.id {
348 return Err(Http3SettingsBuildError::DuplicateSetting { id: setting.id });
349 }
350 }
351 let id = encode_varint(setting.id.value()).map_err(|error| {
352 Http3SettingsBuildError::SettingVarInt {
353 field: Http3FramePayloadField::SettingIdentifier,
354 error,
355 }
356 })?;
357 let value = encode_varint(setting.value).map_err(|error| {
358 Http3SettingsBuildError::SettingVarInt {
359 field: Http3FramePayloadField::SettingValue,
360 error,
361 }
362 })?;
363 payload_length = add_setting_payload(index, payload_length, id.byte_len())?;
364 payload_length = add_setting_payload(index, payload_length, value.byte_len())?;
365 }
366 Ok(payload_length)
367}
368
369fn add_setting_payload(
370 index: usize,
371 current: usize,
372 addition: usize,
373) -> Result<usize, Http3SettingsBuildError> {
374 current
375 .checked_add(addition)
376 .ok_or(Http3SettingsBuildError::PayloadLengthOverflow {
377 index,
378 current,
379 addition,
380 })
381}
382
383#[derive(Clone, Copy, Debug, Eq, PartialEq)]
387pub enum Http3SettingsSemanticError {
388 DuplicateSetting {
390 id: Http3SettingId,
392 offset: usize,
394 },
395 ProhibitedSetting {
397 id: Http3SettingId,
399 offset: usize,
401 },
402}
403
404impl fmt::Display for Http3SettingsSemanticError {
405 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406 match self {
407 Self::DuplicateSetting { id, offset } => write!(
408 f,
409 "HTTP/3 SETTINGS identifier {} at payload offset {offset} duplicates an earlier identifier",
410 id.value()
411 ),
412 Self::ProhibitedSetting { id, offset } => write!(
413 f,
414 "HTTP/3 SETTINGS identifier {} at payload offset {offset} is reserved for HTTP/2",
415 id.value()
416 ),
417 }
418 }
419}
420
421impl core::error::Error for Http3SettingsSemanticError {}
422
423pub(super) fn validate(
425 settings: Http3Settings<'_>,
426) -> Result<Http3PeerSettings, Http3SettingsSemanticError> {
427 let mut peer_settings = Http3PeerSettings::default();
428 let mut offset = 0;
429
430 for (index, setting) in settings.settings().enumerate() {
431 let id = setting.id();
432 if prohibited_setting(id) {
433 return Err(Http3SettingsSemanticError::ProhibitedSetting { id, offset });
434 }
435 for previous in settings.settings().take(index) {
436 if previous.id() == id {
437 return Err(Http3SettingsSemanticError::DuplicateSetting { id, offset });
438 }
439 }
440
441 match id {
442 Http3SettingId::QPACK_MAX_TABLE_CAPACITY => {
443 peer_settings.qpack_max_table_capacity = setting.value();
444 }
445 Http3SettingId::MAX_FIELD_SECTION_SIZE => {
446 peer_settings.maximum_field_section_size = Some(setting.value());
447 }
448 Http3SettingId::QPACK_BLOCKED_STREAMS => {
449 peer_settings.qpack_blocked_streams = setting.value();
450 }
451 _ => {}
452 }
453
454 if let Some(next_offset) = offset.checked_add(setting.as_bytes().len()) {
456 offset = next_offset;
457 }
458 }
459
460 Ok(peer_settings)
461}
462
463pub(super) const fn prohibited_setting(id: Http3SettingId) -> bool {
465 matches!(id.value(), 0x00 | 0x02 | 0x03 | 0x04 | 0x05)
466}
467
468#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
470pub struct Http3PeerSettings {
471 qpack_max_table_capacity: u64,
472 maximum_field_section_size: Option<u64>,
473 qpack_blocked_streams: u64,
474}
475
476impl Http3PeerSettings {
477 pub const fn qpack_max_table_capacity(self) -> u64 {
479 self.qpack_max_table_capacity
480 }
481
482 pub const fn maximum_field_section_size(self) -> Option<u64> {
484 self.maximum_field_section_size
485 }
486
487 pub const fn qpack_blocked_streams(self) -> u64 {
489 self.qpack_blocked_streams
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn setting_payload_length_overflow_preserves_entry_index() {
499 assert_eq!(
500 add_setting_payload(7, usize::MAX, 1),
501 Err(Http3SettingsBuildError::PayloadLengthOverflow {
502 index: 7,
503 current: usize::MAX,
504 addition: 1,
505 })
506 );
507 }
508}