use flate2::read::{DeflateDecoder, GzDecoder};
use std::io::Read;
use crate::body::Body;
use crate::error::{Error, ErrorKind, Result};
use crate::header::HeaderMap;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum CompressionMode {
#[default]
Auto,
Manual,
Disabled,
}
impl CompressionMode {
pub(crate) fn should_add_accept_encoding(self) -> bool {
matches!(self, Self::Auto)
}
pub(crate) fn should_auto_decode(self) -> bool {
matches!(self, Self::Auto)
}
}
pub(crate) const DEFAULT_ACCEPT_ENCODING: &str = {
#[cfg(all(feature = "brotli", feature = "zstd"))]
{
"gzip, deflate, br, zstd"
}
#[cfg(all(feature = "brotli", not(feature = "zstd")))]
{
"gzip, deflate, br"
}
#[cfg(all(not(feature = "brotli"), feature = "zstd"))]
{
"gzip, deflate, zstd"
}
#[cfg(all(not(feature = "brotli"), not(feature = "zstd")))]
{
"gzip, deflate"
}
};
pub(crate) fn decode_response_body(headers: &mut HeaderMap, body: Vec<u8>) -> Result<Body> {
let Some(content_encoding) = headers.get("content-encoding") else {
return Ok(Body::from(body));
};
let decoded = match content_encoding {
"gzip" => decode_gzip(&body)?,
"deflate" => decode_deflate(&body)?,
#[cfg(feature = "brotli")]
"br" => decode_brotli(&body)?,
#[cfg(feature = "zstd")]
"zstd" => decode_zstd(&body)?,
_ => return Ok(Body::from(body)),
};
headers.remove("content-encoding");
headers.remove("content-length");
Ok(Body::from(decoded))
}
pub(crate) fn maybe_decode_response_body(
headers: &mut HeaderMap,
body: Vec<u8>,
compression_mode: CompressionMode,
) -> Result<Body> {
if compression_mode.should_auto_decode() {
decode_response_body(headers, body)
} else {
Ok(Body::from(body))
}
}
fn decode_gzip(body: &[u8]) -> Result<Vec<u8>> {
let mut decoder = GzDecoder::new(body);
let mut decoded = Vec::new();
decoder
.read_to_end(&mut decoded)
.map_err(|err| Error::with_source(ErrorKind::Decode, "failed to decode gzip body", err))?;
Ok(decoded)
}
fn decode_deflate(body: &[u8]) -> Result<Vec<u8>> {
let mut decoder = DeflateDecoder::new(body);
let mut decoded = Vec::new();
decoder.read_to_end(&mut decoded).map_err(|err| {
Error::with_source(ErrorKind::Decode, "failed to decode deflate body", err)
})?;
Ok(decoded)
}
#[cfg(feature = "brotli")]
fn decode_brotli(body: &[u8]) -> Result<Vec<u8>> {
let mut decoder = brotli::Decompressor::new(body, 4096);
let mut decoded = Vec::new();
decoder.read_to_end(&mut decoded).map_err(|err| {
Error::with_source(ErrorKind::Decode, "failed to decode brotli body", err)
})?;
Ok(decoded)
}
#[cfg(feature = "zstd")]
fn decode_zstd(body: &[u8]) -> Result<Vec<u8>> {
let mut decoder = zstd::stream::read::Decoder::new(body).map_err(|err| {
Error::with_source(ErrorKind::Decode, "failed to initialize zstd decoder", err)
})?;
let mut decoded = Vec::new();
decoder
.read_to_end(&mut decoded)
.map_err(|err| Error::with_source(ErrorKind::Decode, "failed to decode zstd body", err))?;
Ok(decoded)
}