pub const DEFAULT_HEADER_TABLE_SIZE: u32 = 4096;
pub const DEFAULT_ENABLE_PUSH: bool = false;
pub const DEFAULT_MAX_CONCURRENT_STREAMS: Option<u32> = None;
pub const DEFAULT_INITIAL_WINDOW_SIZE: u32 = 65535;
pub const DEFAULT_MAX_FRAME_SIZE: u32 = 16384;
pub const DEFAULT_MAX_HEADER_LIST_SIZE: Option<u32> = None;
pub const MIN_MAX_FRAME_SIZE: u32 = 16384;
pub const MAX_MAX_FRAME_SIZE: u32 = 16_777_215;
pub const MAX_INITIAL_WINDOW_SIZE: u32 = 2_147_483_647;
pub const SETTINGS_ENABLE_CONNECT_PROTOCOL: u16 = 0x08;
pub const SETTINGS_WT_INITIAL_MAX_DATA: u16 = 0x2b61;
pub const SETTINGS_WT_INITIAL_MAX_STREAM_DATA_UNI: u16 = 0x2b62;
pub const SETTINGS_WT_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: u16 = 0x2b63;
pub const SETTINGS_WT_INITIAL_MAX_STREAMS_UNI: u16 = 0x2b64;
pub const SETTINGS_WT_INITIAL_MAX_STREAMS_BIDI: u16 = 0x2b65;
pub const SETTINGS_WT_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: u16 = 0x2b66;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum SettingId {
HeaderTableSize = 0x01,
EnablePush = 0x02,
MaxConcurrentStreams = 0x03,
InitialWindowSize = 0x04,
MaxFrameSize = 0x05,
MaxHeaderListSize = 0x06,
EnableConnectProtocol = 0x08,
NoRfc7540Priorities = 0x09,
WtInitialMaxData = 0x2b61,
WtInitialMaxStreamDataUni = 0x2b62,
WtInitialMaxStreamDataBidiLocal = 0x2b63,
WtInitialMaxStreamsUni = 0x2b64,
WtInitialMaxStreamsBidi = 0x2b65,
WtInitialMaxStreamDataBidiRemote = 0x2b66,
}
impl SettingId {
#[must_use]
pub const fn from_u16(value: u16) -> Option<Self> {
match value {
0x01 => Some(Self::HeaderTableSize),
0x02 => Some(Self::EnablePush),
0x03 => Some(Self::MaxConcurrentStreams),
0x04 => Some(Self::InitialWindowSize),
0x05 => Some(Self::MaxFrameSize),
0x06 => Some(Self::MaxHeaderListSize),
0x08 => Some(Self::EnableConnectProtocol),
0x09 => Some(Self::NoRfc7540Priorities),
0x2b61 => Some(Self::WtInitialMaxData),
0x2b62 => Some(Self::WtInitialMaxStreamDataUni),
0x2b63 => Some(Self::WtInitialMaxStreamDataBidiLocal),
0x2b64 => Some(Self::WtInitialMaxStreamsUni),
0x2b65 => Some(Self::WtInitialMaxStreamsBidi),
0x2b66 => Some(Self::WtInitialMaxStreamDataBidiRemote),
_ => None,
}
}
#[must_use]
pub const fn as_u16(self) -> u16 {
self as u16
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Setting {
pub id: u16,
pub value: u32,
}
impl Setting {
#[must_use]
pub const fn new(id: u16, value: u32) -> Self {
Self { id, value }
}
#[must_use]
pub const fn from_setting_id(id: SettingId, value: u32) -> Self {
Self::new(id.as_u16(), value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Settings {
pub header_table_size: u32,
pub enable_push: bool,
pub max_concurrent_streams: Option<u32>,
pub initial_window_size: u32,
pub max_frame_size: u32,
pub max_header_list_size: Option<u32>,
pub enable_connect_protocol: bool,
pub no_rfc7540_priorities: bool,
pub wt_initial: WtInitialSettings,
}
impl Default for Settings {
fn default() -> Self {
Self {
header_table_size: DEFAULT_HEADER_TABLE_SIZE,
enable_push: DEFAULT_ENABLE_PUSH,
max_concurrent_streams: DEFAULT_MAX_CONCURRENT_STREAMS,
initial_window_size: DEFAULT_INITIAL_WINDOW_SIZE,
max_frame_size: DEFAULT_MAX_FRAME_SIZE,
max_header_list_size: DEFAULT_MAX_HEADER_LIST_SIZE,
enable_connect_protocol: false,
no_rfc7540_priorities: false,
wt_initial: WtInitialSettings::default(),
}
}
}
impl Settings {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn apply(&mut self, setting: Setting) -> Result<(), SettingsError> {
match SettingId::from_u16(setting.id) {
Some(SettingId::HeaderTableSize) => {
self.header_table_size = setting.value;
}
Some(SettingId::EnablePush) => {
if setting.value > 1 {
return Err(SettingsError::InvalidEnablePush(setting.value));
}
self.enable_push = setting.value == 1;
}
Some(SettingId::MaxConcurrentStreams) => {
self.max_concurrent_streams = Some(setting.value);
}
Some(SettingId::InitialWindowSize) => {
if setting.value > MAX_INITIAL_WINDOW_SIZE {
return Err(SettingsError::InvalidInitialWindowSize(setting.value));
}
self.initial_window_size = setting.value;
}
Some(SettingId::MaxFrameSize) => {
if setting.value < MIN_MAX_FRAME_SIZE || setting.value > MAX_MAX_FRAME_SIZE {
return Err(SettingsError::InvalidMaxFrameSize(setting.value));
}
self.max_frame_size = setting.value;
}
Some(SettingId::MaxHeaderListSize) => {
self.max_header_list_size = Some(setting.value);
}
Some(SettingId::EnableConnectProtocol) => {
if setting.value > 1 {
return Err(SettingsError::InvalidEnableConnectProtocol(setting.value));
}
self.enable_connect_protocol = setting.value == 1;
}
Some(SettingId::NoRfc7540Priorities) => {
if setting.value > 1 {
return Err(SettingsError::InvalidNoRfc7540Priorities(setting.value));
}
self.no_rfc7540_priorities = setting.value == 1;
}
Some(SettingId::WtInitialMaxData) => {
self.wt_initial.initial_max_data = Some(setting.value);
}
Some(SettingId::WtInitialMaxStreamDataUni) => {
self.wt_initial.initial_max_stream_data_uni = Some(setting.value);
}
Some(SettingId::WtInitialMaxStreamDataBidiLocal) => {
self.wt_initial.initial_max_stream_data_bidi_local = Some(setting.value);
}
Some(SettingId::WtInitialMaxStreamsUni) => {
self.wt_initial.initial_max_streams_uni = Some(setting.value);
}
Some(SettingId::WtInitialMaxStreamsBidi) => {
self.wt_initial.initial_max_streams_bidi = Some(setting.value);
}
Some(SettingId::WtInitialMaxStreamDataBidiRemote) => {
self.wt_initial.initial_max_stream_data_bidi_remote = Some(setting.value);
}
None => {
}
}
Ok(())
}
#[must_use]
pub fn to_settings_list(&self) -> Vec<Setting> {
let mut list = Vec::with_capacity(8);
list.push(Setting::from_setting_id(
SettingId::HeaderTableSize,
self.header_table_size,
));
list.push(Setting::from_setting_id(
SettingId::EnablePush,
u32::from(self.enable_push),
));
if let Some(max) = self.max_concurrent_streams {
list.push(Setting::from_setting_id(
SettingId::MaxConcurrentStreams,
max,
));
}
list.push(Setting::from_setting_id(
SettingId::InitialWindowSize,
self.initial_window_size,
));
list.push(Setting::from_setting_id(
SettingId::MaxFrameSize,
self.max_frame_size,
));
if let Some(max) = self.max_header_list_size {
list.push(Setting::from_setting_id(SettingId::MaxHeaderListSize, max));
}
if self.enable_connect_protocol {
list.push(Setting::from_setting_id(
SettingId::EnableConnectProtocol,
1,
));
}
if self.no_rfc7540_priorities {
list.push(Setting::from_setting_id(SettingId::NoRfc7540Priorities, 1));
}
list.extend(self.wt_initial.to_settings_list());
list
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SettingsError {
InvalidEnablePush(u32),
InvalidInitialWindowSize(u32),
InvalidMaxFrameSize(u32),
InvalidEnableConnectProtocol(u32),
InvalidNoRfc7540Priorities(u32),
}
impl std::fmt::Display for SettingsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidEnablePush(v) => {
write!(f, "invalid ENABLE_PUSH value: {v} (must be 0 or 1)")
}
Self::InvalidInitialWindowSize(v) => {
write!(
f,
"invalid INITIAL_WINDOW_SIZE value: {v} (must be <= 2147483647)"
)
}
Self::InvalidMaxFrameSize(v) => {
write!(
f,
"invalid MAX_FRAME_SIZE value: {v} (must be 16384..=16777215)"
)
}
Self::InvalidEnableConnectProtocol(v) => {
write!(
f,
"invalid ENABLE_CONNECT_PROTOCOL value: {v} (must be 0 or 1)"
)
}
Self::InvalidNoRfc7540Priorities(v) => {
write!(
f,
"invalid NO_RFC7540_PRIORITIES value: {v} (must be 0 or 1)"
)
}
}
}
}
impl std::error::Error for SettingsError {}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct WtInitialSettings {
pub initial_max_data: Option<u32>,
pub initial_max_stream_data_uni: Option<u32>,
pub initial_max_stream_data_bidi_local: Option<u32>,
pub initial_max_stream_data_bidi_remote: Option<u32>,
pub initial_max_streams_uni: Option<u32>,
pub initial_max_streams_bidi: Option<u32>,
}
impl WtInitialSettings {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn apply(&mut self, setting: Setting) {
match setting.id {
SETTINGS_WT_INITIAL_MAX_DATA => {
self.initial_max_data = Some(setting.value);
}
SETTINGS_WT_INITIAL_MAX_STREAM_DATA_UNI => {
self.initial_max_stream_data_uni = Some(setting.value);
}
SETTINGS_WT_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL => {
self.initial_max_stream_data_bidi_local = Some(setting.value);
}
SETTINGS_WT_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE => {
self.initial_max_stream_data_bidi_remote = Some(setting.value);
}
SETTINGS_WT_INITIAL_MAX_STREAMS_UNI => {
self.initial_max_streams_uni = Some(setting.value);
}
SETTINGS_WT_INITIAL_MAX_STREAMS_BIDI => {
self.initial_max_streams_bidi = Some(setting.value);
}
_ => {
}
}
}
#[must_use]
pub fn to_settings_list(&self) -> Vec<Setting> {
let mut list = Vec::new();
if let Some(v) = self.initial_max_data {
list.push(Setting::new(SETTINGS_WT_INITIAL_MAX_DATA, v));
}
if let Some(v) = self.initial_max_stream_data_uni {
list.push(Setting::new(SETTINGS_WT_INITIAL_MAX_STREAM_DATA_UNI, v));
}
if let Some(v) = self.initial_max_stream_data_bidi_local {
list.push(Setting::new(
SETTINGS_WT_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
v,
));
}
if let Some(v) = self.initial_max_stream_data_bidi_remote {
list.push(Setting::new(
SETTINGS_WT_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
v,
));
}
if let Some(v) = self.initial_max_streams_uni {
list.push(Setting::new(SETTINGS_WT_INITIAL_MAX_STREAMS_UNI, v));
}
if let Some(v) = self.initial_max_streams_bidi {
list.push(Setting::new(SETTINGS_WT_INITIAL_MAX_STREAMS_BIDI, v));
}
list
}
}