#[allow(clippy::module_inception)]
mod conn;
mod connector;
mod http;
mod proxy;
mod tls_info;
#[cfg(unix)]
mod uds;
mod verbose;
use std::{
fmt::{self, Debug, Formatter},
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
};
use ::http::{Extensions, HeaderMap, HeaderValue};
use tokio::io::{AsyncRead, AsyncWrite};
use tower::{
BoxError,
util::{BoxCloneSyncService, BoxCloneSyncServiceLayer},
};
pub use self::http::{HttpInfo, TcpConnectOptions};
#[cfg(feature = "socks")]
pub(super) use self::proxy::socks;
pub(crate) use self::{conn::Conn, connector::Connector, proxy::tunnel, tls_info::TlsInfoFactory};
use crate::{client::http::ConnectRequest, dns::DynResolver, proxy::matcher::Intercept};
pub type HttpConnector = self::http::HttpConnector<DynResolver>;
pub type BoxedConnectorService = BoxCloneSyncService<Unnameable, Conn, BoxError>;
pub type BoxedConnectorLayer =
BoxCloneSyncServiceLayer<BoxedConnectorService, Unnameable, Conn, BoxError>;
pub struct Unnameable(pub(super) ConnectRequest);
trait AsyncConn: AsyncRead + AsyncWrite + Connection + Send + Sync + Unpin + 'static {}
trait AsyncConnWithInfo: AsyncConn + TlsInfoFactory {}
impl<T> AsyncConn for T where T: AsyncRead + AsyncWrite + Connection + Send + Sync + Unpin + 'static {}
impl<T> AsyncConnWithInfo for T where T: AsyncConn + TlsInfoFactory {}
pub trait Connection {
fn connected(&self) -> Connected;
}
#[derive(Clone, Copy, Debug, PartialEq)]
enum Alpn {
H2,
None,
}
#[derive(Clone)]
struct PoisonPill {
poisoned: Arc<AtomicBool>,
}
#[derive(Debug)]
struct Extra(Box<dyn ExtraInner>);
trait ExtraInner: Send + Sync + Debug {
fn clone_box(&self) -> Box<dyn ExtraInner>;
fn set(&self, res: &mut Extensions);
}
#[derive(Debug, Clone)]
struct ExtraEnvelope<T>(T);
#[derive(Debug)]
struct ExtraChain<T>(Box<dyn ExtraInner>, T);
#[derive(Debug, Default, Clone)]
struct ProxyIdentity {
is_proxied: bool,
auth: Option<HeaderValue>,
headers: Option<HeaderMap>,
}
#[derive(Debug)]
pub struct Connected {
alpn: Alpn,
proxy: Box<ProxyIdentity>,
extra: Option<Extra>,
poisoned: PoisonPill,
}
impl fmt::Debug for PoisonPill {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"PoisonPill@{:p} {{ poisoned: {} }}",
self.poisoned,
self.poisoned.load(Ordering::Relaxed)
)
}
}
impl PoisonPill {
#[inline]
fn healthy() -> Self {
Self {
poisoned: Arc::new(AtomicBool::new(false)),
}
}
#[inline]
fn poison(&self) {
self.poisoned.store(true, Ordering::Relaxed)
}
#[inline]
fn poisoned(&self) -> bool {
self.poisoned.load(Ordering::Relaxed)
}
}
impl Connected {
pub fn new() -> Connected {
Connected {
alpn: Alpn::None,
proxy: Box::new(ProxyIdentity::default()),
extra: None,
poisoned: PoisonPill::healthy(),
}
}
pub fn extra<T: Clone + Send + Sync + Debug + 'static>(mut self, extra: T) -> Connected {
if let Some(prev) = self.extra {
self.extra = Some(Extra(Box::new(ExtraChain(prev.0, extra))));
} else {
self.extra = Some(Extra(Box::new(ExtraEnvelope(extra))));
}
self
}
pub fn set_extras(&self, extensions: &mut Extensions) {
if let Some(extra) = &self.extra {
extra.set(extensions);
}
}
pub fn proxy(mut self, proxy: Intercept) -> Connected {
self.proxy.is_proxied = true;
if let Some(auth) = proxy.basic_auth() {
self.proxy.auth.replace(auth.clone());
}
if let Some(headers) = proxy.custom_headers() {
self.proxy.headers.replace(headers.clone());
}
self
}
#[inline]
pub fn is_proxied(&self) -> bool {
self.proxy.is_proxied
}
#[inline]
pub fn proxy_auth(&self) -> Option<&HeaderValue> {
self.proxy.auth.as_ref()
}
#[inline]
pub fn proxy_headers(&self) -> Option<&HeaderMap> {
self.proxy.headers.as_ref()
}
#[inline]
pub fn negotiated_h2(mut self) -> Connected {
self.alpn = Alpn::H2;
self
}
#[inline]
pub fn is_negotiated_h2(&self) -> bool {
self.alpn == Alpn::H2
}
#[inline]
pub fn poisoned(&self) -> bool {
self.poisoned.poisoned()
}
#[inline]
pub fn poison(&self) {
self.poisoned.poison();
debug!(
"connection was poisoned. this connection will not be reused for subsequent requests"
);
}
pub(crate) fn clone(&self) -> Connected {
Connected {
alpn: self.alpn,
proxy: self.proxy.clone(),
extra: self.extra.clone(),
poisoned: self.poisoned.clone(),
}
}
}
impl Extra {
#[inline]
fn set(&self, res: &mut Extensions) {
self.0.set(res);
}
}
impl Clone for Extra {
fn clone(&self) -> Extra {
Extra(self.0.clone_box())
}
}
impl<T> ExtraInner for ExtraEnvelope<T>
where
T: Clone + Send + Sync + Debug + 'static,
{
fn clone_box(&self) -> Box<dyn ExtraInner> {
Box::new(self.clone())
}
fn set(&self, res: &mut Extensions) {
res.insert(self.0.clone());
}
}
impl<T: Clone> Clone for ExtraChain<T> {
fn clone(&self) -> Self {
ExtraChain(self.0.clone_box(), self.1.clone())
}
}
impl<T> ExtraInner for ExtraChain<T>
where
T: Clone + Send + Sync + Debug + 'static,
{
fn clone_box(&self) -> Box<dyn ExtraInner> {
Box::new(self.clone())
}
fn set(&self, res: &mut Extensions) {
self.0.set(res);
res.insert(self.1.clone());
}
}