use crate::async_impl::h3_client::dns::resolve; use crate::dns::DynResolver; use crate::error::BoxError; use bytes::Bytes; use h3::client::SendRequest; use h3_quinn::{Connection, OpenStreams}; use http::Uri; use hyper::client::connect::dns::Name; use quinn::{ClientConfig, Endpoint, TransportConfig}; use std::net::{IpAddr, SocketAddr}; use std::str::FromStr; use std::sync::Arc; type H3Connection = ( h3::client::Connection, SendRequest, ); #[derive(Clone)] pub(crate) struct H3Connector { resolver: DynResolver, endpoint: Endpoint, } impl H3Connector { pub fn new( resolver: DynResolver, tls: rustls::ClientConfig, local_addr: Option, transport_config: TransportConfig, ) -> H3Connector { let mut config = ClientConfig::new(Arc::new(tls)); // FIXME: Replace this when there is a setter. config.transport_config(Arc::new(transport_config)); let socket_addr = match local_addr { Some(ip) => SocketAddr::new(ip, 0), None => "[::]:0".parse::().unwrap(), }; let mut endpoint = Endpoint::client(socket_addr).expect("unable to create QUIC endpoint"); endpoint.set_default_client_config(config); Self { resolver, endpoint } } pub async fn connect(&mut self, dest: Uri) -> Result { let host = dest.host().ok_or("destination must have a host")?; let port = dest.port_u16().unwrap_or(443); let addrs = if let Some(addr) = IpAddr::from_str(host).ok() { // If the host is already an IP address, skip resolving. vec![SocketAddr::new(addr, port)] } else { let addrs = resolve(&mut self.resolver, Name::from_str(host)?).await?; let addrs = addrs.map(|mut addr| { addr.set_port(port); addr }); addrs.collect() }; self.remote_connect(addrs, host).await } async fn remote_connect( &mut self, addrs: Vec, server_name: &str, ) -> Result { let mut err = None; for addr in addrs { match self.endpoint.connect(addr, server_name)?.await { Ok(new_conn) => { let quinn_conn = Connection::new(new_conn); return Ok(h3::client::new(quinn_conn).await?); } Err(e) => err = Some(e), } } match err { Some(e) => Err(Box::new(e) as BoxError), None => Err("failed to establish connection for HTTP/3 request".into()), } } }