rouille_ng/
content_encoding.rs1use std::str;
11use Request;
12use Response;
13
14pub fn apply(request: &Request, response: Response) -> Response {
39 if !response_is_text(&response) {
41 return response;
42 }
43
44 if response
47 .headers
48 .iter()
49 .any(|&(ref key, _)| key.eq_ignore_ascii_case("Content-Encoding"))
50 {
51 return response;
52 }
53
54 let mut response = Some(response);
56
57 for encoding in accepted_content_encodings(request) {
61 if brotli(encoding, &mut response) {
63 return response.take().unwrap();
64 }
65
66 if gzip(encoding, &mut response) {
68 return response.take().unwrap();
69 }
70
71 if encoding.eq_ignore_ascii_case("identity") {
73 return response.take().unwrap();
74 }
75 }
76
77 response.take().unwrap()
79}
80
81fn response_is_text(response: &Response) -> bool {
85 response.headers.iter().any(|&(ref key, ref value)| {
86 if !key.eq_ignore_ascii_case("Content-Type") {
87 return false;
88 }
89
90 value.starts_with("text/")
92 || value.contains("javascript")
93 || value.contains("json")
94 || value.contains("xml")
95 || value.contains("font")
96 })
97}
98
99pub fn accepted_content_encodings(request: &Request) -> AcceptedContentEncodingsIter {
117 let elems = request.header("Accept-Encoding").unwrap_or("").split(',');
118 AcceptedContentEncodingsIter { elements: elems }
119}
120
121pub struct AcceptedContentEncodingsIter<'a> {
123 elements: str::Split<'a, char>,
124}
125
126impl<'a> Iterator for AcceptedContentEncodingsIter<'a> {
127 type Item = &'a str;
128
129 #[inline]
130 fn next(&mut self) -> Option<&'a str> {
131 loop {
132 match self.elements.next() {
133 None => return None,
134 Some(e) => {
135 let e = e.trim();
136 if !e.is_empty() {
137 return Some(e);
138 }
139 }
140 }
141 }
142 }
143
144 #[inline]
145 fn size_hint(&self) -> (usize, Option<usize>) {
146 let (_, max) = self.elements.size_hint();
147 (0, max)
148 }
149}
150
151#[cfg(feature = "gzip")]
152fn gzip(e: &str, response: &mut Option<Response>) -> bool {
153 use deflate::deflate_bytes_gzip;
154 use std::io;
155 use std::mem;
156 use ResponseBody;
157
158 if !e.eq_ignore_ascii_case("gzip") {
159 return false;
160 }
161
162 let response = response.as_mut().unwrap();
163 response
164 .headers
165 .push(("Content-Encoding".into(), "gzip".into()));
166 let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
167 let (mut raw_data, size) = previous_body.into_reader_and_size();
168 let mut src = match size {
169 Some(size) => Vec::with_capacity(size),
170 None => Vec::new(),
171 };
172 io::copy(&mut raw_data, &mut src).expect("Failed reading response body while gzipping");
173 let zipped = deflate_bytes_gzip(&src);
174 response.data = ResponseBody::from_data(zipped);
175 true
176}
177
178#[cfg(not(feature = "gzip"))]
179#[inline]
180fn gzip(e: &str, response: &mut Option<Response>) -> bool {
181 false
182}
183
184#[cfg(feature = "brotli")]
185fn brotli(e: &str, response: &mut Option<Response>) -> bool {
186 use brotli2::read::BrotliEncoder;
187 use std::mem;
188 use ResponseBody;
189
190 if !e.eq_ignore_ascii_case("br") {
191 return false;
192 }
193
194 let response = response.as_mut().unwrap();
195 response
196 .headers
197 .push(("Content-Encoding".into(), "br".into()));
198 let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
199 let (raw_data, _) = previous_body.into_reader_and_size();
200 response.data = ResponseBody::from_reader(BrotliEncoder::new(raw_data, 6));
201 true
202}
203
204#[cfg(not(feature = "brotli"))]
205#[inline]
206fn brotli(e: &str, response: &mut Option<Response>) -> bool {
207 false
208}
209
210#[cfg(test)]
211mod tests {
212 use content_encoding;
213 use Request;
214
215 #[test]
216 fn no_req_encodings() {
217 let request = Request::fake_http("GET", "/", vec![], vec![]);
218 assert_eq!(
219 content_encoding::accepted_content_encodings(&request).count(),
220 0
221 );
222 }
223
224 #[test]
225 fn empty_req_encodings() {
226 let request = {
227 let h = vec![("Accept-Encoding".to_owned(), "".to_owned())];
228 Request::fake_http("GET", "/", h, vec![])
229 };
230
231 assert_eq!(
232 content_encoding::accepted_content_encodings(&request).count(),
233 0
234 );
235 }
236
237 #[test]
238 fn one_req_encoding() {
239 let request = {
240 let h = vec![("Accept-Encoding".to_owned(), "foo".to_owned())];
241 Request::fake_http("GET", "/", h, vec![])
242 };
243
244 let mut list = content_encoding::accepted_content_encodings(&request);
245 assert_eq!(list.next().unwrap(), "foo");
246 assert_eq!(list.next(), None);
247 }
248
249 #[test]
250 fn multi_req_encoding() {
251 let request = {
252 let h = vec![("Accept-Encoding".to_owned(), "foo, bar".to_owned())];
253 Request::fake_http("GET", "/", h, vec![])
254 };
255
256 let mut list = content_encoding::accepted_content_encodings(&request);
257 assert_eq!(list.next().unwrap(), "foo");
258 assert_eq!(list.next().unwrap(), "bar");
259 assert_eq!(list.next(), None);
260 }
261
262 }