+use brotli;
use std::{
io::{Cursor, Write},
time::Instant,
Zlib,
/// LZMA compression.
Lzma,
+ /// Brotli compression.
+ Brotli,
}
/// The outer decompressor tracks the number of callbacks and time spent
HtpContentEncoding::Gzip
| HtpContentEncoding::Deflate
| HtpContentEncoding::Zlib
+ | HtpContentEncoding::Brotli
| HtpContentEncoding::Lzma => Ok(Decompressor::new(Box::new(InnerDecompressor::new(
encoding, self.inner, options,
)?))),
}
}
+/// Simple wrapper around an lzma implementation
+struct BrotliBufWriter(brotli::DecompressorWriter<Cursor<Box<[u8]>>>);
+
+impl Write for BrotliBufWriter {
+ fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
+ self.0.write(data)
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ self.0.flush()
+ }
+}
+
+impl BufWriter for BrotliBufWriter {
+ fn get_mut(&mut self) -> Option<&mut Cursor<Box<[u8]>>> {
+ Some(self.0.get_mut())
+ }
+
+ fn finish(self: Box<Self>) -> std::io::Result<Cursor<Box<[u8]>>> {
+ self.0
+ .into_inner()
+ .map_err(|_e| std::io::Error::new(std::io::ErrorKind::Other, "brotli"))
+ }
+
+ fn try_finish(&mut self) -> std::io::Result<()> {
+ Ok(())
+ }
+}
+
/// Structure that represents each decompressor in the chain.
struct InnerDecompressor {
/// Decoder implementation that will write to a temporary buffer.
Box::new(ZlibBufWriter(flate2::write::ZlibDecoder::new(buf))),
false,
)),
+ HtpContentEncoding::Brotli => Ok((
+ Box::new(BrotliBufWriter(brotli::DecompressorWriter::new(
+ buf,
+ ENCODING_CHUNK_SIZE,
+ ))),
+ false,
+ )),
HtpContentEncoding::Lzma => {
if let Some(options) = options.lzma {
Ok((
HtpContentEncoding::Gzip => HtpContentEncoding::Deflate,
HtpContentEncoding::Deflate => HtpContentEncoding::Zlib,
HtpContentEncoding::Zlib => HtpContentEncoding::Gzip,
+ // For other encodings, we retry with deflate, zlib and gzip
HtpContentEncoding::Lzma => HtpContentEncoding::Deflate,
+ HtpContentEncoding::Brotli => HtpContentEncoding::Deflate,
HtpContentEncoding::None => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
HtpContentEncoding::Gzip
| HtpContentEncoding::Deflate
| HtpContentEncoding::Zlib
+ | HtpContentEncoding::Brotli
| HtpContentEncoding::Lzma => {
// Send data buffer to the decompressor if it exists
if req.request_decompressor.is_none() && data.is_none() {
HtpContentEncoding::Gzip
} else if ce.cmp_nocase_nozero(b"deflate") || ce.cmp_nocase_nozero(b"x-deflate") {
HtpContentEncoding::Deflate
+ } else if ce.cmp_nocase_nozero(b"br") {
+ HtpContentEncoding::Brotli
} else if ce.cmp_nocase_nozero(b"lzma") {
HtpContentEncoding::Lzma
} else if ce.cmp_nocase_nozero(b"inflate") || ce.cmp_nocase_nozero(b"none") {
HtpContentEncoding::Gzip
| HtpContentEncoding::Deflate
| HtpContentEncoding::Zlib
+ | HtpContentEncoding::Brotli
| HtpContentEncoding::Lzma => {
self.request_prepend_decompressor(request_content_encoding_processing)?;
}
HtpContentEncoding::Gzip
| HtpContentEncoding::Deflate
| HtpContentEncoding::Zlib
+ | HtpContentEncoding::Brotli
| HtpContentEncoding::Lzma => {
// Send data buffer to the decompressor if it exists
if resp.response_decompressor.is_none() && data.is_none() {
HtpContentEncoding::Deflate
} else if ce.cmp_nocase_nozero(b"lzma") {
HtpContentEncoding::Lzma
+ } else if ce.cmp_nocase_nozero(b"br") {
+ HtpContentEncoding::Brotli
} else if ce.cmp_nocase_nozero(b"inflate") || ce.cmp_nocase_nozero(b"none") {
HtpContentEncoding::None
} else {
HtpContentEncoding::Gzip
| HtpContentEncoding::Deflate
| HtpContentEncoding::Zlib
+ | HtpContentEncoding::Brotli
| HtpContentEncoding::Lzma => {
self.response_prepend_decompressor(response_content_encoding_processing)?;
}