1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-06-30 00:14:58 +02:00

Fix AcceptEncoding header (#2501)

This commit is contained in:
Rob Ede
2022-01-03 13:17:57 +00:00
committed by GitHub
parent b708924590
commit e890307091
33 changed files with 1360 additions and 889 deletions

313
tests/compression.rs Normal file
View File

@ -0,0 +1,313 @@
use actix_http::ContentEncoding;
use actix_web::{
dev::BodyEncoding as _,
http::{header, StatusCode},
middleware::Compress,
web, App, HttpResponse,
};
use bytes::Bytes;
mod test_utils;
use test_utils::{brotli, gzip, zstd};
static LOREM: &[u8] = include_bytes!("fixtures/lorem.txt");
static LOREM_GZIP: &[u8] = include_bytes!("fixtures/lorem.txt.gz");
static LOREM_BR: &[u8] = include_bytes!("fixtures/lorem.txt.br");
static LOREM_ZSTD: &[u8] = include_bytes!("fixtures/lorem.txt.zst");
static LOREM_XZ: &[u8] = include_bytes!("fixtures/lorem.txt.xz");
macro_rules! test_server {
() => {
actix_test::start(|| {
App::new()
.wrap(Compress::default())
.route("/static", web::to(|| HttpResponse::Ok().body(LOREM)))
.route(
"/static-gzip",
web::to(|| {
HttpResponse::Ok()
// signal to compressor that content should not be altered
.encode_with(ContentEncoding::Identity)
// signal to client that content is encoded
.insert_header(ContentEncoding::Gzip)
.body(LOREM_GZIP)
}),
)
.route(
"/static-br",
web::to(|| {
HttpResponse::Ok()
// signal to compressor that content should not be altered
.encode_with(ContentEncoding::Identity)
// signal to client that content is encoded
.insert_header(ContentEncoding::Brotli)
.body(LOREM_BR)
}),
)
.route(
"/static-zstd",
web::to(|| {
HttpResponse::Ok()
// signal to compressor that content should not be altered
.encode_with(ContentEncoding::Identity)
// signal to client that content is encoded
.insert_header(ContentEncoding::Zstd)
.body(LOREM_ZSTD)
}),
)
.route(
"/static-xz",
web::to(|| {
HttpResponse::Ok()
// signal to compressor that content should not be altered
.encode_with(ContentEncoding::Identity)
// signal to client that content is encoded as 7zip
.insert_header((header::CONTENT_ENCODING, "xz"))
.body(LOREM_XZ)
}),
)
.route(
"/echo",
web::to(|body: Bytes| HttpResponse::Ok().body(body)),
)
})
};
}
#[actix_rt::test]
async fn negotiate_encoding_identity() {
let srv = test_server!();
let req = srv
.post("/static")
.insert_header((header::ACCEPT_ENCODING, "identity"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING), None);
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM));
srv.stop().await;
}
#[actix_rt::test]
async fn negotiate_encoding_gzip() {
let srv = test_server!();
let req = srv
.post("/static")
.insert_header((header::ACCEPT_ENCODING, "gzip,br,zstd"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "gzip");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM));
let mut res = srv
.post("/static")
.no_decompress()
.insert_header((header::ACCEPT_ENCODING, "gzip,br,zstd"))
.send()
.await
.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(gzip::decode(bytes), LOREM);
srv.stop().await;
}
#[actix_rt::test]
async fn negotiate_encoding_br() {
let srv = test_server!();
let req = srv
.post("/static")
.insert_header((header::ACCEPT_ENCODING, "br,zstd,gzip"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "br");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM));
let mut res = srv
.post("/static")
.no_decompress()
.insert_header((header::ACCEPT_ENCODING, "br,zstd,gzip"))
.send()
.await
.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(brotli::decode(bytes), LOREM);
srv.stop().await;
}
#[actix_rt::test]
async fn negotiate_encoding_zstd() {
let srv = test_server!();
let req = srv
.post("/static")
.insert_header((header::ACCEPT_ENCODING, "zstd,gzip,br"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "zstd");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM));
let mut res = srv
.post("/static")
.no_decompress()
.insert_header((header::ACCEPT_ENCODING, "zstd,gzip,br"))
.send()
.await
.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(zstd::decode(bytes), LOREM);
srv.stop().await;
}
#[cfg(all(
feature = "compress-brotli",
feature = "compress-gzip",
feature = "compress-zstd",
))]
#[actix_rt::test]
async fn client_encoding_prefers_brotli() {
let srv = test_server!();
let req = srv.post("/static").send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "br");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM));
srv.stop().await;
}
#[actix_rt::test]
async fn gzip_no_decompress() {
let srv = test_server!();
let req = srv
.post("/static-gzip")
// don't decompress response body
.no_decompress()
// signal that we want a compressed body
.insert_header((header::ACCEPT_ENCODING, "gzip,br,zstd"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "gzip");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM_GZIP));
srv.stop().await;
}
#[actix_rt::test]
async fn manual_custom_coding() {
let srv = test_server!();
let req = srv
.post("/static-xz")
// don't decompress response body
.no_decompress()
// signal that we want a compressed body
.insert_header((header::ACCEPT_ENCODING, "xz"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "xz");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM_XZ));
srv.stop().await;
}
#[actix_rt::test]
async fn deny_identity_coding() {
let srv = test_server!();
let req = srv
.post("/static")
// signal that we want a compressed body
.insert_header((header::ACCEPT_ENCODING, "br, identity;q=0"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "br");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM));
srv.stop().await;
}
#[actix_rt::test]
async fn deny_identity_coding_no_decompress() {
let srv = test_server!();
let req = srv
.post("/static-br")
// don't decompress response body
.no_decompress()
// signal that we want a compressed body
.insert_header((header::ACCEPT_ENCODING, "br, identity;q=0"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "br");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM_BR));
srv.stop().await;
}
// TODO: fix test
// currently fails because negotiation doesn't consider unknown encoding types
#[ignore]
#[actix_rt::test]
async fn deny_identity_for_manual_coding() {
let srv = test_server!();
let req = srv
.post("/static-xz")
// don't decompress response body
.no_decompress()
// signal that we want a compressed body
.insert_header((header::ACCEPT_ENCODING, "xz, identity;q=0"))
.send();
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(header::CONTENT_ENCODING).unwrap(), "xz");
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(LOREM_XZ));
srv.stop().await;
}

