1use std::{net::SocketAddr, time::Duration};
2
3use anyhow::{Context, Result, ensure};
4use ipnet::IpNet;
5use tokio::sync::Semaphore;
6
7use super::{DeadlineDuration, HttpConfig, env::Env};
8
9impl HttpConfig {
10 pub(crate) fn validate(&self) -> Result<()> {
17 ensure!(
18 !self.trust_proxy_headers || !self.trusted_proxies.is_empty(),
19 "TRUSTED_PROXIES must contain at least one proxy CIDR when PROXY=true"
20 );
21 ensure!(
22 (1..=Semaphore::MAX_PERMITS).contains(&self.max_http_connections),
23 "MAX_HTTP_CONNECTIONS must be between 1 and {}",
24 Semaphore::MAX_PERMITS
25 );
26 ensure!(
27 (Duration::from_secs(1)..=Duration::from_hours(24)).contains(&self.header_read_timeout),
28 "HEADER_READ_TIMEOUT must be between 1 and 86400 seconds"
29 );
30 Ok(())
31 }
32
33 pub(super) fn from_env(env: &Env<'_>) -> Result<Self> {
34 let trust_proxy_headers = env.var("PROXY").default(false)?;
35 let trusted_proxies = env
36 .var::<String>("TRUSTED_PROXIES")
37 .optional()?
38 .map(|raw| {
39 raw.split(',')
40 .map(|cidr| {
41 cidr.trim()
42 .parse::<IpNet>()
43 .context("TRUSTED_PROXIES must be a comma-separated list of IP CIDRs")
44 })
45 .collect::<Result<Vec<_>>>()
46 })
47 .transpose()?
48 .unwrap_or_default();
49 let config = Self {
50 bind_address: env
51 .var("HTTP_INTERFACE")
52 .alias("BIND_ADDRESS")
53 .default(SocketAddr::from(([0, 0, 0, 0], 8070)))?,
54 trust_proxy_headers,
55 trusted_proxies,
56 max_http_connections: env.var("MAX_HTTP_CONNECTIONS").default(4096)?,
57 header_read_timeout: env
58 .var("HEADER_READ_TIMEOUT")
59 .default(Duration::from_secs(10))?,
60 shutdown_timeout: env
61 .var("SHUTDOWN_TIMEOUT_MS")
62 .default(DeadlineDuration::from_millis(10_000)?)?,
63 };
64 config.validate()?;
65 Ok(config)
66 }
67}