rest/
config.rs

1use core::service::ServiceConf;
2use serde::{Deserialize, Serialize};
3
4/// MiddlewaresConf with defaults set to true.
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct MiddlewaresConf {
7    #[serde(default = "default_true")]
8    pub trace: bool,
9    #[serde(default = "default_true")]
10    pub log: bool,
11    #[serde(default = "default_true")]
12    pub prometheus: bool,
13    #[serde(default = "default_true")]
14    pub max_connections: bool,
15    #[serde(default = "default_true")]
16    pub breaker: bool,
17    #[serde(default = "default_true")]
18    pub shedding: bool,
19    #[serde(default = "default_true")]
20    pub timeout: bool,
21    #[serde(default = "default_true")]
22    pub recover: bool,
23    #[serde(default = "default_true")]
24    pub metrics: bool,
25    #[serde(default = "default_true")]
26    pub max_bytes: bool,
27    #[serde(default = "default_true")]
28    pub gzip: bool,
29}
30
31fn default_true() -> bool {
32    true
33}
34
35impl Default for MiddlewaresConf {
36    fn default() -> Self {
37        Self {
38            trace: true,
39            log: true,
40            prometheus: true,
41            max_connections: true,
42            breaker: true,
43            shedding: true,
44            timeout: true,
45            recover: true,
46            metrics: true,
47            max_bytes: true,
48            gzip: true,
49        }
50    }
51}
52
53/// Private key configuration.
54#[derive(Debug, Clone, Default, Serialize, Deserialize)]
55pub struct PrivateKeyConf {
56    #[serde(default)]
57    pub fingerprint: String,
58    #[serde(default)]
59    pub key_file: String,
60}
61
62fn default_signature_strict() -> bool {
63    false
64}
65
66/// Default expiry is 1h; stored as seconds (3600s) to avoid extra Duration deps.
67fn default_signature_expiry_secs() -> u64 {
68    3600
69}
70
71/// Signature configuration.
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct SignatureConf {
74    #[serde(default = "default_signature_strict")]
75    pub strict: bool,
76    /// Expiry in seconds (default 1h).
77    #[serde(default = "default_signature_expiry_secs")]
78    pub expiry_secs: u64,
79    #[serde(default)]
80    pub private_keys: Vec<PrivateKeyConf>,
81}
82
83impl Default for SignatureConf {
84    fn default() -> Self {
85        Self {
86            strict: false,
87            expiry_secs: 3600,
88            private_keys: Vec::new(),
89        }
90    }
91}
92
93fn default_host() -> String {
94    "0.0.0.0".to_string()
95}
96fn default_port() -> u16 {
97    8888
98}
99fn default_max_connections() -> i64 {
100    10_000
101}
102fn default_max_bytes() -> i64 {
103    16 * 1_048_576 // 16 MiB default limit
104}
105fn default_timeout_ms() -> Option<u64> {
106    Some(3_000)
107}
108fn default_cpu_threshold() -> i64 {
109    900
110}
111fn default_reuse_port() -> bool {
112    false
113}
114fn default_workers() -> Option<usize> {
115    None
116}
117fn default_cpu_affinity() -> Option<Vec<usize>> {
118    None
119}
120fn default_http2() -> bool {
121    false
122}
123fn default_http2_h2c() -> bool {
124    false
125}
126fn default_http1_keep_alive() -> bool {
127    true
128}
129fn default_http1_max_buf_size() -> Option<usize> {
130    None
131}
132fn default_tcp_keepalive_secs() -> Option<u64> {
133    None
134}
135fn default_rate_limit() -> Option<RateLimitConf> {
136    None
137}
138fn default_concurrency_limit() -> Option<usize> {
139    None
140}
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct RateLimitConf {
143    /// tokens per second
144    pub permits_per_second: u64,
145    /// burst size
146    pub burst: u64,
147}
148
149/// RestConf definition (rest package).
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct RestConf {
152    /// Equivalent to embedding `service.ServiceConf`
153    #[serde(flatten, default)]
154    pub service: ServiceConf,
155
156    #[serde(default = "default_host")]
157    pub host: String,
158    #[serde(default = "default_port")]
159    pub port: u16,
160
161    #[serde(default, skip_serializing_if = "Option::is_none")]
162    pub cert_file: Option<String>,
163    #[serde(default, skip_serializing_if = "Option::is_none")]
164    pub key_file: Option<String>,
165
166    #[serde(default = "default_max_connections")]
167    pub max_connections: i64,
168    #[serde(default = "default_max_bytes")]
169    pub max_bytes: i64,
170
171    /// Per-request timeout in milliseconds. None disables it. Default 3000ms.
172    #[serde(
173        default = "default_timeout_ms",
174        alias = "RequestTimeoutMs",
175        skip_serializing_if = "Option::is_none"
176    )]
177    pub timeout: Option<u64>,
178
179    /// range: [0, 1000)
180    #[serde(default = "default_cpu_threshold")]
181    pub cpu_threshold: i64,
182
183    #[serde(default, skip_serializing_if = "Option::is_none")]
184    pub signature: Option<SignatureConf>,
185
186    /// There are default values for all the items in Middlewares.
187    #[serde(default)]
188    pub middlewares: MiddlewaresConf,
189
190    /// TraceIgnorePaths is paths blacklist for trace middleware.
191    #[serde(default)]
192    pub trace_ignore_paths: Vec<String>,
193
194    /// Tokio worker threads (None => tokio default).
195    #[serde(default = "default_workers", skip_serializing_if = "Option::is_none")]
196    pub workers: Option<usize>,
197
198    /// Enable SO_REUSEPORT multi-listener (Linux recommended for multi-core).
199    #[serde(default = "default_reuse_port")]
200    pub reuse_port: bool,
201
202    /// CPU affinity (Linux only). Ignored on unsupported platforms.
203    #[serde(
204        default = "default_cpu_affinity",
205        skip_serializing_if = "Option::is_none"
206    )]
207    pub cpu_affinity: Option<Vec<usize>>,
208
209    /// Enable HTTP/2 (TLS or h2c). Default off (HTTP/1.1).
210    #[serde(default = "default_http2")]
211    pub http2: bool,
212    /// Enable h2c (HTTP/2 without TLS). Default off.
213    #[serde(default = "default_http2_h2c")]
214    pub h2c: bool,
215    /// http1 keep-alive toggle.
216    #[serde(default = "default_http1_keep_alive")]
217    pub http1_keep_alive: bool,
218    /// http1 max buffer size.
219    #[serde(
220        default = "default_http1_max_buf_size",
221        skip_serializing_if = "Option::is_none"
222    )]
223    pub http1_max_buf_size: Option<usize>,
224    /// TCP keepalive seconds (None => OS default).
225    #[serde(
226        default = "default_tcp_keepalive_secs",
227        skip_serializing_if = "Option::is_none"
228    )]
229    pub tcp_keepalive_secs: Option<u64>,
230
231    /// Global rate limit config (tokens/s, burst). None => disabled.
232    #[serde(
233        default = "default_rate_limit",
234        skip_serializing_if = "Option::is_none"
235    )]
236    pub rate_limit: Option<RateLimitConf>,
237
238    /// Global concurrent in-flight request limit. None => disabled.
239    #[serde(
240        default = "default_concurrency_limit",
241        skip_serializing_if = "Option::is_none"
242    )]
243    pub concurrency_limit: Option<usize>,
244}
245
246impl Default for RestConf {
247    fn default() -> Self {
248        Self::new()
249    }
250}
251
252impl RestConf {
253    pub fn new() -> Self {
254        let conf = Self {
255            service: ServiceConf::default(),
256            host: default_host(),
257            port: default_port(),
258            cert_file: None,
259            key_file: None,
260            max_connections: default_max_connections(),
261            max_bytes: default_max_bytes(),
262            timeout: default_timeout_ms(),
263            cpu_threshold: default_cpu_threshold(),
264            signature: None,
265            middlewares: MiddlewaresConf::default(),
266            trace_ignore_paths: Vec::new(),
267            workers: default_workers(),
268            reuse_port: default_reuse_port(),
269            cpu_affinity: default_cpu_affinity(),
270            http2: default_http2(),
271            h2c: default_http2_h2c(),
272            http1_keep_alive: default_http1_keep_alive(),
273            http1_max_buf_size: default_http1_max_buf_size(),
274            tcp_keepalive_secs: default_tcp_keepalive_secs(),
275            rate_limit: default_rate_limit(),
276            concurrency_limit: default_concurrency_limit(),
277        };
278        conf.validate().expect("RestConf::new validation failed");
279        conf
280    }
281}
282
283impl RestConf {
284    pub fn addr_string(&self) -> String {
285        format!("{}:{}", self.host, self.port)
286    }
287
288    pub fn validate(&self) -> Result<(), String> {
289        if !(0..1000).contains(&self.cpu_threshold) {
290            return Err(format!(
291                "CpuThreshold out of range [0,1000): {}",
292                self.cpu_threshold
293            ));
294        }
295        if self.port == 0 {
296            return Err("Port must be > 0".to_string());
297        }
298        if let Some(w) = self.workers
299            && w == 0
300        {
301            return Err("Workers must be >= 1".to_string());
302        }
303        if let Some(aff) = &self.cpu_affinity
304            && aff.is_empty()
305        {
306            return Err("CpuAffinity cannot be empty when set".to_string());
307        }
308        if self.h2c && !self.http2 {
309            return Err("h2c requires http2=true".to_string());
310        }
311        if let Some(rl) = &self.rate_limit
312            && (rl.permits_per_second == 0 || rl.burst == 0)
313        {
314            return Err("RateLimit permits_per_second and burst must be > 0".to_string());
315        }
316        if let Some(c) = self.concurrency_limit
317            && c == 0
318        {
319            return Err("ConcurrencyLimit must be > 0".to_string());
320        }
321        Ok(())
322    }
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328    use core::service::Mode;
329
330    #[test]
331    fn defaults_should_work() {
332        let c = RestConf::default();
333        assert_eq!(c.host, "0.0.0.0");
334        assert!(c.port > 0);
335        assert_eq!(c.max_connections, 10_000);
336        assert_eq!(c.max_bytes, 16 * 1_048_576);
337        assert_eq!(c.timeout, Some(3_000));
338        assert_eq!(c.cpu_threshold, 900);
339        assert_eq!(c.service.mode, Mode::Pro);
340        assert!(c.middlewares.trace);
341        assert!(c.middlewares.gzip);
342    }
343}