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    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, h2)) = config.accept(stream).await {
565                                    let info = ConnInfo { peer, secure: true, client_cert };
566                                    serve_connection(tls_stream, info, shared, shutdown, done, _guard, h2).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                            false,
580                        ));
581                    }
582                    Err(e) => {
583                        // typically out of file descriptors: back off instead of dying
584                        eprintln!("Failed to accept connection: {}", e);
585                        tokio::time::sleep(Duration::from_millis(100)).await;
586                    }
587                },
588            }
589        }
590
591        let _ = trigger.send(true);
592        drop(done_tx);
593        drop(listener);
594        let drain = async {
595            let _ = done_rx.recv().await;
596            for task in tasks {
597                let _ = task.await;
598            }
599        };
600        if tokio::time::timeout(grace, drain).await.is_err() {
601            eprintln!(
602                "Shutdown grace period of {:?} elapsed, stopping anyway",
603                grace
604            );
605        }
606        Ok(())
607    }
608
609    /// Prepare the app for [`crate::testing::TestClient`]: routes and
610    /// middleware as in a running server, but no listener and no background tasks
611    pub(crate) fn into_in_process(self) -> InProcess {
612        let app = self.finalize();
613        InProcess {
614            shared: Arc::new(app.into_shared(Shutdown::never())),
615        }
616    }
617
618    fn into_shared(self, shutdown: Shutdown) -> Shared {
619        Shared {
620            router: self.router,
621            middlewares: self.middlewares,
622            max_body_size: self.max_body_size,
623            body_limits: self.body_limits,
624            state: Arc::new(self.state),
625            shutdown,
626            header_read_timeout: self.header_read_timeout,
627        }
628    }
629
630    /// Last step before serving: publish the OpenAPI document, now that all
631    /// routes are known.
632    fn finalize(mut self) -> Self {
633        let Some(info) = self.openapi.take() else {
634            return self;
635        };
636        let spec = openapi::build(&info, self.router.registered(), &self.docs);
637        let spec = bytes::Bytes::from(serde_json::to_vec(&spec).unwrap_or_default());
638
639        let spec_handler = {
640            let spec = spec.clone();
641            move |_req: Request| {
642                let spec = spec.clone();
643                async move {
644                    Ok(Response::ok()
645                        .with_header("content-type", "application/json")
646                        .with_body(spec))
647                }
648            }
649        };
650        self.insert_hidden("GET", &info.spec_path, Arc::new(spec_handler));
651
652        if let Some(docs_path) = &info.docs_path {
653            let page = openapi::swagger_ui_html(&info.title, &info.spec_path);
654            let page_handler = move |_req: Request| {
655                let page = page.clone();
656                async move { Ok(Response::html(page)) }
657            };
658            self.insert_hidden("GET", docs_path, Arc::new(page_handler));
659        }
660        self
661    }
662
663    fn insert_hidden(&mut self, method: &str, path: &str, handler: DynHandler) {
664        if let Err(e) = self
665            .router
666            .insert_as(RouteKind::Hidden, method, path, handler)
667        {
668            panic!("Failed to register {} route {}: {}", method, path, e);
669        }
670    }
671}
672
673/// One connection, until it closes or shutdown drains it
674async fn serve_connection<S>(
675    stream: S,
676    info: ConnInfo,
677    shared: Arc<Shared>,
678    shutdown: Shutdown,
679    _done: tokio::sync::mpsc::Sender<()>,
680    _guard: ConnGuard,
681    h2: bool,
682) where
683    S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + 'static,
684{
685    let io = TokioIo::new(stream);
686    let info = Arc::new(info);
687    let service = service_fn(|req| {
688        let (shared, info) = (shared.clone(), info.clone());
689        async move { Ok::<_, Infallible>(handle_request(req, &info, shared).await) }
690    });
691
692    if h2 {
693        // negotiated through ALPN on a TLS connection
694        let conn = http2::Builder::new(TokioExecutor::new())
695            .timer(TokioTimer::new())
696            .max_concurrent_streams(128)
697            // drop peers that went away without closing
698            .keep_alive_interval(Duration::from_secs(30))
699            .keep_alive_timeout(Duration::from_secs(20))
700            .serve_connection(io, service);
701        drive(conn, &shutdown, |c| c.graceful_shutdown()).await;
702    } else {
703        let mut builder = http1::Builder::new();
704        if let Some(timeout) = shared.header_read_timeout {
705            builder
706                .timer(TokioTimer::new())
707                .header_read_timeout(timeout);
708        }
709        let conn = builder.serve_connection(io, service).with_upgrades();
710        drive(conn, &shutdown, |c| c.graceful_shutdown()).await;
711    }
712}
713
714/// Run a connection to its end; on shutdown idle connections close now and
715/// busy ones after their response
716async fn drive<C>(conn: C, shutdown: &Shutdown, graceful: impl Fn(std::pin::Pin<&mut C>))
717where
718    C: std::future::Future<Output = std::result::Result<(), hyper::Error>>,
719{
720    tokio::pin!(conn);
721    let mut draining = false;
722    loop {
723        tokio::select! {
724            result = conn.as_mut() => {
725                // a header timeout is routine (slow or idle client), not worth a log line
726                if let Err(err) = &result {
727                    if !err.is_timeout() {
728                        eprintln!("Error serving connection: {:?}", err);
729                    }
730                }
731                return;
732            }
733            _ = shutdown.wait(), if !draining => {
734                graceful(conn.as_mut());
735                draining = true;
736            }
737        }
738    }
739}
740
741/// CTRL+C everywhere, plus SIGTERM on Unix (what container runtimes send)
742async fn termination_signal() {
743    #[cfg(unix)]
744    {
745        use tokio::signal::unix::{signal, SignalKind};
746        match signal(SignalKind::terminate()) {
747            Ok(mut term) => {
748                tokio::select! {
749                    _ = tokio::signal::ctrl_c() => {}
750                    _ = term.recv() => {}
751                }
752                return;
753            }
754            Err(e) => eprintln!("Cannot listen for SIGTERM: {}", e),
755        }
756    }
757    let _ = tokio::signal::ctrl_c().await;
758}
759
760impl Default for App {
761    fn default() -> Self {
762        Self::new()
763    }
764}
765
766impl RouteGroup {
767    fn new(prefix: &str) -> Self {
768        let prefix = prefix.trim_end_matches('/');
769        Self {
770            prefix: prefix.to_string(),
771            routes: Vec::new(),
772            middlewares: Vec::new(),
773            docs: Vec::new(),
774            body_limit: None,
775            ws_paths: Vec::new(),
776        }
777    }
778
779    /// Accept WebSocket connections on `path` below the group prefix; the
780    /// group's middleware (authentication, ticket checks, ...) runs before the
781    /// upgrade (feature `websocket`)
782    #[cfg(feature = "websocket")]
783    pub fn ws_with<F, Fut>(
784        mut self,
785        path: &str,
786        options: crate::websocket::WsOptions,
787        handler: F,
788    ) -> Self
789    where
790        F: Fn(Request, crate::websocket::WebSocket) -> Fut + Send + Sync + 'static,
791        Fut: std::future::Future<Output = ()> + Send + 'static,
792    {
793        let route = crate::websocket::WsRoute::new(handler, options);
794        self.routes.push((
795            "GET".to_string(),
796            path.to_string(),
797            Arc::new(route) as DynHandler,
798        ));
799        self.ws_paths.push(path.to_string());
800        self
801    }
802
803    /// Request body limit for this group's routes (`None` = unlimited),
804    /// overriding the application default
805    pub fn max_body_size(mut self, limit: Option<usize>) -> Self {
806        self.body_limit = Some(limit);
807        self
808    }
809
810    /// Describe a route of this group for the OpenAPI document; `path` is
811    /// relative to the group prefix, as given to `get`/`post`/...
812    pub fn doc(mut self, method: &str, path: &str, operation: Operation) -> Self {
813        self.docs
814            .push((method.to_ascii_uppercase(), path.to_string(), operation));
815        self
816    }
817
818    pub fn get<H>(mut self, path: &str, handler: H) -> Self
819    where
820        H: Handler,
821    {
822        self.routes.push((
823            "GET".to_string(),
824            path.to_string(),
825            Arc::new(handler) as DynHandler,
826        ));
827        self
828    }
829
830    pub fn post<H>(mut self, path: &str, handler: H) -> Self
831    where
832        H: Handler,
833    {
834        self.routes.push((
835            "POST".to_string(),
836            path.to_string(),
837            Arc::new(handler) as DynHandler,
838        ));
839        self
840    }
841
842    pub fn put<H>(mut self, path: &str, handler: H) -> Self
843    where
844        H: Handler,
845    {
846        self.routes.push((
847            "PUT".to_string(),
848            path.to_string(),
849            Arc::new(handler) as DynHandler,
850        ));
851        self
852    }
853
854    pub fn delete<H>(mut self, path: &str, handler: H) -> Self
855    where
856        H: Handler,
857    {
858        self.routes.push((
859            "DELETE".to_string(),
860            path.to_string(),
861            Arc::new(handler) as DynHandler,
862        ));
863        self
864    }
865
866    pub fn patch<H>(mut self, path: &str, handler: H) -> Self
867    where
868        H: Handler,
869    {
870        self.routes.push((
871            "PATCH".to_string(),
872            path.to_string(),
873            Arc::new(handler) as DynHandler,
874        ));
875        self
876    }
877
878    /// Add middleware that runs only for the routes of this group
879    ///
880    /// It runs after the application-wide middleware, in registration order,
881    /// regardless of whether it is added before or after the routes.
882    pub fn middleware<M>(mut self, middleware: M) -> Self
883    where
884        M: Middleware,
885    {
886        self.middlewares.push(Arc::new(middleware));
887        self
888    }
889}
890
891async fn handle_request(
892    req: hyper::Request<hyper::body::Incoming>,
893    conn: &ConnInfo,
894    shared: Arc<Shared>,
895) -> hyper::Response<crate::response::HyperBody> {
896    let path = req.uri().path().to_string();
897    let max_body_size = body_limit_for(&shared.body_limits, &path, shared.max_body_size);
898
899    // Convert hyper request to our Request type
900    let (parts, body) = req.into_parts();
901
902    // Enforce the body size limit while reading, so it also holds for chunked
903    // bodies without a Content-Length
904    let body_bytes = match max_body_size {
905        Some(max_size) => match read_limited(body, max_size).await {
906            Ok(bytes) => bytes,
907            Err(too_large) => return too_large.into_hyper(max_size),
908        },
909        None => match body.collect().await {
910            Ok(collected) => collected.to_bytes(),
911            Err(_) => bytes::Bytes::new(),
912        },
913    };
914
915    let mut headers = parts.headers;
916    // HTTP/2 carries the host as :authority and sends no Host header
917    if !headers.contains_key(http::header::HOST) {
918        if let Some(authority) = parts.uri.authority() {
919            if let Ok(v) = http::HeaderValue::from_str(authority.as_str()) {
920                headers.insert(http::header::HOST, v);
921            }
922        }
923    }
924    let mut request = Request::new(parts.method, parts.uri, headers, body_bytes);
925    request.extensions = parts.extensions;
926    run_pipeline(request, conn, &shared).await.into_hyper()
927}
928
929/// An app prepared for in-process calls without sockets, see [`crate::testing`]
930pub(crate) struct InProcess {
931    shared: Arc<Shared>,
932}
933
934impl InProcess {
935    pub(crate) async fn call(
936        &self,
937        request: Request,
938        peer: SocketAddr,
939        secure: bool,
940    ) -> hyper::Response<crate::response::HyperBody> {
941        let max = body_limit_for(
942            &self.shared.body_limits,
943            request.uri.path(),
944            self.shared.max_body_size,
945        );
946        if let Some(max) = max.filter(|max| request.body.len() > *max) {
947            return BodyTooLarge.into_hyper(max);
948        }
949        let conn = ConnInfo {
950            peer,
951            secure,
952            client_cert: None,
953        };
954        run_pipeline(request, &conn, &self.shared)
955            .await
956            .into_hyper()
957    }
958}
959
960/// Route, middleware and error conversion for one fully read request
961async fn run_pipeline(mut request: Request, conn: &ConnInfo, shared: &Shared) -> Response {
962    let Shared {
963        router,
964        middlewares,
965        state,
966        shutdown,
967        ..
968    } = shared;
969    let method = request.method.to_string();
970    let is_head = request.method == http::Method::HEAD;
971    let path = request.uri.path().to_string();
972    request.remote_addr = Some(conn.peer);
973    request.secure = conn.secure;
974    if let Some(cert) = &conn.client_cert {
975        request
976            .extensions
977            .insert(crate::request::ClientCertificate(cert.clone()));
978    }
979    request.state = Some(state.clone());
980    request.shutdown = Some(shutdown.clone());
981
982    // Match route. A miss still runs the middleware chain (CORS preflight,
983    // request ids, logging, security headers) and ends in 404 / 405 / OPTIONS.
984    let route = match router.match_route_with_pattern(&method, &path) {
985        Ok((handler, params, pattern)) => {
986            request.params = params;
987            request.matched_path = Some(pattern);
988            Route::Found(handler)
989        }
990        Err(_) => Route::Miss {
991            allow: router.allowed_methods(&path),
992        },
993    };
994
995    let result = if middlewares.is_empty() {
996        dispatch(route, request).await
997    } else {
998        // Route handler (or the miss) runs as the last link of the chain
999        let mut chain = middlewares.to_vec();
1000        let final_handler = Arc::new(move |req: Request, _next: Next| {
1001            let route = route.clone();
1002            async move { dispatch(route, req).await }
1003        });
1004        chain.push(final_handler as Arc<dyn Middleware>);
1005
1006        Next::new(Arc::new(chain)).run(request).await
1007    };
1008
1009    // Convert Result to Response
1010    let response = match result {
1011        Ok(resp) => resp,
1012        Err(err) => Response::from_error(err),
1013    };
1014
1015    // HEAD: same status and headers as GET, no body
1016    if is_head {
1017        response.into_head()
1018    } else {
1019        response
1020    }
1021}
1022
1023/// Most of a rejected request body that is read and thrown away before the 413
1024/// is sent. Answering without reading makes the OS reset the connection on
1025/// close, and clients (notably on Windows) then lose the response.
1026const DRAIN_CAP: u64 = 8 * 1024 * 1024;
1027const DRAIN_TIME: Duration = Duration::from_secs(3);
1028
1029struct BodyTooLarge;
1030
1031impl BodyTooLarge {
1032    fn into_hyper(self, max_size: usize) -> hyper::Response<crate::response::HyperBody> {
1033        Response::from_error(Error::custom(
1034            413,
1035            format!("Request body too large (max {} bytes)", max_size),
1036        ))
1037        // the connection is not reusable: unread body bytes may remain
1038        .with_header("connection", "close")
1039        .into_hyper()
1040    }
1041}
1042
1043/// Collect at most `max` bytes; on overflow discard (a bounded amount of) the rest
1044async fn read_limited(
1045    mut body: hyper::body::Incoming,
1046    max: usize,
1047) -> std::result::Result<bytes::Bytes, BodyTooLarge> {
1048    use bytes::BufMut;
1049    use hyper::body::Body;
1050
1051    // a declared length is rejected before a single byte is buffered
1052    if body.size_hint().lower() > max as u64 {
1053        drain(&mut body, 0).await;
1054        return Err(BodyTooLarge);
1055    }
1056
1057    let mut buf = bytes::BytesMut::new();
1058    while let Some(frame) = body.frame().await {
1059        let Ok(frame) = frame else {
1060            return Ok(bytes::Bytes::new());
1061        };
1062        if let Ok(data) = frame.into_data() {
1063            if buf.len() + data.len() > max {
1064                drain(&mut body, (buf.len() + data.len()) as u64).await;
1065                return Err(BodyTooLarge);
1066            }
1067            buf.put(data);
1068        }
1069    }
1070    Ok(buf.freeze())
1071}
1072
1073/// Read and drop up to [`DRAIN_CAP`] bytes (counting `already` read), for at most [`DRAIN_TIME`]
1074async fn drain(body: &mut hyper::body::Incoming, already: u64) {
1075    let _ = tokio::time::timeout(DRAIN_TIME, async {
1076        let mut seen = already;
1077        while seen < DRAIN_CAP {
1078            match body.frame().await {
1079                Some(Ok(frame)) => {
1080                    if let Ok(data) = frame.into_data() {
1081                        seen += data.len() as u64;
1082                    }
1083                }
1084                _ => return,
1085            }
1086        }
1087    })
1088    .await;
1089}
1090
1091/// Limit for `path`: the longest matching prefix rule, else the default
1092fn body_limit_for(
1093    rules: &[(String, Option<usize>)],
1094    path: &str,
1095    default: Option<usize>,
1096) -> Option<usize> {
1097    rules
1098        .iter()
1099        .filter(|(prefix, _)| {
1100            prefix.is_empty()
1101                || path == prefix
1102                || path
1103                    .strip_prefix(prefix.as_str())
1104                    .is_some_and(|rest| rest.starts_with('/'))
1105        })
1106        .max_by_key(|(prefix, _)| prefix.len())
1107        .map(|(_, limit)| *limit)
1108        .unwrap_or(default)
1109}
1110
1111/// Outcome of routing
1112#[derive(Clone)]
1113enum Route {
1114    Found(DynHandler),
1115    /// No handler for this method; `allow` is empty when the path is unknown
1116    Miss {
1117        allow: Vec<&'static str>,
1118    },
1119}
1120
1121async fn dispatch(route: Route, req: Request) -> Result<Response> {
1122    match route {
1123        Route::Found(handler) => handler.call(req).await,
1124        Route::Miss { allow } if allow.is_empty() => Err(Error::not_found("Route not found")),
1125        Route::Miss { allow } => {
1126            let allow = allow.join(", ");
1127            if req.method == http::Method::OPTIONS {
1128                // automatic OPTIONS for known paths (CORS preflights are answered
1129                // earlier, by the cors middleware)
1130                Ok(Response::new(http::StatusCode::NO_CONTENT).with_header("allow", allow))
1131            } else {
1132                Ok(
1133                    Response::from_error(Error::custom(405, "Method not allowed"))
1134                        .with_header("allow", allow),
1135                )
1136            }
1137        }
1138    }
1139}