switch encoder/compress to use AnyBody

This commit is contained in:
Rob Ede 2021-11-16 16:56:41 +00:00
parent 5167bf16a2
commit 0b13d27b03
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
5 changed files with 47 additions and 45 deletions

View File

@ -1,8 +1,11 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Changed
* Compress middleware's response type is now `AnyBody<Encoder<B>>`. [#2448]
### Fixed ### Fixed
* Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#????] * Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#2448]
[#2423]: https://github.com/actix/actix-web/pull/2423 [#2423]: https://github.com/actix/actix-web/pull/2423

View File

@ -3,19 +3,21 @@
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Added ### Added
* `AnyBody::empty` for quickly creating an empty body. [#2446] * `AnyBody::empty` for quickly creating an empty body. [#2446]
* `impl Clone` for `AnyBody<S> where S: Clone`. [#????] * `impl Clone` for `AnyBody<S> where S: Clone`. [#2448]
* `AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#????] * `AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448]
### Changed ### Changed
* Rename `AnyBody::{Message => Body}`. [#2446] * Rename `AnyBody::{Message => Body}`. [#2446]
* Rename `AnyBody::{from_message => new_boxed}`. [#????] * Rename `AnyBody::{from_message => new_boxed}`. [#2448]
* Rename `AnyBody::{from_slice => copy_from_slice}`. [#????] * Rename `AnyBody::{from_slice => copy_from_slice}`. [#2448]
* Rename `BoxAnyBody` to `BoxBody` [#????] * Rename `BoxAnyBody` to `BoxBody` [#2448]
* Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#????] * Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448]
* `Encoder::response` now returns `AnyBody<Encoder<B>>`. [#2448]
### Removed ### Removed
* `AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446] * `AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446]
* `BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446] * `BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446]
* `EncoderError::Boxed`; it is no longer required. [#2446]
[#2446]: https://github.com/actix/actix-web/pull/2446 [#2446]: https://github.com/actix/actix-web/pull/2446

View File

@ -24,7 +24,7 @@ use flate2::write::{GzEncoder, ZlibEncoder};
use zstd::stream::write::Encoder as ZstdEncoder; use zstd::stream::write::Encoder as ZstdEncoder;
use crate::{ use crate::{
body::{Body, BodySize, BoxBody, MessageBody, ResponseBody}, body::{AnyBody, BodySize, MessageBody},
http::{ http::{
header::{ContentEncoding, CONTENT_ENCODING}, header::{ContentEncoding, CONTENT_ENCODING},
HeaderValue, StatusCode, HeaderValue, StatusCode,
@ -50,8 +50,8 @@ impl<B: MessageBody> Encoder<B> {
pub fn response( pub fn response(
encoding: ContentEncoding, encoding: ContentEncoding,
head: &mut ResponseHead, head: &mut ResponseHead,
body: ResponseBody<B>, body: AnyBody<B>,
) -> ResponseBody<Encoder<B>> { ) -> AnyBody<Encoder<B>> {
let can_encode = !(head.headers().contains_key(&CONTENT_ENCODING) let can_encode = !(head.headers().contains_key(&CONTENT_ENCODING)
|| head.status == StatusCode::SWITCHING_PROTOCOLS || head.status == StatusCode::SWITCHING_PROTOCOLS
|| head.status == StatusCode::NO_CONTENT || head.status == StatusCode::NO_CONTENT
@ -59,18 +59,15 @@ impl<B: MessageBody> Encoder<B> {
|| encoding == ContentEncoding::Auto); || encoding == ContentEncoding::Auto);
let body = match body { let body = match body {
ResponseBody::Other(b) => match b { AnyBody::None => return AnyBody::None,
Body::None => return ResponseBody::Other(Body::None), AnyBody::Bytes(buf) => {
Body::Bytes(buf) => { if can_encode {
if can_encode { EncoderBody::Bytes(buf)
EncoderBody::Bytes(buf) } else {
} else { return AnyBody::Bytes(buf);
return ResponseBody::Other(Body::Bytes(buf));
}
} }
Body::Body(stream) => EncoderBody::BoxedStream(stream), }
}, AnyBody::Body(body) => EncoderBody::Stream(body),
ResponseBody::Body(stream) => EncoderBody::Stream(stream),
}; };
if can_encode { if can_encode {
@ -78,7 +75,8 @@ impl<B: MessageBody> Encoder<B> {
if let Some(enc) = ContentEncoder::encoder(encoding) { if let Some(enc) = ContentEncoder::encoder(encoding) {
update_head(encoding, head); update_head(encoding, head);
head.no_chunking(false); head.no_chunking(false);
return ResponseBody::Body(Encoder {
return AnyBody::Body(Encoder {
body, body,
eof: false, eof: false,
fut: None, fut: None,
@ -87,7 +85,7 @@ impl<B: MessageBody> Encoder<B> {
} }
} }
ResponseBody::Body(Encoder { AnyBody::Body(Encoder {
body, body,
eof: false, eof: false,
fut: None, fut: None,
@ -100,7 +98,6 @@ impl<B: MessageBody> Encoder<B> {
enum EncoderBody<B> { enum EncoderBody<B> {
Bytes(Bytes), Bytes(Bytes),
Stream(#[pin] B), Stream(#[pin] B),
BoxedStream(BoxBody),
} }
impl<B> MessageBody for EncoderBody<B> impl<B> MessageBody for EncoderBody<B>
@ -113,7 +110,6 @@ where
match self { match self {
EncoderBody::Bytes(ref b) => b.size(), EncoderBody::Bytes(ref b) => b.size(),
EncoderBody::Stream(ref b) => b.size(), EncoderBody::Stream(ref b) => b.size(),
EncoderBody::BoxedStream(ref b) => b.size(),
} }
} }
@ -130,9 +126,6 @@ where
} }
} }
EncoderBodyProj::Stream(b) => b.poll_next(cx).map_err(EncoderError::Body), EncoderBodyProj::Stream(b) => b.poll_next(cx).map_err(EncoderError::Body),
EncoderBodyProj::BoxedStream(ref mut b) => {
b.as_pin_mut().poll_next(cx).map_err(EncoderError::Boxed)
}
} }
} }
} }
@ -348,9 +341,6 @@ pub enum EncoderError<E> {
#[display(fmt = "body")] #[display(fmt = "body")]
Body(E), Body(E),
#[display(fmt = "boxed")]
Boxed(Box<dyn StdError>),
#[display(fmt = "blocking")] #[display(fmt = "blocking")]
Blocking(BlockingError), Blocking(BlockingError),
@ -362,7 +352,6 @@ impl<E: StdError + 'static> StdError for EncoderError<E> {
fn source(&self) -> Option<&(dyn StdError + 'static)> { fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self { match self {
EncoderError::Body(err) => Some(err), EncoderError::Body(err) => Some(err),
EncoderError::Boxed(err) => Some(&**err),
EncoderError::Blocking(err) => Some(err), EncoderError::Blocking(err) => Some(err),
EncoderError::Io(err) => Some(err), EncoderError::Io(err) => Some(err),
} }

View File

@ -10,13 +10,14 @@ use std::{
}; };
use actix_http::{ use actix_http::{
body::{MessageBody, ResponseBody}, body::{AnyBody, MessageBody},
encoding::Encoder, encoding::Encoder,
http::header::{ContentEncoding, ACCEPT_ENCODING}, http::header::{ContentEncoding, ACCEPT_ENCODING},
StatusCode, StatusCode,
}; };
use actix_service::{Service, Transform}; use actix_service::{Service, Transform};
use actix_utils::future::{ok, Either, Ready}; use actix_utils::future::{ok, Either, Ready};
use bytes::Bytes;
use futures_core::ready; use futures_core::ready;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use pin_project::pin_project; use pin_project::pin_project;
@ -61,7 +62,7 @@ where
B: MessageBody, B: MessageBody,
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>, S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{ {
type Response = ServiceResponse<ResponseBody<Encoder<B>>>; type Response = ServiceResponse<AnyBody<Encoder<B>>>;
type Error = Error; type Error = Error;
type Transform = CompressMiddleware<S>; type Transform = CompressMiddleware<S>;
type InitError = (); type InitError = ();
@ -110,7 +111,7 @@ where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>, S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
B: MessageBody, B: MessageBody,
{ {
type Response = ServiceResponse<ResponseBody<Encoder<B>>>; type Response = ServiceResponse<AnyBody<Encoder<B>>>;
type Error = Error; type Error = Error;
type Future = Either<CompressResponse<S, B>, Ready<Result<Self::Response, Self::Error>>>; type Future = Either<CompressResponse<S, B>, Ready<Result<Self::Response, Self::Error>>>;
@ -142,15 +143,19 @@ where
// There is an HTTP header but we cannot match what client as asked for // There is an HTTP header but we cannot match what client as asked for
Some(Err(_)) => { Some(Err(_)) => {
let res = HttpResponse::with_body( let res = HttpResponse::new(StatusCode::NOT_ACCEPTABLE);
StatusCode::NOT_ACCEPTABLE,
SUPPORTED_ALGORITHM_NAMES.as_str(),
);
let enc = ContentEncoding::Identity;
Either::right(ok(req.into_response(res.map_body(move |head, body| { let res: HttpResponse<AnyBody<Encoder<B>>> = res.map_body(move |head, _| {
Encoder::response(enc, head, ResponseBody::Other(body.into())) let body_bytes = Bytes::from(SUPPORTED_ALGORITHM_NAMES.as_bytes());
}))))
Encoder::response(
ContentEncoding::Identity,
head,
AnyBody::Bytes(body_bytes),
)
});
Either::right(ok(req.into_response(res)))
} }
} }
} }
@ -172,7 +177,7 @@ where
B: MessageBody, B: MessageBody,
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>, S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
{ {
type Output = Result<ServiceResponse<ResponseBody<Encoder<B>>>, Error>; type Output = Result<ServiceResponse<AnyBody<Encoder<B>>>, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
@ -186,7 +191,7 @@ where
}; };
Poll::Ready(Ok(resp.map_body(move |head, body| { Poll::Ready(Ok(resp.map_body(move |head, body| {
Encoder::response(enc, head, ResponseBody::Body(body)) Encoder::response(enc, head, AnyBody::Body(body))
}))) })))
} }
Err(e) => Poll::Ready(Err(e)), Err(e) => Poll::Ready(Err(e)),

View File

@ -227,6 +227,9 @@ impl<B> HttpResponse<B> {
} }
} }
// TODO: into_body equivalent
// TODO: into_boxed_body
/// Extract response body /// Extract response body
pub fn into_body(self) -> B { pub fn into_body(self) -> B {
self.res.into_body() self.res.into_body()