This commit is contained in:
Jonas Platte 2022-12-20 21:31:23 +01:00
parent 978ae63358
commit b8e1aad4d6
No known key found for this signature in database
GPG key ID: 7D261D771D915378
10 changed files with 89 additions and 84 deletions

5
axum-core/src/layer.rs Normal file
View file

@ -0,0 +1,5 @@
trait Layer<S> {
type Service;
fn layer(&self, inner: S) -> Self::Service;
}

View file

@ -48,13 +48,19 @@
#![allow(elided_lifetimes_in_paths, clippy::type_complexity)] #![allow(elided_lifetimes_in_paths, clippy::type_complexity)]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![cfg_attr(test, allow(clippy::float_cmp))] #![cfg_attr(test, allow(clippy::float_cmp))]
#![feature(type_alias_impl_trait)]
#[macro_use] #[macro_use]
pub(crate) mod macros; pub(crate) mod macros;
mod error; mod error;
mod ext_traits; mod ext_traits;
//mod layer;
mod service;
pub use self::error::Error; pub use self::error::Error;
pub use self::ext_traits::{request::RequestExt, request_parts::RequestPartsExt};
pub use self::service::Service;
pub mod body; pub mod body;
pub mod extract; pub mod extract;
@ -62,5 +68,3 @@ pub mod response;
/// Alias for a type-erased error type. /// Alias for a type-erased error type.
pub type BoxError = Box<dyn std::error::Error + Send + Sync>; pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
pub use self::ext_traits::{request::RequestExt, request_parts::RequestPartsExt};

39
axum-core/src/service.rs Normal file
View file

@ -0,0 +1,39 @@
#![allow(missing_docs)] // temporary
use http::Request;
use std::{
convert::Infallible,
future::Future,
task::{Context, Poll},
};
use tower_service::Service as TowerService;
use crate::response::{IntoResponse, Response};
pub trait Service<S, ReqBody> {
type Future: Future<Output = Response>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<()>;
fn call(&mut self, req: Request<ReqBody>, state: &S) -> Self::Future;
}
impl<T, S, ReqBody, Resp> Service<S, ReqBody> for T
where
T: TowerService<Request<ReqBody>, Response = Resp, Error = Infallible>,
Resp: IntoResponse,
{
type Future = impl Future<Output = Response>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<()> {
TowerService::poll_ready(self, cx).map(|result| result.unwrap_or_else(|e| match e {}))
}
fn call(&mut self, req: Request<ReqBody>, _state: &S) -> Self::Future {
let fut = TowerService::call(self, req);
async move {
match fut.await {
Ok(res) => res.into_response(),
Err(e) => match e {},
}
}
}
}

View file

