Skip to main content

mockforge_proxy/
lib.rs

1//! Proxy functionality for forwarding requests to upstream services
2//!
3//! This crate provides proxy/reverse-proxy capabilities for MockForge:
4//! - config: Proxy configuration and rule management
5//! - handler: Request/response handling and processing
6//! - client: HTTP client functionality for upstream requests
7//! - middleware: Proxy middleware and request transformation
8//! - routing: Route matching and rule evaluation
9
10pub mod body_transform;
11pub mod client;
12pub mod conditional;
13pub mod config;
14pub mod conformance;
15/// Upstream egress guard (SSRF mitigation, #1012 / MF-002).
16pub mod egress;
17pub mod handler;
18pub mod middleware;
19/// Reality-slider mock/proxy switching middleware (#222). Moved from
20/// `mockforge_http::reality_proxy` under #555 phase 8. Only foreign dep
21/// is `mockforge_core::consistency::UnifiedState`, already in the proxy
22/// crate's dep graph. `mockforge_http::reality_proxy` is now a thin shim
23/// re-exporting from here.
24pub mod reality;
25pub mod routing;
26/// Browser/mobile intercepting proxy server. Moved from
27/// `mockforge_http::proxy_server` under #555 phase 1 — the file's only
28/// non-test caller was a re-export through http itself, and its imports
29/// already lived in this crate (`body_transform`, `config::ProxyConfig`).
30/// `mockforge_http::proxy_server` is now a thin shim re-exporting from here.
31pub mod server;
32
33// Re-export commonly used types
34pub use body_transform::BodyTransformationMiddleware;
35pub use config::{BodyTransform, BodyTransformRule, MigrationMode, TransformOperation};
36pub use middleware::*;
37pub use routing::*;
38
39pub use client::{ProxyClient, ProxyResponse};
40pub use conditional::{evaluate_proxy_condition, find_matching_rule};
41pub use config::{ProxyConfig, ProxyRule};
42pub use handler::ProxyHandler;
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use axum::http::Method;
48
49    #[test]
50    fn test_proxy_config() {
51        let mut config = ProxyConfig::new("http://api.example.com".to_string());
52        config.enabled = true;
53        assert!(config.should_proxy(&Method::GET, "/proxy/users"));
54        assert!(!config.should_proxy(&Method::GET, "/api/users"));
55
56        let stripped = config.strip_prefix("/proxy/users");
57        assert_eq!(stripped, "/users");
58    }
59
60    #[test]
61    fn test_proxy_config_no_prefix() {
62        let mut config = ProxyConfig::new("http://api.example.com".to_string());
63        config.prefix = None;
64        config.enabled = true;
65
66        assert!(config.should_proxy(&Method::GET, "/api/users"));
67        assert!(config.should_proxy(&Method::GET, "/any/path"));
68
69        let stripped = config.strip_prefix("/api/users");
70        assert_eq!(stripped, "/api/users");
71    }
72
73    #[test]
74    fn test_proxy_config_with_rules() {
75        let mut config = ProxyConfig::new("http://default.example.com".to_string());
76        config.enabled = true;
77        config.rules.push(ProxyRule {
78            path_pattern: "/api/users/*".to_string(),
79            target_url: "http://users.example.com".to_string(),
80            enabled: true,
81            pattern: "/api/users/*".to_string(),
82            upstream_url: "http://users.example.com".to_string(),
83            migration_mode: MigrationMode::Auto,
84            migration_group: None,
85            condition: None,
86        });
87        config.rules.push(ProxyRule {
88            path_pattern: "/api/orders/*".to_string(),
89            target_url: "http://orders.example.com".to_string(),
90            enabled: true,
91            pattern: "/api/orders/*".to_string(),
92            upstream_url: "http://orders.example.com".to_string(),
93            migration_mode: MigrationMode::Auto,
94            migration_group: None,
95            condition: None,
96        });
97
98        assert!(config.should_proxy(&Method::GET, "/api/users/123"));
99        assert!(config.should_proxy(&Method::GET, "/api/orders/456"));
100
101        assert_eq!(config.get_upstream_url("/api/users/123"), "http://users.example.com");
102        assert_eq!(config.get_upstream_url("/api/orders/456"), "http://orders.example.com");
103        assert_eq!(config.get_upstream_url("/api/products"), "http://default.example.com");
104    }
105
106    #[test]
107    fn test_proxy_config_passthrough() {
108        let mut config = ProxyConfig::new("http://api.example.com".to_string());
109        config.passthrough_by_default = true;
110        config.prefix = None;
111        config.enabled = true;
112
113        assert!(config.should_proxy(&Method::GET, "/api/users"));
114        assert!(config.should_proxy(&Method::POST, "/api/orders"));
115
116        config.passthrough_by_default = false;
117        config.prefix = Some("/proxy".to_string());
118
119        assert!(config.should_proxy(&Method::GET, "/proxy/users"));
120        assert!(!config.should_proxy(&Method::GET, "/api/users"));
121    }
122}