use std::time::Duration;
#[cfg(feature = "http3")]
#[doc(hidden)]
pub use crate::client::conn::quic::{H3Connection, H3Error, QuicConnector};
#[cfg(feature = "http3")]
pub(crate) use crate::client::core::proto::h3::client::drive_request;
#[cfg(feature = "http3")]
#[doc(hidden)]
pub use crate::client::core::proto::h3::client::{H3WebSocket, WsError, WsMessage};
#[cfg(feature = "http3")]
#[doc(hidden)]
#[inline]
pub fn __test_connect_request(uri: http::Uri) -> crate::client::http::ConnectRequest {
crate::client::http::ConnectRequest::new(uri, None)
}
pub type QuicConfig = quinn::TransportConfig;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum H3SettingId {
QpackMaxTableCapacity,
MaxFieldSectionSize,
QpackBlockedStreams,
NumPlaceholders,
Grease(u64),
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Http3Options {
pub max_idle_timeout: Option<Duration>,
pub stream_receive_window: Option<u64>,
pub conn_receive_window: Option<u64>,
pub send_window: Option<u64>,
pub congestion_bbr: bool,
pub max_concurrent_bidi_streams: Option<u64>,
pub max_concurrent_uni_streams: Option<u64>,
pub initial_max_data: Option<u64>,
pub initial_max_stream_data_bidi_local: Option<u64>,
pub initial_max_stream_data_bidi_remote: Option<u64>,
pub initial_max_stream_data_uni: Option<u64>,
pub max_udp_payload_size: Option<u32>,
pub active_connection_id_limit: Option<u32>,
pub initial_packet_padding: Option<u16>,
pub enable_0rtt: bool,
pub max_field_section_size: Option<u64>,
pub send_grease: bool,
pub qpack_max_table_capacity: Option<u64>,
pub qpack_blocked_streams: Option<u64>,
pub experimental_settings: Vec<(u64, u64)>,
pub settings_order: Vec<H3SettingId>,
pub enable_connect_protocol: bool,
}
impl Http3Options {
pub fn customize(f: impl FnOnce(&mut Self)) -> Self {
let mut opts = Self::default();
f(&mut opts);
opts
}
#[inline]
pub fn with_max_idle_timeout(mut self, v: Option<Duration>) -> Self {
self.max_idle_timeout = v;
self
}
#[inline]
pub fn with_stream_receive_window(mut self, v: Option<u64>) -> Self {
self.stream_receive_window = v;
self
}
#[inline]
pub fn with_conn_receive_window(mut self, v: Option<u64>) -> Self {
self.conn_receive_window = v;
self
}
#[inline]
pub fn with_send_window(mut self, v: Option<u64>) -> Self {
self.send_window = v;
self
}
#[inline]
pub fn with_congestion_bbr(mut self, v: bool) -> Self {
self.congestion_bbr = v;
self
}
#[inline]
pub fn with_max_concurrent_bidi_streams(mut self, v: Option<u64>) -> Self {
self.max_concurrent_bidi_streams = v;
self
}
#[inline]
pub fn with_max_concurrent_uni_streams(mut self, v: Option<u64>) -> Self {
self.max_concurrent_uni_streams = v;
self
}
#[inline]
pub fn with_initial_max_data(mut self, v: Option<u64>) -> Self {
self.initial_max_data = v;
self
}
#[inline]
pub fn with_initial_max_stream_data_bidi_local(mut self, v: Option<u64>) -> Self {
self.initial_max_stream_data_bidi_local = v;
self
}
#[inline]
pub fn with_initial_max_stream_data_bidi_remote(mut self, v: Option<u64>) -> Self {
self.initial_max_stream_data_bidi_remote = v;
self
}
#[inline]
pub fn with_initial_max_stream_data_uni(mut self, v: Option<u64>) -> Self {
self.initial_max_stream_data_uni = v;
self
}
#[inline]
pub fn with_max_udp_payload_size(mut self, v: Option<u32>) -> Self {
self.max_udp_payload_size = v;
self
}
#[inline]
pub fn with_active_connection_id_limit(mut self, v: Option<u32>) -> Self {
self.active_connection_id_limit = v;
self
}
#[inline]
pub fn with_initial_packet_padding(mut self, v: Option<u16>) -> Self {
self.initial_packet_padding = v;
self
}
#[inline]
pub fn with_enable_0rtt(mut self, v: bool) -> Self {
self.enable_0rtt = v;
self
}
#[inline]
pub fn with_max_field_section_size(mut self, v: Option<u64>) -> Self {
self.max_field_section_size = v;
self
}
#[inline]
pub fn with_send_grease(mut self, v: bool) -> Self {
self.send_grease = v;
self
}
#[inline]
pub fn with_qpack_max_table_capacity(mut self, v: Option<u64>) -> Self {
self.qpack_max_table_capacity = v;
self
}
#[inline]
pub fn with_qpack_blocked_streams(mut self, v: Option<u64>) -> Self {
self.qpack_blocked_streams = v;
self
}
#[inline]
pub fn with_experimental_settings(mut self, v: Vec<(u64, u64)>) -> Self {
self.experimental_settings = v;
self
}
#[inline]
pub fn with_settings_order(mut self, v: Vec<H3SettingId>) -> Self {
self.settings_order = v;
self
}
#[inline]
pub fn with_enable_connect_protocol(mut self, v: bool) -> Self {
self.enable_connect_protocol = v;
self
}
}
impl Default for Http3Options {
#[inline]
fn default() -> Self {
Http3Options {
max_idle_timeout: Some(Duration::from_secs(30)),
stream_receive_window: Some(8 * 1024 * 1024),
conn_receive_window: Some(8 * 1024 * 1024),
send_window: Some(8 * 1024 * 1024),
congestion_bbr: false,
max_concurrent_bidi_streams: Some(100),
max_concurrent_uni_streams: Some(100),
initial_max_data: Some(10 * 1024 * 1024),
initial_max_stream_data_bidi_local: Some(8 * 1024 * 1024),
initial_max_stream_data_bidi_remote: Some(8 * 1024 * 1024),
initial_max_stream_data_uni: Some(8 * 1024 * 1024),
max_udp_payload_size: Some(65527),
active_connection_id_limit: Some(2),
initial_packet_padding: None,
enable_0rtt: true,
max_field_section_size: Some(16 * 1024),
send_grease: true,
qpack_max_table_capacity: Some(4096),
qpack_blocked_streams: Some(100),
experimental_settings: Vec::new(),
settings_order: vec![
H3SettingId::QpackMaxTableCapacity,
H3SettingId::MaxFieldSectionSize,
H3SettingId::QpackBlockedStreams,
],
enable_connect_protocol: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn http3_options_default_matches_chrome_143() {
let opts = Http3Options::default();
assert_eq!(opts.max_idle_timeout, Some(Duration::from_secs(30)));
assert_eq!(opts.stream_receive_window, Some(8 * 1024 * 1024));
assert_eq!(opts.conn_receive_window, Some(8 * 1024 * 1024));
assert_eq!(opts.send_window, Some(8 * 1024 * 1024));
assert!(!opts.congestion_bbr);
assert_eq!(opts.max_concurrent_bidi_streams, Some(100));
assert_eq!(opts.max_concurrent_uni_streams, Some(100));
assert_eq!(opts.initial_max_data, Some(10 * 1024 * 1024));
assert_eq!(
opts.initial_max_stream_data_bidi_local,
Some(8 * 1024 * 1024)
);
assert_eq!(
opts.initial_max_stream_data_bidi_remote,
Some(8 * 1024 * 1024)
);
assert_eq!(opts.initial_max_stream_data_uni, Some(8 * 1024 * 1024));
assert_eq!(opts.max_udp_payload_size, Some(65527));
assert_eq!(opts.active_connection_id_limit, Some(2));
assert_eq!(opts.initial_packet_padding, None);
assert!(opts.enable_0rtt);
assert_eq!(opts.max_field_section_size, Some(16 * 1024));
assert!(opts.send_grease);
assert_eq!(opts.qpack_max_table_capacity, Some(4096));
assert_eq!(opts.qpack_blocked_streams, Some(100));
assert!(opts.experimental_settings.is_empty());
assert_eq!(
opts.settings_order,
vec![
H3SettingId::QpackMaxTableCapacity,
H3SettingId::MaxFieldSectionSize,
H3SettingId::QpackBlockedStreams,
]
);
assert!(opts.enable_connect_protocol);
}
#[test]
fn http3_options_has_initial_packet_padding_field() {
let opts = Http3Options::default();
assert_eq!(opts.initial_packet_padding, None);
}
#[test]
fn chrome_96_firefox_88_have_different_initial_packet_padding() {
let chrome_opts = Http3Options {
initial_packet_padding: Some(1200),
..Http3Options::default()
};
assert_eq!(chrome_opts.initial_packet_padding, Some(1200));
let ff_opts = Http3Options {
initial_packet_padding: Some(1232),
..Http3Options::default()
};
assert_eq!(ff_opts.initial_packet_padding, Some(1232));
let safari_opts = Http3Options {
initial_packet_padding: None,
..Http3Options::default()
};
assert_eq!(safari_opts.initial_packet_padding, None);
assert_ne!(
chrome_opts.initial_packet_padding,
ff_opts.initial_packet_padding
);
}
#[test]
fn initial_packet_padding_can_be_set_to_chrome_baseline() {
let opts = Http3Options {
initial_packet_padding: Some(1200),
..Http3Options::default()
};
assert_eq!(opts.initial_packet_padding, Some(1200));
}
#[test]
fn initial_packet_padding_can_be_set_to_firefox_baseline() {
let opts = Http3Options {
initial_packet_padding: Some(1232),
..Http3Options::default()
};
assert_eq!(opts.initial_packet_padding, Some(1232));
}
}