use std::{net::SocketAddr, sync::Arc};
use crate::{
client::http3::Http3Options,
error::Error,
tls::{AlpnProtocol, TlsOptions},
};
pub fn build_quinn_client_config(
tls_opts: &TlsOptions,
h3_opts: &Http3Options,
) -> crate::Result<quinn::ClientConfig> {
let mut root_store = rustls::RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
build_quinn_client_config_with_root_store(tls_opts, h3_opts, root_store)
}
pub fn build_quinn_client_config_with_root_store(
_tls_opts: &TlsOptions,
h3_opts: &Http3Options,
root_store: rustls::RootCertStore,
) -> crate::Result<quinn::ClientConfig> {
let alpn_protocols = vec![AlpnProtocol::HTTP3.as_wire_bytes().to_vec()];
let provider = Arc::new(rustls::crypto::ring::default_provider());
let mut tls_config = rustls::ClientConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()
.map_err(Error::tls)?
.with_root_certificates(root_store)
.with_no_client_auth();
tls_config.alpn_protocols = alpn_protocols;
if h3_opts.enable_0rtt {
tls_config.enable_early_data = true;
}
let quic_client_config =
quinn::crypto::rustls::QuicClientConfig::try_from(Arc::new(tls_config))
.map_err(Error::tls)?;
let mut client_config = quinn::ClientConfig::new(Arc::new(quic_client_config));
client_config.transport_config(Arc::new(build_transport_config(h3_opts)?));
Ok(client_config)
}
pub fn build_quinn_endpoint(local_addr: SocketAddr) -> crate::Result<quinn::Endpoint> {
quinn::Endpoint::client(local_addr).map_err(Error::tls)
}
fn build_transport_config(h3_opts: &Http3Options) -> crate::Result<quinn::TransportConfig> {
let mut transport = quinn::TransportConfig::default();
if let Some(idle) = h3_opts.max_idle_timeout {
let ms = idle.as_millis();
let ms = u64::try_from(ms).map_err(Error::tls)?;
let varint = quinn::VarInt::from_u64(ms).map_err(Error::tls)?;
transport.max_idle_timeout(Some(varint.into()));
}
if let Some(wnd) = h3_opts.stream_receive_window {
let varint = quinn::VarInt::from_u64(wnd).map_err(Error::tls)?;
transport.stream_receive_window(varint);
}
if let Some(wnd) = h3_opts.conn_receive_window {
let varint = quinn::VarInt::from_u64(wnd).map_err(Error::tls)?;
transport.receive_window(varint);
}
if let Some(wnd) = h3_opts.send_window {
transport.send_window(wnd);
}
if let Some(n) = h3_opts.max_concurrent_bidi_streams {
let n = u32::try_from(n).map_err(Error::tls)?;
transport.max_concurrent_bidi_streams(n.into());
}
if let Some(n) = h3_opts.max_concurrent_uni_streams {
let n = u32::try_from(n).map_err(Error::tls)?;
transport.max_concurrent_uni_streams(n.into());
}
if let Some(wnd) = h3_opts.initial_max_data {
let varint = quinn::VarInt::from_u64(wnd).map_err(Error::tls)?;
transport.receive_window(varint);
}
if let Some(wnd) = h3_opts.initial_max_stream_data_bidi_local {
let varint = quinn::VarInt::from_u64(wnd).map_err(Error::tls)?;
transport.stream_receive_window(varint);
}
Ok(transport)
}