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