1pub mod errors;
22
23use futures::FutureExt;
24use std::collections::HashMap;
25use std::net::SocketAddr;
26use std::sync::{
27 Arc, OnceLock,
28 atomic::{AtomicBool, AtomicUsize, Ordering},
29};
30
31use crate::doc::{DocumentableDTO, DocumentationRegistrant};
32use crate::logging::CorrelationContext;
33use crate::middleware::rate::{RateLimiter, create_from_env};
34use crate::response::{ErrorResult, ResponseType, ServiceResult, TypedServiceResult};
35use crate::validation::Validate;
36use http::{HeaderMap, Method, Request, Response};
37use http_body_util::BodyExt;
38use hyper::body::Incoming;
39use hyper::server::conn::http1;
40use hyper::service::service_fn;
41use hyper_util::rt::TokioIo;
42use schemars::generate::SchemaSettings;
43use serde_json::Value;
44use tokio::net::TcpListener;
45use tracing::{info, warn};
46
47#[derive(Clone)]
48struct Holder {
49 path: String,
50 limit: u32,
51}
52
53#[derive(Clone, Debug)]
56pub struct DtoEntity {
57 pub(crate) schema: Value,
58 pub(crate) example: Value,
59 pub(crate) name: &'static str,
60}
61
62#[derive(Clone, Debug, PartialEq, Eq)]
65pub enum FileParameterType {
66 Image,
68 Document,
70 Spreadsheet,
72 Video,
74 Audio,
76 Archive,
78 Any,
80}
81
82impl FileParameterType {
83 pub fn allowed_extensions_example(&self) -> &'static str {
85 match self {
86 Self::Image => ".png, .jpg, .jpeg, .gif, .webp",
87 Self::Document => ".pdf, .doc, .docx, .txt",
88 Self::Spreadsheet => ".xls, .xlsx, .csv",
89 Self::Video => ".mp4, .mov, .avi",
90 Self::Audio => ".mp3, .wav, .aac",
91 Self::Archive => ".zip, .tar, .gz",
92 Self::Any => "Any extension",
93 }
94 }
95}
96
97#[derive(Clone, Debug)]
100pub struct FileParameter {
101 pub name: String,
103 pub description: String,
105 pub file_type: FileParameterType,
107 pub count: Option<u32>,
109 pub limit: Option<u32>,
111}
112
113#[derive(Debug, Clone)]
119pub struct RouteDescription {
120 pub group: String,
122 pub name: String,
124 pub description: String,
126 pub request_body: Option<DtoEntity>,
128 pub response_examples: HashMap<u16, DtoEntity>,
130 pub headers: HashMap<String, String>,
132 pub path_parameters: HashMap<String, String>,
134 pub path_parameter_defaults: HashMap<String, String>,
136 pub query_parameters: HashMap<String, String>,
138 pub query_parameter_defaults: HashMap<String, String>,
140 pub file_parameters: HashMap<String, FileParameter>,
142 pub rate_limit: Option<u32>,
144 pub authentication_required: bool,
146 pub authentication_comment: Option<String>,
148 pub summary: String,
150 pub tags: Vec<String>,
152 pub is_deprecated: bool,
154}
155
156impl RouteDescription {
157 pub fn new(summary: impl Into<String>) -> Self {
159 let s = summary.into();
160 Self {
161 group: "Default".to_string(),
162 name: s.clone(),
163 description: String::new(),
164 request_body: None,
165 response_examples: Default::default(),
166 headers: Default::default(),
167 path_parameters: Default::default(),
168 path_parameter_defaults: Default::default(),
169 query_parameters: Default::default(),
170 query_parameter_defaults: Default::default(),
171 file_parameters: Default::default(),
172 rate_limit: None,
173 authentication_required: false,
174 authentication_comment: None,
175 summary: s.clone(),
176 tags: vec!["Default".to_string()],
177 is_deprecated: false,
178 }
179 }
180 pub fn group(mut self, group: impl Into<String>) -> Self {
182 let g = group.into();
183 self.group = g.clone();
184 self.tags = vec![g];
185 self
186 }
187 pub fn name(mut self, name: impl Into<String>) -> Self {
189 let n = name.into();
190 self.name = n.clone();
191 self.summary = n;
192 self
193 }
194 pub fn description(mut self, desc: impl Into<String>) -> Self {
196 self.description = desc.into();
197 self
198 }
199 pub fn with_rate_limit(mut self, limit: u32) -> Self {
201 self.rate_limit = Some(limit);
202 self
203 }
204 pub fn rate_limit(mut self, limit: u32) -> Self {
206 self.rate_limit = Some(limit);
207 self
208 }
209 pub fn with_body<T>(mut self) -> Self
212 where
213 T: DocumentableDTO + Validate,
214 {
215 let settings = SchemaSettings::openapi3();
216 let generator = settings.into_generator();
217 let schema = generator.into_root_schema_for::<T>();
218 let example = T::make_example();
219 self.request_body = Some(DtoEntity {
220 schema: schema.to_value(),
221 example: example.unwrap(),
222 name: std::any::type_name::<T>(),
223 });
224 self
225 }
226 pub fn authentication(mut self, required: bool) -> Self {
228 self.authentication_required = required;
229 self
230 }
231
232 pub fn response<T>(mut self, code: u16) -> Self
234 where
235 T: DocumentableDTO,
236 {
237 let settings = SchemaSettings::openapi3();
238 let generator = settings.into_generator();
239 let schema = generator.into_root_schema_for::<T>();
240 let example = T::make_example();
241 let response = DtoEntity {
242 schema: schema.to_value(),
243 example: example.unwrap(),
244 name: std::any::type_name::<T>(),
245 };
246 self.response_examples.insert(code, response);
247 self
248 }
249 pub fn authentication_required(mut self, required: bool) -> Self {
251 self.authentication_required = required;
252 self
253 }
254 pub fn authentication_comment(mut self, comment: impl Into<String>) -> Self {
256 self.authentication_comment = Some(comment.into());
257 self
258 }
259 pub fn header(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
261 self.headers.insert(k.into(), v.into());
262 self
263 }
264 pub fn path_param(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
266 self.path_parameters.insert(k.into(), v.into());
267 self
268 }
269 pub fn path_param_with_default(
271 mut self,
272 k: impl Into<String>,
273 v: impl Into<String>,
274 default: impl Into<String>,
275 ) -> Self {
276 let k = k.into();
277 self.path_parameters.insert(k.clone(), v.into());
278 self.path_parameter_defaults.insert(k, default.into());
279 self
280 }
281 pub fn query_param(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
283 self.query_parameters.insert(k.into(), v.into());
284 self
285 }
286 pub fn query_param_with_default(
288 mut self,
289 k: impl Into<String>,
290 v: impl Into<String>,
291 default: impl Into<String>,
292 ) -> Self {
293 let k = k.into();
294 self.query_parameters.insert(k.clone(), v.into());
295 self.query_parameter_defaults.insert(k, default.into());
296 self
297 }
298 pub fn file_param(
300 mut self,
301 name: impl Into<String>,
302 description: impl Into<String>,
303 file_type: FileParameterType,
304 ) -> Self {
305 let name = name.into();
306 self.file_parameters.insert(
307 name.clone(),
308 FileParameter {
309 name,
310 description: description.into(),
311 file_type,
312 count: None,
313 limit: None,
314 },
315 );
316 self
317 }
318 pub fn file_param_with_limit(
321 mut self,
322 name: impl Into<String>,
323 description: impl Into<String>,
324 file_type: FileParameterType,
325 limit: u32,
326 count: u32,
327 ) -> Self {
328 let name = name.into();
329 assert!(limit >= 1, "File parameter limit must be greater than 0");
330 assert!(count >= 1, "File parameter count must be greater than 0");
331 self.file_parameters.insert(
332 name.clone(),
333 FileParameter {
334 name,
335 description: description.into(),
336 file_type,
337 count: Some(count),
338 limit: Some(limit),
339 },
340 );
341 self
342 }
343 pub fn deprecated(mut self, deprecated: bool) -> Self {
345 self.is_deprecated = deprecated;
346 self
347 }
348 pub fn tag(mut self, tag: impl Into<String>) -> Self {
350 let t = tag.into();
351 if !self.tags.contains(&t) {
352 self.tags.push(t);
353 }
354 self
355 }
356 pub fn effective_rate_limit(&self, global: Option<u32>) -> Option<u32> {
358 self.rate_limit.or(global)
359 }
360}
361
362impl Default for RouteDescription {
363 fn default() -> Self {
364 Self::new("No description")
365 }
366}
367
368static TOTAL_COUNT: AtomicUsize = AtomicUsize::new(0);
371static LIMITER: OnceLock<Arc<dyn RateLimiter>> = OnceLock::new();
372static MOUNTED_HANDLERS: AtomicBool = AtomicBool::new(false);
373
374pub type Middleware = Arc<
378 dyn for<'a> Fn(
379 &'a mut CorrelationContext,
380 ) -> futures::future::BoxFuture<'a, Result<(), ErrorResult>>
381 + Send
382 + Sync,
383>;
384
385pub type BoxHandler = Arc<
388 dyn Fn(
389 CorrelationContext,
390 HeaderMap,
391 Method,
392 String,
393 Vec<u8>,
394 ) -> futures::future::BoxFuture<'static, Response<String>>
395 + Send
396 + Sync,
397>;
398
399pub(crate) struct RouteEntry {
401 method: Method,
402 path: String,
403 handler: BoxHandler,
404 middlewares: Vec<Middleware>,
405}
406
407#[derive(Default)]
408struct TrieNode {
409 static_children: HashMap<String, TrieNode>,
410 param_child: Option<(String, Box<TrieNode>)>, wildcard_routes: HashMap<Method, RouteEntry>, exact_routes: HashMap<Method, RouteEntry>, }
414
415pub struct Router {
419 root: TrieNode,
420 global_middlewares: Vec<Middleware>,
421
422 test_client_handler: Option<BoxHandler>,
426}
427
428impl Router {
429 pub fn new() -> Self {
431 Self {
432 root: TrieNode::default(),
433 global_middlewares: Vec::new(),
434 test_client_handler: None,
435 }
436 }
437
438 pub(crate) fn insert(&mut self, route: RouteEntry) {
477 let segments: Vec<String> = route
478 .path
479 .trim_matches('/')
480 .split('/')
481 .filter(|s| !s.is_empty())
482 .map(String::from)
483 .collect();
484 Self::insert_at(&mut self.root, &segments, route);
485 }
486
487 fn insert_at(node: &mut TrieNode, segments: &[String], route: RouteEntry) {
488 match segments.split_first() {
489 None => {
490 node.exact_routes.insert(route.method.clone(), route);
491 }
492 Some((seg, _rest)) if seg == "*" => {
493 node.wildcard_routes.insert(route.method.clone(), route);
494 }
495 Some((seg, rest)) if seg.starts_with(':') => {
496 let name = seg[1..].to_string();
497 let (existing_name, child) = node
498 .param_child
499 .get_or_insert_with(|| (name.clone(), Box::new(TrieNode::default())));
500 debug_assert_eq!(
501 existing_name, &name,
502 "conflicting param names at same position: {existing_name} vs {name}"
503 );
504 Self::insert_at(child, rest, route);
505 }
506 Some((seg, rest)) => {
507 let child = node.static_children.entry(seg.clone()).or_default();
508 Self::insert_at(child, rest, route);
509 }
510 }
511 }
512
513 pub(crate) fn resolve(
556 &self,
557 method: &Method,
558 path: &str,
559 ) -> Option<(&RouteEntry, HashMap<String, String>)> {
560 let normal_path = normalize_path(path);
561 let segments: Vec<&str> = normal_path
562 .trim_matches('/')
563 .split('/')
564 .filter(|s| !s.is_empty())
565 .collect();
566
567 let mut params = Vec::new();
568 let route = Self::find(&self.root, &segments, method, &mut params)?;
569 Some((route, params.into_iter().collect()))
570 }
571
572 pub fn set_test_client_handler(&mut self, handler: BoxHandler) {
574 self.test_client_handler = Some(handler);
575 }
576
577 fn ensure_global_middleware(&mut self) {
578 if MOUNTED_HANDLERS
579 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
580 .is_ok()
581 {
582 self.global_middlewares.push(Arc::new(|ctx| {
583 async move {
584 if ctx.request_id().is_empty() {
585 let rid = hex::encode(rand::random::<[u8; 8]>());
586 ctx.set_request_id(&rid);
587 }
588 Ok(())
589 }
590 .boxed()
591 }));
592 self.global_middlewares.push(Arc::new(|ctx| {
593 async move {
594 let headers = ctx.headers();
595 if let Some(v) = headers
596 .get("x-correlation-id")
597 .and_then(|h| h.to_str().ok())
598 {
599 if !v.is_empty() {
600 ctx.set_correlation_id(v);
601 }
602 }
603 if let Some(v) = headers
604 .get("x-correlation-flow")
605 .and_then(|h| h.to_str().ok())
606 {
607 if let Ok(flow) = v.parse::<crate::logging::CorrelationFlow>() {
608 ctx.set_flow(flow);
609 }
610 }
611 Ok(())
612 }
613 .boxed()
614 }));
615 }
616 }
617
618 pub fn mount<T, F, Fut>(
633 &mut self,
634 base_path: &str,
635 version: u32,
636 path: &str,
637 has_body: bool,
638 method: Method,
639 description: RouteDescription,
640 handler: F,
641 middlewares: Vec<Middleware>,
642 ) where
643 T: serde::Serialize + Send + Sync + 'static,
644 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
645 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
646 {
647 self.ensure_global_middleware();
648 let full_path = calculate_full_path(base_path, version, path);
649 let meta_path = Holder {
650 path: full_path.to_string(),
651 limit: description.rate_limit.unwrap_or(0),
652 };
653
654 if let Ok(mut reg) = DocumentationRegistrant::global().write() {
655 reg.register_route(
656 &full_path,
657 method.as_str(),
658 "Controller",
659 description.clone(),
660 );
661 }
662
663 if has_body {
664 let expected = description.request_body.clone();
665 if expected.is_none() && description.file_parameters.is_empty() {
667 panic!(
668 "Request body class must be specified in the route description for {}",
669 full_path
670 );
671 }
672 }
673
674 let mut all_middlewares = Vec::new();
675 if let Some(limit) = description.rate_limit {
676 if limit > 0 {
677 let path_clone = meta_path.clone();
678
679 let m: Middleware = Arc::new(move |ctx: &mut CorrelationContext| {
680 let headers = ctx.headers();
681 let path_inner = path_clone.clone();
682 let fut = async move {
683 let limiter = LIMITER.get_or_init(|| create_from_env()).clone();
684 let key = ctx.user_id().unwrap_or_else(|| {
685 headers
686 .get("x-forwarded-for")
687 .and_then(|v| v.to_str().ok())
688 .unwrap_or("anonymous")
689 .to_string()
690 }) + ":"
691 + &path_inner.path;
692 if limiter.is_allowed(&key, path_inner.limit).await {
693 return Ok(());
694 }
695 Err(ErrorResult::from_error(
696 "Too many requests. Please try again later.",
697 429,
698 ))
699 };
700
701 FutureExt::boxed(fut)
704 });
705 all_middlewares.push(m);
706 }
707 }
708 all_middlewares.extend(middlewares);
709
710 let file_counts: Vec<(String, u32)> = description
713 .file_parameters
714 .iter()
715 .filter_map(|(name, p)| p.count.map(|c| (name.clone(), c)))
716 .collect();
717 let handler = Arc::new(handler);
718
719 let boxed: BoxHandler = Arc::new(move |ctx, _headers, _method, _path, _body| {
720 let file_counts = file_counts.clone();
721 let handler = handler.clone();
722 Box::pin(async move {
723 let ctx = Arc::new(ctx);
724 if let Some(err) = file_count_violation(ctx.clone(), &file_counts) {
725 return crate::response::error_response(&err, ctx.clone());
726 }
727 match handler((*ctx).clone()).await {
728 Ok(typed) => {
729 let body = match typed.serialize() {
730 Ok(b) => b,
731 Err(e) => {
732 let err =
733 ErrorResult::from_error(&anyhow::anyhow!(e.to_string()), 500);
734 return crate::response::error_response(&err, ctx.clone());
735 }
736 };
737 if body.is_empty() {
738 let err = ErrorResult::new("Response body is null", None, 503);
739 return crate::response::error_response(&err, ctx.clone());
740 }
741 let mut builder = Response::builder()
742 .status(typed.code())
743 .header("X-Request-ID", ctx.request_id())
744 .header("X-Correlation-ID", ctx.correlation_id())
745 .header("X-Correlation-Flow", ctx.flow().to_string());
746 let ct = match typed.response_type() {
747 ResponseType::Json => "application/json",
748 ResponseType::File => {
749 let filename = body.rsplit('/').next().unwrap_or("file");
750 builder = builder.header(
751 "Content-Disposition",
752 format!("attachment; filename=\"{}\"", filename),
753 );
754 "application/octet-stream"
755 }
756 ResponseType::Xml => "application/xml",
757 ResponseType::Javascript => "application/javascript",
758 ResponseType::Html => "text/html",
759 ResponseType::Text => "text/plain",
760 };
761 builder = builder.header("Content-Type", ct);
762 builder.body(body).unwrap()
763 }
764 Err(e) => crate::response::error_response(&e, ctx.clone()),
765 }
766 })
767 });
768
769 self.insert(RouteEntry {
770 method: method.clone(),
771 path: full_path.clone(),
772 handler: boxed,
773 middlewares: all_middlewares,
774 });
775 info!(
776 target: "routing",
777 handler = "Controller",
778 method = method.as_str(),
779 path = %full_path,
780 "Mounted a '{}' handler which listens on '{}'",
781 method.as_str(),
782 full_path
783 );
784 TOTAL_COUNT.fetch_add(1, Ordering::SeqCst);
785 }
786
787 pub fn mount_typed<F, Fut>(
792 &mut self,
793 base_path: &str,
794 version: u32,
795 path: &str,
796 method: Method,
797 description: RouteDescription,
798 handler: F,
799 middlewares: Vec<Middleware>,
800 ) where
801 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
802 Fut: Future<Output = Result<Box<dyn TypedServiceResult>, ErrorResult>> + Send + 'static,
803 {
804 self.ensure_global_middleware();
805 let full_path = calculate_full_path(base_path, version, path);
806 let meta_path = Holder {
807 path: full_path.to_string(),
808 limit: description.rate_limit.unwrap_or(0),
809 };
810
811 if let Ok(mut reg) = DocumentationRegistrant::global().write() {
812 reg.register_route(
813 &full_path,
814 method.as_str(),
815 "Controller",
816 description.clone(),
817 );
818 }
819
820 let mut all_middlewares = Vec::new();
821 if let Some(limit) = description.rate_limit {
822 if limit > 0 {
823 let path_clone = meta_path.clone();
824 let m: Middleware = Arc::new(move |ctx: &mut CorrelationContext| {
825 let headers = ctx.headers();
826 let path_inner = path_clone.clone();
827 let fut = async move {
828 let limiter = LIMITER.get_or_init(|| create_from_env()).clone();
829 let key = ctx.user_id().unwrap_or_else(|| {
830 headers
831 .get("x-forwarded-for")
832 .and_then(|v| v.to_str().ok())
833 .unwrap_or("anonymous")
834 .to_string()
835 }) + ":"
836 + &path_inner.path;
837 if limiter.is_allowed(&key, path_inner.limit).await {
838 return Ok(());
839 }
840 Err(ErrorResult::from_error(
841 "Too many requests. Please try again later.",
842 429,
843 ))
844 };
845 FutureExt::boxed(fut)
846 });
847 all_middlewares.push(m);
848 }
849 }
850 all_middlewares.extend(middlewares);
851
852 let file_counts: Vec<(String, u32)> = description
855 .file_parameters
856 .iter()
857 .filter_map(|(name, p)| p.count.map(|c| (name.clone(), c)))
858 .collect();
859 let handler = Arc::new(handler);
860
861 let boxed: BoxHandler = Arc::new(move |ctx, _headers, _method, _path, _body| {
862 let file_counts = file_counts.clone();
863 let handler = handler.clone();
864 Box::pin(async move {
865 let ctx = Arc::new(ctx);
866 if let Some(err) = file_count_violation(ctx.clone(), &file_counts) {
867 return crate::response::error_response(&err, ctx.clone());
868 }
869 match handler((*ctx).clone()).await {
870 Ok(typed) => crate::response::build_response(typed.as_ref(), ctx.clone()),
871 Err(e) => crate::response::error_response(&e, ctx.clone()),
872 }
873 })
874 });
875
876 self.insert(RouteEntry {
877 method: method.clone(),
878 path: full_path.clone(),
879 handler: boxed,
880 middlewares: all_middlewares,
881 });
882 info!(
883 handler = "Controller",
884 method = method.as_str(),
885 path = %full_path,
886 "Mounted a '{}' handler which listens on '{}'",
887 method.as_str(),
888 full_path
889 );
890 TOTAL_COUNT.fetch_add(1, Ordering::SeqCst);
891 }
892
893 pub fn mount_raw(
898 &mut self,
899 base_path: &str,
900 version: u32,
901 path: &str,
902 method: Method,
903 handler: BoxHandler,
904 middlewares: Vec<Middleware>,
905 ) {
906 self.ensure_global_middleware();
907 let full_path = calculate_full_path(base_path, version, path);
908 self.insert(RouteEntry {
909 method: method.clone(),
910 path: full_path.clone(),
911 handler,
912 middlewares,
913 });
914 info!(
915 handler = "raw",
916 method = method.as_str(),
917 path = %full_path,
918 "Mounted a '{}' handler which listens on '{}'",
919 method.as_str(),
920 full_path
921 );
922 TOTAL_COUNT.fetch_add(1, Ordering::SeqCst);
923 }
924
925 pub fn mount_static(
929 &mut self,
930 base_path: &str,
931 version: u32,
932 path: &str,
933 fs_path: String,
934 middlewares: Vec<Middleware>,
935 ) {
936 let full_path = if version == 0 {
937 no_trailing_slash(&format!(
938 "/{}/{}/*",
939 base_path.trim_matches('/'),
940 path.trim_matches('/')
941 ))
942 } else {
943 no_trailing_slash(&format!(
944 "/v{}/{}/{}/*",
945 version,
946 base_path.trim_matches('/'),
947 path.trim_matches('/')
948 ))
949 };
950 let fs_path_clone = fs_path.clone();
951 let full_path_no_wildcard = full_path.trim_end_matches("/*").to_string();
952 let handler: BoxHandler = Arc::new(move |ctx, _headers, _method, req_path, _body| {
953 let fs_path = fs_path_clone.clone();
954 let req_path = req_path.clone();
955 let ctx = ctx.clone();
956 let prefix = full_path_no_wildcard.clone();
957 Box::pin(async move {
958 let rel = req_path.trim_start_matches(&prefix).trim_start_matches('/');
959 let candidates = if rel.is_empty() {
961 vec![
962 format!("{}/index.html", fs_path.trim_end_matches('/')),
963 fs_path.clone(),
964 ]
965 } else {
966 vec![format!("{}/{}", fs_path.trim_end_matches('/'), rel)]
967 };
968 for candidate in &candidates {
969 if let Ok(bytes) = std::fs::read(candidate) {
970 let ct = guess_content_type(candidate);
971 let body = String::from_utf8_lossy(&bytes).into_owned();
972 return Response::builder()
973 .status(200)
974 .header("X-Request-ID", ctx.request_id())
975 .header("Content-Type", ct)
976 .body(body)
977 .unwrap();
978 }
979 }
980 let file_path = if rel.is_empty() {
982 fs_path.clone()
983 } else {
984 format!("{}/{}", fs_path.trim_end_matches('/'), rel)
985 };
986 let typed = ServiceResult::new(
987 "success",
988 "OK",
989 Some(format!("Serving static file: {}", file_path)),
990 200,
991 )
992 .with_response_type(ResponseType::Text);
993 let body = TypedServiceResult::serialize(&typed).unwrap();
994 Response::builder()
995 .status(TypedServiceResult::code(&typed))
996 .header("X-Request-ID", ctx.request_id())
997 .header("Content-Type", "text/plain")
998 .body(body)
999 .unwrap()
1000 })
1001 });
1002 self.insert(RouteEntry {
1003 method: Method::GET,
1004 path: full_path.clone(),
1005 handler,
1006 middlewares,
1007 });
1008 info!(
1009 handler = "static",
1010 path = %full_path,
1011 "Mounted a '{}' handler which listens on '{}'",
1012 "static file",
1013 full_path
1014 );
1015 TOTAL_COUNT.fetch_add(1, Ordering::SeqCst);
1016 }
1017
1018 fn find<'a>(
1019 node: &'a TrieNode,
1020 segments: &[&str],
1021 method: &Method,
1022 params: &mut Vec<(String, String)>,
1023 ) -> Option<&'a RouteEntry> {
1024 match segments.split_first() {
1025 None => node
1026 .exact_routes
1027 .get(method)
1028 .or_else(|| node.wildcard_routes.get(method)),
1029 Some((seg, rest)) => {
1030 if let Some(child) = node.static_children.get(*seg) {
1032 if let Some(r) = Self::find(child, rest, method, params) {
1033 return Some(r);
1034 }
1035 }
1036
1037 if let Some((name, child)) = &node.param_child {
1038 params.push((name.clone(), (*seg).to_string()));
1039 if let Some(r) = Self::find(child, rest, method, params) {
1040 return Some(r);
1041 }
1042 params.pop();
1043 }
1044
1045 node.wildcard_routes.get(method)
1046 }
1047 }
1048 }
1049}
1050
1051impl Default for Router {
1052 fn default() -> Self {
1053 Self::new()
1054 }
1055}
1056
1057fn guess_content_type(path: &str) -> &'static str {
1058 let lower = path.to_ascii_lowercase();
1059 if lower.ends_with(".html") || lower.ends_with(".htm") {
1060 "text/html"
1061 } else if lower.ends_with(".js") {
1062 "application/javascript"
1063 } else if lower.ends_with(".css") {
1064 "text/css"
1065 } else if lower.ends_with(".json") {
1066 "application/json"
1067 } else if lower.ends_with(".png") {
1068 "image/png"
1069 } else if lower.ends_with(".jpg") || lower.ends_with(".jpeg") {
1070 "image/jpeg"
1071 } else if lower.ends_with(".svg") {
1072 "image/svg+xml"
1073 } else if lower.ends_with(".yaml") || lower.ends_with(".yml") {
1074 "application/yaml"
1075 } else {
1076 "text/plain"
1077 }
1078}
1079fn normalize_path(p: &str) -> String {
1080 let p = p.trim().replace("//", "/");
1081 if p.ends_with('/') && p.len() > 1 {
1082 p[..p.len() - 1].to_string()
1083 } else if p.is_empty() {
1084 "/".to_string()
1085 } else {
1086 p
1087 }
1088}
1089fn no_trailing_slash(path: &str) -> String {
1090 let p = path.trim().replace("//", "/");
1091 if p.is_empty() || p == "/" {
1092 return "/".to_string();
1093 }
1094 if p.ends_with('/') {
1095 p[..p.rfind('/').unwrap()].to_string()
1096 } else {
1097 p
1098 }
1099}
1100fn file_count_violation(
1105 ctx: Arc<CorrelationContext>,
1106 counts: &[(String, u32)],
1107) -> Option<ErrorResult> {
1108 if counts.is_empty() {
1109 return None;
1110 }
1111 let mp = ctx.multipart()?;
1112 for (name, max) in counts {
1113 let occurrences = mp.files.iter().filter(|f| &f.field_name == name).count()
1114 + mp.fields.get(name).map_or(0, |v| v.len());
1115 if occurrences as u32 > *max {
1116 return Some(ErrorResult::bad_request(format!(
1117 "Too many '{}' parts: got {}, maximum is {}",
1118 name, occurrences, max
1119 )));
1120 }
1121 }
1122 None
1123}
1124
1125fn calculate_full_path(base_path: &str, version: u32, path: &str) -> String {
1126 let decoded = if version == 0 {
1127 let dddd = no_trailing_slash(&format!(
1128 "/{}/{}",
1129 base_path.trim_matches('/'),
1130 path.trim_start_matches('/')
1131 ));
1132 urlencoding::decode(dddd.leak())
1133 } else {
1134 let dddd = no_trailing_slash(&format!(
1135 "/v{}/{}/{}",
1136 version,
1137 base_path.trim_matches('/'),
1138 path.trim_start_matches('/')
1139 ));
1140 urlencoding::decode(dddd.leak())
1141 };
1142
1143 if decoded.is_err() {
1144 return base_path.to_string();
1145 }
1146
1147 decoded.unwrap().to_string()
1148}
1149
1150#[async_trait::async_trait]
1153pub trait RouteController: Send + Sync {
1157 fn base_path(&self) -> &str;
1159 fn version(&self) -> u32 {
1161 0
1162 }
1163 fn full_path(&self, path: &str) -> String {
1165 calculate_full_path(self.base_path(), self.version(), path)
1166 }
1167 async fn register_routes(&self, router: &mut Router);
1169 async fn register(&self, router: &mut Router) {
1171 self.register_routes(router).await;
1172 }
1173
1174 fn type_name(&self) -> &'static str {
1176 std::any::type_name::<Self>()
1177 }
1178}
1179
1180pub trait RouteControllerExt: RouteController {
1188 fn mount_get<T, F, Fut>(
1190 &self,
1191 router: &mut Router,
1192 path: &str,
1193 description: RouteDescription,
1194 handler: F,
1195 middlewares: Vec<Middleware>,
1196 ) where
1197 T: serde::Serialize + Send + Sync + 'static,
1198 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1199 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1200 {
1201 router.mount(
1202 self.base_path(),
1203 self.version(),
1204 path,
1205 false,
1206 Method::GET,
1207 description,
1208 handler,
1209 middlewares,
1210 );
1211 }
1212 fn mount_post<T, F, Fut>(
1214 &self,
1215 router: &mut Router,
1216 path: &str,
1217 description: RouteDescription,
1218 handler: F,
1219 middlewares: Vec<Middleware>,
1220 ) where
1221 T: serde::Serialize + Send + Sync + 'static,
1222 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1223 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1224 {
1225 router.mount(
1226 self.base_path(),
1227 self.version(),
1228 path,
1229 true,
1230 Method::POST,
1231 description,
1232 handler,
1233 middlewares,
1234 );
1235 }
1236 fn mount_put<T, F, Fut>(
1238 &self,
1239 router: &mut Router,
1240 path: &str,
1241 description: RouteDescription,
1242 handler: F,
1243 middlewares: Vec<Middleware>,
1244 ) where
1245 T: serde::Serialize + Send + Sync + 'static,
1246 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1247 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1248 {
1249 router.mount(
1250 self.base_path(),
1251 self.version(),
1252 path,
1253 true,
1254 Method::PUT,
1255 description,
1256 handler,
1257 middlewares,
1258 );
1259 }
1260 fn mount_patch<T, F, Fut>(
1262 &self,
1263 router: &mut Router,
1264 path: &str,
1265 has_body: bool,
1266 description: RouteDescription,
1267 handler: F,
1268 middlewares: Vec<Middleware>,
1269 ) where
1270 T: serde::Serialize + Send + Sync + 'static,
1271 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1272 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1273 {
1274 router.mount(
1275 self.base_path(),
1276 self.version(),
1277 path,
1278 has_body,
1279 Method::PATCH,
1280 description,
1281 handler,
1282 middlewares,
1283 );
1284 }
1285 fn mount_patch_with_body<T, F, Fut>(
1287 &self,
1288 router: &mut Router,
1289 path: &str,
1290 description: RouteDescription,
1291 handler: F,
1292 middlewares: Vec<Middleware>,
1293 ) where
1294 T: serde::Serialize + Send + Sync + 'static,
1295 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1296 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1297 {
1298 self.mount_patch(router, path, true, description, handler, middlewares);
1299 }
1300 fn mount_patch_without_body<T, F, Fut>(
1302 &self,
1303 router: &mut Router,
1304 path: &str,
1305 description: RouteDescription,
1306 handler: F,
1307 middlewares: Vec<Middleware>,
1308 ) where
1309 T: serde::Serialize + Send + Sync + 'static,
1310 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1311 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1312 {
1313 self.mount_patch(router, path, false, description, handler, middlewares);
1314 }
1315 fn mount_delete<T, F, Fut>(
1317 &self,
1318 router: &mut Router,
1319 path: &str,
1320 description: RouteDescription,
1321 handler: F,
1322 middlewares: Vec<Middleware>,
1323 ) where
1324 T: serde::Serialize + Send + Sync + 'static,
1325 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1326 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1327 {
1328 router.mount(
1329 self.base_path(),
1330 self.version(),
1331 path,
1332 false,
1333 Method::DELETE,
1334 description,
1335 handler,
1336 middlewares,
1337 );
1338 }
1339 fn mount_options<T, F, Fut>(
1341 &self,
1342 router: &mut Router,
1343 path: &str,
1344 description: RouteDescription,
1345 handler: F,
1346 middlewares: Vec<Middleware>,
1347 ) where
1348 T: serde::Serialize + Send + Sync + 'static,
1349 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1350 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1351 {
1352 router.mount(
1353 self.base_path(),
1354 self.version(),
1355 path,
1356 false,
1357 Method::OPTIONS,
1358 description,
1359 handler,
1360 middlewares,
1361 );
1362 }
1363 fn mount_head<T, F, Fut>(
1365 &self,
1366 router: &mut Router,
1367 path: &str,
1368 description: RouteDescription,
1369 handler: F,
1370 middlewares: Vec<Middleware>,
1371 ) where
1372 T: serde::Serialize + Send + Sync + 'static,
1373 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1374 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1375 {
1376 router.mount(
1377 self.base_path(),
1378 self.version(),
1379 path,
1380 false,
1381 Method::HEAD,
1382 description,
1383 handler,
1384 middlewares,
1385 );
1386 }
1387 fn mount_trace<T, F, Fut>(
1389 &self,
1390 router: &mut Router,
1391 path: &str,
1392 description: RouteDescription,
1393 handler: F,
1394 middlewares: Vec<Middleware>,
1395 ) where
1396 T: serde::Serialize + Send + Sync + 'static,
1397 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1398 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1399 {
1400 router.mount(
1401 self.base_path(),
1402 self.version(),
1403 path,
1404 false,
1405 Method::TRACE,
1406 description,
1407 handler,
1408 middlewares,
1409 );
1410 }
1411 fn mount_connect<T, F, Fut>(
1413 &self,
1414 router: &mut Router,
1415 path: &str,
1416 description: RouteDescription,
1417 handler: F,
1418 middlewares: Vec<Middleware>,
1419 ) where
1420 T: serde::Serialize + Send + Sync + 'static,
1421 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1422 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1423 {
1424 router.mount(
1425 self.base_path(),
1426 self.version(),
1427 path,
1428 false,
1429 Method::CONNECT,
1430 description,
1431 handler,
1432 middlewares,
1433 );
1434 }
1435 fn mount_copy<T, F, Fut>(
1437 &self,
1438 router: &mut Router,
1439 path: &str,
1440 description: RouteDescription,
1441 handler: F,
1442 middlewares: Vec<Middleware>,
1443 ) where
1444 T: serde::Serialize + Send + Sync + 'static,
1445 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1446 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1447 {
1448 router.mount(
1449 self.base_path(),
1450 self.version(),
1451 path,
1452 false,
1453 Method::from_bytes(b"COPY").unwrap(),
1454 description,
1455 handler,
1456 middlewares,
1457 );
1458 }
1459 fn mount_move<T, F, Fut>(
1461 &self,
1462 router: &mut Router,
1463 path: &str,
1464 description: RouteDescription,
1465 handler: F,
1466 middlewares: Vec<Middleware>,
1467 ) where
1468 T: serde::Serialize + Send + Sync + 'static,
1469 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1470 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1471 {
1472 router.mount(
1473 self.base_path(),
1474 self.version(),
1475 path,
1476 false,
1477 Method::from_bytes(b"MOVE").unwrap(),
1478 description,
1479 handler,
1480 middlewares,
1481 );
1482 }
1483 fn mount_lock<T, F, Fut>(
1485 &self,
1486 router: &mut Router,
1487 path: &str,
1488 description: RouteDescription,
1489 handler: F,
1490 middlewares: Vec<Middleware>,
1491 ) where
1492 T: serde::Serialize + Send + Sync + 'static,
1493 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1494 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1495 {
1496 router.mount(
1497 self.base_path(),
1498 self.version(),
1499 path,
1500 true,
1501 Method::from_bytes(b"LOCK").unwrap(),
1502 description,
1503 handler,
1504 middlewares,
1505 );
1506 }
1507 fn mount_unlock<T, F, Fut>(
1509 &self,
1510 router: &mut Router,
1511 path: &str,
1512 description: RouteDescription,
1513 handler: F,
1514 middlewares: Vec<Middleware>,
1515 ) where
1516 T: serde::Serialize + Send + Sync + 'static,
1517 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1518 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1519 {
1520 router.mount(
1521 self.base_path(),
1522 self.version(),
1523 path,
1524 false,
1525 Method::from_bytes(b"UNLOCK").unwrap(),
1526 description,
1527 handler,
1528 middlewares,
1529 );
1530 }
1531 fn mount_propfind<T, F, Fut>(
1533 &self,
1534 router: &mut Router,
1535 path: &str,
1536 description: RouteDescription,
1537 handler: F,
1538 middlewares: Vec<Middleware>,
1539 ) where
1540 T: serde::Serialize + Send + Sync + 'static,
1541 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1542 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1543 {
1544 router.mount(
1545 self.base_path(),
1546 self.version(),
1547 path,
1548 true,
1549 Method::from_bytes(b"PROPFIND").unwrap(),
1550 description,
1551 handler,
1552 middlewares,
1553 );
1554 }
1555 fn mount_mkcol<T, F, Fut>(
1557 &self,
1558 router: &mut Router,
1559 path: &str,
1560 description: RouteDescription,
1561 handler: F,
1562 middlewares: Vec<Middleware>,
1563 ) where
1564 T: serde::Serialize + Send + Sync + 'static,
1565 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1566 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1567 {
1568 router.mount(
1569 self.base_path(),
1570 self.version(),
1571 path,
1572 false,
1573 Method::from_bytes(b"MKCOL").unwrap(),
1574 description,
1575 handler,
1576 middlewares,
1577 );
1578 }
1579 fn mount_search<T, F, Fut>(
1581 &self,
1582 router: &mut Router,
1583 path: &str,
1584 description: RouteDescription,
1585 handler: F,
1586 middlewares: Vec<Middleware>,
1587 ) where
1588 T: serde::Serialize + Send + Sync + 'static,
1589 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1590 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1591 {
1592 router.mount(
1593 self.base_path(),
1594 self.version(),
1595 path,
1596 true,
1597 Method::from_bytes(b"SEARCH").unwrap(),
1598 description,
1599 handler,
1600 middlewares,
1601 );
1602 }
1603 fn mount_report<T, F, Fut>(
1605 &self,
1606 router: &mut Router,
1607 path: &str,
1608 description: RouteDescription,
1609 handler: F,
1610 middlewares: Vec<Middleware>,
1611 ) where
1612 T: serde::Serialize + Send + Sync + 'static,
1613 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1614 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1615 {
1616 router.mount(
1617 self.base_path(),
1618 self.version(),
1619 path,
1620 true,
1621 Method::from_bytes(b"REPORT").unwrap(),
1622 description,
1623 handler,
1624 middlewares,
1625 );
1626 }
1627 fn mount_checkin<T, F, Fut>(
1629 &self,
1630 router: &mut Router,
1631 path: &str,
1632 description: RouteDescription,
1633 handler: F,
1634 middlewares: Vec<Middleware>,
1635 ) where
1636 T: serde::Serialize + Send + Sync + 'static,
1637 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1638 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1639 {
1640 router.mount(
1641 self.base_path(),
1642 self.version(),
1643 path,
1644 false,
1645 Method::from_bytes(b"CHECKIN").unwrap(),
1646 description,
1647 handler,
1648 middlewares,
1649 );
1650 }
1651 fn mount_checkout<T, F, Fut>(
1653 &self,
1654 router: &mut Router,
1655 path: &str,
1656 description: RouteDescription,
1657 handler: F,
1658 middlewares: Vec<Middleware>,
1659 ) where
1660 T: serde::Serialize + Send + Sync + 'static,
1661 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1662 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1663 {
1664 router.mount(
1665 self.base_path(),
1666 self.version(),
1667 path,
1668 false,
1669 Method::from_bytes(b"CHECKOUT").unwrap(),
1670 description,
1671 handler,
1672 middlewares,
1673 );
1674 }
1675 fn mount_uncheckout<T, F, Fut>(
1677 &self,
1678 router: &mut Router,
1679 path: &str,
1680 description: RouteDescription,
1681 handler: F,
1682 middlewares: Vec<Middleware>,
1683 ) where
1684 T: serde::Serialize + Send + Sync + 'static,
1685 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1686 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1687 {
1688 router.mount(
1689 self.base_path(),
1690 self.version(),
1691 path,
1692 false,
1693 Method::from_bytes(b"UNCHECKOUT").unwrap(),
1694 description,
1695 handler,
1696 middlewares,
1697 );
1698 }
1699 fn mount_merge<T, F, Fut>(
1701 &self,
1702 router: &mut Router,
1703 path: &str,
1704 description: RouteDescription,
1705 handler: F,
1706 middlewares: Vec<Middleware>,
1707 ) where
1708 T: serde::Serialize + Send + Sync + 'static,
1709 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1710 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1711 {
1712 router.mount(
1713 self.base_path(),
1714 self.version(),
1715 path,
1716 true,
1717 Method::from_bytes(b"MERGE").unwrap(),
1718 description,
1719 handler,
1720 middlewares,
1721 );
1722 }
1723 fn mount_acl<T, F, Fut>(
1725 &self,
1726 router: &mut Router,
1727 path: &str,
1728 description: RouteDescription,
1729 handler: F,
1730 middlewares: Vec<Middleware>,
1731 ) where
1732 T: serde::Serialize + Send + Sync + 'static,
1733 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1734 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1735 {
1736 router.mount(
1737 self.base_path(),
1738 self.version(),
1739 path,
1740 true,
1741 Method::from_bytes(b"ACL").unwrap(),
1742 description,
1743 handler,
1744 middlewares,
1745 );
1746 }
1747 fn mount_custom<T, F, Fut>(
1749 &self,
1750 router: &mut Router,
1751 path: &str,
1752 method: Method,
1753 description: RouteDescription,
1754 handler: F,
1755 middlewares: Vec<Middleware>,
1756 ) where
1757 T: serde::Serialize + Send + Sync + 'static,
1758 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1759 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1760 {
1761 router.mount(
1762 self.base_path(),
1763 self.version(),
1764 path,
1765 false,
1766 method,
1767 description,
1768 handler,
1769 middlewares,
1770 );
1771 }
1772 fn mount_static(
1774 &self,
1775 router: &mut Router,
1776 path: &str,
1777 fs_path: String,
1778 middlewares: Vec<Middleware>,
1779 ) {
1780 router.mount_static(self.base_path(), self.version(), path, fs_path, middlewares);
1781 }
1782 fn mount_typed<F, Fut>(
1784 &self,
1785 router: &mut Router,
1786 path: &str,
1787 method: Method,
1788 description: RouteDescription,
1789 handler: F,
1790 middlewares: Vec<Middleware>,
1791 ) where
1792 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1793 Fut: Future<Output = Result<Box<dyn TypedServiceResult>, ErrorResult>> + Send + 'static,
1794 {
1795 router.mount_typed(
1796 self.base_path(),
1797 self.version(),
1798 path,
1799 method,
1800 description,
1801 handler,
1802 middlewares,
1803 );
1804 }
1805 fn mount_get_typed<F, Fut>(
1807 &self,
1808 router: &mut Router,
1809 path: &str,
1810 description: RouteDescription,
1811 handler: F,
1812 middlewares: Vec<Middleware>,
1813 ) where
1814 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1815 Fut: Future<Output = Result<Box<dyn TypedServiceResult>, ErrorResult>> + Send + 'static,
1816 {
1817 self.mount_typed(router, path, Method::GET, description, handler, middlewares);
1818 }
1819 fn mount_post_typed<F, Fut>(
1821 &self,
1822 router: &mut Router,
1823 path: &str,
1824 description: RouteDescription,
1825 handler: F,
1826 middlewares: Vec<Middleware>,
1827 ) where
1828 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1829 Fut: Future<Output = Result<Box<dyn TypedServiceResult>, ErrorResult>> + Send + 'static,
1830 {
1831 self.mount_typed(
1832 router,
1833 path,
1834 Method::POST,
1835 description,
1836 handler,
1837 middlewares,
1838 );
1839 }
1840}
1841impl<T: RouteController> RouteControllerExt for T {}
1842
1843pub struct ConfigurationRegistrant {
1853 router: Arc<tokio::sync::RwLock<Router>>,
1854 addr: SocketAddr,
1855}
1856
1857impl ConfigurationRegistrant {
1858 pub fn new(addr: SocketAddr) -> Self {
1860 Self {
1861 router: Arc::new(tokio::sync::RwLock::new(Router::new())),
1862 addr,
1863 }
1864 }
1865
1866 pub async fn mount_controller<C: RouteController + 'static>(&self, controller: C) {
1868 let mut r = self.router.write().await;
1869 info!(
1870 target: "routing",
1871 handler = std::any::type_name::<C>(),
1872 path = %controller.base_path(),
1873 "Mounted controller '{}' at '{}'",
1874 std::any::type_name::<C>(),
1875 controller.base_path()
1876 );
1877 controller.register_routes(&mut r).await;
1878 }
1879
1880 pub async fn mount_middleware(&self, _mw: Middleware) {
1882 let mut r = self.router.write().await;
1883 r.global_middlewares.push(_mw);
1884 }
1885
1886 pub async fn serve(self: Arc<Self>) -> anyhow::Result<SocketAddr> {
1889 let listener = TcpListener::bind(self.addr).await?;
1890 let addr = listener.local_addr()?;
1891 info!(address = %addr, "HTTP server listening");
1892 let router = self.router.clone();
1893 tokio::spawn(async move {
1894 loop {
1895 let (stream, remote) = match listener.accept().await {
1896 Ok(v) => v,
1897 Err(e) => {
1898 warn!(error = %e, "Failed to accept HTTP connection");
1899 continue;
1900 }
1901 };
1902 let io = TokioIo::new(stream);
1903 let router = router.clone();
1904 tokio::spawn(async move {
1905 let svc = service_fn(move |req: Request<Incoming>| {
1906 let router = router.clone();
1907 let remote = remote;
1908 async move { handle_request(router, req, remote).await }
1909 });
1910 if let Err(e) = http1::Builder::new().serve_connection(io, svc).await {
1911 tracing::debug!(remote_addr = %remote, error = %e, "HTTP connection ended with an error");
1912 }
1913 });
1914 }
1915 });
1916 Ok(addr)
1917 }
1918
1919 pub fn router_handle(&self) -> Arc<tokio::sync::RwLock<Router>> {
1921 self.router.clone()
1922 }
1923}
1924
1925async fn handle_request(
1926 router: Arc<tokio::sync::RwLock<Router>>,
1927 req: Request<Incoming>,
1928 remote_addr: SocketAddr,
1929) -> Result<Response<String>, ErrorResult> {
1930 let request_started = std::time::Instant::now();
1931 let method = req.method().clone();
1932 let path = req.uri().path();
1933 let old_path = path;
1934 let path = urlencoding::decode(path);
1935 if path.is_err() {
1936 tracing::error!(
1937 "This should not be possible. Encountered an error while processing the URL {}",
1938 old_path
1939 );
1940 return Err(ErrorResult::bad_request("Invalid path parameter"));
1941 }
1942 let path = path.ok().unwrap().to_string();
1943 let query = req.uri().query().unwrap_or("").to_string();
1944 let headers = req.headers().clone();
1945 let (_parts, body) = req.into_parts();
1946 let body_bytes = match body.collect().await {
1947 Ok(collected) => collected.to_bytes().to_vec(),
1948 Err(e) => {
1949 warn!(remote_addr = %remote_addr, error = %e, "Failed to read request body");
1950 vec![]
1951 }
1952 };
1953
1954 let params: HashMap<String, String> = serde_urlencoded::from_str(&query).unwrap_or_default();
1955 let mut ctx = build_correlation_context(&headers, ¶ms, &body_bytes);
1956
1957 if let Some(ct) = headers
1961 .get(http::header::CONTENT_TYPE)
1962 .and_then(|v| v.to_str().ok())
1963 {
1964 if ct.to_lowercase().starts_with("multipart/form-data") {
1965 if let Some(boundary) =
1966 crate::utils::request_parser::RequestParser::multipart_boundary(ct)
1967 {
1968 let mp = crate::utils::request_parser::RequestParser::parse_multipart(
1969 &body_bytes,
1970 &boundary,
1971 );
1972 ctx.set_multipart(mp);
1973 }
1974 }
1975 }
1976 {
1977 let guard = router.read().await;
1978 for mw in &guard.global_middlewares {
1979 if let Err(e) = mw(&mut ctx).await {
1980 let resp = crate::response::error_response(&e, Arc::new(ctx.clone()));
1981 return Ok(finish_request(
1982 &method,
1983 &path,
1984 request_started,
1985 adapt_response(resp),
1986 ));
1987 }
1988 }
1989 }
1990
1991 let limit = ctx
1992 .query_param("limit")
1993 .and_then(|v| v.parse().ok())
1994 .unwrap_or(15usize);
1995 let cursor = ctx.query_param("cursor");
1996 ctx.set_pagination(cursor, limit);
1997
1998 let test_hook = {
2002 let guard = router.read().await;
2003 let has_header = headers.contains_key("x-moovable-test-client")
2004 || headers.contains_key("x-tm30-test-client");
2005 if has_header {
2006 guard.test_client_handler.clone()
2007 } else {
2008 None
2009 }
2010 };
2011 if let Some(hook) = test_hook {
2012 let resp = hook(
2013 ctx.clone(),
2014 headers.clone(),
2015 method.clone(),
2016 path.clone(),
2017 body_bytes,
2018 )
2019 .await;
2020 return Ok(finish_request(
2021 &method,
2022 &path,
2023 request_started,
2024 adapt_string_response(resp, Arc::new(ctx.clone())),
2025 ));
2026 }
2027
2028 let guard = router.read().await;
2029 if let Some((entry, params)) = guard.resolve(&method, &path) {
2030 for mw in &entry.middlewares {
2031 if let Err(e) = mw(&mut ctx).await {
2032 let resp = crate::response::error_response(&e, Arc::new(ctx.clone()));
2033 return Ok(finish_request(
2034 &method,
2035 &path,
2036 request_started,
2037 adapt_response(resp),
2038 ));
2039 }
2040 }
2041 ctx.set_params(params);
2042 let handler = entry.handler.clone();
2043 let path_clone = path.clone();
2044 let headers_clone = headers.clone();
2045 let method_clone = method.clone();
2046 drop(guard);
2047 let resp = handler(
2048 ctx.clone(),
2049 headers_clone,
2050 method_clone,
2051 path_clone,
2052 body_bytes,
2053 )
2054 .await;
2055 Ok(finish_request(
2056 &method,
2057 &path,
2058 request_started,
2059 adapt_string_response(resp, Arc::new(ctx.clone())),
2060 ))
2061 } else {
2062 drop(guard);
2063 let err = ErrorResult::not_found(format!("The requested resource was not found: {path}"));
2064 let resp = crate::response::error_response(&err, Arc::new(ctx.clone()));
2065 Ok(finish_request(
2066 &method,
2067 &path,
2068 request_started,
2069 adapt_response(resp),
2070 ))
2071 }
2072}
2073
2074fn finish_request(
2075 method: &Method,
2076 path: &str,
2077 started: std::time::Instant,
2078 response: Response<String>,
2079) -> Response<String> {
2080 crate::middleware::monitoring::RequestLogger::log(
2081 path,
2082 method.as_str(),
2083 response.status().as_u16(),
2084 started.elapsed(),
2085 );
2086 response
2087}
2088
2089fn build_correlation_context(
2090 headers: &HeaderMap,
2091 query: &HashMap<String, String>,
2092 body: &[u8],
2093) -> CorrelationContext {
2094 let corr_id = headers
2095 .get("x-correlation-id")
2096 .and_then(|v| v.to_str().ok())
2097 .unwrap_or("");
2098 let flow_str = headers
2099 .get("x-correlation-flow")
2100 .and_then(|v| v.to_str().ok())
2101 .unwrap_or("ONCE");
2102 let flow = flow_str
2103 .parse()
2104 .unwrap_or(crate::logging::CorrelationFlow::Once);
2105 let ctx = if corr_id.is_empty() {
2106 CorrelationContext::new()
2107 } else {
2108 CorrelationContext::with_ids(corr_id, &hex::encode(rand::random::<[u8; 8]>()))
2109 };
2110 ctx.set_flow(flow);
2111 ctx.set_headers(headers.clone());
2114 ctx.set_query_params(query.clone());
2115 ctx.set_body(body.to_vec());
2116 let req_id = headers.get("x-request-id").and_then(|v| v.to_str().ok());
2117 if let Some(rid) = req_id {
2118 ctx.set_request_id(rid);
2119 } else {
2120 let rid = hex::encode(rand::random::<[u8; 8]>());
2121 ctx.set_request_id(&rid);
2122 }
2123 ctx
2124}
2125
2126fn adapt_response(r: Response<String>) -> Response<String> {
2127 r
2128}
2129fn adapt_string_response(
2130 mut r: Response<String>,
2131 ctx: Arc<CorrelationContext>,
2132) -> Response<String> {
2133 let headers = r.headers_mut();
2134 headers
2135 .entry("x-request-id")
2136 .or_insert(ctx.request_id().parse().unwrap());
2137 headers
2138 .entry("x-correlation-id")
2139 .or_insert(ctx.correlation_id().parse().unwrap());
2140 headers
2141 .entry("x-correlation-flow")
2142 .or_insert(ctx.flow().to_string().parse().unwrap());
2143 r
2144}
2145
2146pub fn json_response<T: serde::Serialize + Send + Sync>(
2149 result: ServiceResult<T>,
2150 ctx: Arc<CorrelationContext>,
2151) -> Response<String> {
2152 crate::response::build_response(&result, ctx)
2153}
2154
2155pub fn typed_response(
2158 result: &dyn TypedServiceResult,
2159 ctx: Arc<CorrelationContext>,
2160) -> Response<String> {
2161 let body = result.serialize().unwrap_or_else(|e| {
2162 ErrorResult::from_error(&anyhow::anyhow!(e.to_string()), 500)
2163 .message
2164 .clone()
2165 });
2166 let mut builder = Response::builder()
2167 .status(result.code())
2168 .header("X-Request-ID", ctx.request_id())
2169 .header("X-Correlation-ID", ctx.correlation_id())
2170 .header("X-Correlation-Flow", ctx.flow().to_string());
2171 let ct = match result.response_type() {
2172 ResponseType::Json => "application/json",
2173 ResponseType::File => {
2174 let filename = body.rsplit('/').next().unwrap_or("file");
2175 builder = builder.header(
2176 "Content-Disposition",
2177 format!("attachment; filename=\"{}\"", filename),
2178 );
2179 "application/octet-stream"
2180 }
2181 ResponseType::Xml => "application/xml",
2182 ResponseType::Javascript => "application/javascript",
2183 ResponseType::Html => "text/html",
2184 ResponseType::Text => "text/plain",
2185 };
2186 builder = builder.header("Content-Type", ct);
2187 builder.body(body).unwrap()
2188}