Skip to main content

ullun/
app.rs

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