1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-04-20 04:17:01 +02:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Rob Ede
bc54cdd772
prepare actix-http release 2.2.2 2022-01-21 21:23:22 +00:00
Rob Ede
ad7e3c06e7
migrate to brotli crate 2022-01-21 21:16:23 +00:00
Rob Ede
0669ed0f06
soft-deprecate NormalizePath::default in v3 (#2529) 2021-12-18 22:57:23 +00:00
Rob Ede
c9c36679e4
bump actix http version 2021-08-11 19:21:40 +01:00
Rob Ede
655d7b4f05
sec fixes 2021-08-08 21:21:48 +01:00
15 changed files with 133 additions and 62 deletions

View File

@ -3,6 +3,13 @@
## Unreleased - 2020-xx-xx ## Unreleased - 2020-xx-xx
## 3.3.3 - 2021-12-18
### Changed
* Soft-deprecate `NormalizePath::default()`, noting upcoming behavior change in v4. [#2529]
[#2529]: https://github.com/actix/actix-web/pull/2529
## 3.3.2 - 2020-12-01 ## 3.3.2 - 2020-12-01
### Fixed ### Fixed
* Removed an occasional `unwrap` on `None` panic in `NormalizePathNormalization`. [#1762] * Removed an occasional `unwrap` on `None` panic in `NormalizePathNormalization`. [#1762]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-web" name = "actix-web"
version = "3.3.2" version = "3.3.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust" description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust"
readme = "README.md" readme = "README.md"
@ -115,7 +115,7 @@ actix-http = { version = "2.1.0", features = ["actors"] }
rand = "0.7" rand = "0.7"
env_logger = "0.8" env_logger = "0.8"
serde_derive = "1.0" serde_derive = "1.0"
brotli2 = "0.3.2" brotli = "3.3.3"
flate2 = "1.0.13" flate2 = "1.0.13"
criterion = "0.3" criterion = "0.3"

View File

@ -6,10 +6,10 @@
<p> <p>
[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web) [![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web)
[![Documentation](https://docs.rs/actix-web/badge.svg?version=3.3.2)](https://docs.rs/actix-web/3.3.2) [![Documentation](https://docs.rs/actix-web/badge.svg?version=3.3.3)](https://docs.rs/actix-web/3.3.3)
[![Version](https://img.shields.io/badge/rustc-1.42+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.42.html) [![Version](https://img.shields.io/badge/rustc-1.42+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.42.html)
![License](https://img.shields.io/crates/l/actix-web.svg) ![License](https://img.shields.io/crates/l/actix-web.svg)
[![Dependency Status](https://deps.rs/crate/actix-web/3.3.2/status.svg)](https://deps.rs/crate/actix-web/3.3.2) [![Dependency Status](https://deps.rs/crate/actix-web/3.3.3/status.svg)](https://deps.rs/crate/actix-web/3.3.3)
<br /> <br />
[![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web) [![Build Status](https://travis-ci.org/actix/actix-web.svg?branch=master)](https://travis-ci.org/actix/actix-web)
[![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web)

View File

@ -3,6 +3,18 @@
## Unreleased - 2020-xx-xx ## Unreleased - 2020-xx-xx
## 2.2.2 - 2022-01-21
### Changed
- Migrate to `brotli` crate. [ad7e3c06]
[ad7e3c06]: https://github.com/actix/actix-web/commit/ad7e3c06
## 2.2.1 - 2021-08-09
### Fixed
- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977)
## 2.2.0 - 2020-11-25 ## 2.2.0 - 2020-11-25
### Added ### Added
* HttpResponse builders for 1xx status codes. [#1768] * HttpResponse builders for 1xx status codes. [#1768]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-http" name = "actix-http"
version = "2.2.0" version = "2.2.2"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "HTTP primitives for the Actix ecosystem" description = "HTTP primitives for the Actix ecosystem"
readme = "README.md" readme = "README.md"
@ -31,7 +31,7 @@ openssl = ["actix-tls/openssl", "actix-connect/openssl"]
rustls = ["actix-tls/rustls", "actix-connect/rustls"] rustls = ["actix-tls/rustls", "actix-connect/rustls"]
# enable compressison support # enable compressison support
compress = ["flate2", "brotli2"] compress = ["flate2", "brotli"]
# support for secure cookies # support for secure cookies
secure-cookies = ["cookie/secure"] secure-cookies = ["cookie/secure"]
@ -82,7 +82,7 @@ serde_urlencoded = "0.7"
time = { version = "0.2.7", default-features = false, features = ["std"] } time = { version = "0.2.7", default-features = false, features = ["std"] }
# compression # compression
brotli2 = { version="0.3.2", optional = true } brotli = { version = "3.3.3", optional = true }
flate2 = { version = "1.0.13", optional = true } flate2 = { version = "1.0.13", optional = true }
[dev-dependencies] [dev-dependencies]

View File

@ -3,9 +3,9 @@
> HTTP primitives for the Actix ecosystem. > HTTP primitives for the Actix ecosystem.
[![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http) [![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http)
[![Documentation](https://docs.rs/actix-http/badge.svg?version=2.2.0)](https://docs.rs/actix-http/2.2.0) [![Documentation](https://docs.rs/actix-http/badge.svg?version=2.2.2)](https://docs.rs/actix-http/2.2.2)
![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-http) ![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-http)
[![Dependency Status](https://deps.rs/crate/actix-http/2.2.0/status.svg)](https://deps.rs/crate/actix-http/2.2.0) [![Dependency Status](https://deps.rs/crate/actix-http/2.2.2/status.svg)](https://deps.rs/crate/actix-http/2.2.2)
[![Join the chat at https://gitter.im/actix/actix-web](https://badges.gitter.im/actix/actix-web.svg)](https://gitter.im/actix/actix-web?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/actix/actix-web](https://badges.gitter.im/actix/actix-web.svg)](https://gitter.im/actix/actix-web?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Documentation & Resources ## Documentation & Resources

View File

@ -4,7 +4,7 @@ use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_threadpool::{run, CpuFuture}; use actix_threadpool::{run, CpuFuture};
use brotli2::write::BrotliDecoder; use brotli::DecompressorWriter as BrotliDecoder;
use bytes::Bytes; use bytes::Bytes;
use flate2::write::{GzDecoder, ZlibDecoder}; use flate2::write::{GzDecoder, ZlibDecoder};
use futures_core::{ready, Stream}; use futures_core::{ready, Stream};
@ -31,7 +31,7 @@ where
pub fn new(stream: S, encoding: ContentEncoding) -> Decoder<S> { pub fn new(stream: S, encoding: ContentEncoding) -> Decoder<S> {
let decoder = match encoding { let decoder = match encoding {
ContentEncoding::Br => Some(ContentDecoder::Br(Box::new( ContentEncoding::Br => Some(ContentDecoder::Br(Box::new(
BrotliDecoder::new(Writer::new()), BrotliDecoder::new(Writer::new(), 8 * 1024),
))), ))),
ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new( ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new(
ZlibDecoder::new(Writer::new()), ZlibDecoder::new(Writer::new()),

View File

@ -5,7 +5,7 @@ use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_threadpool::{run, CpuFuture}; use actix_threadpool::{run, CpuFuture};
use brotli2::write::BrotliEncoder; use brotli::CompressorWriter as BrotliEncoder;
use bytes::Bytes; use bytes::Bytes;
use flate2::write::{GzEncoder, ZlibEncoder}; use flate2::write::{GzEncoder, ZlibEncoder};
use futures_core::ready; use futures_core::ready;
@ -212,9 +212,12 @@ impl ContentEncoder {
Writer::new(), Writer::new(),
flate2::Compression::fast(), flate2::Compression::fast(),
))), ))),
ContentEncoding::Br => { ContentEncoding::Br => Some(ContentEncoder::Br(BrotliEncoder::new(
Some(ContentEncoder::Br(BrotliEncoder::new(Writer::new(), 3))) Writer::new(),
} 32 * 1024,
3,
22,
))),
_ => None, _ => None,
} }
} }
@ -230,8 +233,8 @@ impl ContentEncoder {
fn finish(self) -> Result<Bytes, io::Error> { fn finish(self) -> Result<Bytes, io::Error> {
match self { match self {
ContentEncoder::Br(encoder) => match encoder.finish() { ContentEncoder::Br(mut encoder) => match encoder.flush() {
Ok(writer) => Ok(writer.buf.freeze()), Ok(()) => Ok(encoder.into_inner().buf.freeze()),
Err(err) => Err(err), Err(err) => Err(err),
}, },
ContentEncoder::Gzip(encoder) => match encoder.finish() { ContentEncoder::Gzip(encoder) => match encoder.finish() {

View File

@ -55,7 +55,7 @@ impl Error {
/// Similar to `as_response_error` but downcasts. /// Similar to `as_response_error` but downcasts.
pub fn as_error<T: ResponseError + 'static>(&self) -> Option<&T> { pub fn as_error<T: ResponseError + 'static>(&self) -> Option<&T> {
ResponseError::downcast_ref(self.cause.as_ref()) <dyn ResponseError>::downcast_ref(self.cause.as_ref())
} }
} }

View File

@ -67,6 +67,7 @@ pub(crate) trait MessageType: Sized {
let mut has_upgrade_websocket = false; let mut has_upgrade_websocket = false;
let mut expect = false; let mut expect = false;
let mut chunked = false; let mut chunked = false;
let mut seen_te = false;
let mut content_length = None; let mut content_length = None;
{ {
@ -85,8 +86,17 @@ pub(crate) trait MessageType: Sized {
}; };
match name { match name {
header::CONTENT_LENGTH => { header::CONTENT_LENGTH if content_length.is_some() => {
if let Ok(s) = value.to_str() { debug!("multiple Content-Length");
return Err(ParseError::Header);
}
header::CONTENT_LENGTH => match value.to_str() {
Ok(s) if s.trim().starts_with("+") => {
debug!("illegal Content-Length: {:?}", s);
return Err(ParseError::Header);
}
Ok(s) => {
if let Ok(len) = s.parse::<u64>() { if let Ok(len) = s.parse::<u64>() {
if len != 0 { if len != 0 {
content_length = Some(len); content_length = Some(len);
@ -95,15 +105,31 @@ pub(crate) trait MessageType: Sized {
debug!("illegal Content-Length: {:?}", s); debug!("illegal Content-Length: {:?}", s);
return Err(ParseError::Header); return Err(ParseError::Header);
} }
} else { }
Err(_) => {
debug!("illegal Content-Length: {:?}", value); debug!("illegal Content-Length: {:?}", value);
return Err(ParseError::Header); return Err(ParseError::Header);
} }
} },
// transfer-encoding // transfer-encoding
header::TRANSFER_ENCODING if seen_te => {
debug!("multiple Transfer-Encoding not allowed");
return Err(ParseError::Header);
}
header::TRANSFER_ENCODING => { header::TRANSFER_ENCODING => {
seen_te = true;
if let Ok(s) = value.to_str().map(|s| s.trim()) { if let Ok(s) = value.to_str().map(|s| s.trim()) {
chunked = s.eq_ignore_ascii_case("chunked"); if s.eq_ignore_ascii_case("chunked") {
chunked = true;
} else if s.eq_ignore_ascii_case("identity") {
// allow silently since multiple TE headers are already checked
} else {
debug!("illegal Transfer-Encoding: {:?}", s);
return Err(ParseError::Header);
}
} else { } else {
return Err(ParseError::Header); return Err(ParseError::Header);
} }
@ -510,19 +536,11 @@ impl ChunkedState {
size: &mut u64, size: &mut u64,
) -> Poll<Result<ChunkedState, io::Error>> { ) -> Poll<Result<ChunkedState, io::Error>> {
let radix = 16; let radix = 16;
match byte!(rdr) {
b @ b'0'..=b'9' => { let rem = match byte!(rdr) {
*size *= radix; b @ b'0'..=b'9' => b - b'0',
*size += u64::from(b - b'0'); b @ b'a'..=b'f' => b + 10 - b'a',
} b @ b'A'..=b'F' => b + 10 - b'A',
b @ b'a'..=b'f' => {
*size *= radix;
*size += u64::from(b + 10 - b'a');
}
b @ b'A'..=b'F' => {
*size *= radix;
*size += u64::from(b + 10 - b'A');
}
b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)), b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)),
b';' => return Poll::Ready(Ok(ChunkedState::Extension)), b';' => return Poll::Ready(Ok(ChunkedState::Extension)),
b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)), b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),
@ -532,8 +550,23 @@ impl ChunkedState {
"Invalid chunk size line: Invalid Size", "Invalid chunk size line: Invalid Size",
))); )));
} }
};
match size.checked_mul(radix) {
Some(n) => {
*size = n as u64;
*size += rem as u64;
Poll::Ready(Ok(ChunkedState::Size))
}
None => {
debug!("chunk size would overflow");
Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size line: Invalid Size",
)))
}
} }
Poll::Ready(Ok(ChunkedState::Size))
} }
fn read_size_lws(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> { fn read_size_lws(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
@ -552,6 +585,11 @@ impl ChunkedState {
fn read_extension(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> { fn read_extension(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
match byte!(rdr) { match byte!(rdr) {
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)), b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
// strictly 0x20 (space) should be disallowed but we don't parse quoted strings here
0x00..=0x08 | 0x0a..=0x1f | 0x7f => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid character in chunk extension",
))),
_ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions _ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions
} }
} }
@ -977,13 +1015,7 @@ mod tests {
"GET /test HTTP/1.1\r\n\ "GET /test HTTP/1.1\r\n\
transfer-encoding: chnked\r\n\r\n", transfer-encoding: chnked\r\n\r\n",
); );
let req = parse_ready!(&mut buf); expect_parse_err!(&mut buf);
if let Ok(val) = req.chunked() {
assert!(!val);
} else {
unreachable!("Error");
}
} }
#[test] #[test]

View File

@ -80,6 +80,7 @@ pub(crate) trait MessageType: Sized {
match length { match length {
BodySize::Stream => { BodySize::Stream => {
if chunked { if chunked {
skip_len = true;
if camel_case { if camel_case {
dst.put_slice(b"\r\nTransfer-Encoding: chunked\r\n") dst.put_slice(b"\r\nTransfer-Encoding: chunked\r\n")
} else { } else {

View File

@ -65,7 +65,7 @@ actix-http-test = { version = "2.0.0", features = ["openssl"] }
actix-utils = "2.0.0" actix-utils = "2.0.0"
actix-server = "1.0.0" actix-server = "1.0.0"
actix-tls = { version = "2.0.0", features = ["openssl", "rustls"] } actix-tls = { version = "2.0.0", features = ["openssl", "rustls"] }
brotli2 = "0.3.2" brotli = "3.3.3"
flate2 = "1.0.13" flate2 = "1.0.13"
futures-util = { version = "0.3.5", default-features = false } futures-util = { version = "0.3.5", default-features = false }
env_logger = "0.7" env_logger = "0.7"

View File

@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use brotli2::write::BrotliEncoder; use brotli::CompressorWriter as BrotliEncoder;
use bytes::Bytes; use bytes::Bytes;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use flate2::write::GzEncoder; use flate2::write::GzEncoder;
@ -506,9 +506,10 @@ async fn test_client_gzip_encoding_large_random() {
async fn test_client_brotli_encoding() { async fn test_client_brotli_encoding() {
let srv = test::start(|| { let srv = test::start(|| {
App::new().service(web::resource("/").route(web::to(|data: Bytes| { App::new().service(web::resource("/").route(web::to(|data: Bytes| {
let mut e = BrotliEncoder::new(Vec::new(), 5); let mut e = BrotliEncoder::new(Vec::new(), 8096, 5, 22);
e.write_all(&data).unwrap(); e.write_all(&data).unwrap();
let data = e.finish().unwrap(); e.flush().unwrap();
let data = e.into_inner();
HttpResponse::Ok() HttpResponse::Ok()
.header("content-encoding", "br") .header("content-encoding", "br")
.body(data) .body(data)
@ -533,9 +534,9 @@ async fn test_client_brotli_encoding_large_random() {
let srv = test::start(|| { let srv = test::start(|| {
App::new().service(web::resource("/").route(web::to(|data: Bytes| { App::new().service(web::resource("/").route(web::to(|data: Bytes| {
let mut e = BrotliEncoder::new(Vec::new(), 5); let mut e = BrotliEncoder::new(Vec::new(), 8096, 5, 22);
e.write_all(&data).unwrap(); e.write_all(&data).unwrap();
let data = e.finish().unwrap(); let data = e.into_inner();
HttpResponse::Ok() HttpResponse::Ok()
.header("content-encoding", "br") .header("content-encoding", "br")
.body(data) .body(data)

View File

@ -31,7 +31,7 @@ impl Default for TrailingSlash {
} }
} }
#[derive(Default, Clone, Copy)] #[derive(Clone, Copy)]
/// `Middleware` to normalize request's URI in place /// `Middleware` to normalize request's URI in place
/// ///
/// Performs following: /// Performs following:
@ -56,6 +56,18 @@ impl Default for TrailingSlash {
pub struct NormalizePath(TrailingSlash); pub struct NormalizePath(TrailingSlash);
impl Default for NormalizePath {
fn default() -> Self {
log::warn!(
"`NormalizePath::default()` is deprecated. The default trailing slash behavior will \
change in v4 from `Always` to `Trim`. Update your call to `NormalizePath::new(...)` to \
avoid inaccessible routes when upgrading."
);
Self(TrailingSlash::default())
}
}
impl NormalizePath { impl NormalizePath {
/// Create new `NormalizePath` middleware with the specified trailing slash style. /// Create new `NormalizePath` middleware with the specified trailing slash style.
pub fn new(trailing_slash_style: TrailingSlash) -> Self { pub fn new(trailing_slash_style: TrailingSlash) -> Self {

View File

@ -7,7 +7,8 @@ use actix_http::http::header::{
ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH,
TRANSFER_ENCODING, TRANSFER_ENCODING,
}; };
use brotli2::write::{BrotliDecoder, BrotliEncoder};
use brotli::{CompressorWriter as BrotliEncoder, DecompressorWriter as BrotliDecoder};
use bytes::Bytes; use bytes::Bytes;
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder};
@ -340,9 +341,9 @@ async fn test_body_br_streaming() {
println!("TEST: {:?}", bytes.len()); println!("TEST: {:?}", bytes.len());
// decode br // decode br
let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); let mut e = BrotliDecoder::new(Vec::with_capacity(2048), 8096);
e.write_all(bytes.as_ref()).unwrap(); e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap(); let dec = e.into_inner().unwrap();
println!("T: {:?}", Bytes::copy_from_slice(&dec)); println!("T: {:?}", Bytes::copy_from_slice(&dec));
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
} }
@ -439,9 +440,9 @@ async fn test_body_brotli() {
let bytes = response.body().await.unwrap(); let bytes = response.body().await.unwrap();
// decode brotli // decode brotli
let mut e = BrotliDecoder::new(Vec::with_capacity(2048)); let mut e = BrotliDecoder::new(Vec::with_capacity(2048), 8096);
e.write_all(bytes.as_ref()).unwrap(); e.write_all(bytes.as_ref()).unwrap();
let dec = e.finish().unwrap(); let dec = e.into_inner().unwrap();
assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref())); assert_eq!(Bytes::from(dec), Bytes::from_static(STR.as_ref()));
} }
@ -650,9 +651,10 @@ async fn test_brotli_encoding() {
) )
}); });
let mut e = BrotliEncoder::new(Vec::new(), 5); let mut e = BrotliEncoder::new(Vec::new(), 8096, 5, 22);
e.write_all(STR.as_ref()).unwrap(); e.write_all(STR.as_ref()).unwrap();
let enc = e.finish().unwrap(); e.flush().unwrap();
let enc = e.into_inner();
// client request // client request
let request = srv let request = srv
@ -684,9 +686,10 @@ async fn test_brotli_encoding_large() {
) )
}); });
let mut e = BrotliEncoder::new(Vec::new(), 5); let mut e = BrotliEncoder::new(Vec::new(), 8096, 5, 22);
e.write_all(data.as_ref()).unwrap(); e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap(); e.flush().unwrap();
let enc = e.into_inner();
// client request // client request
let request = srv let request = srv
@ -724,9 +727,9 @@ async fn test_brotli_encoding_large_openssl() {
}); });
// body // body
let mut e = BrotliEncoder::new(Vec::new(), 3); let mut e = BrotliEncoder::new(Vec::new(), 8096, 5, 22);
e.write_all(data.as_ref()).unwrap(); e.write_all(data.as_ref()).unwrap();
let enc = e.finish().unwrap(); let enc = e.into_inner();
// client request // client request
let mut response = srv let mut response = srv