http_types/transfer/
te.rsuse crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ACCEPT_ENCODING};
use crate::transfer::{Encoding, EncodingProposal, TransferEncoding};
use crate::utils::sort_by_weight;
use crate::{Error, StatusCode};
use std::fmt::{self, Debug, Write};
use std::option;
use std::slice;
#[allow(clippy::upper_case_acronyms)]
pub struct TE {
wildcard: bool,
entries: Vec<EncodingProposal>,
}
impl TE {
pub fn new() -> Self {
Self {
entries: vec![],
wildcard: false,
}
}
pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
let mut entries = vec![];
let headers = match headers.as_ref().get(ACCEPT_ENCODING) {
Some(headers) => headers,
None => return Ok(None),
};
let mut wildcard = false;
for value in headers {
for part in value.as_str().trim().split(',') {
let part = part.trim();
if part.is_empty() {
continue;
} else if part == "*" {
wildcard = true;
continue;
}
if let Some(entry) = EncodingProposal::from_str(part)? {
entries.push(entry);
}
}
}
Ok(Some(Self { entries, wildcard }))
}
pub fn push(&mut self, prop: impl Into<EncodingProposal>) {
self.entries.push(prop.into());
}
pub fn wildcard(&self) -> bool {
self.wildcard
}
pub fn set_wildcard(&mut self, wildcard: bool) {
self.wildcard = wildcard
}
pub fn sort(&mut self) {
sort_by_weight(&mut self.entries);
}
pub fn negotiate(&mut self, available: &[Encoding]) -> crate::Result<TransferEncoding> {
self.sort();
for encoding in &self.entries {
if available.contains(encoding) {
return Ok(encoding.into());
}
}
if self.wildcard {
if let Some(encoding) = available.iter().next() {
return Ok(encoding.into());
}
}
let mut err = Error::new_adhoc("No suitable Transfer-Encoding found");
err.set_status(StatusCode::NotAcceptable);
Err(err)
}
pub fn apply(&self, mut headers: impl AsMut<Headers>) {
headers.as_mut().insert(ACCEPT_ENCODING, self.value());
}
pub fn name(&self) -> HeaderName {
ACCEPT_ENCODING
}
pub fn value(&self) -> HeaderValue {
let mut output = String::new();
for (n, directive) in self.entries.iter().enumerate() {
let directive: HeaderValue = (*directive).into();
match n {
0 => write!(output, "{}", directive).unwrap(),
_ => write!(output, ", {}", directive).unwrap(),
};
}
if self.wildcard {
match output.len() {
0 => write!(output, "*").unwrap(),
_ => write!(output, ", *").unwrap(),
}
}
unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
}
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self.entries.iter(),
}
}
pub fn iter_mut(&mut self) -> IterMut<'_> {
IterMut {
inner: self.entries.iter_mut(),
}
}
}
impl IntoIterator for TE {
type Item = EncodingProposal;
type IntoIter = IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter {
inner: self.entries.into_iter(),
}
}
}
impl<'a> IntoIterator for &'a TE {
type Item = &'a EncodingProposal;
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a mut TE {
type Item = &'a mut EncodingProposal;
type IntoIter = IterMut<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
#[derive(Debug)]
pub struct IntoIter {
inner: std::vec::IntoIter<EncodingProposal>,
}
impl Iterator for IntoIter {
type Item = EncodingProposal;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Debug)]
pub struct Iter<'a> {
inner: slice::Iter<'a, EncodingProposal>,
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a EncodingProposal;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
#[derive(Debug)]
pub struct IterMut<'a> {
inner: slice::IterMut<'a, EncodingProposal>,
}
impl<'a> Iterator for IterMut<'a> {
type Item = &'a mut EncodingProposal;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ToHeaderValues for TE {
type Iter = option::IntoIter<HeaderValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
Ok(self.value().to_header_values().unwrap())
}
}
impl Debug for TE {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut list = f.debug_list();
for directive in &self.entries {
list.entry(directive);
}
list.finish()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::transfer::Encoding;
use crate::Response;
#[test]
fn smoke() -> crate::Result<()> {
let mut accept = TE::new();
accept.push(Encoding::Gzip);
let mut headers = Response::new(200);
accept.apply(&mut headers);
let accept = TE::from_headers(headers)?.unwrap();
assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip);
Ok(())
}
#[test]
fn wildcard() -> crate::Result<()> {
let mut accept = TE::new();
accept.set_wildcard(true);
let mut headers = Response::new(200);
accept.apply(&mut headers);
let accept = TE::from_headers(headers)?.unwrap();
assert!(accept.wildcard());
Ok(())
}
#[test]
fn wildcard_and_header() -> crate::Result<()> {
let mut accept = TE::new();
accept.push(Encoding::Gzip);
accept.set_wildcard(true);
let mut headers = Response::new(200);
accept.apply(&mut headers);
let accept = TE::from_headers(headers)?.unwrap();
assert!(accept.wildcard());
assert_eq!(accept.iter().next().unwrap(), Encoding::Gzip);
Ok(())
}
#[test]
fn iter() -> crate::Result<()> {
let mut accept = TE::new();
accept.push(Encoding::Gzip);
accept.push(Encoding::Brotli);
let mut headers = Response::new(200);
accept.apply(&mut headers);
let accept = TE::from_headers(headers)?.unwrap();
let mut accept = accept.iter();
assert_eq!(accept.next().unwrap(), Encoding::Gzip);
assert_eq!(accept.next().unwrap(), Encoding::Brotli);
Ok(())
}
#[test]
fn reorder_based_on_weight() -> crate::Result<()> {
let mut accept = TE::new();
accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?);
accept.push(EncodingProposal::new(Encoding::Identity, None)?);
accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
let mut headers = Response::new(200);
accept.apply(&mut headers);
let mut accept = TE::from_headers(headers)?.unwrap();
accept.sort();
let mut accept = accept.iter();
assert_eq!(accept.next().unwrap(), Encoding::Brotli);
assert_eq!(accept.next().unwrap(), Encoding::Gzip);
assert_eq!(accept.next().unwrap(), Encoding::Identity);
Ok(())
}
#[test]
fn reorder_based_on_weight_and_location() -> crate::Result<()> {
let mut accept = TE::new();
accept.push(EncodingProposal::new(Encoding::Identity, None)?);
accept.push(EncodingProposal::new(Encoding::Gzip, None)?);
accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
let mut res = Response::new(200);
accept.apply(&mut res);
let mut accept = TE::from_headers(res)?.unwrap();
accept.sort();
let mut accept = accept.iter();
assert_eq!(accept.next().unwrap(), Encoding::Brotli);
assert_eq!(accept.next().unwrap(), Encoding::Gzip);
assert_eq!(accept.next().unwrap(), Encoding::Identity);
Ok(())
}
#[test]
fn negotiate() -> crate::Result<()> {
let mut accept = TE::new();
accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
accept.push(EncodingProposal::new(Encoding::Gzip, Some(0.4))?);
accept.push(EncodingProposal::new(Encoding::Identity, None)?);
assert_eq!(
accept.negotiate(&[Encoding::Brotli, Encoding::Gzip])?,
Encoding::Brotli,
);
Ok(())
}
#[test]
fn negotiate_not_acceptable() -> crate::Result<()> {
let mut accept = TE::new();
let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err();
assert_eq!(err.status(), 406);
let mut accept = TE::new();
accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
let err = accept.negotiate(&[Encoding::Gzip]).unwrap_err();
assert_eq!(err.status(), 406);
Ok(())
}
#[test]
fn negotiate_wildcard() -> crate::Result<()> {
let mut accept = TE::new();
accept.push(EncodingProposal::new(Encoding::Brotli, Some(0.8))?);
accept.set_wildcard(true);
assert_eq!(accept.negotiate(&[Encoding::Gzip])?, Encoding::Gzip);
Ok(())
}
}