Skip to main content

ullun/
app.rs

1//! Main application orchestrator
2
3use crate::error::{Error, Result};
4use crate::handler::{DynHandler, Handler, HandlerWithParams};
5use crate::middleware::{Middleware, Next};
6use crate::request::{Params, Request};
7use crate::response::Response;
8use crate::router::Router;
9use http_body_util::BodyExt;
10use hyper::server::conn::http1;
11use hyper::service::service_fn;
12use hyper_util::rt::TokioIo;
13use std::convert::Infallible;
14use std::net::SocketAddr;
15use std::sync::Arc;
16use tokio::net::TcpListener;
17
18/// Main application struct
19pub struct App {
20    router: Router,
21    middlewares: Vec<Arc<dyn Middleware>>,
22    max_body_size: Option<usize>, // in bytes
23}
24
25/// Route group builder
26pub struct RouteGroup {
27    prefix: String,
28    routes: Vec<(String, String, DynHandler)>, // (method, path, handler)
29    middlewares: Vec<Arc<dyn Middleware>>,
30}
31
32impl App {
33    pub fn new() -> Self {
34        Self {
35            router: Router::new(),
36            middlewares: Vec::new(),
37            max_body_size: Some(10 * 1024 * 1024), // 10 MB default
38        }
39    }
40
41    /// Set maximum request body size in bytes (None = unlimited)
42    pub fn max_body_size(mut self, size: Option<usize>) -> Self {
43        self.max_body_size = size;
44        self
45    }
46
47    /// Create a route group with a common prefix
48    pub fn group(mut self, prefix: &str, configure: impl FnOnce(RouteGroup) -> RouteGroup) -> Self {
49        let group = RouteGroup::new(prefix);
50        let group = configure(group);
51        
52        // Register all routes with prefix
53        for (method, path, handler) in group.routes {
54            let full_path = format!("{}{}", group.prefix, path);
55            if let Err(e) = self.router.insert(&method, &full_path, handler) {
56                panic!("Failed to register {} route {}: {}", method, full_path, e);
57            }
58        }
59        
60        // Note: Group-level middlewares not yet supported - would apply globally
61        // Use route-specific middleware wrapping instead for now
62        if !group.middlewares.is_empty() {
63            eprintln!("Warning: RouteGroup.middleware() is not yet implemented. Middlewares will be ignored.");
64        }
65        
66        self
67    }
68
69    /// Add GET route
70    pub fn get<H>(mut self, path: &str, handler: H) -> Self
71    where
72        H: Handler,
73    {
74        if let Err(e) = self
75            .router
76            .insert("GET", path, Arc::new(handler) as DynHandler)
77        {
78            panic!("Failed to register GET route {}: {}", path, e);
79        }
80        self
81    }
82
83    /// Add GET route with path params extractor
84    pub fn get_with_params<F, Fut>(mut self, path: &str, handler: F) -> Self
85    where
86        F: Fn(Params) -> Fut + Send + Sync + 'static,
87        Fut: std::future::Future<Output = Result<Response>> + Send + 'static,
88    {
89        if let Err(e) = self.router.insert(
90            "GET",
91            path,
92            Arc::new(HandlerWithParams::new(handler)) as DynHandler,
93        ) {
94            panic!("Failed to register GET route {}: {}", path, e);
95        }
96        self
97    }
98
99    /// Add POST route
100    pub fn post<H>(mut self, path: &str, handler: H) -> Self
101    where
102        H: Handler,
103    {
104        if let Err(e) = self
105            .router
106            .insert("POST", path, Arc::new(handler) as DynHandler)
107        {
108            panic!("Failed to register POST route {}: {}", path, e);
109        }
110        self
111    }
112
113    /// Add PUT route
114    pub fn put<H>(mut self, path: &str, handler: H) -> Self
115    where
116        H: Handler,
117    {
118        if let Err(e) = self
119            .router
120            .insert("PUT", path, Arc::new(handler) as DynHandler)
121        {
122            panic!("Failed to register PUT route {}: {}", path, e);
123        }
124        self
125    }
126
127    /// Add DELETE route
128    pub fn delete<H>(mut self, path: &str, handler: H) -> Self
129    where
130        H: Handler,
131    {
132        if let Err(e) = self
133            .router
134            .insert("DELETE", path, Arc::new(handler) as DynHandler)
135        {
136            panic!("Failed to register DELETE route {}: {}", path, e);
137        }
138        self
139    }
140
141    /// Add PATCH route
142    pub fn patch<H>(mut self, path: &str, handler: H) -> Self
143    where
144        H: Handler,
145    {
146        if let Err(e) = self
147            .router
148            .insert("PATCH", path, Arc::new(handler) as DynHandler)
149        {
150            panic!("Failed to register PATCH route {}: {}", path, e);
151        }
152        self
153    }
154
155    /// Add middleware
156    pub fn middleware<M>(mut self, middleware: M) -> Self
157    where
158        M: Middleware,
159    {
160        self.middlewares.push(Arc::new(middleware));
161        self
162    }
163
164    /// Serve static files from a directory
165    pub fn serve_static(mut self, route_prefix: &str, dir_path: &str) -> Self {
166        use crate::static_files::StaticFileHandler;
167        let handler = StaticFileHandler::new(dir_path);
168        // Use catch-all parameter syntax: {*param} matches rest of path
169        let prefix = route_prefix.trim_end_matches('/');
170        let route = format!("{}/*filepath", prefix);
171        if let Err(e) = self.router.insert("GET", &route, Arc::new(handler) as DynHandler) {
172            panic!("Failed to register static file route {}: {}", route, e);
173        }
174        self
175    }
176
177    /// Run the server with graceful shutdown on CTRL+C
178    pub async fn run(self, addr: &str) -> Result<()> {
179        let addr: SocketAddr = addr
180            .parse()
181            .map_err(|e| Error::internal(format!("Invalid address: {}", e)))?;
182
183        let listener = TcpListener::bind(addr)
184            .await
185            .map_err(|e| Error::internal(format!("Failed to bind: {}", e)))?;
186
187        println!("šŸš€ ullun-api listening on http://{}", addr);
188
189        let router = Arc::new(self.router);
190        let middlewares = Arc::new(self.middlewares);
191        let max_body_size = self.max_body_size;
192
193        // Graceful shutdown signal
194        let shutdown = async {
195            tokio::signal::ctrl_c()
196                .await
197                .expect("Failed to install CTRL+C handler");
198            println!("\nšŸ›‘ Shutting down gracefully...");
199        };
200
201        tokio::select! {
202            _ = shutdown => {
203                println!("āœ… Server stopped");
204                Ok(())
205            }
206            result = Self::serve_loop(listener, router, middlewares, max_body_size) => result
207        }
208    }
209
210    async fn serve_loop(
211        listener: TcpListener,
212        router: Arc<Router>,
213        middlewares: Arc<Vec<Arc<dyn Middleware>>>,
214        max_body_size: Option<usize>,
215    ) -> Result<()> {
216        loop {
217            let (stream, _) = listener
218                .accept()
219                .await
220                .map_err(|e| Error::internal(format!("Failed to accept: {}", e)))?;
221
222            let io = TokioIo::new(stream);
223            let router = router.clone();
224            let middlewares = middlewares.clone();
225
226            tokio::task::spawn(async move {
227                if let Err(err) = http1::Builder::new()
228                    .serve_connection(
229                        io,
230                        service_fn(|req| {
231                            let router = router.clone();
232                            let middlewares = middlewares.clone();
233                            async move {
234                                Ok::<_, Infallible>(
235                                    handle_request(req, router, middlewares, max_body_size).await,
236                                )
237                            }
238                        }),
239                    )
240                    .await
241                {
242                    eprintln!("Error serving connection: {:?}", err);
243                }
244            });
245        }
246    }
247}
248
249impl Default for App {
250    fn default() -> Self {
251        Self::new()
252    }
253}
254
255impl RouteGroup {
256    fn new(prefix: &str) -> Self {
257        let prefix = prefix.trim_end_matches('/');
258        Self {
259            prefix: prefix.to_string(),
260            routes: Vec::new(),
261            middlewares: Vec::new(),
262        }
263    }
264
265    pub fn get<H>(mut self, path: &str, handler: H) -> Self
266    where
267        H: Handler,
268    {
269        self.routes.push(("GET".to_string(), path.to_string(), Arc::new(handler) as DynHandler));
270        self
271    }
272
273    pub fn post<H>(mut self, path: &str, handler: H) -> Self
274    where
275        H: Handler,
276    {
277        self.routes.push(("POST".to_string(), path.to_string(), Arc::new(handler) as DynHandler));
278        self
279    }
280
281    pub fn put<H>(mut self, path: &str, handler: H) -> Self
282    where
283        H: Handler,
284    {
285        self.routes.push(("PUT".to_string(), path.to_string(), Arc::new(handler) as DynHandler));
286        self
287    }
288
289    pub fn delete<H>(mut self, path: &str, handler: H) -> Self
290    where
291        H: Handler,
292    {
293        self.routes.push(("DELETE".to_string(), path.to_string(), Arc::new(handler) as DynHandler));
294        self
295    }
296
297    pub fn patch<H>(mut self, path: &str, handler: H) -> Self
298    where
299        H: Handler,
300    {
301        self.routes.push(("PATCH".to_string(), path.to_string(), Arc::new(handler) as DynHandler));
302        self
303    }
304
305    pub fn middleware<M>(mut self, middleware: M) -> Self
306    where
307        M: Middleware,
308    {
309        self.middlewares.push(Arc::new(middleware));
310        self
311    }
312}
313
314async fn handle_request(
315    req: hyper::Request<hyper::body::Incoming>,
316    router: Arc<Router>,
317    middlewares: Arc<Vec<Arc<dyn Middleware>>>,
318    max_body_size: Option<usize>,
319) -> hyper::Response<http_body_util::Full<bytes::Bytes>> {
320    let method = req.method().to_string();
321    let path = req.uri().path().to_string();
322
323    // Convert hyper request to our Request type
324    let (parts, body) = req.into_parts();
325
326    // Enforce body size limit during streaming
327    let body_bytes = if let Some(max_size) = max_body_size {
328        // Check Content-Length header first (fast path)
329        if let Some(content_length) = parts.headers.get(http::header::CONTENT_LENGTH) {
330            if let Ok(length_str) = content_length.to_str() {
331                if let Ok(length) = length_str.parse::<usize>() {
332                    if length > max_size {
333                        let error_response = Response::from_error(Error::custom(
334                            413,
335                            format!("Request body too large (max {} bytes)", max_size),
336                        ));
337                        return error_response.into_hyper();
338                    }
339                }
340            }
341        }
342        
343        // Limit actual stream reading (prevents bypass via chunked encoding)
344        let limited = http_body_util::Limited::new(body, max_size);
345        match limited.collect().await {
346            Ok(collected) => collected.to_bytes(),
347            Err(_) => {
348                // Body exceeded limit during reading
349                let error_response = Response::from_error(Error::custom(
350                    413,
351                    format!("Request body too large (max {} bytes)", max_size),
352                ));
353                return error_response.into_hyper();
354            }
355        }
356    } else {
357        match body.collect().await {
358            Ok(collected) => collected.to_bytes(),
359            Err(_) => bytes::Bytes::new(),
360        }
361    };
362
363    let mut request = Request::new(parts.method, parts.uri, parts.headers, body_bytes);
364
365    // Match route
366    let result = match router.match_route(&method, &path) {
367        Ok((handler, params)) => {
368            request.params = params;
369
370            // Run middlewares + handler
371            if middlewares.is_empty() {
372                handler.call(request).await
373            } else {
374                // Create middleware chain
375                let mut chain = middlewares.to_vec();
376
377                // Add final handler as last middleware
378                let final_handler = Arc::new(move |req: Request, _next: Next| {
379                    let h = handler.clone();
380                    async move { h.call(req).await }
381                });
382                chain.push(final_handler as Arc<dyn Middleware>);
383
384                let next = Next::new(Arc::new(chain));
385                next.run(request).await
386            }
387        }
388        Err(e) => Err(e),
389    };
390
391    // Convert Result to Response
392    let response = match result {
393        Ok(resp) => resp,
394        Err(err) => Response::from_error(err),
395    };
396
397    response.into_hyper()
398}