Merge branch 'master' into msgbody-err

This commit is contained in:
Rob Ede 2021-05-05 15:07:25 +01:00 committed by GitHub
commit 1f521166f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 135 additions and 49 deletions

View File

@ -1,6 +1,8 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Changed
* Update `language-tags` to `0.3`.
## 4.0.0-beta.6 - 2021-04-17 ## 4.0.0-beta.6 - 2021-04-17

View File

@ -78,7 +78,7 @@ encoding_rs = "0.8"
futures-core = { version = "0.3.7", default-features = false } futures-core = { version = "0.3.7", default-features = false }
futures-util = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false }
itoa = "0.4" itoa = "0.4"
language-tags = "0.2" language-tags = "0.3"
once_cell = "1.5" once_cell = "1.5"
log = "0.4" log = "0.4"
mime = "0.3" mime = "0.3"

View File

@ -11,6 +11,8 @@
* The `MessageBody` trait now has an associated `Error` type. [#2183] * The `MessageBody` trait now has an associated `Error` type. [#2183]
* `header` mod is now public. [#2171] * `header` mod is now public. [#2171]
* `uri` 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 ### Removed
* Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] * Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171]
@ -18,6 +20,7 @@
[#2171]: https://github.com/actix/actix-web/pull/2171 [#2171]: https://github.com/actix/actix-web/pull/2171
[#2183]: https://github.com/actix/actix-web/pull/2183 [#2183]: https://github.com/actix/actix-web/pull/2183
[#2196]: https://github.com/actix/actix-web/pull/2196
## 3.0.0-beta.6 - 2021-04-17 ## 3.0.0-beta.6 - 2021-04-17

View File

@ -57,7 +57,7 @@ h2 = "0.3.1"
http = "0.2.2" http = "0.2.2"
httparse = "1.3" httparse = "1.3"
itoa = "0.4" itoa = "0.4"
language-tags = "0.2" language-tags = "0.3"
local-channel = "0.1" local-channel = "0.1"
once_cell = "1.5" once_cell = "1.5"
log = "0.4" log = "0.4"

View File

@ -15,8 +15,15 @@ macro_rules! downcast_get_type_id {
/// making it impossible for safe code to construct outside of /// making it impossible for safe code to construct outside of
/// this module. This ensures that safe code cannot violate /// this module. This ensures that safe code cannot violate
/// type-safety by implementing this method. /// 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)] #[doc(hidden)]
fn __private_get_type_id__(&self) -> (std::any::TypeId, PrivateHelper) fn __private_get_type_id__(
&self,
_: PrivateHelper,
) -> (std::any::TypeId, PrivateHelper)
where where
Self: 'static, Self: 'static,
{ {
@ -39,7 +46,9 @@ macro_rules! downcast {
impl dyn $name + 'static { impl dyn $name + 'static {
/// Downcasts generic body to a specific type. /// Downcasts generic body to a specific type.
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> { pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() { if self.__private_get_type_id__(PrivateHelper(())).0
== std::any::TypeId::of::<T>()
{
// SAFETY: external crates cannot override the default // SAFETY: external crates cannot override the default
// implementation of `__private_get_type_id__`, since // implementation of `__private_get_type_id__`, since
// it requires returning a private type. We can therefore // 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. /// Downcasts a generic body to a mutable specific type.
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> { pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() { if self.__private_get_type_id__(PrivateHelper(())).0
== std::any::TypeId::of::<T>()
{
// SAFETY: external crates cannot override the default // SAFETY: external crates cannot override the default
// implementation of `__private_get_type_id__`, since // implementation of `__private_get_type_id__`, since
// it requires returning a private type. We can therefore // it requires returning a private type. We can therefore

View File

@ -78,7 +78,7 @@ impl Response<Body> {
pub fn from_error(error: Error) -> Response<Body> { pub fn from_error(error: Error) -> Response<Body> {
let mut resp = error.as_response_error().error_response(); let mut resp = error.as_response_error().error_response();
if resp.head.status == StatusCode::INTERNAL_SERVER_ERROR { if resp.head.status == StatusCode::INTERNAL_SERVER_ERROR {
error!("Internal Server Error: {:?}", error); debug!("Internal Server Error: {:?}", error);
} }
resp.error = Some(error); resp.error = Some(error);
resp resp

View File

@ -24,14 +24,11 @@ crate::__define_common_header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use language_tags::langtag;
/// use actix_web::HttpResponse; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{AcceptLanguage, LanguageTag, qitem}; /// use actix_web::http::header::{AcceptLanguage, LanguageTag, qitem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// let mut langtag: LanguageTag = Default::default(); /// let langtag = LanguageTag::parse("en-US").unwrap();
/// langtag.language = Some("en".to_owned());
/// langtag.region = Some("US".to_owned());
/// builder.insert_header( /// builder.insert_header(
/// AcceptLanguage(vec![ /// AcceptLanguage(vec![
/// qitem(langtag), /// qitem(langtag),
@ -40,16 +37,15 @@ crate::__define_common_header! {
/// ``` /// ```
/// ///
/// ``` /// ```
/// use language_tags::langtag;
/// use actix_web::HttpResponse; /// 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(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// AcceptLanguage(vec![ /// AcceptLanguage(vec![
/// qitem(langtag!(da)), /// qitem(LanguageTag::parse("da").unwrap()),
/// QualityItem::new(langtag!(en;;;GB), q(800)), /// QualityItem::new(LanguageTag::parse("en-GB").unwrap(), q(800)),
/// QualityItem::new(langtag!(en), q(700)), /// QualityItem::new(LanguageTag::parse("en").unwrap(), q(700)),
/// ]) /// ])
/// ); /// );
/// ``` /// ```

View File

@ -24,28 +24,26 @@ crate::__define_common_header! {
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// use language_tags::langtag;
/// use actix_web::HttpResponse; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{ContentLanguage, qitem}; /// use actix_web::http::header::{ContentLanguage, LanguageTag, qitem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentLanguage(vec![ /// ContentLanguage(vec![
/// qitem(langtag!(en)), /// qitem(LanguageTag::parse("en").unwrap()),
/// ]) /// ])
/// ); /// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use language_tags::langtag;
/// use actix_web::HttpResponse; /// use actix_web::HttpResponse;
/// use actix_web::http::header::{ContentLanguage, qitem}; /// use actix_web::http::header::{ContentLanguage, LanguageTag, qitem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// builder.insert_header( /// builder.insert_header(
/// ContentLanguage(vec![ /// ContentLanguage(vec![
/// qitem(langtag!(da)), /// qitem(LanguageTag::parse("da").unwrap()),
/// qitem(langtag!(en;;;GB)), /// qitem(LanguageTag::parse("en-GB").unwrap()),
/// ]) /// ])
/// ); /// );
/// ``` /// ```

View File

@ -1,8 +1,14 @@
//! For either helper, see [`Either`]. //! For either helper, see [`Either`].
use std::{
future::Future,
mem,
pin::Pin,
task::{Context, Poll},
};
use bytes::Bytes; use bytes::Bytes;
use futures_core::future::LocalBoxFuture; use futures_core::ready;
use futures_util::{FutureExt as _, TryFutureExt as _};
use crate::{ use crate::{
dev, dev,
@ -180,41 +186,111 @@ where
R: FromRequest + 'static, R: FromRequest + 'static,
{ {
type Error = EitherExtractError<L::Error, R::Error>; type Error = EitherExtractError<L::Error, R::Error>;
type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>; type Future = EitherExtractFut<L, R>;
type Config = (); type Config = ();
fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
let req2 = req.clone(); EitherExtractFut {
req: req.clone(),
Bytes::from_request(req, payload) state: EitherExtractState::Bytes {
.map_err(EitherExtractError::Bytes) bytes: Bytes::from_request(req, payload),
.and_then(|bytes| bytes_to_l_or_r(req2, bytes)) },
.boxed_local() }
} }
} }
async fn bytes_to_l_or_r<L, R>( #[pin_project::pin_project]
req: HttpRequest, pub struct EitherExtractFut<L, R>
bytes: Bytes,
) -> Result<Either<L, R>, EitherExtractError<L::Error, R::Error>>
where where
L: FromRequest + 'static, R: FromRequest,
R: FromRequest + 'static, L: FromRequest,
{ {
req: HttpRequest,
#[pin]
state: EitherExtractState<L, R>,
}
#[pin_project::pin_project(project = EitherExtractProj)]
pub enum EitherExtractState<L, R>
where
L: FromRequest,
R: FromRequest,
{
Bytes {
#[pin]
bytes: <Bytes as FromRequest>::Future,
},
Left {
#[pin]
left: L::Future,
fallback: Bytes,
},
Right {
#[pin]
right: R::Future,
left_err: Option<L::Error>,
},
}
impl<R, RF, RE, L, LF, LE> Future for EitherExtractFut<L, R>
where
L: FromRequest<Future = LF, Error = LE>,
R: FromRequest<Future = RF, Error = RE>,
LF: Future<Output = Result<L, LE>> + 'static,
RF: Future<Output = Result<R, RE>> + 'static,
LE: Into<Error>,
RE: Into<Error>,
{
type Output = Result<Either<L, R>, EitherExtractError<LE, RE>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
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 fallback = bytes.clone();
let a_err; let left =
L::from_request(&this.req, &mut payload_from_bytes(bytes));
let mut pl = payload_from_bytes(bytes); EitherExtractState::Left { left, fallback }
match L::from_request(&req, &mut pl).await { }
Ok(a_data) => return Ok(Either::Left(a_data)), Err(err) => break Err(EitherExtractError::Bytes(err)),
// store A's error for returning if B also fails }
Err(err) => a_err = 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);
let mut pl = payload_from_bytes(fallback); };
match R::from_request(&req, &mut pl).await { Poll::Ready(ready)
Ok(b_data) => return Ok(Either::Right(b_data)),
Err(b_err) => Err(EitherExtractError::Extract(a_err, b_err)),
} }
} }