Skip to main content

rust_serv/proxy/
handler.rs

1//! Proxy handler
2
3use super::config::ProxyConfig;
4
5/// Proxy handler manages multiple proxy configurations
6#[derive(Debug, Clone)]
7pub struct ProxyHandler {
8    /// List of proxy configurations (in order of priority)
9    configs: Vec<ProxyConfig>,
10    /// Default timeout for all proxies
11    default_timeout: std::time::Duration,
12}
13
14impl ProxyHandler {
15    /// Create a new proxy handler
16    pub fn new() -> Self {
17        Self {
18            configs: Vec::new(),
19            default_timeout: std::time::Duration::from_secs(30),
20        }
21    }
22
23    /// Add a proxy configuration
24    pub fn add_proxy(&mut self, config: ProxyConfig) {
25        self.configs.push(config);
26    }
27
28    /// Remove a proxy by path
29    pub fn remove_proxy(&mut self, path: &str) -> bool {
30        let len_before = self.configs.len();
31        self.configs.retain(|c| c.path != path);
32        self.configs.len() < len_before
33    }
34
35    /// Find matching proxy for a path
36    pub fn find_match(&self, path: &str) -> Option<&ProxyConfig> {
37        // Return first match (configs are checked in order)
38        self.configs.iter().find(|c| c.matches(path))
39    }
40
41    /// Check if a path should be proxied
42    pub fn should_proxy(&self, path: &str) -> bool {
43        self.find_match(path).is_some()
44    }
45
46    /// Get target URL for a path
47    pub fn get_target_url(&self, path: &str) -> Option<String> {
48        self.find_match(path).map(|c| c.build_target_url(path))
49    }
50
51    /// Get proxy count
52    pub fn proxy_count(&self) -> usize {
53        self.configs.len()
54    }
55
56    /// Clear all proxies
57    pub fn clear(&mut self) {
58        self.configs.clear();
59    }
60
61    /// Get all paths
62    pub fn paths(&self) -> Vec<String> {
63        self.configs.iter().map(|c| c.path.clone()).collect()
64    }
65
66    /// Check if a proxy exists for a path
67    pub fn has_proxy(&self, path: &str) -> bool {
68        self.configs.iter().any(|c| c.path == path)
69    }
70
71    /// Set default timeout
72    pub fn set_default_timeout(&mut self, timeout: std::time::Duration) {
73        self.default_timeout = timeout;
74    }
75
76    /// Get default timeout
77    pub fn default_timeout(&self) -> std::time::Duration {
78        self.default_timeout
79    }
80
81    /// Get mutable configs
82    pub fn configs_mut(&mut self) -> &mut Vec<ProxyConfig> {
83        &mut self.configs
84    }
85
86    /// Get configs reference
87    pub fn configs(&self) -> &[ProxyConfig] {
88        &self.configs
89    }
90}
91
92impl Default for ProxyHandler {
93    fn default() -> Self {
94        Self::new()
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_handler_creation() {
104        let handler = ProxyHandler::new();
105        assert_eq!(handler.proxy_count(), 0);
106    }
107
108    #[test]
109    fn test_add_proxy() {
110        let mut handler = ProxyHandler::new();
111        handler.add_proxy(ProxyConfig::new("/api", "http://localhost:3000"));
112        
113        assert_eq!(handler.proxy_count(), 1);
114        assert!(handler.has_proxy("/api"));
115    }
116
117    #[test]
118    fn test_remove_proxy() {
119        let mut handler = ProxyHandler::new();
120        handler.add_proxy(ProxyConfig::new("/api", "http://localhost:3000"));
121        
122        assert!(handler.remove_proxy("/api"));
123        assert_eq!(handler.proxy_count(), 0);
124        assert!(!handler.has_proxy("/api"));
125    }
126
127    #[test]
128    fn test_remove_nonexistent() {
129        let mut handler = ProxyHandler::new();
130        assert!(!handler.remove_proxy("/nonexistent"));
131    }
132
133    #[test]
134    fn test_find_match() {
135        let mut handler = ProxyHandler::new();
136        handler.add_proxy(ProxyConfig::new("/api", "http://localhost:3000"));
137        
138        let config = handler.find_match("/api/users").unwrap();
139        assert_eq!(config.path, "/api");
140        assert_eq!(config.target, "http://localhost:3000");
141    }
142
143    #[test]
144    fn test_find_match_no_match() {
145        let handler = ProxyHandler::new();
146        assert!(handler.find_match("/api").is_none());
147    }
148
149    #[test]
150    fn test_should_proxy() {
151        let mut handler = ProxyHandler::new();
152        handler.add_proxy(ProxyConfig::new("/api", "http://localhost:3000"));
153        
154        assert!(handler.should_proxy("/api"));
155        assert!(handler.should_proxy("/api/users"));
156        assert!(!handler.should_proxy("/other"));
157    }
158
159    #[test]
160    fn test_get_target_url() {
161        let mut handler = ProxyHandler::new();
162        handler.add_proxy(ProxyConfig::new("/api", "http://localhost:3000"));
163        
164        let url = handler.get_target_url("/api/users").unwrap();
165        assert_eq!(url, "http://localhost:3000/users");
166    }
167
168    #[test]
169    fn test_get_target_url_no_match() {
170        let handler = ProxyHandler::new();
171        assert!(handler.get_target_url("/api").is_none());
172    }
173
174    #[test]
175    fn test_multiple_proxies() {
176        let mut handler = ProxyHandler::new();
177        
178        handler.add_proxy(ProxyConfig::new("/api", "http://api:3000"));
179        handler.add_proxy(ProxyConfig::new("/web", "http://web:8080"));
180        handler.add_proxy(ProxyConfig::new("/admin", "http://admin:9000"));
181        
182        assert_eq!(handler.proxy_count(), 3);
183        
184        assert!(handler.should_proxy("/api"));
185        assert!(handler.should_proxy("/web"));
186        assert!(handler.should_proxy("/admin"));
187        assert!(!handler.should_proxy("/other"));
188    }
189
190    #[test]
191    fn test_proxy_priority() {
192        let mut handler = ProxyHandler::new();
193        
194        // More specific path should be added first for priority
195        handler.add_proxy(ProxyConfig::new("/api/v1", "http://api-v1:3000"));
196        handler.add_proxy(ProxyConfig::new("/api", "http://api:3000"));
197        
198        // /api/v1/users should match /api/v1 (first)
199        let config = handler.find_match("/api/v1/users").unwrap();
200        assert_eq!(config.path, "/api/v1");
201        
202        // /api/other should match /api (second)
203        let config = handler.find_match("/api/other").unwrap();
204        assert_eq!(config.path, "/api");
205    }
206
207    #[test]
208    fn test_clear() {
209        let mut handler = ProxyHandler::new();
210        handler.add_proxy(ProxyConfig::new("/api", "http://localhost:3000"));
211        handler.add_proxy(ProxyConfig::new("/web", "http://localhost:8080"));
212        
213        handler.clear();
214        
215        assert_eq!(handler.proxy_count(), 0);
216        assert!(!handler.should_proxy("/api"));
217    }
218
219    #[test]
220    fn test_paths() {
221        let mut handler = ProxyHandler::new();
222        handler.add_proxy(ProxyConfig::new("/api", "http://localhost:3000"));
223        handler.add_proxy(ProxyConfig::new("/web", "http://localhost:8080"));
224        
225        let mut paths = handler.paths();
226        paths.sort();
227        
228        assert_eq!(paths, vec!["/api", "/web"]);
229    }
230
231    #[test]
232    fn test_default_timeout() {
233        let mut handler = ProxyHandler::new();
234        assert_eq!(handler.default_timeout(), std::time::Duration::from_secs(30));
235        
236        handler.set_default_timeout(std::time::Duration::from_secs(60));
237        assert_eq!(handler.default_timeout(), std::time::Duration::from_secs(60));
238    }
239
240    #[test]
241    fn test_default() {
242        let handler = ProxyHandler::default();
243        assert_eq!(handler.proxy_count(), 0);
244    }
245
246    #[test]
247    fn test_configs_access() {
248        let mut handler = ProxyHandler::new();
249        handler.add_proxy(ProxyConfig::new("/api", "http://localhost:3000"));
250        
251        // Immutable access
252        assert_eq!(handler.configs().len(), 1);
253        
254        // Mutable access
255        handler.configs_mut().push(ProxyConfig::new("/web", "http://localhost:8080"));
256        assert_eq!(handler.proxy_count(), 2);
257    }
258
259    #[test]
260    fn test_empty_path() {
261        let mut handler = ProxyHandler::new();
262        handler.add_proxy(ProxyConfig::new("/", "http://localhost:3000"));
263        
264        assert!(handler.should_proxy("/"));
265        assert!(handler.should_proxy("/anything"));
266        
267        let url = handler.get_target_url("/api").unwrap();
268        assert_eq!(url, "http://localhost:3000/api");
269    }
270}