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 full_path
660 );
661 }
662 }
663
664 let mut all_middlewares = Vec::new();
665 if let Some(limit) = description.rate_limit {
666 if limit > 0 {
667 let path_clone = meta_path.clone();
668
669 let m: Middleware = Arc::new(move |ctx: &mut CorrelationContext| {
670 let headers = ctx.headers();
671 let path_inner = path_clone.clone();
672 let fut = async move {
673 let limiter = LIMITER.get_or_init(|| create_from_env()).clone();
674 let key = ctx.user_id().unwrap_or_else(|| {
675 headers
676 .get("x-forwarded-for")
677 .and_then(|v| v.to_str().ok())
678 .unwrap_or("anonymous")
679 .to_string()
680 }) + ":"
681 + &path_inner.path;
682 if limiter.is_allowed(&key, path_inner.limit).await {
683 return Ok(());
684 }
685 Err(ErrorResult::from_error(
686 "Too many requests. Please try again later.",
687 429,
688 ))
689 };
690
691 FutureExt::boxed(fut)
694 });
695 all_middlewares.push(m);
696 }
697 }
698 all_middlewares.extend(middlewares);
699
700 let file_counts: Vec<(String, u32)> = description
703 .file_parameters
704 .iter()
705 .filter_map(|(name, p)| p.count.map(|c| (name.clone(), c)))
706 .collect();
707 let handler = Arc::new(handler);
708
709 let boxed: BoxHandler = Arc::new(move |ctx, _headers, _method, _path, _body| {
710 let file_counts = file_counts.clone();
711 let handler = handler.clone();
712 Box::pin(async move {
713 let ctx = Arc::new(ctx);
714 if let Some(err) = file_count_violation(ctx.clone(), &file_counts) {
715 return crate::response::error_response(&err, ctx.clone());
716 }
717 match handler((*ctx).clone()).await {
718 Ok(typed) => {
719 let body = match typed.serialize() {
720 Ok(b) => b,
721 Err(e) => {
722 let err =
723 ErrorResult::from_error(&anyhow::anyhow!(e.to_string()), 500);
724 return crate::response::error_response(&err, ctx.clone());
725 }
726 };
727 if body.is_empty() {
728 let err = ErrorResult::new("Response body is null", None, 503);
729 return crate::response::error_response(&err, ctx.clone());
730 }
731 let mut builder = Response::builder()
732 .status(typed.code())
733 .header("X-Request-ID", ctx.request_id())
734 .header("X-Correlation-ID", ctx.correlation_id())
735 .header("X-Correlation-Flow", ctx.flow().to_string());
736 let ct = match typed.response_type() {
737 ResponseType::Json => "application/json",
738 ResponseType::File => {
739 let filename = body.rsplit('/').next().unwrap_or("file");
740 builder = builder.header(
741 "Content-Disposition",
742 format!("attachment; filename=\"{}\"", filename),
743 );
744 "application/octet-stream"
745 }
746 ResponseType::Xml => "application/xml",
747 ResponseType::Javascript => "application/javascript",
748 ResponseType::Html => "text/html",
749 ResponseType::Text => "text/plain",
750 };
751 builder = builder.header("Content-Type", ct);
752 builder.body(body).unwrap()
753 }
754 Err(e) => crate::response::error_response(&e, ctx.clone()),
755 }
756 })
757 });
758
759 self.insert(RouteEntry {
760 method: method.clone(),
761 path: full_path.clone(),
762 handler: boxed,
763 middlewares: all_middlewares,
764 });
765 info!(
766 target: "routing",
767 handler = "Controller",
768 method = method.as_str(),
769 path = %full_path,
770 "Mounted a '{}' handler which listens on '{}'",
771 method.as_str(),
772 full_path
773 );
774 TOTAL_COUNT.fetch_add(1, Ordering::SeqCst);
775 }
776
777 pub fn mount_typed<F, Fut>(
782 &mut self,
783 base_path: &str,
784 version: u32,
785 path: &str,
786 method: Method,
787 description: RouteDescription,
788 handler: F,
789 middlewares: Vec<Middleware>,
790 ) where
791 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
792 Fut: Future<Output = Result<Box<dyn TypedServiceResult>, ErrorResult>> + Send + 'static,
793 {
794 self.ensure_global_middleware();
795 let full_path = calculate_full_path(base_path, version, path);
796 let meta_path = Holder {
797 path: full_path.to_string(),
798 limit: description.rate_limit.unwrap_or(0),
799 };
800
801 if let Ok(mut reg) = DocumentationRegistrant::global().write() {
802 reg.register_route(
803 &full_path,
804 method.as_str(),
805 "Controller",
806 description.clone(),
807 );
808 }
809
810 let mut all_middlewares = Vec::new();
811 if let Some(limit) = description.rate_limit {
812 if limit > 0 {
813 let path_clone = meta_path.clone();
814 let m: Middleware = Arc::new(move |ctx: &mut CorrelationContext| {
815 let headers = ctx.headers();
816 let path_inner = path_clone.clone();
817 let fut = async move {
818 let limiter = LIMITER.get_or_init(|| create_from_env()).clone();
819 let key = ctx.user_id().unwrap_or_else(|| {
820 headers
821 .get("x-forwarded-for")
822 .and_then(|v| v.to_str().ok())
823 .unwrap_or("anonymous")
824 .to_string()
825 }) + ":"
826 + &path_inner.path;
827 if limiter.is_allowed(&key, path_inner.limit).await {
828 return Ok(());
829 }
830 Err(ErrorResult::from_error(
831 "Too many requests. Please try again later.",
832 429,
833 ))
834 };
835 FutureExt::boxed(fut)
836 });
837 all_middlewares.push(m);
838 }
839 }
840 all_middlewares.extend(middlewares);
841
842 let file_counts: Vec<(String, u32)> = description
845 .file_parameters
846 .iter()
847 .filter_map(|(name, p)| p.count.map(|c| (name.clone(), c)))
848 .collect();
849 let handler = Arc::new(handler);
850
851 let boxed: BoxHandler = Arc::new(move |ctx, _headers, _method, _path, _body| {
852 let file_counts = file_counts.clone();
853 let handler = handler.clone();
854 Box::pin(async move {
855 let ctx = Arc::new(ctx);
856 if let Some(err) = file_count_violation(ctx.clone(), &file_counts) {
857 return crate::response::error_response(&err, ctx.clone());
858 }
859 match handler((*ctx).clone()).await {
860 Ok(typed) => crate::response::build_response(typed.as_ref(), ctx.clone()),
861 Err(e) => crate::response::error_response(&e, ctx.clone()),
862 }
863 })
864 });
865
866 self.insert(RouteEntry {
867 method: method.clone(),
868 path: full_path.clone(),
869 handler: boxed,
870 middlewares: all_middlewares,
871 });
872 info!(
873 handler = "Controller",
874 method = method.as_str(),
875 path = %full_path,
876 "Mounted a '{}' handler which listens on '{}'",
877 method.as_str(),
878 full_path
879 );
880 TOTAL_COUNT.fetch_add(1, Ordering::SeqCst);
881 }
882
883 pub fn mount_raw(
888 &mut self,
889 base_path: &str,
890 version: u32,
891 path: &str,
892 method: Method,
893 handler: BoxHandler,
894 middlewares: Vec<Middleware>,
895 ) {
896 self.ensure_global_middleware();
897 let full_path = calculate_full_path(base_path, version, path);
898 self.insert(RouteEntry {
899 method: method.clone(),
900 path: full_path.clone(),
901 handler,
902 middlewares,
903 });
904 info!(
905 handler = "raw",
906 method = method.as_str(),
907 path = %full_path,
908 "Mounted a '{}' handler which listens on '{}'",
909 method.as_str(),
910 full_path
911 );
912 TOTAL_COUNT.fetch_add(1, Ordering::SeqCst);
913 }
914
915 pub fn mount_static(
919 &mut self,
920 base_path: &str,
921 version: u32,
922 path: &str,
923 fs_path: String,
924 middlewares: Vec<Middleware>,
925 ) {
926 let full_path = if version == 0 {
927 no_trailing_slash(&format!(
928 "/{}/{}/*",
929 base_path.trim_matches('/'),
930 path.trim_matches('/')
931 ))
932 } else {
933 no_trailing_slash(&format!(
934 "/v{}/{}/{}/*",
935 version,
936 base_path.trim_matches('/'),
937 path.trim_matches('/')
938 ))
939 };
940 let fs_path_clone = fs_path.clone();
941 let full_path_no_wildcard = full_path.trim_end_matches("/*").to_string();
942 let handler: BoxHandler = Arc::new(move |ctx, _headers, _method, req_path, _body| {
943 let fs_path = fs_path_clone.clone();
944 let req_path = req_path.clone();
945 let ctx = ctx.clone();
946 let prefix = full_path_no_wildcard.clone();
947 Box::pin(async move {
948 let rel = req_path.trim_start_matches(&prefix).trim_start_matches('/');
949 let candidates = if rel.is_empty() {
951 vec![
952 format!("{}/index.html", fs_path.trim_end_matches('/')),
953 fs_path.clone(),
954 ]
955 } else {
956 vec![format!("{}/{}", fs_path.trim_end_matches('/'), rel)]
957 };
958 for candidate in &candidates {
959 if let Ok(bytes) = std::fs::read(candidate) {
960 let ct = guess_content_type(candidate);
961 let body = String::from_utf8_lossy(&bytes).into_owned();
962 return Response::builder()
963 .status(200)
964 .header("X-Request-ID", ctx.request_id())
965 .header("Content-Type", ct)
966 .body(body)
967 .unwrap();
968 }
969 }
970 let file_path = if rel.is_empty() {
972 fs_path.clone()
973 } else {
974 format!("{}/{}", fs_path.trim_end_matches('/'), rel)
975 };
976 let typed = ServiceResult::new(
977 "success",
978 "OK",
979 Some(format!("Serving static file: {}", file_path)),
980 200,
981 )
982 .with_response_type(ResponseType::Text);
983 let body = TypedServiceResult::serialize(&typed).unwrap();
984 Response::builder()
985 .status(TypedServiceResult::code(&typed))
986 .header("X-Request-ID", ctx.request_id())
987 .header("Content-Type", "text/plain")
988 .body(body)
989 .unwrap()
990 })
991 });
992 self.insert(RouteEntry {
993 method: Method::GET,
994 path: full_path.clone(),
995 handler,
996 middlewares,
997 });
998 info!(
999 handler = "static",
1000 path = %full_path,
1001 "Mounted a '{}' handler which listens on '{}'",
1002 "static file",
1003 full_path
1004 );
1005 TOTAL_COUNT.fetch_add(1, Ordering::SeqCst);
1006 }
1007
1008 fn find<'a>(
1009 node: &'a TrieNode,
1010 segments: &[&str],
1011 method: &Method,
1012 params: &mut Vec<(String, String)>,
1013 ) -> Option<&'a RouteEntry> {
1014 match segments.split_first() {
1015 None => node
1016 .exact_routes
1017 .get(method)
1018 .or_else(|| node.wildcard_routes.get(method)),
1019 Some((seg, rest)) => {
1020 if let Some(child) = node.static_children.get(*seg) {
1022 if let Some(r) = Self::find(child, rest, method, params) {
1023 return Some(r);
1024 }
1025 }
1026
1027 if let Some((name, child)) = &node.param_child {
1028 params.push((name.clone(), (*seg).to_string()));
1029 if let Some(r) = Self::find(child, rest, method, params) {
1030 return Some(r);
1031 }
1032 params.pop();
1033 }
1034
1035 node.wildcard_routes.get(method)
1036 }
1037 }
1038 }
1039}
1040
1041impl Default for Router {
1042 fn default() -> Self {
1043 Self::new()
1044 }
1045}
1046
1047fn guess_content_type(path: &str) -> &'static str {
1048 let lower = path.to_ascii_lowercase();
1049 if lower.ends_with(".html") || lower.ends_with(".htm") {
1050 "text/html"
1051 } else if lower.ends_with(".js") {
1052 "application/javascript"
1053 } else if lower.ends_with(".css") {
1054 "text/css"
1055 } else if lower.ends_with(".json") {
1056 "application/json"
1057 } else if lower.ends_with(".png") {
1058 "image/png"
1059 } else if lower.ends_with(".jpg") || lower.ends_with(".jpeg") {
1060 "image/jpeg"
1061 } else if lower.ends_with(".svg") {
1062 "image/svg+xml"
1063 } else if lower.ends_with(".yaml") || lower.ends_with(".yml") {
1064 "application/yaml"
1065 } else {
1066 "text/plain"
1067 }
1068}
1069fn normalize_path(p: &str) -> String {
1070 let p = p.trim().replace("//", "/");
1071 if p.ends_with('/') && p.len() > 1 {
1072 p[..p.len() - 1].to_string()
1073 } else if p.is_empty() {
1074 "/".to_string()
1075 } else {
1076 p
1077 }
1078}
1079fn no_trailing_slash(path: &str) -> String {
1080 let p = path.trim().replace("//", "/");
1081 if p.is_empty() || p == "/" {
1082 return "/".to_string();
1083 }
1084 if p.ends_with('/') {
1085 p[..p.rfind('/').unwrap()].to_string()
1086 } else {
1087 p
1088 }
1089}
1090fn file_count_violation(
1095 ctx: Arc<CorrelationContext>,
1096 counts: &[(String, u32)],
1097) -> Option<ErrorResult> {
1098 if counts.is_empty() {
1099 return None;
1100 }
1101 let mp = ctx.multipart()?;
1102 for (name, max) in counts {
1103 let occurrences = mp.files.iter().filter(|f| &f.field_name == name).count()
1104 + mp.fields.get(name).map_or(0, |v| v.len());
1105 if occurrences as u32 > *max {
1106 return Some(ErrorResult::bad_request(format!(
1107 "Too many '{}' parts: got {}, maximum is {}",
1108 name, occurrences, max
1109 )));
1110 }
1111 }
1112 None
1113}
1114
1115fn calculate_full_path(base_path: &str, version: u32, path: &str) -> String {
1116 let decoded = if version == 0 {
1117 let dddd = no_trailing_slash(&format!(
1118 "/{}/{}",
1119 base_path.trim_matches('/'),
1120 path.trim_start_matches('/')
1121 ));
1122 urlencoding::decode(dddd.leak())
1123 } else {
1124 let dddd = no_trailing_slash(&format!(
1125 "/v{}/{}/{}",
1126 version,
1127 base_path.trim_matches('/'),
1128 path.trim_start_matches('/')
1129 ));
1130 urlencoding::decode(dddd.leak())
1131 };
1132
1133 if decoded.is_err() {
1134 return base_path.to_string();
1135 }
1136
1137 decoded.unwrap().to_string()
1138}
1139
1140#[async_trait::async_trait]
1143pub trait RouteController: Send + Sync {
1147 fn base_path(&self) -> &str;
1149 fn version(&self) -> u32 {
1151 0
1152 }
1153 fn full_path(&self, path: &str) -> String {
1155 calculate_full_path(self.base_path(), self.version(), path)
1156 }
1157 async fn register_routes(&self, router: &mut Router);
1159 async fn register(&self, router: &mut Router) {
1161 self.register_routes(router).await;
1162 }
1163
1164 fn type_name(&self) -> &'static str {
1166 std::any::type_name::<Self>()
1167 }
1168}
1169
1170pub trait RouteControllerExt: RouteController {
1178 fn mount_get<T, F, Fut>(
1180 &self,
1181 router: &mut Router,
1182 path: &str,
1183 description: RouteDescription,
1184 handler: F,
1185 middlewares: Vec<Middleware>,
1186 ) where
1187 T: serde::Serialize + Send + Sync + 'static,
1188 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1189 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1190 {
1191 router.mount(
1192 self.base_path(),
1193 self.version(),
1194 path,
1195 false,
1196 Method::GET,
1197 description,
1198 handler,
1199 middlewares,
1200 );
1201 }
1202 fn mount_post<T, F, Fut>(
1204 &self,
1205 router: &mut Router,
1206 path: &str,
1207 description: RouteDescription,
1208 handler: F,
1209 middlewares: Vec<Middleware>,
1210 ) where
1211 T: serde::Serialize + Send + Sync + 'static,
1212 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1213 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1214 {
1215 router.mount(
1216 self.base_path(),
1217 self.version(),
1218 path,
1219 true,
1220 Method::POST,
1221 description,
1222 handler,
1223 middlewares,
1224 );
1225 }
1226 fn mount_put<T, F, Fut>(
1228 &self,
1229 router: &mut Router,
1230 path: &str,
1231 description: RouteDescription,
1232 handler: F,
1233 middlewares: Vec<Middleware>,
1234 ) where
1235 T: serde::Serialize + Send + Sync + 'static,
1236 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1237 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1238 {
1239 router.mount(
1240 self.base_path(),
1241 self.version(),
1242 path,
1243 true,
1244 Method::PUT,
1245 description,
1246 handler,
1247 middlewares,
1248 );
1249 }
1250 fn mount_patch<T, F, Fut>(
1252 &self,
1253 router: &mut Router,
1254 path: &str,
1255 has_body: bool,
1256 description: RouteDescription,
1257 handler: F,
1258 middlewares: Vec<Middleware>,
1259 ) where
1260 T: serde::Serialize + Send + Sync + 'static,
1261 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1262 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1263 {
1264 router.mount(
1265 self.base_path(),
1266 self.version(),
1267 path,
1268 has_body,
1269 Method::PATCH,
1270 description,
1271 handler,
1272 middlewares,
1273 );
1274 }
1275 fn mount_patch_with_body<T, F, Fut>(
1277 &self,
1278 router: &mut Router,
1279 path: &str,
1280 description: RouteDescription,
1281 handler: F,
1282 middlewares: Vec<Middleware>,
1283 ) where
1284 T: serde::Serialize + Send + Sync + 'static,
1285 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1286 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1287 {
1288 self.mount_patch(router, path, true, description, handler, middlewares);
1289 }
1290 fn mount_patch_without_body<T, F, Fut>(
1292 &self,
1293 router: &mut Router,
1294 path: &str,
1295 description: RouteDescription,
1296 handler: F,
1297 middlewares: Vec<Middleware>,
1298 ) where
1299 T: serde::Serialize + Send + Sync + 'static,
1300 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1301 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1302 {
1303 self.mount_patch(router, path, false, description, handler, middlewares);
1304 }
1305 fn mount_delete<T, F, Fut>(
1307 &self,
1308 router: &mut Router,
1309 path: &str,
1310 description: RouteDescription,
1311 handler: F,
1312 middlewares: Vec<Middleware>,
1313 ) where
1314 T: serde::Serialize + Send + Sync + 'static,
1315 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1316 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1317 {
1318 router.mount(
1319 self.base_path(),
1320 self.version(),
1321 path,
1322 false,
1323 Method::DELETE,
1324 description,
1325 handler,
1326 middlewares,
1327 );
1328 }
1329 fn mount_options<T, F, Fut>(
1331 &self,
1332 router: &mut Router,
1333 path: &str,
1334 description: RouteDescription,
1335 handler: F,
1336 middlewares: Vec<Middleware>,
1337 ) where
1338 T: serde::Serialize + Send + Sync + 'static,
1339 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1340 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1341 {
1342 router.mount(
1343 self.base_path(),
1344 self.version(),
1345 path,
1346 false,
1347 Method::OPTIONS,
1348 description,
1349 handler,
1350 middlewares,
1351 );
1352 }
1353 fn mount_head<T, F, Fut>(
1355 &self,
1356 router: &mut Router,
1357 path: &str,
1358 description: RouteDescription,
1359 handler: F,
1360 middlewares: Vec<Middleware>,
1361 ) where
1362 T: serde::Serialize + Send + Sync + 'static,
1363 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1364 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1365 {
1366 router.mount(
1367 self.base_path(),
1368 self.version(),
1369 path,
1370 false,
1371 Method::HEAD,
1372 description,
1373 handler,
1374 middlewares,
1375 );
1376 }
1377 fn mount_trace<T, F, Fut>(
1379 &self,
1380 router: &mut Router,
1381 path: &str,
1382 description: RouteDescription,
1383 handler: F,
1384 middlewares: Vec<Middleware>,
1385 ) where
1386 T: serde::Serialize + Send + Sync + 'static,
1387 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1388 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1389 {
1390 router.mount(
1391 self.base_path(),
1392 self.version(),
1393 path,
1394 false,
1395 Method::TRACE,
1396 description,
1397 handler,
1398 middlewares,
1399 );
1400 }
1401 fn mount_connect<T, F, Fut>(
1403 &self,
1404 router: &mut Router,
1405 path: &str,
1406 description: RouteDescription,
1407 handler: F,
1408 middlewares: Vec<Middleware>,
1409 ) where
1410 T: serde::Serialize + Send + Sync + 'static,
1411 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1412 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1413 {
1414 router.mount(
1415 self.base_path(),
1416 self.version(),
1417 path,
1418 false,
1419 Method::CONNECT,
1420 description,
1421 handler,
1422 middlewares,
1423 );
1424 }
1425 fn mount_copy<T, F, Fut>(
1427 &self,
1428 router: &mut Router,
1429 path: &str,
1430 description: RouteDescription,
1431 handler: F,
1432 middlewares: Vec<Middleware>,
1433 ) where
1434 T: serde::Serialize + Send + Sync + 'static,
1435 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1436 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1437 {
1438 router.mount(
1439 self.base_path(),
1440 self.version(),
1441 path,
1442 false,
1443 Method::from_bytes(b"COPY").unwrap(),
1444 description,
1445 handler,
1446 middlewares,
1447 );
1448 }
1449 fn mount_move<T, F, Fut>(
1451 &self,
1452 router: &mut Router,
1453 path: &str,
1454 description: RouteDescription,
1455 handler: F,
1456 middlewares: Vec<Middleware>,
1457 ) where
1458 T: serde::Serialize + Send + Sync + 'static,
1459 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1460 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1461 {
1462 router.mount(
1463 self.base_path(),
1464 self.version(),
1465 path,
1466 false,
1467 Method::from_bytes(b"MOVE").unwrap(),
1468 description,
1469 handler,
1470 middlewares,
1471 );
1472 }
1473 fn mount_lock<T, F, Fut>(
1475 &self,
1476 router: &mut Router,
1477 path: &str,
1478 description: RouteDescription,
1479 handler: F,
1480 middlewares: Vec<Middleware>,
1481 ) where
1482 T: serde::Serialize + Send + Sync + 'static,
1483 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1484 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1485 {
1486 router.mount(
1487 self.base_path(),
1488 self.version(),
1489 path,
1490 true,
1491 Method::from_bytes(b"LOCK").unwrap(),
1492 description,
1493 handler,
1494 middlewares,
1495 );
1496 }
1497 fn mount_unlock<T, F, Fut>(
1499 &self,
1500 router: &mut Router,
1501 path: &str,
1502 description: RouteDescription,
1503 handler: F,
1504 middlewares: Vec<Middleware>,
1505 ) where
1506 T: serde::Serialize + Send + Sync + 'static,
1507 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1508 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1509 {
1510 router.mount(
1511 self.base_path(),
1512 self.version(),
1513 path,
1514 false,
1515 Method::from_bytes(b"UNLOCK").unwrap(),
1516 description,
1517 handler,
1518 middlewares,
1519 );
1520 }
1521 fn mount_propfind<T, F, Fut>(
1523 &self,
1524 router: &mut Router,
1525 path: &str,
1526 description: RouteDescription,
1527 handler: F,
1528 middlewares: Vec<Middleware>,
1529 ) where
1530 T: serde::Serialize + Send + Sync + 'static,
1531 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1532 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1533 {
1534 router.mount(
1535 self.base_path(),
1536 self.version(),
1537 path,
1538 true,
1539 Method::from_bytes(b"PROPFIND").unwrap(),
1540 description,
1541 handler,
1542 middlewares,
1543 );
1544 }
1545 fn mount_mkcol<T, F, Fut>(
1547 &self,
1548 router: &mut Router,
1549 path: &str,
1550 description: RouteDescription,
1551 handler: F,
1552 middlewares: Vec<Middleware>,
1553 ) where
1554 T: serde::Serialize + Send + Sync + 'static,
1555 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1556 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1557 {
1558 router.mount(
1559 self.base_path(),
1560 self.version(),
1561 path,
1562 false,
1563 Method::from_bytes(b"MKCOL").unwrap(),
1564 description,
1565 handler,
1566 middlewares,
1567 );
1568 }
1569 fn mount_search<T, F, Fut>(
1571 &self,
1572 router: &mut Router,
1573 path: &str,
1574 description: RouteDescription,
1575 handler: F,
1576 middlewares: Vec<Middleware>,
1577 ) where
1578 T: serde::Serialize + Send + Sync + 'static,
1579 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1580 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1581 {
1582 router.mount(
1583 self.base_path(),
1584 self.version(),
1585 path,
1586 true,
1587 Method::from_bytes(b"SEARCH").unwrap(),
1588 description,
1589 handler,
1590 middlewares,
1591 );
1592 }
1593 fn mount_report<T, F, Fut>(
1595 &self,
1596 router: &mut Router,
1597 path: &str,
1598 description: RouteDescription,
1599 handler: F,
1600 middlewares: Vec<Middleware>,
1601 ) where
1602 T: serde::Serialize + Send + Sync + 'static,
1603 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1604 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1605 {
1606 router.mount(
1607 self.base_path(),
1608 self.version(),
1609 path,
1610 true,
1611 Method::from_bytes(b"REPORT").unwrap(),
1612 description,
1613 handler,
1614 middlewares,
1615 );
1616 }
1617 fn mount_checkin<T, F, Fut>(
1619 &self,
1620 router: &mut Router,
1621 path: &str,
1622 description: RouteDescription,
1623 handler: F,
1624 middlewares: Vec<Middleware>,
1625 ) where
1626 T: serde::Serialize + Send + Sync + 'static,
1627 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1628 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1629 {
1630 router.mount(
1631 self.base_path(),
1632 self.version(),
1633 path,
1634 false,
1635 Method::from_bytes(b"CHECKIN").unwrap(),
1636 description,
1637 handler,
1638 middlewares,
1639 );
1640 }
1641 fn mount_checkout<T, F, Fut>(
1643 &self,
1644 router: &mut Router,
1645 path: &str,
1646 description: RouteDescription,
1647 handler: F,
1648 middlewares: Vec<Middleware>,
1649 ) where
1650 T: serde::Serialize + Send + Sync + 'static,
1651 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1652 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1653 {
1654 router.mount(
1655 self.base_path(),
1656 self.version(),
1657 path,
1658 false,
1659 Method::from_bytes(b"CHECKOUT").unwrap(),
1660 description,
1661 handler,
1662 middlewares,
1663 );
1664 }
1665 fn mount_uncheckout<T, F, Fut>(
1667 &self,
1668 router: &mut Router,
1669 path: &str,
1670 description: RouteDescription,
1671 handler: F,
1672 middlewares: Vec<Middleware>,
1673 ) where
1674 T: serde::Serialize + Send + Sync + 'static,
1675 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1676 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1677 {
1678 router.mount(
1679 self.base_path(),
1680 self.version(),
1681 path,
1682 false,
1683 Method::from_bytes(b"UNCHECKOUT").unwrap(),
1684 description,
1685 handler,
1686 middlewares,
1687 );
1688 }
1689 fn mount_merge<T, F, Fut>(
1691 &self,
1692 router: &mut Router,
1693 path: &str,
1694 description: RouteDescription,
1695 handler: F,
1696 middlewares: Vec<Middleware>,
1697 ) where
1698 T: serde::Serialize + Send + Sync + 'static,
1699 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1700 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1701 {
1702 router.mount(
1703 self.base_path(),
1704 self.version(),
1705 path,
1706 true,
1707 Method::from_bytes(b"MERGE").unwrap(),
1708 description,
1709 handler,
1710 middlewares,
1711 );
1712 }
1713 fn mount_acl<T, F, Fut>(
1715 &self,
1716 router: &mut Router,
1717 path: &str,
1718 description: RouteDescription,
1719 handler: F,
1720 middlewares: Vec<Middleware>,
1721 ) where
1722 T: serde::Serialize + Send + Sync + 'static,
1723 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1724 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1725 {
1726 router.mount(
1727 self.base_path(),
1728 self.version(),
1729 path,
1730 true,
1731 Method::from_bytes(b"ACL").unwrap(),
1732 description,
1733 handler,
1734 middlewares,
1735 );
1736 }
1737 fn mount_custom<T, F, Fut>(
1739 &self,
1740 router: &mut Router,
1741 path: &str,
1742 method: Method,
1743 description: RouteDescription,
1744 handler: F,
1745 middlewares: Vec<Middleware>,
1746 ) where
1747 T: serde::Serialize + Send + Sync + 'static,
1748 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1749 Fut: Future<Output = Result<ServiceResult<T>, ErrorResult>> + Send + 'static,
1750 {
1751 router.mount(
1752 self.base_path(),
1753 self.version(),
1754 path,
1755 false,
1756 method,
1757 description,
1758 handler,
1759 middlewares,
1760 );
1761 }
1762 fn mount_static(
1764 &self,
1765 router: &mut Router,
1766 path: &str,
1767 fs_path: String,
1768 middlewares: Vec<Middleware>,
1769 ) {
1770 router.mount_static(self.base_path(), self.version(), path, fs_path, middlewares);
1771 }
1772 fn mount_typed<F, Fut>(
1774 &self,
1775 router: &mut Router,
1776 path: &str,
1777 method: Method,
1778 description: RouteDescription,
1779 handler: F,
1780 middlewares: Vec<Middleware>,
1781 ) where
1782 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1783 Fut: Future<Output = Result<Box<dyn TypedServiceResult>, ErrorResult>> + Send + 'static,
1784 {
1785 router.mount_typed(
1786 self.base_path(),
1787 self.version(),
1788 path,
1789 method,
1790 description,
1791 handler,
1792 middlewares,
1793 );
1794 }
1795 fn mount_get_typed<F, Fut>(
1797 &self,
1798 router: &mut Router,
1799 path: &str,
1800 description: RouteDescription,
1801 handler: F,
1802 middlewares: Vec<Middleware>,
1803 ) where
1804 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1805 Fut: Future<Output = Result<Box<dyn TypedServiceResult>, ErrorResult>> + Send + 'static,
1806 {
1807 self.mount_typed(router, path, Method::GET, description, handler, middlewares);
1808 }
1809 fn mount_post_typed<F, Fut>(
1811 &self,
1812 router: &mut Router,
1813 path: &str,
1814 description: RouteDescription,
1815 handler: F,
1816 middlewares: Vec<Middleware>,
1817 ) where
1818 F: Fn(CorrelationContext) -> Fut + Send + Sync + 'static,
1819 Fut: Future<Output = Result<Box<dyn TypedServiceResult>, ErrorResult>> + Send + 'static,
1820 {
1821 self.mount_typed(
1822 router,
1823 path,
1824 Method::POST,
1825 description,
1826 handler,
1827 middlewares,
1828 );
1829 }
1830}
1831impl<T: RouteController> RouteControllerExt for T {}
1832
1833pub struct ConfigurationRegistrant {
1843 router: Arc<tokio::sync::RwLock<Router>>,
1844 addr: SocketAddr,
1845}
1846
1847impl ConfigurationRegistrant {
1848 pub fn new(addr: SocketAddr) -> Self {
1850 Self {
1851 router: Arc::new(tokio::sync::RwLock::new(Router::new())),
1852 addr,
1853 }
1854 }
1855
1856 pub async fn mount_controller<C: RouteController + 'static>(&self, controller: C) {
1858 let mut r = self.router.write().await;
1859 info!(
1860 target: "routing",
1861 handler = std::any::type_name::<C>(),
1862 path = %controller.base_path(),
1863 "Mounted controller '{}' at '{}'",
1864 std::any::type_name::<C>(),
1865 controller.base_path()
1866 );
1867 controller.register_routes(&mut r).await;
1868 }
1869
1870 pub async fn mount_middleware(&self, _mw: Middleware) {
1872 let mut r = self.router.write().await;
1873 r.global_middlewares.push(_mw);
1874 }
1875
1876 pub async fn serve(self: Arc<Self>) -> anyhow::Result<SocketAddr> {
1879 let listener = TcpListener::bind(self.addr).await?;
1880 let addr = listener.local_addr()?;
1881 info!(address = %addr, "HTTP server listening");
1882 let router = self.router.clone();
1883 tokio::spawn(async move {
1884 loop {
1885 let (stream, remote) = match listener.accept().await {
1886 Ok(v) => v,
1887 Err(e) => {
1888 warn!(error = %e, "Failed to accept HTTP connection");
1889 continue;
1890 }
1891 };
1892 let io = TokioIo::new(stream);
1893 let router = router.clone();
1894 tokio::spawn(async move {
1895 let svc = service_fn(move |req: Request<Incoming>| {
1896 let router = router.clone();
1897 let remote = remote;
1898 async move { handle_request(router, req, remote).await }
1899 });
1900 if let Err(e) = http1::Builder::new().serve_connection(io, svc).await {
1901 tracing::debug!(remote_addr = %remote, error = %e, "HTTP connection ended with an error");
1902 }
1903 });
1904 }
1905 });
1906 Ok(addr)
1907 }
1908
1909 pub fn router_handle(&self) -> Arc<tokio::sync::RwLock<Router>> {
1911 self.router.clone()
1912 }
1913}
1914
1915async fn handle_request(
1916 router: Arc<tokio::sync::RwLock<Router>>,
1917 req: Request<Incoming>,
1918 remote_addr: SocketAddr,
1919) -> Result<Response<String>, ErrorResult> {
1920 let request_started = std::time::Instant::now();
1921 let method = req.method().clone();
1922 let path = req.uri().path();
1923 let old_path = path;
1924 let path = urlencoding::decode(path);
1925 if path.is_err() {
1926 tracing::error!(
1927 "This should not be possible. Encountered an error while processing the URL {}",
1928 old_path
1929 );
1930 return Err(ErrorResult::bad_request("Invalid path parameter"));
1931 }
1932 let path = path.ok().unwrap().to_string();
1933 let query = req.uri().query().unwrap_or("").to_string();
1934 let headers = req.headers().clone();
1935 let (_parts, body) = req.into_parts();
1936 let body_bytes = match body.collect().await {
1937 Ok(collected) => collected.to_bytes().to_vec(),
1938 Err(e) => {
1939 warn!(remote_addr = %remote_addr, error = %e, "Failed to read request body");
1940 vec![]
1941 }
1942 };
1943
1944 let params: HashMap<String, String> = serde_urlencoded::from_str(&query).unwrap_or_default();
1945 let mut ctx = build_correlation_context(&headers, ¶ms, &body_bytes);
1946
1947 if let Some(ct) = headers
1951 .get(http::header::CONTENT_TYPE)
1952 .and_then(|v| v.to_str().ok())
1953 {
1954 if ct.to_lowercase().starts_with("multipart/form-data") {
1955 if let Some(boundary) =
1956 crate::utils::request_parser::RequestParser::multipart_boundary(ct)
1957 {
1958 let mp = crate::utils::request_parser::RequestParser::parse_multipart(
1959 &body_bytes,
1960 &boundary,
1961 );
1962 ctx.set_multipart(mp);
1963 }
1964 }
1965 }
1966 {
1967 let guard = router.read().await;
1968 for mw in &guard.global_middlewares {
1969 if let Err(e) = mw(&mut ctx).await {
1970 let resp = crate::response::error_response(&e, Arc::new(ctx.clone()));
1971 return Ok(finish_request(
1972 &method,
1973 &path,
1974 request_started,
1975 adapt_response(resp),
1976 ));
1977 }
1978 }
1979 }
1980
1981 let limit = ctx
1982 .query_param("limit")
1983 .and_then(|v| v.parse().ok())
1984 .unwrap_or(15usize);
1985 let cursor = ctx.query_param("cursor");
1986 ctx.set_pagination(cursor, limit);
1987
1988 let test_hook = {
1992 let guard = router.read().await;
1993 let has_header = headers.contains_key("x-moovable-test-client")
1994 || headers.contains_key("x-tm30-test-client");
1995 if has_header {
1996 guard.test_client_handler.clone()
1997 } else {
1998 None
1999 }
2000 };
2001 if let Some(hook) = test_hook {
2002 let resp = hook(
2003 ctx.clone(),
2004 headers.clone(),
2005 method.clone(),
2006 path.clone(),
2007 body_bytes,
2008 )
2009 .await;
2010 return Ok(finish_request(
2011 &method,
2012 &path,
2013 request_started,
2014 adapt_string_response(resp, Arc::new(ctx.clone())),
2015 ));
2016 }
2017
2018 let guard = router.read().await;
2019 if let Some((entry, params)) = guard.resolve(&method, &path) {
2020 for mw in &entry.middlewares {
2021 if let Err(e) = mw(&mut ctx).await {
2022 let resp = crate::response::error_response(&e, Arc::new(ctx.clone()));
2023 return Ok(finish_request(
2024 &method,
2025 &path,
2026 request_started,
2027 adapt_response(resp),
2028 ));
2029 }
2030 }
2031 ctx.set_params(params);
2032 let handler = entry.handler.clone();
2033 let path_clone = path.clone();
2034 let headers_clone = headers.clone();
2035 let method_clone = method.clone();
2036 drop(guard);
2037 let resp = handler(
2038 ctx.clone(),
2039 headers_clone,
2040 method_clone,
2041 path_clone,
2042 body_bytes,
2043 )
2044 .await;
2045 Ok(finish_request(
2046 &method,
2047 &path,
2048 request_started,
2049 adapt_string_response(resp, Arc::new(ctx.clone())),
2050 ))
2051 } else {
2052 drop(guard);
2053 let err = ErrorResult::not_found(format!("The requested resource was not found: {path}"));
2054 let resp = crate::response::error_response(&err, Arc::new(ctx.clone()));
2055 Ok(finish_request(
2056 &method,
2057 &path,
2058 request_started,
2059 adapt_response(resp),
2060 ))
2061 }
2062}
2063
2064fn finish_request(
2065 method: &Method,
2066 path: &str,
2067 started: std::time::Instant,
2068 response: Response<String>,
2069) -> Response<String> {
2070 crate::middleware::monitoring::RequestLogger::log(
2071 path,
2072 method.as_str(),
2073 response.status().as_u16(),
2074 started.elapsed(),
2075 );
2076 response
2077}
2078
2079fn build_correlation_context(
2080 headers: &HeaderMap,
2081 query: &HashMap<String, String>,
2082 body: &[u8],
2083) -> CorrelationContext {
2084 let corr_id = headers
2085 .get("x-correlation-id")
2086 .and_then(|v| v.to_str().ok())
2087 .unwrap_or("");
2088 let flow_str = headers
2089 .get("x-correlation-flow")
2090 .and_then(|v| v.to_str().ok())
2091 .unwrap_or("ONCE");
2092 let flow = flow_str
2093 .parse()
2094 .unwrap_or(crate::logging::CorrelationFlow::Once);
2095 let ctx = if corr_id.is_empty() {
2096 CorrelationContext::new()
2097 } else {
2098 CorrelationContext::with_ids(corr_id, &hex::encode(rand::random::<[u8; 8]>()))
2099 };
2100 ctx.set_flow(flow);
2101 ctx.set_headers(headers.clone());
2104 ctx.set_query_params(query.clone());
2105 ctx.set_body(body.to_vec());
2106 let req_id = headers.get("x-request-id").and_then(|v| v.to_str().ok());
2107 if let Some(rid) = req_id {
2108 ctx.set_request_id(rid);
2109 } else {
2110 let rid = hex::encode(rand::random::<[u8; 8]>());
2111 ctx.set_request_id(&rid);
2112 }
2113 ctx
2114}
2115
2116fn adapt_response(r: Response<String>) -> Response<String> {
2117 r
2118}
2119fn adapt_string_response(
2120 mut r: Response<String>,
2121 ctx: Arc<CorrelationContext>,
2122) -> Response<String> {
2123 let headers = r.headers_mut();
2124 headers
2125 .entry("x-request-id")
2126 .or_insert(ctx.request_id().parse().unwrap());
2127 headers
2128 .entry("x-correlation-id")
2129 .or_insert(ctx.correlation_id().parse().unwrap());
2130 headers
2131 .entry("x-correlation-flow")
2132 .or_insert(ctx.flow().to_string().parse().unwrap());
2133 r
2134}
2135
2136pub fn json_response<T: serde::Serialize + Send + Sync>(
2139 result: ServiceResult<T>,
2140 ctx: Arc<CorrelationContext>,
2141) -> Response<String> {
2142 crate::response::build_response(&result, ctx)
2143}
2144
2145pub fn typed_response(
2148 result: &dyn TypedServiceResult,
2149 ctx: Arc<CorrelationContext>,
2150) -> Response<String> {
2151 let body = result.serialize().unwrap_or_else(|e| {
2152 ErrorResult::from_error(&anyhow::anyhow!(e.to_string()), 500)
2153 .message
2154 .clone()
2155 });
2156 let mut builder = Response::builder()
2157 .status(result.code())
2158 .header("X-Request-ID", ctx.request_id())
2159 .header("X-Correlation-ID", ctx.correlation_id())
2160 .header("X-Correlation-Flow", ctx.flow().to_string());
2161 let ct = match result.response_type() {
2162 ResponseType::Json => "application/json",
2163 ResponseType::File => {
2164 let filename = body.rsplit('/').next().unwrap_or("file");
2165 builder = builder.header(
2166 "Content-Disposition",
2167 format!("attachment; filename=\"{}\"", filename),
2168 );
2169 "application/octet-stream"
2170 }
2171 ResponseType::Xml => "application/xml",
2172 ResponseType::Javascript => "application/javascript",
2173 ResponseType::Html => "text/html",
2174 ResponseType::Text => "text/plain",
2175 };
2176 builder = builder.header("Content-Type", ct);
2177 builder.body(body).unwrap()
2178}