Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select compression algorithm using features flags #2250

Merged
merged 1 commit into from
Jun 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased - 2021-xx-xx

### Changed

* Change compression algorithm features flags. [#2250]

[#2250]: https://github.com/actix/actix-web/pull/2250

## 4.0.0-beta.7 - 2021-06-17
### Added
Expand Down
23 changes: 16 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ edition = "2018"

[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["openssl", "rustls", "compress", "cookies", "secure-cookies"]
features = ["openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd", "cookies", "secure-cookies"]

[lib]
name = "actix_web"
Expand All @@ -39,10 +39,14 @@ members = [
# resolver = "2"

[features]
default = ["compress", "cookies"]
default = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]

# content-encoding support
compress = ["actix-http/compress"]
# Brotli algorithm content-encoding support
compress-brotli = ["actix-http/compress-brotli", "__compress"]
# Gzip and deflate algorithms content-encoding support
compress-gzip = ["actix-http/compress-gzip", "__compress"]
# Zstd algorithm content-encoding support
compress-zstd = ["actix-http/compress-zstd", "__compress"]

# support for cookies
cookies = ["cookie"]
Expand All @@ -56,6 +60,10 @@ openssl = ["actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"]
# rustls
rustls = ["actix-http/rustls", "actix-tls/accept", "actix-tls/rustls"]

# Internal (PRIVATE!) features used to aid testing and cheking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
__compress = []

[dependencies]
actix-codec = "0.4.0"
actix-macros = "0.2.1"
Expand All @@ -71,6 +79,7 @@ actix-http = "3.0.0-beta.7"

ahash = "0.7"
bytes = "1"
cfg-if = "1"
cookie = { version = "0.15", features = ["percent-encode"], optional = true }
derive_more = "0.99.5"
either = "1.5.3"
Expand Down Expand Up @@ -126,15 +135,15 @@ awc = { path = "awc" }

[[test]]
name = "test_server"
required-features = ["compress", "cookies"]
required-features = ["compress-brotli", "compress-gzip", "compress-zstd", "cookies"]

[[example]]
name = "basic"
required-features = ["compress"]
required-features = ["compress-gzip"]

[[example]]
name = "uds"
required-features = ["compress"]
required-features = ["compress-gzip"]

[[example]]
name = "on_connect"
Expand Down
12 changes: 12 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@

Alternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`.

* Feature flag `compress` has been split into its supported algorithm (brotli, gzip, zstd).
By default all compression algorithms are enabled.
To select algorithm you want to include with `middleware::Compress` use following flags:
- `compress-brotli`
- `compress-gzip`
- `compress-zstd`
If you have set in your `Cargo.toml` dedicated `actix-web` features and you still want
to have compression enabled. Please change features selection like bellow:

Before: `"compress"`
After: `"compress-brotli", "compress-gzip", "compress-zstd"`


## 3.0.0

Expand Down
5 changes: 5 additions & 0 deletions actix-http/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased - 2021-xx-xx

### Changed

* Change compression algorithm features flags. [#2250]

[#2250]: https://github.com/actix/actix-web/pull/2250

## 3.0.0-beta.7 - 2021-06-17
### Added
Expand Down
10 changes: 8 additions & 2 deletions actix-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ edition = "2018"

[package.metadata.docs.rs]
# features that docs.rs will build with
features = ["openssl", "rustls", "compress"]
features = ["openssl", "rustls", "compress-brotli", "compress-gzip", "compress-zstd"]

[lib]
name = "actix_http"
Expand All @@ -32,11 +32,17 @@ openssl = ["actix-tls/openssl"]
rustls = ["actix-tls/rustls"]

# enable compression support
compress = ["flate2", "brotli2", "zstd"]
compress-brotli = ["brotli2", "__compress"]
compress-gzip = ["flate2", "__compress"]
compress-zstd = ["zstd", "__compress"]

# trust-dns as client dns resolver
trust-dns = ["trust-dns-resolver"]

# Internal (PRIVATE!) features used to aid testing and cheking feature status.
# Don't rely on these whatsoever. They may disappear at anytime.
__compress = []

[dependencies]
actix-service = "2.0.0"
actix-codec = "0.4.0"
Expand Down
26 changes: 24 additions & 2 deletions actix-http/src/encoding/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ use std::{
};

use actix_rt::task::{spawn_blocking, JoinHandle};
use brotli2::write::BrotliDecoder;
use bytes::Bytes;
use flate2::write::{GzDecoder, ZlibDecoder};
use futures_core::{ready, Stream};

#[cfg(feature = "compress-brotli")]
use brotli2::write::BrotliDecoder;

#[cfg(feature = "compress-gzip")]
use flate2::write::{GzDecoder, ZlibDecoder};

#[cfg(feature = "compress-zstd")]
use zstd::stream::write::Decoder as ZstdDecoder;

use crate::{
Expand All @@ -37,15 +43,19 @@ where
#[inline]
pub fn new(stream: S, encoding: ContentEncoding) -> Decoder<S> {
let decoder = match encoding {
#[cfg(feature = "compress-brotli")]
ContentEncoding::Br => Some(ContentDecoder::Br(Box::new(
BrotliDecoder::new(Writer::new()),
))),
#[cfg(feature = "compress-gzip")]
ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new(
ZlibDecoder::new(Writer::new()),
))),
#[cfg(feature = "compress-gzip")]
ContentEncoding::Gzip => Some(ContentDecoder::Gzip(Box::new(
GzDecoder::new(Writer::new()),
))),
#[cfg(feature = "compress-zstd")]
ContentEncoding::Zstd => Some(ContentDecoder::Zstd(Box::new(
ZstdDecoder::new(Writer::new()).expect(
"Failed to create zstd decoder. This is a bug. \
Expand Down Expand Up @@ -148,17 +158,22 @@ where
}

enum ContentDecoder {
#[cfg(feature = "compress-gzip")]
Deflate(Box<ZlibDecoder<Writer>>),
#[cfg(feature = "compress-gzip")]
Gzip(Box<GzDecoder<Writer>>),
#[cfg(feature = "compress-brotli")]
Br(Box<BrotliDecoder<Writer>>),
// We need explicit 'static lifetime here because ZstdDecoder need lifetime
// argument, and we use `spawn_blocking` in `Decoder::poll_next` that require `FnOnce() -> R + Send + 'static`
#[cfg(feature = "compress-zstd")]
Zstd(Box<ZstdDecoder<'static, Writer>>),
}

impl ContentDecoder {
fn feed_eof(&mut self) -> io::Result<Option<Bytes>> {
match self {
#[cfg(feature = "compress-brotli")]
ContentDecoder::Br(ref mut decoder) => match decoder.flush() {
Ok(()) => {
let b = decoder.get_mut().take();
Expand All @@ -172,6 +187,7 @@ impl ContentDecoder {
Err(e) => Err(e),
},

#[cfg(feature = "compress-gzip")]
ContentDecoder::Gzip(ref mut decoder) => match decoder.try_finish() {
Ok(_) => {
let b = decoder.get_mut().take();
Expand All @@ -185,6 +201,7 @@ impl ContentDecoder {
Err(e) => Err(e),
},

#[cfg(feature = "compress-gzip")]
ContentDecoder::Deflate(ref mut decoder) => match decoder.try_finish() {
Ok(_) => {
let b = decoder.get_mut().take();
Expand All @@ -197,6 +214,7 @@ impl ContentDecoder {
Err(e) => Err(e),
},

#[cfg(feature = "compress-zstd")]
ContentDecoder::Zstd(ref mut decoder) => match decoder.flush() {
Ok(_) => {
let b = decoder.get_mut().take();
Expand All @@ -213,6 +231,7 @@ impl ContentDecoder {

fn feed_data(&mut self, data: Bytes) -> io::Result<Option<Bytes>> {
match self {
#[cfg(feature = "compress-brotli")]
ContentDecoder::Br(ref mut decoder) => match decoder.write_all(&data) {
Ok(_) => {
decoder.flush()?;
Expand All @@ -227,6 +246,7 @@ impl ContentDecoder {
Err(e) => Err(e),
},

#[cfg(feature = "compress-gzip")]
ContentDecoder::Gzip(ref mut decoder) => match decoder.write_all(&data) {
Ok(_) => {
decoder.flush()?;
Expand All @@ -241,6 +261,7 @@ impl ContentDecoder {
Err(e) => Err(e),
},

#[cfg(feature = "compress-gzip")]
ContentDecoder::Deflate(ref mut decoder) => match decoder.write_all(&data) {
Ok(_) => {
decoder.flush()?;
Expand All @@ -255,6 +276,7 @@ impl ContentDecoder {
Err(e) => Err(e),
},

#[cfg(feature = "compress-zstd")]
ContentDecoder::Zstd(ref mut decoder) => match decoder.write_all(&data) {
Ok(_) => {
decoder.flush()?;
Expand Down
30 changes: 28 additions & 2 deletions actix-http/src/encoding/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ use std::{
};

use actix_rt::task::{spawn_blocking, JoinHandle};
use brotli2::write::BrotliEncoder;
use bytes::Bytes;
use derive_more::Display;
use flate2::write::{GzEncoder, ZlibEncoder};
use futures_core::ready;
use pin_project::pin_project;

#[cfg(feature = "compress-brotli")]
use brotli2::write::BrotliEncoder;

#[cfg(feature = "compress-gzip")]
use flate2::write::{GzEncoder, ZlibEncoder};

#[cfg(feature = "compress-zstd")]
use zstd::stream::write::Encoder as ZstdEncoder;

use crate::{
Expand Down Expand Up @@ -233,28 +239,36 @@ fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) {
}

enum ContentEncoder {
#[cfg(feature = "compress-gzip")]
Deflate(ZlibEncoder<Writer>),
#[cfg(feature = "compress-gzip")]
Gzip(GzEncoder<Writer>),
#[cfg(feature = "compress-brotli")]
Br(BrotliEncoder<Writer>),
// We need explicit 'static lifetime here because ZstdEncoder need lifetime
// argument, and we use `spawn_blocking` in `Encoder::poll_next` that require `FnOnce() -> R + Send + 'static`
#[cfg(feature = "compress-zstd")]
Zstd(ZstdEncoder<'static, Writer>),
}

impl ContentEncoder {
fn encoder(encoding: ContentEncoding) -> Option<Self> {
match encoding {
#[cfg(feature = "compress-gzip")]
ContentEncoding::Deflate => Some(ContentEncoder::Deflate(ZlibEncoder::new(
Writer::new(),
flate2::Compression::fast(),
))),
#[cfg(feature = "compress-gzip")]
ContentEncoding::Gzip => Some(ContentEncoder::Gzip(GzEncoder::new(
Writer::new(),
flate2::Compression::fast(),
))),
#[cfg(feature = "compress-brotli")]
ContentEncoding::Br => {
Some(ContentEncoder::Br(BrotliEncoder::new(Writer::new(), 3)))
}
#[cfg(feature = "compress-zstd")]
ContentEncoding::Zstd => {
let encoder = ZstdEncoder::new(Writer::new(), 3).ok()?;
Some(ContentEncoder::Zstd(encoder))
Expand All @@ -266,27 +280,35 @@ impl ContentEncoder {
#[inline]
pub(crate) fn take(&mut self) -> Bytes {
match *self {
#[cfg(feature = "compress-brotli")]
ContentEncoder::Br(ref mut encoder) => encoder.get_mut().take(),
#[cfg(feature = "compress-gzip")]
ContentEncoder::Deflate(ref mut encoder) => encoder.get_mut().take(),
#[cfg(feature = "compress-gzip")]
ContentEncoder::Gzip(ref mut encoder) => encoder.get_mut().take(),
#[cfg(feature = "compress-zstd")]
ContentEncoder::Zstd(ref mut encoder) => encoder.get_mut().take(),
}
}

fn finish(self) -> Result<Bytes, io::Error> {
match self {
#[cfg(feature = "compress-brotli")]
ContentEncoder::Br(encoder) => match encoder.finish() {
Ok(writer) => Ok(writer.buf.freeze()),
Err(err) => Err(err),
},
#[cfg(feature = "compress-gzip")]
ContentEncoder::Gzip(encoder) => match encoder.finish() {
Ok(writer) => Ok(writer.buf.freeze()),
Err(err) => Err(err),
},
#[cfg(feature = "compress-gzip")]
ContentEncoder::Deflate(encoder) => match encoder.finish() {
Ok(writer) => Ok(writer.buf.freeze()),
Err(err) => Err(err),
},
#[cfg(feature = "compress-zstd")]
ContentEncoder::Zstd(encoder) => match encoder.finish() {
Ok(writer) => Ok(writer.buf.freeze()),
Err(err) => Err(err),
Expand All @@ -296,27 +318,31 @@ impl ContentEncoder {

fn write(&mut self, data: &[u8]) -> Result<(), io::Error> {
match *self {
#[cfg(feature = "compress-brotli")]
ContentEncoder::Br(ref mut encoder) => match encoder.write_all(data) {
Ok(_) => Ok(()),
Err(err) => {
trace!("Error decoding br encoding: {}", err);
Err(err)
}
},
#[cfg(feature = "compress-gzip")]
ContentEncoder::Gzip(ref mut encoder) => match encoder.write_all(data) {
Ok(_) => Ok(()),
Err(err) => {
trace!("Error decoding gzip encoding: {}", err);
Err(err)
}
},
#[cfg(feature = "compress-gzip")]
ContentEncoder::Deflate(ref mut encoder) => match encoder.write_all(data) {
Ok(_) => Ok(()),
Err(err) => {
trace!("Error decoding deflate encoding: {}", err);
Err(err)
}
},
#[cfg(feature = "compress-zstd")]
ContentEncoder::Zstd(ref mut encoder) => match encoder.write_all(data) {
Ok(_) => Ok(()),
Err(err) => {
Expand Down
Loading