Skip to content

Commit

Permalink
Do not compress when Content-Encoding of the response is identity
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin1887 committed Oct 4, 2018
1 parent 96ea63b commit e7cc899
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 41 deletions.
71 changes: 46 additions & 25 deletions contrib/lib/src/compression.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,4 @@
//! This module provides brotli and gzip compression for all non-image
//! responses for requests that send Accept-Encoding br and gzip. If
//! accepted, brotli compression is preferred over gzip.
//!
//! To add this feature to your Rocket application, use
//! .attach(rocket_contrib::Compression::fairing())
//! to your Rocket instance. Note that you must add the
//! "compression" feature for brotli and gzip compression to your rocket_contrib
//! dependency in Cargo.toml. Additionally, you can load only brotli compression
//! using "brotli_compression" feature or load only gzip compression using
//! "gzip_compression" in your rocket_contrib dependency in Cargo.toml.
//!
//! In the brotli algorithm, quality is set to 2 in order to have really fast
//! compressions with compression ratio similar to gzip. Also, text and font
//! compression mode is set regarding the Content-Type of the response.
//!
//! In the gzip algorithm, quality is the default (9) in order to have good
//! compression ratio.
//!
//! For brotli compression, the rust-brotli crate is used.
//! For gzip compression, flate2 crate is used.
//! Returns a response fairing that compresses all responses.
use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::Header;
Expand All @@ -35,6 +15,50 @@ use flate2;
#[cfg(feature = "gzip_compression")]
use flate2::read::GzEncoder;

/// This module provides brotli and gzip compression for all non-image
/// responses for requests that send Accept-Encoding br and gzip. If
/// accepted, brotli compression is preferred over gzip.
///
/// In the brotli algorithm, quality is set to 2 in order to have really fast
/// compressions with compression ratio similar to gzip. Also, text and font
/// compression mode is set regarding the Content-Type of the response.
///
/// In the gzip algorithm, quality is the default (9) in order to have good
/// compression ratio.
///
/// For brotli compression, `rust-brotli` crate is used.
/// For gzip compression, `flate2` crate is used.
///
/// # Usage
///
/// To use, add the `brotli_compression` feature, the `gzip_compression`
/// feature or the `compression` feature for using both to the
/// `rocket_contrib` dependencies section of your `Cargo.toml`:
///
/// ```toml,ignore
/// [dependencies.rocket_contrib]
/// version = "*"
/// default-features = false
/// features = ["compression"]
/// ```
///
/// Then, ensure that the compression [fairing](/rocket/fairing/) is attached to
/// your Rocket application:
///
/// ```rust
/// extern crate rocket;
/// extern crate rocket_contrib;
///
/// use rocket_contrib::Compression;
///
/// fn main() {
/// rocket::ignite()
/// // ...
/// .attach(Compression::fairing())
/// // ...
/// # ;
/// }
/// ```
pub struct Compression(());

impl Compression {
Expand All @@ -58,10 +82,7 @@ impl Compression {
}

fn already_encoded(response: &Response) -> bool {
response
.headers()
.get("Content-Encoding")
.any(|e| e != "identity" && e != "chunked")
response.headers().get("Content-Encoding").next().is_some()
}

fn set_body_and_header<'r, B: Read + 'r>(
Expand Down
32 changes: 16 additions & 16 deletions contrib/lib/tests/compression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const HELLO: &str = r"This is a message to hello with more than 100 bytes \

fn rocket() -> rocket::Rocket {
rocket::ignite()
.mount("/", routes![index, font, image, already_encoded, chunked])
.mount("/", routes![index, font, image, already_encoded, identity])
.attach(rocket_contrib::Compression::fairing())
}

Expand Down Expand Up @@ -61,10 +61,10 @@ pub fn already_encoded() -> Response<'static> {
.sized_body(Cursor::new(encoded))
.finalize()
}
#[get("/chunked")]
pub fn chunked() -> Response<'static> {
#[get("/identity")]
pub fn identity() -> Response<'static> {
Response::build()
.header(ContentEncoding(vec![Encoding::Chunked]))
.header(ContentEncoding(vec![Encoding::Identity]))
.sized_body(Cursor::new(String::from(HELLO)))
.finalize()
}
Expand Down Expand Up @@ -126,30 +126,30 @@ fn test_already_encoded() {
assert_eq!(s, String::from(HELLO));
}

/// This function should compress the content in br because the ContentEncoding
/// is chunked, not a compression encoding
/// This function should not compress the content because if a Content-Encoding
/// header is set the responder do not want to compress
#[test]
fn test_chunked() {
fn test_identity_encoded() {
let client = Client::new(rocket()).expect("valid rocket instance");
let mut response = client
.get("/chunked")
.get("/identity")
.header(Header::new("Accept-Encoding", "deflate, gzip, brotli"))
.dispatch();
assert_eq!(response.status(), Status::Ok);
assert!(
response
!response
.headers()
.get("Content-Encoding")
.any(|x| x == "br")
);
let mut body_plain = Cursor::new(Vec::<u8>::new());
brotli::BrotliDecompress(
&mut Cursor::new(response.body_bytes().unwrap()),
&mut body_plain,
)
.unwrap();
assert!(
!response
.headers()
.get("Content-Encoding")
.any(|x| x == "gzip")
);
assert_eq!(
String::from_utf8(body_plain.get_mut().to_vec()).unwrap(),
String::from_utf8(response.body_bytes().unwrap()).unwrap(),
String::from(HELLO)
);
}
Expand Down

0 comments on commit e7cc899

Please sign in to comment.