mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-18 07:23:51 +01:00
128 lines
4.2 KiB
Rust
128 lines
4.2 KiB
Rust
use crate::dns::LocalhostResolver;
|
|
use crate::error::Result;
|
|
use log::{debug, info, warn};
|
|
use reqwest::{Client, Proxy, redirect};
|
|
use std::sync::Arc;
|
|
use yaak_models::models::DnsOverride;
|
|
use yaak_tls::{ClientCertificateConfig, get_tls_config};
|
|
|
|
#[derive(Clone)]
|
|
pub struct HttpConnectionProxySettingAuth {
|
|
pub user: String,
|
|
pub password: String,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum HttpConnectionProxySetting {
|
|
Disabled,
|
|
System,
|
|
Enabled {
|
|
http: String,
|
|
https: String,
|
|
auth: Option<HttpConnectionProxySettingAuth>,
|
|
bypass: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct HttpConnectionOptions {
|
|
pub id: String,
|
|
pub validate_certificates: bool,
|
|
pub proxy: HttpConnectionProxySetting,
|
|
pub client_certificate: Option<ClientCertificateConfig>,
|
|
pub dns_overrides: Vec<DnsOverride>,
|
|
}
|
|
|
|
impl HttpConnectionOptions {
|
|
/// Build a reqwest Client and return it along with the DNS resolver.
|
|
/// The resolver is returned separately so it can be configured per-request
|
|
/// to emit DNS timing events to the appropriate channel.
|
|
pub(crate) fn build_client(&self) -> Result<(Client, Arc<LocalhostResolver>)> {
|
|
let mut client = Client::builder()
|
|
.connection_verbose(true)
|
|
.redirect(redirect::Policy::none())
|
|
// Decompression is handled by HttpTransaction, not reqwest
|
|
.no_gzip()
|
|
.no_brotli()
|
|
.no_deflate()
|
|
.referer(false)
|
|
.tls_info(true)
|
|
// Disable connection pooling to ensure DNS resolution happens on each request
|
|
// This is needed so we can emit DNS timing events for each request
|
|
.pool_max_idle_per_host(0);
|
|
|
|
// Configure TLS with optional client certificate
|
|
let config =
|
|
get_tls_config(self.validate_certificates, true, self.client_certificate.clone())?;
|
|
client = client.use_preconfigured_tls(config);
|
|
|
|
// Configure DNS resolver - keep a reference to configure per-request
|
|
let resolver = LocalhostResolver::new(self.dns_overrides.clone());
|
|
client = client.dns_resolver(resolver.clone());
|
|
|
|
// Configure proxy
|
|
match self.proxy.clone() {
|
|
HttpConnectionProxySetting::System => { /* Default */ }
|
|
HttpConnectionProxySetting::Disabled => {
|
|
client = client.no_proxy();
|
|
}
|
|
HttpConnectionProxySetting::Enabled { http, https, auth, bypass } => {
|
|
for p in build_enabled_proxy(http, https, auth, bypass) {
|
|
client = client.proxy(p)
|
|
}
|
|
}
|
|
}
|
|
|
|
info!(
|
|
"Building new HTTP client validate_certificates={} client_cert={}",
|
|
self.validate_certificates,
|
|
self.client_certificate.is_some()
|
|
);
|
|
|
|
Ok((client.build()?, resolver))
|
|
}
|
|
}
|
|
|
|
fn build_enabled_proxy(
|
|
http: String,
|
|
https: String,
|
|
auth: Option<HttpConnectionProxySettingAuth>,
|
|
bypass: String,
|
|
) -> Vec<Proxy> {
|
|
debug!("Using proxy http={http} https={https} bypass={bypass}");
|
|
|
|
let mut proxies = Vec::new();
|
|
|
|
if !http.is_empty() {
|
|
match Proxy::http(http) {
|
|
Ok(mut proxy) => {
|
|
if let Some(HttpConnectionProxySettingAuth { user, password }) = auth.clone() {
|
|
debug!("Using http proxy auth");
|
|
proxy = proxy.basic_auth(user.as_str(), password.as_str());
|
|
}
|
|
proxies.push(proxy.no_proxy(reqwest::NoProxy::from_string(&bypass)));
|
|
}
|
|
Err(e) => {
|
|
warn!("Failed to apply http proxy {e:?}");
|
|
}
|
|
};
|
|
}
|
|
|
|
if !https.is_empty() {
|
|
match Proxy::https(https) {
|
|
Ok(mut proxy) => {
|
|
if let Some(HttpConnectionProxySettingAuth { user, password }) = auth {
|
|
debug!("Using https proxy auth");
|
|
proxy = proxy.basic_auth(user.as_str(), password.as_str());
|
|
}
|
|
proxies.push(proxy.no_proxy(reqwest::NoProxy::from_string(&bypass)));
|
|
}
|
|
Err(e) => {
|
|
warn!("Failed to apply https proxy {e:?}");
|
|
}
|
|
};
|
|
}
|
|
|
|
proxies
|
|
}
|