hyperlocal_v07/
server.rs

1use std::{io, path::Path};
2
3use hyper::server::{Builder, Server};
4
5use conn::SocketIncoming;
6
7pub(crate) mod conn {
8    use futures_util::stream::Stream;
9    use hyper::server::accept::Accept;
10    use pin_project::pin_project;
11    use std::{
12        io,
13        path::Path,
14        pin::Pin,
15        task::{Context, Poll},
16    };
17    use tokio::net::{UnixListener, UnixStream};
18
19    /// A stream of connections from binding to a socket.
20    #[pin_project]
21    #[derive(Debug)]
22    pub struct SocketIncoming {
23        listener: UnixListener,
24    }
25
26    impl SocketIncoming {
27        /// Creates a new `SocketIncoming` binding to provided socket path.
28        pub fn bind(path: impl AsRef<Path>) -> Result<Self, std::io::Error> {
29            let listener = UnixListener::bind(path)?;
30
31            Ok(Self { listener })
32        }
33
34        /// Creates a new `SocketIncoming` from Tokio's `UnixListener`
35        ///
36        /// ```rust,ignore
37        /// let socket = SocketIncoming::from_listener(unix_listener);
38        /// let server = Server::builder(socket).serve(service);
39        /// ```
40        pub fn from_listener(listener: UnixListener) -> Self {
41            Self { listener }
42        }
43    }
44
45    impl Accept for SocketIncoming {
46        type Conn = UnixStream;
47        type Error = io::Error;
48
49        fn poll_accept(
50            self: Pin<&mut Self>,
51            cx: &mut Context<'_>,
52        ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
53            let listener = self.project().listener;
54            let mut incoming = listener.incoming();
55            Pin::new(&mut incoming).poll_next(cx)
56        }
57    }
58
59    impl From<UnixListener> for SocketIncoming {
60        fn from(listener: UnixListener) -> Self {
61            Self::from_listener(listener)
62        }
63    }
64}
65
66/// Extension trait for provisioning a hyper HTTP server over a Unix domain
67/// socket.
68///
69/// # Example
70///
71/// ```rust
72/// use hyper::{Server, Body, Response, service::{make_service_fn, service_fn}};
73/// use hyperlocal::UnixServerExt;
74///
75/// # async {
76/// let make_service = make_service_fn(|_| async {
77///     Ok::<_, hyper::Error>(service_fn(|_req| async {
78///         Ok::<_, hyper::Error>(Response::new(Body::from("It works!")))
79///     }))
80/// });
81///
82/// Server::bind_unix("/tmp/hyperlocal.sock")?.serve(make_service).await?;
83/// # Ok::<_, Box<dyn std::error::Error + Send + Sync>>(())
84/// # };
85/// ```
86pub trait UnixServerExt {
87    /// Convenience method for constructing a Server listening on a Unix socket.
88    fn bind_unix(path: impl AsRef<Path>) -> Result<Builder<SocketIncoming>, io::Error>;
89}
90
91impl UnixServerExt for Server<SocketIncoming, ()> {
92    fn bind_unix(path: impl AsRef<Path>) -> Result<Builder<SocketIncoming>, io::Error> {
93        let incoming = SocketIncoming::bind(path)?;
94        Ok(Server::builder(incoming))
95    }
96}