awc_uds/
lib.rs

1//! `actix-uds` is a custom connector for [awc](https://docs.rs/awc)
2//!
3//!
4//! Usage:
5//! ```no_run compile_fail
6//! use awc::{ClientBuilder, Connector};
7//! use awc_uds::UdsConnector;
8//!
9//! let socket_path = Path::new("/run/my-server.sock");
10//! let connector = Connector::new().connector(UdsConnector::new(socket_path));
11//! let client = ClientBuilder::new().connector(connector).finish();
12//! client.get("http://localhost/").send().await
13//! ```
14
15use actix_rt::net::UnixStream;
16use actix_service::Service;
17use actix_tls::connect::{ConnectError, ConnectInfo, Connection};
18use awc::http::Uri;
19use std::future::Future;
20use std::path::{Path, PathBuf};
21use std::pin::Pin;
22use std::task::{Context, Poll};
23
24type Fut<R, E> = Pin<Box<dyn Future<Output = Result<R, E>>>>;
25
26#[derive(Clone)]
27pub struct UdsConnector(PathBuf);
28
29impl UdsConnector {
30    pub fn new(path: impl AsRef<Path>) -> Self {
31        UdsConnector(path.as_ref().to_path_buf())
32    }
33}
34
35impl Service<ConnectInfo<Uri>> for UdsConnector {
36    type Response = Connection<Uri, UnixStream>;
37    type Error = ConnectError;
38    type Future = Fut<Self::Response, Self::Error>;
39
40    fn poll_ready(&self, _ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
41        Poll::Ready(Ok(()))
42    }
43
44    fn call(&self, req: ConnectInfo<Uri>) -> Self::Future {
45        let uri = req.request().clone();
46        let path = self.0.clone();
47        let fut = async {
48            let stream = UnixStream::connect(path).await.map_err(ConnectError::Io)?;
49            Ok(Connection::new(uri, stream))
50        };
51        Box::pin(fut)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    #[actix_rt::test]
58    async fn it_works() {
59        use super::UdsConnector;
60        use actix_web::{get, App, HttpRequest, HttpServer};
61        use awc::{ClientBuilder, Connector};
62        use std::time::Duration;
63        use tempfile::tempdir;
64
65        let socket_dir = tempdir().unwrap();
66        let socket_path = socket_dir.path().join("awc-uds.sock");
67        let socket_path2 = socket_path.clone();
68
69        #[get("/")]
70        async fn hello(_req: HttpRequest) -> String {
71            "hello".to_string()
72        }
73        let _handle = actix_rt::spawn(async {
74            let _server = HttpServer::new(|| App::new().service(hello))
75                .bind_uds(socket_path)
76                .unwrap()
77                .run()
78                .await;
79        });
80
81        actix_rt::time::sleep(Duration::from_secs(1)).await;
82        let connector = Connector::new().connector(UdsConnector::new(socket_path2));
83        let client = ClientBuilder::new().connector(connector).finish();
84        let resp = client
85            .get("http://localhost/")
86            .send()
87            .await
88            .unwrap()
89            .body()
90            .await
91            .unwrap();
92        assert_eq!(resp, b"hello".as_ref());
93        println!("{:?}", resp);
94    }
95}