Skip to main content

redirectionio/router/
mod.rs

1pub mod request_matcher;
2mod route;
3mod route_datetime;
4mod route_header;
5mod route_ip;
6mod route_time;
7mod route_weekday;
8mod trace;
9
10use core::cmp::Reverse;
11use std::{
12    collections::{HashMap, HashSet},
13    sync::Arc,
14};
15
16#[cfg(feature = "dot")]
17use dot_graph::{Edge, Graph, Kind, Node};
18pub use request_matcher::{DateTimeMatcher, HostMatcher, IpMatcher, MethodMatcher, PathAndQueryMatcher, SchemeMatcher};
19pub use route::{IntoRoute, Route};
20pub use route_datetime::RouteDateTime;
21pub use route_header::{RouteHeader, RouteHeaderKind};
22pub use route_ip::RouteIp;
23pub use route_time::RouteTime;
24pub use route_weekday::RouteWeekday;
25pub use trace::{RouteTrace, Trace};
26
27#[cfg(feature = "dot")]
28use crate::dot::DotBuilder;
29use crate::{http::Request, router_config::RouterConfig};
30
31#[derive(Debug, Clone)]
32pub struct Router<T> {
33    matcher: SchemeMatcher<T>,
34    pub config: Arc<RouterConfig>,
35    pub routes: HashMap<String, Arc<Route<T>>>,
36}
37
38impl<T> Default for Router<T> {
39    fn default() -> Self {
40        let config = Arc::new(RouterConfig::default());
41
42        Router {
43            matcher: SchemeMatcher::new(config.clone()),
44            config,
45            routes: HashMap::new(),
46        }
47    }
48}
49
50impl<T> Router<T> {
51    pub fn from_config(config: RouterConfig) -> Self {
52        Self::from_arc_config(Arc::new(config))
53    }
54
55    pub fn from_arc_config(config: Arc<RouterConfig>) -> Self {
56        Self {
57            matcher: SchemeMatcher::new(config.clone()),
58            config,
59            routes: HashMap::new(),
60        }
61    }
62
63    pub fn insert_route(&mut self, route: Route<T>) {
64        let arc_route = Arc::new(route);
65
66        self.matcher.insert(arc_route.clone());
67        self.routes.insert(arc_route.id().to_string(), arc_route);
68    }
69
70    pub fn get_route_by_id(&self, id: &str) -> Option<Arc<Route<T>>> {
71        self.routes.get(id).cloned()
72    }
73
74    pub fn remove(&mut self, id: &str) -> Option<Arc<Route<T>>> {
75        if self.routes.contains_key(id) {
76            self.routes.remove(id);
77
78            self.matcher.remove(id)
79        } else {
80            None
81        }
82    }
83
84    pub fn batch_remove(&mut self, ids: &HashSet<String>) {
85        self.routes.retain(|id, _| !ids.contains(id));
86        self.matcher.batch_remove(ids);
87    }
88
89    pub fn rebuild_request(&self, request: &Request) -> Request {
90        Request::rebuild_with_config(self.config.as_ref(), request)
91    }
92
93    pub fn match_request(&self, request: &Request) -> Vec<Arc<Route<T>>> {
94        self.matcher.match_request(request)
95    }
96
97    pub fn len(&self) -> usize {
98        self.routes.len()
99    }
100
101    pub fn is_empty(&self) -> bool {
102        self.routes.is_empty()
103    }
104
105    pub fn routes(&self) -> &HashMap<String, Arc<Route<T>>> {
106        &self.routes
107    }
108
109    pub fn trace_request(&self, request: &Request) -> Vec<Trace<T>> {
110        let request_rebuild = Request::rebuild_with_config(self.config.as_ref(), request);
111
112        self.matcher.trace(&request_rebuild)
113    }
114
115    pub fn get_route(&self, request: &Request) -> Option<Arc<Route<T>>> {
116        let mut routes = self.match_request(request);
117
118        if routes.is_empty() {
119            return None;
120        }
121
122        routes.sort_by_key(|b| Reverse(b.priority()));
123        routes.first().cloned()
124    }
125
126    pub fn get_trace(&self, request: &Request) -> RouteTrace<T> {
127        let traces = self.trace_request(request);
128        let mut routes_traces = Trace::get_routes_from_traces(&traces);
129        let mut routes = Vec::new();
130
131        for route in &routes_traces {
132            routes.push(route.clone());
133        }
134
135        routes_traces.sort_by_key(|b| Reverse(b.priority()));
136
137        let final_route = routes_traces.first().cloned();
138
139        RouteTrace::new(traces, routes, final_route)
140    }
141
142    pub fn cache(&mut self, limit: Option<u64>) {
143        let mut prev_cache_limit = match limit {
144            Some(limit) => limit as i64,
145            None => (self.routes.len() / 10).clamp(100, 10_000) as i64,
146        };
147
148        let mut level = 0;
149        let mut retry = 0;
150
151        while prev_cache_limit > 0 {
152            let next_cache_limit = self.matcher.cache(prev_cache_limit as u64, level) as i64;
153
154            if next_cache_limit == prev_cache_limit {
155                retry += 1;
156
157                if retry > 5 {
158                    break;
159                }
160            }
161
162            level += 1;
163            prev_cache_limit = next_cache_limit;
164        }
165
166        if prev_cache_limit > 0 {
167            for route in self.routes.values() {
168                prev_cache_limit -= route.compile() as i64;
169
170                if prev_cache_limit <= 0 {
171                    break;
172                }
173            }
174        }
175    }
176
177    #[cfg(feature = "dot")]
178    pub fn graph(&self) -> Graph {
179        let mut graph = Graph::new("router", Kind::Digraph);
180        graph.add_node(Node::new("router"));
181
182        let mut id = 0;
183
184        if let Some(key) = self.matcher.graph(&mut id, &mut graph) {
185            graph.add_edge(Edge::new("router", &key, ""));
186        }
187
188        graph
189    }
190}
191
192impl<T> Router<T>
193where
194    T: IntoRoute<T>,
195{
196    pub fn insert(&mut self, item: T) {
197        self.insert_route(item.into_route(self.config.as_ref()));
198    }
199
200    pub fn apply_change_set(&mut self, added: Vec<T>, updated: Vec<T>, mut removed: HashSet<String>) {
201        let updated_route = updated
202            .into_iter()
203            .map(|item| item.into_route(self.config.as_ref()))
204            .collect::<Vec<Route<T>>>();
205
206        removed.extend(updated_route.iter().map(|item| item.id().to_string()));
207        self.batch_remove(&removed);
208
209        for item in updated_route {
210            self.insert_route(item);
211        }
212
213        for item in added {
214            self.insert(item);
215        }
216    }
217}