diff --git a/tests/integration_tests/Cargo.toml b/tests/integration_tests/Cargo.toml index 96863abba..8a6b86a22 100644 --- a/tests/integration_tests/Cargo.toml +++ b/tests/integration_tests/Cargo.toml @@ -12,7 +12,7 @@ version = "0.1.0" bytes = "1.0" futures-util = "0.3" prost = "0.9" -tokio = {version = "1.0", features = ["macros", "rt-multi-thread", "net"]} +tokio = {version = "1.0", features = ["fs", "macros", "rt-multi-thread", "net"]} tonic = {path = "../../tonic"} [dev-dependencies] diff --git a/tests/integration_tests/tests/connect_info.rs b/tests/integration_tests/tests/connect_info.rs index 936eedac1..2a4cf7f10 100644 --- a/tests/integration_tests/tests/connect_info.rs +++ b/tests/integration_tests/tests/connect_info.rs @@ -48,3 +48,80 @@ async fn getting_connect_info() { jh.await.unwrap(); } + +#[cfg(unix)] +pub mod unix { + use std::{convert::TryFrom as _, path::Path}; + + use futures_util::FutureExt; + use tokio::{ + net::{UnixListener, UnixStream}, + sync::oneshot, + }; + use tokio_stream::wrappers::UnixListenerStream; + use tonic::{ + transport::{server::UdsConnectInfo, Endpoint, Server, Uri}, + Request, Response, Status, + }; + use tower::service_fn; + + use integration_tests::pb::{test_client, test_server, Input, Output}; + + struct Svc {} + + #[tonic::async_trait] + impl test_server::Test for Svc { + async fn unary_call(&self, req: Request) -> Result, Status> { + let conn_info = req.extensions().get::().unwrap(); + + // Client-side unix sockets are unnamed. + assert!(req.remote_addr().is_none()); + assert!(conn_info.peer_addr.as_ref().unwrap().is_unnamed()); + // This should contain process credentials for the client socket. + assert!(conn_info.peer_cred.as_ref().is_some()); + + Ok(Response::new(Output {})) + } + } + + #[tokio::test] + async fn getting_connect_info() { + let unix_socket_path = "/tmp/tonic/uds-integration-test"; + tokio::fs::create_dir_all(Path::new(unix_socket_path).parent().unwrap()) + .await + .unwrap(); + + let uds = UnixListener::bind(unix_socket_path).unwrap(); + let uds_stream = UnixListenerStream::new(uds); + + let service = test_server::TestServer::new(Svc {}); + let (tx, rx) = oneshot::channel::<()>(); + + let jh = tokio::spawn(async move { + Server::builder() + .add_service(service) + .serve_with_incoming_shutdown(uds_stream, rx.map(drop)) + .await + .unwrap(); + }); + + let channel = Endpoint::try_from("http://[::]:50051") + .unwrap() + .connect_with_connector(service_fn(move |_: Uri| { + UnixStream::connect(unix_socket_path) + })) + .await + .unwrap(); + + let mut client = test_client::TestClient::new(channel); + + client.unary_call(Input {}).await.unwrap(); + + tx.send(()).unwrap(); + jh.await.unwrap(); + + // tokio's `UnixListener` does not cleanup the socket automatically - we need to manually + // remove the file at the end of the test. + tokio::fs::remove_file(unix_socket_path).await.unwrap(); + } +}