redirectionio/router/
mod.rs1pub 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}