-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(transport): provide generic access to connect info
- Loading branch information
1 parent
f613386
commit f1c5257
Showing
8 changed files
with
380 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
use futures_util::FutureExt; | ||
use integration_tests::pb::{test_client, test_server, Input, Output}; | ||
use std::time::Duration; | ||
use tokio::sync::oneshot; | ||
use tonic::{ | ||
transport::{server::TcpConnectInfo, Endpoint, Server}, | ||
Request, Response, Status, | ||
}; | ||
|
||
#[tokio::test] | ||
async fn getting_connect_info() { | ||
struct Svc; | ||
|
||
#[tonic::async_trait] | ||
impl test_server::Test for Svc { | ||
async fn unary_call(&self, req: Request<Input>) -> Result<Response<Output>, Status> { | ||
assert!(req.remote_addr().is_some()); | ||
assert!(req.extensions().get::<TcpConnectInfo>().is_some()); | ||
|
||
Ok(Response::new(Output {})) | ||
} | ||
} | ||
|
||
let svc = test_server::TestServer::new(Svc); | ||
|
||
let (tx, rx) = oneshot::channel::<()>(); | ||
|
||
let jh = tokio::spawn(async move { | ||
Server::builder() | ||
.add_service(svc) | ||
.serve_with_shutdown("127.0.0.1:1400".parse().unwrap(), rx.map(drop)) | ||
.await | ||
.unwrap(); | ||
}); | ||
|
||
tokio::time::sleep(Duration::from_millis(100)).await; | ||
|
||
let channel = Endpoint::from_static("http://127.0.0.1:1400") | ||
.connect() | ||
.await | ||
.unwrap(); | ||
|
||
let mut client = test_client::TestClient::new(channel); | ||
|
||
client.unary_call(Input {}).await.unwrap(); | ||
|
||
tx.send(()).unwrap(); | ||
|
||
jh.await.unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,158 @@ | ||
use crate::transport::Certificate; | ||
use hyper::server::conn::AddrStream; | ||
use std::net::SocketAddr; | ||
use tokio::net::TcpStream; | ||
|
||
#[cfg(feature = "tls")] | ||
use crate::transport::Certificate; | ||
#[cfg(feature = "tls")] | ||
use std::sync::Arc; | ||
#[cfg(feature = "tls")] | ||
use tokio_rustls::{rustls::Session, server::TlsStream}; | ||
|
||
/// Trait that connected IO resources implement. | ||
/// Trait that connected IO resources implement and use to produce info about the connection. | ||
/// | ||
/// The goal for this trait is to allow users to implement | ||
/// custom IO types that can still provide the same connection | ||
/// metadata. | ||
/// | ||
/// # Example | ||
/// | ||
/// The `ConnectInfo` returned will be accessible through [request extensions][ext]: | ||
/// | ||
/// ``` | ||
/// use tonic::{Request, transport::server::Connected}; | ||
/// | ||
/// // A `Stream` that yields connections | ||
/// struct MyConnector {} | ||
/// | ||
/// // Return metadata about the connection as `MyConnectInfo` | ||
/// impl Connected for MyConnector { | ||
/// type ConnectInfo = MyConnectInfo; | ||
/// | ||
/// fn connect_info(&self) -> Self::ConnectInfo { | ||
/// MyConnectInfo {} | ||
/// } | ||
/// } | ||
/// | ||
/// #[derive(Clone)] | ||
/// struct MyConnectInfo { | ||
/// // Metadata about your connection | ||
/// } | ||
/// | ||
/// // The connect info can be accessed through request extensions: | ||
/// # fn foo(request: Request<()>) { | ||
/// let connect_info: &MyConnectInfo = request | ||
/// .extensions() | ||
/// .get::<MyConnectInfo>() | ||
/// .expect("bug in tonic"); | ||
/// # } | ||
/// ``` | ||
/// | ||
/// [ext]: crate::Request::extensions | ||
pub trait Connected { | ||
/// Return the remote address this IO resource is connected too. | ||
fn remote_addr(&self) -> Option<SocketAddr> { | ||
None | ||
} | ||
/// The connection info type the IO resources generates. | ||
// all these bounds are necessary to set this as a request extension | ||
type ConnectInfo: Clone + Send + Sync + 'static; | ||
|
||
/// Return the set of connected peer TLS certificates. | ||
fn peer_certs(&self) -> Option<Vec<Certificate>> { | ||
None | ||
/// Create type holding information about the connection. | ||
fn connect_info(&self) -> Self::ConnectInfo; | ||
} | ||
|
||
/// Connection info for standard TCP streams. | ||
/// | ||
/// This type will be accessible through [request extensions][ext] if you're using the default | ||
/// non-TLS connector. | ||
/// | ||
/// See [`Connected`] for more details. | ||
/// | ||
/// [ext]: crate::Request::extensions | ||
#[derive(Debug, Clone)] | ||
pub struct TcpConnectInfo { | ||
remote_addr: Option<SocketAddr>, | ||
} | ||
|
||
impl TcpConnectInfo { | ||
/// Return the remote address the IO resource is connected too. | ||
pub fn remote_addr(&self) -> Option<SocketAddr> { | ||
self.remote_addr | ||
} | ||
} | ||
|
||
impl Connected for AddrStream { | ||
fn remote_addr(&self) -> Option<SocketAddr> { | ||
Some(self.remote_addr()) | ||
type ConnectInfo = TcpConnectInfo; | ||
|
||
fn connect_info(&self) -> Self::ConnectInfo { | ||
TcpConnectInfo { | ||
remote_addr: Some(self.remote_addr()), | ||
} | ||
} | ||
} | ||
|
||
impl Connected for TcpStream { | ||
fn remote_addr(&self) -> Option<SocketAddr> { | ||
self.peer_addr().ok() | ||
type ConnectInfo = TcpConnectInfo; | ||
|
||
fn connect_info(&self) -> Self::ConnectInfo { | ||
TcpConnectInfo { | ||
remote_addr: self.peer_addr().ok(), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(feature = "tls")] | ||
impl<T: Connected> Connected for TlsStream<T> { | ||
fn remote_addr(&self) -> Option<SocketAddr> { | ||
let (inner, _) = self.get_ref(); | ||
|
||
inner.remote_addr() | ||
} | ||
impl<T> Connected for TlsStream<T> | ||
where | ||
T: Connected, | ||
{ | ||
type ConnectInfo = TlsConnectInfo<T::ConnectInfo>; | ||
|
||
fn peer_certs(&self) -> Option<Vec<Certificate>> { | ||
let (_, session) = self.get_ref(); | ||
fn connect_info(&self) -> Self::ConnectInfo { | ||
let (inner, session) = self.get_ref(); | ||
let inner = inner.connect_info(); | ||
|
||
if let Some(certs) = session.get_peer_certificates() { | ||
let certs = if let Some(certs) = session.get_peer_certificates() { | ||
let certs = certs | ||
.into_iter() | ||
.map(|c| Certificate::from_pem(c.0)) | ||
.collect(); | ||
Some(certs) | ||
Some(Arc::new(certs)) | ||
} else { | ||
None | ||
} | ||
}; | ||
|
||
TlsConnectInfo { inner, certs } | ||
} | ||
} | ||
|
||
/// Connection info for TLS streams. | ||
/// | ||
/// This type will be accessible through [request extensions][ext] if you're using a TLS connector. | ||
/// | ||
/// See [`Connected`] for more details. | ||
/// | ||
/// [ext]: crate::Request::extensions | ||
#[cfg(feature = "tls")] | ||
#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] | ||
#[derive(Debug, Clone)] | ||
pub struct TlsConnectInfo<T> { | ||
inner: T, | ||
certs: Option<Arc<Vec<Certificate>>>, | ||
} | ||
|
||
#[cfg(feature = "tls")] | ||
#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] | ||
impl<T> TlsConnectInfo<T> { | ||
/// Get a reference to the underlying connection info. | ||
pub fn get_ref(&self) -> &T { | ||
&self.inner | ||
} | ||
|
||
/// Get a mutable reference to the underlying connection info. | ||
pub fn get_mut(&mut self) -> &mut T { | ||
&mut self.inner | ||
} | ||
|
||
/// Return the set of connected peer TLS certificates. | ||
pub fn peer_certs(&self) -> Option<Arc<Vec<Certificate>>> { | ||
self.certs.clone() | ||
} | ||
} |
Oops, something went wrong.