1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//! Routerify <> Hyperlocal
//! Serve unix sockets with routerify
//!
//! Basic usage works by replacing [`RouterService`](routerify::RouterService) with [`UnixRouterService`], which adapts the
//! request in order to be compatible with [`RequestService`](routerify::RequestService).
//!
//! Since routerify requires an IP [`SocketAddr`](std::net::SocketAddr), the loopback address `127.0.0.1` with port 0 is used as a placeholder.
//! In order to access the unix socket's peer address and peer credential, the [`UnixRequestExt`] extension traits adds methods to the request object.
//!
//! # Example
//! ```no_run
//! use hyper::{Body, Response, Server};
//! use hyperlocal::UnixServerExt;
//! use routerify::{Error, Router};
//! use routerify_unixsocket::{UnixRequestExt, UnixRouterService};
//! use std::{fs, path::Path};
//!
//! #[tokio::main]
//! async fn main() {
//!     let path = Path::new("/tmp/hyperlocal.sock");
//!     if path.exists() {
//!         fs::remove_file(path).unwrap();
//!     }
//!
//!     let router: Router<Body, Error> = Router::builder()
//!         .get("/", |req| async move {
//!             let s = format!("You are: {:?}", req.unix_peer_cred());
//!             Ok(Response::new(Body::from(s)))
//!         })
//!         .build()
//!         .unwrap();
//!
//!     let service = UnixRouterService::new(router).unwrap();
//!     Server::bind_unix(path)
//!         .unwrap()
//!         .serve(service)
//!         .await
//!         .unwrap()
//! }
//! ```

mod router_service;
pub use router_service::UnixRouterService;

mod ext;
pub use ext::UnixRequestExt;

mod request_meta;

#[cfg(test)]
mod tests {
    use super::*;
    use futures::future::poll_fn;
    use hyper::{Body, Method, Request, Response};
    use routerify::{Error, Router};
    use std::task::Poll;
    use tokio::net::UnixStream;
    use tower::Service;

    #[tokio::test]
    pub async fn unixsocket() {
        const RESPONSE_TEXT: &str = "Hello world!";

        let mut router_service = {
            let router: Router<Body, Error> = Router::builder()
                .get("/", |_req| async move {
                    Ok(Response::new(Body::from(RESPONSE_TEXT)))
                })
                .build()
                .unwrap();

            UnixRouterService::new(router).unwrap()
        };

        poll_fn(|ctx| -> Poll<_> { router_service.poll_ready(ctx) })
            .await
            .expect("router service is not ready");

        let (_, server) = UnixStream::pair().unwrap();

        let mut service = router_service.call(&server).await.unwrap();
        poll_fn(|ctx| -> Poll<_> { service.poll_ready(ctx) })
            .await
            .expect("request service is not ready");

        let req = Request::builder()
            .method(Method::GET)
            .uri("/")
            .body(hyper::Body::empty())
            .unwrap();

        let resp: Response<hyper::body::Body> = service.call(req).await.unwrap();
        let body = resp.into_body();
        let body = String::from_utf8(hyper::body::to_bytes(body).await.unwrap().to_vec()).unwrap();
        assert_eq!(RESPONSE_TEXT, body);
    }
}