| 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<Connection, Bytes>, |
| SendRequest<OpenStreams, Bytes>, |
| ); |
| |
| #[derive(Clone)] |
| pub(crate) struct H3Connector { |
| resolver: DynResolver, |
| endpoint: Endpoint, |
| } |
| |
| impl H3Connector { |
| pub fn new( |
| resolver: DynResolver, |
| tls: rustls::ClientConfig, |
| local_addr: Option<IpAddr>, |
| 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::<SocketAddr>().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<H3Connection, BoxError> { |
| 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<SocketAddr>, |
| server_name: &str, |
| ) -> Result<H3Connection, BoxError> { |
| 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()), |
| } |
| } |
| } |