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
 98
 99
100
//! 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 {
                    assert!(req.unix_peer_addr().is_some());
                    assert!(req.unix_peer_cred().is_some());

                    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);
    }
}