rskit_server/
middleware.rs1use std::sync::Arc;
2
3use axum::Router;
4
5pub const HTTP_INTERCEPTOR_ORDER: [&str; 5] =
10 ["tracing", "logging", "auth", "validation", "metrics"];
11
12pub const HTTP_BASELINE_LAYER_ORDER: [&str; 5] = [
16 "request_id",
17 "cors",
18 "security_headers",
19 "body_limit",
20 "timeout",
21];
22
23pub type RouterTransform = Arc<dyn Fn(Router) -> Router + Send + Sync + 'static>;
26
27#[derive(Clone, Default)]
29pub struct HttpMiddlewareStack {
30 tracing: Vec<RouterTransform>,
31 logging: Vec<RouterTransform>,
32 auth: Vec<RouterTransform>,
33 validation: Vec<RouterTransform>,
34 metrics: Vec<RouterTransform>,
35}
36
37impl HttpMiddlewareStack {
38 #[must_use]
40 pub fn new() -> Self {
41 Self::default()
42 }
43
44 #[must_use]
46 pub fn with_tracing_transform<F>(mut self, transform: F) -> Self
47 where
48 F: Fn(Router) -> Router + Send + Sync + 'static,
49 {
50 self.tracing.push(Arc::new(transform));
51 self
52 }
53
54 #[must_use]
56 pub fn with_logging_transform<F>(mut self, transform: F) -> Self
57 where
58 F: Fn(Router) -> Router + Send + Sync + 'static,
59 {
60 self.logging.push(Arc::new(transform));
61 self
62 }
63
64 #[must_use]
66 pub fn with_auth_transform<F>(mut self, transform: F) -> Self
67 where
68 F: Fn(Router) -> Router + Send + Sync + 'static,
69 {
70 self.auth.push(Arc::new(transform));
71 self
72 }
73
74 #[must_use]
76 pub fn with_validation_transform<F>(mut self, transform: F) -> Self
77 where
78 F: Fn(Router) -> Router + Send + Sync + 'static,
79 {
80 self.validation.push(Arc::new(transform));
81 self
82 }
83
84 #[must_use]
86 pub fn with_metrics_transform<F>(mut self, transform: F) -> Self
87 where
88 F: Fn(Router) -> Router + Send + Sync + 'static,
89 {
90 self.metrics.push(Arc::new(transform));
91 self
92 }
93
94 pub fn apply(&self, router: Router) -> Router {
96 let router = apply_phase(router, &self.metrics);
97 let router = apply_phase(router, &self.validation);
98 let router = apply_phase(router, &self.auth);
99 let router = apply_phase(router, &self.logging);
100 apply_phase(router, &self.tracing)
101 }
102}
103
104fn apply_phase(router: Router, transforms: &[RouterTransform]) -> Router {
105 transforms
106 .iter()
107 .fold(router, |router, transform| transform(router))
108}
109
110#[cfg(test)]
111mod tests {
112 use std::convert::Infallible;
113 use std::sync::Arc;
114
115 use axum::{Router, body::Body, http::Request, response::Response, routing::get};
116 use parking_lot::Mutex;
117 use tower::{Layer, Service, ServiceExt};
118
119 use super::HttpMiddlewareStack;
120
121 #[derive(Clone)]
122 struct RecordLayer {
123 name: &'static str,
124 events: Arc<Mutex<Vec<&'static str>>>,
125 }
126
127 impl RecordLayer {
128 fn new(name: &'static str, events: Arc<Mutex<Vec<&'static str>>>) -> Self {
129 Self { name, events }
130 }
131 }
132
133 impl<S> Layer<S> for RecordLayer {
134 type Service = RecordService<S>;
135
136 fn layer(&self, inner: S) -> Self::Service {
137 RecordService {
138 inner,
139 name: self.name,
140 events: Arc::clone(&self.events),
141 }
142 }
143 }
144
145 #[derive(Clone)]
146 struct RecordService<S> {
147 inner: S,
148 name: &'static str,
149 events: Arc<Mutex<Vec<&'static str>>>,
150 }
151
152 impl<S> Service<Request<Body>> for RecordService<S>
153 where
154 S: Service<Request<Body>, Response = Response, Error = Infallible> + Clone + Send + 'static,
155 S::Future: Send + 'static,
156 {
157 type Response = Response;
158 type Error = Infallible;
159 type Future = futures::future::BoxFuture<'static, Result<Response, Infallible>>;
160
161 fn poll_ready(
162 &mut self,
163 cx: &mut std::task::Context<'_>,
164 ) -> std::task::Poll<Result<(), Self::Error>> {
165 self.inner.poll_ready(cx)
166 }
167
168 fn call(&mut self, req: Request<Body>) -> Self::Future {
169 let mut inner = self.inner.clone();
170 let events = Arc::clone(&self.events);
171 let name = self.name;
172 Box::pin(async move {
173 events.lock().push(name);
174 inner.call(req).await
175 })
176 }
177 }
178
179 #[tokio::test]
180 async fn stack_applies_request_phases_in_locked_order() {
181 let events = Arc::new(Mutex::new(Vec::new()));
182 let app = Router::new().route("/", get(|| async { "ok" }));
183 let app = HttpMiddlewareStack::new()
184 .with_tracing_transform({
185 let events = Arc::clone(&events);
186 move |router| router.layer(RecordLayer::new("tracing", Arc::clone(&events)))
187 })
188 .with_logging_transform({
189 let events = Arc::clone(&events);
190 move |router| router.layer(RecordLayer::new("logging", Arc::clone(&events)))
191 })
192 .with_auth_transform({
193 let events = Arc::clone(&events);
194 move |router| router.layer(RecordLayer::new("auth", Arc::clone(&events)))
195 })
196 .with_validation_transform({
197 let events = Arc::clone(&events);
198 move |router| router.layer(RecordLayer::new("validation", Arc::clone(&events)))
199 })
200 .with_metrics_transform({
201 let events = Arc::clone(&events);
202 move |router| router.layer(RecordLayer::new("metrics", Arc::clone(&events)))
203 })
204 .apply(app);
205
206 let response = app
207 .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
208 .await
209 .unwrap();
210
211 assert_eq!(response.status(), axum::http::StatusCode::OK);
212 assert_eq!(
213 *events.lock(),
214 vec!["tracing", "logging", "auth", "validation", "metrics"]
215 );
216 }
217}