use std::fmt::Debug;
use std::sync::Arc;
use http::Uri;
use http::uri::Scheme;
use crate::Error;
use crate::config::Config;
use crate::http;
use super::resolver::{ResolvedSocketAddrs, Resolver};
mod buf;
pub use buf::{Buffers, LazyBuffers};
mod tcp;
pub use self::tcp::TcpConnector;
mod io;
pub use io::TransportAdapter;
mod chain;
pub use chain::{ChainedConnector, Either};
mod connect;
pub use connect::ConnectProxyConnector;
#[cfg(feature = "_test")]
mod test;
#[cfg(feature = "_test")]
pub use test::{set_handler, set_handler_cb};
#[cfg(feature = "socks-proxy")]
mod socks;
#[cfg(feature = "socks-proxy")]
pub use self::socks::SocksConnector;
#[cfg(feature = "_rustls")]
pub use crate::tls::rustls::RustlsConnector;
#[cfg(feature = "native-tls")]
pub use crate::tls::native_tls::NativeTlsConnector;
pub mod time;
use self::time::Instant;
pub use crate::timings::NextTimeout;
pub trait Connector<In: Transport = ()>: Debug + Send + Sync + 'static {
type Out: Transport;
fn connect(
&self,
details: &ConnectionDetails,
chained: Option<In>,
) -> Result<Option<Self::Out>, Error>;
fn chain<Next: Connector<Self::Out>>(self, next: Next) -> ChainedConnector<In, Self, Next>
where
Self: Sized,
{
ChainedConnector::new(self, next)
}
}
pub(crate) fn boxed_connector<In, C>(c: C) -> Box<dyn Connector<In, Out = Box<dyn Transport>>>
where
In: Transport,
C: Connector<In>,
{
#[derive(Debug)]
struct BoxingConnector;
impl<In: Transport> Connector<In> for BoxingConnector {
type Out = Box<dyn Transport>;
fn connect(
&self,
_: &ConnectionDetails,
chained: Option<In>,
) -> Result<Option<Self::Out>, Error> {
if let Some(transport) = chained {
Ok(Some(Box::new(transport)))
} else {
Ok(None)
}
}
}
Box::new(c.chain(BoxingConnector))
}
pub struct ConnectionDetails<'a> {
pub uri: &'a Uri,
pub addrs: ResolvedSocketAddrs,
pub config: &'a Config,
pub request_level: bool,
pub resolver: &'a dyn Resolver,
pub now: Instant,
pub timeout: NextTimeout,
pub current_time: Arc<dyn Fn() -> Instant + Send + Sync + 'static>,
pub run_connector: Arc<RunConnector>,
}
pub(crate) type RunConnector =
dyn Fn(&ConnectionDetails) -> Result<Box<dyn Transport>, Error> + Send + Sync;
impl<'a> ConnectionDetails<'a> {
pub fn needs_tls(&self) -> bool {
self.uri.scheme() == Some(&Scheme::HTTPS)
}
}
pub trait Transport: Debug + Send + Sync + 'static {
fn buffers(&mut self) -> &mut dyn Buffers;
fn transmit_output(&mut self, amount: usize, timeout: NextTimeout) -> Result<(), Error>;
#[doc(hidden)]
fn maybe_await_input(&mut self, timeout: NextTimeout) -> Result<bool, Error> {
if self.buffers().can_use_input() {
return Ok(true);
}
self.await_input(timeout)
}
fn await_input(&mut self, timeout: NextTimeout) -> Result<bool, Error>;
fn is_open(&mut self) -> bool;
fn is_tls(&self) -> bool {
false
}
#[doc(hidden)]
fn boxed(self) -> Box<dyn Transport>
where
Self: Sized + 'static,
{
Box::new(self)
}
}
#[derive(Debug)]
pub struct DefaultConnector {
inner: Box<dyn Connector<(), Out = Box<dyn Transport>>>,
}
impl DefaultConnector {
pub fn new() -> Self {
Self::default()
}
}
impl Default for DefaultConnector {
fn default() -> Self {
let inner = ();
#[cfg(feature = "_test")]
let inner = inner.chain(test::TestConnector);
#[cfg(feature = "socks-proxy")]
let inner = inner.chain(SocksConnector::default());
#[cfg(not(feature = "socks-proxy"))]
let inner = inner.chain(no_proxy::WarnOnNoSocksConnector);
let inner = inner.chain(ConnectProxyConnector::default());
let inner = inner.chain(TcpConnector::default());
#[cfg(feature = "_rustls")]
let inner = inner.chain(RustlsConnector::default());
#[cfg(feature = "_tls")]
let inner = inner.chain(no_tls::WarnOnMissingTlsProvider(
crate::tls::TlsProvider::Rustls,
));
#[cfg(feature = "native-tls")]
let inner = inner.chain(NativeTlsConnector::default());
#[cfg(feature = "_tls")]
let inner = inner.chain(no_tls::WarnOnMissingTlsProvider(
crate::tls::TlsProvider::NativeTls,
));
DefaultConnector {
inner: boxed_connector(inner),
}
}
}
impl Connector<()> for DefaultConnector {
type Out = Box<dyn Transport>;
fn connect(
&self,
details: &ConnectionDetails,
chained: Option<()>,
) -> Result<Option<Self::Out>, Error> {
self.inner.connect(details, chained)
}
}
#[cfg(not(feature = "socks-proxy"))]
mod no_proxy {
use super::{ConnectionDetails, Connector, Debug, Error, Transport};
#[derive(Debug)]
pub(crate) struct WarnOnNoSocksConnector;
impl<In: Transport> Connector<In> for WarnOnNoSocksConnector {
type Out = In;
fn connect(
&self,
details: &ConnectionDetails,
chained: Option<In>,
) -> Result<Option<Self::Out>, Error> {
if chained.is_none() {
if let Some(proxy) = details.config.proxy() {
if proxy.protocol().is_socks() {
if proxy.is_from_env() {
warn!(
"Enable feature socks-proxy to use proxy
configured by environment variables"
);
} else {
panic!(
"Enable feature socks-proxy to use
manually configured proxy"
);
}
}
}
}
Ok(chained)
}
}
}
#[cfg(feature = "_tls")]
mod no_tls {
use crate::tls::TlsProvider;
use super::{ConnectionDetails, Connector, Debug, Error, Transport};
#[derive(Debug)]
pub(crate) struct WarnOnMissingTlsProvider(pub TlsProvider);
impl<In: Transport> Connector<In> for WarnOnMissingTlsProvider {
type Out = In;
fn connect(
&self,
details: &ConnectionDetails,
chained: Option<In>,
) -> Result<Option<Self::Out>, Error> {
let already_tls = chained.as_ref().map(|c| c.is_tls()).unwrap_or(false);
if already_tls {
return Ok(chained);
}
let tls_config = details.config.tls_config();
if details.needs_tls()
&& tls_config.provider() == self.0
&& !self.0.is_feature_enabled()
{
panic!(
"uri scheme is https, provider is {:?} but feature is not enabled: {}",
self.0,
self.0.feature_name()
);
}
Ok(chained)
}
}
}
impl<T: Transport> Transport for Box<T>
where
T: ?Sized,
{
fn buffers(&mut self) -> &mut dyn Buffers {
(**self).buffers()
}
fn transmit_output(&mut self, amount: usize, timeout: NextTimeout) -> Result<(), Error> {
(**self).transmit_output(amount, timeout)
}
fn await_input(&mut self, timeout: NextTimeout) -> Result<bool, Error> {
(**self).await_input(timeout)
}
fn is_open(&mut self) -> bool {
(**self).is_open()
}
fn is_tls(&self) -> bool {
(**self).is_tls()
}
}