use apollo_configuration::ErrorCollector;
use apollo_configuration::Validate;
use apollo_configuration::configuration;
use apollo_configuration::types::{Duration, HeaderName, Url};
use apollo_redaction::Redacted;
use apollo_redaction::strategy::UrlPasswordRedactor;
use std::num::NonZeroUsize;
use std::time::Duration as StdDuration;
pub type ProxyUrl = Redacted<Url, UrlPasswordRedactor>;
#[non_exhaustive]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Protocol {
#[default]
Http1,
Http2,
Alpn,
}
impl Validate for Protocol {}
#[non_exhaustive]
#[configuration]
pub struct Http2Config {
#[config(default = Duration::from(StdDuration::from_secs(300)))]
pub connection_idle_timeout: Duration,
#[config(default = Duration::from(StdDuration::from_secs(30)))]
pub keep_alive_interval: Duration,
#[config(default = Duration::from(StdDuration::from_secs(20)))]
pub keep_alive_timeout: Duration,
#[config(default = true)]
pub keep_alive_while_idle: bool,
}
#[non_exhaustive]
#[configuration]
pub struct TcpConfig {
#[config(default = true)]
pub nodelay: bool,
#[config(default = Duration::from(StdDuration::from_secs(60)))]
pub keepalive: Duration,
}
#[non_exhaustive]
#[configuration]
pub struct Http1Config {
#[config(default = NonZeroUsize::new(10).unwrap())]
pub pool_max_idle_per_host: NonZeroUsize,
#[config(default = Duration::from(StdDuration::from_secs(90)))]
pub pool_idle_timeout: Duration,
}
#[non_exhaustive]
#[configuration]
pub struct MetricsConfig {
pub request_body_size: bool,
pub response_body_size: bool,
pub url_scheme: bool,
}
#[non_exhaustive]
#[configuration]
pub struct SpansConfig {
pub url_scheme: bool,
pub request_body_size: bool,
pub response_body_size: bool,
pub user_agent: bool,
pub network_transport: bool,
pub network_local_address: Option<String>,
pub network_local_port: Option<u16>,
#[config(skip_validate)]
pub request_headers: Vec<HeaderName>,
#[config(skip_validate)]
pub response_headers: Vec<HeaderName>,
}
#[non_exhaustive]
#[configuration]
pub struct TelemetryConfig {
pub metrics: MetricsConfig,
pub spans: SpansConfig,
}
#[non_exhaustive]
#[configuration]
pub struct ProxyConfig {
#[config(required, validate = validate_proxy_url)]
pub url: ProxyUrl,
}
fn validate_proxy_url(url: &ProxyUrl, mut errors: ErrorCollector<'_>) {
let url = url.unredact();
if url.host_str().is_none_or(str::is_empty) {
errors.report_simple("proxy URL must include a host");
}
match url.scheme() {
"http" | "https" => {}
other => errors.report_simple(format!(
"unsupported proxy URL scheme `{other}`; must be http or https"
)),
}
}
#[non_exhaustive]
#[configuration]
pub struct TlsConfig {
pub certificate_authorities: Option<Redacted<String>>,
#[config(default = true)]
pub use_native_certificate_store: bool,
pub client_authentication: Option<ClientAuthentication>,
pub danger_accept_invalid_certs: bool,
}
#[non_exhaustive]
#[configuration]
pub struct ClientAuthentication {
#[config(required)]
pub certificate_chain: Redacted<String>,
#[config(required)]
pub key: Redacted<String>,
}
#[non_exhaustive]
#[configuration]
pub struct HttpClientConfig {
#[config(default = Duration::from(StdDuration::from_secs(10)))]
pub connect_timeout: Duration,
pub http1: Http1Config,
pub http2: Http2Config,
pub protocol: Protocol,
pub tcp: TcpConfig,
pub telemetry: TelemetryConfig,
pub tls: TlsConfig,
pub proxy: Option<ProxyConfig>,
}