1pub mod predicates;
20
21#[cfg(test)]
22#[path = "../../tests/unit/router/tests.rs"]
23mod tests;
24
25pub use predicates::*;
26
27use async_trait::async_trait;
28use once_cell::sync::Lazy;
29use serde::{Deserialize, Serialize};
30use std::collections::HashMap;
31use std::sync::Arc;
32use std::sync::RwLock as StdRwLock;
33use tokio::sync::RwLock;
34
35use crate::config::Config;
36use crate::core::{ProxyError, ProxyRequest, Route};
37use crate::{FilterFactory, debug_fmt, error_fmt, trace_fmt, warn_fmt};
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct RouteConfig {
42 pub id: String,
44 pub target: String,
46 #[serde(default)]
48 pub filters: Vec<FilterConfig>,
49 #[serde(default = "default_priority")]
51 pub priority: i32,
52 #[serde(default)]
54 pub predicates: Vec<PredicateConfig>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct FilterConfig {
60 #[serde(rename = "type")]
62 pub type_: String,
63 pub config: serde_json::Value,
65}
66
67fn default_priority() -> i32 {
68 0
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct PredicateConfig {
74 pub type_: String,
76 pub config: serde_json::Value,
78}
79
80#[async_trait]
82pub trait Predicate: Send + Sync + std::fmt::Debug {
83 async fn matches(&self, request: &ProxyRequest) -> bool;
85
86 fn predicate_type(&self) -> &str;
88}
89
90pub type PredicateConstructor = fn(serde_json::Value) -> Result<Arc<dyn Predicate>, ProxyError>;
92
93static PREDICATE_REGISTRY: Lazy<StdRwLock<HashMap<String, PredicateConstructor>>> =
96 Lazy::new(|| StdRwLock::new(HashMap::new()));
97
98pub fn register_predicate(name: &str, ctor: PredicateConstructor) {
100 PREDICATE_REGISTRY
101 .write()
102 .expect("PREDICATE_REGISTRY poisoned")
103 .insert(name.to_string(), ctor);
104}
105
106fn get_registered_predicate(name: &str) -> Option<PredicateConstructor> {
108 PREDICATE_REGISTRY
109 .read()
110 .expect("PREDICATE_REGISTRY poisoned")
111 .get(name)
112 .copied()
113}
114
115#[derive(Debug)]
117pub struct PredicateRouter {
118 routes: RwLock<HashMap<String, RouteWithPredicates>>,
120 sorted_routes: RwLock<Vec<RouteWithPredicates>>,
122 config: Arc<Config>,
124}
125
126#[derive(Debug, Clone)]
128struct RouteWithPredicates {
129 route: Route,
131 predicates: Vec<Arc<dyn Predicate>>,
133 priority: i32,
135}
136
137impl PredicateRouter {
138 pub async fn new(config: Arc<Config>) -> Result<Self, ProxyError> {
140 let router = Self {
141 routes: RwLock::new(HashMap::new()),
142 sorted_routes: RwLock::new(Vec::new()),
143 config,
144 };
145
146 router.load_routes_from_config().await?;
148
149 Ok(router)
150 }
151
152 async fn load_routes_from_config(&self) -> Result<(), ProxyError> {
154 let route_configs: Option<Vec<RouteConfig>> = self.config.get("routes")?;
156
157 if let Some(route_configs) = route_configs {
158 for route_config in route_configs {
160 let mut predicates = Vec::new();
162 for predicate_config in &route_config.predicates {
163 let predicate = PredicateFactory::create_predicate(
164 &predicate_config.type_,
165 predicate_config.config.clone(),
166 )?;
167 predicates.push(predicate);
168 }
169
170 let mut filters = Vec::new();
172 for filter_config in &route_config.filters {
173 let filter = FilterFactory::create_filter(
174 &filter_config.type_,
175 filter_config.config.clone(),
176 )?;
177 filters.push(filter);
178 }
179
180 let path_pattern = route_config
182 .predicates
183 .iter()
184 .find(|p| p.type_ == "path")
185 .map(|p| {
186 p.config
187 .get("pattern")
188 .and_then(|v| v.as_str())
189 .unwrap_or("/*")
190 })
191 .unwrap_or("/*")
192 .to_string();
193
194 let route = Route {
195 id: route_config.id.clone(),
196 target_base_url: route_config.target.clone(),
197 path_pattern,
198 filters: if filters.is_empty() {
199 None
200 } else {
201 Some(filters)
202 },
203 };
204
205 self.add_route_with_predicates(route, predicates, route_config.priority)
207 .await?;
208 }
209 }
210
211 Ok(())
212 }
213
214 async fn add_route_with_predicates(
216 &self,
217 route: Route,
218 predicates: Vec<Arc<dyn Predicate>>,
219 priority: i32,
220 ) -> Result<(), ProxyError> {
221 let route_with_predicates = RouteWithPredicates {
222 route: route.clone(),
223 predicates,
224 priority,
225 };
226
227 {
229 let mut routes = self.routes.write().await;
230 routes.insert(route.id.clone(), route_with_predicates.clone());
231 }
232
233 {
235 let mut sorted_routes = self.sorted_routes.write().await;
236 sorted_routes.push(route_with_predicates);
237
238 sorted_routes.sort_by(|a, b| b.priority.cmp(&a.priority));
240 }
241
242 Ok(())
243 }
244}
245
246#[async_trait]
247impl crate::core::Router for PredicateRouter {
248 async fn route(&self, request: &ProxyRequest) -> Result<Route, ProxyError> {
249 let sorted_routes = self.sorted_routes.read().await;
251 trace_fmt!(
252 "Router",
253 "Routing request {} {} against {} routes",
254 request.method,
255 request.path,
256 sorted_routes.len()
257 );
258
259 for route_with_predicates in sorted_routes.iter() {
260 let mut all_match = true;
262 let route_id = &route_with_predicates.route.id;
263
264 trace_fmt!(
265 "Router",
266 "Checking route '{}' with {} predicates",
267 route_id,
268 route_with_predicates.predicates.len()
269 );
270
271 for predicate in &route_with_predicates.predicates {
272 let predicate_type = predicate.predicate_type();
273 let matches = predicate.matches(request).await;
274
275 trace_fmt!(
276 "Router",
277 " Predicate '{}' for route '{}': {}",
278 predicate_type,
279 route_id,
280 if matches { "match" } else { "no match" }
281 );
282
283 if !matches {
284 all_match = false;
285 break;
286 }
287 }
288
289 if all_match {
291 debug_fmt!(
292 "Router",
293 "Route '{}' matched request {} {}",
294 route_id,
295 request.method,
296 request.path
297 );
298 return Ok(route_with_predicates.route.clone());
299 }
300 }
301
302 let err = ProxyError::RoutingError(format!(
304 "No route matched the request: {} {}",
305 request.method, request.path
306 ));
307 warn_fmt!("Router", "{}", err);
308 Err(err)
309 }
310
311 async fn get_routes(&self) -> Vec<Route> {
312 let routes = self.routes.read().await;
313 routes.values().map(|r| r.route.clone()).collect()
314 }
315
316 async fn add_route(&self, route: Route) -> Result<(), ProxyError> {
317 self.add_route_with_predicates(route, Vec::new(), 0).await
320 }
321
322 async fn remove_route(&self, route_id: &str) -> Result<(), ProxyError> {
323 {
325 let mut routes = self.routes.write().await;
326 if routes.remove(route_id).is_none() {
327 return Err(ProxyError::RoutingError(format!(
328 "Route not found: {route_id}"
329 )));
330 }
331 }
332
333 {
335 let mut sorted_routes = self.sorted_routes.write().await;
336 sorted_routes.retain(|r| r.route.id != route_id);
337 }
338
339 Ok(())
340 }
341}
342
343#[derive(Debug)]
345pub struct PredicateFactory;
346
347impl PredicateFactory {
348 pub fn create_predicate(
350 predicate_type: &str,
351 config: serde_json::Value,
352 ) -> Result<Arc<dyn Predicate>, ProxyError> {
353 debug_fmt!(
354 "Router",
355 "Creating predicate of type '{}' with config: {}",
356 predicate_type,
357 config
358 );
359
360 if let Some(ctor) = get_registered_predicate(predicate_type) {
362 return ctor(config);
363 }
364
365 match predicate_type {
366 "path" => {
367 let path_config: PathPredicateConfig =
368 serde_json::from_value(config).map_err(|e| {
369 let err =
370 ProxyError::RoutingError(format!("Invalid path predicate config: {e}"));
371 error_fmt!("Router", "{}", err);
372 err
373 })?;
374
375 match PathPredicate::new(path_config) {
376 Ok(predicate) => Ok(Arc::new(predicate)),
377 Err(error) => Err(error),
378 }
379 }
380 "method" => {
381 let method_config: MethodPredicateConfig =
382 serde_json::from_value(config).map_err(|e| {
383 let err = ProxyError::RoutingError(format!(
384 "Invalid method predicate config: {e}"
385 ));
386 error_fmt!("Router", "{}", err);
387 err
388 })?;
389 Ok(Arc::new(MethodPredicate::new(method_config)))
390 }
391 "header" => {
392 let header_config: HeaderPredicateConfig =
393 serde_json::from_value(config).map_err(|e| {
394 let err = ProxyError::RoutingError(format!(
395 "Invalid header predicate config: {e}"
396 ));
397 error_fmt!("Router", "{}", err);
398 err
399 })?;
400 Ok(Arc::new(HeaderPredicate::new(header_config)))
401 }
402 "query" => {
403 let query_config: QueryPredicateConfig =
404 serde_json::from_value(config).map_err(|e| {
405 let err = ProxyError::RoutingError(format!(
406 "Invalid query predicate config: {e}"
407 ));
408 error_fmt!("Router", "{}", err);
409 err
410 })?;
411 Ok(Arc::new(QueryPredicate::new(query_config)))
412 }
413 _ => {
414 let err =
415 ProxyError::RoutingError(format!("Unknown predicate type: {predicate_type}"));
416 error_fmt!("Router", "{}", err);
417 Err(err)
418 }
419 }
420 }
421}