mirror of
https://github.com/fafhrd91/actix-web
synced 2025-01-19 14:14:41 +01:00
feat(compress): use response content type to decide compress
This commit is contained in:
parent
2bfc170fb0
commit
14b09e35d1
@ -1,6 +1,7 @@
|
|||||||
//! For middleware documentation, see [`Compress`].
|
//! For middleware documentation, see [`Compress`].
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt,
|
||||||
future::Future,
|
future::Future,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
@ -11,14 +12,14 @@ use actix_http::encoding::Encoder;
|
|||||||
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 futures_core::ready;
|
use futures_core::ready;
|
||||||
|
use mime::Mime;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use mime::Mime;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{EitherBody, MessageBody},
|
body::{EitherBody, MessageBody},
|
||||||
http::{
|
http::{
|
||||||
header::{self, AcceptEncoding, ContentType, Encoding, HeaderValue},
|
header::{self, AcceptEncoding, ContentEncoding, Encoding, HeaderValue},
|
||||||
StatusCode,
|
StatusCode,
|
||||||
},
|
},
|
||||||
service::{ServiceRequest, ServiceResponse},
|
service::{ServiceRequest, ServiceResponse},
|
||||||
@ -72,17 +73,22 @@ use crate::{
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [feature flags]: ../index.html#crate-features
|
/// [feature flags]: ../index.html#crate-features
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Compress {
|
pub struct Compress {
|
||||||
pub compress: fn(Mime) -> bool,
|
pub compress: fn(&HeaderValue) -> bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Compress {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("Compress").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Default for Compress {
|
impl Default for Compress {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Compress {
|
Compress {
|
||||||
compress: |_| { true }
|
compress: |_| false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,13 +104,16 @@ where
|
|||||||
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||||
|
|
||||||
fn new_transform(&self, service: S) -> Self::Future {
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
ok(CompressMiddleware { service, compress: self.compress })
|
ok(CompressMiddleware {
|
||||||
|
service,
|
||||||
|
compress: self.compress,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompressMiddleware<S> {
|
pub struct CompressMiddleware<S> {
|
||||||
service: S,
|
service: S,
|
||||||
compress: fn(Mime) -> bool,
|
compress: fn(&HeaderValue) -> bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, B> Service<ServiceRequest> for CompressMiddleware<S>
|
impl<S, B> Service<ServiceRequest> for CompressMiddleware<S>
|
||||||
@ -131,6 +140,7 @@ where
|
|||||||
encoding: Encoding::identity(),
|
encoding: Encoding::identity(),
|
||||||
fut: self.service.call(req),
|
fut: self.service.call(req),
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
|
compress: self.compress,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +168,7 @@ where
|
|||||||
fut: self.service.call(req),
|
fut: self.service.call(req),
|
||||||
encoding,
|
encoding,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
|
compress: self.compress,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,6 +183,7 @@ pin_project! {
|
|||||||
fut: S::Future,
|
fut: S::Future,
|
||||||
encoding: Encoding,
|
encoding: Encoding,
|
||||||
_phantom: PhantomData<B>,
|
_phantom: PhantomData<B>,
|
||||||
|
compress: fn(&HeaderValue) -> bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,8 +194,8 @@ where
|
|||||||
{
|
{
|
||||||
type Output = Result<ServiceResponse<EitherBody<Encoder<B>>>, Error>;
|
type Output = Result<ServiceResponse<EitherBody<Encoder<B>>>, Error>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let this = self.project();
|
let this = self.as_mut().project();
|
||||||
|
|
||||||
match ready!(this.fut.poll(cx)) {
|
match ready!(this.fut.poll(cx)) {
|
||||||
Ok(resp) => {
|
Ok(resp) => {
|
||||||
@ -195,7 +207,19 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
Poll::Ready(Ok(resp.map_body(move |head, body| {
|
Poll::Ready(Ok(resp.map_body(move |head, body| {
|
||||||
EitherBody::left(Encoder::response(enc, head, body))
|
let content_type = head.headers.get(header::CONTENT_TYPE);
|
||||||
|
let should_compress = content_type
|
||||||
|
.map(|value| (self.compress)(value))
|
||||||
|
.unwrap_or(true);
|
||||||
|
if should_compress {
|
||||||
|
EitherBody::left(Encoder::response(enc, head, body))
|
||||||
|
} else {
|
||||||
|
EitherBody::left(Encoder::response(
|
||||||
|
ContentEncoding::Identity,
|
||||||
|
head,
|
||||||
|
body,
|
||||||
|
))
|
||||||
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +283,7 @@ mod tests {
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::http::header::ContentType;
|
||||||
use crate::{middleware::DefaultHeaders, test, web, App};
|
use crate::{middleware::DefaultHeaders, test, web, App};
|
||||||
|
|
||||||
pub fn gzip_decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
|
pub fn gzip_decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
|
||||||
@ -345,9 +370,9 @@ mod tests {
|
|||||||
App::new().wrap(Compress::default()).route(
|
App::new().wrap(Compress::default()).route(
|
||||||
"/image",
|
"/image",
|
||||||
web::get().to(move || {
|
web::get().to(move || {
|
||||||
let mut builder = HttpResponse::Ok();
|
let builder = HttpResponse::Ok()
|
||||||
builder.body(DATA);
|
.insert_header(ContentType::jpeg())
|
||||||
builder.insert_header(ContentType::jpeg());
|
.body(DATA);
|
||||||
builder
|
builder
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -357,9 +382,12 @@ mod tests {
|
|||||||
.uri("/image")
|
.uri("/image")
|
||||||
.insert_header((header::ACCEPT_ENCODING, "gzip"))
|
.insert_header((header::ACCEPT_ENCODING, "gzip"))
|
||||||
.to_request();
|
.to_request();
|
||||||
let res = test::call_service(&app, req).await;
|
let res = test::call_service(&app, req).await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
assert_eq!(res.headers().get(header::CONTENT_TYPE).unwrap(), "gzip");
|
assert_eq!(
|
||||||
|
res.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||||
|
"image/jpeg"
|
||||||
|
);
|
||||||
let bytes = test::read_body(res).await;
|
let bytes = test::read_body(res).await;
|
||||||
assert_eq!(bytes, DATA.as_bytes());
|
assert_eq!(bytes, DATA.as_bytes());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user