elif_http/middleware/
ioc_middleware.rs

1//! IoC-enabled middleware system
2//!
3//! Provides middleware creation and dependency injection using the IoC container.
4
5use std::collections::HashMap;
6use std::sync::Arc;
7
8use serde_json;
9
10use crate::errors::HttpError;
11use crate::middleware::v2::{Middleware, Next, NextFuture};
12use crate::request::ElifRequest;
13use crate::response::ElifResponse;
14use elif_core::container::{IocContainer, ScopeId};
15
16/// Trait for middleware that can be created from IoC container
17pub trait IocMiddleware: Middleware {
18    /// Create middleware instance with dependencies resolved from IoC container
19    fn from_ioc_container(
20        container: &IocContainer,
21        scope: Option<&ScopeId>,
22    ) -> Result<Self, String>
23    where
24        Self: Sized;
25}
26
27/// Middleware factory for IoC-enabled middleware
28pub struct IocMiddlewareFactory<M> {
29    _phantom: std::marker::PhantomData<M>,
30}
31
32impl<M> IocMiddlewareFactory<M> {
33    pub fn new() -> Self {
34        Self {
35            _phantom: std::marker::PhantomData,
36        }
37    }
38}
39
40impl<M> Default for IocMiddlewareFactory<M> {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl<M> IocMiddlewareFactory<M>
47where
48    M: IocMiddleware + 'static,
49{
50    /// Create middleware instance from IoC container
51    pub fn create(
52        &self,
53        container: &IocContainer,
54        scope: Option<&ScopeId>,
55    ) -> Result<M, HttpError> {
56        M::from_ioc_container(container, scope).map_err(|e| HttpError::InternalError {
57            message: format!("Failed to create middleware: {}", e),
58        })
59    }
60}
61
62/// Registry for managing IoC-enabled middleware
63pub struct MiddlewareRegistry {
64    factories: HashMap<String, Box<dyn MiddlewareFactory>>,
65    container: Arc<IocContainer>,
66}
67
68/// Trait for creating middleware instances
69pub trait MiddlewareFactory: Send + Sync {
70    /// Create middleware instance from IoC container
71    fn create_middleware(
72        &self,
73        container: &IocContainer,
74        scope: Option<&ScopeId>,
75    ) -> Result<Arc<dyn Middleware>, HttpError>;
76}
77
78impl<M> MiddlewareFactory for IocMiddlewareFactory<M>
79where
80    M: IocMiddleware + 'static,
81{
82    fn create_middleware(
83        &self,
84        container: &IocContainer,
85        scope: Option<&ScopeId>,
86    ) -> Result<Arc<dyn Middleware>, HttpError> {
87        let middleware = self.create(container, scope)?;
88        Ok(Arc::new(middleware))
89    }
90}
91
92impl MiddlewareRegistry {
93    /// Create new middleware registry with IoC container
94    pub fn new(container: Arc<IocContainer>) -> Self {
95        Self {
96            factories: HashMap::new(),
97            container,
98        }
99    }
100
101    /// Register an IoC-enabled middleware type
102    pub fn register<M>(&mut self, name: &str) -> Result<(), HttpError>
103    where
104        M: IocMiddleware + 'static,
105    {
106        let factory = Box::new(IocMiddlewareFactory::<M>::new());
107        self.factories.insert(name.to_string(), factory);
108        Ok(())
109    }
110
111    /// Register a custom middleware factory
112    pub fn register_factory(&mut self, name: &str, factory: Box<dyn MiddlewareFactory>) {
113        self.factories.insert(name.to_string(), factory);
114    }
115
116    /// Create middleware instance by name
117    pub fn create_middleware(
118        &self,
119        name: &str,
120        scope: Option<&ScopeId>,
121    ) -> Result<Arc<dyn Middleware>, HttpError> {
122        let factory = self
123            .factories
124            .get(name)
125            .ok_or_else(|| HttpError::InternalError {
126                message: format!("Middleware '{}' not registered", name),
127            })?;
128
129        factory.create_middleware(&self.container, scope)
130    }
131
132    /// Create multiple middleware instances by names
133    pub fn create_middleware_pipeline(
134        &self,
135        names: &[&str],
136        scope: Option<&ScopeId>,
137    ) -> Result<Vec<Arc<dyn Middleware>>, HttpError> {
138        names
139            .iter()
140            .map(|name| self.create_middleware(name, scope))
141            .collect()
142    }
143
144    /// Get list of registered middleware names
145    pub fn registered_middleware(&self) -> Vec<String> {
146        self.factories.keys().cloned().collect()
147    }
148}
149
150/// Builder for middleware registry
151pub struct MiddlewareRegistryBuilder {
152    container: Option<Arc<IocContainer>>,
153    middleware: Vec<(String, Box<dyn MiddlewareFactory>)>,
154}
155
156impl MiddlewareRegistryBuilder {
157    /// Create new builder
158    pub fn new() -> Self {
159        Self {
160            container: None,
161            middleware: Vec::new(),
162        }
163    }
164
165    /// Set the IoC container
166    pub fn container(mut self, container: Arc<IocContainer>) -> Self {
167        self.container = Some(container);
168        self
169    }
170
171    /// Register an IoC-enabled middleware
172    pub fn register<M>(mut self, name: &str) -> Self
173    where
174        M: IocMiddleware + 'static,
175    {
176        let factory = Box::new(IocMiddlewareFactory::<M>::new());
177        self.middleware.push((name.to_string(), factory));
178        self
179    }
180
181    /// Register a custom middleware factory
182    pub fn register_factory(mut self, name: &str, factory: Box<dyn MiddlewareFactory>) -> Self {
183        self.middleware.push((name.to_string(), factory));
184        self
185    }
186
187    /// Build the middleware registry
188    pub fn build(self) -> Result<MiddlewareRegistry, HttpError> {
189        let container = self.container.ok_or_else(|| HttpError::InternalError {
190            message: "IoC container is required for middleware registry".to_string(),
191        })?;
192
193        let mut registry = MiddlewareRegistry::new(container);
194
195        for (name, factory) in self.middleware {
196            registry.register_factory(&name, factory);
197        }
198
199        Ok(registry)
200    }
201}
202
203impl Default for MiddlewareRegistryBuilder {
204    fn default() -> Self {
205        Self::new()
206    }
207}
208
209/// Wrapper middleware that lazily creates IoC-enabled middleware per request
210pub struct LazyIocMiddleware {
211    middleware_name: String,
212    registry: Arc<MiddlewareRegistry>,
213}
214
215impl std::fmt::Debug for LazyIocMiddleware {
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        f.debug_struct("LazyIocMiddleware")
218            .field("middleware_name", &self.middleware_name)
219            .finish()
220    }
221}
222
223impl LazyIocMiddleware {
224    /// Create new lazy IoC middleware
225    pub fn new(middleware_name: String, registry: Arc<MiddlewareRegistry>) -> Self {
226        Self {
227            middleware_name,
228            registry,
229        }
230    }
231}
232
233impl Middleware for LazyIocMiddleware {
234    fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
235        let middleware_name = self.middleware_name.clone();
236        let registry = self.registry.clone();
237
238        Box::pin(async move {
239            // Create a new scope for this request if needed
240            let scope_result = registry.container.create_scope();
241            let scope = scope_result.ok();
242
243            // Create the actual middleware instance
244            match registry.create_middleware(&middleware_name, scope.as_ref()) {
245                Ok(middleware) => {
246                    // Delegate to the actual middleware
247                    let result = middleware.handle(request, next).await;
248
249                    // Clean up scope
250                    if let Some(scope_id) = scope {
251                        let _ = registry.container.dispose_scope(&scope_id).await;
252                    }
253
254                    result
255                }
256                Err(e) => {
257                    // CRITICAL: Middleware instantiation failure - do NOT continue processing
258                    eprintln!(
259                        "CRITICAL: Failed to instantiate middleware '{}': {:?}",
260                        middleware_name, e
261                    );
262
263                    // Clean up scope if it was created
264                    if let Some(scope_id) = scope {
265                        let _ = registry.container.dispose_scope(&scope_id).await;
266                    }
267
268                    // Return an error response immediately
269                    ElifResponse::internal_server_error()
270                        .json(&serde_json::json!({
271                            "error": {
272                                "code": "MIDDLEWARE_INIT_FAILED",
273                                "message": "Internal server error",
274                                "hint": "A required middleware component failed to initialize"
275                            }
276                        }))
277                        .unwrap_or_else(|_| ElifResponse::internal_server_error())
278                }
279            }
280        })
281    }
282
283    fn name(&self) -> &'static str {
284        "LazyIocMiddleware"
285    }
286}
287
288/// Request-scoped middleware context for dependency injection
289#[derive(Clone, Debug)]
290pub struct MiddlewareContext {
291    pub request_id: String,
292    pub user_id: Option<String>,
293    pub session_id: Option<String>,
294    pub correlation_id: Option<String>,
295    pub custom_data: HashMap<String, String>,
296}
297
298impl MiddlewareContext {
299    /// Create middleware context from request
300    pub fn from_request(request: &ElifRequest) -> Self {
301        Self {
302            request_id: request
303                .header("x-request-id")
304                .and_then(|h| h.to_str().ok())
305                .unwrap_or("unknown")
306                .to_string(),
307            user_id: request
308                .header("x-user-id")
309                .and_then(|h| h.to_str().ok())
310                .map(String::from),
311            session_id: request
312                .header("x-session-id")
313                .and_then(|h| h.to_str().ok())
314                .map(String::from),
315            correlation_id: request
316                .header("x-correlation-id")
317                .and_then(|h| h.to_str().ok())
318                .map(String::from),
319            custom_data: HashMap::new(),
320        }
321    }
322
323    /// Add custom data to the context
324    pub fn with_data(mut self, key: String, value: String) -> Self {
325        self.custom_data.insert(key, value);
326        self
327    }
328}
329
330/// Middleware group for organizing related middleware
331pub struct MiddlewareGroup {
332    name: String,
333    middleware_names: Vec<String>,
334    registry: Arc<MiddlewareRegistry>,
335}
336
337impl MiddlewareGroup {
338    /// Create new middleware group
339    pub fn new(
340        name: String,
341        middleware_names: Vec<String>,
342        registry: Arc<MiddlewareRegistry>,
343    ) -> Self {
344        Self {
345            name,
346            middleware_names,
347            registry,
348        }
349    }
350
351    /// Create all middleware in the group
352    pub fn create_middleware(
353        &self,
354        scope: Option<&ScopeId>,
355    ) -> Result<Vec<Arc<dyn Middleware>>, HttpError> {
356        self.middleware_names
357            .iter()
358            .map(|name| self.registry.create_middleware(name, scope))
359            .collect()
360    }
361
362    /// Get group name
363    pub fn name(&self) -> &str {
364        &self.name
365    }
366
367    /// Get middleware names in the group
368    pub fn middleware_names(&self) -> &[String] {
369        &self.middleware_names
370    }
371}
372
373#[cfg(test)]
374mod tests {
375    use super::*;
376    use crate::middleware::v2::Middleware;
377    use elif_core::container::{IocContainer, ServiceBinder};
378
379    // Test service for middleware injection
380    #[derive(Default, Clone, Debug)]
381    pub struct TestLoggerService {
382        pub name: String,
383    }
384
385    unsafe impl Send for TestLoggerService {}
386    unsafe impl Sync for TestLoggerService {}
387
388    // Test middleware with IoC dependencies
389    #[derive(Debug)]
390    pub struct TestIocMiddleware {
391        logger: Arc<TestLoggerService>,
392    }
393
394    impl IocMiddleware for TestIocMiddleware {
395        fn from_ioc_container(
396            container: &IocContainer,
397            _scope: Option<&ScopeId>,
398        ) -> Result<Self, String> {
399            let logger = container
400                .resolve::<TestLoggerService>()
401                .map_err(|e| format!("Failed to resolve TestLoggerService: {}", e))?;
402
403            Ok(Self { logger })
404        }
405    }
406
407    impl Middleware for TestIocMiddleware {
408        fn handle(&self, request: ElifRequest, next: Next) -> NextFuture<'static> {
409            // Clone the logger name to avoid lifetime issues
410            let logger_name = self.logger.name.clone();
411            Box::pin(async move {
412                // Use the injected logger service
413                println!("TestIocMiddleware: Using logger: {}", logger_name);
414                next.run(request).await
415            })
416        }
417
418        fn name(&self) -> &'static str {
419            "TestIocMiddleware"
420        }
421    }
422
423    #[tokio::test]
424    async fn test_ioc_middleware_creation() {
425        let mut container = IocContainer::new();
426
427        // Register the logger service
428        let logger_service = TestLoggerService {
429            name: "TestLogger".to_string(),
430        };
431        container.bind_instance::<TestLoggerService, TestLoggerService>(logger_service);
432        container.build().expect("Container build failed");
433
434        let container_arc = Arc::new(container);
435        let mut registry = MiddlewareRegistry::new(container_arc);
436
437        // Register the IoC middleware
438        registry
439            .register::<TestIocMiddleware>("test_middleware")
440            .expect("Failed to register middleware");
441
442        // Create middleware instance
443        let middleware = registry
444            .create_middleware("test_middleware", None)
445            .expect("Failed to create middleware");
446
447        assert_eq!(middleware.name(), "TestIocMiddleware");
448    }
449
450    #[tokio::test]
451    async fn test_middleware_registry_builder() {
452        let mut container = IocContainer::new();
453        container.bind::<TestLoggerService, TestLoggerService>();
454        container.build().expect("Container build failed");
455
456        let registry = MiddlewareRegistryBuilder::new()
457            .container(Arc::new(container))
458            .register::<TestIocMiddleware>("test_ioc")
459            .build()
460            .expect("Failed to build middleware registry");
461
462        let registered = registry.registered_middleware();
463        assert!(registered.contains(&"test_ioc".to_string()));
464
465        let middleware = registry
466            .create_middleware("test_ioc", None)
467            .expect("Failed to create middleware");
468
469        assert_eq!(middleware.name(), "TestIocMiddleware");
470    }
471
472    #[tokio::test]
473    async fn test_middleware_pipeline_creation() {
474        let mut container = IocContainer::new();
475        container.bind::<TestLoggerService, TestLoggerService>();
476        container.build().expect("Container build failed");
477
478        let registry = MiddlewareRegistryBuilder::new()
479            .container(Arc::new(container))
480            .register::<TestIocMiddleware>("ioc1")
481            .register::<TestIocMiddleware>("ioc2")
482            .build()
483            .expect("Failed to build middleware registry");
484
485        let middleware_pipeline = registry
486            .create_middleware_pipeline(&["ioc1", "ioc2"], None)
487            .expect("Failed to create middleware pipeline");
488
489        assert_eq!(middleware_pipeline.len(), 2);
490    }
491
492    #[tokio::test]
493    async fn test_lazy_ioc_middleware() {
494        use crate::request::method::ElifMethod as HttpMethod;
495        use crate::response::headers::ElifHeaderMap;
496
497        let mut container = IocContainer::new();
498        container.bind::<TestLoggerService, TestLoggerService>();
499        container.build().expect("Container build failed");
500
501        let registry = Arc::new(
502            MiddlewareRegistryBuilder::new()
503                .container(Arc::new(container))
504                .register::<TestIocMiddleware>("lazy_test")
505                .build()
506                .expect("Failed to build middleware registry"),
507        );
508
509        let lazy_middleware = LazyIocMiddleware::new("lazy_test".to_string(), registry);
510
511        let request = ElifRequest::new(
512            HttpMethod::GET,
513            "/test".parse().unwrap(),
514            ElifHeaderMap::new(),
515        );
516
517        let next = Next::new(|_req| Box::pin(async { ElifResponse::ok().text("Success") }));
518
519        let response = lazy_middleware.handle(request, next).await;
520        assert_eq!(
521            response.status_code(),
522            crate::response::status::ElifStatusCode::OK
523        );
524    }
525
526    #[tokio::test]
527    async fn test_lazy_ioc_middleware_instantiation_failure() {
528        use crate::request::method::ElifMethod as HttpMethod;
529        use crate::response::headers::ElifHeaderMap;
530
531        // Create container without the required service - this will cause middleware instantiation to fail
532        let container = IocContainer::new();
533        // Note: We're NOT binding TestLoggerService, so TestIocMiddleware will fail to instantiate
534
535        let registry = Arc::new(
536            MiddlewareRegistryBuilder::new()
537                .container(Arc::new(container))
538                .register::<TestIocMiddleware>("failing_middleware")
539                .build()
540                .expect("Failed to build middleware registry"),
541        );
542
543        let lazy_middleware = LazyIocMiddleware::new("failing_middleware".to_string(), registry);
544
545        let request = ElifRequest::new(
546            HttpMethod::GET,
547            "/test".parse().unwrap(),
548            ElifHeaderMap::new(),
549        );
550
551        let next = Next::new(|_req| {
552            Box::pin(async {
553                // This should never be called if middleware instantiation fails
554                panic!("Next middleware should not be called when middleware instantiation fails!");
555            })
556        });
557
558        let response = lazy_middleware.handle(request, next).await;
559
560        // Verify we get an internal server error response
561        assert_eq!(
562            response.status_code(),
563            crate::response::status::ElifStatusCode::INTERNAL_SERVER_ERROR
564        );
565
566        // The key test is that the next middleware was NOT called (it would panic)
567        // and we got a 500 error response instead
568    }
569
570    #[tokio::test]
571    async fn test_middleware_context_from_request() {
572        use crate::request::method::ElifMethod as HttpMethod;
573        use crate::response::headers::{ElifHeaderMap, ElifHeaderName, ElifHeaderValue};
574
575        let mut headers = ElifHeaderMap::new();
576        headers.insert(
577            ElifHeaderName::from_str("x-request-id").unwrap(),
578            ElifHeaderValue::from_str("req-123").unwrap(),
579        );
580        headers.insert(
581            ElifHeaderName::from_str("x-user-id").unwrap(),
582            ElifHeaderValue::from_str("user-456").unwrap(),
583        );
584
585        let request = ElifRequest::new(HttpMethod::POST, "/api/test".parse().unwrap(), headers);
586
587        let context = MiddlewareContext::from_request(&request);
588
589        assert_eq!(context.request_id, "req-123");
590        assert_eq!(context.user_id, Some("user-456".to_string()));
591        assert!(context.session_id.is_none());
592    }
593
594    #[tokio::test]
595    async fn test_middleware_group() {
596        let mut container = IocContainer::new();
597        container.bind::<TestLoggerService, TestLoggerService>();
598        container.build().expect("Container build failed");
599
600        let registry = Arc::new(
601            MiddlewareRegistryBuilder::new()
602                .container(Arc::new(container))
603                .register::<TestIocMiddleware>("group1")
604                .register::<TestIocMiddleware>("group2")
605                .build()
606                .expect("Failed to build middleware registry"),
607        );
608
609        let group = MiddlewareGroup::new(
610            "test_group".to_string(),
611            vec!["group1".to_string(), "group2".to_string()],
612            registry,
613        );
614
615        assert_eq!(group.name(), "test_group");
616        assert_eq!(group.middleware_names().len(), 2);
617
618        let middleware = group
619            .create_middleware(None)
620            .expect("Failed to create group middleware");
621
622        assert_eq!(middleware.len(), 2);
623    }
624}