From 9a4090761cecdf1df373cc6949d73c0dc2d9f968 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Nov 2023 02:53:32 +0000 Subject: [PATCH] feat: use PRITIT for FromRequest --- actix-files/src/path_buf.rs | 7 +- actix-web/Cargo.toml | 1 + actix-web/src/data.rs | 8 +- actix-web/src/extract.rs | 169 +++++++-------------------------- actix-web/src/info.rs | 15 ++- actix-web/src/request.rs | 11 +-- actix-web/src/request_data.rs | 18 ++-- actix-web/src/service.rs | 4 +- actix-web/src/types/either.rs | 124 +++--------------------- actix-web/src/types/form.rs | 50 +++------- actix-web/src/types/header.rs | 9 +- actix-web/src/types/json.rs | 62 ++++-------- actix-web/src/types/path.rs | 35 +++---- actix-web/src/types/payload.rs | 83 +++++----------- actix-web/src/types/query.rs | 22 ++--- rust-toolchain | 1 + 16 files changed, 160 insertions(+), 459 deletions(-) create mode 100644 rust-toolchain diff --git a/actix-files/src/path_buf.rs b/actix-files/src/path_buf.rs index c1983279b..2d1ffe36b 100644 --- a/actix-files/src/path_buf.rs +++ b/actix-files/src/path_buf.rs @@ -3,7 +3,6 @@ use std::{ str::FromStr, }; -use actix_utils::future::{ready, Ready}; use actix_web::{dev::Payload, FromRequest, HttpRequest}; use crate::error::UriSegmentError; @@ -88,10 +87,10 @@ impl AsRef for PathBufWrap { impl FromRequest for PathBufWrap { type Error = UriSegmentError; - type Future = Ready>; - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - ready(req.match_info().unprocessed().parse()) + #[inline] + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { + req.match_info().unprocessed().parse() } } diff --git a/actix-web/Cargo.toml b/actix-web/Cargo.toml index d9cf0b94f..4105252da 100644 --- a/actix-web/Cargo.toml +++ b/actix-web/Cargo.toml @@ -100,6 +100,7 @@ serde_json = "1.0" serde_urlencoded = "0.7" smallvec = "1.6.1" socket2 = "0.5" +tokio = { version = "1.24.2", features = ["macros"] } time = { version = "0.3", default-features = false, features = ["formatting"] } url = "2.1" diff --git a/actix-web/src/data.rs b/actix-web/src/data.rs index ebb98af3f..a4e1fdb5d 100644 --- a/actix-web/src/data.rs +++ b/actix-web/src/data.rs @@ -1,7 +1,6 @@ use std::{any::type_name, ops::Deref, sync::Arc}; use actix_http::Extensions; -use actix_utils::future::{err, ok, Ready}; use futures_core::future::LocalBoxFuture; use serde::{de, Serialize}; @@ -159,12 +158,11 @@ where impl FromRequest for Data { type Error = Error; - type Future = Ready>; #[inline] - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { if let Some(st) = req.app_data::>() { - ok(st.clone()) + Ok(st.clone()) } else { log::debug!( "Failed to extract `Data<{}>` for `{}` handler. For the Data extractor to work \ @@ -174,7 +172,7 @@ impl FromRequest for Data { req.match_name().unwrap_or_else(|| req.path()) ); - err(error::ErrorInternalServerError( + Err(error::ErrorInternalServerError( "Requested application data is not configured correctly. \ View/enable debug logs for more details.", )) diff --git a/actix-web/src/extract.rs b/actix-web/src/extract.rs index 249b56114..da7a13276 100644 --- a/actix-web/src/extract.rs +++ b/actix-web/src/extract.rs @@ -3,13 +3,11 @@ use std::{ convert::Infallible, future::Future, - marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use actix_http::{Method, Uri}; -use actix_utils::future::{ok, Ready}; use futures_core::ready; use pin_project_lite::pin_project; @@ -66,33 +64,17 @@ pub trait FromRequest: Sized { /// The associated error which can be returned. type Error: Into; - /// Future that resolves to a `Self`. - /// - /// To use an async function or block, the futures must be boxed. The following snippet will be - /// common when creating async/await extractors (that do not consume the body). - /// - /// ```ignore - /// type Future = Pin>>>; - /// // or - /// type Future = futures_util::future::LocalBoxFuture<'static, Result>; - /// - /// fn from_request(req: HttpRequest, ...) -> Self::Future { - /// let req = req.clone(); - /// Box::pin(async move { - /// ... - /// }) - /// } - /// ``` - type Future: Future>; - - /// Create a `Self` from request parts asynchronously. - fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future; + /// Creates a `Self` from request parts asynchronously. + fn from_request( + req: &HttpRequest, + payload: &mut Payload, + ) -> impl Future>; /// Create a `Self` from request head asynchronously. /// /// This method is short for `T::from_request(req, &mut Payload::None)`. - fn extract(req: &HttpRequest) -> Self::Future { - Self::from_request(req, &mut Payload::None) + fn extract(req: &HttpRequest) -> impl Future> { + async { Self::from_request(req, &mut Payload::None).await } } } @@ -146,12 +128,19 @@ where T: FromRequest, { type Error = Infallible; - type Future = FromRequestOptFuture; #[inline] - fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { - FromRequestOptFuture { - fut: T::from_request(req, payload), + async fn from_request(req: &HttpRequest, payload: &mut Payload) -> Result { + match T::from_request(req, payload).await { + Ok(t) => Ok(Some(t)), + Err(err) => { + log::debug!( + "Error from `Option<{}>` extractor: {}", + std::any::type_name::(), + err.into() + ); + Ok(None) + } } } } @@ -203,9 +192,11 @@ where /// /// impl FromRequest for Thing { /// type Error = Error; -/// type Future = Ready>; /// -/// fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { +/// async fn from_request( +/// req: &HttpRequest, +/// payload: &mut dev::Payload, +/// ) -> Result { /// if rand::random() { /// ok(Thing { name: "thingy".into() }) /// } else { @@ -232,36 +223,10 @@ where T::Error: Into, { type Error = Infallible; - type Future = FromRequestResFuture; #[inline] - fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { - FromRequestResFuture { - fut: T::from_request(req, payload), - _phantom: PhantomData, - } - } -} - -pin_project! { - pub struct FromRequestResFuture { - #[pin] - fut: Fut, - _phantom: PhantomData, - } -} - -impl Future for FromRequestResFuture -where - Fut: Future>, - Ei: Into, -{ - type Output = Result, Infallible>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - let res = ready!(this.fut.poll(cx)); - Poll::Ready(Ok(res.map_err(Into::into))) + async fn from_request(req: &HttpRequest, payload: &mut Payload) -> Result { + Ok(T::from_request(req, payload).await.map_err(Into::into)) } } @@ -279,10 +244,9 @@ where /// ``` impl FromRequest for Uri { type Error = Infallible; - type Future = Ready>; - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(req.uri().clone()) + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { + Ok(req.uri().clone()) } } @@ -300,10 +264,9 @@ impl FromRequest for Uri { /// ``` impl FromRequest for Method { type Error = Infallible; - type Future = Ready>; - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(req.method().clone()) + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { + Ok(req.method().clone()) } } @@ -319,88 +282,24 @@ mod tuple_from_req { impl<$($T: FromRequest + 'static),+> FromRequest for ($($T,)+) { type Error = Error; - type Future = $fut<$($T),+>; - fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { - $fut { - $( - $T: ExtractFuture::Future { - fut: $T::from_request(req, payload) - }, - )+ - } - } - } - - pin_project! { - pub struct $fut<$($T: FromRequest),+> { + async fn from_request(req: &HttpRequest, payload: &mut Payload) -> Result { $( - #[pin] - $T: ExtractFuture<$T::Future, $T>, - )+ - } - } - - impl<$($T: FromRequest),+> Future for $fut<$($T),+> - { - type Output = Result<($($T,)+), Error>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.project(); - - let mut ready = true; - $( - match this.$T.as_mut().project() { - ExtractProj::Future { fut } => match fut.poll(cx) { - Poll::Ready(Ok(output)) => { - let _ = this.$T.as_mut().project_replace(ExtractFuture::Done { output }); - }, - Poll::Ready(Err(err)) => return Poll::Ready(Err(err.into())), - Poll::Pending => ready = false, - }, - ExtractProj::Done { .. } => {}, - ExtractProj::Empty => unreachable!("FromRequest polled after finished"), - } + let $T = $T::from_request(req, payload).await.map_err(Into::into)?; )+ - if ready { - Poll::Ready(Ok( - ($( - match this.$T.project_replace(ExtractFuture::Empty) { - ExtractReplaceProj::Done { output } => output, - _ => unreachable!("FromRequest polled after finished"), - }, - )+) - )) - } else { - Poll::Pending - } + Ok(($($T,)+)) } } }; } - pin_project! { - #[project = ExtractProj] - #[project_replace = ExtractReplaceProj] - enum ExtractFuture { - Future { - #[pin] - fut: Fut - }, - Done { - output: Res, - }, - Empty - } - } - impl FromRequest for () { type Error = Infallible; - type Future = Ready>; - fn from_request(_: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(()) + #[inline] + async fn from_request(_: &HttpRequest, _: &mut Payload) -> Result { + Ok(()) } } diff --git a/actix-web/src/info.rs b/actix-web/src/info.rs index c5d9638f4..f6061cc72 100644 --- a/actix-web/src/info.rs +++ b/actix-web/src/info.rs @@ -1,6 +1,5 @@ use std::{convert::Infallible, net::SocketAddr}; -use actix_utils::future::{err, ok, Ready}; use derive_more::{Display, Error}; use crate::{ @@ -198,10 +197,10 @@ impl ConnectionInfo { impl FromRequest for ConnectionInfo { type Error = Infallible; - type Future = Ready>; - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(req.connection_info().clone()) + #[inline] + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { + Ok(req.connection_info().clone()) } } @@ -240,14 +239,14 @@ impl ResponseError for MissingPeerAddr {} impl FromRequest for PeerAddr { type Error = MissingPeerAddr; - type Future = Ready>; - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + #[inline] + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { match req.peer_addr() { - Some(addr) => ok(PeerAddr(addr)), + Some(addr) => Ok(PeerAddr(addr)), None => { log::error!("Missing peer address."); - err(MissingPeerAddr) + Err(MissingPeerAddr) } } } diff --git a/actix-web/src/request.rs b/actix-web/src/request.rs index ece36a388..990b5577a 100644 --- a/actix-web/src/request.rs +++ b/actix-web/src/request.rs @@ -1,5 +1,6 @@ use std::{ cell::{Ref, RefCell, RefMut}, + convert::Infallible, fmt, net, rc::Rc, str, @@ -7,7 +8,6 @@ use std::{ use actix_http::{Message, RequestHead}; use actix_router::{Path, Url}; -use actix_utils::future::{ok, Ready}; #[cfg(feature = "cookies")] use cookie::{Cookie, ParseError as CookieParseError}; use smallvec::SmallVec; @@ -20,7 +20,7 @@ use crate::{ http::{header::HeaderMap, Method, Uri, Version}, info::ConnectionInfo, rmap::ResourceMap, - Error, FromRequest, HttpMessage, + FromRequest, HttpMessage, }; #[cfg(feature = "cookies")] @@ -417,12 +417,11 @@ impl Drop for HttpRequest { /// ); /// ``` impl FromRequest for HttpRequest { - type Error = Error; - type Future = Ready>; + type Error = Infallible; #[inline] - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - ok(req.clone()) + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { + Ok(req.clone()) } } diff --git a/actix-web/src/request_data.rs b/actix-web/src/request_data.rs index bffbf74da..872a62d0c 100644 --- a/actix-web/src/request_data.rs +++ b/actix-web/src/request_data.rs @@ -1,6 +1,4 @@ -use std::{any::type_name, ops::Deref}; - -use actix_utils::future::{err, ok, Ready}; +use std::ops::Deref; use crate::{ dev::Payload, error::ErrorInternalServerError, Error, FromRequest, HttpMessage as _, @@ -66,19 +64,19 @@ impl Deref for ReqData { impl FromRequest for ReqData { type Error = Error; - type Future = Ready>; - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + #[inline] + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { if let Some(st) = req.extensions().get::() { - ok(ReqData(st.clone())) + Ok(ReqData(st.clone())) } else { log::debug!( - "Failed to construct App-level ReqData extractor. \ - Request path: {:?} (type: {})", + "Failed to extract App-level ReqData<{}>. Request path: {}", + std::any::type_name::(), req.path(), - type_name::(), ); - err(ErrorInternalServerError( + + Err(ErrorInternalServerError( "Missing expected request extension data", )) } diff --git a/actix-web/src/service.rs b/actix-web/src/service.rs index 0e17c9949..ee3448c4c 100644 --- a/actix-web/src/service.rs +++ b/actix-web/src/service.rs @@ -125,11 +125,11 @@ impl ServiceRequest { /// # todo!() /// } /// ``` - pub fn extract(&mut self) -> ::Future + pub async fn extract(&mut self) -> Result::Error> where T: FromRequest, { - T::from_request(&self.req, &mut self.payload) + T::from_request(&self.req, &mut self.payload).await } /// Construct request from parts. diff --git a/actix-web/src/types/either.rs b/actix-web/src/types/either.rs index db244fd9a..477334a37 100644 --- a/actix-web/src/types/either.rs +++ b/actix-web/src/types/either.rs @@ -1,15 +1,6 @@ //! For either helper, see [`Either`]. -use std::{ - future::Future, - mem, - pin::Pin, - task::{Context, Poll}, -}; - use bytes::Bytes; -use futures_core::ready; -use pin_project_lite::pin_project; use crate::{ body::EitherBody, @@ -158,7 +149,7 @@ where fn from(err: EitherExtractError) -> Error { match err { EitherExtractError::Bytes(err) => err, - EitherExtractError::Extract(a_err, _b_err) => a_err.into(), + EitherExtractError::Extract(l_err, _r_err) => l_err.into(), } } } @@ -170,112 +161,25 @@ where R: FromRequest + 'static, { type Error = EitherExtractError; - type Future = EitherExtractFut; - fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { - EitherExtractFut { - req: req.clone(), - state: EitherExtractState::Bytes { - bytes: Bytes::from_request(req, payload), + async fn from_request( + req: &HttpRequest, + payload: &mut dev::Payload, + ) -> Result { + let buf = Bytes::from_request(req, payload) + .await + .map_err(EitherExtractError::Bytes)?; + + match L::from_request(req, &mut payload_from_bytes(buf.clone())).await { + Ok(left) => Ok(Either::Left(left)), + Err(l_err) => match R::from_request(req, &mut payload_from_bytes(buf)).await { + Ok(right) => Ok(Either::Right(right)), + Err(r_err) => Err(EitherExtractError::Extract(l_err, r_err)), }, } } } -pin_project! { - pub struct EitherExtractFut - where - R: FromRequest, - L: FromRequest, - { - req: HttpRequest, - #[pin] - state: EitherExtractState, - } -} - -pin_project! { - #[project = EitherExtractProj] - pub enum EitherExtractState - where - L: FromRequest, - R: FromRequest, - { - Bytes { - #[pin] - bytes: ::Future, - }, - Left { - #[pin] - left: L::Future, - fallback: Bytes, - }, - Right { - #[pin] - right: R::Future, - left_err: Option, - }, - } -} - -impl Future for EitherExtractFut -where - L: FromRequest, - R: FromRequest, - LF: Future> + 'static, - RF: Future> + 'static, - LE: Into, - RE: Into, -{ - type Output = Result, EitherExtractError>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.project(); - let ready = loop { - let next = match this.state.as_mut().project() { - EitherExtractProj::Bytes { bytes } => { - let res = ready!(bytes.poll(cx)); - match res { - Ok(bytes) => { - let fallback = bytes.clone(); - let left = L::from_request(this.req, &mut payload_from_bytes(bytes)); - EitherExtractState::Left { left, fallback } - } - Err(err) => break Err(EitherExtractError::Bytes(err)), - } - } - EitherExtractProj::Left { left, fallback } => { - let res = ready!(left.poll(cx)); - match res { - Ok(extracted) => break Ok(Either::Left(extracted)), - Err(left_err) => { - let right = R::from_request( - this.req, - &mut payload_from_bytes(mem::take(fallback)), - ); - EitherExtractState::Right { - left_err: Some(left_err), - right, - } - } - } - } - EitherExtractProj::Right { right, left_err } => { - let res = ready!(right.poll(cx)); - match res { - Ok(data) => break Ok(Either::Right(data)), - Err(err) => { - break Err(EitherExtractError::Extract(left_err.take().unwrap(), err)); - } - } - } - }; - this.state.set(next); - }; - Poll::Ready(ready) - } -} - fn payload_from_bytes(bytes: Bytes) -> dev::Payload { let (_, mut h1_payload) = actix_http::h1::Payload::create(true); h1_payload.unread_data(bytes); diff --git a/actix-web/src/types/form.rs b/actix-web/src/types/form.rs index 7096b1e9c..c4b8702d1 100644 --- a/actix-web/src/types/form.rs +++ b/actix-web/src/types/form.rs @@ -13,7 +13,7 @@ use std::{ use actix_http::Payload; use bytes::BytesMut; use encoding_rs::{Encoding, UTF_8}; -use futures_core::{future::LocalBoxFuture, ready}; +use futures_core::future::LocalBoxFuture; use futures_util::{FutureExt as _, StreamExt as _}; use serde::{de::DeserializeOwned, Serialize}; @@ -126,51 +126,21 @@ where T: DeserializeOwned + 'static, { type Error = Error; - type Future = FormExtractFut; #[inline] - fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { - let FormConfig { limit, err_handler } = FormConfig::from_req(req).clone(); + async fn from_request(req: &HttpRequest, payload: &mut Payload) -> Result { + let FormConfig { limit, err_handler } = FormConfig::from_req(req); - FormExtractFut { - fut: UrlEncoded::new(req, payload).limit(limit), - req: req.clone(), - err_handler, + match UrlEncoded::new(req, payload).limit(*limit).await { + Ok(form) => Ok(Form(form)), + Err(err) => Err(match err_handler { + Some(err_handler) => (err_handler)(err, req), + None => err.into(), + }), } } } -type FormErrHandler = Option Error>>; - -pub struct FormExtractFut { - fut: UrlEncoded, - err_handler: FormErrHandler, - req: HttpRequest, -} - -impl Future for FormExtractFut -where - T: DeserializeOwned + 'static, -{ - type Output = Result, Error>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - let res = ready!(Pin::new(&mut this.fut).poll(cx)); - - let res = match res { - Err(err) => match &this.err_handler { - Some(err_handler) => Err((err_handler)(err, &this.req)), - None => Err(err.into()), - }, - Ok(item) => Ok(Form(item)), - }; - - Poll::Ready(res) - } -} - impl fmt::Display for Form { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) @@ -198,6 +168,8 @@ impl Responder for Form { } } +type FormErrHandler = Option Error>>; + /// [`Form`] extractor configuration. /// /// ``` diff --git a/actix-web/src/types/header.rs b/actix-web/src/types/header.rs index 977dc032e..309a280cb 100644 --- a/actix-web/src/types/header.rs +++ b/actix-web/src/types/header.rs @@ -2,8 +2,6 @@ use std::{fmt, ops}; -use actix_utils::future::{ready, Ready}; - use crate::{ dev::Payload, error::ParseError, extract::FromRequest, http::header::Header as ParseHeader, HttpRequest, @@ -61,13 +59,12 @@ where T: ParseHeader, { type Error = ParseError; - type Future = Ready>; #[inline] - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { match ParseHeader::parse(req) { - Ok(header) => ready(Ok(Header(header))), - Err(err) => ready(Err(err)), + Ok(header) => Ok(Header(header)), + Err(err) => Err(err), } } } diff --git a/actix-web/src/types/json.rs b/actix-web/src/types/json.rs index 6b75c0cfe..3a236c53d 100644 --- a/actix-web/src/types/json.rs +++ b/actix-web/src/types/json.rs @@ -138,10 +138,8 @@ impl Responder for Json { /// See [here](#extractor) for example of usage as an extractor. impl FromRequest for Json { type Error = Error; - type Future = JsonExtractFut; - #[inline] - fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { + async fn from_request(req: &HttpRequest, payload: &mut Payload) -> Result { let config = JsonConfig::from_req(req); let limit = config.limit; @@ -149,52 +147,30 @@ impl FromRequest for Json { let ctype_fn = config.content_type.as_deref(); let err_handler = config.err_handler.clone(); - JsonExtractFut { - req: Some(req.clone()), - fut: JsonBody::new(req, payload, ctype_fn, ctype_required).limit(limit), - err_handler, + match JsonBody::new(req, payload, ctype_fn, ctype_required) + .limit(limit) + .await + { + Ok(data) => Ok(Json(data)), + Err(err) => { + log::debug!( + "Failed to deserialize Json<{}> from payload. \ + Request path: {}", + std::any::type_name::(), + req.path(), + ); + + Err(match err_handler.as_ref() { + Some(err_handler) => (err_handler)(err, req), + None => err.into(), + }) + } } } } type JsonErrorHandler = Option Error + Send + Sync>>; -pub struct JsonExtractFut { - req: Option, - fut: JsonBody, - err_handler: JsonErrorHandler, -} - -impl Future for JsonExtractFut { - type Output = Result, Error>; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - let res = ready!(Pin::new(&mut this.fut).poll(cx)); - - let res = match res { - Err(err) => { - let req = this.req.take().unwrap(); - log::debug!( - "Failed to deserialize Json from payload. \ - Request path: {}", - req.path() - ); - - if let Some(err_handler) = this.err_handler.as_ref() { - Err((*err_handler)(err, &req)) - } else { - Err(err.into()) - } - } - Ok(data) => Ok(Json(data)), - }; - - Poll::Ready(res) - } -} - /// `Json` extractor configuration. /// /// # Examples diff --git a/actix-web/src/types/path.rs b/actix-web/src/types/path.rs index cc87bb80f..8bcc2efb2 100644 --- a/actix-web/src/types/path.rs +++ b/actix-web/src/types/path.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use actix_router::PathDeserializer; -use actix_utils::future::{ready, Ready}; use derive_more::{AsRef, Deref, DerefMut, Display, From}; use serde::de; @@ -69,33 +68,29 @@ where T: de::DeserializeOwned, { type Error = Error; - type Future = Ready>; - #[inline] - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { let error_handler = req .app_data::() .or_else(|| req.app_data::>().map(Data::get_ref)) .and_then(|c| c.err_handler.clone()); - ready( - de::Deserialize::deserialize(PathDeserializer::new(req.match_info())) - .map(Path) - .map_err(move |err| { - log::debug!( - "Failed during Path extractor deserialization. \ + de::Deserialize::deserialize(PathDeserializer::new(req.match_info())) + .map(Path) + .map_err(move |err| { + log::debug!( + "Failed during Path extractor deserialization. \ Request path: {:?}", - req.path() - ); + req.path() + ); - if let Some(error_handler) = error_handler { - let e = PathError::Deserialize(err); - (error_handler)(e, req) - } else { - ErrorNotFound(err) - } - }), - ) + if let Some(error_handler) = error_handler { + let e = PathError::Deserialize(err); + (error_handler)(e, req) + } else { + ErrorNotFound(err) + } + }) } } diff --git a/actix-web/src/types/payload.rs b/actix-web/src/types/payload.rs index abb4e6b7f..6a0ed8e00 100644 --- a/actix-web/src/types/payload.rs +++ b/actix-web/src/types/payload.rs @@ -9,9 +9,8 @@ use std::{ }; use actix_http::error::PayloadError; -use actix_utils::future::{ready, Either, Ready}; use bytes::{Bytes, BytesMut}; -use encoding_rs::{Encoding, UTF_8}; +use encoding_rs::Encoding; use futures_core::{ready, stream::Stream}; use mime::Mime; @@ -131,11 +130,13 @@ impl Stream for Payload { /// See [here](#Examples) for example of usage as an extractor. impl FromRequest for Payload { type Error = Error; - type Future = Ready>; #[inline] - fn from_request(_: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { - ready(Ok(Payload(payload.take()))) + async fn from_request( + _: &HttpRequest, + payload: &mut dev::Payload, + ) -> Result { + Ok(Payload(payload.take())) } } @@ -157,33 +158,21 @@ impl FromRequest for Payload { /// ``` impl FromRequest for Bytes { type Error = Error; - type Future = Either>>; - #[inline] - fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { + async fn from_request( + req: &HttpRequest, + payload: &mut dev::Payload, + ) -> Result { // allow both Config and Data let cfg = PayloadConfig::from_req(req); - if let Err(err) = cfg.check_mimetype(req) { - return Either::right(ready(Err(err))); - } + // check content-type + cfg.check_mimetype(req)?; - Either::left(BytesExtractFut { - body_fut: HttpMessageBody::new(req, payload).limit(cfg.limit), - }) - } -} - -/// Future for `Bytes` extractor. -pub struct BytesExtractFut { - body_fut: HttpMessageBody, -} - -impl Future for BytesExtractFut { - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::new(&mut self.body_fut).poll(cx).map_err(Into::into) + HttpMessageBody::new(req, payload) + .limit(cfg.limit) + .await + .map_err(Into::into) } } @@ -204,49 +193,29 @@ impl Future for BytesExtractFut { /// } impl FromRequest for String { type Error = Error; - type Future = Either>>; - #[inline] - fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { + async fn from_request( + req: &HttpRequest, + payload: &mut dev::Payload, + ) -> Result { let cfg = PayloadConfig::from_req(req); // check content-type - if let Err(err) = cfg.check_mimetype(req) { - return Either::right(ready(Err(err))); - } + cfg.check_mimetype(req)?; // check charset - let encoding = match req.encoding() { - Ok(enc) => enc, - Err(err) => return Either::right(ready(Err(err.into()))), - }; + let encoding = req.encoding()?; let limit = cfg.limit; - let body_fut = HttpMessageBody::new(req, payload).limit(limit); - Either::left(StringExtractFut { body_fut, encoding }) - } -} + let body = HttpMessageBody::new(req, payload).limit(limit).await?; -/// Future for `String` extractor. -pub struct StringExtractFut { - body_fut: HttpMessageBody, - encoding: &'static Encoding, -} - -impl Future for StringExtractFut { - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let encoding = self.encoding; - - Pin::new(&mut self.body_fut).poll(cx).map(|out| { - let body = out?; - bytes_to_string(body, encoding) - }) + bytes_to_string(body, encoding) } } fn bytes_to_string(body: Bytes, encoding: &'static Encoding) -> Result { + use encoding_rs::UTF_8; + if encoding == UTF_8 { Ok(str::from_utf8(body.as_ref()) .map_err(|_| ErrorBadRequest("Can not decode body"))? diff --git a/actix-web/src/types/query.rs b/actix-web/src/types/query.rs index e71b886f2..b194d756a 100644 --- a/actix-web/src/types/query.rs +++ b/actix-web/src/types/query.rs @@ -2,7 +2,6 @@ use std::{fmt, ops, sync::Arc}; -use actix_utils::future::{err, ok, Ready}; use serde::de::DeserializeOwned; use crate::{dev::Payload, error::QueryPayloadError, Error, FromRequest, HttpRequest}; @@ -108,32 +107,27 @@ impl fmt::Display for Query { /// See [here](#Examples) for example of usage as an extractor. impl FromRequest for Query { type Error = Error; - type Future = Ready>; - #[inline] - fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result { let error_handler = req .app_data::() .and_then(|c| c.err_handler.clone()); serde_urlencoded::from_str::(req.query_string()) - .map(|val| ok(Query(val))) + .map(|val| Ok(Query(val))) .unwrap_or_else(move |e| { - let e = QueryPayloadError::Deserialize(e); + let err = QueryPayloadError::Deserialize(e); log::debug!( "Failed during Query extractor deserialization. \ - Request path: {:?}", + Request path: {}", req.path() ); - let e = if let Some(error_handler) = error_handler { - (error_handler)(e, req) - } else { - e.into() - }; - - err(e) + Err(match error_handler { + Some(error_handler) => (error_handler)(err, req), + None => err.into(), + }) }) } } diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000..bf867e0ae --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly