mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into msgbody-err
This commit is contained in:
commit
1f521166f6
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)),
|
||||||
/// ])
|
/// ])
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -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()),
|
||||||
/// ])
|
/// ])
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -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,
|
||||||
{
|
{
|
||||||
let fallback = bytes.clone();
|
req: HttpRequest,
|
||||||
let a_err;
|
#[pin]
|
||||||
|
state: EitherExtractState<L, R>,
|
||||||
|
}
|
||||||
|
|
||||||
let mut pl = payload_from_bytes(bytes);
|
#[pin_project::pin_project(project = EitherExtractProj)]
|
||||||
match L::from_request(&req, &mut pl).await {
|
pub enum EitherExtractState<L, R>
|
||||||
Ok(a_data) => return Ok(Either::Left(a_data)),
|
where
|
||||||
// store A's error for returning if B also fails
|
L: FromRequest,
|
||||||
Err(err) => a_err = err,
|
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>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
let mut pl = payload_from_bytes(fallback);
|
impl<R, RF, RE, L, LF, LE> Future for EitherExtractFut<L, R>
|
||||||
match R::from_request(&req, &mut pl).await {
|
where
|
||||||
Ok(b_data) => return Ok(Either::Right(b_data)),
|
L: FromRequest<Future = LF, Error = LE>,
|
||||||
Err(b_err) => Err(EitherExtractError::Extract(a_err, b_err)),
|
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 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue