// Copied from
// https://mmstick.tk/post/jmP
// and andded the gzip(), deflate(), brotli(), and the check_*() methods
use rocket::request::{FromRequest, Request};
use rocket::http::Status;
use rocket::Outcome;
use std::collections::HashMap;
pub const GZIP: u8 = 1;
pub const DEFLATE: u8 = 2;
pub const BROTLI: u8 = 4;
#[derive(Clone, Debug, PartialEq, Hash)]
pub enum CompressionEncoding { Brotli, Gzip, Deflate, Uncompressed }
#[derive(Copy, Clone, Debug)]
pub struct AcceptCompression {
supported: u8
}
impl AcceptCompression {
pub fn new(supported: u8) -> AcceptCompression { AcceptCompression { supported } }
pub fn contains_gzip(self) -> bool { self.supported & GZIP != 0 }
pub fn contains_deflate(self) -> bool { self.supported & DEFLATE != 0 }
pub fn contains_brotli(self) -> bool { self.supported & BROTLI != 0 }
pub fn is_uncompressed(self) -> bool { self.supported == 0 }
// Consider maybe using &self instead of self??
pub fn preferred(self) -> CompressionEncoding {
if self.supported & GZIP != 0 {
CompressionEncoding::Gzip
} else if self.supported & BROTLI != 0 {
CompressionEncoding::Brotli
// Disabled deflate due to issues with old versions of IE
// } else if self.supported & DEFLATE != 0 {
// CompressionEncoding::Deflate
} else {
CompressionEncoding::Uncompressed
}
}
/// Returns a new AcceptCompression that specifies the gzip method
#[inline(always)]
pub fn gzip() -> Self { AcceptCompression { supported: GZIP } }
/// Returns a new AcceptCompression that specifies the deflate method
#[inline(always)]
pub fn deflate() -> Self { AcceptCompression { supported: DEFLATE } }
/// Returns a new AcceptCompression that specifies the brotli method
#[inline(always)]
pub fn brotli() -> Self { AcceptCompression { supported: BROTLI } }
/// Returns a new AcceptCompression that uses no compression
#[inline(always)]
pub fn no_compression() -> Self { AcceptCompression { supported: 0 } }
/// Returns a new AcceptCompression that specifies the gzip method
pub fn checked_gzip(&self) -> Self {
if self.contains_gzip() {
AcceptCompression {
supported: GZIP
}
} else {
AcceptCompression::no_compression()
}
}
/// Returns a new AcceptCompression that specifies the deflate method
pub fn checked_deflate(&self) -> Self {
if self.contains_deflate() {
AcceptCompression {
supported: DEFLATE
}
} else {
AcceptCompression::no_compression()
}
}
/// Returns a new AcceptCompression that specifies the brotli method
pub fn checked_brotli(&self) -> Self {
if self.contains_brotli() {
AcceptCompression {
supported: BROTLI
}
} else {
AcceptCompression::no_compression()
}
}
}
impl<'a, 'r> FromRequest<'a, 'r> for AcceptCompression {
type Error = ();
fn from_request(request: &'a Request<'r>) -> Outcome<AcceptCompression, (Status, ()), ()> {
let mut supported = 0u8;
let headers = request.headers();
if let Some(encoding) = headers.get("Accept-Encoding").next() {
if encoding.contains("gzip") { supported |= GZIP; }
if encoding.contains("deflate") { supported |= DEFLATE; }
if encoding.contains("br") { supported |= BROTLI; }
}
Outcome::Success(AcceptCompression { supported })
}
}