5
tests/fixtures/lorem.txt vendored Normal file
View File

@ -0,0 +1,5 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin interdum tincidunt lacus, sed tempor lorem consectetur et. Pellentesque et egestas sem, at cursus massa. Nunc feugiat elit sit amet ipsum commodo luctus. Proin auctor dignissim pharetra. Integer iaculis quam a tellus auctor, vitae auctor nisl viverra. Nullam consequat maximus porttitor. Pellentesque tortor enim, molestie at varius non, tempor non nibh. Suspendisse tempus erat lorem, vel faucibus magna blandit vel. Sed pellentesque ligula augue, vitae fermentum eros blandit et. Cras dignissim in massa ut varius. Vestibulum commodo nunc sit amet pellentesque dignissim.
Donec imperdiet blandit lobortis. Suspendisse fringilla nunc quis venenatis tempor. Nunc tempor sed erat sed convallis. Pellentesque aliquet elit lectus, quis vulputate arcu pharetra sed. Etiam laoreet aliquet arcu cursus vehicula. Maecenas odio odio, elementum faucibus sollicitudin vitae, pellentesque ac purus. Donec venenatis faucibus lorem, et finibus lacus tincidunt vitae. Quisque laoreet metus sapien, vitae euismod mauris lobortis malesuada. Integer sit amet elementum turpis. Maecenas ex mauris, dapibus eu placerat vitae, rutrum convallis enim. Nulla vitae orci ultricies, sagittis turpis et, lacinia dui. Praesent egestas urna turpis, sit amet feugiat mauris tristique eu. Quisque id tempor libero. Donec ullamcorper dapibus lorem, vel consequat risus congue a.
Nullam dignissim ut lectus vitae tempor. Pellentesque ut odio fringilla, volutpat mi et, vulputate tellus. Fusce eget diam non odio tincidunt viverra eu vel augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam sed eleifend purus, vitae aliquam orci. Cras fringilla justo eget tempus bibendum. Phasellus venenatis, odio nec pulvinar commodo, quam neque lacinia turpis, ut rutrum tortor massa eu nulla. Vivamus tincidunt ut lectus a gravida. Donec varius mi quis enim interdum ultrices. Sed aliquam consectetur nisi vitae viverra. Praesent nec ligula egestas, porta lectus sed, consectetur augue.

