//! Convert an extractor into a middleware. //! //! See [`extractor_middleware`] for more details. use super::{FromRequest, RequestParts}; use crate::BoxError; use crate::{body::BoxBody, response::IntoResponse}; use bytes::Bytes; use futures_util::{future::BoxFuture, ready}; use http::{Request, Response}; use pin_project_lite::pin_project; use std::{ fmt, future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use tower_layer::Layer; use tower_service::Service; /// Convert an extractor into a middleware. /// /// If the extractor succeeds the value will be discarded and the inner service /// will be called. If the extractor fails the rejection will be returned and /// the inner service will _not_ be called. /// /// This can be used to perform validation of requests if the validation doesn't /// produce any useful output, and run the extractor for several handlers /// without repeating it in the function signature. /// /// Note that if the extractor consumes the request body, as `String` or /// [`Bytes`] does, an empty body will be left in its place. Thus wont be /// accessible to subsequent extractors or handlers. /// /// # Example /// /// ```rust /// use axum::{ /// extract::{extractor_middleware, FromRequest, RequestParts}, /// routing::{get, post}, /// Router, /// }; /// use http::StatusCode; /// use async_trait::async_trait; /// /// // An extractor that performs authorization. /// struct RequireAuth; /// /// #[async_trait] /// impl FromRequest for RequireAuth /// where /// B: Send, /// { /// type Rejection = StatusCode; /// /// async fn from_request(req: &mut RequestParts) -> Result { /// let auth_header = req /// .headers() /// .and_then(|headers| headers.get(http::header::AUTHORIZATION)) /// .and_then(|value| value.to_str().ok()); /// /// if let Some(value) = auth_header { /// if value == "secret" { /// return Ok(Self); /// } /// } /// /// Err(StatusCode::UNAUTHORIZED) /// } /// } /// /// async fn handler() { /// // If we get here the request has been authorized /// } /// /// async fn other_handler() { /// // If we get here the request has been authorized /// } /// /// let app = Router::new() /// .route("/", get(handler)) /// .route("/foo", post(other_handler)) /// // The extractor will run before all routes /// .layer(extractor_middleware::()); /// # async { /// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap(); /// # }; /// ``` pub fn extractor_middleware() -> ExtractorMiddlewareLayer { ExtractorMiddlewareLayer(PhantomData) } /// [`Layer`] that applies [`ExtractorMiddleware`] that runs an extractor and /// discards the value. /// /// See [`extractor_middleware`] for more details. /// /// [`Layer`]: tower::Layer pub struct ExtractorMiddlewareLayer(PhantomData E>); impl Clone for ExtractorMiddlewareLayer { fn clone(&self) -> Self { Self(PhantomData) } } impl fmt::Debug for ExtractorMiddlewareLayer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ExtractorMiddleware") .field("extractor", &format_args!("{}", std::any::type_name::())) .finish() } } impl Layer for ExtractorMiddlewareLayer { type Service = ExtractorMiddleware; fn layer(&self, inner: S) -> Self::Service { ExtractorMiddleware { inner, _extractor: PhantomData, } } } /// Middleware that runs an extractor and discards the value. /// /// See [`extractor_middleware`] for more details. pub struct ExtractorMiddleware { inner: S, _extractor: PhantomData E>, } #[test] fn traits() { use crate::tests::*; assert_send::>(); assert_sync::>(); } impl Clone for ExtractorMiddleware where S: Clone, { fn clone(&self) -> Self { Self { inner: self.inner.clone(), _extractor: PhantomData, } } } impl fmt::Debug for ExtractorMiddleware where S: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ExtractorMiddleware") .field("inner", &self.inner) .field("extractor", &format_args!("{}", std::any::type_name::())) .finish() } } impl Service> for ExtractorMiddleware where E: FromRequest + 'static, ReqBody: Default + Send + 'static, S: Service, Response = Response> + Clone, ResBody: http_body::Body + Send + Sync + 'static, ResBody::Error: Into, { type Response = Response; type Error = S::Error; type Future = ResponseFuture; #[inline] fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, req: Request) -> Self::Future { let extract_future = Box::pin(async move { let mut req = super::RequestParts::new(req); let extracted = E::from_request(&mut req).await; (req, extracted) }); ResponseFuture { state: State::Extracting { future: extract_future, }, svc: Some(self.inner.clone()), } } } pin_project! { /// Response future for [`ExtractorMiddleware`]. #[allow(missing_debug_implementations)] pub struct ResponseFuture where E: FromRequest, S: Service>, { #[pin] state: State, svc: Option, } } pin_project! { #[project = StateProj] enum State where E: FromRequest, S: Service>, { Extracting { future: BoxFuture<'static, (RequestParts, Result)> }, Call { #[pin] future: S::Future }, } } impl Future for ResponseFuture where E: FromRequest, S: Service, Response = Response>, ReqBody: Default, ResBody: http_body::Body + Send + Sync + 'static, ResBody::Error: Into, { type Output = Result, S::Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { let mut this = self.as_mut().project(); let new_state = match this.state.as_mut().project() { StateProj::Extracting { future } => { let (req, extracted) = ready!(future.as_mut().poll(cx)); match extracted { Ok(_) => { let mut svc = this.svc.take().expect("future polled after completion"); let req = req.try_into_request().unwrap_or_default(); let future = svc.call(req); State::Call { future } } Err(err) => { let res = err.into_response().map(crate::body::box_body); return Poll::Ready(Ok(res)); } } } StateProj::Call { future } => { return future .poll(cx) .map(|result| result.map(|response| response.map(crate::body::box_body))); } }; this.state.set(new_state); } } }