Skip to main content

zlayer_proxy/
config.rs

1//! Proxy configuration types
2//!
3//! This module defines configuration types for the proxy server.
4
5use serde::{Deserialize, Serialize};
6use std::net::SocketAddr;
7use std::path::PathBuf;
8use std::time::Duration;
9
10/// Main proxy configuration
11#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12pub struct ProxyConfig {
13    /// Server configuration
14    #[serde(default)]
15    pub server: ServerConfig,
16
17    /// TLS configuration (optional)
18    #[serde(default)]
19    pub tls: Option<TlsConfig>,
20
21    /// Connection pool settings
22    #[serde(default)]
23    pub pool: PoolConfig,
24
25    /// Timeouts
26    #[serde(default)]
27    pub timeouts: TimeoutConfig,
28
29    /// Header configuration
30    #[serde(default)]
31    pub headers: HeaderConfig,
32}
33
34/// Server bind configuration
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ServerConfig {
37    /// HTTP bind address
38    #[serde(default = "default_http_addr")]
39    pub http_addr: SocketAddr,
40
41    /// HTTPS bind address (if TLS enabled)
42    #[serde(default = "default_https_addr")]
43    pub https_addr: SocketAddr,
44
45    /// Enable HTTP/2
46    #[serde(default = "default_http2_enabled")]
47    pub http2_enabled: bool,
48
49    /// Maximum concurrent connections
50    #[serde(default = "default_max_connections")]
51    pub max_connections: usize,
52}
53
54fn default_http_addr() -> SocketAddr {
55    "0.0.0.0:80".parse().unwrap()
56}
57
58fn default_https_addr() -> SocketAddr {
59    "0.0.0.0:443".parse().unwrap()
60}
61
62fn default_http2_enabled() -> bool {
63    true
64}
65
66fn default_max_connections() -> usize {
67    10000
68}
69
70impl Default for ServerConfig {
71    fn default() -> Self {
72        Self {
73            http_addr: default_http_addr(),
74            https_addr: default_https_addr(),
75            http2_enabled: default_http2_enabled(),
76            max_connections: default_max_connections(),
77        }
78    }
79}
80
81/// TLS configuration
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct TlsConfig {
84    /// Path to certificate file (PEM)
85    pub cert_path: PathBuf,
86
87    /// Path to private key file (PEM)
88    pub key_path: PathBuf,
89
90    /// Minimum TLS version
91    #[serde(default = "default_min_tls_version")]
92    pub min_version: TlsVersion,
93
94    /// Enable ALPN for HTTP/2
95    #[serde(default = "default_true")]
96    pub alpn_h2: bool,
97}
98
99fn default_min_tls_version() -> TlsVersion {
100    TlsVersion::Tls12
101}
102
103fn default_true() -> bool {
104    true
105}
106
107/// TLS version
108#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
109pub enum TlsVersion {
110    #[serde(rename = "1.2")]
111    Tls12,
112    #[serde(rename = "1.3")]
113    Tls13,
114}
115
116/// Connection pool configuration
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct PoolConfig {
119    /// Maximum idle connections per backend
120    #[serde(default = "default_max_idle")]
121    pub max_idle_per_backend: usize,
122
123    /// Idle connection timeout
124    #[serde(default = "default_idle_timeout")]
125    pub idle_timeout: Duration,
126}
127
128fn default_max_idle() -> usize {
129    32
130}
131
132fn default_idle_timeout() -> Duration {
133    Duration::from_secs(90)
134}
135
136impl Default for PoolConfig {
137    fn default() -> Self {
138        Self {
139            max_idle_per_backend: default_max_idle(),
140            idle_timeout: default_idle_timeout(),
141        }
142    }
143}
144
145/// Timeout configuration
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct TimeoutConfig {
148    /// Connection timeout
149    #[serde(default = "default_connect_timeout")]
150    pub connect: Duration,
151
152    /// Request timeout (total)
153    #[serde(default = "default_request_timeout")]
154    pub request: Duration,
155
156    /// Read timeout
157    #[serde(default = "default_read_timeout")]
158    pub read: Duration,
159
160    /// Write timeout
161    #[serde(default = "default_write_timeout")]
162    pub write: Duration,
163}
164
165fn default_connect_timeout() -> Duration {
166    Duration::from_secs(5)
167}
168
169fn default_request_timeout() -> Duration {
170    Duration::from_secs(30)
171}
172
173fn default_read_timeout() -> Duration {
174    Duration::from_secs(30)
175}
176
177fn default_write_timeout() -> Duration {
178    Duration::from_secs(30)
179}
180
181impl Default for TimeoutConfig {
182    fn default() -> Self {
183        Self {
184            connect: default_connect_timeout(),
185            request: default_request_timeout(),
186            read: default_read_timeout(),
187            write: default_write_timeout(),
188        }
189    }
190}
191
192/// Header configuration
193#[allow(clippy::struct_excessive_bools)]
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct HeaderConfig {
196    /// Add X-Forwarded-For header
197    #[serde(default = "default_true")]
198    pub x_forwarded_for: bool,
199
200    /// Add X-Forwarded-Proto header
201    #[serde(default = "default_true")]
202    pub x_forwarded_proto: bool,
203
204    /// Add X-Forwarded-Host header
205    #[serde(default = "default_true")]
206    pub x_forwarded_host: bool,
207
208    /// Add X-Real-IP header
209    #[serde(default = "default_true")]
210    pub x_real_ip: bool,
211
212    /// Add Via header
213    #[serde(default = "default_true")]
214    pub via: bool,
215
216    /// Server name for Via header
217    #[serde(default = "default_server_name")]
218    pub server_name: String,
219
220    /// Enable HSTS (HTTP Strict Transport Security)
221    #[serde(default = "default_true")]
222    pub hsts: bool,
223
224    /// HSTS max-age in seconds (default: 1 year)
225    #[serde(default = "default_hsts_max_age")]
226    pub hsts_max_age: u64,
227
228    /// Include subdomains in HSTS
229    #[serde(default = "default_true")]
230    pub hsts_subdomains: bool,
231}
232
233fn default_server_name() -> String {
234    "zlayer-proxy".to_string()
235}
236
237fn default_hsts_max_age() -> u64 {
238    31_536_000 // 1 year
239}
240
241impl Default for HeaderConfig {
242    fn default() -> Self {
243        Self {
244            x_forwarded_for: true,
245            x_forwarded_proto: true,
246            x_forwarded_host: true,
247            x_real_ip: true,
248            via: true,
249            server_name: default_server_name(),
250            hsts: true,
251            hsts_max_age: default_hsts_max_age(),
252            hsts_subdomains: true,
253        }
254    }
255}
256
257#[cfg(test)]
258mod tests {
259    use super::*;
260
261    #[test]
262    fn test_default_config() {
263        let config = ProxyConfig::default();
264        assert_eq!(
265            config.server.http_addr,
266            "0.0.0.0:80".parse::<SocketAddr>().unwrap()
267        );
268        assert!(config.server.http2_enabled);
269        assert!(config.tls.is_none());
270        assert_eq!(config.timeouts.connect, Duration::from_secs(5));
271    }
272
273    #[test]
274    fn test_config_serialization() {
275        let config = ProxyConfig::default();
276        let json = serde_json::to_string(&config).unwrap();
277        let parsed: ProxyConfig = serde_json::from_str(&json).unwrap();
278        assert_eq!(parsed.server.http_addr, config.server.http_addr);
279    }
280}