1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::defaults::*;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct ServerConfig {
11 pub listen: String,
12 pub fallback: String,
13 #[serde(default = "default_tcp_timeout_secs")]
14 pub tcp_idle_timeout_secs: u64,
15 #[serde(default = "default_udp_timeout_secs")]
16 pub udp_timeout_secs: u64,
17 #[serde(default = "default_max_udp_payload")]
18 pub max_udp_payload: usize,
19 #[serde(default = "default_max_udp_buffer_bytes")]
20 pub max_udp_buffer_bytes: usize,
21 #[serde(default = "default_max_header_bytes")]
22 pub max_header_bytes: usize,
23 #[serde(default)]
25 pub max_connections: Option<usize>,
26 #[serde(default)]
28 pub rate_limit: Option<RateLimitConfig>,
29 #[serde(default)]
31 pub fallback_pool: Option<FallbackPoolConfig>,
32 #[serde(default)]
34 pub resource_limits: Option<ResourceLimitsConfig>,
35 #[serde(default)]
37 pub tcp: TcpConfig,
38 #[serde(default)]
40 pub outbounds: HashMap<String, OutboundConfig>,
41 #[serde(default, rename = "rule-providers")]
43 pub rule_providers: HashMap<String, RuleProviderConfig>,
44 #[serde(default)]
46 pub rules: Vec<RouteRuleConfig>,
47 #[serde(default)]
49 pub geoip: Option<GeoipConfig>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct GeoipConfig {
59 #[serde(default = "default_geoip_source")]
61 pub source: String,
62 #[serde(default)]
64 pub path: Option<String>,
65 #[serde(default)]
67 pub url: Option<String>,
68 #[serde(default = "default_geoip_auto_update")]
70 pub auto_update: bool,
71 #[serde(default = "default_geoip_interval")]
73 pub interval: u64,
74 #[serde(default)]
76 pub cache_path: Option<String>,
77}
78
79impl Default for GeoipConfig {
80 fn default() -> Self {
81 Self {
82 source: default_geoip_source(),
83 path: None,
84 url: None,
85 auto_update: default_geoip_auto_update(),
86 interval: default_geoip_interval(),
87 cache_path: None,
88 }
89 }
90}
91
92fn default_geoip_source() -> String {
93 "geolite2-country".to_string()
94}
95
96fn default_geoip_auto_update() -> bool {
97 true
98}
99
100fn default_geoip_interval() -> u64 {
101 604800 }
103
104#[derive(Debug, Clone, Default, Serialize, Deserialize)]
106pub struct GeoResult {
107 pub country: String,
109 pub region: String,
111 pub city: String,
113 pub asn: u32,
115 pub org: String,
117 pub longitude: f64,
119 pub latitude: f64,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct TcpConfig {
126 #[serde(default = "default_tcp_no_delay")]
128 pub no_delay: bool,
129 #[serde(default = "default_tcp_keepalive_secs")]
131 pub keepalive_secs: u64,
132 #[serde(default = "default_tcp_reuse_port")]
134 pub reuse_port: bool,
135 #[serde(default = "default_tcp_fast_open")]
137 pub fast_open: bool,
138 #[serde(default = "default_tcp_fast_open_qlen")]
140 pub fast_open_qlen: u32,
141 #[serde(default = "default_tcp_prefer_ipv4")]
143 pub prefer_ipv4: bool,
144}
145
146impl Default for TcpConfig {
147 fn default() -> Self {
148 Self {
149 no_delay: default_tcp_no_delay(),
150 keepalive_secs: default_tcp_keepalive_secs(),
151 reuse_port: default_tcp_reuse_port(),
152 fast_open: default_tcp_fast_open(),
153 fast_open_qlen: default_tcp_fast_open_qlen(),
154 prefer_ipv4: default_tcp_prefer_ipv4(),
155 }
156 }
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct FallbackPoolConfig {
167 #[serde(default = "default_pool_max_idle")]
169 pub max_idle: usize,
170 #[serde(default = "default_pool_max_age_secs")]
172 pub max_age_secs: u64,
173 #[serde(default = "default_pool_fill_batch")]
175 pub fill_batch: usize,
176 #[serde(default = "default_pool_fill_delay_ms")]
178 pub fill_delay_ms: u64,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct ResourceLimitsConfig {
184 #[serde(default = "default_relay_buffer_size")]
186 pub relay_buffer_size: usize,
187 #[serde(default)]
189 pub tcp_send_buffer: usize,
190 #[serde(default)]
192 pub tcp_recv_buffer: usize,
193 #[serde(default = "default_connection_backlog")]
195 pub connection_backlog: u32,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct RateLimitConfig {
201 #[serde(default = "default_rate_limit_max_connections")]
203 pub max_connections_per_ip: u32,
204 #[serde(default = "default_rate_limit_window_secs")]
206 pub window_secs: u64,
207 #[serde(default = "default_rate_limit_cleanup_secs")]
209 pub cleanup_interval_secs: u64,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct TlsConfig {
214 pub cert: String,
216 pub key: String,
218 #[serde(default)]
220 pub alpn: Vec<String>,
221 #[serde(default = "default_min_tls_version")]
223 pub min_version: String,
224 #[serde(default = "default_max_tls_version")]
226 pub max_version: String,
227 #[serde(default)]
230 pub client_ca: Option<String>,
231 #[serde(default)]
234 pub cipher_suites: Vec<String>,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct WebSocketConfig {
239 #[serde(default = "default_ws_enabled")]
240 pub enabled: bool,
241 #[serde(default = "default_ws_mode")]
242 pub mode: String,
243 #[serde(default = "default_ws_path")]
244 pub path: String,
245 #[serde(default)]
246 pub host: Option<String>,
247 #[serde(default)]
248 pub listen: Option<String>,
249 #[serde(default = "default_ws_max_frame_bytes")]
250 pub max_frame_bytes: usize,
251}
252
253impl Default for WebSocketConfig {
254 fn default() -> Self {
255 Self {
256 enabled: default_ws_enabled(),
257 mode: default_ws_mode(),
258 path: default_ws_path(),
259 host: None,
260 listen: None,
261 max_frame_bytes: default_ws_max_frame_bytes(),
262 }
263 }
264}
265
266#[derive(Debug, Clone, Default, Serialize, Deserialize)]
267pub struct AuthConfig {
268 #[serde(default)]
273 pub passwords: Vec<String>,
274
275 #[serde(default)]
282 pub users: Vec<UserEntry>,
283
284 #[serde(default)]
289 pub http_url: Option<String>,
290
291 #[serde(default)]
293 pub http_node_token: Option<String>,
294
295 #[serde(default)]
297 pub http_codec: Option<String>,
298
299 #[serde(default = "default_http_cache_ttl_secs")]
302 pub http_cache_ttl_secs: u64,
303
304 #[serde(default = "default_http_cache_stale_ttl_secs")]
307 pub http_cache_stale_ttl_secs: u64,
308
309 #[serde(default = "default_http_cache_neg_ttl_secs")]
312 pub http_cache_neg_ttl_secs: u64,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct UserEntry {
317 pub id: String,
318 pub password: String,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize, Default)]
322pub struct MetricsConfig {
323 pub listen: Option<String>,
324 #[serde(default)]
326 pub geoip: Option<GeoipConfig>,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize, Default)]
330pub struct LoggingConfig {
331 pub level: Option<String>,
333 pub format: Option<String>,
335 pub output: Option<String>,
337 #[serde(default)]
339 pub filters: HashMap<String, String>,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct OutboundConfig {
345 #[serde(rename = "type")]
347 pub outbound_type: String,
348 #[serde(default)]
350 pub addr: Option<String>,
351 #[serde(default)]
353 pub password: Option<String>,
354 #[serde(default)]
356 pub sni: Option<String>,
357 #[serde(default)]
359 pub bind: Option<String>,
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct RuleProviderConfig {
365 pub format: String,
367 #[serde(default)]
369 pub behavior: Option<String>,
370 pub source: String,
372 #[serde(default)]
374 pub path: Option<String>,
375 #[serde(default)]
377 pub url: Option<String>,
378 #[serde(default)]
380 pub interval: Option<u64>,
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct RouteRuleConfig {
386 #[serde(default, rename = "rule-set")]
388 pub rule_set: Option<String>,
389 #[serde(default, rename = "type")]
391 pub rule_type: Option<String>,
392 #[serde(default)]
394 pub value: Option<String>,
395 pub outbound: String,
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 #[test]
404 fn geoip_config_defaults() {
405 let cfg = GeoipConfig::default();
406 assert_eq!(cfg.source, "geolite2-country");
407 assert!(cfg.path.is_none());
408 assert!(cfg.url.is_none());
409 assert!(cfg.auto_update);
410 assert_eq!(cfg.interval, 604800);
411 assert!(cfg.cache_path.is_none());
412 }
413
414 #[test]
415 fn geoip_config_deserialize_minimal() {
416 let toml_str = r#"source = "dbip-city""#;
417 let cfg: GeoipConfig = toml::from_str(toml_str).unwrap();
418 assert_eq!(cfg.source, "dbip-city");
419 assert!(cfg.auto_update);
420 assert_eq!(cfg.interval, 604800);
421 }
422
423 #[test]
424 fn geoip_config_deserialize_full() {
425 let toml_str = r#"
426source = "geolite2-city"
427path = "/tmp/test.mmdb"
428url = "https://example.com/geo.mmdb"
429auto_update = false
430interval = 3600
431cache_path = "/tmp/cache.mmdb"
432"#;
433 let cfg: GeoipConfig = toml::from_str(toml_str).unwrap();
434 assert_eq!(cfg.source, "geolite2-city");
435 assert_eq!(cfg.path.as_deref(), Some("/tmp/test.mmdb"));
436 assert_eq!(cfg.url.as_deref(), Some("https://example.com/geo.mmdb"));
437 assert!(!cfg.auto_update);
438 assert_eq!(cfg.interval, 3600);
439 assert_eq!(cfg.cache_path.as_deref(), Some("/tmp/cache.mmdb"));
440 }
441
442 #[test]
443 fn geo_result_default() {
444 let r = GeoResult::default();
445 assert!(r.country.is_empty());
446 assert!(r.region.is_empty());
447 assert!(r.city.is_empty());
448 assert_eq!(r.asn, 0);
449 assert!(r.org.is_empty());
450 assert_eq!(r.longitude, 0.0);
451 assert_eq!(r.latitude, 0.0);
452 }
453
454 #[test]
455 fn geo_result_serde_roundtrip() {
456 let r = GeoResult {
457 country: "CN".into(),
458 region: "Shanghai".into(),
459 city: "Shanghai".into(),
460 asn: 4134,
461 org: "China Telecom".into(),
462 longitude: 121.47,
463 latitude: 31.23,
464 };
465 let json = serde_json::to_string(&r).unwrap();
466 let r2: GeoResult = serde_json::from_str(&json).unwrap();
467 assert_eq!(r2.country, "CN");
468 assert_eq!(r2.asn, 4134);
469 assert!((r2.longitude - 121.47).abs() < 0.001);
470 }
471
472 #[test]
473 fn metrics_config_default() {
474 let cfg = MetricsConfig::default();
475 assert!(cfg.listen.is_none());
476 assert!(cfg.geoip.is_none());
477 }
478
479 #[test]
480 fn metrics_config_with_geoip() {
481 let toml_str = r#"
482listen = "0.0.0.0:9100"
483
484[geoip]
485source = "dbip-country"
486"#;
487 let cfg: MetricsConfig = toml::from_str(toml_str).unwrap();
488 assert_eq!(cfg.listen.as_deref(), Some("0.0.0.0:9100"));
489 let geoip = cfg.geoip.unwrap();
490 assert_eq!(geoip.source, "dbip-country");
491 }
492}