elif_http/
server_with_middleware.rs1use std::sync::Arc;
6use axum::{
7 Router,
8 routing::get,
9 response::Json,
10};
11use serde_json::{json, Value};
12use tokio::signal;
13use tracing::info;
14use elif_core::Container;
15
16use crate::{
17 HttpConfig, HttpError, HttpResult,
18 MiddlewarePipeline, LoggingMiddleware, TimingMiddleware,
19};
20
21pub struct MiddlewareHttpServer {
23 container: Arc<Container>,
24 config: HttpConfig,
25 middleware: MiddlewarePipeline,
26}
27
28impl MiddlewareHttpServer {
29 pub fn new(container: Arc<Container>, config: HttpConfig) -> HttpResult<Self> {
31 let server = Self {
32 container,
33 config,
34 middleware: MiddlewarePipeline::new(),
35 };
36
37 Ok(server)
38 }
39
40 pub fn with_middleware(
42 container: Arc<Container>,
43 config: HttpConfig,
44 middleware: MiddlewarePipeline
45 ) -> HttpResult<Self> {
46 Ok(Self {
47 container,
48 config,
49 middleware,
50 })
51 }
52
53 pub fn with_default_middleware(mut self) -> Self {
55 self.middleware = self.middleware
56 .add(LoggingMiddleware::new())
57 .add(TimingMiddleware::new());
58 self
59 }
60
61 pub fn middleware(&self) -> &MiddlewarePipeline {
63 &self.middleware
64 }
65
66 pub fn middleware_mut(&mut self) -> &mut MiddlewarePipeline {
68 &mut self.middleware
69 }
70
71 pub async fn run(&self) -> HttpResult<()> {
73 let addr = "127.0.0.1:3000".to_string(); let router = Router::new()
77 .route("/health", get(middleware_health_check))
78 .route("/middleware/info", get(middleware_info_handler))
79 .with_state((self.container.clone(), self.middleware.len()));
80
81 let listener = tokio::net::TcpListener::bind(&addr)
82 .await
83 .map_err(|e| HttpError::startup(format!("Failed to bind to {}: {}", addr, e)))?;
84
85 info!("HTTP server with middleware listening on {}", addr);
86 info!("Middleware pipeline: {:?}", self.middleware.names());
87
88 axum::serve(listener, router)
89 .with_graceful_shutdown(shutdown_signal())
90 .await
91 .map_err(|e| HttpError::startup(format!("Server failed: {}", e)))?;
92
93 info!("HTTP server stopped gracefully");
94 Ok(())
95 }
96}
97
98pub async fn middleware_health_check() -> Json<Value> {
100 Json(json!({
101 "status": "healthy",
102 "timestamp": chrono::Utc::now().to_rfc3339(),
103 "version": "0.1.0",
104 "server": "middleware-enabled",
105 "middleware": "active"
106 }))
107}
108
109pub async fn middleware_info_handler(
111 axum::extract::State((container, middleware_count)): axum::extract::State<(Arc<Container>, usize)>
112) -> Json<Value> {
113 Json(json!({
114 "middleware_count": middleware_count,
115 "container_registered": true, "timestamp": chrono::Utc::now().to_rfc3339(),
117 }))
118}
119
120async fn shutdown_signal() {
122 let ctrl_c = async {
123 signal::ctrl_c()
124 .await
125 .expect("failed to install Ctrl+C handler");
126 };
127
128 #[cfg(unix)]
129 let terminate = async {
130 let mut signal = signal::unix::signal(signal::unix::SignalKind::terminate())
131 .expect("failed to install signal handler");
132 signal.recv().await;
133 };
134
135 #[cfg(not(unix))]
136 let terminate = std::future::pending::<()>();
137
138 tokio::select! {
139 _ = ctrl_c => {},
140 _ = terminate => {},
141 }
142
143 info!("Shutdown signal received, starting graceful shutdown");
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::{LoggingMiddleware, TimingMiddleware};
150
151 struct MockContainer;
153
154 #[tokio::test]
155 async fn test_middleware_pipeline() {
156 let pipeline = MiddlewarePipeline::new()
157 .add(LoggingMiddleware::new())
158 .add(TimingMiddleware::new());
159
160 assert_eq!(pipeline.len(), 2);
161 assert_eq!(
162 pipeline.names(),
163 vec!["LoggingMiddleware", "TimingMiddleware"]
164 );
165 }
166
167 #[tokio::test]
168 async fn test_custom_middleware_pipeline() {
169 let pipeline = MiddlewarePipeline::new()
170 .add(TimingMiddleware::new())
171 .add(LoggingMiddleware::new().with_body_logging());
172
173 assert_eq!(pipeline.len(), 2);
174 assert_eq!(
175 pipeline.names(),
176 vec!["TimingMiddleware", "LoggingMiddleware"]
177 );
178 }
179}