BIN
tests/fixtures/lorem.txt.br vendored Normal file

Binary file not shown.

BIN
tests/fixtures/lorem.txt.gz vendored Normal file

Binary file not shown.

BIN
tests/fixtures/lorem.txt.xz vendored Normal file

Binary file not shown.

BIN
tests/fixtures/lorem.txt.zst vendored Normal file

Binary file not shown.

View File

@ -11,30 +11,28 @@ use std::{
};
use actix_web::{
cookie::{Cookie, CookieBuilder},
dev::BodyEncoding,
http::header::{
ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING,
http::{
header::{self, ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, TRANSFER_ENCODING},
StatusCode,
},
middleware::{Compress, NormalizePath, TrailingSlash},
web, App, Error, HttpResponse,
};
use brotli2::write::{BrotliDecoder, BrotliEncoder};
use bytes::Bytes;
use cookie::{Cookie, CookieBuilder};
use flate2::{
read::GzDecoder,
write::{GzEncoder, ZlibDecoder, ZlibEncoder},
Compression,
};
use futures_core::ready;
use rand::{distributions::Alphanumeric, Rng as _};
#[cfg(feature = "openssl")]
use openssl::{
pkey::PKey,
ssl::{SslAcceptor, SslMethod},
x509::X509,
};
use rand::{distributions::Alphanumeric, Rng};
use zstd::stream::{read::Decoder as ZstdDecoder, write::Encoder as ZstdEncoder};
mod test_utils;
use test_utils::{brotli, deflate, gzip, zstd};
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
@ -122,165 +120,85 @@ async fn test_body() {
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
});
let mut response = srv.get("/").send().await.unwrap();
assert!(response.status().is_success());
let mut res = srv.get("/").send().await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
async fn test_body_gzip() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Gzip))
.service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
});
let mut response = srv
.get("/")
.no_decompress()
.append_header((ACCEPT_ENCODING, "gzip"))
.send()
.await
.unwrap();
assert!(response.status().is_success());
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
async fn test_body_gzip2() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Gzip))
.service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
});
let mut response = srv
.get("/")
.no_decompress()
.append_header((ACCEPT_ENCODING, "gzip"))
.send()
.await
.unwrap();
assert!(response.status().is_success());
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
srv.stop().await;
}
#[actix_rt::test]
async fn test_body_encoding_override() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Gzip))
.wrap(Compress::default())
.service(web::resource("/").route(web::to(|| {
HttpResponse::Ok()
.encoding(ContentEncoding::Deflate)
.encode_with(ContentEncoding::Deflate)
.body(STR)
})))
.service(web::resource("/raw").route(web::to(|| {
let mut response =
HttpResponse::with_body(actix_web::http::StatusCode::OK, STR);
response.encoding(ContentEncoding::Deflate);
response.map_into_boxed_body()
let mut res = HttpResponse::with_body(actix_web::http::StatusCode::OK, STR);
res.encode_with(ContentEncoding::Deflate);
res.map_into_boxed_body()
})))
});
// Builder
let mut response = srv
let mut res = srv
.get("/")
.no_decompress()
.append_header((ACCEPT_ENCODING, "deflate"))
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = ZlibDecoder::new(Vec::new());
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(deflate::decode(bytes), STR.as_bytes());
// Raw Response
let mut response = srv
let mut res = srv
.request(actix_web::http::Method::GET, srv.url("/raw"))
.no_decompress()
.append_header((ACCEPT_ENCODING, "deflate"))
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = ZlibDecoder::new(Vec::new());
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(deflate::decode(bytes), STR.as_bytes());
srv.stop().await;
}
#[actix_rt::test]
async fn test_body_gzip_large() {
async fn body_gzip_large() {
let data = STR.repeat(10);
let srv_data = data.clone();
let srv = actix_test::start_with(actix_test::config().h1(), move || {
let data = srv_data.clone();
App::new()
.wrap(Compress::new(ContentEncoding::Gzip))
.service(
web::resource("/")
.route(web::to(move || HttpResponse::Ok().body(data.clone()))),
)
App::new().wrap(Compress::default()).service(
web::resource("/").route(web::to(move || HttpResponse::Ok().body(data.clone()))),
)
});
let mut response = srv
let mut res = srv
.get("/")
.no_decompress()
.append_header((ACCEPT_ENCODING, "gzip"))
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from(data));
let bytes = res.body().await.unwrap();
assert_eq!(gzip::decode(bytes), data.as_bytes());
srv.stop().await;
}
@ -296,32 +214,22 @@ async fn test_body_gzip_large_random() {
let srv = actix_test::start_with(actix_test::config().h1(), move || {
let data = srv_data.clone();
App::new()
.wrap(Compress::new(ContentEncoding::Gzip))
.service(
web::resource("/")
.route(web::to(move || HttpResponse::Ok().body(data.clone()))),
)
App::new().wrap(Compress::default()).service(
web::resource("/").route(web::to(move || HttpResponse::Ok().body(data.clone()))),
)
});
let mut response = srv
let mut res = srv
.get("/")
.no_decompress()
.append_header((ACCEPT_ENCODING, "gzip"))
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(dec.len(), data.len());
assert_eq!(Bytes::from(dec), Bytes::from(data));
let bytes = res.body().await.unwrap();
assert_eq!(gzip::decode(bytes), data.as_bytes());
srv.stop().await;
}
@ -330,34 +238,25 @@ async fn test_body_gzip_large_random() {
async fn test_body_chunked_implicit() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Gzip))
.wrap(Compress::default())
.service(web::resource("/").route(web::get().to(move || {
HttpResponse::Ok()
.streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24))
})))
});
let mut response = srv
let mut res = srv
.get("/")
.no_decompress()
.append_header((ACCEPT_ENCODING, "gzip"))
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(
response.headers().get(TRANSFER_ENCODING).unwrap(),
&b"chunked"[..]
);
assert_eq!(res.status(), StatusCode::OK);
assert_eq!(res.headers().get(TRANSFER_ENCODING).unwrap(), "chunked");
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = GzDecoder::new(&bytes[..]);
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(gzip::decode(bytes), STR.as_bytes());
srv.stop().await;
}
@ -366,32 +265,24 @@ async fn test_body_chunked_implicit() {
async fn test_body_br_streaming() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Br))
.wrap(Compress::default())
.service(web::resource("/").route(web::to(move || {
HttpResponse::Ok()
.streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24))
})))
});
let mut response = srv
let mut res = srv
.get("/")
.append_header((ACCEPT_ENCODING, "br"))
.no_decompress()
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
println!("TEST: {:?}", bytes.len());
// decode br
let mut e = BrotliDecoder::new(Vec::with_capacity(2048));
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
println!("T: {:?}", Bytes::copy_from_slice(&dec));
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(brotli::decode(bytes), STR.as_bytes());
srv.stop().await;
}
@ -404,16 +295,13 @@ async fn test_head_binary() {
)
});
let mut response = srv.head("/").send().await.unwrap();
assert!(response.status().is_success());
let mut res = srv.head("/").send().await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
{
let len = response.headers().get(CONTENT_LENGTH).unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
}
let len = res.headers().get(header::CONTENT_LENGTH).unwrap();
assert_eq!(format!("{}", STR.len()), len.to_str().unwrap());
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert!(bytes.is_empty());
srv.stop().await;
@ -429,12 +317,11 @@ async fn test_no_chunking() {
})))
});
let mut response = srv.get("/").send().await.unwrap();
assert!(response.status().is_success());
assert!(!response.headers().contains_key(TRANSFER_ENCODING));
let mut res = srv.get("/").send().await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
assert!(!res.headers().contains_key(TRANSFER_ENCODING));
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
@ -444,27 +331,21 @@ async fn test_no_chunking() {
async fn test_body_deflate() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Deflate))
.wrap(Compress::default())
.service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))))
});
// client request
let mut response = srv
let mut res = srv
.get("/")
.append_header((ACCEPT_ENCODING, "deflate"))
.no_decompress()
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let mut e = ZlibDecoder::new(Vec::new());
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(deflate::decode(bytes), STR.as_bytes());
srv.stop().await;
}
@ -473,28 +354,21 @@ async fn test_body_deflate() {
async fn test_body_brotli() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Br))
.wrap(Compress::default())
.service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))))
});
// client request
let mut response = srv
let mut res = srv
.get("/")
.append_header((ACCEPT_ENCODING, "br"))
.no_decompress()
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
// decode brotli
let mut e = BrotliDecoder::new(Vec::with_capacity(2048));
e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(brotli::decode(bytes), STR.as_bytes());
srv.stop().await;
}
@ -503,28 +377,21 @@ async fn test_body_brotli() {
async fn test_body_zstd() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Zstd))
.wrap(Compress::default())
.service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))))
});
// client request
let mut response = srv
let mut res = srv
.get("/")
.append_header((ACCEPT_ENCODING, "zstd"))
.no_decompress()
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = ZstdDecoder::new(&bytes[..]).unwrap();
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(zstd::decode(bytes), STR.as_bytes());
srv.stop().await;
}
@ -533,31 +400,24 @@ async fn test_body_zstd() {
async fn test_body_zstd_streaming() {
let srv = actix_test::start_with(actix_test::config().h1(), || {
App::new()
.wrap(Compress::new(ContentEncoding::Zstd))
.wrap(Compress::default())
.service(web::resource("/").route(web::to(move || {
HttpResponse::Ok()
.streaming(TestBody::new(Bytes::from_static(STR.as_ref()), 24))
})))
});
// client request
let mut response = srv
let mut res = srv
.get("/")
.append_header((ACCEPT_ENCODING, "zstd"))
.no_decompress()
.send()
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
// decode
let mut e = ZstdDecoder::new(&bytes[..]).unwrap();
let mut dec = Vec::new();
e.read_to_end(&mut dec).unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(zstd::decode(bytes), STR.as_bytes());
srv.stop().await;
}
@ -570,20 +430,14 @@ async fn test_zstd_encoding() {
)
});
let mut e = ZstdEncoder::new(Vec::new(), 5).unwrap();
e.write_all(STR.as_ref()).unwrap();
let enc = e.finish().unwrap();
// client request
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "zstd"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(zstd::encode(STR));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
@ -607,21 +461,15 @@ async fn test_zstd_encoding_large() {
)
});
let mut e = ZstdEncoder::new(Vec::new(), 5).unwrap();
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
// client request
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "zstd"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(zstd::encode(&data));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().limit(320_000).await.unwrap();
assert_eq!(bytes, Bytes::from(data));
let bytes = res.body().limit(320_000).await.unwrap();
assert_eq!(bytes, data.as_bytes());
srv.stop().await;
}
@ -634,20 +482,14 @@ async fn test_encoding() {
)
});
// client request
let mut e = GzEncoder::new(Vec::new(), Compression::default());
e.write_all(STR.as_ref()).unwrap();
let enc = e.finish().unwrap();
let request = srv
.post("/")
.insert_header((CONTENT_ENCODING, "gzip"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(gzip::encode(STR));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
@ -661,21 +503,15 @@ async fn test_gzip_encoding() {
)
});
// client request
let mut e = GzEncoder::new(Vec::new(), Compression::default());
e.write_all(STR.as_ref()).unwrap();
let enc = e.finish().unwrap();
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "gzip"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(gzip::encode(STR));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
let bytes = res.body().await.unwrap();
assert_eq!(bytes, STR.as_bytes());
srv.stop().await;
}
@ -689,21 +525,15 @@ async fn test_gzip_encoding_large() {
)
});
// client request
let mut e = GzEncoder::new(Vec::new(), Compression::default());
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
let request = srv
let req = srv
.post("/")
.append_header((CONTENT_ENCODING, "gzip"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(gzip::encode(&data));
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data));
let bytes = res.body().await.unwrap();
assert_eq!(bytes, data);
srv.stop().await;
}
@ -722,22 +552,15 @@ async fn test_reading_gzip_encoding_large_random() {
)
});
// client request
let mut e = GzEncoder::new(Vec::new(), Compression::default());
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "gzip"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(gzip::encode(&data));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
assert_eq!(bytes.len(), data.len());
assert_eq!(bytes, Bytes::from(data));
let bytes = res.body().await.unwrap();
assert_eq!(bytes, data.as_bytes());
srv.stop().await;
}
@ -750,20 +573,14 @@ async fn test_reading_deflate_encoding() {
)
});
let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
e.write_all(STR.as_ref()).unwrap();
let enc = e.finish().unwrap();
// client request
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "deflate"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(deflate::encode(STR));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
@ -778,20 +595,14 @@ async fn test_reading_deflate_encoding_large() {
)
});
let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
// client request
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "deflate"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(deflate::encode(&data));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
@ -811,20 +622,14 @@ async fn test_reading_deflate_encoding_large_random() {
)
});
let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
// client request
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "deflate"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(deflate::encode(&data));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes.len(), data.len());
assert_eq!(bytes, Bytes::from(data));
@ -839,20 +644,14 @@ async fn test_brotli_encoding() {
)
});
let mut e = BrotliEncoder::new(Vec::new(), 5);
e.write_all(STR.as_ref()).unwrap();
let enc = e.finish().unwrap();
// client request
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "br"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(brotli::encode(STR));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
srv.stop().await;
@ -876,20 +675,14 @@ async fn test_brotli_encoding_large() {
)
});
let mut e = BrotliEncoder::new(Vec::new(), 5);
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
// client request
let request = srv
.post("/")
.append_header((CONTENT_ENCODING, "br"))
.send_body(enc.clone());
let mut response = request.await.unwrap();
assert!(response.status().is_success());
.send_body(brotli::encode(&data));
let mut res = request.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().limit(320_000).await.unwrap();
let bytes = res.body().limit(320_000).await.unwrap();
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
@ -898,32 +691,27 @@ async fn test_brotli_encoding_large() {
#[cfg(feature = "openssl")]
#[actix_rt::test]
async fn test_brotli_encoding_large_openssl() {
use actix_web::http::header;
let data = STR.repeat(10);
let srv =
actix_test::start_with(actix_test::config().openssl(openssl_config()), move || {
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
HttpResponse::Ok()
.encoding(ContentEncoding::Identity)
.encode_with(ContentEncoding::Identity)
.body(bytes)
})))
});
// body
let mut enc = BrotliEncoder::new(Vec::new(), 3);
enc.write_all(data.as_ref()).unwrap();
let enc = enc.finish().unwrap();
// client request
let mut response = srv
let mut res = srv
.post("/")
.append_header((actix_web::http::header::CONTENT_ENCODING, "br"))
.send_body(enc)
.append_header((header::CONTENT_ENCODING, "br"))
.send_body(brotli::encode(&data))
.await
.unwrap();
assert!(response.status().is_success());
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes, Bytes::from(data));
srv.stop().await;
@ -971,27 +759,20 @@ mod plus_rustls {
let srv = actix_test::start_with(actix_test::config().rustls(tls_config()), || {
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
HttpResponse::Ok()
.encoding(ContentEncoding::Identity)
.encode_with(ContentEncoding::Identity)
.body(bytes)
})))
});
// encode data
let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap();
// client request
let req = srv
.post("/")
.insert_header((actix_web::http::header::CONTENT_ENCODING, "deflate"))
.send_stream(TestBody::new(Bytes::from(enc), 1024));
.send_stream(TestBody::new(Bytes::from(deflate::encode(&data)), 1024));
let mut response = req.await.unwrap();
assert!(response.status().is_success());
let mut res = req.await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
// read response
let bytes = response.body().await.unwrap();
let bytes = res.body().await.unwrap();
assert_eq!(bytes.len(), data.len());
assert_eq!(bytes, Bytes::from(data));
@ -1084,8 +865,8 @@ async fn test_normalize() {
.service(web::resource("/one").route(web::to(HttpResponse::Ok)))
});
let response = srv.get("/one/").send().await.unwrap();
assert!(response.status().is_success());
let res = srv.get("/one/").send().await.unwrap();
assert_eq!(res.status(), StatusCode::OK);
srv.stop().await
}
@ -1148,15 +929,20 @@ async fn test_accept_encoding_no_match() {
.service(web::resource("/").route(web::to(move || HttpResponse::Ok().finish())))
});
let response = srv
let mut res = srv
.get("/")
.append_header((ACCEPT_ENCODING, "compress, identity;q=0"))
.insert_header((ACCEPT_ENCODING, "xz, identity;q=0"))
.no_decompress()
.send()
.await
.unwrap();
assert_eq!(response.status().as_u16(), 406);
assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
assert_eq!(res.headers().get(CONTENT_ENCODING), None);
let bytes = res.body().await.unwrap();
// body should contain the supported encodings
assert!(!bytes.is_empty());
srv.stop().await;
}

