use core::task::{Context, Poll};
use std::sync::LazyLock;
use futures::future::BoxFuture;
use futures::FutureExt;
use hyper_util::client::legacy::connect::{proxy::Tunnel, HttpConnector};
use hyper_util::client::proxy::matcher::Matcher;
use super::conn_stream::{ConnStream, ConnStreamError};
#[cfg(feature = "tls-core")]
use super::https;
use super::Connector;
static PROXY_MATCHER: LazyLock<Matcher> = LazyLock::new(Matcher::from_env);
#[derive(Clone)]
pub(super) struct HttpProxyConnector {
matcher: &'static Matcher,
#[cfg(feature = "tls-core")]
tls_config: Option<rustls::ClientConfig>,
#[cfg(feature = "tls-core")]
proxy_dialer: Option<hyper_rustls::HttpsConnector<HttpConnector>>,
direct: Connector,
}
impl HttpProxyConnector {
pub(super) fn new(direct: Connector) -> Self {
#[cfg(feature = "tls-core")]
let tls_config = https::build_tls_config().ok();
#[cfg(feature = "tls-core")]
let proxy_dialer = tls_config.clone().map(|tls_config| {
hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(tls_config)
.https_or_http()
.enable_http1()
.build()
});
Self {
matcher: &PROXY_MATCHER,
#[cfg(feature = "tls-core")]
tls_config,
#[cfg(feature = "tls-core")]
proxy_dialer,
direct,
}
}
pub(super) fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), ConnStreamError>> {
tower_service::Service::poll_ready(&mut self.direct, cx)
}
pub(super) fn build_conn_stream(
&mut self,
uri: hyper::Uri,
require_tls: bool,
) -> BoxFuture<'static, Result<ConnStream, ConnStreamError>> {
let Some(intercept) = self.matcher.intercept(&uri) else {
return tower_service::Service::call(&mut self.direct, uri);
};
#[cfg(feature = "tls-core")]
if let (Some(tls_config), Some(proxy_dialer)) = (&self.tls_config, &self.proxy_dialer) {
let mut tunnel = Tunnel::new(intercept.uri().clone(), proxy_dialer.clone());
if let Some(auth) = intercept.basic_auth() {
tunnel = tunnel.with_auth(auth.clone());
}
let mut https = hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(tls_config.clone())
.https_or_http()
.enable_http1()
.wrap_connector(tunnel);
return ConnStream::from_https_connector_with_uri(&mut https, uri, require_tls).boxed();
}
if !require_tls && intercept.uri().scheme_str() == Some("http") {
let mut tunnel = Tunnel::new(intercept.uri().clone(), HttpConnector::new());
if let Some(auth) = intercept.basic_auth() {
tunnel = tunnel.with_auth(auth.clone());
}
return ConnStream::from_tunnel_with_uri(&mut tunnel, uri).boxed();
}
tower_service::Service::call(&mut self.direct, uri)
}
}