checkpoint

This commit is contained in:
David Pedersen
2021-12-31 15:25:35 +01:00
parent 044d35d193
commit e3d5b13eea
7 changed files with 130 additions and 63 deletions
+59 -33
View File
@@ -8,10 +8,11 @@ use super::{Extension, FromRequest, RequestParts};
use crate::{AddExtension, AddExtensionLayer}; use crate::{AddExtension, AddExtensionLayer};
use async_trait::async_trait; use async_trait::async_trait;
use hyper::server::conn::AddrStream; use hyper::server::conn::AddrStream;
use pin_project_lite::pin_project;
use std::{ use std::{
convert::Infallible, convert::Infallible,
fmt, fmt,
future::ready, future::{ready, Future},
marker::PhantomData, marker::PhantomData,
net::SocketAddr, net::SocketAddr,
task::{Context, Poll}, task::{Context, Poll},
@@ -25,44 +26,44 @@ use tower_service::Service;
/// ///
/// [`MakeService`]: tower::make::MakeService /// [`MakeService`]: tower::make::MakeService
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info /// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
pub struct IntoMakeServiceWithConnectInfo<S, C> { pub struct WithConnectInfo<M, C> {
svc: S, make_svc: M,
_connect_info: PhantomData<fn() -> C>, _connect_info: PhantomData<fn() -> C>,
} }
#[test] #[test]
fn traits() { fn traits() {
use crate::test_helpers::*; use crate::test_helpers::*;
assert_send::<IntoMakeServiceWithConnectInfo<(), NotSendSync>>(); assert_send::<WithConnectInfo<(), NotSendSync>>();
} }
impl<S, C> IntoMakeServiceWithConnectInfo<S, C> { impl<M, C> WithConnectInfo<M, C> {
pub(crate) fn new(svc: S) -> Self { pub(crate) fn new(make_svc: M) -> Self {
Self { Self {
svc, make_svc,
_connect_info: PhantomData, _connect_info: PhantomData,
} }
} }
} }
impl<S, C> fmt::Debug for IntoMakeServiceWithConnectInfo<S, C> impl<M, C> fmt::Debug for WithConnectInfo<M, C>
where where
S: fmt::Debug, M: fmt::Debug,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IntoMakeServiceWithConnectInfo") f.debug_struct("IntoMakeServiceWithConnectInfo")
.field("svc", &self.svc) .field("svc", &self.make_svc)
.finish() .finish()
} }
} }
impl<S, C> Clone for IntoMakeServiceWithConnectInfo<S, C> impl<M, C> Clone for WithConnectInfo<M, C>
where where
S: Clone, M: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
svc: self.svc.clone(), make_svc: self.make_svc.clone(),
_connect_info: PhantomData, _connect_info: PhantomData,
} }
} }
@@ -79,40 +80,64 @@ where
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info /// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
pub trait Connected<T>: Clone + Send + Sync + 'static { pub trait Connected<T>: Clone + Send + Sync + 'static {
/// Create type holding information about the connection. /// Create type holding information about the connection.
fn connect_info(target: T) -> Self; fn connect_info(target: &T) -> Self;
} }
impl Connected<&AddrStream> for SocketAddr { impl Connected<&AddrStream> for SocketAddr {
fn connect_info(target: &AddrStream) -> Self { fn connect_info(target: &&AddrStream) -> Self {
target.remote_addr() target.remote_addr()
} }
} }
impl<S, C, T> Service<T> for IntoMakeServiceWithConnectInfo<S, C> impl<M, C, T> Service<T> for WithConnectInfo<M, C>
where where
S: Clone, M: Service<T>,
C: Connected<T>, C: Connected<T>,
{ {
type Response = AddExtension<S, ConnectInfo<C>>; type Response = AddExtension<M::Response, ConnectInfo<C>>;
type Error = Infallible; type Error = M::Error;
type Future = ResponseFuture<S, C>; type Future = ResponseFuture<M::Future, C>;
#[inline] #[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(())) self.make_svc.poll_ready(cx)
} }
fn call(&mut self, target: T) -> Self::Future { fn call(&mut self, target: T) -> Self::Future {
let connect_info = ConnectInfo(C::connect_info(target)); let connect_info = C::connect_info(&target);
let svc = AddExtensionLayer::new(connect_info).layer(self.svc.clone()); ResponseFuture {
ResponseFuture::new(ready(Ok(svc))) future: self.make_svc.call(target),
connect_info: Some(connect_info),
}
} }
} }
opaque_future! { pin_project! {
/// Response future for [`IntoMakeServiceWithConnectInfo`]. /// Response future for [`IntoMakeServiceWithConnectInfo`].
pub type ResponseFuture<S, C> = pub struct ResponseFuture<F, C> {
std::future::Ready<Result<AddExtension<S, ConnectInfo<C>>, Infallible>>; #[pin]
future: F,
connect_info: Option<C>,
}
}
impl<F, C, S, E> Future for ResponseFuture<F, C>
where
F: Future<Output = Result<S, E>>,
C: Clone,
{
type Output = Result<AddExtension<S, ConnectInfo<C>>, E>;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let svc = futures_util::ready!(this.future.poll(cx))?;
let connect_info = this
.connect_info
.take()
.expect("future polled after completion");
let svc = AddExtensionLayer::new(ConnectInfo(connect_info)).layer(svc);
Poll::Ready(Ok(svc))
}
} }
/// Extractor for getting connection information produced by a [`Connected`]. /// Extractor for getting connection information produced by a [`Connected`].
@@ -161,7 +186,7 @@ mod tests {
let app = Router::new().route("/", get(handler)); let app = Router::new().route("/", get(handler));
let server = Server::from_tcp(listener) let server = Server::from_tcp(listener)
.unwrap() .unwrap()
.serve(app.into_make_service_with_connect_info::<SocketAddr, _>()); .serve(app.into_make_service().with_connect_info::<SocketAddr, _>());
tx.send(()).unwrap(); tx.send(()).unwrap();
server.await.expect("server error"); server.await.expect("server error");
}); });
@@ -182,7 +207,7 @@ mod tests {
} }
impl Connected<&AddrStream> for MyConnectInfo { impl Connected<&AddrStream> for MyConnectInfo {
fn connect_info(_target: &AddrStream) -> Self { fn connect_info(_target: &&AddrStream) -> Self {
Self { Self {
value: "it worked!", value: "it worked!",
} }
@@ -199,9 +224,10 @@ mod tests {
let (tx, rx) = tokio::sync::oneshot::channel(); let (tx, rx) = tokio::sync::oneshot::channel();
tokio::spawn(async move { tokio::spawn(async move {
let app = Router::new().route("/", get(handler)); let app = Router::new().route("/", get(handler));
let server = Server::from_tcp(listener) let server = Server::from_tcp(listener).unwrap().serve(
.unwrap() app.into_make_service()
.serve(app.into_make_service_with_connect_info::<MyConnectInfo, _>()); .with_connect_info::<MyConnectInfo, _>(),
);
tx.send(()).unwrap(); tx.send(()).unwrap();
server.await.expect("server error"); server.await.expect("server error");
}); });
+4 -4
View File
@@ -73,7 +73,7 @@
use crate::{ use crate::{
body::{boxed, Body, Bytes, HttpBody}, body::{boxed, Body, Bytes, HttpBody},
extract::{ extract::{
connect_info::{Connected, IntoMakeServiceWithConnectInfo}, connect_info::{Connected, WithConnectInfo},
FromRequest, RequestParts, FromRequest, RequestParts,
}, },
response::{IntoResponse, Response}, response::{IntoResponse, Response},
@@ -218,7 +218,7 @@ pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
/// ``` /// ```
/// ///
/// [`MakeService`]: tower::make::MakeService /// [`MakeService`]: tower::make::MakeService
fn into_make_service(self) -> IntoMakeService<IntoService<Self, T, B>> { fn into_make_service(self) -> IntoMakeService<crate::routing::Shared<IntoService<Self, T, B>>> {
IntoMakeService::new(self.into_service()) IntoMakeService::new(self.into_service())
} }
@@ -252,11 +252,11 @@ pub trait Handler<T, B = Body>: Clone + Send + Sized + 'static {
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info /// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
fn into_make_service_with_connect_info<C, Target>( fn into_make_service_with_connect_info<C, Target>(
self, self,
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, B>, C> ) -> WithConnectInfo<IntoService<Self, T, B>, C>
where where
C: Connected<Target>, C: Connected<Target>,
{ {
IntoMakeServiceWithConnectInfo::new(self.into_service()) WithConnectInfo::new(self.into_service())
} }
} }
+1 -1
View File
@@ -4,7 +4,7 @@ use crate::response::Response;
use futures_util::future::Either; use futures_util::future::Either;
use std::{convert::Infallible, future::ready}; use std::{convert::Infallible, future::ready};
pub use super::{into_make_service::IntoMakeServiceFuture, route::RouteFuture}; pub use super::{into_make_service::SharedFuture, route::RouteFuture};
opaque_future! { opaque_future! {
/// Response future for [`Router`](super::Router). /// Response future for [`Router`](super::Router).
+52 -9
View File
@@ -1,31 +1,74 @@
use crate::extract::connect_info::{Connected, WithConnectInfo};
use std::{ use std::{
convert::Infallible, convert::Infallible,
future::ready, future::ready,
task::{Context, Poll}, task::{Context, Poll},
}; };
use tower_layer::Layer;
use tower_service::Service; use tower_service::Service;
/// A [`MakeService`] that produces axum router services. /// A [`MakeService`] that produces axum router services.
/// ///
/// [`MakeService`]: tower::make::MakeService /// [`MakeService`]: tower::make::MakeService
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct IntoMakeService<S> { pub struct IntoMakeService<M> {
svc: S, make_svc: M,
} }
impl<S> IntoMakeService<S> { impl<S> IntoMakeService<Shared<S>> {
pub(crate) fn new(svc: S) -> Self { pub(crate) fn new(svc: S) -> Self {
Self { svc } Self {
make_svc: Shared(svc),
}
} }
} }
impl<S, T> Service<T> for IntoMakeService<S> impl<M> IntoMakeService<M> {
pub fn with_connect_info<C, Target>(self) -> IntoMakeService<WithConnectInfo<M, C>>
where
C: Connected<Target>,
{
self.layer(tower_layer::layer_fn(WithConnectInfo::new))
}
pub fn layer<L>(self, layer: L) -> IntoMakeService<L::Service>
where
L: Layer<M>,
{
IntoMakeService {
make_svc: layer.layer(self.make_svc),
}
}
}
impl<M, T> Service<T> for IntoMakeService<M>
where
M: Service<T>,
{
type Response = M::Response;
type Error = M::Error;
type Future = M::Future;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.make_svc.poll_ready(cx)
}
fn call(&mut self, target: T) -> Self::Future {
self.make_svc.call(target)
}
}
#[derive(Debug, Clone)]
pub struct Shared<S>(S);
impl<S, T> Service<T> for Shared<S>
where where
S: Clone, S: Clone,
{ {
type Response = S; type Response = S;
type Error = Infallible; type Error = Infallible;
type Future = IntoMakeServiceFuture<S>; type Future = SharedFuture<S>;
#[inline] #[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@@ -33,13 +76,13 @@ where
} }
fn call(&mut self, _target: T) -> Self::Future { fn call(&mut self, _target: T) -> Self::Future {
IntoMakeServiceFuture::new(ready(Ok(self.svc.clone()))) SharedFuture::new(ready(Ok(self.0.clone())))
} }
} }
opaque_future! { opaque_future! {
/// Response future for [`IntoMakeService`]. /// Response future for [`Shared`].
pub type IntoMakeServiceFuture<S> = pub type SharedFuture<S> =
std::future::Ready<Result<S, Infallible>>; std::future::Ready<Result<S, Infallible>>;
} }
+7 -13
View File
@@ -4,7 +4,7 @@ use self::{future::RouterFuture, not_found::NotFound};
use crate::{ use crate::{
body::{boxed, Body, Bytes, HttpBody}, body::{boxed, Body, Bytes, HttpBody},
extract::{ extract::{
connect_info::{Connected, IntoMakeServiceWithConnectInfo}, connect_info::{Connected, WithConnectInfo},
MatchedPath, OriginalUri, MatchedPath, OriginalUri,
}, },
response::Response, response::Response,
@@ -38,7 +38,11 @@ mod strip_prefix;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use self::{into_make_service::IntoMakeService, method_filter::MethodFilter, route::Route}; pub use self::{
into_make_service::{IntoMakeService, Shared},
method_filter::MethodFilter,
route::Route,
};
pub use self::method_routing::{ pub use self::method_routing::{
any, any_service, delete, delete_service, get, get_service, head, head_service, on, on_service, any, any_service, delete, delete_service, get, get_service, head, head_service, on, on_service,
@@ -381,20 +385,10 @@ where
/// ``` /// ```
/// ///
/// [`MakeService`]: tower::make::MakeService /// [`MakeService`]: tower::make::MakeService
pub fn into_make_service(self) -> IntoMakeService<Self> { pub fn into_make_service(self) -> IntoMakeService<Shared<Self>> {
IntoMakeService::new(self) IntoMakeService::new(self)
} }
#[doc = include_str!("../docs/routing/into_make_service_with_connect_info.md")]
pub fn into_make_service_with_connect_info<C, Target>(
self,
) -> IntoMakeServiceWithConnectInfo<Self, C>
where
C: Connected<Target>,
{
IntoMakeServiceWithConnectInfo::new(self)
}
#[inline] #[inline]
fn call_route(&self, match_: matchit::Match<&RouteId>, mut req: Request<B>) -> RouterFuture<B> { fn call_route(&self, match_: matchit::Match<&RouteId>, mut req: Request<B>) -> RouterFuture<B> {
let id = *match_.value; let id = *match_.value;
+2 -1
View File
@@ -39,7 +39,8 @@ async fn main() {
let mut app = Router::new() let mut app = Router::new()
.route("/", get(handler)) .route("/", get(handler))
.into_make_service_with_connect_info::<SocketAddr, _>(); .into_make_service()
.with_connect_info::<SocketAddr, _>();
loop { loop {
let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx)) let stream = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx))
+5 -2
View File
@@ -55,7 +55,10 @@ async fn main() {
let app = Router::new().route("/", get(handler)); let app = Router::new().route("/", get(handler));
axum::Server::builder(ServerAccept { uds }) axum::Server::builder(ServerAccept { uds })
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo, _>()) .serve(
app.into_make_service()
.with_connect_info::<UdsConnectInfo, _>(),
)
.await .await
.unwrap(); .unwrap();
}); });
@@ -156,7 +159,7 @@ struct UdsConnectInfo {
} }
impl connect_info::Connected<&UnixStream> for UdsConnectInfo { impl connect_info::Connected<&UnixStream> for UdsConnectInfo {
fn connect_info(target: &UnixStream) -> Self { fn connect_info(target: &&UnixStream) -> Self {
let peer_addr = target.peer_addr().unwrap(); let peer_addr = target.peer_addr().unwrap();
let peer_cred = target.peer_cred().unwrap(); let peer_cred = target.peer_cred().unwrap();