tako_rs_core/router/
dispatch.rs1use std::sync::Arc;
5use std::sync::atomic::Ordering;
6
7use http::Method;
8use http::StatusCode;
9use smallvec::SmallVec;
10
11use super::Router;
12use crate::body::TakoBody;
13use crate::extractors::params::PathParams;
14use crate::handler::BoxHandler;
15use crate::middleware::Next;
16use crate::route::Route;
17#[cfg(feature = "signals")]
18use crate::signals::Signal;
19#[cfg(feature = "signals")]
20use crate::signals::SignalArbiter;
21#[cfg(feature = "signals")]
22use crate::signals::ids;
23use crate::types::Request;
24use crate::types::Response;
25
26#[inline]
33pub(crate) fn empty_status_response(status: StatusCode) -> Response {
34 let mut resp = http::Response::new(TakoBody::empty());
35 *resp.status_mut() = status;
36 resp
37}
38
39impl Router {
40 async fn run_with_global_middlewares_for_endpoint(
45 &self,
46 req: Request,
47 endpoint: BoxHandler,
48 ) -> Response {
49 if self.has_global_middleware.load(Ordering::Acquire) {
50 Next {
51 global_middlewares: self.middlewares.load_full(),
52 route_middlewares: Arc::default(),
53 index: 0,
54 endpoint,
55 }
56 .run(req)
57 .await
58 } else {
59 endpoint.call(req).await
60 }
61 }
62
63 #[inline]
65 pub async fn dispatch(&self, mut req: Request) -> Response {
66 if self.has_router_state.load(Ordering::Acquire) {
70 req.extensions_mut().insert(Arc::clone(&self.router_state));
71 }
72
73 #[cfg(feature = "signals")]
77 let (req_method_str, req_path_str) = (req.method().to_string(), req.uri().path().to_string());
78 #[cfg(feature = "signals")]
79 {
80 SignalArbiter::emit_app(
81 Signal::with_capacity(ids::REQUEST_STARTED, 2)
82 .meta("method", req_method_str.clone())
83 .meta("path", req_path_str.clone()),
84 )
85 .await;
86 }
87
88 let route_match = {
92 if let Some(method_router) = self.inner.get(req.method())
93 && let Ok(matched) = method_router.at(req.uri().path())
94 {
95 let route = Arc::clone(matched.value);
96 let mut it = matched.params.iter();
97 let first = it.next();
98 let params = first.map(|(fk, fv)| {
99 let mut p = SmallVec::<[(String, String); 4]>::new();
100 p.push((fk.to_string(), fv.to_string()));
101 for (k, v) in it {
102 p.push((k.to_string(), v.to_string()));
103 }
104 PathParams(p)
105 });
106 Some((route, params))
107 } else {
108 None
109 }
110 };
111
112 let response = if let Some((route, params)) = route_match {
114 if let Some(res) = Self::enforce_protocol_guard(&route, &req) {
119 res
120 } else {
121 #[cfg(feature = "signals")]
122 let route_signals = route.signal_arbiter();
123
124 #[cfg(feature = "plugins")]
126 route.setup_plugins_once();
127
128 if let Some(mode) = route.get_simd_json_mode() {
130 req.extensions_mut().insert(mode);
131 }
132
133 if let Some(params) = params {
134 req.extensions_mut().insert(params);
135 }
136
137 req
141 .extensions_mut()
142 .insert(crate::router_state::MatchedPath(route.path.clone()));
143
144 let effective_timeout = route.get_timeout().or(self.timeout);
146
147 let needs_chain = self.has_global_middleware.load(Ordering::Acquire)
149 || route.has_middleware.load(Ordering::Acquire);
150
151 #[cfg(feature = "signals")]
152 {
153 let method_str = req_method_str.clone();
158 let path_str = req_path_str.clone();
159 let route_template = route.path.clone();
160
161 route_signals
162 .emit(
163 Signal::with_capacity(ids::ROUTE_REQUEST_STARTED, 3)
164 .meta("method", method_str.clone())
165 .meta("path", path_str.clone())
166 .meta("route", route_template.clone()),
167 )
168 .await;
169
170 let response = if !needs_chain && effective_timeout.is_none() {
171 route.handler.call(req).await
172 } else {
173 let next = Next {
174 global_middlewares: self.middlewares.load_full(),
175 route_middlewares: route.middlewares.load_full(),
176 index: 0,
177 endpoint: route.handler.clone(),
178 };
179 self.run_with_timeout(req, next, effective_timeout).await
180 };
181
182 route_signals
183 .emit(
184 Signal::with_capacity(ids::ROUTE_REQUEST_COMPLETED, 4)
185 .meta("method", method_str)
186 .meta("path", path_str)
187 .meta("route", route_template)
188 .meta("status", response.status().as_u16().to_string()),
189 )
190 .await;
191
192 response
193 }
194
195 #[cfg(not(feature = "signals"))]
196 {
197 if !needs_chain && effective_timeout.is_none() {
198 route.handler.call(req).await
199 } else {
200 let next = Next {
201 global_middlewares: self.middlewares.load_full(),
202 route_middlewares: route.middlewares.load_full(),
203 index: 0,
204 endpoint: route.handler.clone(),
205 };
206 self.run_with_timeout(req, next, effective_timeout).await
207 }
208 }
209 }
210 } else {
211 let tsr_path = {
214 let p = req.uri().path();
215 if p.ends_with('/') {
216 p.trim_end_matches('/').to_string()
217 } else {
218 format!("{p}/")
219 }
220 };
221
222 if let Some(method_router) = self.inner.get(req.method())
223 && let Ok(matched) = method_router.at(&tsr_path)
224 && matched.value.tsr
225 {
226 let handler = move |_req: Request| {
227 let tsr_path = tsr_path.clone();
228 async move {
229 match http::HeaderValue::from_str(&tsr_path) {
235 Ok(loc) => {
236 let mut resp = empty_status_response(StatusCode::TEMPORARY_REDIRECT);
237 resp.headers_mut().insert(http::header::LOCATION, loc);
238 resp
239 }
240 Err(_) => empty_status_response(StatusCode::TEMPORARY_REDIRECT),
241 }
242 }
243 };
244
245 self
246 .run_with_global_middlewares_for_endpoint(req, BoxHandler::new::<_, (Request,)>(handler))
247 .await
248 } else {
249 let allowed = self.collect_allowed_methods(req.uri().path());
254 if !allowed.is_empty() {
255 let allow_value = join_methods(&allowed);
256 let handler = move |_req: Request| {
257 let allow_value = allow_value.clone();
258 async move {
259 let mut resp = empty_status_response(StatusCode::METHOD_NOT_ALLOWED);
265 if let Ok(v) = http::HeaderValue::from_str(&allow_value) {
266 resp.headers_mut().insert(http::header::ALLOW, v);
267 }
268 resp
269 }
270 };
271 self
272 .run_with_global_middlewares_for_endpoint(
273 req,
274 BoxHandler::new::<_, (Request,)>(handler),
275 )
276 .await
277 } else if let Some(handler) = &self.fallback {
278 self
279 .run_with_global_middlewares_for_endpoint(req, handler.clone())
280 .await
281 } else {
282 let handler = |_req: Request| async { empty_status_response(StatusCode::NOT_FOUND) };
283
284 self
285 .run_with_global_middlewares_for_endpoint(
286 req,
287 BoxHandler::new::<_, (Request,)>(handler),
288 )
289 .await
290 }
291 }
292 };
293
294 let response = self.maybe_apply_error_handler(response);
295
296 #[cfg(feature = "signals")]
297 {
298 SignalArbiter::emit_app(
299 Signal::with_capacity(ids::REQUEST_COMPLETED, 3)
300 .meta("method", req_method_str)
301 .meta("path", req_path_str)
302 .meta("status", response.status().as_u16().to_string()),
303 )
304 .await;
305 }
306
307 response
308 }
309
310 fn maybe_apply_error_handler(&self, response: Response) -> Response {
314 let status = response.status();
315 if status.is_server_error() {
316 if let Some(handler) = &self.error_handler {
317 return handler(response);
318 }
319 } else if status.is_client_error()
320 && let Some(handler) = &self.client_error_handler
321 {
322 return handler(response);
323 }
324 response
325 }
326
327 fn collect_allowed_methods(&self, path: &str) -> SmallVec<[Method; 4]> {
332 let mut allowed = SmallVec::<[Method; 4]>::new();
333 for (method, m) in self.inner.iter() {
334 if m.at(path).is_ok() {
335 allowed.push(method);
336 }
337 }
338 allowed
339 }
340
341 fn enforce_protocol_guard(route: &Route, req: &Request) -> Option<Response> {
345 if let Some(guard) = route.protocol_guard()
346 && guard != req.version()
347 {
348 return Some(empty_status_response(
349 StatusCode::HTTP_VERSION_NOT_SUPPORTED,
350 ));
351 }
352 None
353 }
354}
355
356fn join_methods(methods: &[Method]) -> String {
358 let mut out = String::with_capacity(methods.len() * 8);
359 for (i, m) in methods.iter().enumerate() {
360 if i > 0 {
361 out.push_str(", ");
362 }
363 out.push_str(m.as_str());
364 }
365 out
366}