Skip to main content

ullun/
app.rs

1//! Main application orchestrator
2
3use crate::error::{Error, Result};
4use crate::handler::{BoxFuture, DynHandler, Handler, HandlerWithParams};
5use crate::limits::{self, ConnGuard, ConnLimits};
6use crate::middleware::{Layered, Middleware, Next};
7use crate::openapi::{self, OpenApiInfo, Operation, RouteKind};
8use crate::request::{Params, Request, StateMap};
9use crate::response::Response;
10use crate::router::Router;
11use crate::shutdown::{Context, Shutdown};
12use http_body_util::BodyExt;
13use hyper::server::conn::http1;
14use hyper::service::service_fn;
15use hyper_util::rt::{TokioIo, TokioTimer};
16use std::collections::HashMap;
17use std::convert::Infallible;
18use std::net::SocketAddr;
19use std::sync::Arc;
20use std::time::Duration;
21use tokio::net::TcpListener;
22
23/// Main application struct
24pub struct App {
25    router: Router,
26    middlewares: Vec<Arc<dyn Middleware>>,
27    max_body_size: Option<usize>, // in bytes
28    docs: HashMap<(String, String), Operation>,
29    openapi: Option<OpenApiInfo>,
30    state: StateMap,
31    background: Vec<BackgroundTask>,
32    shutdown_grace: Duration,
33    body_limits: Vec<(String, Option<usize>)>,
34    header_read_timeout: Option<Duration>,
35    max_connections: Option<usize>,
36    max_connections_per_ip: Option<usize>,
37    #[cfg(feature = "tls")]
38    tls: Option<crate::tls::TlsConfig>,
39    #[cfg(feature = "tls")]
40    tls_reload: Option<Duration>,
41}
42
43/// Facts about one connection that every request on it inherits
44struct ConnInfo {
45    peer: SocketAddr,
46    secure: bool,
47    client_cert: Option<Vec<u8>>,
48}
49
50type BackgroundTask = Box<dyn FnOnce(Context) -> BoxFuture<'static, ()> + Send>;
51
52/// Everything a running server shares between connections
53struct Shared {
54    router: Router,
55    middlewares: Vec<Arc<dyn Middleware>>,
56    max_body_size: Option<usize>,
57    body_limits: Vec<(String, Option<usize>)>,
58    state: Arc<StateMap>,
59    shutdown: Shutdown,
60    header_read_timeout: Option<Duration>,
61}
62
63/// Route group builder
64pub struct RouteGroup {
65    prefix: String,
66    routes: Vec<(String, String, DynHandler)>, // (method, path, handler)
67    middlewares: Vec<Arc<dyn Middleware>>,
68    docs: Vec<(String, String, Operation)>, // (method, path relative to prefix, docs)
69    body_limit: Option<Option<usize>>,
70    /// Paths registered with `ws_with`, documented as WebSocket upgrades
71    ws_paths: Vec<String>,
72}
73
74impl App {
75    pub fn new() -> Self {
76        Self {
77            router: Router::new(),
78            middlewares: Vec::new(),
79            max_body_size: Some(10 * 1024 * 1024), // 10 MB default
80            docs: HashMap::new(),
81            openapi: None,
82            state: StateMap::default(),
83            background: Vec::new(),
84            shutdown_grace: Duration::from_secs(10),
85            body_limits: Vec::new(),
86            header_read_timeout: Some(Duration::from_secs(30)),
87            max_connections: None,
88            max_connections_per_ip: None,
89            #[cfg(feature = "tls")]
90            tls: None,
91            #[cfg(feature = "tls")]
92            tls_reload: None,
93        }
94    }
95
96    /// Set maximum request body size in bytes (None = unlimited)
97    pub fn max_body_size(mut self, size: Option<usize>) -> Self {
98        self.max_body_size = size;
99        self
100    }
101
102    /// Different request body limit for everything under `prefix`
103    ///
104    /// `None` removes the limit. The longest matching prefix wins over the
105    /// default from [`App::max_body_size`], e.g. a small default with a large
106    /// limit for an upload route:
107    /// `App::new().max_body_size(Some(64 * 1024)).body_limit("/api/files", Some(25 << 20))`.
108    pub fn body_limit(mut self, prefix: &str, limit: Option<usize>) -> Self {
109        self.body_limits
110            .push((prefix.trim_end_matches('/').to_string(), limit));
111        self
112    }
113
114    /// Register shared application state, readable in handlers and
115    /// middleware with [`Request::state`]
116    ///
117    /// One value per type; registering the same type again replaces it. Use
118    /// interior mutability (`Mutex`, `RwLock`, atomics) for mutable state: the
119    /// value is shared by all requests and is not cloned.
120    ///
121    /// ```
122    /// use ullun::prelude::*;
123    /// use std::sync::atomic::{AtomicU64, Ordering};
124    ///
125    /// #[derive(Default)]
126    /// struct Hits(AtomicU64);
127    ///
128    /// async fn count(req: Request) -> Result<Response> {
129    ///     let hits = req.state::<Hits>()?.0.fetch_add(1, Ordering::Relaxed) + 1;
130    ///     Ok(Response::text(hits.to_string()))
131    /// }
132    ///
133    /// let app = App::new().state(Hits::default()).get("/", count);
134    /// ```
135    pub fn state<T: Send + Sync + 'static>(mut self, value: T) -> Self {
136        self.state.insert(value);
137        self
138    }
139
140    /// Run `task` alongside the server, started when it starts serving
141    ///
142    /// The task gets a [`Context`] with the shared state and the shutdown
143    /// signal; it should return when `ctx.sleep(..)` reports `false` or
144    /// `ctx.shutdown().wait()` completes. Finished tasks are awaited during
145    /// shutdown, for at most [`App::shutdown_grace`].
146    ///
147    /// ```
148    /// use std::time::Duration;
149    /// use ullun::prelude::*;
150    ///
151    /// struct Jobs;
152    ///
153    /// let app = App::new().state(Jobs).background(|ctx: Context| async move {
154    ///     while ctx.sleep(Duration::from_secs(60)).await {
155    ///         let _jobs = ctx.state::<Jobs>().unwrap();
156    ///         // purge expired sessions, refresh caches, ...
157    ///     }
158    /// });
159    /// ```
160    pub fn background<F, Fut>(mut self, task: F) -> Self
161    where
162        F: FnOnce(Context) -> Fut + Send + 'static,
163        Fut: std::future::Future<Output = ()> + Send + 'static,
164    {
165        self.background
166            .push(Box::new(move |ctx| Box::pin(task(ctx))));
167        self
168    }
169
170    /// Serve HTTPS instead of HTTP (feature `tls`), see [`crate::tls`]
171    #[cfg(feature = "tls")]
172    pub fn tls(mut self, config: crate::tls::TlsConfig) -> Self {
173        self.tls = Some(config);
174        self
175    }
176
177    /// Check the certificate files for changes this often and switch to renewed
178    /// certificates without a restart (feature `tls`; needs
179    /// `TlsConfig::from_pem_files`). A failed reload is logged and the previous
180    /// certificate stays in use.
181    #[cfg(feature = "tls")]
182    pub fn tls_reload_every(mut self, interval: Duration) -> Self {
183        self.tls_reload = Some(interval);
184        self
185    }
186
187    /// How long a client may take to send the request headers (default 30
188    /// seconds, `None` disables). Also applies to an idle keep-alive connection
189    /// waiting for its next request, and stops slowloris-style clients that
190    /// trickle bytes to hold connections open.
191    pub fn header_read_timeout(mut self, timeout: impl Into<Option<Duration>>) -> Self {
192        self.header_read_timeout = timeout.into();
193        self
194    }
195
196    /// Cap on simultaneously open connections. Further connections get an
197    /// immediate `503` with `Retry-After` and are closed (plain `503` bytes
198    /// even on a TLS port, which a TLS client reports as a failed handshake).
199    pub fn max_connections(mut self, max: usize) -> Self {
200        self.max_connections = Some(max.max(1));
201        self
202    }
203
204    /// Cap on simultaneously open connections from one peer IP, so a single
205    /// client cannot use up [`App::max_connections`]. Behind a reverse proxy
206    /// every connection comes from the proxy: leave this unset there.
207    pub fn max_connections_per_ip(mut self, max: usize) -> Self {
208        self.max_connections_per_ip = Some(max.max(1));
209        self
210    }
211
212    /// How long shutdown waits for in-flight requests and background tasks
213    /// (default 10 seconds)
214    pub fn shutdown_grace(mut self, grace: Duration) -> Self {
215        self.shutdown_grace = grace;
216        self
217    }
218
219    /// Create a route group with a common prefix
220    pub fn group(mut self, prefix: &str, configure: impl FnOnce(RouteGroup) -> RouteGroup) -> Self {
221        let group = RouteGroup::new(prefix);
222        let group = configure(group);
223
224        if let Some(limit) = group.body_limit {
225            self.body_limits.push((group.prefix.clone(), limit));
226        }
227        for (method, path, op) in group.docs {
228            let full_path = format!("{}{}", group.prefix, path);
229            self.docs.insert((method, full_path), op);
230        }
231
232        // Group middlewares wrap only this group's routes (inside the global ones)
233        let middlewares = group.middlewares;
234        for (method, path, handler) in group.routes {
235            let full_path = format!("{}{}", group.prefix, path);
236            let handler: DynHandler = if middlewares.is_empty() {
237                handler
238            } else {
239                Arc::new(Layered::new(middlewares.clone(), handler))
240            };
241            let kind = if method == "GET" && group.ws_paths.contains(&path) {
242                RouteKind::WebSocket
243            } else {
244                RouteKind::Http
245            };
246            if let Err(e) = self.router.insert_as(kind, &method, &full_path, handler) {
247                panic!("Failed to register {} route {}: {}", method, full_path, e);
248            }
249        }
250
251        self
252    }
253
254    /// Describe a registered route for the OpenAPI document
255    ///
256    /// `path` is the path exactly as registered, e.g. `/users/{id}`.
257    pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
258        self.docs
259            .insert((method.to_ascii_uppercase(), path.to_string()), operation);
260        self
261    }
262
263    /// Publish an OpenAPI 3.1 document of all routes (and optionally Swagger UI)
264    ///
265    /// See the [`openapi`] module.
266    pub fn openapi(mut self, info: OpenApiInfo) -> Self {
267        self.openapi = Some(info);
268        self
269    }
270
271    /// Add GET route
272    pub fn get<H>(mut self, path: &str, handler: H) -> Self
273    where
274        H: Handler,
275    {
276        if let Err(e) = self
277            .router
278            .insert("GET", path, Arc::new(handler) as DynHandler)
279        {
280            panic!("Failed to register GET route {}: {}", path, e);
281        }
282        self
283    }
284
285    /// Add GET route with path params extractor
286    pub fn get_with_params<F, Fut>(mut self, path: &str, handler: F) -> Self
287    where
288        F: Fn(Params) -> Fut + Send + Sync + 'static,
289        Fut: std::future::Future<Output = Result<Response>> + Send + 'static,
290    {
291        if let Err(e) = self.router.insert(
292            "GET",
293            path,
294            Arc::new(HandlerWithParams::new(handler)) as DynHandler,
295        ) {
296            panic!("Failed to register GET route {}: {}", path, e);
297        }
298        self
299    }
300
301    /// Add POST route
302    pub fn post<H>(mut self, path: &str, handler: H) -> Self
303    where
304        H: Handler,
305    {
306        if let Err(e) = self
307            .router
308            .insert("POST", path, Arc::new(handler) as DynHandler)
309        {
310            panic!("Failed to register POST route {}: {}", path, e);
311        }
312        self
313    }
314
315    /// Add PUT route
316    pub fn put<H>(mut self, path: &str, handler: H) -> Self
317    where
318        H: Handler,
319    {
320        if let Err(e) = self
321            .router
322            .insert("PUT", path, Arc::new(handler) as DynHandler)
323        {
324            panic!("Failed to register PUT route {}: {}", path, e);
325        }
326        self
327    }
328
329    /// Add DELETE route
330    pub fn delete<H>(mut self, path: &str, handler: H) -> Self
331    where
332        H: Handler,
333    {
334        if let Err(e) = self
335            .router
336            .insert("DELETE", path, Arc::new(handler) as DynHandler)
337        {
338            panic!("Failed to register DELETE route {}: {}", path, e);
339        }
340        self
341    }
342
343    /// Add PATCH route
344    pub fn patch<H>(mut self, path: &str, handler: H) -> Self
345    where
346        H: Handler,
347    {
348        if let Err(e) = self
349            .router
350            .insert("PATCH", path, Arc::new(handler) as DynHandler)
351        {
352            panic!("Failed to register PATCH route {}: {}", path, e);
353        }
354        self
355    }
356
357    /// Accept WebSocket connections on `path` (feature `websocket`)
358    ///
359    /// The handler receives the original request (path params, query, headers,
360    /// extensions set by middleware) and the upgraded socket, and runs on its
361    /// own task until it returns.
362    #[cfg(feature = "websocket")]
363    pub fn ws<F, Fut>(self, path: &str, handler: F) -> Self
364    where
365        F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
366        Fut: std::future::Future<Output = ()> + Send + 'static,
367    {
368        self.ws_with(path, crate::websocket::WsOptions::default(), handler)
369    }
370
371    /// Like [`App::ws`] with explicit size limit and allowed origins
372    #[cfg(feature = "websocket")]
373    pub fn ws_with<F, Fut>(
374        mut self,
375        path: &str,
376        options: crate::websocket::WsOptions,
377        handler: F,
378    ) -> Self
379    where
380        F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
381        Fut: std::future::Future<Output = ()> + Send + 'static,
382    {
383        let route = crate::websocket::WsRoute::new(handler, options);
384        if let Err(e) = self.router.insert_as(
385            RouteKind::WebSocket,
386            "GET",
387            path,
388            Arc::new(route) as DynHandler,
389        ) {
390            panic!("Failed to register WebSocket route {}: {}", path, e);
391        }
392        self
393    }
394
395    /// Add middleware
396    pub fn middleware<M>(mut self, middleware: M) -> Self
397    where
398        M: Middleware,
399    {
400        self.middlewares.push(Arc::new(middleware));
401        self
402    }
403
404    /// Serve static files from a directory
405    pub fn serve_static(mut self, route_prefix: &str, dir_path: &str) -> Self {
406        use crate::static_files::StaticFileHandler;
407        let handler = StaticFileHandler::new(dir_path);
408        // matchit 0.9 catch-all syntax: {*param} matches the rest of the path
409        let prefix = route_prefix.trim_end_matches('/');
410        let handler: DynHandler = Arc::new(handler);
411        let route = format!("{}/{{*filepath}}", prefix);
412        if let Err(e) = self
413            .router
414            .insert_as(RouteKind::Hidden, "GET", &route, handler.clone())
415        {
416            panic!("Failed to register static file route {}: {}", route, e);
417        }
418        // The catch-all needs at least one segment, so also serve the prefix
419        // root (-> index.html). Skipped when the user already owns that route.
420        let root = if prefix.is_empty() { "/" } else { prefix };
421        let _ = self
422            .router
423            .insert_as(RouteKind::Hidden, "GET", root, handler.clone());
424        if !prefix.is_empty() {
425            let _ =
426                self.router
427                    .insert_as(RouteKind::Hidden, "GET", &format!("{}/", prefix), handler);
428        }
429        self
430    }
431
432    /// Run the server until CTRL+C or SIGTERM, then shut down gracefully
433    ///
434    /// Stops accepting connections, lets in-flight requests finish and waits
435    /// for background tasks, each for at most [`App::shutdown_grace`].
436    pub async fn run(self, addr: &str) -> Result<()> {
437        let addr: SocketAddr = addr
438            .parse()
439            .map_err(|e| Error::internal(format!("Invalid address: {}", e)))?;
440
441        let listener = TcpListener::bind(addr)
442            .await
443            .map_err(|e| Error::internal(format!("Failed to bind: {}", e)))?;
444
445        #[cfg(feature = "tls")]
446        let scheme = if self.tls.is_some() { "https" } else { "http" };
447        #[cfg(not(feature = "tls"))]
448        let scheme = "http";
449        println!("šŸš€ ullun-api listening on {}://{}", scheme, addr);
450
451        let result = self
452            .serve_with_shutdown(listener, async {
453                termination_signal().await;
454                println!("\nšŸ›‘ Shutting down gracefully...");
455            })
456            .await;
457        println!("āœ… Server stopped");
458        result
459    }
460
461    /// Serve on an already bound listener until the future is dropped.
462    ///
463    /// Useful for tests, benchmarks and embedding: bind to port `0`, read the
464    /// assigned port from `listener.local_addr()`, then call `serve`.
465    /// Unlike [`App::run`], this does not install signal handlers; use
466    /// [`App::serve_with_shutdown`] for a graceful stop.
467    pub async fn serve(self, listener: TcpListener) -> Result<()> {
468        self.serve_with_shutdown(listener, std::future::pending())
469            .await
470    }
471
472    /// Serve until `signal` completes, then shut down gracefully
473    ///
474    /// New connections are refused, idle keep-alive connections are closed,
475    /// running requests finish, the shutdown signal reaches
476    /// [`Request::shutdown_signal`] and background tasks, and the call returns
477    /// once everything has stopped or [`App::shutdown_grace`] has passed.
478    pub async fn serve_with_shutdown(
479        mut self,
480        listener: TcpListener,
481        signal: impl std::future::Future<Output = ()>,
482    ) -> Result<()> {
483        self = self.finalize();
484        let grace = self.shutdown_grace;
485        let background = std::mem::take(&mut self.background);
486        #[cfg(feature = "tls")]
487        let tls = self.tls.take();
488        #[cfg(feature = "tls")]
489        let tls_reload = self.tls_reload;
490        let conn_limits = ConnLimits::new(self.max_connections, self.max_connections_per_ip);
491        let (trigger, rx) = tokio::sync::watch::channel(false);
492        let shutdown = Shutdown::new(rx);
493        let shared = Arc::new(self.into_shared(shutdown.clone()));
494
495        #[allow(unused_mut)]
496        let mut tasks: Vec<_> = background
497            .into_iter()
498            .map(|task| {
499                let ctx = Context::new(shared.state.clone(), shutdown.clone());
500                tokio::spawn(task(ctx))
501            })
502            .collect();
503
504        #[cfg(feature = "tls")]
505        if let (Some(config), Some(every)) = (tls.clone(), tls_reload) {
506            let shutdown = shutdown.clone();
507            tasks.push(tokio::spawn(async move {
508                while shutdown.sleep(every).await {
509                    match config.reload() {
510                        Ok(true) => println!("šŸ” TLS certificate reloaded"),
511                        Ok(false) => {}
512                        Err(e) => eprintln!(
513                            "TLS certificate reload failed, keeping the previous one: {}",
514                            e.message()
515                        ),
516                    }
517                }
518            }));
519        }
520
521        // every connection holds a clone; the channel closes when all are done
522        let (done_tx, mut done_rx) = tokio::sync::mpsc::channel::<()>(1);
523        tokio::pin!(signal);
524        loop {
525            tokio::select! {
526                _ = &mut signal => break,
527                accepted = listener.accept() => match accepted {
528                    Ok((stream, peer)) => {
529                        let Some(guard) = conn_limits.admit(peer.ip()) else {
530                            tokio::spawn(limits::refuse(stream));
531                            continue;
532                        };
533                        #[cfg(feature = "tls")]
534                        if let Some(config) = tls.clone() {
535                            let (shared, shutdown, done) = (shared.clone(), shutdown.clone(), done_tx.clone());
536                            tokio::spawn(async move {
537                                let _guard = guard;
538                                // the handshake runs here, not in the accept loop: a slow
539                                // client must not hold up everybody else
540                                // scanners, plain HTTP on the TLS port and aborted handshakes just end here
541                                if let Ok((tls_stream, client_cert)) = config.accept(stream).await {
542                                    let info = ConnInfo { peer, secure: true, client_cert };
543                                    serve_connection(tls_stream, info, shared, shutdown, done, _guard).await;
544                                }
545                            });
546                            continue;
547                        }
548                        let info = ConnInfo { peer, secure: false, client_cert: None };
549                        tokio::spawn(serve_connection(
550                            stream,
551                            info,
552                            shared.clone(),
553                            shutdown.clone(),
554                            done_tx.clone(),
555                            guard,
556                        ));
557                    }
558                    Err(e) => {
559                        // typically out of file descriptors: back off instead of dying
560                        eprintln!("Failed to accept connection: {}", e);
561                        tokio::time::sleep(Duration::from_millis(100)).await;
562                    }
563                },
564            }
565        }
566
567        let _ = trigger.send(true);
568        drop(done_tx);
569        drop(listener);
570        let drain = async {
571            let _ = done_rx.recv().await;
572            for task in tasks {
573                let _ = task.await;
574            }
575        };
576        if tokio::time::timeout(grace, drain).await.is_err() {
577            eprintln!(
578                "Shutdown grace period of {:?} elapsed, stopping anyway",
579                grace
580            );
581        }
582        Ok(())
583    }
584
585    fn into_shared(self, shutdown: Shutdown) -> Shared {
586        Shared {
587            router: self.router,
588            middlewares: self.middlewares,
589            max_body_size: self.max_body_size,
590            body_limits: self.body_limits,
591            state: Arc::new(self.state),
592            shutdown,
593            header_read_timeout: self.header_read_timeout,
594        }
595    }
596
597    /// Last step before serving: publish the OpenAPI document, now that all
598    /// routes are known.
599    fn finalize(mut self) -> Self {
600        let Some(info) = self.openapi.take() else {
601            return self;
602        };
603        let spec = openapi::build(&info, self.router.registered(), &self.docs);
604        let spec = bytes::Bytes::from(serde_json::to_vec(&spec).unwrap_or_default());
605
606        let spec_handler = {
607            let spec = spec.clone();
608            move |_req: Request| {
609                let spec = spec.clone();
610                async move {
611                    Ok(Response::ok()
612                        .with_header("content-type", "application/json")
613                        .with_body(spec))
614                }
615            }
616        };
617        self.insert_hidden("GET", &info.spec_path, Arc::new(spec_handler));
618
619        if let Some(docs_path) = &info.docs_path {
620            let page = openapi::swagger_ui_html(&info.title, &info.spec_path);
621            let page_handler = move |_req: Request| {
622                let page = page.clone();
623                async move { Ok(Response::html(page)) }
624            };
625            self.insert_hidden("GET", docs_path, Arc::new(page_handler));
626        }
627        self
628    }
629
630    fn insert_hidden(&mut self, method: &str, path: &str, handler: DynHandler) {
631        if let Err(e) = self
632            .router
633            .insert_as(RouteKind::Hidden, method, path, handler)
634        {
635            panic!("Failed to register {} route {}: {}", method, path, e);
636        }
637    }
638}
639
640/// One connection, until it closes or shutdown drains it
641async fn serve_connection<S>(
642    stream: S,
643    info: ConnInfo,
644    shared: Arc<Shared>,
645    shutdown: Shutdown,
646    _done: tokio::sync::mpsc::Sender<()>,
647    _guard: ConnGuard,
648) where
649    S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
650{
651    let io = TokioIo::new(stream);
652    let info = Arc::new(info);
653    let service = service_fn(|req| {
654        let (shared, info) = (shared.clone(), info.clone());
655        async move { Ok::<_, Infallible>(handle_request(req, &info, shared).await) }
656    });
657    let mut builder = http1::Builder::new();
658    if let Some(timeout) = shared.header_read_timeout {
659        builder
660            .timer(TokioTimer::new())
661            .header_read_timeout(timeout);
662    }
663    let conn = builder.serve_connection(io, service).with_upgrades();
664    tokio::pin!(conn);
665
666    let mut draining = false;
667    loop {
668        tokio::select! {
669            result = conn.as_mut() => {
670                // a header timeout is routine (slow or idle client), not worth a log line
671                if let Err(err) = &result {
672                    if !err.is_timeout() {
673                        eprintln!("Error serving connection: {:?}", err);
674                    }
675                }
676                return;
677            }
678            _ = shutdown.wait(), if !draining => {
679                // idle connections close now, busy ones after their response
680                conn.as_mut().graceful_shutdown();
681                draining = true;
682            }
683        }
684    }
685}
686
687/// CTRL+C everywhere, plus SIGTERM on Unix (what container runtimes send)
688async fn termination_signal() {
689    #[cfg(unix)]
690    {
691        use tokio::signal::unix::{signal, SignalKind};
692        match signal(SignalKind::terminate()) {
693            Ok(mut term) => {
694                tokio::select! {
695                    _ = tokio::signal::ctrl_c() => {}
696                    _ = term.recv() => {}
697                }
698                return;
699            }
700            Err(e) => eprintln!("Cannot listen for SIGTERM: {}", e),
701        }
702    }
703    let _ = tokio::signal::ctrl_c().await;
704}
705
706impl Default for App {
707    fn default() -> Self {
708        Self::new()
709    }
710}
711
712impl RouteGroup {
713    fn new(prefix: &str) -> Self {
714        let prefix = prefix.trim_end_matches('/');
715        Self {
716            prefix: prefix.to_string(),
717            routes: Vec::new(),
718            middlewares: Vec::new(),
719            docs: Vec::new(),
720            body_limit: None,
721            ws_paths: Vec::new(),
722        }
723    }
724
725    /// Accept WebSocket connections on `path` below the group prefix; the
726    /// group's middleware (authentication, ticket checks, ...) runs before the
727    /// upgrade (feature `websocket`)
728    #[cfg(feature = "websocket")]
729    pub fn ws_with<F, Fut>(
730        mut self,
731        path: &str,
732        options: crate::websocket::WsOptions,
733        handler: F,
734    ) -> Self
735    where
736        F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
737        Fut: std::future::Future<Output = ()> + Send + 'static,
738    {
739        let route = crate::websocket::WsRoute::new(handler, options);
740        self.routes.push((
741            "GET".to_string(),
742            path.to_string(),
743            Arc::new(route) as DynHandler,
744        ));
745        self.ws_paths.push(path.to_string());
746        self
747    }
748
749    /// Request body limit for this group's routes (`None` = unlimited),
750    /// overriding the application default
751    pub fn max_body_size(mut self, limit: Option<usize>) -> Self {
752        self.body_limit = Some(limit);
753        self
754    }
755
756    /// Describe a route of this group for the OpenAPI document; `path` is
757    /// relative to the group prefix, as given to `get`/`post`/...
758    pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
759        self.docs
760            .push((method.to_ascii_uppercase(), path.to_string(), operation));
761        self
762    }
763
764    pub fn get<H>(mut self, path: &str, handler: H) -> Self
765    where
766        H: Handler,
767    {
768        self.routes.push((
769            "GET".to_string(),
770            path.to_string(),
771            Arc::new(handler) as DynHandler,
772        ));
773        self
774    }
775
776    pub fn post<H>(mut self, path: &str, handler: H) -> Self
777    where
778        H: Handler,
779    {
780        self.routes.push((
781            "POST".to_string(),
782            path.to_string(),
783            Arc::new(handler) as DynHandler,
784        ));
785        self
786    }
787
788    pub fn put<H>(mut self, path: &str, handler: H) -> Self
789    where
790        H: Handler,
791    {
792        self.routes.push((
793            "PUT".to_string(),
794            path.to_string(),
795            Arc::new(handler) as DynHandler,
796        ));
797        self
798    }
799
800    pub fn delete<H>(mut self, path: &str, handler: H) -> Self
801    where
802        H: Handler,
803    {
804        self.routes.push((
805            "DELETE".to_string(),
806            path.to_string(),
807            Arc::new(handler) as DynHandler,
808        ));
809        self
810    }
811
812    pub fn patch<H>(mut self, path: &str, handler: H) -> Self
813    where
814        H: Handler,
815    {
816        self.routes.push((
817            "PATCH".to_string(),
818            path.to_string(),
819            Arc::new(handler) as DynHandler,
820        ));
821        self
822    }
823
824    /// Add middleware that runs only for the routes of this group
825    ///
826    /// It runs after the application-wide middleware, in registration order,
827    /// regardless of whether it is added before or after the routes.
828    pub fn middleware<M>(mut self, middleware: M) -> Self
829    where
830        M: Middleware,
831    {
832        self.middlewares.push(Arc::new(middleware));
833        self
834    }
835}
836
837async fn handle_request(
838    req: hyper::Request<hyper::body::Incoming>,
839    conn: &ConnInfo,
840    shared: Arc<Shared>,
841) -> hyper::Response<crate::response::HyperBody> {
842    let Shared {
843        router,
844        middlewares,
845        max_body_size,
846        body_limits,
847        state,
848        shutdown,
849        ..
850    } = &*shared;
851    let method = req.method().to_string();
852    let is_head = req.method() == http::Method::HEAD;
853    let path = req.uri().path().to_string();
854    let max_body_size = body_limit_for(body_limits, &path, *max_body_size);
855
856    // Convert hyper request to our Request type
857    let (parts, body) = req.into_parts();
858
859    // Enforce the body size limit while reading, so it also holds for chunked
860    // bodies without a Content-Length
861    let body_bytes = match max_body_size {
862        Some(max_size) => match read_limited(body, max_size).await {
863            Ok(bytes) => bytes,
864            Err(too_large) => return too_large.into_hyper(max_size),
865        },
866        None => match body.collect().await {
867            Ok(collected) => collected.to_bytes(),
868            Err(_) => bytes::Bytes::new(),
869        },
870    };
871
872    let mut request = Request::new(parts.method, parts.uri, parts.headers, body_bytes);
873    request.extensions = parts.extensions;
874    request.remote_addr = Some(conn.peer);
875    request.secure = conn.secure;
876    if let Some(cert) = &conn.client_cert {
877        request
878            .extensions
879            .insert(crate::request::ClientCertificate(cert.clone()));
880    }
881    request.state = Some(state.clone());
882    request.shutdown = Some(shutdown.clone());
883
884    // Match route. A miss still runs the middleware chain (CORS preflight,
885    // request ids, logging, security headers) and ends in 404 / 405 / OPTIONS.
886    let route = match router.match_route_with_pattern(&method, &path) {
887        Ok((handler, params, pattern)) => {
888            request.params = params;
889            request.matched_path = Some(pattern);
890            Route::Found(handler)
891        }
892        Err(_) => Route::Miss {
893            allow: router.allowed_methods(&path),
894        },
895    };
896
897    let result = if middlewares.is_empty() {
898        dispatch(route, request).await
899    } else {
900        // Route handler (or the miss) runs as the last link of the chain
901        let mut chain = middlewares.to_vec();
902        let final_handler = Arc::new(move |req: Request, _next: Next| {
903            let route = route.clone();
904            async move { dispatch(route, req).await }
905        });
906        chain.push(final_handler as Arc<dyn Middleware>);
907
908        Next::new(Arc::new(chain)).run(request).await
909    };
910
911    // Convert Result to Response
912    let response = match result {
913        Ok(resp) => resp,
914        Err(err) => Response::from_error(err),
915    };
916
917    // HEAD: same status and headers as GET, no body
918    let response = if is_head {
919        response.into_head()
920    } else {
921        response
922    };
923
924    response.into_hyper()
925}
926
927/// Most of a rejected request body that is read and thrown away before the 413
928/// is sent. Answering without reading makes the OS reset the connection on
929/// close, and clients (notably on Windows) then lose the response.
930const DRAIN_CAP: u64 = 8 * 1024 * 1024;
931const DRAIN_TIME: Duration = Duration::from_secs(3);
932
933struct BodyTooLarge;
934
935impl BodyTooLarge {
936    fn into_hyper(self, max_size: usize) -> hyper::Response<crate::response::HyperBody> {
937        Response::from_error(Error::custom(
938            413,
939            format!("Request body too large (max {} bytes)", max_size),
940        ))
941        // the connection is not reusable: unread body bytes may remain
942        .with_header("connection", "close")
943        .into_hyper()
944    }
945}
946
947/// Collect at most `max` bytes; on overflow discard (a bounded amount of) the rest
948async fn read_limited(
949    mut body: hyper::body::Incoming,
950    max: usize,
951) -> std::result::Result<bytes::Bytes, BodyTooLarge> {
952    use bytes::BufMut;
953    use hyper::body::Body;
954
955    // a declared length is rejected before a single byte is buffered
956    if body.size_hint().lower() > max as u64 {
957        drain(&mut body, 0).await;
958        return Err(BodyTooLarge);
959    }
960
961    let mut buf = bytes::BytesMut::new();
962    while let Some(frame) = body.frame().await {
963        let Ok(frame) = frame else {
964            return Ok(bytes::Bytes::new());
965        };
966        if let Ok(data) = frame.into_data() {
967            if buf.len() + data.len() > max {
968                drain(&mut body, (buf.len() + data.len()) as u64).await;
969                return Err(BodyTooLarge);
970            }
971            buf.put(data);
972        }
973    }
974    Ok(buf.freeze())
975}
976
977/// Read and drop up to [`DRAIN_CAP`] bytes (counting `already` read), for at most [`DRAIN_TIME`]
978async fn drain(body: &mut hyper::body::Incoming, already: u64) {
979    let _ = tokio::time::timeout(DRAIN_TIME, async {
980        let mut seen = already;
981        while seen < DRAIN_CAP {
982            match body.frame().await {
983                Some(Ok(frame)) => {
984                    if let Ok(data) = frame.into_data() {
985                        seen += data.len() as u64;
986                    }
987                }
988                _ => return,
989            }
990        }
991    })
992    .await;
993}
994
995/// Limit for `path`: the longest matching prefix rule, else the default
996fn body_limit_for(
997    rules: &[(String, Option<usize>)],
998    path: &str,
999    default: Option<usize>,
1000) -> Option<usize> {
1001    rules
1002        .iter()
1003        .filter(|(prefix, _)| {
1004            prefix.is_empty()
1005                || path == prefix
1006                || path
1007                    .strip_prefix(prefix.as_str())
1008                    .is_some_and(|rest| rest.starts_with('/'))
1009        })
1010        .max_by_key(|(prefix, _)| prefix.len())
1011        .map(|(_, limit)| *limit)
1012        .unwrap_or(default)
1013}
1014
1015/// Outcome of routing
1016#[derive(Clone)]
1017enum Route {
1018    Found(DynHandler),
1019    /// No handler for this method; `allow` is empty when the path is unknown
1020    Miss {
1021        allow: Vec<&'static str>,
1022    },
1023}
1024
1025async fn dispatch(route: Route, req: Request) -> Result<Response> {
1026    match route {
1027        Route::Found(handler) => handler.call(req).await,
1028        Route::Miss { allow } if allow.is_empty() => Err(Error::not_found("Route not found")),
1029        Route::Miss { allow } => {
1030            let allow = allow.join(", ");
1031            if req.method == http::Method::OPTIONS {
1032                // automatic OPTIONS for known paths (CORS preflights are answered
1033                // earlier, by the cors middleware)
1034                Ok(Response::new(http::StatusCode::NO_CONTENT).with_header("allow", allow))
1035            } else {
1036                Ok(
1037                    Response::from_error(Error::custom(405, "Method not allowed"))
1038                        .with_header("allow", allow),
1039                )
1040            }
1041        }
1042    }
1043}