elif_http/middleware/
v2.rs

1//! # Middleware V2
2//!
3//! New middleware system with handle(request, next) pattern for Laravel-style simplicity.
4//! This is the new middleware API that will replace the current one.
5
6use 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;
13// use axum::extract::Request;
14// use super::Middleware as OldMiddleware; // Import the old middleware trait
15
16/// Type alias for boxed future in Next
17pub type NextFuture<'a> = Pin<Box<dyn Future<Output = ElifResponse> + Send + 'a>>;
18
19/// Next represents the rest of the middleware chain
20pub struct Next {
21    handler: Box<dyn FnOnce(ElifRequest) -> NextFuture<'static> + Send>,
22}
23
24impl Next {
25    /// Create a new Next with a handler function
26    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    /// Run the rest of the middleware chain with the given request
36    pub async fn run(self, request: ElifRequest) -> ElifResponse {
37        (self.handler)(request).await
38    }
39
40    /// Run the rest of the middleware chain and return a boxed future
41    /// This is a convenience method for middleware implementations
42    pub fn call(self, request: ElifRequest) -> NextFuture<'static> {
43        Box::pin(async move { self.run(request).await })
44    }
45}
46
47/// New middleware trait with Laravel-style handle(request, next) pattern
48/// Uses boxed futures to be dyn-compatible
49pub trait Middleware: Send + Sync + std::fmt::Debug {
50    /// Handle the request and call the next middleware in the chain
51    fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static>;
52
53    /// Optional middleware name for debugging
54    fn name(&self) -> &'static str {
55        "Middleware"
56    }
57}
58
59/// Middleware pipeline for the new system
60#[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    /// Create a new empty middleware pipeline
73    pub fn new() -> Self {
74        Self {
75            middleware: Vec::new(),
76        }
77    }
78
79    /// Add middleware to the pipeline
80    pub fn add<M: Middleware + 'static>(mut self, middleware: M) -> Self {
81        self.middleware.push(Arc::new(middleware));
82        self
83    }
84
85    /// Add middleware to the pipeline (mutable version)
86    pub fn add_mut<M: Middleware + 'static>(&mut self, middleware: M) {
87        self.middleware.push(Arc::new(middleware));
88    }
89
90    /// Create a pipeline from a vector of Arc<dyn Middleware>
91    pub fn from_middleware_vec(middleware: Vec<Arc<dyn Middleware>>) -> Self {
92        Self { middleware }
93    }
94
95    /// Add an already-boxed middleware to the pipeline
96    pub fn add_boxed(mut self, middleware: Arc<dyn Middleware>) -> Self {
97        self.middleware.push(middleware);
98        self
99    }
100
101    /// Extend this pipeline with middleware from another pipeline
102    /// The middleware from this pipeline will execute before the middleware from the other pipeline
103    pub fn extend(mut self, other: Self) -> Self {
104        self.middleware.extend(other.middleware);
105        self
106    }
107
108    /// Execute the middleware pipeline with a handler
109    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    /// Get number of middleware in pipeline
131    pub fn len(&self) -> usize {
132        self.middleware.len()
133    }
134
135    /// Check if pipeline is empty
136    pub fn is_empty(&self) -> bool {
137        self.middleware.is_empty()
138    }
139
140    /// Get middleware names for debugging
141    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
160// Legacy middleware adapter removed - all middleware should use V2 system directly
161
162/// Conditional middleware wrapper that can skip execution based on path patterns and HTTP methods
163pub 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    /// Skip middleware execution for paths matching these patterns
192    /// Supports basic wildcards: "/api/*" matches "/api/users", "/api/posts", etc.
193    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    /// Only execute middleware for these HTTP methods
199    pub fn only_methods(mut self, methods: Vec<ElifMethod>) -> Self {
200        self.only_methods = Some(methods);
201        self
202    }
203
204    /// Add a custom condition function that determines whether to run the middleware
205    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    /// Check if a path matches any of the skip patterns
214    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    /// Simple glob-style path matching (supports single * wildcard)
224    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    /// Check if the request should be processed by this middleware
233    fn should_execute(&self, request: &ElifRequest) -> bool {
234        // Check skip paths
235        if self.should_skip_path(request.path()) {
236            return false;
237        }
238
239        // Check method restrictions
240        if let Some(ref allowed_methods) = self.only_methods {
241            if !allowed_methods.contains(&request.method) {
242                return false;
243            }
244        }
245
246        // Check custom condition
247        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            // Execute the wrapped middleware
261            self.middleware.handle(request, next)
262        } else {
263            // Skip the middleware and go directly to next
264            Box::pin(async move { next.run(request).await })
265        }
266    }
267
268    fn name(&self) -> &'static str {
269        "ConditionalMiddleware"
270    }
271}
272
273/// Middleware factories for common patterns
274pub mod factories {
275    use super::*;
276    use std::time::Duration;
277
278    /// Rate limiting middleware factory
279    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    /// Rate limiting middleware with custom window
286    pub fn rate_limit_with_window(requests: u32, window: Duration) -> RateLimitMiddleware {
287        RateLimitMiddleware::new().limit(requests).window(window)
288    }
289
290    /// Authentication middleware factory
291    pub fn bearer_auth(token: String) -> SimpleAuthMiddleware {
292        SimpleAuthMiddleware::new(token)
293    }
294
295    /// CORS middleware factory
296    pub fn cors() -> CorsMiddleware {
297        CorsMiddleware::new()
298    }
299
300    /// CORS middleware with specific origins
301    pub fn cors_with_origins(origins: Vec<String>) -> CorsMiddleware {
302        CorsMiddleware::new().allow_origins(origins)
303    }
304
305    /// Timeout middleware factory
306    pub fn timeout(duration: Duration) -> TimeoutMiddleware {
307        TimeoutMiddleware::new(duration)
308    }
309
310    /// Body size limit middleware factory
311    pub fn body_limit(max_bytes: u64) -> BodyLimitMiddleware {
312        BodyLimitMiddleware::new(max_bytes)
313    }
314
315    /// Profiler middleware factory
316    pub fn profiler() -> ProfilerMiddleware {
317        ProfilerMiddleware::new()
318    }
319
320    /// Disabled profiler middleware factory
321    pub fn profiler_disabled() -> ProfilerMiddleware {
322        ProfilerMiddleware::disabled()
323    }
324}
325
326/// Middleware composition utilities
327pub mod composition {
328    use super::*;
329
330    /// Compose two middleware into a pipeline
331    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    /// Chain multiple middleware together (alias for compose for better readability)
340    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    /// Create a middleware group from multiple middleware
349    pub fn group(middleware: Vec<Arc<dyn Middleware>>) -> MiddlewarePipelineV2 {
350        MiddlewarePipelineV2::from(middleware)
351    }
352
353    /// Compose three middleware into a pipeline
354    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    /// Compose four middleware into a pipeline
367    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
387/// A composed middleware that executes two middleware in sequence
388pub 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
408// For now, let's implement composition via pipeline extension
409// The composed middleware pattern is complex with Rust lifetimes in this context
410impl<M1, M2> ComposedMiddleware<M1, M2>
411where
412    M1: Middleware + 'static,
413    M2: Middleware + 'static,
414{
415    /// Convert to a pipeline for easier execution
416    pub fn to_pipeline(self) -> MiddlewarePipelineV2 {
417        MiddlewarePipelineV2::new().add(self.first).add(self.second)
418    }
419}
420
421/// Middleware introspection and debugging utilities
422pub mod introspection {
423    use super::*;
424    use std::time::Instant;
425
426    /// Execution statistics for middleware
427    #[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            // Safe division using u128 to avoid overflow and division-by-zero
451            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    /// Debug information about a middleware pipeline
458    #[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        /// Get debug information about the pipeline
467        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        /// Create a debug pipeline that wraps each middleware with timing
476        pub fn with_debug(self) -> DebugPipeline {
477            DebugPipeline::new(self)
478        }
479    }
480
481    /// A wrapper around MiddlewarePipelineV2 that provides debugging capabilities
482    #[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        /// Get execution statistics for all middleware
502        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        /// Get statistics for a specific middleware
510        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        /// Reset all statistics
519        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        /// Execute the pipeline with debug tracking
530        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    /// A middleware wrapper that tracks execution statistics
548    #[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        /// Get the current statistics for this middleware
566        pub fn stats(&self) -> MiddlewareStats {
567            self.stats
568                .lock()
569                .expect("Middleware stats mutex should not be poisoned")
570                .clone()
571        }
572
573        /// Reset statistics for this middleware
574        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    /// Utility function to wrap middleware with instrumentation
607    pub fn instrument<M: Middleware + 'static>(
608        middleware: M,
609        name: String,
610    ) -> InstrumentedMiddleware<M> {
611        InstrumentedMiddleware::new(middleware, name)
612    }
613}
614
615/// Rate limiting middleware
616pub struct RateLimitMiddleware {
617    requests_per_window: u32,
618    window: Duration,
619    // Simple in-memory store - in production you'd use Redis or similar
620    requests: Arc<Mutex<HashMap<String, (std::time::Instant, u32)>>>,
621    // Last cleanup time to avoid O(N) cleanup on every request
622    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, // Default: 60 requests per minute
646            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        // Simple IP-based rate limiting - in production you might use user ID, API key, etc.
664        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        // Periodic cleanup to avoid O(N) operation on every request
675        // Only clean up every 30 seconds to balance memory usage vs performance
676        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                // Time for cleanup - acquire requests lock and clean
685                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                // Release both locks before continuing
692                drop(requests);
693            }
694        }
695
696        // Now handle the actual rate limiting logic
697        let mut requests = self
698            .requests
699            .lock()
700            .expect("Rate limiter mutex should not be poisoned");
701
702        // Check current rate
703        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; // Rate limited
707                }
708                *count += 1;
709            } else {
710                // Reset window
711                *timestamp = now;
712                *count = 1;
713            }
714        } else {
715            // First request from this client
716            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/// CORS middleware
751#[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            // Handle preflight OPTIONS request
803            if request.method == ElifMethod::OPTIONS {
804                let mut preflight_response = ElifResponse::ok();
805                // Add headers safely - ignore failures to avoid replacing response
806                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            // Add CORS headers to response safely - never replace the response on failure
818            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/// Timeout middleware
832#[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/// Body size limit middleware
868#[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            // Check if request has body and if it exceeds limit
884            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/// Example logging middleware using the new pattern
906#[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            // Before request
913            let start = std::time::Instant::now();
914            let method = request.method.clone();
915            let path = request.path().to_string();
916
917            // Pass to next middleware
918            let response = next.run(request).await;
919
920            // After response
921            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/// Middleware profiler that logs timing for each middleware in the pipeline
940#[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/// Example auth middleware using the new pattern
996#[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            // Extract token
1014            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            // Validate token
1037            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            // Token is valid, proceed to next middleware
1047            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    /// Test middleware that adds a header to requests
1063    #[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                // Add a custom header to track middleware execution
1079                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                // Add response header - simplified for now
1091                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                    // Verify both middleware executed by checking headers they added
1116                    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        /// Test middleware that tracks execution order
1149        #[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                    // Add execution order to request headers (before handler)
1165                    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                    // Call next middleware/handler
1174                    let response = next.run(request).await;
1175
1176                    // Add execution order to response headers (after handler)
1177                    let response_header = format!("x-after-{}", name.to_lowercase());
1178                    response.header(&response_header, "executed").unwrap_or(
1179                        // If header addition fails, return original response
1180                        ElifResponse::ok().text("fallback"),
1181                    )
1182                })
1183            }
1184
1185            fn name(&self) -> &'static str {
1186                self.name
1187            }
1188        }
1189
1190        // Create pipeline with multiple middleware
1191        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                    // Verify all middleware ran before the handler
1206                    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        // Verify response and that all middleware ran after the handler
1225        assert_eq!(
1226            response.status_code(),
1227            crate::response::status::ElifStatusCode::OK
1228        );
1229
1230        // Convert to axum response to check headers
1231        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        // Verify pipeline info
1238        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        // Test with valid token
1247        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        // Test with invalid token
1268        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    // Legacy compatibility test removed - all middleware use V2 system directly
1305
1306    #[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        // Test skipped path
1315        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                    // Middleware should be skipped - no header added
1325                    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        // Test non-skipped path
1342        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                    // Middleware should execute - header added
1352                    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        // Test allowed method
1378        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                    // Middleware should execute for POST
1388                    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        // Test disallowed method
1405        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                    // Middleware should be skipped for GET
1415                    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        // Test with condition met
1441        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                    // Middleware should execute when X-Debug header present
1452                    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        // Test without condition met
1469        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                    // Middleware should be skipped when X-Debug header not present
1479                    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); // 2 requests per minute
1501        let pipeline = MiddlewarePipelineV2::new().add(rate_limiter);
1502
1503        // First request should pass
1504        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        // Second request should also pass
1522        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        // Third request should be rate limited
1540        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        // Test OPTIONS preflight request
1566        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        // Test normal request with CORS headers added
1584        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        // Test request that completes within timeout
1611        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        // Test request that times out
1632        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); // 10 bytes max
1659        let pipeline = MiddlewarePipelineV2::new().add(body_limit);
1660
1661        // Test request with small body
1662        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        // Test request with large body
1682        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        // Test compose function
1710        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                    // Both middleware should have executed
1722                    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        // Test compose3 function
1742        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                    // All three middleware should have executed
1758                    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                    // All group middleware should have executed
1804                    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                    // Both composed middleware should have executed
1847                    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                    // Simulate some processing time
1909                    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        // Check that we can get stats (even if middleware aren't individually tracked yet)
1922        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                    // Verify middleware executed
1946                    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        // Record some executions
1968        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        // Test instrumented middleware stats
1978        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        // Test reset functionality
1986        instrumented.reset_stats();
1987        let reset_stats = instrumented.stats();
1988        assert_eq!(reset_stats.executions, 0);
1989    }
1990}