mockforge_core/proxy/
config.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct ProxyConfig {
8 pub enabled: bool,
10 pub target_url: Option<String>,
12 pub timeout_seconds: u64,
14 pub follow_redirects: bool,
16 pub headers: std::collections::HashMap<String, String>,
18 pub prefix: Option<String>,
20 pub passthrough_by_default: bool,
22 pub rules: Vec<ProxyRule>,
24}
25
26#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
28pub struct ProxyRule {
29 pub path_pattern: String,
31 pub target_url: String,
33 pub enabled: bool,
35 pub pattern: String,
37 pub upstream_url: String,
39}
40
41impl Default for ProxyRule {
42 fn default() -> Self {
43 Self {
44 path_pattern: "/".to_string(),
45 target_url: "http://localhost:9080".to_string(),
46 enabled: true,
47 pattern: "/".to_string(),
48 upstream_url: "http://localhost:9080".to_string(),
49 }
50 }
51}
52
53impl ProxyConfig {
54 pub fn new(upstream_url: String) -> Self {
56 Self {
57 enabled: true,
58 target_url: Some(upstream_url),
59 timeout_seconds: 30,
60 follow_redirects: true,
61 headers: std::collections::HashMap::new(),
62 prefix: Some("/proxy/".to_string()),
63 passthrough_by_default: true,
64 rules: Vec::new(),
65 }
66 }
67
68 pub fn should_proxy(&self, _method: &axum::http::Method, path: &str) -> bool {
70 if !self.enabled {
71 return false;
72 }
73
74 for rule in &self.rules {
76 if rule.enabled && self.path_matches_pattern(&rule.path_pattern, path) {
77 return true;
78 }
79 }
80
81 match &self.prefix {
83 None => true, Some(prefix) => path.starts_with(prefix),
85 }
86 }
87
88 pub fn get_upstream_url(&self, path: &str) -> String {
90 for rule in &self.rules {
92 if rule.enabled && self.path_matches_pattern(&rule.path_pattern, path) {
93 return rule.target_url.clone();
94 }
95 }
96
97 if let Some(base_url) = &self.target_url {
99 base_url.clone()
100 } else {
101 path.to_string()
102 }
103 }
104
105 pub fn strip_prefix(&self, path: &str) -> String {
107 match &self.prefix {
108 Some(prefix) => {
109 if path.starts_with(prefix) {
110 let stripped = path.strip_prefix(prefix).unwrap_or(path);
111 if stripped.starts_with('/') {
113 stripped.to_string()
114 } else {
115 format!("/{}", stripped)
116 }
117 } else {
118 path.to_string()
119 }
120 }
121 None => path.to_string(), }
123 }
124
125 fn path_matches_pattern(&self, pattern: &str, path: &str) -> bool {
127 if let Some(prefix) = pattern.strip_suffix("/*") {
128 path.starts_with(prefix)
129 } else {
130 path == pattern
131 }
132 }
133}
134
135impl Default for ProxyConfig {
136 fn default() -> Self {
137 Self {
138 enabled: false,
139 target_url: None,
140 timeout_seconds: 30,
141 follow_redirects: true,
142 headers: std::collections::HashMap::new(),
143 prefix: None,
144 passthrough_by_default: false,
145 rules: Vec::new(),
146 }
147 }
148}