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