Skip to main content

wisegate_core/
default_config.rs

1//! Ready-to-use configuration for library consumers.
2//!
3//! [`DefaultConfig`] implements every configuration trait
4//! ([`RateLimitingProvider`], [`ProxyProvider`], [`FilteringProvider`],
5//! [`ConnectionProvider`], [`AuthenticationProvider`]) with the same defaults
6//! the CLI uses. Tweak its public fields to customize behaviour without
7//! defining a fresh struct or implementing each trait by hand.
8//!
9//! # Example
10//!
11//! ```
12//! use std::sync::Arc;
13//! use std::time::Duration;
14//! use wisegate_core::{DefaultConfig, RateLimiter};
15//!
16//! let mut config = DefaultConfig::default();
17//! config.rate_limit.max_requests = 200;
18//! config.rate_limit.window_duration = Duration::from_secs(30);
19//! config.blocked_methods = vec!["TRACE".into(), "CONNECT".into()];
20//!
21//! let _limiter = RateLimiter::new();
22//! let _shared_config = Arc::new(config);
23//! ```
24
25use crate::auth::Credentials;
26use crate::defaults;
27use crate::types::{
28    AuthenticationProvider, ConnectionProvider, FilteringProvider, ProxyConfig, ProxyProvider,
29    RateLimitCleanupConfig, RateLimitConfig, RateLimitingProvider,
30};
31
32/// Configuration backed by plain fields — pre-populated with WiseGate's defaults.
33///
34/// Use this when you want to drop wisegate-core into an application without
35/// designing your own configuration plumbing. Mutate the fields directly,
36/// then pass the struct (often wrapped in an `Arc`) to
37/// [`request_handler::handle_request`](crate::request_handler::handle_request).
38#[derive(Debug, Clone)]
39pub struct DefaultConfig {
40    /// Rate limit policy applied per client IP.
41    pub rate_limit: RateLimitConfig,
42    /// Rate limiter housekeeping policy.
43    pub cleanup: RateLimitCleanupConfig,
44    /// Upstream proxy behaviour (timeout, body size cap).
45    pub proxy: ProxyConfig,
46    /// Trusted proxy IPs. `Some` activates strict mode; `None` is permissive.
47    pub allowed_proxy_ips: Option<Vec<String>>,
48    /// Client IPs that should be rejected with 403.
49    pub blocked_ips: Vec<String>,
50    /// HTTP methods that should be rejected with 405.
51    pub blocked_methods: Vec<String>,
52    /// URL substrings that should be rejected with 404.
53    pub blocked_patterns: Vec<String>,
54    /// Hard cap on concurrent connections; `0` disables the limit.
55    pub max_connections: usize,
56    /// Basic Auth credential set (empty disables Basic Auth).
57    pub auth_credentials: Credentials,
58    /// Realm advertised in `WWW-Authenticate`.
59    pub auth_realm: String,
60    /// Bearer token (`None` or empty disables bearer auth).
61    pub bearer_token: Option<String>,
62    /// Forward the `Authorization` header to the upstream after wisegate auth.
63    pub forward_authorization_header: bool,
64}
65
66impl Default for DefaultConfig {
67    fn default() -> Self {
68        Self {
69            rate_limit: RateLimitConfig {
70                max_requests: defaults::RATE_LIMIT_REQUESTS,
71                window_duration: defaults::RATE_LIMIT_WINDOW,
72            },
73            cleanup: RateLimitCleanupConfig {
74                threshold: defaults::RATE_LIMIT_CLEANUP_THRESHOLD,
75                interval: defaults::RATE_LIMIT_CLEANUP_INTERVAL,
76            },
77            proxy: ProxyConfig {
78                timeout: defaults::PROXY_TIMEOUT,
79                max_body_size: defaults::MAX_BODY_SIZE,
80            },
81            allowed_proxy_ips: None,
82            blocked_ips: Vec::new(),
83            blocked_methods: Vec::new(),
84            blocked_patterns: Vec::new(),
85            max_connections: defaults::MAX_CONNECTIONS,
86            auth_credentials: Credentials::new(),
87            auth_realm: defaults::AUTH_REALM.to_string(),
88            bearer_token: None,
89            forward_authorization_header: false,
90        }
91    }
92}
93
94impl RateLimitingProvider for DefaultConfig {
95    fn rate_limit_config(&self) -> &RateLimitConfig {
96        &self.rate_limit
97    }
98    fn rate_limit_cleanup_config(&self) -> &RateLimitCleanupConfig {
99        &self.cleanup
100    }
101}
102
103impl ProxyProvider for DefaultConfig {
104    fn proxy_config(&self) -> &ProxyConfig {
105        &self.proxy
106    }
107    fn allowed_proxy_ips(&self) -> Option<&[String]> {
108        self.allowed_proxy_ips.as_deref()
109    }
110}
111
112impl FilteringProvider for DefaultConfig {
113    fn blocked_ips(&self) -> &[String] {
114        &self.blocked_ips
115    }
116    fn blocked_methods(&self) -> &[String] {
117        &self.blocked_methods
118    }
119    fn blocked_patterns(&self) -> &[String] {
120        &self.blocked_patterns
121    }
122}
123
124impl ConnectionProvider for DefaultConfig {
125    fn max_connections(&self) -> usize {
126        self.max_connections
127    }
128}
129
130impl AuthenticationProvider for DefaultConfig {
131    fn auth_credentials(&self) -> &Credentials {
132        &self.auth_credentials
133    }
134    fn auth_realm(&self) -> &str {
135        &self.auth_realm
136    }
137    fn bearer_token(&self) -> Option<&str> {
138        self.bearer_token.as_deref()
139    }
140    fn forward_authorization_header(&self) -> bool {
141        self.forward_authorization_header
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use crate::types::ConfigProvider;
149
150    fn assert_is_config_provider<T: ConfigProvider>(_: &T) {}
151
152    #[test]
153    fn default_config_implements_config_provider() {
154        let config = DefaultConfig::default();
155        assert_is_config_provider(&config);
156    }
157
158    #[test]
159    fn default_values_match_cli_defaults() {
160        let config = DefaultConfig::default();
161        assert_eq!(
162            config.rate_limit.max_requests,
163            defaults::RATE_LIMIT_REQUESTS
164        );
165        assert_eq!(config.max_connections, defaults::MAX_CONNECTIONS);
166        assert!(config.allowed_proxy_ips.is_none());
167        assert!(!config.forward_authorization_header);
168    }
169}