#[cfg(feature = "h3")]
use crate::dns::{DnsCache, DnsConfig};
#[cfg(feature = "h3")]
use crate::error::{Error, ErrorKind, Result};
#[cfg(feature = "h3")]
use crate::tls::TlsConfig;
#[cfg(feature = "h3")]
use crate::url::Url;
#[cfg(feature = "h3")]
use async_net::UdpSocket;
#[cfg(feature = "h3")]
const MAX_DATAGRAM_SIZE: usize = 1350;
#[cfg(feature = "h3")]
pub(crate) fn resolve_quic_peer_addr(
url: &Url,
dns_cache: &DnsCache,
dns_config: DnsConfig,
) -> Result<std::net::SocketAddr> {
dns_cache
.resolve_socket_addrs(url.host(), url.effective_port(), dns_config)?
.into_iter()
.next()
.ok_or_else(|| Error::new(ErrorKind::Transport, "failed to resolve http3 peer address"))
}
#[cfg(feature = "h3")]
pub(crate) async fn bind_quic_socket(
peer_addr: std::net::SocketAddr,
local_addr: Option<std::net::SocketAddr>,
) -> Result<(UdpSocket, std::net::SocketAddr)> {
let bind_addr = local_addr.unwrap_or_else(|| match peer_addr {
std::net::SocketAddr::V4(_) => std::net::SocketAddr::from(([0, 0, 0, 0], 0)),
std::net::SocketAddr::V6(_) => std::net::SocketAddr::from(([0, 0, 0, 0, 0, 0, 0, 0], 0)),
});
let socket = UdpSocket::bind(bind_addr).await.map_err(|err| {
Error::with_source(ErrorKind::Transport, "failed to bind http3 udp socket", err)
})?;
let local_addr = socket.local_addr().map_err(|err| {
Error::with_source(
ErrorKind::Transport,
"failed to inspect http3 udp socket",
err,
)
})?;
Ok((socket, local_addr))
}
#[cfg(feature = "h3")]
pub(crate) fn build_quiche_config(tls_config: &TlsConfig) -> Result<quiche::Config> {
let _protocols = tls_config
.validate_h3_alpn()
.map_err(|message| Error::new(ErrorKind::Transport, message))?;
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).map_err(|err| {
Error::with_source(
ErrorKind::Transport,
"failed to initialize quiche config",
err,
)
})?;
config.verify_peer(!tls_config.accept_invalid_certs);
if let Some(root_store) = &tls_config.root_store {
match root_store {
crate::RootStore::System | crate::RootStore::WebPki => {}
crate::RootStore::PemFile(path) => config
.load_verify_locations_from_file(path.to_str().ok_or_else(|| {
Error::new(ErrorKind::Transport, "invalid PEM root store path")
})?)
.map_err(|err| {
Error::with_source(
ErrorKind::Transport,
"failed to load HTTP/3 PEM root store",
err,
)
})?,
crate::RootStore::Pem(_) => {
return Err(Error::new(
ErrorKind::Transport,
"http3 root_store currently supports PEM files only",
));
}
}
}
config
.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)
.map_err(|err| {
Error::with_source(
ErrorKind::Transport,
"failed to configure quiche application protocols",
err,
)
})?;
config.set_max_idle_timeout(30_000);
config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
config.set_initial_max_data(10_000_000);
config.set_initial_max_stream_data_bidi_local(1_000_000);
config.set_initial_max_stream_data_bidi_remote(1_000_000);
config.set_initial_max_stream_data_uni(1_000_000);
config.set_initial_max_streams_bidi(100);
config.set_initial_max_streams_uni(100);
config.set_disable_active_migration(true);
Ok(config)
}