spacegate_kernel/service/
http_gateway.rs1pub mod builder;
2use std::{collections::HashMap, ops::Index, sync::Arc};
3
4use crate::{
5 backend_service::ArcHyperService,
6 extension::{GatewayName, MatchedSgRouter},
7 helper_layers::{
8 map_request::{add_extension::add_extension, MapRequestLayer},
9 reload::Reloader,
10 route::{Router, RouterService},
11 },
12 utils::fold_box_layers::fold_layers,
13 BoxLayer, SgBody,
14};
15
16use hyper::{header::HOST, Request};
17
18use tower_layer::Layer;
19use tracing::{debug, instrument};
20
21use super::http_route::{match_hostname::HostnameTree, match_request::MatchRequest, HttpRoute, HttpRouter};
22
23pub type HttpRouterService = RouterService<HttpRoutedService, GatewayRouter, ArcHyperService>;
30
31#[derive(Debug)]
32pub struct Gateway {
33 pub gateway_name: Arc<str>,
34 pub http_routes: HashMap<String, HttpRoute>,
35 pub http_plugins: Vec<BoxLayer>,
36 pub http_fallback: ArcHyperService,
37 pub http_route_reloader: Reloader<HttpRouterService>,
38 pub ext: hyper::http::Extensions,
39}
40
41impl Gateway {
42 pub fn builder(gateway_name: impl Into<Arc<str>>) -> builder::GatewayBuilder {
46 builder::GatewayBuilder::new(gateway_name)
47 }
48 pub fn as_service(&self) -> ArcHyperService {
49 let gateway_name = GatewayName::new(self.gateway_name.clone());
50 let add_gateway_name_layer = MapRequestLayer::new(add_extension(gateway_name, true));
51 let gateway_plugins = self.http_plugins.iter();
52 let http_routes = self.http_routes.values();
53 let route = create_http_router(http_routes, self.http_fallback.clone());
54 #[cfg(feature = "reload")]
55 let service = {
56 let reloader = self.http_route_reloader.clone();
57 reloader.into_layer().layer(route)
58 };
59 #[cfg(not(feature = "reload"))]
60 let service = route;
61 ArcHyperService::new(add_gateway_name_layer.layer(fold_layers(gateway_plugins, ArcHyperService::new(service))))
62 }
63}
64
65#[derive(Debug, Clone)]
66pub struct HttpRoutedService {
67 services: Arc<[Vec<ArcHyperService>]>,
68}
69
70#[derive(Debug, Clone)]
71pub struct GatewayRouter {
72 pub routers: Arc<[HttpRouter]>,
73 pub hostname_tree: Arc<HostnameTree<Vec<(usize, i16)>>>,
74}
75
76impl Index<(usize, usize)> for HttpRoutedService {
77 type Output = ArcHyperService;
78 fn index(&self, index: (usize, usize)) -> &Self::Output {
79 #[allow(clippy::indexing_slicing)]
80 &self.services.as_ref()[index.0][index.1]
81 }
82}
83impl Router for GatewayRouter {
84 type Index = (usize, usize);
85 #[instrument(skip_all, fields(http.host =? req.headers().get(HOST) ))]
86 fn route(&self, req: &mut Request<SgBody>) -> Option<Self::Index> {
90 let host = req.uri().host().or(req.headers().get(HOST).and_then(|x| x.to_str().ok()))?;
91 let indices = self.hostname_tree.get(host)?;
92 for (route_index, _p) in indices {
93 for (idx1, matches) in self.routers.as_ref().index(*route_index).rules.iter().enumerate() {
94 let index = (*route_index, idx1);
96 if let Some(ref matches) = matches {
97 for m in matches.as_ref() {
98 if m.match_request(req) {
99 req.extensions_mut().insert(MatchedSgRouter(m.clone()));
100 tracing::trace!("matches {m:?} [{route_index},{idx1}:{_p}]");
101 if let Err(e) = m.rewrite(req) {
102 tracing::warn!("rewrite failed: {e:?}");
103 return None;
104 }
105 return Some(index);
106 }
107 }
108 continue;
109 } else {
110 tracing::trace!("matches wildcard [{route_index},{idx1}:{_p}]");
111 return Some(index);
112 }
113 }
114 }
115 tracing::trace!("no rule matched");
116
117 None
118 }
119}
120
121pub fn create_http_router<'a>(routes: impl Iterator<Item = &'a HttpRoute>, fallback: ArcHyperService) -> RouterService<HttpRoutedService, GatewayRouter, ArcHyperService> {
122 let mut services = Vec::new();
123 let mut routers = Vec::new();
124 let mut hostname_tree = HostnameTree::<Vec<_>>::new();
125 for (idx, route) in routes.enumerate() {
126 let priority = route.priority;
127 let idx_with_priority = (idx, priority);
128 let mut rules_services = Vec::with_capacity(route.rules.len());
130 let mut rules_router = Vec::with_capacity(route.rules.len());
131 for rule in route.rules.iter() {
132 let rule_service = fold_layers(route.plugins.iter(), ArcHyperService::new(rule.as_service()));
133 rules_services.push(rule_service);
134 rules_router.push(rule.r#match.clone());
135 }
136 if route.hostnames.is_empty() {
137 if let Some(indices) = hostname_tree.get_mut("*") {
138 indices.push(idx_with_priority)
139 } else {
140 hostname_tree.set("*", vec![idx_with_priority]);
141 }
142 } else {
143 for hostname in route.hostnames.iter() {
144 if let Some(indices) = hostname_tree.get_mut(hostname) {
145 indices.push(idx_with_priority)
146 } else {
147 hostname_tree.set("*", vec![idx_with_priority]);
148 }
149 }
150 }
151 services.push(rules_services);
152 routers.push(HttpRouter {
153 hostnames: route.hostnames.clone().into(),
154 rules: rules_router.into_iter().map(|x| x.map(|v| v.into_iter().map(Arc::new).collect::<Arc<[_]>>())).collect(),
155 ext: route.ext.clone(),
156 });
157 }
158
159 hostname_tree.iter_mut().for_each(|indices| indices.sort_unstable_by_key(|(_, p)| -*p));
162 debug!("hostname_tree: {hostname_tree:?}");
163 RouterService::new(
164 HttpRoutedService { services: services.into() },
165 GatewayRouter {
166 routers: routers.into(),
167 hostname_tree: Arc::new(hostname_tree),
168 },
169 fallback,
170 )
171}