use std::sync::Arc;
use http::Uri;
use crate::proxy_credential::CredentialResolver;
use super::{NoProxy, ProxyAuth, ProxyConfig};
#[derive(Clone, Default)]
pub struct ProxySettings {
pub(crate) http_proxy: Option<ProxyConfig>,
pub(crate) https_proxy: Option<ProxyConfig>,
pub(crate) no_proxy: NoProxy,
pub(crate) custom: Option<Arc<dyn CustomProxy>>,
pub(crate) credential_resolver: Option<Arc<dyn CredentialResolver>>,
}
impl std::fmt::Debug for ProxySettings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProxySettings")
.field("http_proxy", &self.http_proxy)
.field("https_proxy", &self.https_proxy)
.field("no_proxy", &self.no_proxy)
.field("custom", &self.custom.as_ref().map(|_| ".."))
.finish()
}
}
impl ProxySettings {
pub fn from_env() -> Self {
let http_proxy = env_proxy("HTTP_PROXY", "http_proxy");
let https_proxy = env_proxy("HTTPS_PROXY", "https_proxy");
let no_proxy = NoProxy::from_env();
Self {
http_proxy,
https_proxy,
no_proxy,
custom: None,
credential_resolver: None,
}
}
pub fn all(proxy: ProxyConfig) -> Self {
Self {
http_proxy: Some(proxy.clone()),
https_proxy: Some(proxy),
no_proxy: NoProxy::default(),
custom: None,
credential_resolver: None,
}
}
pub fn http(mut self, proxy: ProxyConfig) -> Self {
self.http_proxy = Some(proxy);
self
}
pub fn https(mut self, proxy: ProxyConfig) -> Self {
self.https_proxy = Some(proxy);
self
}
pub fn no_proxy(mut self, no_proxy: NoProxy) -> Self {
self.no_proxy = no_proxy;
self
}
pub fn custom(
mut self,
f: impl Fn(&Uri) -> Option<ProxyConfig> + Send + Sync + 'static,
) -> Self {
self.custom = Some(Arc::new(f));
self
}
pub fn proxy_credential_resolver(mut self, resolver: impl CredentialResolver) -> Self {
self.credential_resolver = Some(Arc::new(resolver));
self
}
pub(crate) fn proxy_for(&self, uri: &Uri) -> Option<ProxyConfig> {
if let Some(host) = uri.host()
&& self.no_proxy.matches_with_port(host, uri.port_u16())
{
return None;
}
if let Some(ref custom) = self.custom {
return custom.proxy_for(uri);
}
let mut proxy = match uri.scheme_str() {
Some("https") => self.https_proxy.clone(),
_ => self.http_proxy.clone(),
}?;
if proxy.auth.is_none()
&& let Some(ref resolver) = self.credential_resolver
&& let Ok(authority) = proxy.authority()
&& let Some((user, pass)) = resolver.resolve(authority.as_str())
{
proxy.auth = Some(ProxyAuth {
username: user,
password: pass,
});
}
Some(proxy)
}
}
pub(super) fn env_proxy(upper: &str, lower: &str) -> Option<ProxyConfig> {
let val = std::env::var(upper)
.or_else(|_| std::env::var(lower))
.ok()?;
ProxyConfig::detect_from_url(&val)
}
pub trait CustomProxy: Send + Sync + 'static {
fn proxy_for(&self, uri: &Uri) -> Option<ProxyConfig>;
}
impl<F> CustomProxy for F
where
F: Fn(&Uri) -> Option<ProxyConfig> + Send + Sync + 'static,
{
fn proxy_for(&self, uri: &Uri) -> Option<ProxyConfig> {
(self)(uri)
}
}