1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
use http::header::{HeaderMap, ACCEPT_ENCODING};
use super::error::FeatureError;
/// Represents a supported content encoding.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub enum ContentEncoding {
/// A format using the Brotli algorithm.
Br,
/// A format using the zlib structure with deflate algorithm.
Deflate,
/// Gzip algorithm.
Gzip,
/// Indicates no operation is done with encoding.
#[default]
NoOp,
}
impl ContentEncoding {
pub fn from_headers(headers: &HeaderMap) -> Self {
let mut prefer = ContentEncodingWithQValue::default();
for encoding in Self::_from_headers(headers) {
prefer.try_update(encoding);
}
prefer.enc
}
fn _from_headers(headers: &HeaderMap) -> impl Iterator<Item = ContentEncodingWithQValue> + '_ {
headers
.get_all(ACCEPT_ENCODING)
.iter()
.filter_map(|hval| hval.to_str().ok())
.flat_map(|s| s.split(','))
.filter_map(|v| {
let mut v = v.splitn(2, ';');
Self::try_parse(v.next().unwrap().trim()).ok().map(|enc| {
let val = v
.next()
.and_then(|v| QValue::parse(v.trim()))
.unwrap_or_else(QValue::one);
ContentEncodingWithQValue { enc, val }
})
})
}
pub(super) fn try_parse(s: &str) -> Result<Self, FeatureError> {
if s.eq_ignore_ascii_case("gzip") {
Ok(Self::Gzip)
} else if s.eq_ignore_ascii_case("deflate") {
Ok(Self::Deflate)
} else if s.eq_ignore_ascii_case("br") {
Ok(Self::Br)
} else if s.eq_ignore_ascii_case("identity") {
Ok(Self::NoOp)
} else {
Err(FeatureError::Unknown(s.to_string().into_boxed_str()))
}
}
}
struct ContentEncodingWithQValue {
enc: ContentEncoding,
val: QValue,
}
impl Default for ContentEncodingWithQValue {
fn default() -> Self {
Self {
enc: ContentEncoding::NoOp,
val: QValue::zero(),
}
}
}
impl ContentEncodingWithQValue {
fn try_update(&mut self, other: Self) {
if other.val > self.val {
match other.enc {
#[cfg(not(feature = "br"))]
ContentEncoding::Br => return,
#[cfg(not(feature = "de"))]
ContentEncoding::Deflate => return,
#[cfg(not(feature = "gz"))]
ContentEncoding::Gzip => return,
_ => {}
};
*self = other;
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct QValue(u16);
impl QValue {
const fn zero() -> Self {
Self(0)
}
const fn one() -> Self {
Self(1000)
}
// Parse a q-value as specified in RFC 7231 section 5.3.1.
fn parse(s: &str) -> Option<Self> {
let mut c = s.chars();
// Parse "q=" (case-insensitively).
match c.next() {
Some('q') | Some('Q') => (),
_ => return None,
};
match c.next() {
Some('=') => (),
_ => return None,
};
// Parse leading digit. Since valid q-values are between 0.000 and 1.000, only "0" and "1"
// are allowed.
let mut value = match c.next() {
Some('0') => 0,
Some('1') => 1000,
_ => return None,
};
// Parse optional decimal point.
match c.next() {
Some('.') => (),
None => return Some(Self(value)),
_ => return None,
};
// Parse optional fractional digits. The value of each digit is multiplied by `factor`.
// Since the q-value is represented as an integer between 0 and 1000, `factor` is `100` for
// the first digit, `10` for the next, and `1` for the digit after that.
let mut factor = 100;
loop {
match c.next() {
Some(n @ '0'..='9') => {
// If `factor` is less than `1`, three digits have already been parsed. A
// q-value having more than 3 fractional digits is invalid.
if factor < 1 {
return None;
}
// Add the digit's value multiplied by `factor` to `value`.
value += factor * (n as u16 - '0' as u16);
}
None => {
// No more characters to parse. Check that the value representing the q-value is
// in the valid range.
return if value <= 1000 { Some(Self(value)) } else { None };
}
_ => return None,
};
factor /= 10;
}
}
}