use core::future::Future; use core::task::{Context, Poll}; use std::convert::TryFrom; use std::pin::Pin; use std::sync::Arc; use std::io; use futures_util::future::*; use rustls::ServerName; use hyper::client::connect::Connection; use hyper::client::HttpConnector; use hyper::service::Service; use hyper::Uri; use hyper_rustls::MaybeHttpsStream; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_rustls::TlsConnector; #[derive(Clone)] pub struct HttpsConnectorFixedDnsname { http: T, tls_config: Arc, fixed_dnsname: &'static str, } type BoxError = Box; impl HttpsConnectorFixedDnsname { pub fn new(mut tls_config: rustls::ClientConfig, fixed_dnsname: &'static str) -> Self { let mut http = HttpConnector::new(); http.enforce_http(false); tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; Self { http, tls_config: Arc::new(tls_config), fixed_dnsname, } } } impl Service for HttpsConnectorFixedDnsname where T: Service, T::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static, T::Future: Send + 'static, T::Error: Into, { type Response = MaybeHttpsStream; type Error = BoxError; #[allow(clippy::type_complexity)] type Future = Pin, BoxError>> + Send>>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { match self.http.poll_ready(cx) { Poll::Ready(Ok(())) => Poll::Ready(Ok(())), Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())), Poll::Pending => Poll::Pending, } } fn call(&mut self, dst: Uri) -> Self::Future { let is_https = dst.scheme_str() == Some("https"); assert!(is_https); let cfg = self.tls_config.clone(); let connecting_future = self.http.call(dst); let dnsname = ServerName::try_from(self.fixed_dnsname).expect("Invalid fixed dnsname"); let f = async move { let tcp = connecting_future.await.map_err(Into::into)?; let connector = TlsConnector::from(cfg); let tls = connector .connect(dnsname, tcp) .await .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; Ok(MaybeHttpsStream::Https(tls)) }; f.boxed() } }