mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-29 00:00:18 +02:00
Bring back Handler::into_service (#212)
It was removed as part of https://github.com/tokio-rs/axum/pull/184 but I do actually think it has some utility. So makes sense to keep even if axum doesn't use it directly for routing.
This commit is contained in:
+14
-1
@@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
use crate::body::{box_body, BoxBody};
|
use crate::body::{box_body, BoxBody};
|
||||||
use crate::util::{Either, EitherProj};
|
use crate::util::{Either, EitherProj};
|
||||||
use futures_util::{future::BoxFuture, ready};
|
use futures_util::{
|
||||||
|
future::{BoxFuture, Map},
|
||||||
|
ready,
|
||||||
|
};
|
||||||
use http::{Method, Request, Response};
|
use http::{Method, Request, Response};
|
||||||
use http_body::Empty;
|
use http_body::Empty;
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use std::{
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
fmt,
|
fmt,
|
||||||
future::Future,
|
future::Future,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
@@ -59,3 +63,12 @@ where
|
|||||||
f.debug_struct("OnMethodFuture").finish()
|
f.debug_struct("OnMethodFuture").finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opaque_future! {
|
||||||
|
/// The response future for [`IntoService`](super::IntoService).
|
||||||
|
pub type IntoServiceFuture =
|
||||||
|
Map<
|
||||||
|
BoxFuture<'static, Response<BoxBody>>,
|
||||||
|
fn(Response<BoxBody>) -> Result<Response<BoxBody>, Infallible>,
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use super::Handler;
|
||||||
|
use crate::body::BoxBody;
|
||||||
|
use http::{Request, Response};
|
||||||
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
|
fmt,
|
||||||
|
marker::PhantomData,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
use tower::Service;
|
||||||
|
|
||||||
|
/// An adapter that makes a [`Handler`] into a [`Service`].
|
||||||
|
///
|
||||||
|
/// Created with [`Handler::into_service`].
|
||||||
|
pub struct IntoService<H, B, T> {
|
||||||
|
handler: H,
|
||||||
|
_marker: PhantomData<fn() -> (B, T)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, B, T> IntoService<H, B, T> {
|
||||||
|
pub(super) fn new(handler: H) -> Self {
|
||||||
|
Self {
|
||||||
|
handler,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, B, T> fmt::Debug for IntoService<H, B, T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("IntoService")
|
||||||
|
.field(&format_args!("..."))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, B, T> Clone for IntoService<H, B, T>
|
||||||
|
where
|
||||||
|
H: Clone,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
handler: self.handler.clone(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, T, B> Service<Request<B>> for IntoService<H, B, T>
|
||||||
|
where
|
||||||
|
H: Handler<B, T> + Clone + Send + 'static,
|
||||||
|
B: Send + 'static,
|
||||||
|
{
|
||||||
|
type Response = Response<BoxBody>;
|
||||||
|
type Error = Infallible;
|
||||||
|
type Future = super::future::IntoServiceFuture;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
// `IntoService` can only be constructed from async functions which are always ready, or from
|
||||||
|
// `Layered` which bufferes in `<Layered as Handler>::call` and is therefore also always
|
||||||
|
// ready.
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request<B>) -> Self::Future {
|
||||||
|
use futures_util::future::FutureExt;
|
||||||
|
|
||||||
|
let handler = self.handler.clone();
|
||||||
|
let future = Handler::call(handler, req).map(Ok::<_, Infallible> as _);
|
||||||
|
|
||||||
|
super::future::IntoServiceFuture { future }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,9 @@ use std::{
|
|||||||
use tower::{BoxError, Layer, Service, ServiceExt};
|
use tower::{BoxError, Layer, Service, ServiceExt};
|
||||||
|
|
||||||
pub mod future;
|
pub mod future;
|
||||||
|
mod into_service;
|
||||||
|
|
||||||
|
pub use self::into_service::IntoService;
|
||||||
|
|
||||||
/// Route requests to the given handler regardless of the HTTP method of the
|
/// Route requests to the given handler regardless of the HTTP method of the
|
||||||
/// request.
|
/// request.
|
||||||
@@ -255,6 +258,34 @@ pub trait Handler<B, T>: Clone + Send + Sized + 'static {
|
|||||||
{
|
{
|
||||||
Layered::new(layer.layer(any(self)))
|
Layered::new(layer.layer(any(self)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the handler into a [`Service`].
|
||||||
|
///
|
||||||
|
/// 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))
|
||||||
|
/// .await?;
|
||||||
|
/// # Ok::<_, hyper::Error>(())
|
||||||
|
/// # };
|
||||||
|
/// ```
|
||||||
|
fn into_service(self) -> IntoService<Self, B, T> {
|
||||||
|
IntoService::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
extract,
|
extract,
|
||||||
handler::{any, delete, get, on, patch, post, Handler},
|
handler::{any, delete, get, on, patch, post, Handler},
|
||||||
|
response::IntoResponse,
|
||||||
route,
|
route,
|
||||||
routing::nest,
|
routing::nest,
|
||||||
routing::{MethodFilter, RoutingDsl},
|
routing::{MethodFilter, RoutingDsl},
|
||||||
@@ -604,6 +605,26 @@ async fn multiple_methods_for_one_handler() {
|
|||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn handler_into_service() {
|
||||||
|
async fn handle(body: String) -> impl IntoResponse {
|
||||||
|
format!("you said: {}", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
let addr = run_in_background(handle.into_service()).await;
|
||||||
|
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.post(format!("http://{}", addr))
|
||||||
|
.body("hi there!")
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
assert_eq!(res.text().await.unwrap(), "you said: hi there!");
|
||||||
|
}
|
||||||
|
|
||||||
/// Run a `tower::Service` in the background and get a URI for it.
|
/// Run a `tower::Service` in the background and get a URI for it.
|
||||||
pub(crate) async fn run_in_background<S, ResBody>(svc: S) -> SocketAddr
|
pub(crate) async fn run_in_background<S, ResBody>(svc: S) -> SocketAddr
|
||||||
where
|
where
|
||||||
|
|||||||
Reference in New Issue
Block a user