1use crate::request::{ElifRequest, ElifMethod};
7use crate::response::ElifResponse;
8use std::future::Future;
9use std::pin::Pin;
10use std::sync::{Arc, Mutex};
11use std::time::Duration;
12use std::collections::HashMap;
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 = Box::new(move |req: ElifRequest| {
115 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 {
265 next.run(request).await
266 })
267 }
268 }
269
270 fn name(&self) -> &'static str {
271 "ConditionalMiddleware"
272 }
273}
274
275pub mod factories {
277 use super::*;
278 use std::time::Duration;
279
280 pub fn rate_limit(requests_per_minute: u32) -> RateLimitMiddleware {
282 RateLimitMiddleware::new()
283 .limit(requests_per_minute)
284 .window(Duration::from_secs(60))
285 }
286
287 pub fn rate_limit_with_window(requests: u32, window: Duration) -> RateLimitMiddleware {
289 RateLimitMiddleware::new()
290 .limit(requests)
291 .window(window)
292 }
293
294 pub fn bearer_auth(token: String) -> SimpleAuthMiddleware {
296 SimpleAuthMiddleware::new(token)
297 }
298
299 pub fn cors() -> CorsMiddleware {
301 CorsMiddleware::new()
302 }
303
304 pub fn cors_with_origins(origins: Vec<String>) -> CorsMiddleware {
306 CorsMiddleware::new().allow_origins(origins)
307 }
308
309 pub fn timeout(duration: Duration) -> TimeoutMiddleware {
311 TimeoutMiddleware::new(duration)
312 }
313
314 pub fn body_limit(max_bytes: u64) -> BodyLimitMiddleware {
316 BodyLimitMiddleware::new(max_bytes)
317 }
318
319 pub fn profiler() -> ProfilerMiddleware {
321 ProfilerMiddleware::new()
322 }
323
324 pub fn profiler_disabled() -> ProfilerMiddleware {
326 ProfilerMiddleware::disabled()
327 }
328}
329
330pub mod composition {
332 use super::*;
333
334 pub fn compose<M1, M2>(first: M1, second: M2) -> MiddlewarePipelineV2
336 where
337 M1: Middleware + 'static,
338 M2: Middleware + 'static,
339 {
340 MiddlewarePipelineV2::new().add(first).add(second)
341 }
342
343 pub fn chain<M1, M2>(first: M1, second: M2) -> MiddlewarePipelineV2
345 where
346 M1: Middleware + 'static,
347 M2: Middleware + 'static,
348 {
349 compose(first, second)
350 }
351
352 pub fn group(middleware: Vec<Arc<dyn Middleware>>) -> MiddlewarePipelineV2 {
354 MiddlewarePipelineV2::from(middleware)
355 }
356
357 pub fn compose3<M1, M2, M3>(first: M1, second: M2, third: M3) -> MiddlewarePipelineV2
359 where
360 M1: Middleware + 'static,
361 M2: Middleware + 'static,
362 M3: Middleware + 'static,
363 {
364 MiddlewarePipelineV2::new().add(first).add(second).add(third)
365 }
366
367 pub fn compose4<M1, M2, M3, M4>(first: M1, second: M2, third: M3, fourth: M4) -> MiddlewarePipelineV2
369 where
370 M1: Middleware + 'static,
371 M2: Middleware + 'static,
372 M3: Middleware + 'static,
373 M4: Middleware + 'static,
374 {
375 MiddlewarePipelineV2::new().add(first).add(second).add(third).add(fourth)
376 }
377}
378
379pub struct ComposedMiddleware<M1, M2> {
381 first: M1,
382 second: M2,
383}
384
385impl<M1: std::fmt::Debug, M2: std::fmt::Debug> std::fmt::Debug for ComposedMiddleware<M1, M2> {
386 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
387 f.debug_struct("ComposedMiddleware")
388 .field("first", &self.first)
389 .field("second", &self.second)
390 .finish()
391 }
392}
393
394impl<M1, M2> ComposedMiddleware<M1, M2> {
395 pub fn new(first: M1, second: M2) -> Self {
396 Self { first, second }
397 }
398
399}
400
401impl<M1, M2> ComposedMiddleware<M1, M2>
404where
405 M1: Middleware + 'static,
406 M2: Middleware + 'static,
407{
408 pub fn to_pipeline(self) -> MiddlewarePipelineV2 {
410 MiddlewarePipelineV2::new()
411 .add(self.first)
412 .add(self.second)
413 }
414}
415
416pub mod introspection {
418 use super::*;
419 use std::time::Instant;
420
421 #[derive(Debug, Clone)]
423 pub struct MiddlewareStats {
424 pub name: String,
425 pub executions: u64,
426 pub total_time: Duration,
427 pub avg_time: Duration,
428 pub last_execution: Option<Instant>,
429 }
430
431 impl MiddlewareStats {
432 pub fn new(name: String) -> Self {
433 Self {
434 name,
435 executions: 0,
436 total_time: Duration::ZERO,
437 avg_time: Duration::ZERO,
438 last_execution: None,
439 }
440 }
441
442 pub fn record_execution(&mut self, duration: Duration) {
443 self.executions += 1;
444 self.total_time += duration;
445 self.avg_time = Duration::from_nanos((self.total_time.as_nanos() / self.executions as u128) as u64);
447 self.last_execution = Some(Instant::now());
448 }
449 }
450
451 #[derive(Debug, Clone)]
453 pub struct PipelineInfo {
454 pub middleware_count: usize,
455 pub middleware_names: Vec<String>,
456 pub execution_order: Vec<String>,
457 }
458
459 impl MiddlewarePipelineV2 {
460 pub fn debug_info(&self) -> PipelineInfo {
462 PipelineInfo {
463 middleware_count: self.len(),
464 middleware_names: self.names().into_iter().map(|s| s.to_string()).collect(),
465 execution_order: self.names().into_iter().map(|s| s.to_string()).collect(),
466 }
467 }
468
469 pub fn with_debug(self) -> DebugPipeline {
471 DebugPipeline::new(self)
472 }
473 }
474
475 #[derive(Debug)]
477 pub struct DebugPipeline {
478 pipeline: MiddlewarePipelineV2,
479 stats: Arc<Mutex<HashMap<String, MiddlewareStats>>>,
480 }
481
482 impl DebugPipeline {
483 pub fn new(pipeline: MiddlewarePipelineV2) -> Self {
484 let mut stats = HashMap::new();
485 for name in pipeline.names() {
486 stats.insert(name.to_string(), MiddlewareStats::new(name.to_string()));
487 }
488
489 Self {
490 pipeline,
491 stats: Arc::new(Mutex::new(stats)),
492 }
493 }
494
495 pub fn stats(&self) -> HashMap<String, MiddlewareStats> {
497 self.stats.lock().expect("Stats mutex should not be poisoned").clone()
498 }
499
500 pub fn middleware_stats(&self, name: &str) -> Option<MiddlewareStats> {
502 self.stats.lock().expect("Stats mutex should not be poisoned").get(name).cloned()
503 }
504
505 pub fn reset_stats(&self) {
507 let mut stats = self.stats.lock().expect("Stats mutex should not be poisoned");
508 for (name, stat) in stats.iter_mut() {
509 *stat = MiddlewareStats::new(name.clone());
510 }
511 }
512
513 pub async fn execute_debug<F, Fut>(&self, request: ElifRequest, handler: F) -> (ElifResponse, Duration)
515 where
516 F: FnOnce(ElifRequest) -> Fut + Send + 'static,
517 Fut: Future<Output = ElifResponse> + Send + 'static,
518 {
519 let start_time = Instant::now();
520 let response = self.pipeline.execute(request, handler).await;
521 let total_duration = start_time.elapsed();
522
523 (response, total_duration)
524 }
525 }
526
527 #[derive(Debug)]
529 pub struct InstrumentedMiddleware<M> {
530 middleware: M,
531 name: String,
532 stats: Arc<Mutex<MiddlewareStats>>,
533 }
534
535 impl<M> InstrumentedMiddleware<M> {
536 pub fn new(middleware: M, name: String) -> Self {
537 let stats = Arc::new(Mutex::new(MiddlewareStats::new(name.clone())));
538 Self {
539 middleware,
540 name,
541 stats,
542 }
543 }
544
545 pub fn stats(&self) -> MiddlewareStats {
547 self.stats.lock().expect("Middleware stats mutex should not be poisoned").clone()
548 }
549
550 pub fn reset_stats(&self) {
552 let mut stats = self.stats.lock().expect("Middleware stats mutex should not be poisoned");
553 *stats = MiddlewareStats::new(self.name.clone());
554 }
555 }
556
557 impl<M: Middleware> Middleware for InstrumentedMiddleware<M> {
558 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
559 let stats = self.stats.clone();
560 let middleware_result = self.middleware.handle(request, next);
561
562 Box::pin(async move {
563 let start = Instant::now();
564 let response = middleware_result.await;
565 let duration = start.elapsed();
566
567 stats.lock().expect("Middleware stats mutex should not be poisoned").record_execution(duration);
568 response
569 })
570 }
571
572 fn name(&self) -> &'static str {
573 "InstrumentedMiddleware"
574 }
575 }
576
577 pub fn instrument<M: Middleware + 'static>(middleware: M, name: String) -> InstrumentedMiddleware<M> {
579 InstrumentedMiddleware::new(middleware, name)
580 }
581}
582
583pub struct RateLimitMiddleware {
585 requests_per_window: u32,
586 window: Duration,
587 requests: Arc<Mutex<HashMap<String, (std::time::Instant, u32)>>>,
589 last_cleanup: Arc<Mutex<std::time::Instant>>,
591}
592
593impl std::fmt::Debug for RateLimitMiddleware {
594 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
595 f.debug_struct("RateLimitMiddleware")
596 .field("requests_per_window", &self.requests_per_window)
597 .field("window", &self.window)
598 .field("last_cleanup", &"<Mutex<Instant>>")
599 .finish()
600 }
601}
602
603impl RateLimitMiddleware {
604 pub fn new() -> Self {
605 let now = std::time::Instant::now();
606 Self {
607 requests_per_window: 60, window: Duration::from_secs(60),
609 requests: Arc::new(Mutex::new(HashMap::new())),
610 last_cleanup: Arc::new(Mutex::new(now)),
611 }
612 }
613
614 pub fn limit(mut self, requests: u32) -> Self {
615 self.requests_per_window = requests;
616 self
617 }
618
619 pub fn window(mut self, window: Duration) -> Self {
620 self.window = window;
621 self
622 }
623
624 fn get_client_id(&self, request: &ElifRequest) -> String {
625 request.header("x-forwarded-for")
627 .and_then(|h| h.to_str().ok())
628 .unwrap_or("unknown")
629 .to_string()
630 }
631
632 fn is_rate_limited(&self, client_id: &str) -> bool {
633 let now = std::time::Instant::now();
634
635 const CLEANUP_INTERVAL: Duration = Duration::from_secs(30);
638
639 {
640 let mut last_cleanup = self.last_cleanup.lock().expect("Cleanup time mutex should not be poisoned");
641 if now.duration_since(*last_cleanup) > CLEANUP_INTERVAL {
642 let mut requests = self.requests.lock().expect("Rate limiter mutex should not be poisoned");
644 requests.retain(|_, (timestamp, _)| now.duration_since(*timestamp) < self.window);
645 *last_cleanup = now;
646 drop(requests);
648 }
649 }
650
651 let mut requests = self.requests.lock().expect("Rate limiter mutex should not be poisoned");
653
654 if let Some((timestamp, count)) = requests.get_mut(client_id) {
656 if now.duration_since(*timestamp) < self.window {
657 if *count >= self.requests_per_window {
658 return true; }
660 *count += 1;
661 } else {
662 *timestamp = now;
664 *count = 1;
665 }
666 } else {
667 requests.insert(client_id.to_string(), (now, 1));
669 }
670
671 false
672 }
673}
674
675impl Middleware for RateLimitMiddleware {
676 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
677 let client_id = self.get_client_id(&request);
678 let is_limited = self.is_rate_limited(&client_id);
679
680 Box::pin(async move {
681 if is_limited {
682 ElifResponse::with_status(crate::response::status::ElifStatusCode::TOO_MANY_REQUESTS)
683 .json_value(serde_json::json!({
684 "error": {
685 "code": "rate_limited",
686 "message": "Too many requests. Please try again later."
687 }
688 }))
689 } else {
690 next.run(request).await
691 }
692 })
693 }
694
695 fn name(&self) -> &'static str {
696 "RateLimitMiddleware"
697 }
698}
699
700#[derive(Debug)]
702pub struct CorsMiddleware {
703 allowed_origins: Vec<String>,
704 allowed_methods: Vec<String>,
705 allowed_headers: Vec<String>,
706}
707
708impl CorsMiddleware {
709 pub fn new() -> Self {
710 Self {
711 allowed_origins: vec!["*".to_string()],
712 allowed_methods: vec!["GET".to_string(), "POST".to_string(), "PUT".to_string(), "DELETE".to_string(), "OPTIONS".to_string()],
713 allowed_headers: vec!["Content-Type".to_string(), "Authorization".to_string()],
714 }
715 }
716
717 pub fn allow_origins(mut self, origins: Vec<String>) -> Self {
718 self.allowed_origins = origins;
719 self
720 }
721
722 pub fn allow_methods(mut self, methods: Vec<String>) -> Self {
723 self.allowed_methods = methods;
724 self
725 }
726
727 pub fn allow_headers(mut self, headers: Vec<String>) -> Self {
728 self.allowed_headers = headers;
729 self
730 }
731}
732
733impl Middleware for CorsMiddleware {
734 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
735 let allowed_origins = self.allowed_origins.clone();
736 let allowed_methods = self.allowed_methods.clone();
737 let allowed_headers = self.allowed_headers.clone();
738
739 Box::pin(async move {
740 if request.method == ElifMethod::OPTIONS {
742 let mut preflight_response = ElifResponse::ok();
743 let _ = preflight_response.add_header("Access-Control-Allow-Origin", allowed_origins.join(","));
745 let _ = preflight_response.add_header("Access-Control-Allow-Methods", allowed_methods.join(","));
746 let _ = preflight_response.add_header("Access-Control-Allow-Headers", allowed_headers.join(","));
747 return preflight_response;
748 }
749
750 let mut response = next.run(request).await;
751
752 let _ = response.add_header("Access-Control-Allow-Origin", allowed_origins.join(","));
754 let _ = response.add_header("Access-Control-Allow-Methods", allowed_methods.join(","));
755 let _ = response.add_header("Access-Control-Allow-Headers", allowed_headers.join(","));
756
757 response
758 })
759 }
760
761 fn name(&self) -> &'static str {
762 "CorsMiddleware"
763 }
764}
765
766#[derive(Debug)]
768pub struct TimeoutMiddleware {
769 timeout: Duration,
770}
771
772impl TimeoutMiddleware {
773 pub fn new(timeout: Duration) -> Self {
774 Self { timeout }
775 }
776}
777
778impl Middleware for TimeoutMiddleware {
779 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
780 let timeout = self.timeout;
781 Box::pin(async move {
782 match tokio::time::timeout(timeout, next.run(request)).await {
783 Ok(response) => response,
784 Err(_) => ElifResponse::with_status(crate::response::status::ElifStatusCode::REQUEST_TIMEOUT)
785 .json_value(serde_json::json!({
786 "error": {
787 "code": "timeout",
788 "message": "Request timed out"
789 }
790 }))
791 }
792 })
793 }
794
795 fn name(&self) -> &'static str {
796 "TimeoutMiddleware"
797 }
798}
799
800#[derive(Debug)]
802pub struct BodyLimitMiddleware {
803 max_bytes: u64,
804}
805
806impl BodyLimitMiddleware {
807 pub fn new(max_bytes: u64) -> Self {
808 Self { max_bytes }
809 }
810}
811
812impl Middleware for BodyLimitMiddleware {
813 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
814 let max_bytes = self.max_bytes;
815 Box::pin(async move {
816 if let Some(body) = request.body_bytes() {
818 if body.len() as u64 > max_bytes {
819 return ElifResponse::with_status(crate::response::status::ElifStatusCode::PAYLOAD_TOO_LARGE)
820 .json_value(serde_json::json!({
821 "error": {
822 "code": "payload_too_large",
823 "message": format!("Request body too large. Maximum allowed: {} bytes", max_bytes)
824 }
825 }));
826 }
827 }
828
829 next.run(request).await
830 })
831 }
832
833 fn name(&self) -> &'static str {
834 "BodyLimitMiddleware"
835 }
836}
837
838#[derive(Debug)]
840pub struct LoggingMiddleware;
841
842impl Middleware for LoggingMiddleware {
843 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
844 Box::pin(async move {
845 let start = std::time::Instant::now();
847 let method = request.method.clone();
848 let path = request.path().to_string();
849
850 let response = next.run(request).await;
852
853 let duration = start.elapsed();
855 println!("{} {} - {} - {:?}", method, path, response.status_code(), duration);
856
857 response
858 })
859 }
860
861 fn name(&self) -> &'static str {
862 "LoggingMiddleware"
863 }
864}
865
866#[derive(Debug)]
868pub struct ProfilerMiddleware {
869 enabled: bool,
870}
871
872impl ProfilerMiddleware {
873 pub fn new() -> Self {
874 Self { enabled: true }
875 }
876
877 pub fn disabled() -> Self {
878 Self { enabled: false }
879 }
880}
881
882impl Middleware for ProfilerMiddleware {
883 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
884 let enabled = self.enabled;
885 Box::pin(async move {
886 if !enabled {
887 return next.run(request).await;
888 }
889
890 let start = std::time::Instant::now();
891 let method = request.method.clone();
892 let path = request.path().to_string();
893
894 println!("⏱️ [PROFILER] Starting request {} {}", method, path);
895
896 let response = next.run(request).await;
897
898 let duration = start.elapsed();
899 println!("⏱️ [PROFILER] Completed {} {} in {:?} - Status: {}",
900 method, path, duration, response.status_code());
901
902 response
903 })
904 }
905
906 fn name(&self) -> &'static str {
907 "ProfilerMiddleware"
908 }
909}
910
911#[derive(Debug)]
913pub struct SimpleAuthMiddleware {
914 required_token: String,
915}
916
917impl SimpleAuthMiddleware {
918 pub fn new(token: String) -> Self {
919 Self {
920 required_token: token,
921 }
922 }
923}
924
925impl Middleware for SimpleAuthMiddleware {
926 fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
927 let required_token = self.required_token.clone();
928 Box::pin(async move {
929 let token = match request.header("Authorization") {
931 Some(h) => {
932 match h.to_str() {
933 Ok(header_str) if header_str.starts_with("Bearer ") => &header_str[7..],
934 _ => {
935 return ElifResponse::unauthorized()
936 .json_value(serde_json::json!({
937 "error": {
938 "code": "unauthorized",
939 "message": "Missing or invalid authorization header"
940 }
941 }));
942 }
943 }
944 }
945 None => {
946 return ElifResponse::unauthorized()
947 .json_value(serde_json::json!({
948 "error": {
949 "code": "unauthorized",
950 "message": "Missing authorization header"
951 }
952 }));
953 }
954 };
955
956 if token != required_token {
958 return ElifResponse::unauthorized()
959 .json_value(serde_json::json!({
960 "error": {
961 "code": "unauthorized",
962 "message": "Invalid token"
963 }
964 }));
965 }
966
967 next.run(request).await
969 })
970 }
971
972 fn name(&self) -> &'static str {
973 "SimpleAuthMiddleware"
974 }
975}
976
977#[cfg(test)]
978mod tests {
979 use super::*;
980 use crate::request::ElifRequest;
981 use crate::response::ElifResponse;
982
983 #[derive(Debug)]
985 pub struct TestMiddleware {
986 name: &'static str,
987 }
988
989 impl TestMiddleware {
990 pub fn new(name: &'static str) -> Self {
991 Self { name }
992 }
993 }
994
995 impl Middleware for TestMiddleware {
996 fn handle(&self, mut request: ElifRequest, next: Next) -> NextFuture<'static> {
997 let name = self.name;
998 Box::pin(async move {
999 let header_name = crate::response::headers::ElifHeaderName::from_str(&format!("x-middleware-{}", name.to_lowercase())).unwrap();
1001 let header_value = crate::response::headers::ElifHeaderValue::from_str("executed").unwrap();
1002 request.headers.insert(header_name, header_value);
1003
1004 let response = next.run(request).await;
1005
1006 response
1008 })
1009 }
1010
1011 fn name(&self) -> &'static str {
1012 self.name
1013 }
1014 }
1015
1016 #[tokio::test]
1017 async fn test_simple_middleware_execution() {
1018 let pipeline = MiddlewarePipelineV2::new()
1019 .add(TestMiddleware::new("First"))
1020 .add(TestMiddleware::new("Second"));
1021
1022 let request = ElifRequest::new(
1023 crate::request::ElifMethod::GET,
1024 "/test".parse().unwrap(),
1025 crate::response::headers::ElifHeaderMap::new(),
1026 );
1027
1028 let response = pipeline.execute(request, |req| {
1029 Box::pin(async move {
1030 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-first").unwrap()),
1032 "First middleware should have added header");
1033 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-second").unwrap()),
1034 "Second middleware should have added header");
1035
1036 ElifResponse::ok().text("Hello World")
1037 })
1038 }).await;
1039
1040 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::OK);
1041 }
1042
1043 #[tokio::test]
1044 async fn test_middleware_chain_execution_order() {
1045 #[derive(Debug)]
1047 struct OrderTestMiddleware {
1048 name: &'static str,
1049 }
1050
1051 impl OrderTestMiddleware {
1052 fn new(name: &'static str) -> Self {
1053 Self { name }
1054 }
1055 }
1056
1057 impl Middleware for OrderTestMiddleware {
1058 fn handle(&self, mut request: ElifRequest, next: Next) -> NextFuture<'static> {
1059 let name = self.name;
1060 Box::pin(async move {
1061 let header_name_str = format!("x-before-{}", name.to_lowercase());
1063 let header_name = crate::response::headers::ElifHeaderName::from_str(&header_name_str).unwrap();
1064 let header_value = crate::response::headers::ElifHeaderValue::from_str("executed").unwrap();
1065 request.headers.insert(header_name, header_value);
1066
1067 let response = next.run(request).await;
1069
1070 let response_header = format!("x-after-{}", name.to_lowercase());
1072 response.header(&response_header, "executed").unwrap_or(
1073 ElifResponse::ok().text("fallback")
1075 )
1076 })
1077 }
1078
1079 fn name(&self) -> &'static str {
1080 self.name
1081 }
1082 }
1083
1084 let pipeline = MiddlewarePipelineV2::new()
1086 .add(OrderTestMiddleware::new("First"))
1087 .add(OrderTestMiddleware::new("Second"))
1088 .add(OrderTestMiddleware::new("Third"));
1089
1090 let request = ElifRequest::new(
1091 crate::request::ElifMethod::GET,
1092 "/test".parse().unwrap(),
1093 crate::response::headers::ElifHeaderMap::new(),
1094 );
1095
1096 let response = pipeline.execute(request, |req| {
1097 Box::pin(async move {
1098 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-before-first").unwrap()));
1100 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-before-second").unwrap()));
1101 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-before-third").unwrap()));
1102
1103 ElifResponse::ok().text("Handler executed")
1104 })
1105 }).await;
1106
1107 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::OK);
1109
1110 let axum_response = response.into_axum_response();
1112 let (parts, _body) = axum_response.into_parts();
1113 assert!(parts.headers.contains_key("x-after-first"));
1114 assert!(parts.headers.contains_key("x-after-second"));
1115 assert!(parts.headers.contains_key("x-after-third"));
1116
1117 assert_eq!(pipeline.len(), 3);
1119 assert_eq!(pipeline.names(), vec!["First", "Second", "Third"]);
1120 }
1121
1122 #[tokio::test]
1123 async fn test_auth_middleware() {
1124 let auth_middleware = SimpleAuthMiddleware::new("secret123".to_string());
1125
1126 let mut headers = crate::response::headers::ElifHeaderMap::new();
1128 headers.insert(crate::response::headers::ElifHeaderName::from_str("authorization").unwrap(), "Bearer secret123".parse().unwrap());
1129 let request = ElifRequest::new(
1130 crate::request::ElifMethod::GET,
1131 "/protected".parse().unwrap(),
1132 headers,
1133 );
1134
1135 let next = Next::new(|_req| {
1136 Box::pin(async {
1137 ElifResponse::ok().text("Protected content")
1138 })
1139 });
1140
1141 let response = auth_middleware.handle(request, next).await;
1142 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::OK);
1143
1144 let mut headers = crate::response::headers::ElifHeaderMap::new();
1146 headers.insert(crate::response::headers::ElifHeaderName::from_str("authorization").unwrap(), "Bearer invalid".parse().unwrap());
1147 let request = ElifRequest::new(
1148 crate::request::ElifMethod::GET,
1149 "/protected".parse().unwrap(),
1150 headers,
1151 );
1152
1153 let next = Next::new(|_req| {
1154 Box::pin(async {
1155 ElifResponse::ok().text("Protected content")
1156 })
1157 });
1158
1159 let response = auth_middleware.handle(request, next).await;
1160 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::UNAUTHORIZED);
1161 }
1162
1163 #[tokio::test]
1164 async fn test_pipeline_info() {
1165 let pipeline = MiddlewarePipelineV2::new()
1166 .add(TestMiddleware::new("Test1"))
1167 .add(TestMiddleware::new("Test2"));
1168
1169 assert_eq!(pipeline.len(), 2);
1170 assert!(!pipeline.is_empty());
1171 assert_eq!(pipeline.names(), vec!["Test1", "Test2"]);
1172
1173 let empty_pipeline = MiddlewarePipelineV2::new();
1174 assert_eq!(empty_pipeline.len(), 0);
1175 assert!(empty_pipeline.is_empty());
1176 }
1177
1178 #[tokio::test]
1181 async fn test_conditional_middleware_skip_paths() {
1182 let base_middleware = TestMiddleware::new("Conditional");
1183 let conditional = ConditionalMiddleware::new(base_middleware)
1184 .skip_paths(vec!["/public/*", "/health"]);
1185
1186 let pipeline = MiddlewarePipelineV2::new().add(conditional);
1187
1188 let request1 = ElifRequest::new(
1190 ElifMethod::GET,
1191 "/public/assets/style.css".parse().unwrap(),
1192 crate::response::headers::ElifHeaderMap::new(),
1193 );
1194
1195 let response1 = pipeline.execute(request1, |req| {
1196 Box::pin(async move {
1197 assert!(!req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-conditional").unwrap()));
1199 ElifResponse::ok().text("OK")
1200 })
1201 }).await;
1202
1203 assert_eq!(response1.status_code(), crate::response::status::ElifStatusCode::OK);
1204
1205 let request2 = ElifRequest::new(
1207 ElifMethod::GET,
1208 "/api/users".parse().unwrap(),
1209 crate::response::headers::ElifHeaderMap::new(),
1210 );
1211
1212 let response2 = pipeline.execute(request2, |req| {
1213 Box::pin(async move {
1214 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-conditional").unwrap()));
1216 ElifResponse::ok().text("OK")
1217 })
1218 }).await;
1219
1220 assert_eq!(response2.status_code(), crate::response::status::ElifStatusCode::OK);
1221 }
1222
1223 #[tokio::test]
1224 async fn test_conditional_middleware_only_methods() {
1225 let base_middleware = TestMiddleware::new("MethodConditional");
1226 let conditional = ConditionalMiddleware::new(base_middleware)
1227 .only_methods(vec![ElifMethod::POST, ElifMethod::PUT]);
1228
1229 let pipeline = MiddlewarePipelineV2::new().add(conditional);
1230
1231 let request1 = ElifRequest::new(
1233 ElifMethod::POST,
1234 "/api/users".parse().unwrap(),
1235 crate::response::headers::ElifHeaderMap::new(),
1236 );
1237
1238 let response1 = pipeline.execute(request1, |req| {
1239 Box::pin(async move {
1240 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-methodconditional").unwrap()));
1242 ElifResponse::ok().text("OK")
1243 })
1244 }).await;
1245
1246 assert_eq!(response1.status_code(), crate::response::status::ElifStatusCode::OK);
1247
1248 let request2 = ElifRequest::new(
1250 ElifMethod::GET,
1251 "/api/users".parse().unwrap(),
1252 crate::response::headers::ElifHeaderMap::new(),
1253 );
1254
1255 let response2 = pipeline.execute(request2, |req| {
1256 Box::pin(async move {
1257 assert!(!req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-methodconditional").unwrap()));
1259 ElifResponse::ok().text("OK")
1260 })
1261 }).await;
1262
1263 assert_eq!(response2.status_code(), crate::response::status::ElifStatusCode::OK);
1264 }
1265
1266 #[tokio::test]
1267 async fn test_conditional_middleware_custom_condition() {
1268 let base_middleware = TestMiddleware::new("CustomConditional");
1269 let conditional = ConditionalMiddleware::new(base_middleware)
1270 .condition(|req| req.header("X-Debug").is_some());
1271
1272 let pipeline = MiddlewarePipelineV2::new().add(conditional);
1273
1274 let mut headers1 = crate::response::headers::ElifHeaderMap::new();
1276 headers1.insert(crate::response::headers::ElifHeaderName::from_str("x-debug").unwrap(), "true".parse().unwrap());
1277 let request1 = ElifRequest::new(
1278 ElifMethod::GET,
1279 "/api/test".parse().unwrap(),
1280 headers1,
1281 );
1282
1283 let response1 = pipeline.execute(request1, |req| {
1284 Box::pin(async move {
1285 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-customconditional").unwrap()));
1287 ElifResponse::ok().text("OK")
1288 })
1289 }).await;
1290
1291 assert_eq!(response1.status_code(), crate::response::status::ElifStatusCode::OK);
1292
1293 let request2 = ElifRequest::new(
1295 ElifMethod::GET,
1296 "/api/test".parse().unwrap(),
1297 crate::response::headers::ElifHeaderMap::new(),
1298 );
1299
1300 let response2 = pipeline.execute(request2, |req| {
1301 Box::pin(async move {
1302 assert!(!req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-customconditional").unwrap()));
1304 ElifResponse::ok().text("OK")
1305 })
1306 }).await;
1307
1308 assert_eq!(response2.status_code(), crate::response::status::ElifStatusCode::OK);
1309 }
1310
1311 #[tokio::test]
1312 async fn test_rate_limit_factory() {
1313 use super::factories;
1314
1315 let rate_limiter = factories::rate_limit(2); let pipeline = MiddlewarePipelineV2::new().add(rate_limiter);
1317
1318 let request1 = ElifRequest::new(
1320 ElifMethod::GET,
1321 "/api/test".parse().unwrap(),
1322 crate::response::headers::ElifHeaderMap::new(),
1323 );
1324
1325 let response1 = pipeline.execute(request1, |_req| {
1326 Box::pin(async move {
1327 ElifResponse::ok().text("OK")
1328 })
1329 }).await;
1330
1331 assert_eq!(response1.status_code(), crate::response::status::ElifStatusCode::OK);
1332
1333 let request2 = ElifRequest::new(
1335 ElifMethod::GET,
1336 "/api/test".parse().unwrap(),
1337 crate::response::headers::ElifHeaderMap::new(),
1338 );
1339
1340 let response2 = pipeline.execute(request2, |_req| {
1341 Box::pin(async move {
1342 ElifResponse::ok().text("OK")
1343 })
1344 }).await;
1345
1346 assert_eq!(response2.status_code(), crate::response::status::ElifStatusCode::OK);
1347
1348 let request3 = ElifRequest::new(
1350 ElifMethod::GET,
1351 "/api/test".parse().unwrap(),
1352 crate::response::headers::ElifHeaderMap::new(),
1353 );
1354
1355 let response3 = pipeline.execute(request3, |_req| {
1356 Box::pin(async move {
1357 ElifResponse::ok().text("OK")
1358 })
1359 }).await;
1360
1361 assert_eq!(response3.status_code(), crate::response::status::ElifStatusCode::TOO_MANY_REQUESTS);
1362 }
1363
1364 #[tokio::test]
1365 async fn test_cors_factory() {
1366 use super::factories;
1367
1368 let cors = factories::cors_with_origins(vec!["https://example.com".to_string()]);
1369 let pipeline = MiddlewarePipelineV2::new().add(cors);
1370
1371 let request1 = ElifRequest::new(
1373 ElifMethod::OPTIONS,
1374 "/api/test".parse().unwrap(),
1375 crate::response::headers::ElifHeaderMap::new(),
1376 );
1377
1378 let response1 = pipeline.execute(request1, |_req| {
1379 Box::pin(async move {
1380 ElifResponse::ok().text("Should not reach here")
1381 })
1382 }).await;
1383
1384 assert_eq!(response1.status_code(), crate::response::status::ElifStatusCode::OK);
1385
1386 let request2 = ElifRequest::new(
1388 ElifMethod::GET,
1389 "/api/test".parse().unwrap(),
1390 crate::response::headers::ElifHeaderMap::new(),
1391 );
1392
1393 let response2 = pipeline.execute(request2, |_req| {
1394 Box::pin(async move {
1395 ElifResponse::ok().text("OK")
1396 })
1397 }).await;
1398
1399 assert_eq!(response2.status_code(), crate::response::status::ElifStatusCode::OK);
1400 }
1401
1402 #[tokio::test]
1403 async fn test_timeout_factory() {
1404 use super::factories;
1405 use std::time::Duration;
1406
1407 let timeout_middleware = factories::timeout(Duration::from_millis(100));
1408 let pipeline = MiddlewarePipelineV2::new().add(timeout_middleware);
1409
1410 let request1 = ElifRequest::new(
1412 ElifMethod::GET,
1413 "/api/fast".parse().unwrap(),
1414 crate::response::headers::ElifHeaderMap::new(),
1415 );
1416
1417 let response1 = pipeline.execute(request1, |_req| {
1418 Box::pin(async move {
1419 tokio::time::sleep(Duration::from_millis(10)).await;
1420 ElifResponse::ok().text("Fast response")
1421 })
1422 }).await;
1423
1424 assert_eq!(response1.status_code(), crate::response::status::ElifStatusCode::OK);
1425
1426 let request2 = ElifRequest::new(
1428 ElifMethod::GET,
1429 "/api/slow".parse().unwrap(),
1430 crate::response::headers::ElifHeaderMap::new(),
1431 );
1432
1433 let response2 = pipeline.execute(request2, |_req| {
1434 Box::pin(async move {
1435 tokio::time::sleep(Duration::from_millis(200)).await;
1436 ElifResponse::ok().text("Slow response")
1437 })
1438 }).await;
1439
1440 assert_eq!(response2.status_code(), crate::response::status::ElifStatusCode::REQUEST_TIMEOUT);
1441 }
1442
1443 #[tokio::test]
1444 async fn test_body_limit_factory() {
1445 use super::factories;
1446 use axum::body::Bytes;
1447
1448 let body_limit = factories::body_limit(10); let pipeline = MiddlewarePipelineV2::new().add(body_limit);
1450
1451 let small_body = Bytes::from("small");
1453 let request1 = ElifRequest::new(
1454 ElifMethod::POST,
1455 "/api/upload".parse().unwrap(),
1456 crate::response::headers::ElifHeaderMap::new(),
1457 ).with_body(small_body);
1458
1459 let response1 = pipeline.execute(request1, |_req| {
1460 Box::pin(async move {
1461 ElifResponse::ok().text("Upload successful")
1462 })
1463 }).await;
1464
1465 assert_eq!(response1.status_code(), crate::response::status::ElifStatusCode::OK);
1466
1467 let large_body = Bytes::from("this body is way too large for the limit");
1469 let request2 = ElifRequest::new(
1470 ElifMethod::POST,
1471 "/api/upload".parse().unwrap(),
1472 crate::response::headers::ElifHeaderMap::new(),
1473 ).with_body(large_body);
1474
1475 let response2 = pipeline.execute(request2, |_req| {
1476 Box::pin(async move {
1477 ElifResponse::ok().text("Should not reach here")
1478 })
1479 }).await;
1480
1481 assert_eq!(response2.status_code(), crate::response::status::ElifStatusCode::PAYLOAD_TOO_LARGE);
1482 }
1483
1484 #[tokio::test]
1485 async fn test_composition_utilities() {
1486 use super::composition;
1487
1488 let middleware1 = TestMiddleware::new("First");
1489 let middleware2 = TestMiddleware::new("Second");
1490
1491 let composed_pipeline = composition::compose(middleware1, middleware2);
1493
1494 let request = ElifRequest::new(
1495 ElifMethod::GET,
1496 "/api/test".parse().unwrap(),
1497 crate::response::headers::ElifHeaderMap::new(),
1498 );
1499
1500 let response = composed_pipeline.execute(request, |req| {
1501 Box::pin(async move {
1502 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-first").unwrap()));
1504 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-second").unwrap()));
1505 ElifResponse::ok().text("Composed response")
1506 })
1507 }).await;
1508
1509 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::OK);
1510 assert_eq!(composed_pipeline.len(), 2);
1511
1512 let middleware1 = TestMiddleware::new("Alpha");
1514 let middleware2 = TestMiddleware::new("Beta");
1515 let middleware3 = TestMiddleware::new("Gamma");
1516
1517 let composed3_pipeline = composition::compose3(middleware1, middleware2, middleware3);
1518
1519 let request2 = ElifRequest::new(
1520 ElifMethod::POST,
1521 "/api/composed".parse().unwrap(),
1522 crate::response::headers::ElifHeaderMap::new(),
1523 );
1524
1525 let response2 = composed3_pipeline.execute(request2, |req| {
1526 Box::pin(async move {
1527 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-alpha").unwrap()));
1529 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-beta").unwrap()));
1530 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-gamma").unwrap()));
1531 ElifResponse::ok().text("Triple composed response")
1532 })
1533 }).await;
1534
1535 assert_eq!(response2.status_code(), crate::response::status::ElifStatusCode::OK);
1536 assert_eq!(composed3_pipeline.len(), 3);
1537 }
1538
1539 #[tokio::test]
1540 async fn test_composition_group() {
1541 use super::composition;
1542
1543 let middleware_vec: Vec<Arc<dyn Middleware>> = vec![
1544 Arc::new(TestMiddleware::new("Group1")),
1545 Arc::new(TestMiddleware::new("Group2")),
1546 Arc::new(TestMiddleware::new("Group3")),
1547 ];
1548
1549 let group_pipeline = composition::group(middleware_vec);
1550
1551 let request = ElifRequest::new(
1552 ElifMethod::DELETE,
1553 "/api/group".parse().unwrap(),
1554 crate::response::headers::ElifHeaderMap::new(),
1555 );
1556
1557 let response = group_pipeline.execute(request, |req| {
1558 Box::pin(async move {
1559 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-group1").unwrap()));
1561 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-group2").unwrap()));
1562 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-group3").unwrap()));
1563 ElifResponse::ok().text("Group response")
1564 })
1565 }).await;
1566
1567 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::OK);
1568 assert_eq!(group_pipeline.len(), 3);
1569 assert_eq!(group_pipeline.names(), vec!["Group1", "Group2", "Group3"]);
1570 }
1571
1572 #[tokio::test]
1573 async fn test_composed_middleware_to_pipeline() {
1574 let middleware1 = TestMiddleware::new("ComposedA");
1575 let middleware2 = TestMiddleware::new("ComposedB");
1576
1577 let composed = ComposedMiddleware::new(middleware1, middleware2);
1578 let pipeline = composed.to_pipeline();
1579
1580 let request = ElifRequest::new(
1581 ElifMethod::PUT,
1582 "/api/composed".parse().unwrap(),
1583 crate::response::headers::ElifHeaderMap::new(),
1584 );
1585
1586 let response = pipeline.execute(request, |req| {
1587 Box::pin(async move {
1588 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-composeda").unwrap()));
1590 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-composedb").unwrap()));
1591 ElifResponse::ok().text("Composed pipeline response")
1592 })
1593 }).await;
1594
1595 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::OK);
1596 assert_eq!(pipeline.len(), 2);
1597 }
1598
1599 #[tokio::test]
1600 async fn test_introspection_debug_info() {
1601 let pipeline = MiddlewarePipelineV2::new()
1602 .add(TestMiddleware::new("Debug1"))
1603 .add(TestMiddleware::new("Debug2"))
1604 .add(TestMiddleware::new("Debug3"));
1605
1606 let debug_info = pipeline.debug_info();
1607
1608 assert_eq!(debug_info.middleware_count, 3);
1609 assert_eq!(debug_info.middleware_names, vec!["Debug1", "Debug2", "Debug3"]);
1610 assert_eq!(debug_info.execution_order, vec!["Debug1", "Debug2", "Debug3"]);
1611 }
1612
1613 #[tokio::test]
1614 async fn test_introspection_debug_pipeline() {
1615 let pipeline = MiddlewarePipelineV2::new()
1616 .add(TestMiddleware::new("Timed1"))
1617 .add(TestMiddleware::new("Timed2"));
1618
1619 let debug_pipeline = pipeline.with_debug();
1620
1621 let request = ElifRequest::new(
1622 ElifMethod::GET,
1623 "/api/debug".parse().unwrap(),
1624 crate::response::headers::ElifHeaderMap::new(),
1625 );
1626
1627 let (response, duration) = debug_pipeline.execute_debug(request, |_req| {
1628 Box::pin(async move {
1629 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
1631 ElifResponse::ok().text("Debug response")
1632 })
1633 }).await;
1634
1635 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::OK);
1636 assert!(duration > std::time::Duration::from_millis(5));
1637
1638 let stats = debug_pipeline.stats();
1640 assert_eq!(stats.len(), 2);
1641 assert!(stats.contains_key("Timed1"));
1642 assert!(stats.contains_key("Timed2"));
1643 }
1644
1645 #[tokio::test]
1646 async fn test_introspection_instrumented_middleware() {
1647 let base_middleware = TestMiddleware::new("Base");
1648 let instrumented = introspection::instrument(base_middleware, "InstrumentedTest".to_string());
1649
1650 let pipeline = MiddlewarePipelineV2::new().add(instrumented);
1651
1652 let request = ElifRequest::new(
1653 ElifMethod::POST,
1654 "/api/instrumented".parse().unwrap(),
1655 crate::response::headers::ElifHeaderMap::new(),
1656 );
1657
1658 let response = pipeline.execute(request, |req| {
1659 Box::pin(async move {
1660 assert!(req.headers.contains_key(&crate::response::headers::ElifHeaderName::from_str("x-middleware-base").unwrap()));
1662 ElifResponse::ok().text("Instrumented response")
1663 })
1664 }).await;
1665
1666 assert_eq!(response.status_code(), crate::response::status::ElifStatusCode::OK);
1667 }
1668
1669 #[tokio::test]
1670 async fn test_introspection_middleware_stats() {
1671 use super::introspection::{MiddlewareStats, instrument};
1672
1673 let mut stats = MiddlewareStats::new("TestStats".to_string());
1674
1675 stats.record_execution(std::time::Duration::from_millis(10));
1677 stats.record_execution(std::time::Duration::from_millis(20));
1678 stats.record_execution(std::time::Duration::from_millis(30));
1679
1680 assert_eq!(stats.executions, 3);
1681 assert_eq!(stats.total_time, std::time::Duration::from_millis(60));
1682 assert_eq!(stats.avg_time, std::time::Duration::from_millis(20));
1683 assert!(stats.last_execution.is_some());
1684
1685 let base_middleware = TestMiddleware::new("StatsTest");
1687 let instrumented = instrument(base_middleware, "StatsInstrumented".to_string());
1688
1689 let initial_stats = instrumented.stats();
1690 assert_eq!(initial_stats.executions, 0);
1691 assert_eq!(initial_stats.total_time, std::time::Duration::ZERO);
1692
1693 instrumented.reset_stats();
1695 let reset_stats = instrumented.stats();
1696 assert_eq!(reset_stats.executions, 0);
1697 }
1698}