76
tests/test_utils.rs Normal file
View File

@ -0,0 +1,76 @@
// compiling some tests will trigger unused function warnings even though other tests use them
#![allow(dead_code)]
use std::io::{Read as _, Write as _};
pub mod gzip {
use super::*;
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), Compression::fast());
encoder.write_all(bytes.as_ref()).unwrap();
encoder.finish().unwrap()
}
pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
let mut decoder = GzDecoder::new(bytes.as_ref());
let mut buf = Vec::new();
decoder.read_to_end(&mut buf).unwrap();
buf
}
}
pub mod deflate {
use super::*;
use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::fast());
encoder.write_all(bytes.as_ref()).unwrap();
encoder.finish().unwrap()
}
pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
let mut decoder = ZlibDecoder::new(bytes.as_ref());
let mut buf = Vec::new();
decoder.read_to_end(&mut buf).unwrap();
buf
}
}
pub mod brotli {
use super::*;
use ::brotli2::{read::BrotliDecoder, write::BrotliEncoder};
pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
let mut encoder = BrotliEncoder::new(Vec::new(), 3);
encoder.write_all(bytes.as_ref()).unwrap();
encoder.finish().unwrap()
}
pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
let mut decoder = BrotliDecoder::new(bytes.as_ref());
let mut buf = Vec::new();
decoder.read_to_end(&mut buf).unwrap();
buf
}
}
pub mod zstd {
use super::*;
use ::zstd::stream::{read::Decoder, write::Encoder};
pub fn encode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
let mut encoder = Encoder::new(Vec::new(), 3).unwrap();
encoder.write_all(bytes.as_ref()).unwrap();
encoder.finish().unwrap()
}
pub fn decode(bytes: impl AsRef<[u8]>) -> Vec<u8> {
let mut decoder = Decoder::new(bytes.as_ref()).unwrap();
let mut buf = Vec::new();
decoder.read_to_end(&mut buf).unwrap();
buf
}
}