@ -145,7 +145,7 @@ pub trait Handler<T, S, B = Body>: Clone + Send + Sized + 'static {
fn layer<L, NewReqBody>(self, layer: L) -> Layered<L, Self, T, S, B, NewReqBody> fn layer<L, NewReqBody>(self, layer: L) -> Layered<L, Self, T, S, B, NewReqBody>
where where
L: Layer<HandlerService<Self, T, S, B>> + Clone, L: Layer<HandlerService<Self, T, S, B>> + Clone,
L::Service: Service<Request<NewReqBody>>, L::Service: crate::Service<S, NewReqBody>,
{ {
Layered { Layered {
layer, layer,

View file

@ -487,7 +487,7 @@ pub use self::typed_header::TypedHeader;
pub use self::form::Form; pub use self::form::Form;
#[doc(inline)] #[doc(inline)]
pub use axum_core::{BoxError, Error, RequestExt, RequestPartsExt}; pub use axum_core::{BoxError, Error, RequestExt, RequestPartsExt, Service};
#[cfg(feature = "macros")] #[cfg(feature = "macros")]
pub use axum_macros::debug_handler; pub use axum_macros::debug_handler;

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
extract::FromRequestParts, extract::FromRequestParts,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
Service,
}; };
use futures_util::{future::BoxFuture, ready}; use futures_util::{future::BoxFuture, ready};
use http::Request; use http::Request;
@ -13,7 +14,6 @@ use std::{
task::{Context, Poll}, task::{Context, Poll},
}; };
use tower_layer::Layer; use tower_layer::Layer;
use tower_service::Service;
/// Create a middleware from an extractor. /// Create a middleware from an extractor.
/// ///
@ -90,16 +90,8 @@ use tower_service::Service;
/// ``` /// ```
/// ///
/// [`Bytes`]: bytes::Bytes /// [`Bytes`]: bytes::Bytes
pub fn from_extractor<E>() -> FromExtractorLayer<E, ()> { pub fn from_extractor<E>() -> FromExtractorLayer<E> {
from_extractor_with_state(())
}
/// Create a middleware from an extractor with the given state.
///
/// See [`State`](crate::extract::State) for more details about accessing state.
pub fn from_extractor_with_state<E, S>(state: S) -> FromExtractorLayer<E, S> {
FromExtractorLayer { FromExtractorLayer {
state,
_marker: PhantomData, _marker: PhantomData,
} }
} }
@ -110,45 +102,32 @@ pub fn from_extractor_with_state<E, S>(state: S) -> FromExtractorLayer<E, S> {
/// See [`from_extractor`] for more details. /// See [`from_extractor`] for more details.
/// ///
/// [`Layer`]: tower::Layer /// [`Layer`]: tower::Layer
pub struct FromExtractorLayer<E, S> { pub struct FromExtractorLayer<E> {
state: S,
_marker: PhantomData<fn() -> E>, _marker: PhantomData<fn() -> E>,
} }
impl<E, S> Clone for FromExtractorLayer<E, S> impl<E> Clone for FromExtractorLayer<E> {
where
S: Clone,
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
state: self.state.clone(),
_marker: PhantomData, _marker: PhantomData,
} }
} }
} }
impl<E, S> fmt::Debug for FromExtractorLayer<E, S> impl<E> fmt::Debug for FromExtractorLayer<E> {
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FromExtractorLayer") f.debug_struct("FromExtractorLayer")
.field("state", &self.state)
.field("extractor", &format_args!("{}", std::any::type_name::<E>())) .field("extractor", &format_args!("{}", std::any::type_name::<E>()))
.finish() .finish()
} }
} }
impl<E, T, S> Layer<T> for FromExtractorLayer<E, S> impl<E, T> Layer<T> for FromExtractorLayer<E> {
where type Service = FromExtractor<T, E>;
S: Clone,
{
type Service = FromExtractor<T, E, S>;
fn layer(&self, inner: T) -> Self::Service { fn layer(&self, inner: T) -> Self::Service {
FromExtractor { FromExtractor {
inner, inner,
state: self.state.clone(),
_extractor: PhantomData, _extractor: PhantomData,
} }
} }
@ -157,66 +136,58 @@ where
/// Middleware that runs an extractor and discards the value. /// Middleware that runs an extractor and discards the value.
/// ///
/// See [`from_extractor`] for more details. /// See [`from_extractor`] for more details.
pub struct FromExtractor<T, E, S> { pub struct FromExtractor<T, E> {
inner: T, inner: T,
state: S,
_extractor: PhantomData<fn() -> E>, _extractor: PhantomData<fn() -> E>,
} }
#[test] #[test]
fn traits() { fn traits() {
use crate::test_helpers::*; use crate::test_helpers::*;
assert_send::<FromExtractor<(), NotSendSync, ()>>(); assert_send::<FromExtractor<(), NotSendSync>>();
assert_sync::<FromExtractor<(), NotSendSync, ()>>(); assert_sync::<FromExtractor<(), NotSendSync>>();
} }
impl<T, E, S> Clone for FromExtractor<T, E, S> impl<T, E> Clone for FromExtractor<T, E>
where where
T: Clone, T: Clone,
S: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
inner: self.inner.clone(), inner: self.inner.clone(),
state: self.state.clone(),
_extractor: PhantomData, _extractor: PhantomData,
} }
} }
} }
impl<T, E, S> fmt::Debug for FromExtractor<T, E, S> impl<T, E> fmt::Debug for FromExtractor<T, E>
where where
T: fmt::Debug, T: fmt::Debug,
S: fmt::Debug,
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FromExtractor") f.debug_struct("FromExtractor")
.field("inner", &self.inner) .field("inner", &self.inner)
.field("state", &self.state)
.field("extractor", &format_args!("{}", std::any::type_name::<E>())) .field("extractor", &format_args!("{}", std::any::type_name::<E>()))
.finish() .finish()
} }
} }
impl<T, E, B, S> Service<Request<B>> for FromExtractor<T, E, S> impl<T, E, B, S> Service<S, B> for FromExtractor<T, E>
where where
E: FromRequestParts<S> + 'static, E: FromRequestParts<S> + 'static,
B: Send + 'static, B: Send + 'static,
T: Service<Request<B>> + Clone, T: Service<S, B> + Clone,
T::Response: IntoResponse,
S: Clone + Send + Sync + 'static, S: Clone + Send + Sync + 'static,
{ {
type Response = Response;
type Error = T::Error;
type Future = ResponseFuture<B, T, E, S>; type Future = ResponseFuture<B, T, E, S>;
#[inline] #[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<()> {
self.inner.poll_ready(cx) self.inner.poll_ready(cx)
} }
fn call(&mut self, req: Request<B>) -> Self::Future { fn call(&mut self, req: Request<B>, st: &S) -> Self::Future {
let state = self.state.clone(); let state = st.to_owned();
let extract_future = Box::pin(async move { let extract_future = Box::pin(async move {
let (mut parts, body) = req.into_parts(); let (mut parts, body) = req.into_parts();
let extracted = E::from_request_parts(&mut parts, &state).await; let extracted = E::from_request_parts(&mut parts, &state).await;
@ -226,6 +197,7 @@ where
ResponseFuture { ResponseFuture {
state: State::Extracting { state: State::Extracting {
st: st.clone(),
future: extract_future, future: extract_future,
}, },
svc: Some(self.inner.clone()), svc: Some(self.inner.clone()),
@ -239,7 +211,7 @@ pin_project! {
pub struct ResponseFuture<B, T, E, S> pub struct ResponseFuture<B, T, E, S>
where where
E: FromRequestParts<S>, E: FromRequestParts<S>,
T: Service<Request<B>>, T: Service<S, B>,
{ {
#[pin] #[pin]
state: State<B, T, E, S>, state: State<B, T, E, S>,
@ -252,9 +224,10 @@ pin_project! {
enum State<B, T, E, S> enum State<B, T, E, S>
where where
E: FromRequestParts<S>, E: FromRequestParts<S>,
T: Service<Request<B>>, T: Service<S, B>,
{ {
Extracting { Extracting {
st: S,
future: BoxFuture<'static, (Request<B>, Result<E, E::Rejection>)>, future: BoxFuture<'static, (Request<B>, Result<E, E::Rejection>)>,
}, },
Call { #[pin] future: T::Future }, Call { #[pin] future: T::Future },
@ -264,35 +237,32 @@ pin_project! {
impl<B, T, E, S> Future for ResponseFuture<B, T, E, S> impl<B, T, E, S> Future for ResponseFuture<B, T, E, S>
where where
E: FromRequestParts<S>, E: FromRequestParts<S>,
T: Service<Request<B>>, T: Service<S, B>,
T::Response: IntoResponse,
{ {
type Output = Result<Response, T::Error>; type Output = Response;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop { loop {
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
let new_state = match this.state.as_mut().project() { let new_state = match this.state.as_mut().project() {
StateProj::Extracting { future } => { StateProj::Extracting { future, st } => {
let (req, extracted) = ready!(future.as_mut().poll(cx)); let (req, extracted) = ready!(future.as_mut().poll(cx));
match extracted { match extracted {
Ok(_) => { Ok(_) => {
let mut svc = this.svc.take().expect("future polled after completion"); let mut svc = this.svc.take().expect("future polled after completion");
let future = svc.call(req); let future = svc.call(req, st);
State::Call { future } State::Call { future }
} }
Err(err) => { Err(err) => {
let res = err.into_response(); let res = err.into_response();
return Poll::Ready(Ok(res)); return Poll::Ready(res);
} }
} }
} }
StateProj::Call { future } => { StateProj::Call { future } => {
return future return future.poll(cx);
.poll(cx)
.map(|result| result.map(IntoResponse::into_response));
} }
}; };
@ -346,10 +316,7 @@ mod tests {
async fn handler() {} async fn handler() {}
let state = Secret("secret"); let state = Secret("secret");
let app = Router::new().route( let app = Router::new().route("/", get(handler.layer(from_extractor())));
"/",
get(handler.layer(from_extractor_with_state::<RequireAuth, _>(state))),
);
let client = TestClient::new(app); let client = TestClient::new(app);

View file

@ -7,9 +7,7 @@ mod from_fn;
mod map_request; mod map_request;
mod map_response; mod map_response;
pub use self::from_extractor::{ pub use self::from_extractor::{from_extractor, FromExtractor, FromExtractorLayer};
from_extractor, from_extractor_with_state, FromExtractor, FromExtractorLayer,
};
pub use self::from_fn::{from_fn, from_fn_with_state, FromFn, FromFnLayer, Next}; pub use self::from_fn::{from_fn, from_fn_with_state, FromFn, FromFnLayer, Next};
pub use self::map_request::{ pub use self::map_request::{
map_request, map_request_with_state, IntoMapRequestResult, MapRequest, MapRequestLayer, map_request, map_request_with_state, IntoMapRequestResult, MapRequest, MapRequestLayer,

View file

@ -919,10 +919,8 @@ where
pub fn layer<L, NewReqBody, NewError>(self, layer: L) -> MethodRouter<S, NewReqBody, NewError> pub fn layer<L, NewReqBody, NewError>(self, layer: L) -> MethodRouter<S, NewReqBody, NewError>
where where
L: Layer<Route<B, E>> + Clone + Send + 'static, L: Layer<Route<B, E>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static, L::Service: crate::Service<S, NewReqBody> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static, <L::Service as crate::Service<S, NewReqBody>>::Future: Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<NewError> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
E: 'static, E: 'static,
S: 'static, S: 'static,
NewReqBody: HttpBody + 'static, NewReqBody: HttpBody + 'static,

View file

@ -314,10 +314,8 @@ where
pub fn layer<L, NewReqBody>(self, layer: L) -> Router<S, NewReqBody> pub fn layer<L, NewReqBody>(self, layer: L) -> Router<S, NewReqBody>
where where
L: Layer<Route<B>> + Clone + Send + 'static, L: Layer<Route<B>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static, L::Service: crate::Service<S, NewReqBody> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static, <L::Service as crate::Service<S, NewReqBody>>::Future: Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
NewReqBody: HttpBody + 'static, NewReqBody: HttpBody + 'static,
{ {
let routes = self let routes = self
@ -688,10 +686,8 @@ where
fn layer<L, NewReqBody>(self, layer: L) -> Endpoint<S, NewReqBody> fn layer<L, NewReqBody>(self, layer: L) -> Endpoint<S, NewReqBody>
where where
L: Layer<Route<B>> + Clone + Send + 'static, L: Layer<Route<B>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static, L::Service: crate::Service<S, NewReqBody> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static, <L::Service as crate::Service<S, NewReqBody>>::Future: Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
NewReqBody: HttpBody + 'static, NewReqBody: HttpBody + 'static,
{ {
match self { match self {

View file

@ -51,10 +51,8 @@ impl<B, E> Route<B, E> {
pub(crate) fn layer<L, NewReqBody, NewError>(self, layer: L) -> Route<NewReqBody, NewError> pub(crate) fn layer<L, NewReqBody, NewError>(self, layer: L) -> Route<NewReqBody, NewError>
where where
L: Layer<Route<B, E>> + Clone + Send + 'static, L: Layer<Route<B, E>> + Clone + Send + 'static,
L::Service: Service<Request<NewReqBody>> + Clone + Send + 'static, L::Service: crate::Service<S, NewReqBody> + Clone + Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Response: IntoResponse + 'static, <L::Service as crate::Service<S, NewReqBody>>::Future: Send + 'static,
<L::Service as Service<Request<NewReqBody>>>::Error: Into<NewError> + 'static,
<L::Service as Service<Request<NewReqBody>>>::Future: Send + 'static,
NewReqBody: 'static, NewReqBody: 'static,
NewError: 'static, NewError: 'static,
{ {