use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
use bytes::{Buf, BufMut};
use thiserror::Error;
use super::{Frame, StreamUni, VarInt, VarIntUnexpectedEnd};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Setting(pub VarInt);
impl Setting {
pub fn decode<B: Buf>(buf: &mut B) -> Result<Self, VarIntUnexpectedEnd> {
Ok(Setting(VarInt::decode(buf)?))
}
pub fn encode<B: BufMut>(&self, buf: &mut B) {
self.0.encode(buf)
}
}
macro_rules! settings {
{$($name:ident = $val:expr,)*} => {
impl Setting {
$(pub const $name: Setting = Setting(VarInt::from_u32($val));)*
}
}
}
settings! {
QPACK_MAX_TABLE_CAPACITY = 0x1, MAX_FIELD_SECTION_SIZE = 0x6,
QPACK_BLOCKED_STREAMS = 0x7,
ENABLE_CONNECT_PROTOCOL = 0x8,
ENABLE_DATAGRAM = 0x33,
ENABLE_DATAGRAM_DEPRECATED = 0xFFD277, WEBTRANSPORT_ENABLE_DEPRECATED = 0x2b603742,
WEBTRANSPORT_MAX_SESSIONS_DEPRECATED = 0x2b603743,
WEBTRANSPORT_MAX_SESSIONS = 0xc671706a,
}
#[derive(Error, Debug, Clone)]
pub enum SettingsError {
#[error("unexpected end of input")]
UnexpectedEnd,
#[error("unexpected stream type {0:?}")]
UnexpectedStreamType(StreamUni),
#[error("unexpected frame {0:?}")]
UnexpectedFrame(Frame),
#[error("invalid size")]
InvalidSize,
}
#[derive(Default, Debug)]
pub struct Settings(HashMap<Setting, VarInt>);
impl Settings {
pub fn decode<B: Buf>(buf: &mut B) -> Result<Self, SettingsError> {
let typ = StreamUni::decode(buf).map_err(|_| SettingsError::UnexpectedEnd)?;
if typ != StreamUni::CONTROL {
return Err(SettingsError::UnexpectedStreamType(typ));
}
let (typ, mut data) = Frame::read(buf).map_err(|_| SettingsError::UnexpectedEnd)?;
if typ != Frame::SETTINGS {
return Err(SettingsError::UnexpectedFrame(typ));
}
let mut settings = Settings::default();
while data.has_remaining() {
let id = Setting::decode(&mut data).map_err(|_| SettingsError::InvalidSize)?;
let value = VarInt::decode(&mut data).map_err(|_| SettingsError::InvalidSize)?;
settings.0.insert(id, value);
}
Ok(settings)
}
pub fn encode<B: BufMut>(&self, buf: &mut B) {
StreamUni::CONTROL.encode(buf);
Frame::SETTINGS.encode(buf);
let mut tmp = Vec::new();
for (id, value) in &self.0 {
id.encode(&mut tmp);
value.encode(&mut tmp);
}
VarInt::from_u32(tmp.len() as u32).encode(buf);
buf.put_slice(&tmp);
}
pub fn enable_webtransport(&mut self, max_sessions: u32) {
let max = VarInt::from_u32(max_sessions);
self.insert(Setting::ENABLE_CONNECT_PROTOCOL, VarInt::from_u32(1));
self.insert(Setting::ENABLE_DATAGRAM, VarInt::from_u32(1));
self.insert(Setting::ENABLE_DATAGRAM_DEPRECATED, VarInt::from_u32(1));
self.insert(Setting::WEBTRANSPORT_MAX_SESSIONS, max);
self.insert(Setting::WEBTRANSPORT_MAX_SESSIONS_DEPRECATED, max);
self.insert(Setting::WEBTRANSPORT_ENABLE_DEPRECATED, VarInt::from_u32(1));
}
pub fn supports_webtransport(&self) -> u64 {
let datagram = self
.get(&Setting::ENABLE_DATAGRAM)
.or(self.get(&Setting::ENABLE_DATAGRAM_DEPRECATED))
.map(|v| v.into_inner());
if datagram != Some(1) {
return 0;
}
if let Some(max) = self.get(&Setting::WEBTRANSPORT_MAX_SESSIONS) {
return max.into_inner();
}
let enabled = self
.get(&Setting::WEBTRANSPORT_ENABLE_DEPRECATED)
.map(|v| v.into_inner());
if enabled != Some(1) {
return 0;
}
self.get(&Setting::WEBTRANSPORT_MAX_SESSIONS_DEPRECATED)
.map(|v| v.into_inner())
.unwrap_or(1)
}
}
impl Deref for Settings {
type Target = HashMap<Setting, VarInt>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Settings {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}