use std::{
collections::{BTreeMap, btree_map},
convert::Infallible,
iter,
pin::pin,
};
use bytes::Buf;
use futures::TryStreamExt;
use snafu::Snafu;
use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite};
use crate::{
buflist::BufList,
codec::{DecodeExt, DecodeFrom, DecodeStreamError, EncodeExt, EncodeInto},
connection::StreamError,
dhttp::{frame::Frame, stream::UnidirectionalStream},
error::{Code, ErrorScope, H3CriticalStreamClosed, H3Error, H3FrameDecodeError},
quic,
varint::VarInt,
};
pub struct Setting {
pub id: VarInt,
pub value: VarInt,
}
impl Setting {
pub const fn new(id: VarInt, value: VarInt) -> Self {
Self { id, value }
}
pub fn check(&self) -> Result<(), InvalidSettingValue> {
let is_bool_setting = self.id == EnableConnectProtocol::ID
|| self.id == EnableWebTransport::ID
|| self.id == H3Datagram::ID;
if is_bool_setting && self.value != VarInt::from_u32(0) && self.value != VarInt::from_u32(1)
{
return Err(InvalidSettingValue::BoolSetting {
id: self.id,
value: self.value,
});
}
Ok(())
}
}
impl From<(VarInt, VarInt)> for Setting {
fn from((id, value): (VarInt, VarInt)) -> Self {
Self::new(id, value)
}
}
#[derive(Snafu, Debug, Clone, Copy)]
pub enum InvalidSettingValue {
#[snafu(display("boolean setting {id} must have value 0 or 1, got {value}"))]
BoolSetting { id: VarInt, value: VarInt },
}
impl H3Error for InvalidSettingValue {
fn code(&self) -> Code {
Code::H3_SETTINGS_ERROR
}
fn scope(&self) -> ErrorScope {
ErrorScope::Connection
}
}
impl<S: AsyncRead + Send> DecodeFrom<S> for Setting {
type Error = StreamError;
async fn decode_from(stream: S) -> Result<Self, Self::Error> {
let decode = async move {
let mut stream = pin!(stream);
let id = stream.decode_one().await?;
let value = stream.decode_one().await?;
Ok(Setting { id, value })
};
let setting = decode.await.map_err(|error: DecodeStreamError| {
error.map_stream_closed(
|_reset_code| H3CriticalStreamClosed::Control.into(),
|decode_error| {
H3FrameDecodeError {
source: decode_error,
}
.into()
},
)
})?;
setting.check()?;
Ok(setting)
}
}
impl<S: AsyncWrite + Send> EncodeInto<S> for Setting {
type Output = ();
type Error = StreamError;
async fn encode_into(self, stream: S) -> Result<Self::Output, Self::Error> {
let Setting { id, value } = self;
let encode = async move {
let mut stream = pin!(stream);
stream.as_mut().encode_one(id).await?;
stream.as_mut().encode_one(value).await?;
Ok(())
};
encode
.await
.map_err(|error: quic::StreamError| match error {
quic::StreamError::Reset { .. } => H3CriticalStreamClosed::Control.into(),
quic::StreamError::Connection { .. } => error.into(),
})
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Settings {
map: BTreeMap<VarInt, VarInt>,
}
impl<S> DecodeFrom<S> for Settings
where
for<'s> &'s mut S: AsyncBufRead,
S: Send,
{
type Error = StreamError;
async fn decode_from(stream: S) -> Result<Self, Self::Error> {
let mut stream = pin!(stream.into_decode_stream::<Setting, StreamError>());
let mut settings = Settings::default();
while let Some(setting) = stream.try_next().await? {
settings.set(setting);
}
Ok(settings)
}
}
impl EncodeInto<BufList> for &Settings {
type Output = Frame<BufList>;
type Error = Infallible;
async fn encode_into(self, stream: BufList) -> Result<Self::Output, Self::Error> {
assert!(!stream.has_remaining());
let mut frame = Frame::new(Frame::SETTINGS_FRAME_TYPE, stream)
.expect("SETTINGS frame type is a valid VarInt");
for setting in self {
frame
.encode_one(setting)
.await
.expect("encoding a Setting into a BufList is infallible");
}
Ok(frame)
}
}
impl EncodeInto<BufList> for Settings {
type Output = Frame<BufList>;
type Error = Infallible;
async fn encode_into(self, stream: BufList) -> Result<Self::Output, Self::Error> {
(&self).encode_into(stream).await
}
}
impl Settings {
pub fn get<S: SettingId>(&self, id: S) -> S::Value {
id.value_from(self)
}
fn get_raw(&self, id: VarInt) -> Option<VarInt> {
self.map.get(&id).copied()
}
pub fn max_field_section_size(&self) -> Option<VarInt> {
self.get(MaxFieldSectionSize)
}
pub fn qpack_max_table_capacity(&self) -> VarInt {
self.get(QpackMaxTableCapacity)
}
pub fn qpack_blocked_streams(&self) -> VarInt {
self.get(QpackBlockedStreams)
}
pub fn enable_connect_protocol(&self) -> bool {
self.get(EnableConnectProtocol)
}
pub fn enable_webtransport(&self) -> bool {
self.get(EnableWebTransport)
}
pub fn h3_datagram(&self) -> bool {
self.get(H3Datagram)
}
pub fn set(&mut self, Setting { id, value }: Setting) {
self.map.insert(id, value);
}
}
impl IntoIterator for Settings {
type Item = Setting;
type IntoIter = iter::Map<btree_map::IntoIter<VarInt, VarInt>, fn((VarInt, VarInt)) -> Setting>;
fn into_iter(self) -> Self::IntoIter {
self.map
.into_iter()
.map(|(id, value)| Setting { id, value })
}
}
impl<'s> IntoIterator for &'s Settings {
type Item = Setting;
type IntoIter = iter::Map<
btree_map::Iter<'s, VarInt, VarInt>,
for<'v> fn((&'v VarInt, &'v VarInt)) -> Setting,
>;
fn into_iter(self) -> Self::IntoIter {
self.map.iter().map(|(&id, &value)| Setting { id, value })
}
}
impl FromIterator<Setting> for Settings {
fn from_iter<T: IntoIterator<Item = Setting>>(iter: T) -> Self {
Self {
map: iter
.into_iter()
.map(|Setting { id, value }| (id, value))
.collect::<BTreeMap<_, _>>(),
}
}
}
impl Extend<Setting> for Settings {
fn extend<T: IntoIterator<Item = Setting>>(&mut self, iter: T) {
self.map
.extend(iter.into_iter().map(|Setting { id, value }| (id, value)));
}
}
pub trait SettingId {
type Value;
fn id(&self) -> VarInt;
fn value_from(&self, settings: &Settings) -> Self::Value;
}
impl SettingId for VarInt {
type Value = Option<VarInt>;
fn id(&self) -> VarInt {
*self
}
fn value_from(&self, settings: &Settings) -> Option<VarInt> {
settings.get_raw(*self)
}
}
pub struct QpackMaxTableCapacity;
impl QpackMaxTableCapacity {
pub const ID: VarInt = VarInt::from_u32(0x01);
pub const DEFAULT: VarInt = VarInt::from_u32(0);
pub const fn setting(value: VarInt) -> Setting {
Setting::new(Self::ID, value)
}
}
impl SettingId for QpackMaxTableCapacity {
type Value = VarInt;
fn id(&self) -> VarInt {
Self::ID
}
fn value_from(&self, settings: &Settings) -> VarInt {
settings.get_raw(Self::ID).unwrap_or(Self::DEFAULT)
}
}
pub struct QpackBlockedStreams;
impl QpackBlockedStreams {
pub const ID: VarInt = VarInt::from_u32(0x07);
pub const DEFAULT: VarInt = VarInt::from_u32(0);
pub const fn setting(value: VarInt) -> Setting {
Setting::new(Self::ID, value)
}
}
impl SettingId for QpackBlockedStreams {
type Value = VarInt;
fn id(&self) -> VarInt {
Self::ID
}
fn value_from(&self, settings: &Settings) -> VarInt {
settings.get_raw(Self::ID).unwrap_or(Self::DEFAULT)
}
}
pub struct MaxFieldSectionSize;
impl MaxFieldSectionSize {
pub const ID: VarInt = VarInt::from_u32(0x06);
pub const fn setting(value: VarInt) -> Setting {
Setting::new(Self::ID, value)
}
}
impl SettingId for MaxFieldSectionSize {
type Value = Option<VarInt>;
fn id(&self) -> VarInt {
Self::ID
}
fn value_from(&self, settings: &Settings) -> Option<VarInt> {
settings.get_raw(Self::ID)
}
}
pub struct EnableConnectProtocol;
impl EnableConnectProtocol {
pub const ID: VarInt = VarInt::from_u32(0x08);
pub const fn setting(enabled: bool) -> Setting {
Setting::new(Self::ID, VarInt::from_u32(enabled as u32))
}
}
impl SettingId for EnableConnectProtocol {
type Value = bool;
fn id(&self) -> VarInt {
Self::ID
}
fn value_from(&self, settings: &Settings) -> bool {
settings
.get_raw(Self::ID)
.is_some_and(|v| v == VarInt::from_u32(1))
}
}
pub struct EnableWebTransport;
impl EnableWebTransport {
pub const ID: VarInt = VarInt::from_u32(0x2b603742);
pub const fn setting(enabled: bool) -> Setting {
Setting::new(Self::ID, VarInt::from_u32(enabled as u32))
}
}
impl SettingId for EnableWebTransport {
type Value = bool;
fn id(&self) -> VarInt {
Self::ID
}
fn value_from(&self, settings: &Settings) -> bool {
settings
.get_raw(Self::ID)
.is_some_and(|v| v == VarInt::from_u32(1))
}
}
pub struct H3Datagram;
impl H3Datagram {
pub const ID: VarInt = VarInt::from_u32(0x33);
pub const fn setting(enabled: bool) -> Setting {
Setting::new(Self::ID, VarInt::from_u32(enabled as u32))
}
}
impl SettingId for H3Datagram {
type Value = bool;
fn id(&self) -> VarInt {
Self::ID
}
fn value_from(&self, settings: &Settings) -> bool {
settings
.get_raw(Self::ID)
.is_some_and(|v| v == VarInt::from_u32(1))
}
}
impl UnidirectionalStream<()> {
pub const CONTROL_STREAM_TYPE: VarInt = VarInt::from_u32(0x00);
}
impl<S: ?Sized> UnidirectionalStream<S> {
pub const fn is_control_stream(&self) -> bool {
self.r#type().into_inner() == UnidirectionalStream::CONTROL_STREAM_TYPE.into_inner()
}
pub async fn initial_control_stream(stream: S) -> Result<Self, StreamError>
where
S: AsyncWrite + Unpin + Sized + Send,
{
Self::initial(UnidirectionalStream::CONTROL_STREAM_TYPE, stream)
.await
.map_err(|error| error.map_stream_reset(|_| H3CriticalStreamClosed::Control.into()))
}
}