#![allow(dead_code)]
use crate::h3::frame::{
Settings as FrameSettings, SETTINGS_ENABLE_CONNECT_PROTOCOL, SETTINGS_MAX_FIELD_SECTION_SIZE,
SETTINGS_QPACK_BLOCKED_STREAMS, SETTINGS_QPACK_MAX_TABLE_CAPACITY,
};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PeerSettings {
qpack_max_table_capacity: u64,
max_field_section_size: Option<u64>,
qpack_blocked_streams: u64,
enable_connect_protocol: bool,
}
impl PeerSettings {
#[inline]
pub fn qpack_max_table_capacity(&self) -> u64 {
self.qpack_max_table_capacity
}
#[inline]
pub fn max_field_section_size(&self) -> Option<u64> {
self.max_field_section_size
}
#[inline]
pub fn qpack_blocked_streams(&self) -> u64 {
self.qpack_blocked_streams
}
#[inline]
pub fn enable_connect_protocol(&self) -> bool {
self.enable_connect_protocol
}
#[inline]
pub fn apply(&mut self, settings: &FrameSettings) {
for (id, value) in settings.iter() {
match id {
SETTINGS_QPACK_MAX_TABLE_CAPACITY => self.qpack_max_table_capacity = value,
SETTINGS_MAX_FIELD_SECTION_SIZE => self.max_field_section_size = Some(value),
SETTINGS_QPACK_BLOCKED_STREAMS => self.qpack_blocked_streams = value,
SETTINGS_ENABLE_CONNECT_PROTOCOL => self.enable_connect_protocol = value != 0,
_ => {}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LocalSettings {
pub qpack_max_table_capacity: u64,
pub qpack_blocked_streams: u64,
pub max_field_section_size: Option<u64>,
pub enable_connect_protocol: bool,
}
impl Default for LocalSettings {
#[inline]
fn default() -> Self {
LocalSettings {
qpack_max_table_capacity: 0,
qpack_blocked_streams: 0,
max_field_section_size: Some(65_536),
enable_connect_protocol: false,
}
}
}
impl LocalSettings {
#[inline]
pub fn to_frame(&self) -> FrameSettings {
debug_assert!(
self.qpack_max_table_capacity < (1 << 30),
"SETTINGS_QPACK_MAX_TABLE_CAPACITY above 2^30-1 (RFC 9204 Section 5)"
);
let mut frame = FrameSettings::new();
frame.insert(
SETTINGS_QPACK_MAX_TABLE_CAPACITY,
self.qpack_max_table_capacity,
);
frame.insert(SETTINGS_QPACK_BLOCKED_STREAMS, self.qpack_blocked_streams);
if let Some(size) = self.max_field_section_size {
frame.insert(SETTINGS_MAX_FIELD_SECTION_SIZE, size);
}
if self.enable_connect_protocol {
frame.insert(SETTINGS_ENABLE_CONNECT_PROTOCOL, 1);
}
frame.insert(0x21, 0); frame
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn peer_settings_defaults_match_rfc() {
let peer = PeerSettings::default();
assert_eq!(peer.qpack_max_table_capacity(), 0);
assert_eq!(peer.max_field_section_size(), None);
assert_eq!(peer.qpack_blocked_streams(), 0);
assert!(!peer.enable_connect_protocol());
}
#[test]
fn apply_overlays_received_values() {
let mut peer = PeerSettings::default();
let mut frame = FrameSettings::new();
frame.insert(SETTINGS_QPACK_MAX_TABLE_CAPACITY, 4096);
frame.insert(SETTINGS_MAX_FIELD_SECTION_SIZE, 16384);
frame.insert(SETTINGS_QPACK_BLOCKED_STREAMS, 16);
frame.insert(SETTINGS_ENABLE_CONNECT_PROTOCOL, 1);
peer.apply(&frame);
assert_eq!(peer.qpack_max_table_capacity(), 4096);
assert_eq!(peer.max_field_section_size(), Some(16384));
assert_eq!(peer.qpack_blocked_streams(), 16);
assert!(peer.enable_connect_protocol());
let mut partial = FrameSettings::new();
partial.insert(SETTINGS_MAX_FIELD_SECTION_SIZE, 0);
peer.apply(&partial);
assert_eq!(peer.qpack_max_table_capacity(), 4096);
assert_eq!(peer.max_field_section_size(), Some(0));
assert_eq!(peer.qpack_blocked_streams(), 16);
}
#[test]
fn local_settings_round_trip_through_frame() {
let local = LocalSettings {
qpack_max_table_capacity: 4096,
qpack_blocked_streams: 16,
max_field_section_size: Some(100),
enable_connect_protocol: true,
};
let mut peer = PeerSettings::default();
peer.apply(&local.to_frame());
assert_eq!(peer.qpack_max_table_capacity(), 4096);
assert_eq!(peer.qpack_blocked_streams(), 16);
assert_eq!(peer.max_field_section_size(), Some(100));
assert!(peer.enable_connect_protocol());
}
#[test]
fn local_settings_frame_always_carries_grease() {
let frame = LocalSettings::default().to_frame();
assert_eq!(frame.get(0x21), Some(0));
assert_eq!(frame.get(SETTINGS_QPACK_MAX_TABLE_CAPACITY), Some(0));
}
}