From 1fcf92e11f1b18053ecc1df13be2d79e0efc1ca9 Mon Sep 17 00:00:00 2001 From: Voldracarno Draconor Date: Wed, 28 Apr 2021 02:23:12 +0200 Subject: [PATCH 1/4] Update dependency "language-tags" (#2188) --- CHANGES.md | 2 ++ Cargo.toml | 2 +- actix-http/CHANGES.md | 1 + actix-http/Cargo.toml | 2 +- src/http/header/accept_language.rs | 14 +++++--------- src/http/header/content_language.rs | 12 +++++------- 6 files changed, 15 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bea9ab935..6fe0174ad 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,8 @@ # Changes ## Unreleased - 2021-xx-xx +### Changed +* Update `language-tags` to `0.3`. ## 4.0.0-beta.6 - 2021-04-17 diff --git a/Cargo.toml b/Cargo.toml index 714da13a0..75b5e3a8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ encoding_rs = "0.8" futures-core = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false } itoa = "0.4" -language-tags = "0.2" +language-tags = "0.3" once_cell = "1.5" log = "0.4" mime = "0.3" diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index e89208748..f1d24c3c3 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -9,6 +9,7 @@ ### Changed * `header` mod is now public. [#2171] * `uri` mod is now public. [#2171] +* Update `language-tags` to `0.3`. ### Removed * Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 55dc6d05e..638557807 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -57,7 +57,7 @@ h2 = "0.3.1" http = "0.2.2" httparse = "1.3" itoa = "0.4" -language-tags = "0.2" +language-tags = "0.3" local-channel = "0.1" once_cell = "1.5" log = "0.4" diff --git a/src/http/header/accept_language.rs b/src/http/header/accept_language.rs index 5fab4f79c..034946d4d 100644 --- a/src/http/header/accept_language.rs +++ b/src/http/header/accept_language.rs @@ -24,14 +24,11 @@ crate::__define_common_header! { /// # Examples /// /// ``` - /// use language_tags::langtag; /// use actix_web::HttpResponse; /// use actix_web::http::header::{AcceptLanguage, LanguageTag, qitem}; /// /// let mut builder = HttpResponse::Ok(); - /// let mut langtag: LanguageTag = Default::default(); - /// langtag.language = Some("en".to_owned()); - /// langtag.region = Some("US".to_owned()); + /// let langtag = LanguageTag::parse("en-US").unwrap(); /// builder.insert_header( /// AcceptLanguage(vec![ /// qitem(langtag), @@ -40,16 +37,15 @@ crate::__define_common_header! { /// ``` /// /// ``` - /// use language_tags::langtag; /// use actix_web::HttpResponse; - /// use actix_web::http::header::{AcceptLanguage, QualityItem, q, qitem}; + /// use actix_web::http::header::{AcceptLanguage, LanguageTag, QualityItem, q, qitem}; /// /// let mut builder = HttpResponse::Ok(); /// builder.insert_header( /// AcceptLanguage(vec![ - /// qitem(langtag!(da)), - /// QualityItem::new(langtag!(en;;;GB), q(800)), - /// QualityItem::new(langtag!(en), q(700)), + /// qitem(LanguageTag::parse("da").unwrap()), + /// QualityItem::new(LanguageTag::parse("en-GB").unwrap(), q(800)), + /// QualityItem::new(LanguageTag::parse("en").unwrap(), q(700)), /// ]) /// ); /// ``` diff --git a/src/http/header/content_language.rs b/src/http/header/content_language.rs index 41e6d9eff..c2469edd1 100644 --- a/src/http/header/content_language.rs +++ b/src/http/header/content_language.rs @@ -24,28 +24,26 @@ crate::__define_common_header! { /// # Examples /// /// ``` - /// use language_tags::langtag; /// use actix_web::HttpResponse; - /// use actix_web::http::header::{ContentLanguage, qitem}; + /// use actix_web::http::header::{ContentLanguage, LanguageTag, qitem}; /// /// let mut builder = HttpResponse::Ok(); /// builder.insert_header( /// ContentLanguage(vec![ - /// qitem(langtag!(en)), + /// qitem(LanguageTag::parse("en").unwrap()), /// ]) /// ); /// ``` /// /// ``` - /// use language_tags::langtag; /// use actix_web::HttpResponse; - /// use actix_web::http::header::{ContentLanguage, qitem}; + /// use actix_web::http::header::{ContentLanguage, LanguageTag, qitem}; /// /// let mut builder = HttpResponse::Ok(); /// builder.insert_header( /// ContentLanguage(vec![ - /// qitem(langtag!(da)), - /// qitem(langtag!(en;;;GB)), + /// qitem(LanguageTag::parse("da").unwrap()), + /// qitem(LanguageTag::parse("en-GB").unwrap()), /// ]) /// ); /// ``` From 3a0fb3f89e82fa3287d9741533e4d8dfd38c1548 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Fri, 30 Apr 2021 22:02:56 -0400 Subject: [PATCH 2/4] Static either extract future (#2184) --- src/types/either.rs | 130 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 27 deletions(-) diff --git a/src/types/either.rs b/src/types/either.rs index 210495e47..d3b003587 100644 --- a/src/types/either.rs +++ b/src/types/either.rs @@ -1,8 +1,14 @@ //! For either helper, see [`Either`]. +use std::{ + future::Future, + mem, + pin::Pin, + task::{Context, Poll}, +}; + use bytes::Bytes; -use futures_core::future::LocalBoxFuture; -use futures_util::{FutureExt as _, TryFutureExt as _}; +use futures_core::ready; use crate::{ dev, @@ -180,41 +186,111 @@ where R: FromRequest + 'static, { type Error = EitherExtractError; - type Future = LocalBoxFuture<'static, Result>; + type Future = EitherExtractFut; type Config = (); fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { - let req2 = req.clone(); - - Bytes::from_request(req, payload) - .map_err(EitherExtractError::Bytes) - .and_then(|bytes| bytes_to_l_or_r(req2, bytes)) - .boxed_local() + EitherExtractFut { + req: req.clone(), + state: EitherExtractState::Bytes { + bytes: Bytes::from_request(req, payload), + }, + } } } -async fn bytes_to_l_or_r( - req: HttpRequest, - bytes: Bytes, -) -> Result, EitherExtractError> +#[pin_project::pin_project] +pub struct EitherExtractFut where - L: FromRequest + 'static, - R: FromRequest + 'static, + R: FromRequest, + L: FromRequest, { - let fallback = bytes.clone(); - let a_err; + req: HttpRequest, + #[pin] + state: EitherExtractState, +} - let mut pl = payload_from_bytes(bytes); - match L::from_request(&req, &mut pl).await { - Ok(a_data) => return Ok(Either::Left(a_data)), - // store A's error for returning if B also fails - Err(err) => a_err = err, - }; +#[pin_project::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, + }, +} - let mut pl = payload_from_bytes(fallback); - match R::from_request(&req, &mut pl).await { - Ok(b_data) => return Ok(Either::Right(b_data)), - Err(b_err) => Err(EitherExtractError::Extract(a_err, b_err)), +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) } } From c17662fe39523ce4230234004c572618932be2c7 Mon Sep 17 00:00:00 2001 From: Luca Palmieri Date: Mon, 3 May 2021 00:58:14 +0100 Subject: [PATCH 3/4] Reduce the level of the emitted log line from `error` to `debug`. (#2196) Co-authored-by: Rob Ede --- actix-http/CHANGES.md | 2 ++ actix-http/src/response.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index f1d24c3c3..cc6330288 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -10,11 +10,13 @@ * `header` mod is now public. [#2171] * `uri` mod is now public. [#2171] * Update `language-tags` to `0.3`. +* Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2196] ### Removed * Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] [#2171]: https://github.com/actix/actix-web/pull/2171 +[#2196]: https://github.com/actix/actix-web/pull/2196 ## 3.0.0-beta.6 - 2021-04-17 diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index a3ab1175c..e11ceb18f 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -78,7 +78,7 @@ impl Response { pub fn from_error(error: Error) -> Response { let mut resp = error.as_response_error().error_response(); if resp.head.status == StatusCode::INTERNAL_SERVER_ERROR { - error!("Internal Server Error: {:?}", error); + debug!("Internal Server Error: {:?}", error); } resp.error = Some(error); resp From dd1a3e7675df26748d070e94912ebe1587cb9241 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Wed, 5 May 2021 06:16:12 -0400 Subject: [PATCH 4/4] Fix loophole in soundness of `__private_get_type_id__` (#2199) --- actix-http/src/macros.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/actix-http/src/macros.rs b/actix-http/src/macros.rs index 7cf0e288b..be8e63d6e 100644 --- a/actix-http/src/macros.rs +++ b/actix-http/src/macros.rs @@ -15,8 +15,15 @@ macro_rules! downcast_get_type_id { /// making it impossible for safe code to construct outside of /// this module. This ensures that safe code cannot violate /// type-safety by implementing this method. + /// + /// We also take `PrivateHelper` as a parameter, to ensure that + /// safe code cannot obtain a `PrivateHelper` instance by + /// delegating to an existing implementation of `__private_get_type_id__` #[doc(hidden)] - fn __private_get_type_id__(&self) -> (std::any::TypeId, PrivateHelper) + fn __private_get_type_id__( + &self, + _: PrivateHelper, + ) -> (std::any::TypeId, PrivateHelper) where Self: 'static, { @@ -39,7 +46,9 @@ macro_rules! downcast { impl dyn $name + 'static { /// Downcasts generic body to a specific type. pub fn downcast_ref(&self) -> Option<&T> { - if self.__private_get_type_id__().0 == std::any::TypeId::of::() { + if self.__private_get_type_id__(PrivateHelper(())).0 + == std::any::TypeId::of::() + { // SAFETY: external crates cannot override the default // implementation of `__private_get_type_id__`, since // it requires returning a private type. We can therefore @@ -53,7 +62,9 @@ macro_rules! downcast { /// Downcasts a generic body to a mutable specific type. pub fn downcast_mut(&mut self) -> Option<&mut T> { - if self.__private_get_type_id__().0 == std::any::TypeId::of::() { + if self.__private_get_type_id__(PrivateHelper(())).0 + == std::any::TypeId::of::() + { // SAFETY: external crates cannot override the default // implementation of `__private_get_type_id__`, since // it requires returning a private type. We can therefore