1use async_trait::async_trait;
45use reinhardt_core::exception::{Error, Result};
46use std::any::{Any, TypeId};
47use std::sync::Arc;
48
49use crate::exception::ExceptionHandler;
50use crate::{Request, Response};
51
52pub type MiddlewareDiRegistration = (TypeId, Arc<dyn Any + Send + Sync>);
60
61#[async_trait]
66pub trait Handler: Send + Sync {
67 async fn handle(&self, request: Request) -> Result<Response>;
73}
74
75#[async_trait]
80impl<T: Handler + ?Sized> Handler for Arc<T> {
81 async fn handle(&self, request: Request) -> Result<Response> {
82 (**self).handle(request).await
83 }
84}
85
86#[async_trait]
92pub trait Middleware: Send + Sync {
93 async fn process(&self, request: Request, next: Arc<dyn Handler>) -> Result<Response>;
104
105 fn should_continue(&self, _request: &Request) -> bool {
127 true
128 }
129
130 fn di_registrations(&self) -> Vec<MiddlewareDiRegistration> {
164 Vec::new()
165 }
166}
167
168pub struct MiddlewareChain {
173 middlewares: Vec<Arc<dyn Middleware>>,
174 handler: Arc<dyn Handler>,
175 exception_handler: Option<Arc<dyn ExceptionHandler>>,
178}
179
180impl MiddlewareChain {
181 pub fn new(handler: Arc<dyn Handler>) -> Self {
202 Self {
203 middlewares: Vec::new(),
204 handler,
205 exception_handler: None,
206 }
207 }
208
209 pub fn with_middlewares(
211 handler: Arc<dyn Handler>,
212 middlewares: Vec<Arc<dyn Middleware>>,
213 ) -> Self {
214 Self {
215 middlewares,
216 handler,
217 exception_handler: None,
218 }
219 }
220
221 pub fn with_exception_handler(mut self, exception_handler: Arc<dyn ExceptionHandler>) -> Self {
257 self.exception_handler = Some(exception_handler);
258 self
259 }
260
261 pub fn with_middleware(mut self, middleware: Arc<dyn Middleware>) -> Self {
289 self.middlewares.push(middleware);
290 self
291 }
292
293 pub fn add_middleware(&mut self, middleware: Arc<dyn Middleware>) {
321 self.middlewares.push(middleware);
322 }
323}
324
325#[async_trait]
326impl Handler for MiddlewareChain {
327 async fn handle(&self, mut request: Request) -> Result<Response> {
328 let exception_handler = self
329 .exception_handler
330 .clone()
331 .or_else(|| request.extensions.get::<Arc<dyn ExceptionHandler>>());
332 if let Some(handler) = &exception_handler {
333 request.install_exception_handler(Arc::clone(handler));
334 }
335 if self.middlewares.is_empty() {
340 let Some(exception_handler) = exception_handler.as_ref() else {
341 return self.handler.handle(request).await;
342 };
343 let mut context = request.clone_for_di();
344 return match self.handler.handle(request).await {
345 Ok(response) => Ok(response),
346 Err(e) => {
347 context.sync_path_params_from_shared_state();
348 context.extensions.insert(crate::ExceptionHandlerInvoked);
349 Ok(exception_handler.handle_exception(&context, e).await)
350 }
351 };
352 }
353
354 if self.middlewares.len() == 1 {
355 let middleware = &self.middlewares[0];
356 if !middleware.should_continue(&request) {
357 let mut context = capture_exception_context(exception_handler.as_ref(), &request);
358 return match self.handler.handle(request).await {
359 Ok(response) => Ok(response),
360 Err(e) => {
361 refresh_exception_context(&mut context);
362 Ok(convert_error(exception_handler.as_ref(), context.as_ref(), e).await)
363 }
364 };
365 }
366
367 let mut context = capture_exception_context(exception_handler.as_ref(), &request);
368 let next: Arc<dyn Handler> = Arc::new(ErrorToResponseHandler {
369 inner: self.handler.clone(),
370 exception_handler: exception_handler.clone(),
371 });
372 let response = match middleware.process(request, next).await {
373 Ok(response) => response,
374 Err(e) => {
375 refresh_exception_context(&mut context);
376 convert_error(exception_handler.as_ref(), context.as_ref(), e).await
377 }
378 };
379 return Ok(response);
380 }
381
382 let mut current_handler: Arc<dyn Handler> = Arc::new(ErrorToResponseHandler {
393 inner: self.handler.clone(),
394 exception_handler: exception_handler.clone(),
395 });
396
397 for middleware in self
398 .middlewares
399 .iter()
400 .rev()
401 .filter(|mw| mw.should_continue(&request))
402 {
403 let mw = middleware.clone();
404 let handler = current_handler.clone();
405
406 current_handler = Arc::new(ConditionalComposedHandler {
407 middleware: mw,
408 next: handler,
409 exception_handler: exception_handler.clone(),
410 });
411 }
412
413 current_handler.handle(request).await
414 }
415}
416
417pub struct ExcludeMiddleware {
457 inner: Arc<dyn Middleware>,
458 exclusions: Vec<String>,
459}
460
461impl ExcludeMiddleware {
462 pub fn new(inner: Arc<dyn Middleware>) -> Self {
464 Self {
465 inner,
466 exclusions: Vec::new(),
467 }
468 }
469
470 pub fn add_exclusion(mut self, pattern: &str) -> Self {
474 self.exclusions.push(pattern.to_string());
475 self
476 }
477
478 pub fn add_exclusion_mut(&mut self, pattern: &str) {
482 self.exclusions.push(pattern.to_string());
483 }
484
485 fn is_excluded(&self, path: &str) -> bool {
487 self.exclusions.iter().any(|pattern| {
488 if pattern.ends_with('/') {
489 path.starts_with(pattern.as_str())
491 } else {
492 path == pattern
494 }
495 })
496 }
497}
498
499#[async_trait]
500impl Middleware for ExcludeMiddleware {
501 async fn process(&self, request: Request, next: Arc<dyn Handler>) -> Result<Response> {
502 self.inner.process(request, next).await
503 }
504
505 fn should_continue(&self, request: &Request) -> bool {
506 if self.is_excluded(request.uri.path()) {
507 return false;
508 }
509 self.inner.should_continue(request)
510 }
511}
512
513fn capture_exception_context(
520 exception_handler: Option<&Arc<dyn ExceptionHandler>>,
521 request: &Request,
522) -> Option<Request> {
523 exception_handler?;
525 Some(request.clone_for_di())
526}
527
528fn refresh_exception_context(context: &mut Option<Request>) {
529 if let Some(context) = context.as_mut() {
530 context.sync_path_params_from_shared_state();
531 }
532}
533
534async fn convert_error(
538 exception_handler: Option<&Arc<dyn ExceptionHandler>>,
539 context: Option<&Request>,
540 error: Error,
541) -> Response {
542 match (exception_handler, context) {
543 (Some(handler), Some(context)) => {
544 context.extensions.insert(crate::ExceptionHandlerInvoked);
545 handler.handle_exception(context, error).await
546 }
547 _ => Response::from(error),
548 }
549}
550
551struct ErrorToResponseHandler {
558 inner: Arc<dyn Handler>,
559 exception_handler: Option<Arc<dyn ExceptionHandler>>,
560}
561
562#[async_trait]
563impl Handler for ErrorToResponseHandler {
564 async fn handle(&self, request: Request) -> Result<Response> {
565 let mut context = capture_exception_context(self.exception_handler.as_ref(), &request);
566 match self.inner.handle(request).await {
567 Ok(response) => Ok(response),
568 Err(e) => {
569 if let Some(context) = context.as_mut() {
570 context.sync_path_params_from_shared_state();
571 }
572 Ok(convert_error(self.exception_handler.as_ref(), context.as_ref(), e).await)
573 }
574 }
575 }
576}
577
578struct ConditionalComposedHandler {
583 middleware: Arc<dyn Middleware>,
584 next: Arc<dyn Handler>,
585 exception_handler: Option<Arc<dyn ExceptionHandler>>,
586}
587
588#[async_trait]
589impl Handler for ConditionalComposedHandler {
590 async fn handle(&self, request: Request) -> Result<Response> {
591 let mut context = capture_exception_context(self.exception_handler.as_ref(), &request);
596 let response = match self.middleware.process(request, self.next.clone()).await {
597 Ok(response) => response,
598 Err(e) => {
599 refresh_exception_context(&mut context);
600 convert_error(self.exception_handler.as_ref(), context.as_ref(), e).await
601 }
602 };
603
604 Ok(response)
605 }
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611 use bytes::Bytes;
612 use hyper::{HeaderMap, Method, Version};
613 use rstest::rstest;
614
615 struct MockHandler {
617 response_body: String,
618 }
619
620 #[async_trait]
621 impl Handler for MockHandler {
622 async fn handle(&self, _request: Request) -> Result<Response> {
623 Ok(Response::ok().with_body(self.response_body.clone()))
624 }
625 }
626
627 struct MockMiddleware {
629 prefix: String,
630 }
631
632 #[async_trait]
633 impl Middleware for MockMiddleware {
634 async fn process(&self, request: Request, next: Arc<dyn Handler>) -> Result<Response> {
635 let response = next.handle(request).await?;
637
638 let current_body = String::from_utf8(response.body.to_vec()).unwrap_or_default();
640 let new_body = format!("{}{}", self.prefix, current_body);
641
642 Ok(Response::ok().with_body(new_body))
643 }
644 }
645
646 fn create_test_request() -> Request {
647 Request::builder()
648 .method(Method::GET)
649 .uri("/")
650 .version(Version::HTTP_11)
651 .headers(HeaderMap::new())
652 .body(Bytes::new())
653 .build()
654 .unwrap()
655 }
656
657 #[tokio::test]
658 async fn test_handler_basic() {
659 let handler = MockHandler {
660 response_body: "Hello".to_string(),
661 };
662
663 let request = create_test_request();
664 let response = handler.handle(request).await.unwrap();
665
666 let body = String::from_utf8(response.body.to_vec()).unwrap();
667 assert_eq!(body, "Hello");
668 }
669
670 #[tokio::test]
671 async fn test_middleware_basic() {
672 let handler = Arc::new(MockHandler {
673 response_body: "World".to_string(),
674 });
675
676 let middleware = MockMiddleware {
677 prefix: "Hello, ".to_string(),
678 };
679
680 let request = create_test_request();
681 let response = middleware.process(request, handler).await.unwrap();
682
683 let body = String::from_utf8(response.body.to_vec()).unwrap();
684 assert_eq!(body, "Hello, World");
685 }
686
687 #[tokio::test]
688 async fn test_middleware_chain_empty() {
689 let handler = Arc::new(MockHandler {
690 response_body: "Test".to_string(),
691 });
692
693 let chain = MiddlewareChain::new(handler);
694
695 let request = create_test_request();
696 let response = chain.handle(request).await.unwrap();
697
698 let body = String::from_utf8(response.body.to_vec()).unwrap();
699 assert_eq!(body, "Test");
700 }
701
702 #[tokio::test]
703 async fn test_middleware_chain_single() {
704 let handler = Arc::new(MockHandler {
705 response_body: "Handler".to_string(),
706 });
707
708 let middleware1 = Arc::new(MockMiddleware {
709 prefix: "MW1:".to_string(),
710 });
711
712 let chain = MiddlewareChain::new(handler).with_middleware(middleware1);
713
714 let request = create_test_request();
715 let response = chain.handle(request).await.unwrap();
716
717 let body = String::from_utf8(response.body.to_vec()).unwrap();
718 assert_eq!(body, "MW1:Handler");
719 }
720
721 #[tokio::test]
722 async fn test_middleware_chain_multiple() {
723 let handler = Arc::new(MockHandler {
724 response_body: "Data".to_string(),
725 });
726
727 let middleware1 = Arc::new(MockMiddleware {
728 prefix: "M1:".to_string(),
729 });
730
731 let middleware2 = Arc::new(MockMiddleware {
732 prefix: "M2:".to_string(),
733 });
734
735 let chain = MiddlewareChain::new(handler)
736 .with_middleware(middleware1)
737 .with_middleware(middleware2);
738
739 let request = create_test_request();
740 let response = chain.handle(request).await.unwrap();
741
742 let body = String::from_utf8(response.body.to_vec()).unwrap();
743 assert_eq!(body, "M1:M2:Data");
745 }
746
747 #[tokio::test]
748 async fn test_middleware_chain_add_middleware() {
749 let handler = Arc::new(MockHandler {
750 response_body: "Result".to_string(),
751 });
752
753 let middleware = Arc::new(MockMiddleware {
754 prefix: "Prefix:".to_string(),
755 });
756
757 let mut chain = MiddlewareChain::new(handler);
758 chain.add_middleware(middleware);
759
760 let request = create_test_request();
761 let response = chain.handle(request).await.unwrap();
762
763 let body = String::from_utf8(response.body.to_vec()).unwrap();
764 assert_eq!(body, "Prefix:Result");
765 }
766
767 struct ConditionalMiddleware {
769 prefix: String,
770 }
771
772 #[async_trait]
773 impl Middleware for ConditionalMiddleware {
774 async fn process(&self, request: Request, next: Arc<dyn Handler>) -> Result<Response> {
775 let response = next.handle(request).await?;
776 let current_body = String::from_utf8(response.body.to_vec()).unwrap_or_default();
777 let new_body = format!("{}{}", self.prefix, current_body);
778 Ok(Response::ok().with_body(new_body))
779 }
780
781 fn should_continue(&self, request: &Request) -> bool {
782 request.uri.path().starts_with("/api/")
783 }
784 }
785
786 #[tokio::test]
787 async fn test_middleware_conditional_skip() {
788 let handler = Arc::new(MockHandler {
789 response_body: "Response".to_string(),
790 });
791
792 let conditional_mw = Arc::new(ConditionalMiddleware {
793 prefix: "API:".to_string(),
794 });
795
796 let chain = MiddlewareChain::new(handler).with_middleware(conditional_mw);
797
798 let api_request = Request::builder()
800 .method(Method::GET)
801 .uri("/api/users")
802 .version(Version::HTTP_11)
803 .headers(HeaderMap::new())
804 .body(Bytes::new())
805 .build()
806 .unwrap();
807 let response = chain.handle(api_request).await.unwrap();
808 let body = String::from_utf8(response.body.to_vec()).unwrap();
809 assert_eq!(body, "API:Response");
810
811 let non_api_request = Request::builder()
813 .method(Method::GET)
814 .uri("/public")
815 .version(Version::HTTP_11)
816 .headers(HeaderMap::new())
817 .body(Bytes::new())
818 .build()
819 .unwrap();
820 let response = chain.handle(non_api_request).await.unwrap();
821 let body = String::from_utf8(response.body.to_vec()).unwrap();
822 assert_eq!(body, "Response"); }
824
825 struct ShortCircuitMiddleware {
827 should_stop: bool,
828 }
829
830 #[async_trait]
831 impl Middleware for ShortCircuitMiddleware {
832 async fn process(&self, request: Request, next: Arc<dyn Handler>) -> Result<Response> {
833 if self.should_stop {
834 return Ok(Response::unauthorized()
836 .with_body("Auth required")
837 .with_stop_chain(true));
838 }
839 next.handle(request).await
840 }
841 }
842
843 #[tokio::test]
844 async fn test_middleware_short_circuit() {
845 let handler = Arc::new(MockHandler {
846 response_body: "Handler Response".to_string(),
847 });
848
849 let short_circuit_mw = Arc::new(ShortCircuitMiddleware { should_stop: true });
850 let normal_mw = Arc::new(MockMiddleware {
851 prefix: "Normal:".to_string(),
852 });
853
854 let chain = MiddlewareChain::new(handler)
855 .with_middleware(short_circuit_mw)
856 .with_middleware(normal_mw);
857
858 let request = create_test_request();
859 let response = chain.handle(request).await.unwrap();
860
861 assert_eq!(response.status, hyper::StatusCode::UNAUTHORIZED);
863 let body = String::from_utf8(response.body.to_vec()).unwrap();
864 assert_eq!(body, "Auth required");
865 }
866
867 #[tokio::test]
868 async fn test_middleware_no_short_circuit() {
869 let handler = Arc::new(MockHandler {
870 response_body: "Handler Response".to_string(),
871 });
872
873 let short_circuit_mw = Arc::new(ShortCircuitMiddleware { should_stop: false });
874 let normal_mw = Arc::new(MockMiddleware {
875 prefix: "Normal:".to_string(),
876 });
877
878 let chain = MiddlewareChain::new(handler)
879 .with_middleware(short_circuit_mw)
880 .with_middleware(normal_mw);
881
882 let request = create_test_request();
883 let response = chain.handle(request).await.unwrap();
884
885 assert_eq!(response.status, hyper::StatusCode::OK);
887 let body = String::from_utf8(response.body.to_vec()).unwrap();
888 assert_eq!(body, "Normal:Handler Response");
889 }
890
891 #[tokio::test]
892 async fn test_middleware_multiple_conditions() {
893 let handler = Arc::new(MockHandler {
894 response_body: "Base".to_string(),
895 });
896
897 let api_mw = Arc::new(ConditionalMiddleware {
899 prefix: "API:".to_string(),
900 });
901
902 let always_mw = Arc::new(MockMiddleware {
904 prefix: "Always:".to_string(),
905 });
906
907 let chain = MiddlewareChain::new(handler)
908 .with_middleware(api_mw)
909 .with_middleware(always_mw);
910
911 let api_request = Request::builder()
913 .method(Method::GET)
914 .uri("/api/test")
915 .version(Version::HTTP_11)
916 .headers(HeaderMap::new())
917 .body(Bytes::new())
918 .build()
919 .unwrap();
920 let response = chain.handle(api_request).await.unwrap();
921 let body = String::from_utf8(response.body.to_vec()).unwrap();
922 assert_eq!(body, "API:Always:Base");
923
924 let non_api_request = Request::builder()
926 .method(Method::GET)
927 .uri("/public")
928 .version(Version::HTTP_11)
929 .headers(HeaderMap::new())
930 .body(Bytes::new())
931 .build()
932 .unwrap();
933 let response = chain.handle(non_api_request).await.unwrap();
934 let body = String::from_utf8(response.body.to_vec()).unwrap();
935 assert_eq!(body, "Always:Base"); }
937
938 #[tokio::test]
939 async fn test_response_should_stop_chain() {
940 let response = Response::ok();
941 assert!(!response.should_stop_chain());
942
943 let stopping_response = Response::unauthorized().with_stop_chain(true);
944 assert!(stopping_response.should_stop_chain());
945 }
946
947 fn create_request_with_path(path: &str) -> Request {
950 Request::builder()
951 .method(Method::GET)
952 .uri(path)
953 .version(Version::HTTP_11)
954 .headers(HeaderMap::new())
955 .body(Bytes::new())
956 .build()
957 .unwrap()
958 }
959
960 #[rstest::rstest]
961 #[case("/api/auth/login", true)]
962 #[case("/api/auth/register", true)]
963 #[case("/api/auth/", true)]
964 #[case("/api/users", false)]
965 #[case("/public", false)]
966 fn test_exclude_middleware_prefix_match(#[case] path: &str, #[case] should_exclude: bool) {
967 let inner: Arc<dyn Middleware> = Arc::new(MockMiddleware {
969 prefix: "MW:".to_string(),
970 });
971 let exclude_mw = ExcludeMiddleware::new(inner).add_exclusion("/api/auth/");
972
973 let request = create_request_with_path(path);
975 let result = exclude_mw.should_continue(&request);
976
977 assert_eq!(result, !should_exclude);
979 }
980
981 #[rstest::rstest]
982 #[case("/health", true)]
983 #[case("/health/check", false)]
984 #[case("/healthz", false)]
985 #[case("/api/health", false)]
986 fn test_exclude_middleware_exact_match(#[case] path: &str, #[case] should_exclude: bool) {
987 let inner: Arc<dyn Middleware> = Arc::new(MockMiddleware {
989 prefix: "MW:".to_string(),
990 });
991 let exclude_mw = ExcludeMiddleware::new(inner).add_exclusion("/health");
992
993 let request = create_request_with_path(path);
995 let result = exclude_mw.should_continue(&request);
996
997 assert_eq!(result, !should_exclude);
999 }
1000
1001 #[rstest::rstest]
1002 fn test_exclude_middleware_no_match_passes_through() {
1003 let inner: Arc<dyn Middleware> = Arc::new(MockMiddleware {
1005 prefix: "MW:".to_string(),
1006 });
1007 let exclude_mw = ExcludeMiddleware::new(inner)
1008 .add_exclusion("/api/auth/")
1009 .add_exclusion("/health");
1010
1011 let request = create_request_with_path("/api/users");
1013 let result = exclude_mw.should_continue(&request);
1014
1015 assert!(result);
1017 }
1018
1019 #[rstest::rstest]
1020 #[tokio::test]
1021 async fn test_exclude_middleware_delegates_process() {
1022 let inner: Arc<dyn Middleware> = Arc::new(MockMiddleware {
1024 prefix: "INNER:".to_string(),
1025 });
1026 let exclude_mw = ExcludeMiddleware::new(inner).add_exclusion("/excluded/");
1027
1028 let handler = Arc::new(MockHandler {
1029 response_body: "Response".to_string(),
1030 });
1031
1032 let request = create_request_with_path("/api/test");
1034 let response = exclude_mw.process(request, handler).await.unwrap();
1035
1036 let body = String::from_utf8(response.body.to_vec()).unwrap();
1038 assert_eq!(body, "INNER:Response");
1039 }
1040
1041 #[rstest::rstest]
1042 fn test_exclude_middleware_multiple_exclusions() {
1043 let inner: Arc<dyn Middleware> = Arc::new(MockMiddleware {
1045 prefix: "MW:".to_string(),
1046 });
1047 let mut exclude_mw = ExcludeMiddleware::new(inner);
1048 exclude_mw.add_exclusion_mut("/api/auth/");
1049 exclude_mw.add_exclusion_mut("/admin/");
1050 exclude_mw.add_exclusion_mut("/health");
1051
1052 assert!(!exclude_mw.should_continue(&create_request_with_path("/api/auth/login")));
1054 assert!(!exclude_mw.should_continue(&create_request_with_path("/admin/dashboard")));
1055 assert!(!exclude_mw.should_continue(&create_request_with_path("/health")));
1056 assert!(exclude_mw.should_continue(&create_request_with_path("/api/users")));
1057 }
1058
1059 #[rstest::rstest]
1060 fn test_exclude_middleware_respects_inner_should_continue() {
1061 let inner: Arc<dyn Middleware> = Arc::new(ConditionalMiddleware {
1063 prefix: "API:".to_string(),
1064 });
1065 let exclude_mw = ExcludeMiddleware::new(inner).add_exclusion("/api/auth/");
1066
1067 assert!(!exclude_mw.should_continue(&create_request_with_path("/api/auth/login")));
1070 assert!(!exclude_mw.should_continue(&create_request_with_path("/public")));
1072 assert!(exclude_mw.should_continue(&create_request_with_path("/api/users")));
1074 }
1075
1076 struct NotFoundHandler;
1082
1083 #[async_trait]
1084 impl Handler for NotFoundHandler {
1085 async fn handle(&self, _request: Request) -> Result<Response> {
1086 Err(reinhardt_core::exception::Error::NotFound(
1087 "not found".into(),
1088 ))
1089 }
1090 }
1091
1092 struct UnauthorizedHandler;
1093
1094 #[async_trait]
1095 impl Handler for UnauthorizedHandler {
1096 async fn handle(&self, _request: Request) -> Result<Response> {
1097 Err(reinhardt_core::exception::Error::Authentication(
1098 "unauthorized".into(),
1099 ))
1100 }
1101 }
1102
1103 struct HeaderAddingMiddleware {
1105 header_name: &'static str,
1106 header_value: &'static str,
1107 }
1108
1109 #[async_trait]
1110 impl Middleware for HeaderAddingMiddleware {
1111 async fn process(&self, request: Request, next: Arc<dyn Handler>) -> Result<Response> {
1112 let response = next.handle(request).await?;
1113 Ok(response.with_header(self.header_name, self.header_value))
1114 }
1115 }
1116
1117 struct RejectingMiddleware;
1119
1120 #[async_trait]
1121 impl Middleware for RejectingMiddleware {
1122 async fn process(&self, _request: Request, _next: Arc<dyn Handler>) -> Result<Response> {
1123 Err(reinhardt_core::exception::Error::Authorization(
1124 "CSRF check failed".into(),
1125 ))
1126 }
1127 }
1128
1129 #[rstest::rstest]
1130 #[tokio::test]
1131 async fn test_chain_post_processing_runs_on_handler_error() {
1132 let handler: Arc<dyn Handler> = Arc::new(NotFoundHandler);
1134 let mut chain = MiddlewareChain::new(handler);
1135 chain.add_middleware(Arc::new(HeaderAddingMiddleware {
1136 header_name: "X-Custom-Security",
1137 header_value: "applied",
1138 }));
1139
1140 let request = create_test_request();
1142 let response = chain.handle(request).await.unwrap();
1143
1144 assert_eq!(response.status, hyper::StatusCode::NOT_FOUND);
1146 assert_eq!(
1147 response
1148 .headers
1149 .get("X-Custom-Security")
1150 .map(|v| v.to_str().unwrap()),
1151 Some("applied")
1152 );
1153 }
1154
1155 #[rstest::rstest]
1156 #[tokio::test]
1157 async fn test_chain_post_processing_runs_on_middleware_error() {
1158 let handler = Arc::new(MockHandler {
1161 response_body: "OK".into(),
1162 });
1163 let mut chain = MiddlewareChain::new(handler);
1164 chain.add_middleware(Arc::new(HeaderAddingMiddleware {
1166 header_name: "X-Frame-Options",
1167 header_value: "DENY",
1168 }));
1169 chain.add_middleware(Arc::new(RejectingMiddleware));
1171
1172 let request = create_test_request();
1174 let response = chain.handle(request).await.unwrap();
1175
1176 assert_eq!(response.status, hyper::StatusCode::FORBIDDEN);
1178 assert_eq!(
1179 response
1180 .headers
1181 .get("X-Frame-Options")
1182 .map(|v| v.to_str().unwrap()),
1183 Some("DENY")
1184 );
1185 }
1186
1187 struct PassthroughMiddleware;
1189
1190 #[async_trait]
1191 impl Middleware for PassthroughMiddleware {
1192 async fn process(&self, request: Request, next: Arc<dyn Handler>) -> Result<Response> {
1193 next.handle(request).await
1194 }
1195 }
1196
1197 #[rstest::rstest]
1198 #[tokio::test]
1199 async fn test_chain_error_preserves_correct_status_code() {
1200 let handler: Arc<dyn Handler> = Arc::new(UnauthorizedHandler);
1203 let mut chain = MiddlewareChain::new(handler);
1204 chain.add_middleware(Arc::new(PassthroughMiddleware));
1205
1206 let request = create_test_request();
1208 let response = chain.handle(request).await.unwrap();
1209
1210 assert_eq!(response.status, hyper::StatusCode::UNAUTHORIZED);
1212 }
1213
1214 struct FailingMiddleware;
1220
1221 #[async_trait]
1222 impl Middleware for FailingMiddleware {
1223 async fn process(&self, _request: Request, _next: Arc<dyn Handler>) -> Result<Response> {
1224 Err(Error::Http("rejected by middleware".to_string()))
1225 }
1226 }
1227
1228 struct ErroringHandler;
1230
1231 #[async_trait]
1232 impl Handler for ErroringHandler {
1233 async fn handle(&self, _request: Request) -> Result<Response> {
1234 Err(Error::NotFound("no route".to_string()))
1235 }
1236 }
1237
1238 struct TeapotHandler;
1240
1241 #[async_trait]
1242 impl ExceptionHandler for TeapotHandler {
1243 async fn handle_exception(&self, _request: &Request, _error: Error) -> Response {
1244 Response::new(hyper::StatusCode::IM_A_TEAPOT).with_body("teapot")
1245 }
1246 }
1247
1248 #[rstest]
1249 #[tokio::test]
1250 async fn test_chain_exception_handler_converts_middleware_error() {
1251 let chain = MiddlewareChain::new(Arc::new(MockHandler {
1253 response_body: "unused".to_string(),
1254 }))
1255 .with_middleware(Arc::new(FailingMiddleware))
1256 .with_exception_handler(Arc::new(TeapotHandler));
1257
1258 let response = chain.handle(create_test_request()).await.unwrap();
1260
1261 assert_eq!(response.status, hyper::StatusCode::IM_A_TEAPOT);
1263 assert_eq!(String::from_utf8(response.body.to_vec()).unwrap(), "teapot");
1264 }
1265
1266 #[rstest]
1267 #[tokio::test]
1268 async fn test_chain_without_exception_handler_keeps_default_middleware_error() {
1269 let chain = MiddlewareChain::new(Arc::new(MockHandler {
1271 response_body: "unused".to_string(),
1272 }))
1273 .with_middleware(Arc::new(FailingMiddleware));
1274
1275 let response = chain.handle(create_test_request()).await.unwrap();
1277
1278 assert_eq!(response.status, hyper::StatusCode::BAD_REQUEST);
1280 }
1281
1282 #[rstest]
1283 #[tokio::test]
1284 async fn test_chain_exception_handler_converts_base_handler_error() {
1285 let chain = MiddlewareChain::new(Arc::new(ErroringHandler))
1287 .with_middleware(Arc::new(PassthroughMiddleware))
1288 .with_exception_handler(Arc::new(TeapotHandler));
1289
1290 let response = chain.handle(create_test_request()).await.unwrap();
1292
1293 assert_eq!(response.status, hyper::StatusCode::IM_A_TEAPOT);
1295 }
1296
1297 #[rstest]
1298 #[tokio::test]
1299 async fn test_bare_chain_with_exception_handler_converts_base_handler_error() {
1300 let chain = MiddlewareChain::new(Arc::new(ErroringHandler))
1302 .with_exception_handler(Arc::new(TeapotHandler));
1303
1304 let response = chain.handle(create_test_request()).await.unwrap();
1306
1307 assert_eq!(response.status, hyper::StatusCode::IM_A_TEAPOT);
1309 assert_eq!(String::from_utf8(response.body.to_vec()).unwrap(), "teapot");
1310 }
1311
1312 #[rstest]
1313 #[tokio::test]
1314 async fn test_bare_chain_without_exception_handler_propagates_error() {
1315 let chain = MiddlewareChain::new(Arc::new(ErroringHandler));
1317
1318 let result = chain.handle(create_test_request()).await;
1320
1321 assert!(result.is_err());
1323 }
1324}