hyperlocal_with_windows/
server.rs

1use hyper::{
2    body::{Body, Incoming},
3    service::service_fn,
4    Request, Response,
5};
6use hyper_util::rt::TokioIo;
7use std::{future::Future, io, path::Path};
8use tokio::net::UnixListener;
9
10/// A cross-platform wrapper around a [`tokio::net::UnixListener`] or a Windows
11/// equivalent. Using this type allows code using Unix sockets to be written
12/// once and run on both Unix and Windows.
13///
14/// [`tokio::net::UnixListener`]:
15///     https://docs.rs/tokio/1.39.1/tokio/net/struct.UnixListener.html
16#[derive(Debug)]
17pub struct CommonUnixListener(UnixListener);
18
19impl CommonUnixListener {
20    /// Open a Unix socket.
21    ///
22    /// # Errors
23    ///
24    /// This function will return any errors that occur while trying to open the
25    /// provided path.
26    pub fn bind(path: impl AsRef<Path>) -> io::Result<Self> {
27        UnixListener::bind(path).map(Self)
28    }
29}
30
31/// Extension trait for provisioning a hyper HTTP server over a Unix domain
32/// socket.
33///
34/// # Example
35///
36/// ```rust
37/// use hyper::Response;
38/// use hyperlocal_with_windows::{
39///     remove_unix_socket_if_present, CommonUnixListener, UnixListenerExt,
40/// };
41///
42/// let future = async move {
43///     let path = std::env::temp_dir().join("hyperlocal.sock");
44///     remove_unix_socket_if_present(&path)
45///         .await
46///         .expect("removed any existing unix socket");
47///     let listener = CommonUnixListener::bind(path).expect("parsed unix path");
48///
49///     listener
50///         .serve(|| {
51///             |_request| async {
52///                 Ok::<_, hyper::Error>(Response::new("Hello, world.".to_string()))
53///             }
54///         })
55///         .await
56///         .expect("failed to serve a connection")
57/// };
58/// ```
59pub trait UnixListenerExt {
60    /// Indefinitely accept and respond to connections.
61    ///
62    /// Pass a function which will generate the function which responds to
63    /// all requests for an individual connection.
64    fn serve<MakeResponseFn, ResponseFn, ResponseFuture, B, E>(
65        self,
66        f: MakeResponseFn,
67    ) -> impl Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>>
68    where
69        MakeResponseFn: Fn() -> ResponseFn,
70        ResponseFn: Fn(Request<Incoming>) -> ResponseFuture,
71        ResponseFuture: Future<Output = Result<Response<B>, E>>,
72        B: Body + 'static,
73        <B as Body>::Error: std::error::Error + Send + Sync,
74        E: std::error::Error + Send + Sync + 'static;
75}
76
77impl UnixListenerExt for UnixListener {
78    fn serve<MakeServiceFn, ResponseFn, ResponseFuture, B, E>(
79        self,
80        f: MakeServiceFn,
81    ) -> impl Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>>
82    where
83        MakeServiceFn: Fn() -> ResponseFn,
84        ResponseFn: Fn(Request<Incoming>) -> ResponseFuture,
85        ResponseFuture: Future<Output = Result<Response<B>, E>>,
86        B: Body + 'static,
87        <B as Body>::Error: std::error::Error + Send + Sync,
88        E: std::error::Error + Send + Sync + 'static,
89    {
90        async move {
91            loop {
92                let (stream, _) = self.accept().await?;
93                let io = TokioIo::new(stream);
94
95                let svc_fn = service_fn(f());
96
97                hyper::server::conn::http1::Builder::new()
98                    // On OSX, disabling keep alive prevents serve_connection from
99                    // blocking and later returning an Err derived from E_NOTCONN.
100                    .keep_alive(false)
101                    .serve_connection(io, svc_fn)
102                    .await?;
103            }
104        }
105    }
106}
107
108impl UnixListenerExt for CommonUnixListener {
109    fn serve<MakeServiceFn, ResponseFn, ResponseFuture, B, E>(
110        self,
111        f: MakeServiceFn,
112    ) -> impl Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>>
113    where
114        MakeServiceFn: Fn() -> ResponseFn,
115        ResponseFn: Fn(Request<Incoming>) -> ResponseFuture,
116        ResponseFuture: Future<Output = Result<Response<B>, E>>,
117        B: Body + 'static,
118        <B as Body>::Error: std::error::Error + Send + Sync,
119        E: std::error::Error + Send + Sync + 'static,
120    {
121        self.0.serve(f)
122    }
123}