1use crate::request::{ElifMethod, ElifRequest};
7use crate::response::ElifResponse;
8use std::collections::HashMap;
9use std::future::Future;
10use std::pin::Pin;
11use std::sync::{Arc, Mutex};
12use std::time::Duration;
13pub type NextFuture<'a> = Pin<Box<dyn Future<Output = ElifResponse> + Send + 'a>>;
18
19pub struct Next {
21 handler: Box<dyn FnOnce(ElifRequest) -> NextFuture<'static> + Send>,
22}
23
24impl Next {
25 pub fn new<F>(handler: F) -> Self
27 where
28 F: FnOnce(ElifRequest) -> NextFuture<'static> + Send + 'static,
29 {
30 Self {
31 handler: Box::new(handler),
32 }
33 }
34
35 pub async fn run(self, request: ElifRequest) -> ElifResponse {
37 (self.handler)(request).await
38 }
39
40 pub fn call(self, request: ElifRequest) -> NextFuture<'static> {
43 Box::pin(async move { self.run(request).await })
44 }
45}
46
47pub trait Middleware: Send + Sync + std::fmt::Debug {
50 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static>;
52
53 fn name(&self) -> &'static str {
55 "Middleware"
56 }
57}
58
59#[derive(Debug)]
61pub struct MiddlewarePipelineV2 {
62 middleware: Vec<Arc<dyn Middleware>>,
63}
64
65impl Default for MiddlewarePipelineV2 {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl MiddlewarePipelineV2 {
72 pub fn new() -> Self {
74 Self {
75 middleware: Vec::new(),
76 }
77 }
78
79 pub fn add<M: Middleware + 'static>(mut self, middleware: M) -> Self {
81 self.middleware.push(Arc::new(middleware));
82 self
83 }
84
85 pub fn add_mut<M: Middleware + 'static>(&mut self, middleware: M) {
87 self.middleware.push(Arc::new(middleware));
88 }
89
90 pub fn from_middleware_vec(middleware: Vec<Arc<dyn Middleware>>) -> Self {
92 Self { middleware }
93 }
94
95 pub fn add_boxed(mut self, middleware: Arc<dyn Middleware>) -> Self {
97 self.middleware.push(middleware);
98 self
99 }
100
101 pub fn extend(mut self, other: Self) -> Self {
104 self.middleware.extend(other.middleware);
105 self
106 }
107
108 pub async fn execute<F, Fut>(&self, request: ElifRequest, handler: F) -> ElifResponse
110 where
111 F: FnOnce(ElifRequest) -> Fut + Send + 'static,
112 Fut: Future<Output = ElifResponse> + Send + 'static,
113 {
114 let mut chain =
115 Box::new(move |req: ElifRequest| Box::pin(handler(req)) as NextFuture<'static>)
116 as Box<dyn FnOnce(ElifRequest) -> NextFuture<'static> + Send>;
117
118 for middleware in self.middleware.iter().rev() {
119 let middleware = middleware.clone();
120 let next_handler = chain;
121 chain = Box::new(move |req: ElifRequest| {
122 let next = Next::new(next_handler);
123 middleware.handle(req, next)
124 });
125 }
126
127 chain(request).await
128 }
129
130 pub fn len(&self) -> usize {
132 self.middleware.len()
133 }
134
135 pub fn is_empty(&self) -> bool {
137 self.middleware.is_empty()
138 }
139
140 pub fn names(&self) -> Vec<&'static str> {
142 self.middleware.iter().map(|m| m.name()).collect()
143 }
144}
145
146impl Clone for MiddlewarePipelineV2 {
147 fn clone(&self) -> Self {
148 Self {
149 middleware: self.middleware.clone(),
150 }
151 }
152}
153
154impl From<Vec<Arc<dyn Middleware>>> for MiddlewarePipelineV2 {
155 fn from(middleware: Vec<Arc<dyn Middleware>>) -> Self {
156 Self { middleware }
157 }
158}
159
160pub struct ConditionalMiddleware<M> {
164 middleware: M,
165 skip_paths: Vec<String>,
166 only_methods: Option<Vec<ElifMethod>>,
167 condition: Option<Arc<dyn Fn(&ElifRequest) -> bool + Send + Sync>>,
168}
169
170impl<M: std::fmt::Debug> std::fmt::Debug for ConditionalMiddleware<M> {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 f.debug_struct("ConditionalMiddleware")
173 .field("middleware", &self.middleware)
174 .field("skip_paths", &self.skip_paths)
175 .field("only_methods", &self.only_methods)
176 .field("condition", &self.condition.as_ref().map(|_| "Some(Fn)"))
177 .finish()
178 }
179}
180
181impl<M> ConditionalMiddleware<M> {
182 pub fn new(middleware: M) -> Self {
183 Self {
184 middleware,
185 skip_paths: Vec::new(),
186 only_methods: None,
187 condition: None,
188 }
189 }
190
191 pub fn skip_paths(mut self, paths: Vec<&str>) -> Self {
194 self.skip_paths = paths.into_iter().map(|s| s.to_string()).collect();
195 self
196 }
197
198 pub fn only_methods(mut self, methods: Vec<ElifMethod>) -> Self {
200 self.only_methods = Some(methods);
201 self
202 }
203
204 pub fn condition<F>(mut self, condition: F) -> Self
206 where
207 F: Fn(&ElifRequest) -> bool + Send + Sync + 'static,
208 {
209 self.condition = Some(Arc::new(condition));
210 self
211 }
212
213 fn should_skip_path(&self, path: &str) -> bool {
215 for pattern in &self.skip_paths {
216 if Self::path_matches(path, pattern) {
217 return true;
218 }
219 }
220 false
221 }
222
223 fn path_matches(path: &str, pattern: &str) -> bool {
225 if let Some((prefix, suffix)) = pattern.split_once('*') {
226 path.starts_with(prefix) && path.ends_with(suffix)
227 } else {
228 path == pattern
229 }
230 }
231
232 fn should_execute(&self, request: &ElifRequest) -> bool {
234 if self.should_skip_path(request.path()) {
236 return false;
237 }
238
239 if let Some(ref allowed_methods) = self.only_methods {
241 if !allowed_methods.contains(&request.method) {
242 return false;
243 }
244 }
245
246 if let Some(ref condition) = self.condition {
248 if !condition(request) {
249 return false;
250 }
251 }
252
253 true
254 }
255}
256
257impl<M: Middleware> Middleware for ConditionalMiddleware<M> {
258 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
259 if self.should_execute(&request) {
260 self.middleware.handle(request, next)
262 } else {
263 Box::pin(async move { next.run(request).await })
265 }
266 }
267
268 fn name(&self) -> &'static str {
269 "ConditionalMiddleware"
270 }
271}
272
273pub mod factories {
275 use super::*;
276 use std::time::Duration;
277
278 pub fn rate_limit(requests_per_minute: u32) -> RateLimitMiddleware {
280 RateLimitMiddleware::new()
281 .limit(requests_per_minute)
282 .window(Duration::from_secs(60))
283 }
284
285 pub fn rate_limit_with_window(requests: u32, window: Duration) -> RateLimitMiddleware {
287 RateLimitMiddleware::new().limit(requests).window(window)
288 }
289
290 pub fn bearer_auth(token: String) -> SimpleAuthMiddleware {
292 SimpleAuthMiddleware::new(token)
293 }
294
295 pub fn cors() -> CorsMiddleware {
297 CorsMiddleware::new()
298 }
299
300 pub fn cors_with_origins(origins: Vec<String>) -> CorsMiddleware {
302 CorsMiddleware::new().allow_origins(origins)
303 }
304
305 pub fn timeout(duration: Duration) -> TimeoutMiddleware {
307 TimeoutMiddleware::new(duration)
308 }
309
310 pub fn body_limit(max_bytes: u64) -> BodyLimitMiddleware {
312 BodyLimitMiddleware::new(max_bytes)
313 }
314
315 pub fn profiler() -> ProfilerMiddleware {
317 ProfilerMiddleware::new()
318 }
319
320 pub fn profiler_disabled() -> ProfilerMiddleware {
322 ProfilerMiddleware::disabled()
323 }
324}
325
326pub mod composition {
328 use super::*;
329
330 pub fn compose<M1, M2>(first: M1, second: M2) -> MiddlewarePipelineV2
332 where
333 M1: Middleware + 'static,
334 M2: Middleware + 'static,
335 {
336 MiddlewarePipelineV2::new().add(first).add(second)
337 }
338
339 pub fn chain<M1, M2>(first: M1, second: M2) -> MiddlewarePipelineV2
341 where
342 M1: Middleware + 'static,
343 M2: Middleware + 'static,
344 {
345 compose(first, second)
346 }
347
348 pub fn group(middleware: Vec<Arc<dyn Middleware>>) -> MiddlewarePipelineV2 {
350 MiddlewarePipelineV2::from(middleware)
351 }
352
353 pub fn compose3<M1, M2, M3>(first: M1, second: M2, third: M3) -> MiddlewarePipelineV2
355 where
356 M1: Middleware + 'static,
357 M2: Middleware + 'static,
358 M3: Middleware + 'static,
359 {
360 MiddlewarePipelineV2::new()
361 .add(first)
362 .add(second)
363 .add(third)
364 }
365
366 pub fn compose4<M1, M2, M3, M4>(
368 first: M1,
369 second: M2,
370 third: M3,
371 fourth: M4,
372 ) -> MiddlewarePipelineV2
373 where
374 M1: Middleware + 'static,
375 M2: Middleware + 'static,
376 M3: Middleware + 'static,
377 M4: Middleware + 'static,
378 {
379 MiddlewarePipelineV2::new()
380 .add(first)
381 .add(second)
382 .add(third)
383 .add(fourth)
384 }
385}
386
387pub struct ComposedMiddleware<M1, M2> {
389 first: M1,
390 second: M2,
391}
392
393impl<M1: std::fmt::Debug, M2: std::fmt::Debug> std::fmt::Debug for ComposedMiddleware<M1, M2> {
394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
395 f.debug_struct("ComposedMiddleware")
396 .field("first", &self.first)
397 .field("second", &self.second)
398 .finish()
399 }
400}
401
402impl<M1, M2> ComposedMiddleware<M1, M2> {
403 pub fn new(first: M1, second: M2) -> Self {
404 Self { first, second }
405 }
406}
407
408impl<M1, M2> ComposedMiddleware<M1, M2>
411where
412 M1: Middleware + 'static,
413 M2: Middleware + 'static,
414{
415 pub fn to_pipeline(self) -> MiddlewarePipelineV2 {
417 MiddlewarePipelineV2::new().add(self.first).add(self.second)
418 }
419}
420
421pub mod introspection {
423 use super::*;
424 use std::time::Instant;
425
426 #[derive(Debug, Clone)]
428 pub struct MiddlewareStats {
429 pub name: String,
430 pub executions: u64,
431 pub total_time: Duration,
432 pub avg_time: Duration,
433 pub last_execution: Option<Instant>,
434 }
435
436 impl MiddlewareStats {
437 pub fn new(name: String) -> Self {
438 Self {
439 name,
440 executions: 0,
441 total_time: Duration::ZERO,
442 avg_time: Duration::ZERO,
443 last_execution: None,
444 }
445 }
446
447 pub fn record_execution(&mut self, duration: Duration) {
448 self.executions += 1;
449 self.total_time += duration;
450 self.avg_time =
452 Duration::from_nanos((self.total_time.as_nanos() / self.executions as u128) as u64);
453 self.last_execution = Some(Instant::now());
454 }
455 }
456
457 #[derive(Debug, Clone)]
459 pub struct PipelineInfo {
460 pub middleware_count: usize,
461 pub middleware_names: Vec<String>,
462 pub execution_order: Vec<String>,
463 }
464
465 impl MiddlewarePipelineV2 {
466 pub fn debug_info(&self) -> PipelineInfo {
468 PipelineInfo {
469 middleware_count: self.len(),
470 middleware_names: self.names().into_iter().map(|s| s.to_string()).collect(),
471 execution_order: self.names().into_iter().map(|s| s.to_string()).collect(),
472 }
473 }
474
475 pub fn with_debug(self) -> DebugPipeline {
477 DebugPipeline::new(self)
478 }
479 }
480
481 #[derive(Debug)]
483 pub struct DebugPipeline {
484 pipeline: MiddlewarePipelineV2,
485 stats: Arc<Mutex<HashMap<String, MiddlewareStats>>>,
486 }
487
488 impl DebugPipeline {
489 pub fn new(pipeline: MiddlewarePipelineV2) -> Self {
490 let mut stats = HashMap::new();
491 for name in pipeline.names() {
492 stats.insert(name.to_string(), MiddlewareStats::new(name.to_string()));
493 }
494
495 Self {
496 pipeline,
497 stats: Arc::new(Mutex::new(stats)),
498 }
499 }
500
501 pub fn stats(&self) -> HashMap<String, MiddlewareStats> {
503 self.stats
504 .lock()
505 .expect("Stats mutex should not be poisoned")
506 .clone()
507 }
508
509 pub fn middleware_stats(&self, name: &str) -> Option<MiddlewareStats> {
511 self.stats
512 .lock()
513 .expect("Stats mutex should not be poisoned")
514 .get(name)
515 .cloned()
516 }
517
518 pub fn reset_stats(&self) {
520 let mut stats = self
521 .stats
522 .lock()
523 .expect("Stats mutex should not be poisoned");
524 for (name, stat) in stats.iter_mut() {
525 *stat = MiddlewareStats::new(name.clone());
526 }
527 }
528
529 pub async fn execute_debug<F, Fut>(
531 &self,
532 request: ElifRequest,
533 handler: F,
534 ) -> (ElifResponse, Duration)
535 where
536 F: FnOnce(ElifRequest) -> Fut + Send + 'static,
537 Fut: Future<Output = ElifResponse> + Send + 'static,
538 {
539 let start_time = Instant::now();
540 let response = self.pipeline.execute(request, handler).await;
541 let total_duration = start_time.elapsed();
542
543 (response, total_duration)
544 }
545 }
546
547 #[derive(Debug)]
549 pub struct InstrumentedMiddleware<M> {
550 middleware: M,
551 name: String,
552 stats: Arc<Mutex<MiddlewareStats>>,
553 }
554
555 impl<M> InstrumentedMiddleware<M> {
556 pub fn new(middleware: M, name: String) -> Self {
557 let stats = Arc::new(Mutex::new(MiddlewareStats::new(name.clone())));
558 Self {
559 middleware,
560 name,
561 stats,
562 }
563 }
564
565 pub fn stats(&self) -> MiddlewareStats {
567 self.stats
568 .lock()
569 .expect("Middleware stats mutex should not be poisoned")
570 .clone()
571 }
572
573 pub fn reset_stats(&self) {
575 let mut stats = self
576 .stats
577 .lock()
578 .expect("Middleware stats mutex should not be poisoned");
579 *stats = MiddlewareStats::new(self.name.clone());
580 }
581 }
582
583 impl<M: Middleware> Middleware for InstrumentedMiddleware<M> {
584 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
585 let stats = self.stats.clone();
586 let middleware_result = self.middleware.handle(request, next);
587
588 Box::pin(async move {
589 let start = Instant::now();
590 let response = middleware_result.await;
591 let duration = start.elapsed();
592
593 stats
594 .lock()
595 .expect("Middleware stats mutex should not be poisoned")
596 .record_execution(duration);
597 response
598 })
599 }
600
601 fn name(&self) -> &'static str {
602 "InstrumentedMiddleware"
603 }
604 }
605
606 pub fn instrument<M: Middleware + 'static>(
608 middleware: M,
609 name: String,
610 ) -> InstrumentedMiddleware<M> {
611 InstrumentedMiddleware::new(middleware, name)
612 }
613}
614
615pub struct RateLimitMiddleware {
617 requests_per_window: u32,
618 window: Duration,
619 requests: Arc<Mutex<HashMap<String, (std::time::Instant, u32)>>>,
621 last_cleanup: Arc<Mutex<std::time::Instant>>,
623}
624
625impl std::fmt::Debug for RateLimitMiddleware {
626 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
627 f.debug_struct("RateLimitMiddleware")
628 .field("requests_per_window", &self.requests_per_window)
629 .field("window", &self.window)
630 .field("last_cleanup", &"<Mutex<Instant>>")
631 .finish()
632 }
633}
634
635impl Default for RateLimitMiddleware {
636 fn default() -> Self {
637 Self::new()
638 }
639}
640
641impl RateLimitMiddleware {
642 pub fn new() -> Self {
643 let now = std::time::Instant::now();
644 Self {
645 requests_per_window: 60, window: Duration::from_secs(60),
647 requests: Arc::new(Mutex::new(HashMap::new())),
648 last_cleanup: Arc::new(Mutex::new(now)),
649 }
650 }
651
652 pub fn limit(mut self, requests: u32) -> Self {
653 self.requests_per_window = requests;
654 self
655 }
656
657 pub fn window(mut self, window: Duration) -> Self {
658 self.window = window;
659 self
660 }
661
662 fn get_client_id(&self, request: &ElifRequest) -> String {
663 request
665 .header("x-forwarded-for")
666 .and_then(|h| h.to_str().ok())
667 .unwrap_or("unknown")
668 .to_string()
669 }
670
671 fn is_rate_limited(&self, client_id: &str) -> bool {
672 let now = std::time::Instant::now();
673
674 const CLEANUP_INTERVAL: Duration = Duration::from_secs(30);
677
678 {
679 let mut last_cleanup = self
680 .last_cleanup
681 .lock()
682 .expect("Cleanup time mutex should not be poisoned");
683 if now.duration_since(*last_cleanup) > CLEANUP_INTERVAL {
684 let mut requests = self
686 .requests
687 .lock()
688 .expect("Rate limiter mutex should not be poisoned");
689 requests.retain(|_, (timestamp, _)| now.duration_since(*timestamp) < self.window);
690 *last_cleanup = now;
691 drop(requests);
693 }
694 }
695
696 let mut requests = self
698 .requests
699 .lock()
700 .expect("Rate limiter mutex should not be poisoned");
701
702 if let Some((timestamp, count)) = requests.get_mut(client_id) {
704 if now.duration_since(*timestamp) < self.window {
705 if *count >= self.requests_per_window {
706 return true; }
708 *count += 1;
709 } else {
710 *timestamp = now;
712 *count = 1;
713 }
714 } else {
715 requests.insert(client_id.to_string(), (now, 1));
717 }
718
719 false
720 }
721}
722
723impl Middleware for RateLimitMiddleware {
724 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
725 let client_id = self.get_client_id(&request);
726 let is_limited = self.is_rate_limited(&client_id);
727
728 Box::pin(async move {
729 if is_limited {
730 ElifResponse::with_status(
731 crate::response::status::ElifStatusCode::TOO_MANY_REQUESTS,
732 )
733 .json_value(serde_json::json!({
734 "error": {
735 "code": "rate_limited",
736 "message": "Too many requests. Please try again later."
737 }
738 }))
739 } else {
740 next.run(request).await
741 }
742 })
743 }
744
745 fn name(&self) -> &'static str {
746 "RateLimitMiddleware"
747 }
748}
749
750#[derive(Debug)]
752pub struct CorsMiddleware {
753 allowed_origins: Vec<String>,
754 allowed_methods: Vec<String>,
755 allowed_headers: Vec<String>,
756}
757
758impl Default for CorsMiddleware {
759 fn default() -> Self {
760 Self::new()
761 }
762}
763
764impl CorsMiddleware {
765 pub fn new() -> Self {
766 Self {
767 allowed_origins: vec!["*".to_string()],
768 allowed_methods: vec![
769 "GET".to_string(),
770 "POST".to_string(),
771 "PUT".to_string(),
772 "DELETE".to_string(),
773 "OPTIONS".to_string(),
774 ],
775 allowed_headers: vec!["Content-Type".to_string(), "Authorization".to_string()],
776 }
777 }
778
779 pub fn allow_origins(mut self, origins: Vec<String>) -> Self {
780 self.allowed_origins = origins;
781 self
782 }
783
784 pub fn allow_methods(mut self, methods: Vec<String>) -> Self {
785 self.allowed_methods = methods;
786 self
787 }
788
789 pub fn allow_headers(mut self, headers: Vec<String>) -> Self {
790 self.allowed_headers = headers;
791 self
792 }
793}
794
795impl Middleware for CorsMiddleware {
796 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
797 let allowed_origins = self.allowed_origins.clone();
798 let allowed_methods = self.allowed_methods.clone();
799 let allowed_headers = self.allowed_headers.clone();
800
801 Box::pin(async move {
802 if request.method == ElifMethod::OPTIONS {
804 let mut preflight_response = ElifResponse::ok();
805 let _ = preflight_response
807 .add_header("Access-Control-Allow-Origin", allowed_origins.join(","));
808 let _ = preflight_response
809 .add_header("Access-Control-Allow-Methods", allowed_methods.join(","));
810 let _ = preflight_response
811 .add_header("Access-Control-Allow-Headers", allowed_headers.join(","));
812 return preflight_response;
813 }
814
815 let mut response = next.run(request).await;
816
817 let _ = response.add_header("Access-Control-Allow-Origin", allowed_origins.join(","));
819 let _ = response.add_header("Access-Control-Allow-Methods", allowed_methods.join(","));
820 let _ = response.add_header("Access-Control-Allow-Headers", allowed_headers.join(","));
821
822 response
823 })
824 }
825
826 fn name(&self) -> &'static str {
827 "CorsMiddleware"
828 }
829}
830
831#[derive(Debug)]
833pub struct TimeoutMiddleware {
834 timeout: Duration,
835}
836
837impl TimeoutMiddleware {
838 pub fn new(timeout: Duration) -> Self {
839 Self { timeout }
840 }
841}
842
843impl Middleware for TimeoutMiddleware {
844 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
845 let timeout = self.timeout;
846 Box::pin(async move {
847 match tokio::time::timeout(timeout, next.run(request)).await {
848 Ok(response) => response,
849 Err(_) => ElifResponse::with_status(
850 crate::response::status::ElifStatusCode::REQUEST_TIMEOUT,
851 )
852 .json_value(serde_json::json!({
853 "error": {
854 "code": "timeout",
855 "message": "Request timed out"
856 }
857 })),
858 }
859 })
860 }
861
862 fn name(&self) -> &'static str {
863 "TimeoutMiddleware"
864 }
865}
866
867#[derive(Debug)]
869pub struct BodyLimitMiddleware {
870 max_bytes: u64,
871}
872
873impl BodyLimitMiddleware {
874 pub fn new(max_bytes: u64) -> Self {
875 Self { max_bytes }
876 }
877}
878
879impl Middleware for BodyLimitMiddleware {
880 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
881 let max_bytes = self.max_bytes;
882 Box::pin(async move {
883 if let Some(body) = request.body_bytes() {
885 if body.len() as u64 > max_bytes {
886 return ElifResponse::with_status(crate::response::status::ElifStatusCode::PAYLOAD_TOO_LARGE)
887 .json_value(serde_json::json!({
888 "error": {
889 "code": "payload_too_large",
890 "message": format!("Request body too large. Maximum allowed: {} bytes", max_bytes)
891 }
892 }));
893 }
894 }
895
896 next.run(request).await
897 })
898 }
899
900 fn name(&self) -> &'static str {
901 "BodyLimitMiddleware"
902 }
903}
904
905#[derive(Debug)]
907pub struct LoggingMiddleware;
908
909impl Middleware for LoggingMiddleware {
910 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
911 Box::pin(async move {
912 let start = std::time::Instant::now();
914 let method = request.method.clone();
915 let path = request.path().to_string();
916
917 let response = next.run(request).await;
919
920 let duration = start.elapsed();
922 println!(
923 "{} {} - {} - {:?}",
924 method,
925 path,
926 response.status_code(),
927 duration
928 );
929
930 response
931 })
932 }
933
934 fn name(&self) -> &'static str {
935 "LoggingMiddleware"
936 }
937}
938
939#[derive(Debug)]
941pub struct ProfilerMiddleware {
942 enabled: bool,
943}
944
945impl Default for ProfilerMiddleware {
946 fn default() -> Self {
947 Self::new()
948 }
949}
950
951impl ProfilerMiddleware {
952 pub fn new() -> Self {
953 Self { enabled: true }
954 }
955
956 pub fn disabled() -> Self {
957 Self { enabled: false }
958 }
959}
960
961impl Middleware for ProfilerMiddleware {
962 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
963 let enabled = self.enabled;
964 Box::pin(async move {
965 if !enabled {
966 return next.run(request).await;
967 }
968
969 let start = std::time::Instant::now();
970 let method = request.method.clone();
971 let path = request.path().to_string();
972
973 println!("⏱️ [PROFILER] Starting request {} {}", method, path);
974
975 let response = next.run(request).await;
976
977 let duration = start.elapsed();
978 println!(
979 "⏱️ [PROFILER] Completed {} {} in {:?} - Status: {}",
980 method,
981 path,
982 duration,
983 response.status_code()
984 );
985
986 response
987 })
988 }
989
990 fn name(&self) -> &'static str {
991 "ProfilerMiddleware"
992 }
993}
994
995#[derive(Debug)]
997pub struct SimpleAuthMiddleware {
998 required_token: String,
999}
1000
1001impl SimpleAuthMiddleware {
1002 pub fn new(token: String) -> Self {
1003 Self {
1004 required_token: token,
1005 }
1006 }
1007}
1008
1009impl Middleware for SimpleAuthMiddleware {
1010 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
1011 let required_token = self.required_token.clone();
1012 Box::pin(async move {
1013 let token = match request.header("Authorization") {
1015 Some(h) => match h.to_str() {
1016 Ok(header_str) if header_str.starts_with("Bearer ") => &header_str[7..],
1017 _ => {
1018 return ElifResponse::unauthorized().json_value(serde_json::json!({
1019 "error": {
1020 "code": "unauthorized",
1021 "message": "Missing or invalid authorization header"
1022 }
1023 }));
1024 }
1025 },
1026 None => {
1027 return ElifResponse::unauthorized().json_value(serde_json::json!({
1028 "error": {
1029 "code": "unauthorized",
1030 "message": "Missing authorization header"
1031 }
1032 }));
1033 }
1034 };
1035
1036 if token != required_token {
1038 return ElifResponse::unauthorized().json_value(serde_json::json!({
1039 "error": {
1040 "code": "unauthorized",
1041 "message": "Invalid token"
1042 }
1043 }));
1044 }
1045
1046 next.run(request).await
1048 })
1049 }
1050
1051 fn name(&self) -> &'static str {
1052 "SimpleAuthMiddleware"
1053 }
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058 use super::*;
1059 use crate::request::ElifRequest;
1060 use crate::response::ElifResponse;
1061
1062 #[derive(Debug)]
1064 pub struct TestMiddleware {
1065 name: &'static str,
1066 }
1067
1068 impl TestMiddleware {
1069 pub fn new(name: &'static str) -> Self {
1070 Self { name }
1071 }
1072 }
1073
1074 impl Middleware for TestMiddleware {
1075 fn handle(&self, mut request: ElifRequest, next: Next) -> NextFuture<'static> {
1076 let name = self.name;
1077 Box::pin(async move {
1078 let header_name = crate::response::headers::ElifHeaderName::from_str(&format!(
1080 "x-middleware-{}",
1081 name.to_lowercase()
1082 ))
1083 .unwrap();
1084 let header_value =
1085 crate::response::headers::ElifHeaderValue::from_str("executed").unwrap();
1086 request.headers.insert(header_name, header_value);
1087
1088 let response = next.run(request).await;
1089
1090 response
1092 })
1093 }
1094
1095 fn name(&self) -> &'static str {
1096 self.name
1097 }
1098 }
1099
1100 #[tokio::test]
1101 async fn test_simple_middleware_execution() {
1102 let pipeline = MiddlewarePipelineV2::new()
1103 .add(TestMiddleware::new("First"))
1104 .add(TestMiddleware::new("Second"));
1105
1106 let request = ElifRequest::new(
1107 crate::request::ElifMethod::GET,
1108 "/test".parse().unwrap(),
1109 crate::response::headers::ElifHeaderMap::new(),
1110 );
1111
1112 let response = pipeline
1113 .execute(request, |req| {
1114 Box::pin(async move {
1115 assert!(
1117 req.headers.contains_key(
1118 &crate::response::headers::ElifHeaderName::from_str(
1119 "x-middleware-first"
1120 )
1121 .unwrap()
1122 ),
1123 "First middleware should have added header"
1124 );
1125 assert!(
1126 req.headers.contains_key(
1127 &crate::response::headers::ElifHeaderName::from_str(
1128 "x-middleware-second"
1129 )
1130 .unwrap()
1131 ),
1132 "Second middleware should have added header"
1133 );
1134
1135 ElifResponse::ok().text("Hello World")
1136 })
1137 })
1138 .await;
1139
1140 assert_eq!(
1141 response.status_code(),
1142 crate::response::status::ElifStatusCode::OK
1143 );
1144 }
1145
1146 #[tokio::test]
1147 async fn test_middleware_chain_execution_order() {
1148 #[derive(Debug)]
1150 struct OrderTestMiddleware {
1151 name: &'static str,
1152 }
1153
1154 impl OrderTestMiddleware {
1155 fn new(name: &'static str) -> Self {
1156 Self { name }
1157 }
1158 }
1159
1160 impl Middleware for OrderTestMiddleware {
1161 fn handle(&self, mut request: ElifRequest, next: Next) -> NextFuture<'static> {
1162 let name = self.name;
1163 Box::pin(async move {
1164 let header_name_str = format!("x-before-{}", name.to_lowercase());
1166 let header_name =
1167 crate::response::headers::ElifHeaderName::from_str(&header_name_str)
1168 .unwrap();
1169 let header_value =
1170 crate::response::headers::ElifHeaderValue::from_str("executed").unwrap();
1171 request.headers.insert(header_name, header_value);
1172
1173 let response = next.run(request).await;
1175
1176 let response_header = format!("x-after-{}", name.to_lowercase());
1178 response.header(&response_header, "executed").unwrap_or(
1179 ElifResponse::ok().text("fallback"),
1181 )
1182 })
1183 }
1184
1185 fn name(&self) -> &'static str {
1186 self.name
1187 }
1188 }
1189
1190 let pipeline = MiddlewarePipelineV2::new()
1192 .add(OrderTestMiddleware::new("First"))
1193 .add(OrderTestMiddleware::new("Second"))
1194 .add(OrderTestMiddleware::new("Third"));
1195
1196 let request = ElifRequest::new(
1197 crate::request::ElifMethod::GET,
1198 "/test".parse().unwrap(),
1199 crate::response::headers::ElifHeaderMap::new(),
1200 );
1201
1202 let response = pipeline
1203 .execute(request, |req| {
1204 Box::pin(async move {
1205 assert!(req.headers.contains_key(
1207 &crate::response::headers::ElifHeaderName::from_str("x-before-first")
1208 .unwrap()
1209 ));
1210 assert!(req.headers.contains_key(
1211 &crate::response::headers::ElifHeaderName::from_str("x-before-second")
1212 .unwrap()
1213 ));
1214 assert!(req.headers.contains_key(
1215 &crate::response::headers::ElifHeaderName::from_str("x-before-third")
1216 .unwrap()
1217 ));
1218
1219 ElifResponse::ok().text("Handler executed")
1220 })
1221 })
1222 .await;
1223
1224 assert_eq!(
1226 response.status_code(),
1227 crate::response::status::ElifStatusCode::OK
1228 );
1229
1230 let axum_response = response.into_axum_response();
1232 let (parts, _body) = axum_response.into_parts();
1233 assert!(parts.headers.contains_key("x-after-first"));
1234 assert!(parts.headers.contains_key("x-after-second"));
1235 assert!(parts.headers.contains_key("x-after-third"));
1236
1237 assert_eq!(pipeline.len(), 3);
1239 assert_eq!(pipeline.names(), vec!["First", "Second", "Third"]);
1240 }
1241
1242 #[tokio::test]
1243 async fn test_auth_middleware() {
1244 let auth_middleware = SimpleAuthMiddleware::new("secret123".to_string());
1245
1246 let mut headers = crate::response::headers::ElifHeaderMap::new();
1248 headers.insert(
1249 crate::response::headers::ElifHeaderName::from_str("authorization").unwrap(),
1250 "Bearer secret123".parse().unwrap(),
1251 );
1252 let request = ElifRequest::new(
1253 crate::request::ElifMethod::GET,
1254 "/protected".parse().unwrap(),
1255 headers,
1256 );
1257
1258 let next =
1259 Next::new(|_req| Box::pin(async { ElifResponse::ok().text("Protected content") }));
1260
1261 let response = auth_middleware.handle(request, next).await;
1262 assert_eq!(
1263 response.status_code(),
1264 crate::response::status::ElifStatusCode::OK
1265 );
1266
1267 let mut headers = crate::response::headers::ElifHeaderMap::new();
1269 headers.insert(
1270 crate::response::headers::ElifHeaderName::from_str("authorization").unwrap(),
1271 "Bearer invalid".parse().unwrap(),
1272 );
1273 let request = ElifRequest::new(
1274 crate::request::ElifMethod::GET,
1275 "/protected".parse().unwrap(),
1276 headers,
1277 );
1278
1279 let next =
1280 Next::new(|_req| Box::pin(async { ElifResponse::ok().text("Protected content") }));
1281
1282 let response = auth_middleware.handle(request, next).await;
1283 assert_eq!(
1284 response.status_code(),
1285 crate::response::status::ElifStatusCode::UNAUTHORIZED
1286 );
1287 }
1288
1289 #[tokio::test]
1290 async fn test_pipeline_info() {
1291 let pipeline = MiddlewarePipelineV2::new()
1292 .add(TestMiddleware::new("Test1"))
1293 .add(TestMiddleware::new("Test2"));
1294
1295 assert_eq!(pipeline.len(), 2);
1296 assert!(!pipeline.is_empty());
1297 assert_eq!(pipeline.names(), vec!["Test1", "Test2"]);
1298
1299 let empty_pipeline = MiddlewarePipelineV2::new();
1300 assert_eq!(empty_pipeline.len(), 0);
1301 assert!(empty_pipeline.is_empty());
1302 }
1303
1304 #[tokio::test]
1307 async fn test_conditional_middleware_skip_paths() {
1308 let base_middleware = TestMiddleware::new("Conditional");
1309 let conditional =
1310 ConditionalMiddleware::new(base_middleware).skip_paths(vec!["/public/*", "/health"]);
1311
1312 let pipeline = MiddlewarePipelineV2::new().add(conditional);
1313
1314 let request1 = ElifRequest::new(
1316 ElifMethod::GET,
1317 "/public/assets/style.css".parse().unwrap(),
1318 crate::response::headers::ElifHeaderMap::new(),
1319 );
1320
1321 let response1 = pipeline
1322 .execute(request1, |req| {
1323 Box::pin(async move {
1324 assert!(!req.headers.contains_key(
1326 &crate::response::headers::ElifHeaderName::from_str(
1327 "x-middleware-conditional"
1328 )
1329 .unwrap()
1330 ));
1331 ElifResponse::ok().text("OK")
1332 })
1333 })
1334 .await;
1335
1336 assert_eq!(
1337 response1.status_code(),
1338 crate::response::status::ElifStatusCode::OK
1339 );
1340
1341 let request2 = ElifRequest::new(
1343 ElifMethod::GET,
1344 "/api/users".parse().unwrap(),
1345 crate::response::headers::ElifHeaderMap::new(),
1346 );
1347
1348 let response2 = pipeline
1349 .execute(request2, |req| {
1350 Box::pin(async move {
1351 assert!(req.headers.contains_key(
1353 &crate::response::headers::ElifHeaderName::from_str(
1354 "x-middleware-conditional"
1355 )
1356 .unwrap()
1357 ));
1358 ElifResponse::ok().text("OK")
1359 })
1360 })
1361 .await;
1362
1363 assert_eq!(
1364 response2.status_code(),
1365 crate::response::status::ElifStatusCode::OK
1366 );
1367 }
1368
1369 #[tokio::test]
1370 async fn test_conditional_middleware_only_methods() {
1371 let base_middleware = TestMiddleware::new("MethodConditional");
1372 let conditional = ConditionalMiddleware::new(base_middleware)
1373 .only_methods(vec![ElifMethod::POST, ElifMethod::PUT]);
1374
1375 let pipeline = MiddlewarePipelineV2::new().add(conditional);
1376
1377 let request1 = ElifRequest::new(
1379 ElifMethod::POST,
1380 "/api/users".parse().unwrap(),
1381 crate::response::headers::ElifHeaderMap::new(),
1382 );
1383
1384 let response1 = pipeline
1385 .execute(request1, |req| {
1386 Box::pin(async move {
1387 assert!(req.headers.contains_key(
1389 &crate::response::headers::ElifHeaderName::from_str(
1390 "x-middleware-methodconditional"
1391 )
1392 .unwrap()
1393 ));
1394 ElifResponse::ok().text("OK")
1395 })
1396 })
1397 .await;
1398
1399 assert_eq!(
1400 response1.status_code(),
1401 crate::response::status::ElifStatusCode::OK
1402 );
1403
1404 let request2 = ElifRequest::new(
1406 ElifMethod::GET,
1407 "/api/users".parse().unwrap(),
1408 crate::response::headers::ElifHeaderMap::new(),
1409 );
1410
1411 let response2 = pipeline
1412 .execute(request2, |req| {
1413 Box::pin(async move {
1414 assert!(!req.headers.contains_key(
1416 &crate::response::headers::ElifHeaderName::from_str(
1417 "x-middleware-methodconditional"
1418 )
1419 .unwrap()
1420 ));
1421 ElifResponse::ok().text("OK")
1422 })
1423 })
1424 .await;
1425
1426 assert_eq!(
1427 response2.status_code(),
1428 crate::response::status::ElifStatusCode::OK
1429 );
1430 }
1431
1432 #[tokio::test]
1433 async fn test_conditional_middleware_custom_condition() {
1434 let base_middleware = TestMiddleware::new("CustomConditional");
1435 let conditional = ConditionalMiddleware::new(base_middleware)
1436 .condition(|req| req.header("X-Debug").is_some());
1437
1438 let pipeline = MiddlewarePipelineV2::new().add(conditional);
1439
1440 let mut headers1 = crate::response::headers::ElifHeaderMap::new();
1442 headers1.insert(
1443 crate::response::headers::ElifHeaderName::from_str("x-debug").unwrap(),
1444 "true".parse().unwrap(),
1445 );
1446 let request1 = ElifRequest::new(ElifMethod::GET, "/api/test".parse().unwrap(), headers1);
1447
1448 let response1 = pipeline
1449 .execute(request1, |req| {
1450 Box::pin(async move {
1451 assert!(req.headers.contains_key(
1453 &crate::response::headers::ElifHeaderName::from_str(
1454 "x-middleware-customconditional"
1455 )
1456 .unwrap()
1457 ));
1458 ElifResponse::ok().text("OK")
1459 })
1460 })
1461 .await;
1462
1463 assert_eq!(
1464 response1.status_code(),
1465 crate::response::status::ElifStatusCode::OK
1466 );
1467
1468 let request2 = ElifRequest::new(
1470 ElifMethod::GET,
1471 "/api/test".parse().unwrap(),
1472 crate::response::headers::ElifHeaderMap::new(),
1473 );
1474
1475 let response2 = pipeline
1476 .execute(request2, |req| {
1477 Box::pin(async move {
1478 assert!(!req.headers.contains_key(
1480 &crate::response::headers::ElifHeaderName::from_str(
1481 "x-middleware-customconditional"
1482 )
1483 .unwrap()
1484 ));
1485 ElifResponse::ok().text("OK")
1486 })
1487 })
1488 .await;
1489
1490 assert_eq!(
1491 response2.status_code(),
1492 crate::response::status::ElifStatusCode::OK
1493 );
1494 }
1495
1496 #[tokio::test]
1497 async fn test_rate_limit_factory() {
1498 use super::factories;
1499
1500 let rate_limiter = factories::rate_limit(2); let pipeline = MiddlewarePipelineV2::new().add(rate_limiter);
1502
1503 let request1 = ElifRequest::new(
1505 ElifMethod::GET,
1506 "/api/test".parse().unwrap(),
1507 crate::response::headers::ElifHeaderMap::new(),
1508 );
1509
1510 let response1 = pipeline
1511 .execute(request1, |_req| {
1512 Box::pin(async move { ElifResponse::ok().text("OK") })
1513 })
1514 .await;
1515
1516 assert_eq!(
1517 response1.status_code(),
1518 crate::response::status::ElifStatusCode::OK
1519 );
1520
1521 let request2 = ElifRequest::new(
1523 ElifMethod::GET,
1524 "/api/test".parse().unwrap(),
1525 crate::response::headers::ElifHeaderMap::new(),
1526 );
1527
1528 let response2 = pipeline
1529 .execute(request2, |_req| {
1530 Box::pin(async move { ElifResponse::ok().text("OK") })
1531 })
1532 .await;
1533
1534 assert_eq!(
1535 response2.status_code(),
1536 crate::response::status::ElifStatusCode::OK
1537 );
1538
1539 let request3 = ElifRequest::new(
1541 ElifMethod::GET,
1542 "/api/test".parse().unwrap(),
1543 crate::response::headers::ElifHeaderMap::new(),
1544 );
1545
1546 let response3 = pipeline
1547 .execute(request3, |_req| {
1548 Box::pin(async move { ElifResponse::ok().text("OK") })
1549 })
1550 .await;
1551
1552 assert_eq!(
1553 response3.status_code(),
1554 crate::response::status::ElifStatusCode::TOO_MANY_REQUESTS
1555 );
1556 }
1557
1558 #[tokio::test]
1559 async fn test_cors_factory() {
1560 use super::factories;
1561
1562 let cors = factories::cors_with_origins(vec!["https://example.com".to_string()]);
1563 let pipeline = MiddlewarePipelineV2::new().add(cors);
1564
1565 let request1 = ElifRequest::new(
1567 ElifMethod::OPTIONS,
1568 "/api/test".parse().unwrap(),
1569 crate::response::headers::ElifHeaderMap::new(),
1570 );
1571
1572 let response1 = pipeline
1573 .execute(request1, |_req| {
1574 Box::pin(async move { ElifResponse::ok().text("Should not reach here") })
1575 })
1576 .await;
1577
1578 assert_eq!(
1579 response1.status_code(),
1580 crate::response::status::ElifStatusCode::OK
1581 );
1582
1583 let request2 = ElifRequest::new(
1585 ElifMethod::GET,
1586 "/api/test".parse().unwrap(),
1587 crate::response::headers::ElifHeaderMap::new(),
1588 );
1589
1590 let response2 = pipeline
1591 .execute(request2, |_req| {
1592 Box::pin(async move { ElifResponse::ok().text("OK") })
1593 })
1594 .await;
1595
1596 assert_eq!(
1597 response2.status_code(),
1598 crate::response::status::ElifStatusCode::OK
1599 );
1600 }
1601
1602 #[tokio::test]
1603 async fn test_timeout_factory() {
1604 use super::factories;
1605 use std::time::Duration;
1606
1607 let timeout_middleware = factories::timeout(Duration::from_millis(100));
1608 let pipeline = MiddlewarePipelineV2::new().add(timeout_middleware);
1609
1610 let request1 = ElifRequest::new(
1612 ElifMethod::GET,
1613 "/api/fast".parse().unwrap(),
1614 crate::response::headers::ElifHeaderMap::new(),
1615 );
1616
1617 let response1 = pipeline
1618 .execute(request1, |_req| {
1619 Box::pin(async move {
1620 tokio::time::sleep(Duration::from_millis(10)).await;
1621 ElifResponse::ok().text("Fast response")
1622 })
1623 })
1624 .await;
1625
1626 assert_eq!(
1627 response1.status_code(),
1628 crate::response::status::ElifStatusCode::OK
1629 );
1630
1631 let request2 = ElifRequest::new(
1633 ElifMethod::GET,
1634 "/api/slow".parse().unwrap(),
1635 crate::response::headers::ElifHeaderMap::new(),
1636 );
1637
1638 let response2 = pipeline
1639 .execute(request2, |_req| {
1640 Box::pin(async move {
1641 tokio::time::sleep(Duration::from_millis(200)).await;
1642 ElifResponse::ok().text("Slow response")
1643 })
1644 })
1645 .await;
1646
1647 assert_eq!(
1648 response2.status_code(),
1649 crate::response::status::ElifStatusCode::REQUEST_TIMEOUT
1650 );
1651 }
1652
1653 #[tokio::test]
1654 async fn test_body_limit_factory() {
1655 use super::factories;
1656 use axum::body::Bytes;
1657
1658 let body_limit = factories::body_limit(10); let pipeline = MiddlewarePipelineV2::new().add(body_limit);
1660
1661 let small_body = Bytes::from("small");
1663 let request1 = ElifRequest::new(
1664 ElifMethod::POST,
1665 "/api/upload".parse().unwrap(),
1666 crate::response::headers::ElifHeaderMap::new(),
1667 )
1668 .with_body(small_body);
1669
1670 let response1 = pipeline
1671 .execute(request1, |_req| {
1672 Box::pin(async move { ElifResponse::ok().text("Upload successful") })
1673 })
1674 .await;
1675
1676 assert_eq!(
1677 response1.status_code(),
1678 crate::response::status::ElifStatusCode::OK
1679 );
1680
1681 let large_body = Bytes::from("this body is way too large for the limit");
1683 let request2 = ElifRequest::new(
1684 ElifMethod::POST,
1685 "/api/upload".parse().unwrap(),
1686 crate::response::headers::ElifHeaderMap::new(),
1687 )
1688 .with_body(large_body);
1689
1690 let response2 = pipeline
1691 .execute(request2, |_req| {
1692 Box::pin(async move { ElifResponse::ok().text("Should not reach here") })
1693 })
1694 .await;
1695
1696 assert_eq!(
1697 response2.status_code(),
1698 crate::response::status::ElifStatusCode::PAYLOAD_TOO_LARGE
1699 );
1700 }
1701
1702 #[tokio::test]
1703 async fn test_composition_utilities() {
1704 use super::composition;
1705
1706 let middleware1 = TestMiddleware::new("First");
1707 let middleware2 = TestMiddleware::new("Second");
1708
1709 let composed_pipeline = composition::compose(middleware1, middleware2);
1711
1712 let request = ElifRequest::new(
1713 ElifMethod::GET,
1714 "/api/test".parse().unwrap(),
1715 crate::response::headers::ElifHeaderMap::new(),
1716 );
1717
1718 let response = composed_pipeline
1719 .execute(request, |req| {
1720 Box::pin(async move {
1721 assert!(req.headers.contains_key(
1723 &crate::response::headers::ElifHeaderName::from_str("x-middleware-first")
1724 .unwrap()
1725 ));
1726 assert!(req.headers.contains_key(
1727 &crate::response::headers::ElifHeaderName::from_str("x-middleware-second")
1728 .unwrap()
1729 ));
1730 ElifResponse::ok().text("Composed response")
1731 })
1732 })
1733 .await;
1734
1735 assert_eq!(
1736 response.status_code(),
1737 crate::response::status::ElifStatusCode::OK
1738 );
1739 assert_eq!(composed_pipeline.len(), 2);
1740
1741 let middleware1 = TestMiddleware::new("Alpha");
1743 let middleware2 = TestMiddleware::new("Beta");
1744 let middleware3 = TestMiddleware::new("Gamma");
1745
1746 let composed3_pipeline = composition::compose3(middleware1, middleware2, middleware3);
1747
1748 let request2 = ElifRequest::new(
1749 ElifMethod::POST,
1750 "/api/composed".parse().unwrap(),
1751 crate::response::headers::ElifHeaderMap::new(),
1752 );
1753
1754 let response2 = composed3_pipeline
1755 .execute(request2, |req| {
1756 Box::pin(async move {
1757 assert!(req.headers.contains_key(
1759 &crate::response::headers::ElifHeaderName::from_str("x-middleware-alpha")
1760 .unwrap()
1761 ));
1762 assert!(req.headers.contains_key(
1763 &crate::response::headers::ElifHeaderName::from_str("x-middleware-beta")
1764 .unwrap()
1765 ));
1766 assert!(req.headers.contains_key(
1767 &crate::response::headers::ElifHeaderName::from_str("x-middleware-gamma")
1768 .unwrap()
1769 ));
1770 ElifResponse::ok().text("Triple composed response")
1771 })
1772 })
1773 .await;
1774
1775 assert_eq!(
1776 response2.status_code(),
1777 crate::response::status::ElifStatusCode::OK
1778 );
1779 assert_eq!(composed3_pipeline.len(), 3);
1780 }
1781
1782 #[tokio::test]
1783 async fn test_composition_group() {
1784 use super::composition;
1785
1786 let middleware_vec: Vec<Arc<dyn Middleware>> = vec![
1787 Arc::new(TestMiddleware::new("Group1")),
1788 Arc::new(TestMiddleware::new("Group2")),
1789 Arc::new(TestMiddleware::new("Group3")),
1790 ];
1791
1792 let group_pipeline = composition::group(middleware_vec);
1793
1794 let request = ElifRequest::new(
1795 ElifMethod::DELETE,
1796 "/api/group".parse().unwrap(),
1797 crate::response::headers::ElifHeaderMap::new(),
1798 );
1799
1800 let response = group_pipeline
1801 .execute(request, |req| {
1802 Box::pin(async move {
1803 assert!(req.headers.contains_key(
1805 &crate::response::headers::ElifHeaderName::from_str("x-middleware-group1")
1806 .unwrap()
1807 ));
1808 assert!(req.headers.contains_key(
1809 &crate::response::headers::ElifHeaderName::from_str("x-middleware-group2")
1810 .unwrap()
1811 ));
1812 assert!(req.headers.contains_key(
1813 &crate::response::headers::ElifHeaderName::from_str("x-middleware-group3")
1814 .unwrap()
1815 ));
1816 ElifResponse::ok().text("Group response")
1817 })
1818 })
1819 .await;
1820
1821 assert_eq!(
1822 response.status_code(),
1823 crate::response::status::ElifStatusCode::OK
1824 );
1825 assert_eq!(group_pipeline.len(), 3);
1826 assert_eq!(group_pipeline.names(), vec!["Group1", "Group2", "Group3"]);
1827 }
1828
1829 #[tokio::test]
1830 async fn test_composed_middleware_to_pipeline() {
1831 let middleware1 = TestMiddleware::new("ComposedA");
1832 let middleware2 = TestMiddleware::new("ComposedB");
1833
1834 let composed = ComposedMiddleware::new(middleware1, middleware2);
1835 let pipeline = composed.to_pipeline();
1836
1837 let request = ElifRequest::new(
1838 ElifMethod::PUT,
1839 "/api/composed".parse().unwrap(),
1840 crate::response::headers::ElifHeaderMap::new(),
1841 );
1842
1843 let response = pipeline
1844 .execute(request, |req| {
1845 Box::pin(async move {
1846 assert!(req.headers.contains_key(
1848 &crate::response::headers::ElifHeaderName::from_str(
1849 "x-middleware-composeda"
1850 )
1851 .unwrap()
1852 ));
1853 assert!(req.headers.contains_key(
1854 &crate::response::headers::ElifHeaderName::from_str(
1855 "x-middleware-composedb"
1856 )
1857 .unwrap()
1858 ));
1859 ElifResponse::ok().text("Composed pipeline response")
1860 })
1861 })
1862 .await;
1863
1864 assert_eq!(
1865 response.status_code(),
1866 crate::response::status::ElifStatusCode::OK
1867 );
1868 assert_eq!(pipeline.len(), 2);
1869 }
1870
1871 #[tokio::test]
1872 async fn test_introspection_debug_info() {
1873 let pipeline = MiddlewarePipelineV2::new()
1874 .add(TestMiddleware::new("Debug1"))
1875 .add(TestMiddleware::new("Debug2"))
1876 .add(TestMiddleware::new("Debug3"));
1877
1878 let debug_info = pipeline.debug_info();
1879
1880 assert_eq!(debug_info.middleware_count, 3);
1881 assert_eq!(
1882 debug_info.middleware_names,
1883 vec!["Debug1", "Debug2", "Debug3"]
1884 );
1885 assert_eq!(
1886 debug_info.execution_order,
1887 vec!["Debug1", "Debug2", "Debug3"]
1888 );
1889 }
1890
1891 #[tokio::test]
1892 async fn test_introspection_debug_pipeline() {
1893 let pipeline = MiddlewarePipelineV2::new()
1894 .add(TestMiddleware::new("Timed1"))
1895 .add(TestMiddleware::new("Timed2"));
1896
1897 let debug_pipeline = pipeline.with_debug();
1898
1899 let request = ElifRequest::new(
1900 ElifMethod::GET,
1901 "/api/debug".parse().unwrap(),
1902 crate::response::headers::ElifHeaderMap::new(),
1903 );
1904
1905 let (response, duration) = debug_pipeline
1906 .execute_debug(request, |_req| {
1907 Box::pin(async move {
1908 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
1910 ElifResponse::ok().text("Debug response")
1911 })
1912 })
1913 .await;
1914
1915 assert_eq!(
1916 response.status_code(),
1917 crate::response::status::ElifStatusCode::OK
1918 );
1919 assert!(duration > std::time::Duration::from_millis(5));
1920
1921 let stats = debug_pipeline.stats();
1923 assert_eq!(stats.len(), 2);
1924 assert!(stats.contains_key("Timed1"));
1925 assert!(stats.contains_key("Timed2"));
1926 }
1927
1928 #[tokio::test]
1929 async fn test_introspection_instrumented_middleware() {
1930 let base_middleware = TestMiddleware::new("Base");
1931 let instrumented =
1932 introspection::instrument(base_middleware, "InstrumentedTest".to_string());
1933
1934 let pipeline = MiddlewarePipelineV2::new().add(instrumented);
1935
1936 let request = ElifRequest::new(
1937 ElifMethod::POST,
1938 "/api/instrumented".parse().unwrap(),
1939 crate::response::headers::ElifHeaderMap::new(),
1940 );
1941
1942 let response = pipeline
1943 .execute(request, |req| {
1944 Box::pin(async move {
1945 assert!(req.headers.contains_key(
1947 &crate::response::headers::ElifHeaderName::from_str("x-middleware-base")
1948 .unwrap()
1949 ));
1950 ElifResponse::ok().text("Instrumented response")
1951 })
1952 })
1953 .await;
1954
1955 assert_eq!(
1956 response.status_code(),
1957 crate::response::status::ElifStatusCode::OK
1958 );
1959 }
1960
1961 #[tokio::test]
1962 async fn test_introspection_middleware_stats() {
1963 use super::introspection::{instrument, MiddlewareStats};
1964
1965 let mut stats = MiddlewareStats::new("TestStats".to_string());
1966
1967 stats.record_execution(std::time::Duration::from_millis(10));
1969 stats.record_execution(std::time::Duration::from_millis(20));
1970 stats.record_execution(std::time::Duration::from_millis(30));
1971
1972 assert_eq!(stats.executions, 3);
1973 assert_eq!(stats.total_time, std::time::Duration::from_millis(60));
1974 assert_eq!(stats.avg_time, std::time::Duration::from_millis(20));
1975 assert!(stats.last_execution.is_some());
1976
1977 let base_middleware = TestMiddleware::new("StatsTest");
1979 let instrumented = instrument(base_middleware, "StatsInstrumented".to_string());
1980
1981 let initial_stats = instrumented.stats();
1982 assert_eq!(initial_stats.executions, 0);
1983 assert_eq!(initial_stats.total_time, std::time::Duration::ZERO);
1984
1985 instrumented.reset_stats();
1987 let reset_stats = instrumented.stats();
1988 assert_eq!(reset_stats.executions, 0);
1989 }
1990}