use std::str;
use Request;
use Response;
pub fn apply(request: &Request, response: Response) -> Response {
if !response_is_text(&response) {
return response;
}
if response
.headers
.iter()
.any(|&(ref key, _)| key.eq_ignore_ascii_case("Content-Encoding"))
{
return response;
}
let mut response = Some(response);
for encoding in accepted_content_encodings(request) {
if brotli(encoding, &mut response) {
return response.take().unwrap();
}
if gzip(encoding, &mut response) {
return response.take().unwrap();
}
if encoding.eq_ignore_ascii_case("identity") {
return response.take().unwrap();
}
}
response.take().unwrap()
}
fn response_is_text(response: &Response) -> bool {
response.headers.iter().any(|&(ref key, ref value)| {
if !key.eq_ignore_ascii_case("Content-Type") {
return false;
}
value.starts_with("text/")
|| value.contains("javascript")
|| value.contains("json")
|| value.contains("xml")
|| value.contains("font")
})
}
pub fn accepted_content_encodings(request: &Request) -> AcceptedContentEncodingsIter {
let elems = request.header("Accept-Encoding").unwrap_or("").split(',');
AcceptedContentEncodingsIter { elements: elems }
}
pub struct AcceptedContentEncodingsIter<'a> {
elements: str::Split<'a, char>,
}
impl<'a> Iterator for AcceptedContentEncodingsIter<'a> {
type Item = &'a str;
#[inline]
fn next(&mut self) -> Option<&'a str> {
loop {
match self.elements.next() {
None => return None,
Some(e) => {
let e = e.trim();
if !e.is_empty() {
return Some(e);
}
}
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (_, max) = self.elements.size_hint();
(0, max)
}
}
#[cfg(feature = "gzip")]
fn gzip(e: &str, response: &mut Option<Response>) -> bool {
use deflate::deflate_bytes_gzip;
use std::io;
use std::mem;
use ResponseBody;
if !e.eq_ignore_ascii_case("gzip") {
return false;
}
let response = response.as_mut().unwrap();
response
.headers
.push(("Content-Encoding".into(), "gzip".into()));
let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
let (mut raw_data, size) = previous_body.into_reader_and_size();
let mut src = match size {
Some(size) => Vec::with_capacity(size),
None => Vec::new(),
};
io::copy(&mut raw_data, &mut src).expect("Failed reading response body while gzipping");
let zipped = deflate_bytes_gzip(&src);
response.data = ResponseBody::from_data(zipped);
true
}
#[cfg(not(feature = "gzip"))]
#[inline]
fn gzip(e: &str, response: &mut Option<Response>) -> bool {
false
}
#[cfg(feature = "brotli")]
fn brotli(e: &str, response: &mut Option<Response>) -> bool {
use brotli2::read::BrotliEncoder;
use std::mem;
use ResponseBody;
if !e.eq_ignore_ascii_case("br") {
return false;
}
let response = response.as_mut().unwrap();
response
.headers
.push(("Content-Encoding".into(), "br".into()));
let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
let (raw_data, _) = previous_body.into_reader_and_size();
response.data = ResponseBody::from_reader(BrotliEncoder::new(raw_data, 6));
true
}
#[cfg(not(feature = "brotli"))]
#[inline]
fn brotli(e: &str, response: &mut Option<Response>) -> bool {
false
}
#[cfg(test)]
mod tests {
use content_encoding;
use Request;
#[test]
fn no_req_encodings() {
let request = Request::fake_http("GET", "/", vec![], vec![]);
assert_eq!(
content_encoding::accepted_content_encodings(&request).count(),
0
);
}
#[test]
fn empty_req_encodings() {
let request = {
let h = vec![("Accept-Encoding".to_owned(), "".to_owned())];
Request::fake_http("GET", "/", h, vec![])
};
assert_eq!(
content_encoding::accepted_content_encodings(&request).count(),
0
);
}
#[test]
fn one_req_encoding() {
let request = {
let h = vec![("Accept-Encoding".to_owned(), "foo".to_owned())];
Request::fake_http("GET", "/", h, vec![])
};
let mut list = content_encoding::accepted_content_encodings(&request);
assert_eq!(list.next().unwrap(), "foo");
assert_eq!(list.next(), None);
}
#[test]
fn multi_req_encoding() {
let request = {
let h = vec![("Accept-Encoding".to_owned(), "foo, bar".to_owned())];
Request::fake_http("GET", "/", h, vec![])
};
let mut list = content_encoding::accepted_content_encodings(&request);
assert_eq!(list.next().unwrap(), "foo");
assert_eq!(list.next().unwrap(), "bar");
assert_eq!(list.next(), None);
}
}