use std::time::Duration;
#[derive(Debug, Clone, Default)]
pub struct HttpClientConfig {
pub proxy: Option<String>,
pub insecure_tls: bool,
pub timeout: Option<Duration>,
pub ua_suffix: Option<String>,
pub allow_private_endpoint: bool,
}
impl HttpClientConfig {
#[cfg(any(
feature = "azure",
feature = "web",
feature = "github",
feature = "gitlab",
feature = "bitbucket",
feature = "slack",
feature = "s3",
feature = "gcs"
))]
pub(crate) fn effective_proxy(&self) -> Option<String> {
self.proxy.clone()
}
#[cfg(any(
feature = "azure",
feature = "web",
feature = "github",
feature = "gitlab",
feature = "bitbucket",
feature = "slack",
feature = "s3",
feature = "gcs"
))]
pub(crate) fn effective_timeout(&self) -> Duration {
self.timeout.unwrap_or(crate::timeouts::HTTP_REQUEST) }
#[cfg(any(
feature = "azure",
feature = "web",
feature = "github",
feature = "gitlab",
feature = "bitbucket",
feature = "slack",
feature = "s3",
feature = "gcs"
))]
pub(crate) fn effective_insecure_tls(&self) -> bool {
self.insecure_tls
}
#[cfg(any(feature = "s3", feature = "gcs", feature = "azure"))]
pub(crate) fn allowing_private_endpoint() -> Self {
Self {
allow_private_endpoint: true,
..Self::default()
}
}
}
#[cfg(any(
feature = "azure",
feature = "web",
feature = "github",
feature = "gitlab",
feature = "bitbucket",
feature = "slack",
feature = "s3",
feature = "gcs"
))]
pub(crate) const REDIRECT_LIMIT: usize = 5;
#[cfg(any(feature = "web", feature = "azure", feature = "s3", feature = "gcs"))]
pub(crate) fn media_type(content_type: &str) -> &str {
content_type
.split_once(';')
.map_or(content_type, |(media_type, _)| media_type)
.trim()
}
pub(crate) fn user_agent(suffix: Option<&str>) -> String {
let base = concat!("keyhog/", env!("CARGO_PKG_VERSION"));
match suffix {
Some(s) if !s.is_empty() => format!("{base} ({s})"),
_ => base.to_string(),
}
}
#[cfg(any(
feature = "azure",
feature = "web",
feature = "github",
feature = "gitlab",
feature = "bitbucket",
feature = "slack",
feature = "s3",
feature = "gcs"
))]
macro_rules! shared_http_policy {
($builder:expr, $cfg:expr) => {{
let cfg = $cfg;
let builder = $builder
.timeout(cfg.effective_timeout()) .redirect(reqwest::redirect::Policy::limited(REDIRECT_LIMIT))
.user_agent(user_agent(cfg.ua_suffix.as_deref()))
.no_gzip()
.no_brotli()
.no_deflate()
.danger_accept_invalid_certs(cfg.effective_insecure_tls());
let builder = match cfg.effective_proxy().as_deref() {
Some("off") | Some("none") | Some("") | None => builder.no_proxy(),
Some(url) => {
let proxy = reqwest::Proxy::all(url)
.map_err(|e| format!("invalid --proxy URL {url:?}: {e}"))?;
builder.proxy(proxy)
}
};
Ok(builder)
}};
}
#[cfg(any(
feature = "azure",
feature = "web",
feature = "github",
feature = "gitlab",
feature = "bitbucket",
feature = "slack",
feature = "s3",
feature = "gcs"
))]
pub(crate) fn blocking_client_builder(
cfg: &HttpClientConfig,
) -> Result<reqwest::blocking::ClientBuilder, String> {
shared_http_policy!(reqwest::blocking::Client::builder(), cfg)
}
#[cfg(any(
feature = "azure",
feature = "web",
feature = "github",
feature = "gitlab",
feature = "bitbucket",
feature = "slack",
feature = "s3",
feature = "gcs"
))]
pub(crate) fn async_client_builder(
cfg: &HttpClientConfig,
) -> Result<reqwest::ClientBuilder, String> {
shared_http_policy!(reqwest::Client::builder(), cfg)
}