use crate::{
HttpHandler, NoopHandler, Proxy, WebSocketHandler, certificate_authority::CertificateAuthority,
};
use hyper_util::{
client::legacy::{Builder as ClientBuilder, connect::Connect},
rt::TokioExecutor,
server::conn::auto::Builder as ServerBuilder,
};
use std::{
future::{Pending, pending},
net::SocketAddr,
sync::Arc,
};
use thiserror::Error;
use tokio::net::TcpListener;
use tokio_rustls::rustls::{ClientConfig, crypto::CryptoProvider};
use tokio_tungstenite::Connector;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
#[cfg(feature = "native-tls-client")]
#[error("{0}")]
NativeTls(#[from] hyper_tls::native_tls::Error),
#[cfg(feature = "rustls-client")]
#[error("{0}")]
Rustls(#[from] tokio_rustls::rustls::Error),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProxyBuilder<T>(T);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct WantsAddr(());
#[derive(Debug)]
pub(crate) enum AddrOrListener {
Addr(SocketAddr),
Listener(TcpListener),
}
impl ProxyBuilder<WantsAddr> {
pub fn new() -> Self {
Self::default()
}
pub fn with_addr(self, addr: SocketAddr) -> ProxyBuilder<WantsCa> {
ProxyBuilder(WantsCa {
al: AddrOrListener::Addr(addr),
})
}
pub fn with_listener(self, listener: TcpListener) -> ProxyBuilder<WantsCa> {
ProxyBuilder(WantsCa {
al: AddrOrListener::Listener(listener),
})
}
}
impl Default for ProxyBuilder<WantsAddr> {
fn default() -> Self {
ProxyBuilder(WantsAddr(()))
}
}
#[derive(Debug)]
pub struct WantsCa {
al: AddrOrListener,
}
impl ProxyBuilder<WantsCa> {
pub fn with_ca<CA: CertificateAuthority>(self, ca: CA) -> ProxyBuilder<WantsClient<CA>> {
ProxyBuilder(WantsClient { al: self.0.al, ca })
}
}
#[derive(Debug)]
pub struct WantsClient<CA> {
al: AddrOrListener,
ca: CA,
}
impl<CA> ProxyBuilder<WantsClient<CA>> {
#[cfg(feature = "rustls-client")]
pub fn with_rustls_connector(
self,
provider: CryptoProvider,
) -> ProxyBuilder<WantsHandlers<CA, impl Connect + Clone, NoopHandler, NoopHandler, Pending<()>>>
{
use hyper_rustls::ConfigBuilderExt;
let rustls_config = match ClientConfig::builder_with_provider(Arc::new(provider))
.with_safe_default_protocol_versions()
{
Ok(config) => config.with_webpki_roots().with_no_client_auth(),
Err(e) => {
return ProxyBuilder(WantsHandlers {
al: self.0.al,
ca: self.0.ca,
http_connector: Err(Error::from(e)),
client: None,
http_handler: NoopHandler::new(),
websocket_handler: NoopHandler::new(),
websocket_connector: None,
server: None,
graceful_shutdown: pending(),
});
}
};
let https = hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(rustls_config.clone())
.https_or_http()
.enable_http1();
#[cfg(feature = "http2")]
let https = https.enable_http2();
let https = https.build();
ProxyBuilder(WantsHandlers {
al: self.0.al,
ca: self.0.ca,
http_connector: Ok(https),
client: None,
http_handler: NoopHandler::new(),
websocket_handler: NoopHandler::new(),
websocket_connector: Some(Connector::Rustls(Arc::new(rustls_config))),
server: None,
graceful_shutdown: pending(),
})
}
#[cfg(feature = "native-tls-client")]
pub fn with_native_tls_connector(
self,
) -> ProxyBuilder<WantsHandlers<CA, impl Connect + Clone, NoopHandler, NoopHandler, Pending<()>>>
{
use hyper_util::client::legacy::connect::HttpConnector;
let tls_connector = match hyper_tls::native_tls::TlsConnector::new() {
Ok(tls_connector) => tls_connector,
Err(e) => {
return ProxyBuilder(WantsHandlers {
al: self.0.al,
ca: self.0.ca,
http_connector: Err(Error::from(e)),
client: None,
http_handler: NoopHandler::new(),
websocket_handler: NoopHandler::new(),
websocket_connector: None,
server: None,
graceful_shutdown: pending(),
});
}
};
let tokio_tls_connector = tokio_native_tls::TlsConnector::from(tls_connector.clone());
let https = hyper_tls::HttpsConnector::from((HttpConnector::new(), tokio_tls_connector));
ProxyBuilder(WantsHandlers {
al: self.0.al,
ca: self.0.ca,
http_connector: Ok(https),
client: None,
http_handler: NoopHandler::new(),
websocket_handler: NoopHandler::new(),
websocket_connector: Some(Connector::NativeTls(tls_connector)),
server: None,
graceful_shutdown: pending(),
})
}
pub fn with_http_connector<C>(
self,
connector: C,
) -> ProxyBuilder<WantsHandlers<CA, C, NoopHandler, NoopHandler, Pending<()>>>
where
C: Connect + Clone + Send + Sync + 'static,
{
ProxyBuilder(WantsHandlers {
al: self.0.al,
ca: self.0.ca,
http_connector: Ok(connector),
client: None,
http_handler: NoopHandler::new(),
websocket_handler: NoopHandler::new(),
websocket_connector: None,
server: None,
graceful_shutdown: pending(),
})
}
}
pub struct WantsHandlers<CA, C, H, W, F> {
al: AddrOrListener,
ca: CA,
http_connector: Result<C, Error>,
client: Option<ClientBuilder>,
http_handler: H,
websocket_handler: W,
websocket_connector: Option<Connector>,
server: Option<ServerBuilder<TokioExecutor>>,
graceful_shutdown: F,
}
impl<CA, C, H, W, F> ProxyBuilder<WantsHandlers<CA, C, H, W, F>> {
pub fn with_http_handler<H2: HttpHandler>(
self,
http_handler: H2,
) -> ProxyBuilder<WantsHandlers<CA, C, H2, W, F>> {
ProxyBuilder(WantsHandlers {
al: self.0.al,
ca: self.0.ca,
http_connector: self.0.http_connector,
client: self.0.client,
http_handler,
websocket_handler: self.0.websocket_handler,
websocket_connector: self.0.websocket_connector,
server: self.0.server,
graceful_shutdown: self.0.graceful_shutdown,
})
}
pub fn with_websocket_handler<W2: WebSocketHandler>(
self,
websocket_handler: W2,
) -> ProxyBuilder<WantsHandlers<CA, C, H, W2, F>> {
ProxyBuilder(WantsHandlers {
al: self.0.al,
ca: self.0.ca,
http_connector: self.0.http_connector,
client: self.0.client,
http_handler: self.0.http_handler,
websocket_handler,
websocket_connector: self.0.websocket_connector,
server: self.0.server,
graceful_shutdown: self.0.graceful_shutdown,
})
}
pub fn with_websocket_connector(self, connector: Connector) -> Self {
ProxyBuilder(WantsHandlers {
websocket_connector: Some(connector),
..self.0
})
}
pub fn with_client(self, client: ClientBuilder) -> Self {
ProxyBuilder(WantsHandlers {
client: Some(client),
..self.0
})
}
pub fn with_server(self, server: ServerBuilder<TokioExecutor>) -> Self {
ProxyBuilder(WantsHandlers {
server: Some(server),
..self.0
})
}
pub fn with_graceful_shutdown<F2: Future<Output = ()> + Send + 'static>(
self,
graceful_shutdown: F2,
) -> ProxyBuilder<WantsHandlers<CA, C, H, W, F2>> {
ProxyBuilder(WantsHandlers {
al: self.0.al,
ca: self.0.ca,
http_connector: self.0.http_connector,
client: self.0.client,
http_handler: self.0.http_handler,
websocket_handler: self.0.websocket_handler,
websocket_connector: self.0.websocket_connector,
server: self.0.server,
graceful_shutdown,
})
}
pub fn build(self) -> Result<Proxy<C, CA, H, W, F>, crate::Error>
where
C: Connect + Clone,
{
Ok(Proxy {
al: self.0.al,
ca: Arc::new(self.0.ca),
http_connector: self.0.http_connector?,
client: self.0.client,
http_handler: self.0.http_handler,
websocket_handler: self.0.websocket_handler,
websocket_connector: self.0.websocket_connector,
server: self.0.server,
graceful_shutdown: self.0.graceful_shutdown,
})
}
}