1
0
Fork 0
mirror of https://github.com/tokio-rs/axum.git synced 2025-04-26 22:06:25 +02:00

Add Handler::{into_make_service, into_make_service_with_connect_info} ()

* Make `IntoMakeService(WithConnectInfo)?` work with any Service

* Add `Handler::{into_make_service, into_make_service_with_connect_info}`

These are useful if you want to run a handler without a `Router`, for
example to make a proxy.
This commit is contained in:
David Pedersen 2021-11-02 17:11:18 +01:00 committed by GitHub
parent 98907b8887
commit 9512d14c99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 52 deletions

View file

@ -39,6 +39,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
is because bodies are streams and requiring streams to be `Sync` is
unnecessary.
- **added:** Implement `IntoResponse` for `http_body::combinators::UnsyncBoxBody`.
- **added:** Add `Handler::into_make_service` for serving a handler without a
`Router`.
- **added:** Add `Handler::into_make_service_with_connect_info` for serving a
handler without a `Router`, and storing info about the incoming connection.
- Routing:
- Big internal refactoring of routing leading to several improvements ([#363])
- **added:** Wildcard routes like `.route("/api/users/*rest", service)` are now supported.

View file

@ -5,7 +5,7 @@
//! [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
use super::{Extension, FromRequest, RequestParts};
use crate::{AddExtension, AddExtensionLayer, Router};
use crate::{AddExtension, AddExtensionLayer};
use async_trait::async_trait;
use hyper::server::conn::AddrStream;
use std::future::ready;
@ -25,8 +25,8 @@ use tower_service::Service;
///
/// [`MakeService`]: tower::make::MakeService
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
pub struct IntoMakeServiceWithConnectInfo<B, C> {
router: Router<B>,
pub struct IntoMakeServiceWithConnectInfo<S, C> {
svc: S,
_connect_info: PhantomData<fn() -> C>,
}
@ -36,19 +36,22 @@ fn traits() {
assert_send::<IntoMakeServiceWithConnectInfo<(), NotSendSync>>();
}
impl<B, C> IntoMakeServiceWithConnectInfo<B, C> {
pub(crate) fn new(router: Router<B>) -> Self {
impl<S, C> IntoMakeServiceWithConnectInfo<S, C> {
pub(crate) fn new(svc: S) -> Self {
Self {
router,
svc,
_connect_info: PhantomData,
}
}
}
impl<B, C> fmt::Debug for IntoMakeServiceWithConnectInfo<B, C> {
impl<S, C> fmt::Debug for IntoMakeServiceWithConnectInfo<S, C>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IntoMakeServiceWithConnectInfo")
.field("router", &self.router)
.field("svc", &self.svc)
.finish()
}
}
@ -73,13 +76,14 @@ impl Connected<&AddrStream> for SocketAddr {
}
}
impl<B, C, T> Service<T> for IntoMakeServiceWithConnectInfo<B, C>
impl<S, C, T> Service<T> for IntoMakeServiceWithConnectInfo<S, C>
where
S: Clone,
C: Connected<T>,
{
type Response = AddExtension<Router<B>, ConnectInfo<C>>;
type Response = AddExtension<S, ConnectInfo<C>>;
type Error = Infallible;
type Future = ResponseFuture<B, C>;
type Future = ResponseFuture<S, C>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -88,15 +92,15 @@ where
fn call(&mut self, target: T) -> Self::Future {
let connect_info = ConnectInfo(C::connect_info(target));
let svc = AddExtensionLayer::new(connect_info).layer(self.router.clone());
let svc = AddExtensionLayer::new(connect_info).layer(self.svc.clone());
ResponseFuture::new(ready(Ok(svc)))
}
}
opaque_future! {
/// Response future for [`IntoMakeServiceWithConnectInfo`].
pub type ResponseFuture<B, C> =
std::future::Ready<Result<AddExtension<Router<B>, ConnectInfo<C>>, Infallible>>;
pub type ResponseFuture<S, C> =
std::future::Ready<Result<AddExtension<S, ConnectInfo<C>>, Infallible>>;
}
/// Extractor for getting connection information produced by a [`Connected`].

View file

@ -70,8 +70,12 @@
use crate::{
body::{box_body, BoxBody},
extract::{FromRequest, RequestParts},
extract::{
connect_info::{Connected, IntoMakeServiceWithConnectInfo},
FromRequest, RequestParts,
},
response::IntoResponse,
routing::IntoMakeService,
BoxError,
};
use async_trait::async_trait;
@ -156,30 +160,102 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
/// Convert the handler into a [`Service`].
///
/// This is commonly used together with [`Router::fallback`]:
///
/// ```rust
/// use axum::{
/// Server,
/// handler::Handler,
/// http::{Uri, Method, StatusCode},
/// response::IntoResponse,
/// routing::{get, Router},
/// };
/// use tower::make::Shared;
/// use std::net::SocketAddr;
///
/// async fn handler(method: Method, uri: Uri) -> impl IntoResponse {
/// (StatusCode::NOT_FOUND, format!("Nothing to see at {} {}", method, uri))
/// }
///
/// let app = Router::new()
/// .route("/", get(|| async {}))
/// .fallback(handler.into_service());
///
/// # async {
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
/// .serve(app.into_make_service())
/// .await?;
/// # Ok::<_, hyper::Error>(())
/// # };
/// ```
///
/// [`Router::fallback`]: crate::routing::Router::fallback
fn into_service(self) -> IntoService<Self, B, T> {
IntoService::new(self)
}
/// Convert the handler into a [`MakeService`].
///
/// This allows you to serve a single handler if you don't need any routing:
///
/// ```rust
/// use axum::{
/// Server, handler::Handler, http::{Uri, Method}, response::IntoResponse,
/// };
/// use tower::make::Shared;
/// use std::net::SocketAddr;
///
/// async fn handler(method: Method, uri: Uri, body: String) -> impl IntoResponse {
/// format!("received `{} {}` with body `{:?}`", method, uri, body)
/// }
///
/// let service = handler.into_service();
///
/// # async {
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
/// .serve(Shared::new(service))
/// .serve(handler.into_make_service())
/// .await?;
/// # Ok::<_, hyper::Error>(())
/// # };
/// ```
fn into_service(self) -> IntoService<Self, B, T> {
IntoService::new(self)
///
/// [`MakeService`]: tower::make::MakeService
fn into_make_service(self) -> IntoMakeService<IntoService<Self, B, T>> {
IntoMakeService::new(self.into_service())
}
/// Convert the handler into a [`MakeService`] which stores information
/// about the incoming connection.
///
/// See [`Router::into_make_service_with_connect_info`] for more details.
///
/// ```rust
/// use axum::{
/// Server,
/// handler::Handler,
/// response::IntoResponse,
/// extract::ConnectInfo,
/// };
/// use std::net::SocketAddr;
///
/// async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> impl IntoResponse {
/// format!("Hello {}", addr)
/// }
///
/// # async {
/// Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000)))
/// .serve(handler.into_make_service_with_connect_info::<SocketAddr, _>())
/// .await?;
/// # Ok::<_, hyper::Error>(())
/// # };
/// ```
///
/// [`MakeService`]: tower::make::MakeService
/// [`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>(
self,
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, B, T>, C>
where
C: Connected<Target>,
{
IntoMakeServiceWithConnectInfo::new(self.into_service())
}
}

View file

@ -1,7 +1,5 @@
use super::Router;
use std::{
convert::Infallible,
fmt,
future::ready,
task::{Context, Poll},
};
@ -10,36 +8,24 @@ use tower_service::Service;
/// A [`MakeService`] that produces axum router services.
///
/// [`MakeService`]: tower::make::MakeService
pub struct IntoMakeService<B> {
router: Router<B>,
#[derive(Debug, Clone)]
pub struct IntoMakeService<S> {
svc: S,
}
impl<B> IntoMakeService<B> {
pub(super) fn new(router: Router<B>) -> Self {
Self { router }
impl<S> IntoMakeService<S> {
pub(crate) fn new(svc: S) -> Self {
Self { svc }
}
}
impl<B> Clone for IntoMakeService<B> {
fn clone(&self) -> Self {
Self {
router: self.router.clone(),
}
}
}
impl<B> fmt::Debug for IntoMakeService<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IntoMakeService")
.field("router", &self.router)
.finish()
}
}
impl<B, T> Service<T> for IntoMakeService<B> {
type Response = Router<B>;
impl<S, T> Service<T> for IntoMakeService<S>
where
S: Clone,
{
type Response = S;
type Error = Infallible;
type Future = IntoMakeServiceFuture<B>;
type Future = IntoMakeServiceFuture<S>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -47,14 +33,14 @@ impl<B, T> Service<T> for IntoMakeService<B> {
}
fn call(&mut self, _target: T) -> Self::Future {
IntoMakeServiceFuture::new(ready(Ok(self.router.clone())))
IntoMakeServiceFuture::new(ready(Ok(self.svc.clone())))
}
}
opaque_future! {
/// Response future for [`IntoMakeService`].
pub type IntoMakeServiceFuture<B> =
std::future::Ready<Result<Router<B>, Infallible>>;
pub type IntoMakeServiceFuture<S> =
std::future::Ready<Result<S, Infallible>>;
}
#[cfg(test)]

View file

@ -302,14 +302,14 @@ where
/// ```
///
/// [`MakeService`]: tower::make::MakeService
pub fn into_make_service(self) -> IntoMakeService<B> {
pub fn into_make_service(self) -> IntoMakeService<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<B, C>
) -> IntoMakeServiceWithConnectInfo<Self, C>
where
C: Connected<Target>,
{