feat(transport): provide generic access to connect info (#647)

This commit is contained in:
David Pedersen
2021-05-28 13:27:37 +02:00
committed by GitHub
parent 31a34681c7
commit e5e311853b
8 changed files with 383 additions and 92 deletions
+14 -1
View File
@@ -6,7 +6,10 @@ use futures::Stream;
use pb::{EchoRequest, EchoResponse}; use pb::{EchoRequest, EchoResponse};
use std::pin::Pin; use std::pin::Pin;
use tonic::{ use tonic::{
transport::{Identity, Server, ServerTlsConfig}, transport::{
server::{TcpConnectInfo, TlsConnectInfo},
Identity, Server, ServerTlsConfig,
},
Request, Response, Status, Streaming, Request, Response, Status, Streaming,
}; };
@@ -19,6 +22,16 @@ pub struct EchoServer;
#[tonic::async_trait] #[tonic::async_trait]
impl pb::echo_server::Echo for EchoServer { impl pb::echo_server::Echo for EchoServer {
async fn unary_echo(&self, request: Request<EchoRequest>) -> EchoResult<EchoResponse> { async fn unary_echo(&self, request: Request<EchoRequest>) -> EchoResult<EchoResponse> {
let conn_info = request
.extensions()
.get::<TlsConnectInfo<TcpConnectInfo>>()
.unwrap();
println!(
"Got a request from {:?} with info {:?}",
request.remote_addr(),
conn_info
);
let message = request.into_inner().message; let message = request.into_inner().message;
Ok(Response::new(EchoResponse { message })) Ok(Response::new(EchoResponse { message }))
} }
+22 -2
View File
@@ -24,7 +24,11 @@ impl Greeter for MyGreeter {
&self, &self,
request: Request<HelloRequest>, request: Request<HelloRequest>,
) -> Result<Response<HelloReply>, Status> { ) -> Result<Response<HelloReply>, Status> {
println!("Got a request: {:?}", request); #[cfg(unix)]
{
let conn_info = request.extensions().get::<unix::UdsConnectInfo>().unwrap();
println!("Got a request {:?} with info {:?}", request, conn_info);
}
let reply = hello_world::HelloReply { let reply = hello_world::HelloReply {
message: format!("Hello {}!", request.into_inner().name), message: format!("Hello {}!", request.into_inner().name),
@@ -64,6 +68,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
mod unix { mod unix {
use std::{ use std::{
pin::Pin, pin::Pin,
sync::Arc,
task::{Context, Poll}, task::{Context, Poll},
}; };
@@ -73,7 +78,22 @@ mod unix {
#[derive(Debug)] #[derive(Debug)]
pub struct UnixStream(pub tokio::net::UnixStream); pub struct UnixStream(pub tokio::net::UnixStream);
impl Connected for UnixStream {} impl Connected for UnixStream {
type ConnectInfo = UdsConnectInfo;
fn connect_info(&self) -> Self::ConnectInfo {
UdsConnectInfo {
peer_addr: self.0.peer_addr().ok().map(Arc::new),
peer_cred: self.0.peer_cred().ok(),
}
}
}
#[derive(Clone, Debug)]
pub struct UdsConnectInfo {
pub peer_addr: Option<Arc<tokio::net::unix::SocketAddr>>,
pub peer_cred: Option<tokio::net::unix::UCred>,
}
impl AsyncRead for UnixStream { impl AsyncRead for UnixStream {
fn poll_read( fn poll_read(
@@ -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();
}
+39 -13
View File
@@ -1,6 +1,8 @@
use crate::metadata::{MetadataMap, MetadataValue}; use crate::metadata::{MetadataMap, MetadataValue};
#[cfg(all(feature = "transport", feature = "tls"))]
use crate::transport::server::TlsConnectInfo;
#[cfg(feature = "transport")] #[cfg(feature = "transport")]
use crate::transport::Certificate; use crate::transport::{server::TcpConnectInfo, Certificate};
use crate::Extensions; use crate::Extensions;
use futures_core::Stream; use futures_core::Stream;
#[cfg(feature = "transport")] #[cfg(feature = "transport")]
@@ -15,13 +17,6 @@ pub struct Request<T> {
extensions: Extensions, extensions: Extensions,
} }
#[derive(Clone)]
pub(crate) struct ConnectionInfo {
pub(crate) remote_addr: Option<SocketAddr>,
#[cfg(feature = "transport")]
pub(crate) peer_certs: Option<Arc<Vec<Certificate>>>,
}
/// Trait implemented by RPC request types. /// Trait implemented by RPC request types.
/// ///
/// Types implementing this trait can be used as arguments to client RPC /// Types implementing this trait can be used as arguments to client RPC
@@ -203,7 +198,32 @@ impl<T> Request<T> {
/// does not implement `Connected`. This currently, /// does not implement `Connected`. This currently,
/// only works on the server side. /// only works on the server side.
pub fn remote_addr(&self) -> Option<SocketAddr> { pub fn remote_addr(&self) -> Option<SocketAddr> {
self.get::<ConnectionInfo>()?.remote_addr #[cfg(feature = "transport")]
{
#[cfg(feature = "tls")]
{
self.extensions()
.get::<TcpConnectInfo>()
.and_then(|i| i.remote_addr())
.or_else(|| {
self.extensions()
.get::<TlsConnectInfo<TcpConnectInfo>>()
.and_then(|i| i.get_ref().remote_addr())
})
}
#[cfg(not(feature = "tls"))]
{
self.extensions()
.get::<TcpConnectInfo>()
.and_then(|i| i.remote_addr())
}
}
#[cfg(not(feature = "transport"))]
{
None
}
} }
/// Get the peer certificates of the connected client. /// Get the peer certificates of the connected client.
@@ -215,11 +235,17 @@ impl<T> Request<T> {
#[cfg(feature = "transport")] #[cfg(feature = "transport")]
#[cfg_attr(docsrs, doc(cfg(feature = "transport")))] #[cfg_attr(docsrs, doc(cfg(feature = "transport")))]
pub fn peer_certs(&self) -> Option<Arc<Vec<Certificate>>> { pub fn peer_certs(&self) -> Option<Arc<Vec<Certificate>>> {
self.get::<ConnectionInfo>()?.peer_certs.clone() #[cfg(feature = "tls")]
} {
self.extensions()
.get::<TlsConnectInfo<TcpConnectInfo>>()
.and_then(|i| i.peer_certs())
}
pub(crate) fn get<I: Send + Sync + 'static>(&self) -> Option<&I> { #[cfg(not(feature = "tls"))]
self.extensions.get::<I>() {
None
}
} }
/// Set the max duration the request is allowed to take. /// Set the max duration the request is allowed to take.
+124 -24
View File
@@ -1,58 +1,158 @@
use crate::transport::Certificate;
use hyper::server::conn::AddrStream; use hyper::server::conn::AddrStream;
use std::net::SocketAddr; use std::net::SocketAddr;
use tokio::net::TcpStream; use tokio::net::TcpStream;
#[cfg(feature = "tls")]
use crate::transport::Certificate;
#[cfg(feature = "tls")]
use std::sync::Arc;
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
use tokio_rustls::{rustls::Session, server::TlsStream}; 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 /// The goal for this trait is to allow users to implement
/// custom IO types that can still provide the same connection /// custom IO types that can still provide the same connection
/// metadata. /// 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 { pub trait Connected {
/// Return the remote address this IO resource is connected too. /// The connection info type the IO resources generates.
fn remote_addr(&self) -> Option<SocketAddr> { // all these bounds are necessary to set this as a request extension
None type ConnectInfo: Clone + Send + Sync + 'static;
}
/// Return the set of connected peer TLS certificates. /// Create type holding information about the connection.
fn peer_certs(&self) -> Option<Vec<Certificate>> { fn connect_info(&self) -> Self::ConnectInfo;
None }
/// 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 { impl Connected for AddrStream {
fn remote_addr(&self) -> Option<SocketAddr> { type ConnectInfo = TcpConnectInfo;
Some(self.remote_addr())
fn connect_info(&self) -> Self::ConnectInfo {
TcpConnectInfo {
remote_addr: Some(self.remote_addr()),
}
} }
} }
impl Connected for TcpStream { impl Connected for TcpStream {
fn remote_addr(&self) -> Option<SocketAddr> { type ConnectInfo = TcpConnectInfo;
self.peer_addr().ok()
fn connect_info(&self) -> Self::ConnectInfo {
TcpConnectInfo {
remote_addr: self.peer_addr().ok(),
}
} }
} }
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
impl<T: Connected> Connected for TlsStream<T> { impl<T> Connected for TlsStream<T>
fn remote_addr(&self) -> Option<SocketAddr> { where
let (inner, _) = self.get_ref(); T: Connected,
{
type ConnectInfo = TlsConnectInfo<T::ConnectInfo>;
inner.remote_addr() fn connect_info(&self) -> Self::ConnectInfo {
} let (inner, session) = self.get_ref();
let inner = inner.connect_info();
fn peer_certs(&self) -> Option<Vec<Certificate>> { let certs = if let Some(certs) = session.get_peer_certificates() {
let (_, session) = self.get_ref();
if let Some(certs) = session.get_peer_certificates() {
let certs = certs let certs = certs
.into_iter() .into_iter()
.map(|c| Certificate::from_pem(c.0)) .map(|c| Certificate::from_pem(c.0))
.collect(); .collect();
Some(certs) Some(Arc::new(certs))
} else { } else {
None 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()
} }
} }
+7 -9
View File
@@ -18,7 +18,7 @@ use tokio::io::{AsyncRead, AsyncWrite};
pub(crate) fn tcp_incoming<IO, IE, L>( pub(crate) fn tcp_incoming<IO, IE, L>(
incoming: impl Stream<Item = Result<IO, IE>>, incoming: impl Stream<Item = Result<IO, IE>>,
_server: Server<L>, _server: Server<L>,
) -> impl Stream<Item = Result<ServerIo, crate::Error>> ) -> impl Stream<Item = Result<ServerIo<IO>, crate::Error>>
where where
IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static,
IE: Into<crate::Error>, IE: Into<crate::Error>,
@@ -26,10 +26,8 @@ where
async_stream::try_stream! { async_stream::try_stream! {
futures_util::pin_mut!(incoming); futures_util::pin_mut!(incoming);
while let Some(stream) = incoming.try_next().await? { while let Some(stream) = incoming.try_next().await? {
yield ServerIo::new_io(stream);
yield ServerIo::new(stream);
} }
} }
} }
@@ -38,7 +36,7 @@ where
pub(crate) fn tcp_incoming<IO, IE, L>( pub(crate) fn tcp_incoming<IO, IE, L>(
incoming: impl Stream<Item = Result<IO, IE>>, incoming: impl Stream<Item = Result<IO, IE>>,
server: Server<L>, server: Server<L>,
) -> impl Stream<Item = Result<ServerIo, crate::Error>> ) -> impl Stream<Item = Result<ServerIo<IO>, crate::Error>>
where where
IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static,
IE: Into<crate::Error>, IE: Into<crate::Error>,
@@ -57,12 +55,12 @@ where
let accept = tokio::spawn(async move { let accept = tokio::spawn(async move {
let io = tls.accept(stream).await?; let io = tls.accept(stream).await?;
Ok(ServerIo::new(io)) Ok(ServerIo::new_tls_io(io))
}); });
tasks.push(accept); tasks.push(accept);
} else { } else {
yield ServerIo::new(stream); yield ServerIo::new_io(stream);
} }
} }
@@ -86,7 +84,7 @@ where
async fn select<IO, IE>( async fn select<IO, IE>(
incoming: &mut (impl Stream<Item = Result<IO, IE>> + Unpin), incoming: &mut (impl Stream<Item = Result<IO, IE>> + Unpin),
tasks: &mut futures_util::stream::futures_unordered::FuturesUnordered< tasks: &mut futures_util::stream::futures_unordered::FuturesUnordered<
tokio::task::JoinHandle<Result<ServerIo, crate::Error>>, tokio::task::JoinHandle<Result<ServerIo<IO>, crate::Error>>,
>, >,
) -> SelectOutput<IO> ) -> SelectOutput<IO>
where where
@@ -124,7 +122,7 @@ where
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
enum SelectOutput<A> { enum SelectOutput<A> {
Incoming(A), Incoming(A),
Io(ServerIo), Io(ServerIo<A>),
Err(crate::Error), Err(crate::Error),
Done, Done,
} }
+44 -18
View File
@@ -7,10 +7,13 @@ mod recover_error;
#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))]
mod tls; mod tls;
pub use conn::Connected; pub use conn::{Connected, TcpConnectInfo};
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
pub use tls::ServerTlsConfig; pub use tls::ServerTlsConfig;
#[cfg(feature = "tls")]
pub use conn::TlsConnectInfo;
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
use super::service::TlsAcceptor; use super::service::TlsAcceptor;
@@ -24,7 +27,7 @@ use crate::transport::Error;
use self::recover_error::RecoverError; use self::recover_error::RecoverError;
use super::service::{GrpcTimeout, Or, Routes, ServerIo}; use super::service::{GrpcTimeout, Or, Routes, ServerIo};
use crate::{body::BoxBody, request::ConnectionInfo}; use crate::body::BoxBody;
use bytes::Bytes; use bytes::Bytes;
use futures_core::Stream; use futures_core::Stream;
use futures_util::{ use futures_util::{
@@ -38,6 +41,7 @@ use pin_project::pin_project;
use std::{ use std::{
fmt, fmt,
future::Future, future::Future,
marker::PhantomData,
net::SocketAddr, net::SocketAddr,
pin::Pin, pin::Pin,
sync::Arc, sync::Arc,
@@ -458,6 +462,7 @@ impl<L> Server<L> {
<<L as Layer<S>>::Service as Service<Request<Body>>>::Error: Into<crate::Error> + Send, <<L as Layer<S>>::Service as Service<Request<Body>>>::Error: Into<crate::Error> + Send,
I: Stream<Item = Result<IO, IE>>, I: Stream<Item = Result<IO, IE>>,
IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static,
IO::ConnectInfo: Clone + Send + Sync + 'static,
IE: Into<crate::Error>, IE: Into<crate::Error>,
F: Future<Output = ()>, F: Future<Output = ()>,
ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static, ResBody: http_body::Body<Data = Bytes> + Send + Sync + 'static,
@@ -487,6 +492,7 @@ impl<L> Server<L> {
concurrency_limit, concurrency_limit,
timeout, timeout,
trace_interceptor, trace_interceptor,
_io: PhantomData,
}; };
let server = hyper::Server::builder(incoming) let server = hyper::Server::builder(incoming)
@@ -674,6 +680,7 @@ where
where where
I: Stream<Item = Result<IO, IE>>, I: Stream<Item = Result<IO, IE>>,
IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static,
IO::ConnectInfo: Clone + Send + Sync + 'static,
IE: Into<crate::Error>, IE: Into<crate::Error>,
L: Layer<Routes<A, B, Request<Body>>>, L: Layer<Routes<A, B, Request<Body>>>,
L::Service: Service<Request<Body>, Response = Response<ResBody>> + Clone + Send + 'static, L::Service: Service<Request<Body>, Response = Response<ResBody>> + Clone + Send + 'static,
@@ -707,6 +714,7 @@ where
where where
I: Stream<Item = Result<IO, IE>>, I: Stream<Item = Result<IO, IE>>,
IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static,
IO::ConnectInfo: Clone + Send + Sync + 'static,
IE: Into<crate::Error>, IE: Into<crate::Error>,
F: Future<Output = ()>, F: Future<Output = ()>,
L: Layer<Routes<A, B, Request<Body>>>, L: Layer<Routes<A, B, Request<Body>>>,
@@ -749,7 +757,6 @@ impl<L> fmt::Debug for Server<L> {
struct Svc<S> { struct Svc<S> {
inner: S, inner: S,
trace_interceptor: Option<TraceInterceptor>, trace_interceptor: Option<TraceInterceptor>,
conn_info: ConnectionInfo,
} }
impl<S, ResBody> Service<Request<Body>> for Svc<S> impl<S, ResBody> Service<Request<Body>> for Svc<S>
@@ -782,8 +789,6 @@ where
tracing::Span::none() tracing::Span::none()
}; };
req.extensions_mut().insert(self.conn_info.clone());
SvcFuture { SvcFuture {
inner: self.inner.call(req), inner: self.inner.call(req),
span, span,
@@ -823,15 +828,17 @@ impl<S> fmt::Debug for Svc<S> {
} }
} }
struct MakeSvc<S> { struct MakeSvc<S, IO> {
concurrency_limit: Option<usize>, concurrency_limit: Option<usize>,
timeout: Option<Duration>, timeout: Option<Duration>,
inner: S, inner: S,
trace_interceptor: Option<TraceInterceptor>, trace_interceptor: Option<TraceInterceptor>,
_io: PhantomData<fn() -> IO>,
} }
impl<S, ResBody> Service<&ServerIo> for MakeSvc<S> impl<S, ResBody, IO> Service<&ServerIo<IO>> for MakeSvc<S, IO>
where where
IO: Connected,
S: Service<Request<Body>, Response = Response<ResBody>> + Clone + Send + 'static, S: Service<Request<Body>, Response = Response<ResBody>> + Clone + Send + 'static,
S::Future: Send + 'static, S::Future: Send + 'static,
S::Error: Into<crate::Error> + Send, S::Error: Into<crate::Error> + Send,
@@ -846,11 +853,8 @@ where
Ok(()).into() Ok(()).into()
} }
fn call(&mut self, io: &ServerIo) -> Self::Future { fn call(&mut self, io: &ServerIo<IO>) -> Self::Future {
let conn_info = crate::request::ConnectionInfo { let conn_info = io.connect_info();
remote_addr: io.remote_addr(),
peer_certs: io.peer_certs().map(Arc::new),
};
let svc = self.inner.clone(); let svc = self.inner.clone();
let concurrency_limit = self.concurrency_limit; let concurrency_limit = self.concurrency_limit;
@@ -863,13 +867,35 @@ where
.layer_fn(|s| GrpcTimeout::new(s, timeout)) .layer_fn(|s| GrpcTimeout::new(s, timeout))
.service(svc); .service(svc);
let svc = Svc { let svc = ServiceBuilder::new()
inner: svc, .layer(BoxService::layer())
trace_interceptor, .map_request(move |mut request: Request<Body>| {
conn_info, match &conn_info {
}; tower::util::Either::A(inner) => {
request.extensions_mut().insert(inner.clone());
}
tower::util::Either::B(inner) => {
#[cfg(feature = "tls")]
{
request.extensions_mut().insert(inner.clone());
request.extensions_mut().insert(inner.get_ref().clone());
}
let svc = BoxService::new(svc); #[cfg(not(feature = "tls"))]
{
// just a type check to make sure we didn't forget to
// insert this into the extensions
let _: &() = inner;
}
}
}
request
})
.service(Svc {
inner: svc,
trace_interceptor,
});
future::ready(Ok(svc)) future::ready(Ok(svc))
} }
+83 -25
View File
@@ -1,10 +1,11 @@
use crate::transport::{server::Connected, Certificate}; use crate::transport::server::Connected;
use hyper::client::connect::{Connected as HyperConnected, Connection}; use hyper::client::connect::{Connected as HyperConnected, Connection};
use std::io; use std::io;
use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
#[cfg(feature = "tls")]
use tokio_rustls::server::TlsStream;
pub(in crate::transport) trait Io: pub(in crate::transport) trait Io:
AsyncRead + AsyncWrite + Send + 'static AsyncRead + AsyncWrite + Send + 'static
@@ -27,7 +28,16 @@ impl Connection for BoxedIo {
} }
} }
impl Connected for BoxedIo {} impl Connected for BoxedIo {
type ConnectInfo = NoneConnectInfo;
fn connect_info(&self) -> Self::ConnectInfo {
NoneConnectInfo
}
}
#[derive(Copy, Clone)]
pub(crate) struct NoneConnectInfo;
impl AsyncRead for BoxedIo { impl AsyncRead for BoxedIo {
fn poll_read( fn poll_read(
@@ -57,52 +67,100 @@ impl AsyncWrite for BoxedIo {
} }
} }
pub(in crate::transport) trait ConnectedIo: Io + Connected {} pub(crate) enum ServerIo<IO> {
Io(IO),
#[cfg(feature = "tls")]
TlsIo(TlsStream<IO>),
}
impl<T> ConnectedIo for T where T: Io + Connected {} use tower::util::Either;
pub(crate) struct ServerIo(Pin<Box<dyn ConnectedIo>>); #[cfg(feature = "tls")]
type ServerIoConnectInfo<IO> =
Either<<IO as Connected>::ConnectInfo, <TlsStream<IO> as Connected>::ConnectInfo>;
impl ServerIo { #[cfg(not(feature = "tls"))]
pub(in crate::transport) fn new<I: ConnectedIo>(io: I) -> Self { type ServerIoConnectInfo<IO> = Either<<IO as Connected>::ConnectInfo, ()>;
ServerIo(Box::pin(io))
impl<IO> ServerIo<IO> {
pub(in crate::transport) fn new_io(io: IO) -> Self {
Self::Io(io)
}
#[cfg(feature = "tls")]
pub(in crate::transport) fn new_tls_io(io: TlsStream<IO>) -> Self {
Self::TlsIo(io)
}
#[cfg(feature = "tls")]
pub(in crate::transport) fn connect_info(&self) -> ServerIoConnectInfo<IO>
where
IO: Connected,
TlsStream<IO>: Connected,
{
match self {
Self::Io(io) => Either::A(io.connect_info()),
Self::TlsIo(io) => Either::B(io.connect_info()),
}
}
#[cfg(not(feature = "tls"))]
pub(in crate::transport) fn connect_info(&self) -> ServerIoConnectInfo<IO>
where
IO: Connected,
{
match self {
Self::Io(io) => Either::A(io.connect_info()),
}
} }
} }
impl Connected for ServerIo { impl<IO> AsyncRead for ServerIo<IO>
fn remote_addr(&self) -> Option<SocketAddr> { where
(&*self.0).remote_addr() IO: AsyncWrite + AsyncRead + Unpin,
} {
fn peer_certs(&self) -> Option<Vec<Certificate>> {
(&self.0).peer_certs()
}
}
impl AsyncRead for ServerIo {
fn poll_read( fn poll_read(
mut self: Pin<&mut Self>, mut self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>, buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> { ) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_read(cx, buf) match &mut *self {
Self::Io(io) => Pin::new(io).poll_read(cx, buf),
#[cfg(feature = "tls")]
Self::TlsIo(io) => Pin::new(io).poll_read(cx, buf),
}
} }
} }
impl AsyncWrite for ServerIo { impl<IO> AsyncWrite for ServerIo<IO>
where
IO: AsyncWrite + AsyncRead + Unpin,
{
fn poll_write( fn poll_write(
mut self: Pin<&mut Self>, mut self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
Pin::new(&mut self.0).poll_write(cx, buf) match &mut *self {
Self::Io(io) => Pin::new(io).poll_write(cx, buf),
#[cfg(feature = "tls")]
Self::TlsIo(io) => Pin::new(io).poll_write(cx, buf),
}
} }
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_flush(cx) match &mut *self {
Self::Io(io) => Pin::new(io).poll_flush(cx),
#[cfg(feature = "tls")]
Self::TlsIo(io) => Pin::new(io).poll_flush(cx),
}
} }
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_shutdown(cx) match &mut *self {
Self::Io(io) => Pin::new(io).poll_shutdown(cx),
#[cfg(feature = "tls")]
Self::TlsIo(io) => Pin::new(io).poll_shutdown(cx),
}
} }
} }