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}
(#444)
* 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:
parent
98907b8887
commit
9512d14c99
5 changed files with 122 additions and 52 deletions
|
@ -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.
|
||||
|
|
|
@ -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`].
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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>,
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue