Skip to main content

rustio_core/
server.rs

1//! Hyper-backed HTTP/1 server.
2//!
3//! Bind an address with [`Server::bind`] and serve either a raw handler
4//! via [`Server::serve`] or a [`Router`] via [`Server::serve_router`].
5
6use std::convert::Infallible;
7use std::future::Future;
8use std::net::SocketAddr;
9use std::sync::Arc;
10
11use hyper::server::conn::http1;
12use hyper::service::service_fn;
13use hyper_util::rt::TokioIo;
14use tokio::net::TcpListener;
15
16use crate::http::{Request, Response};
17use crate::router::Router;
18
19pub struct Server {
20    addr: SocketAddr,
21}
22
23impl Server {
24    pub fn bind(addr: SocketAddr) -> Self {
25        Self { addr }
26    }
27
28    pub async fn serve<F, Fut>(self, handler: F) -> std::io::Result<()>
29    where
30        F: Fn(Request) -> Fut + Clone + Send + Sync + 'static,
31        Fut: Future<Output = Response> + Send + 'static,
32    {
33        let listener = TcpListener::bind(self.addr).await?;
34        eprintln!("rustio-core: listening on http://{}", self.addr);
35
36        loop {
37            let (stream, peer) = listener.accept().await?;
38            let io = TokioIo::new(stream);
39            let handler = handler.clone();
40
41            tokio::spawn(async move {
42                let service = service_fn(move |raw: hyper::Request<hyper::body::Incoming>| {
43                    let handler = handler.clone();
44                    async move {
45                        // `peer` is captured by `Copy` (SocketAddr is
46                        // Copy); attached to every request off this
47                        // connection so handlers can read it via
48                        // `Request::peer_addr`.
49                        let req = Request::new(raw, Some(peer));
50                        Ok::<Response, Infallible>(handler(req).await)
51                    }
52                });
53
54                if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
55                    eprintln!("rustio-core: connection error: {err}");
56                }
57            });
58        }
59    }
60
61    pub async fn serve_router(self, router: Router) -> std::io::Result<()> {
62        let router = Arc::new(router);
63        self.serve(move |req| {
64            let router = router.clone();
65            async move { router.dispatch(req).await }
66        })
67        .await
68    }
69
70    /// Serve a router on an already-bound `TcpListener`.
71    ///
72    /// Use when the caller needs to own the socket — for example to
73    /// bind to port 0 and read back the kernel-assigned address
74    /// before spawning the server (integration tests, pre-fork
75    /// servers that drop privileges after binding).
76    pub async fn serve_router_on(listener: TcpListener, router: Router) -> std::io::Result<()> {
77        let router = Arc::new(router);
78        loop {
79            let (stream, peer) = listener.accept().await?;
80            let io = TokioIo::new(stream);
81            let router = router.clone();
82
83            tokio::spawn(async move {
84                let service = service_fn(move |raw: hyper::Request<hyper::body::Incoming>| {
85                    let router = router.clone();
86                    async move {
87                        let req = Request::new(raw, Some(peer));
88                        Ok::<Response, Infallible>(router.dispatch(req).await)
89                    }
90                });
91                if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
92                    eprintln!("rustio-core: connection error: {err}");
93                }
94            });
95        }
96    }
97}