leyline-http 0.1.5

HTTP client that impersonates Chrome, Firefox, and Safari on the wire: TLS (JA3/JA4), HTTP/2, and HTTP/3 fingerprints
Documentation
#[cfg(not(all(
    feature = "compression-gzip",
    feature = "compression-brotli",
    feature = "compression-zstd",
    feature = "compression-deflate"
)))]
use crate::core::error::Kind;
use crate::core::error::{Error, Result};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ContentEncoding {
    Gzip,
    Brotli,
    Zstd,
    Deflate,
}

impl ContentEncoding {
    pub(crate) fn header_value(self) -> &'static str {
        match self {
            ContentEncoding::Gzip => "gzip",
            ContentEncoding::Brotli => "br",
            ContentEncoding::Zstd => "zstd",
            ContentEncoding::Deflate => "deflate",
        }
    }

    pub(crate) fn encode(self, data: &[u8]) -> Result<Vec<u8>> {
        match self {
            ContentEncoding::Gzip => gzip(data),
            ContentEncoding::Brotli => brotli(data),
            ContentEncoding::Zstd => zstd(data),
            ContentEncoding::Deflate => deflate(data),
        }
    }
}

#[cfg(feature = "compression-gzip")]
fn gzip(data: &[u8]) -> Result<Vec<u8>> {
    use std::io::Write;
    let mut enc = flate2::write::GzEncoder::new(
        Vec::with_capacity(data.len() / 2),
        flate2::Compression::default(),
    );
    enc.write_all(data).map_err(Error::from)?;
    enc.finish().map_err(Error::from)
}

#[cfg(not(feature = "compression-gzip"))]
fn gzip(_data: &[u8]) -> Result<Vec<u8>> {
    Err(feature_off("gzip", "compression-gzip"))
}

#[cfg(feature = "compression-brotli")]
fn brotli(data: &[u8]) -> Result<Vec<u8>> {
    use std::io::Read;
    let mut out = Vec::with_capacity(data.len() / 2);
    brotli::CompressorReader::new(data, 4096, 5, 22)
        .read_to_end(&mut out)
        .map_err(Error::from)?;
    Ok(out)
}

#[cfg(not(feature = "compression-brotli"))]
fn brotli(_data: &[u8]) -> Result<Vec<u8>> {
    Err(feature_off("brotli", "compression-brotli"))
}

#[cfg(feature = "compression-zstd")]
fn zstd(data: &[u8]) -> Result<Vec<u8>> {
    zstd::encode_all(data, 3).map_err(Error::from)
}

#[cfg(not(feature = "compression-zstd"))]
fn zstd(_data: &[u8]) -> Result<Vec<u8>> {
    Err(feature_off("zstd", "compression-zstd"))
}

#[cfg(feature = "compression-deflate")]
fn deflate(data: &[u8]) -> Result<Vec<u8>> {
    use std::io::Write;
    let mut enc = flate2::write::ZlibEncoder::new(
        Vec::with_capacity(data.len() / 2),
        flate2::Compression::default(),
    );
    enc.write_all(data).map_err(Error::from)?;
    enc.finish().map_err(Error::from)
}

#[cfg(not(feature = "compression-deflate"))]
fn deflate(_data: &[u8]) -> Result<Vec<u8>> {
    Err(feature_off("deflate", "compression-deflate"))
}

#[cfg(not(all(
    feature = "compression-gzip",
    feature = "compression-brotli",
    feature = "compression-zstd",
    feature = "compression-deflate"
)))]
fn feature_off(codec: &str, feature: &str) -> Error {
    Error::new(Kind::Body).with_message(format!(
        "{codec} request compression requested but the `{feature}` feature is not compiled in"
    ))
}

#[cfg(test)]
mod tests;