1use crate::handler::{BoxFuture, Handler};
2use crate::request::Request;
3use crate::response::Response;
4use std::future::Future;
5use std::sync::Arc;
6
7pub type Next = Box<dyn FnOnce(Request) -> BoxFuture<Response> + Send>;
9
10pub type Middleware = Arc<dyn Fn(Request, Next) -> BoxFuture<Response> + Send + Sync>;
12
13#[derive(Clone)]
15pub struct MwEntry {
16 pub name: String,
17 pub mw: Middleware,
18}
19
20pub fn named(name: impl Into<String>, mw: impl IntoMiddleware) -> MwEntry {
22 MwEntry {
23 name: name.into(),
24 mw: mw.into_middleware(),
25 }
26}
27
28pub(crate) fn abbreviate_type_name(full: &str) -> String {
29 let trimmed = full
30 .trim_end_matches(">::{{closure}}")
31 .trim_end_matches("::{{closure}}")
32 .trim_end_matches("{{closure}}");
33 let base = trimmed.rsplit("::").next().unwrap_or(trimmed);
34 if base.is_empty() || base == "{{closure}}" {
35 "closure".into()
36 } else {
37 base.to_string()
38 }
39}
40
41pub trait IntoMiddleware {
42 fn into_middleware(self) -> Middleware;
43}
44
45pub trait IntoMwEntry {
47 fn into_mw_entry(self) -> MwEntry;
48}
49
50impl<F, Fut> IntoMiddleware for F
51where
52 F: Fn(Request, Next) -> Fut + Send + Sync + 'static,
53 Fut: Future<Output = Response> + Send + 'static,
54{
55 fn into_middleware(self) -> Middleware {
56 Arc::new(move |req, next| Box::pin(self(req, next)))
57 }
58}
59
60impl IntoMiddleware for Middleware {
61 fn into_middleware(self) -> Middleware {
62 self
63 }
64}
65
66impl IntoMwEntry for MwEntry {
67 fn into_mw_entry(self) -> MwEntry {
68 self
69 }
70}
71
72impl IntoMwEntry for Middleware {
73 fn into_mw_entry(self) -> MwEntry {
74 MwEntry {
75 name: "mw".into(),
76 mw: self,
77 }
78 }
79}
80
81impl<F, Fut> IntoMwEntry for F
82where
83 F: Fn(Request, Next) -> Fut + Send + Sync + 'static,
84 Fut: Future<Output = Response> + Send + 'static,
85{
86 fn into_mw_entry(self) -> MwEntry {
87 MwEntry {
88 name: abbreviate_type_name(std::any::type_name::<F>()),
89 mw: self.into_middleware(),
90 }
91 }
92}
93
94pub fn with_state<S, F, Fut>(state: S, f: F) -> Middleware
100where
101 S: Send + Sync + 'static,
102 F: Fn(Arc<S>, Request, Next) -> Fut + Send + Sync + 'static,
103 Fut: Future<Output = Response> + Send + 'static,
104{
105 let state = Arc::new(state);
106 Arc::new(move |req, next| Box::pin(f(Arc::clone(&state), req, next)))
107}
108
109pub fn with_leaked<S, F, Fut>(state: S, f: F) -> Middleware
114where
115 S: Send + Sync + 'static,
116 F: Fn(&'static S, Request, Next) -> Fut + Send + Sync + 'static,
117 Fut: Future<Output = Response> + Send + 'static,
118{
119 let state: &'static S = Box::leak(Box::new(state));
120 Arc::new(move |req, next| Box::pin(f(state, req, next)))
121}
122
123pub fn build_chain(middleware: &[Middleware], handler: Handler) -> Handler {
125 let mut next = handler;
126
127 for mw in middleware.iter().rev() {
128 let mw = Arc::clone(mw);
129 let inner = next;
130 next = Arc::new(move |req| {
131 let inner = Arc::clone(&inner);
132 let mw = Arc::clone(&mw);
133 mw(
134 req,
135 Box::new(move |r| {
136 let inner = Arc::clone(&inner);
137 inner(r)
138 }),
139 )
140 });
141 }
142
143 next
144}
145
146pub(crate) fn chain_from_entries(entries: &[MwEntry], handler: Handler) -> Handler {
147 let mws: Vec<Middleware> = entries.iter().map(|e| Arc::clone(&e.mw)).collect();
148 build_chain(&mws, handler)
149}
150
151pub fn logger() -> MwEntry {
153 named("logger", |req: Request, next: Next| async move {
154 let method = req.method.as_str().to_string();
155 let path = req.path.clone();
156 let request_id = req
157 .get::<crate::request_id::RequestId>()
158 .map(|r| r.0.clone())
159 .unwrap_or_default();
160 let start = std::time::Instant::now();
161 let res = next(req).await;
162 let status = res.status_code().as_u16();
163 let latency_ms = start.elapsed().as_millis() as u64;
164 if request_id.is_empty() {
165 tracing::info!(
166 method = %method,
167 path = %path,
168 status,
169 latency_ms,
170 "request"
171 );
172 } else {
173 tracing::info!(
174 request_id = %request_id,
175 method = %method,
176 path = %path,
177 status,
178 latency_ms,
179 "request"
180 );
181 }
182 res
183 })
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use http::Method;
190
191 fn ok_handler() -> Handler {
192 Arc::new(|_req: Request| Box::pin(async { Response::text("ok") }))
193 }
194
195 #[tokio::test]
196 async fn onion_order() {
197 let leaf = ok_handler();
198 let outer = |req: Request, next: Next| async move {
199 let mut res = next(req).await;
200 res = res.header("x-outer", "1");
201 res
202 };
203 let inner = |req: Request, next: Next| async move {
204 let mut res = next(req).await;
205 res = res.header("x-inner", "1");
206 res
207 };
208 let chain = build_chain(
209 &[outer.into_middleware(), inner.into_middleware()],
210 leaf,
211 );
212 let res = chain(Request::new(Method::GET, "/")).await;
213 assert_eq!(res.body_bytes(), Some(b"ok".as_slice()));
214 assert_eq!(res.headers.get("x-outer").map(|v| v.to_str().unwrap()), Some("1"));
215 assert_eq!(res.headers.get("x-inner").map(|v| v.to_str().unwrap()), Some("1"));
216 }
217
218 #[tokio::test]
219 async fn with_state_runs() {
220 let leaf = ok_handler();
221 let mw = with_state(7u32, |n, req, next| async move {
222 assert_eq!(*n, 7);
223 next(req).await
224 });
225 let chain = build_chain(&[mw], leaf);
226 assert_eq!(
227 chain(Request::new(Method::GET, "/")).await.body_bytes(),
228 Some(b"ok".as_slice())
229 );
230 }
231
232 #[test]
233 fn abbreviate_closure() {
234 let n = abbreviate_type_name("foo::bar::{{closure}}");
235 assert_eq!(n, "bar");
236 }
237}