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