elif_http/server/server.rs
1//! # Elif HTTP Server
2//!
3//! A NestJS-like HTTP server that provides a clean, intuitive API while using Axum under the hood.
4//! Users interact only with framework types - Axum is completely abstracted away.
5
6use super::lifecycle::{build_internal_router, start_server};
7use crate::{
8 config::HttpConfig,
9 errors::{HttpError, HttpResult},
10 middleware::v2::{Middleware, MiddlewarePipelineV2},
11 routing::ElifRouter,
12};
13use elif_core::container::IocContainer;
14use std::net::SocketAddr;
15use std::sync::Arc;
16use tracing::info;
17
18/// The main HTTP server - NestJS-like experience
19///
20/// # Example
21///
22/// ```rust,no_run
23/// use elif_http::{Server, HttpConfig};
24/// use elif_core::container::{IocContainer, ServiceBinder};
25/// use std::sync::Arc;
26///
27/// #[tokio::main]
28/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
29/// let mut container = IocContainer::new();
30/// container.build().unwrap();
31/// let server = Server::new(container, HttpConfig::default())?;
32/// server.listen("0.0.0.0:3000").await?;
33/// Ok(())
34/// }
35/// ```
36pub struct Server {
37 container: Arc<IocContainer>,
38 config: HttpConfig,
39 router: Option<ElifRouter>,
40 middleware: MiddlewarePipelineV2,
41}
42
43impl Server {
44 /// Create a new server instance
45 pub fn new(container: IocContainer, config: HttpConfig) -> HttpResult<Self> {
46 Ok(Self {
47 container: Arc::new(container),
48 config,
49 router: None,
50 middleware: MiddlewarePipelineV2::new(),
51 })
52 }
53
54 /// Create a new server with existing Arc<IocContainer>
55 pub fn with_container(container: Arc<IocContainer>, config: HttpConfig) -> HttpResult<Self> {
56 Ok(Self {
57 container,
58 config,
59 router: None,
60 middleware: MiddlewarePipelineV2::new(),
61 })
62 }
63
64 /// Set custom routes using framework router
65 ///
66 /// # Example
67 ///
68 /// ```rust,no_run
69 /// use elif_http::{Server, ElifRouter, HttpConfig, ElifRequest, HttpResult};
70 /// use elif_core::container::{IocContainer, ServiceBinder};
71 /// use std::sync::Arc;
72 ///
73 /// # async fn get_users(_req: ElifRequest) -> HttpResult<&'static str> { Ok("users") }
74 /// # async fn create_user(_req: ElifRequest) -> HttpResult<&'static str> { Ok("created") }
75 ///
76 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
77 /// let mut container = IocContainer::new();
78 /// container.build().unwrap();
79 /// let mut server = Server::new(container, HttpConfig::default())?;
80 ///
81 /// let router = ElifRouter::new()
82 /// .get("/users", get_users)
83 /// .post("/users", create_user);
84 ///
85 /// server.use_router(router);
86 /// # Ok(())
87 /// # }
88 /// ```
89 pub fn use_router(&mut self, router: ElifRouter) -> &mut Self {
90 self.router = Some(router);
91 self
92 }
93
94 /// Add middleware to the server
95 ///
96 /// # Example
97 ///
98 /// ```rust,no_run
99 /// use elif_http::{Server, HttpConfig};
100 /// use elif_core::container::{IocContainer, ServiceBinder};
101 /// use std::sync::Arc;
102 ///
103 /// # #[derive(Debug)]
104 /// # struct LoggingMiddleware;
105 /// # impl LoggingMiddleware {
106 /// # fn default() -> Self { LoggingMiddleware }
107 /// # }
108 /// # impl elif_http::Middleware for LoggingMiddleware {
109 /// # fn handle(&self, request: elif_http::ElifRequest, next: elif_http::Next) -> std::pin::Pin<Box<dyn std::future::Future<Output = elif_http::ElifResponse> + Send + 'static>> {
110 /// # next.call(request)
111 /// # }
112 /// # }
113 ///
114 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
115 /// let mut container = IocContainer::new();
116 /// container.build().unwrap();
117 /// let mut server = Server::new(container, HttpConfig::default())?;
118 /// server.use_middleware(LoggingMiddleware::default());
119 /// # Ok(())
120 /// # }
121 /// ```
122 pub fn use_middleware<M>(&mut self, middleware: M) -> &mut Self
123 where
124 M: Middleware + 'static,
125 {
126 self.middleware.add_mut(middleware);
127 self
128 }
129
130 /// Enable debug mode for detailed middleware execution logs
131 ///
132 /// # Example
133 ///
134 /// ```rust,no_run
135 /// use elif_http::{Server, HttpConfig};
136 /// use elif_core::container::{IocContainer, ServiceBinder};
137 ///
138 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
139 /// let mut container = IocContainer::new();
140 /// container.build().unwrap();
141 /// let mut server = Server::new(container, HttpConfig::default())?;
142 /// server.debug_middleware(true);
143 /// # Ok(())
144 /// # }
145 /// ```
146 pub fn debug_middleware(&mut self, enable: bool) -> &mut Self {
147 if enable {
148 // Convert current pipeline to debug pipeline (for future use)
149 let _debug_pipeline = self.middleware.clone().with_debug();
150 println!("š Middleware debug mode enabled");
151 println!(" Middleware chain: {:?}", self.middleware.names());
152 } else {
153 println!("š Middleware debug mode disabled");
154 }
155 self
156 }
157
158 /// Inspect all registered middleware and show execution order
159 ///
160 /// # Example
161 ///
162 /// ```rust,no_run
163 /// use elif_http::{Server, HttpConfig};
164 /// use elif_core::container::{IocContainer, ServiceBinder};
165 ///
166 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
167 /// let mut container = IocContainer::new();
168 /// container.build().unwrap();
169 /// let server = Server::new(container, HttpConfig::default())?;
170 /// server.inspect_middleware();
171 /// # Ok(())
172 /// # }
173 /// ```
174 pub fn inspect_middleware(&self) {
175 let info = self.middleware.debug_info();
176
177 println!("š Middleware Pipeline Inspection");
178 println!(" Total middleware: {}", info.middleware_count);
179
180 if info.middleware_count == 0 {
181 println!(" No middleware registered");
182 return;
183 }
184
185 println!(" Execution order:");
186 for (index, name) in info.middleware_names.iter().enumerate() {
187 println!(" {}. {}", index + 1, name);
188 }
189
190 println!("\nš” Tip: Use debug_middleware(true) for runtime execution logs");
191 }
192
193 /// Add profiler middleware to log timing for each request
194 ///
195 /// # Example
196 ///
197 /// ```rust,no_run
198 /// use elif_http::{Server, HttpConfig};
199 /// use elif_core::container::{IocContainer, ServiceBinder};
200 ///
201 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
202 /// let mut container = IocContainer::new();
203 /// container.build().unwrap();
204 /// let mut server = Server::new(container, HttpConfig::default())?;
205 /// server.use_profiler();
206 /// # Ok(())
207 /// # }
208 /// ```
209 pub fn use_profiler(&mut self) -> &mut Self {
210 use crate::middleware::v2::ProfilerMiddleware;
211 self.use_middleware(ProfilerMiddleware::new());
212 println!("š Profiler middleware enabled - request timings will be logged");
213 self
214 }
215
216 /// Start the server on the specified address
217 ///
218 /// # Example
219 ///
220 /// ```rust,no_run
221 /// # use elif_http::{Server, HttpConfig};
222 /// # use elif_core::container::IocContainer;
223 /// # use std::sync::Arc;
224 /// #
225 /// # #[tokio::main]
226 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
227 /// # let mut container = IocContainer::new();
228 /// # container.build().unwrap();
229 /// # let server = Server::new(container, HttpConfig::default())?;
230 /// server.listen("0.0.0.0:3000").await?;
231 /// # Ok(())
232 /// # }
233 /// ```
234 pub async fn listen<A: Into<String>>(self, addr: A) -> HttpResult<()> {
235 let addr_str = addr.into();
236 let socket_addr: SocketAddr = addr_str
237 .parse()
238 .map_err(|e| HttpError::config(format!("Invalid address '{}': {}", addr_str, e)))?;
239
240 self.listen_on(socket_addr).await
241 }
242
243 /// Start the server on the specified SocketAddr
244 pub async fn listen_on(self, addr: SocketAddr) -> HttpResult<()> {
245 info!("š Starting Elif server on {}", addr);
246 info!(
247 "š Health check endpoint: {}",
248 self.config.health_check_path
249 );
250
251 // Build the internal router
252 let axum_router = build_internal_router(
253 self.container.clone(),
254 self.config.clone(),
255 self.router,
256 self.middleware,
257 )
258 .await?;
259
260 // Start the server
261 start_server(addr, axum_router).await?;
262
263 info!("š Server shut down gracefully");
264 Ok(())
265 }
266
267 // Getter methods for testing and inspection
268 pub fn container(&self) -> &Arc<IocContainer> {
269 &self.container
270 }
271
272 pub fn config(&self) -> &HttpConfig {
273 &self.config
274 }
275
276 pub fn router(&self) -> Option<&ElifRouter> {
277 self.router.as_ref()
278 }
279
280 pub fn middleware(&self) -> &MiddlewarePipelineV2 {
281 &self.middleware
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288 use crate::testing::create_test_container;
289
290 #[test]
291 fn test_server_creation() {
292 let container = create_test_container();
293 let config = HttpConfig::default();
294
295 let server = Server::with_container(container, config);
296 assert!(server.is_ok());
297 }
298
299 #[test]
300 fn test_server_with_arc_container() {
301 let container = create_test_container();
302 let config = HttpConfig::default();
303
304 let server = Server::with_container(container, config);
305 assert!(server.is_ok());
306 }
307
308 #[test]
309 fn test_server_configuration() {
310 let container = create_test_container();
311 let config = HttpConfig::default();
312 let server = Server::with_container(container, config).unwrap();
313
314 assert_eq!(server.config().health_check_path, "/health");
315 }
316
317 #[test]
318 fn test_middleware_debugging_tools() {
319 let container = create_test_container();
320 let config = HttpConfig::default();
321 let mut server = Server::with_container(container, config).unwrap();
322
323 // Add some middleware
324 server
325 .use_middleware(crate::middleware::v2::LoggingMiddleware)
326 .use_middleware(crate::middleware::v2::factories::cors())
327 .use_profiler();
328
329 // Test inspect_middleware - should not panic
330 server.inspect_middleware();
331
332 // Test debug_middleware - should not panic
333 server.debug_middleware(true);
334 server.debug_middleware(false);
335
336 // Verify middleware count
337 assert_eq!(server.middleware().len(), 3); // LoggingMiddleware + CorsMiddleware + ProfilerMiddleware
338 }
339}