1use input;
29use Request;
30use Response;
31
32pub fn apply(request: &Request, mut response: Response) -> Response {
57 if !response_is_text(&response) {
59 return response;
60 }
61
62 if response
65 .headers
66 .iter()
67 .any(|&(ref key, _)| key.eq_ignore_ascii_case("Content-Encoding"))
68 {
69 return response;
70 }
71
72 let encoding_preference = ["br", "gzip", "x-gzip", "identity"];
75 let accept_encoding_header = request.header("Accept-Encoding").unwrap_or("");
76 if let Some(preferred_index) = input::priority_header_preferred(
77 accept_encoding_header,
78 encoding_preference.iter().cloned(),
79 ) {
80 match encoding_preference[preferred_index] {
81 "br" => brotli(&mut response),
82 "gzip" | "x-gzip" => gzip(&mut response),
83 _ => (),
84 }
85 }
86
87 response
88}
89
90fn response_is_text(response: &Response) -> bool {
94 response.headers.iter().any(|&(ref key, ref value)| {
95 if !key.eq_ignore_ascii_case("Content-Type") {
96 return false;
97 }
98
99 let content_type = value.to_lowercase();
100 content_type.starts_with("text/")
101 || content_type.contains("javascript")
102 || content_type.contains("json")
103 || content_type.contains("xml")
104 || content_type.contains("font")
105 })
106}
107
108#[cfg(feature = "gzip")]
109fn gzip(response: &mut Response) {
110 use deflate::deflate_bytes_gzip;
111 use std::io;
112 use std::mem;
113 use ResponseBody;
114
115 response
116 .headers
117 .push(("Content-Encoding".into(), "gzip".into()));
118 let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
119 let (mut raw_data, size) = previous_body.into_reader_and_size();
120 let mut src = match size {
121 Some(size) => Vec::with_capacity(size),
122 None => Vec::new(),
123 };
124 io::copy(&mut raw_data, &mut src).expect("Failed reading response body while gzipping");
125 let zipped = deflate_bytes_gzip(&src);
126 response.data = ResponseBody::from_data(zipped);
127}
128
129#[cfg(not(feature = "gzip"))]
130#[inline]
131fn gzip(response: &mut Response) {}
132
133#[cfg(feature = "brotli")]
134fn brotli(response: &mut Response) {
135 use brotli::enc::reader::CompressorReader;
136 use std::mem;
137 use ResponseBody;
138
139 response
140 .headers
141 .push(("Content-Encoding".into(), "br".into()));
142 let previous_body = mem::replace(&mut response.data, ResponseBody::empty());
143 let (raw_data, _) = previous_body.into_reader_and_size();
144 response.data = ResponseBody::from_reader(CompressorReader::new(raw_data, 0, 6, 22));
146}
147
148#[cfg(not(feature = "brotli"))]
149#[inline]
150fn brotli(response: &mut Response) {}
151
152#[cfg(test)]
153mod tests {
154 use content_encoding;
155 use Request;
156 use Response;
157
158 #[test]
160 fn text_response() {
161 assert!(content_encoding::response_is_text(&Response::text("")));
162 }
163
164 #[test]
165 fn non_text_response() {
166 assert!(!content_encoding::response_is_text(&Response::from_data(
167 "image/jpeg",
168 ""
169 )));
170 }
171
172 #[test]
173 fn no_req_encodings() {
174 let request = Request::fake_http("GET", "/", vec![], vec![]);
175 let response = Response::html("<p>Hello world</p>");
176 let encoded_response = content_encoding::apply(&request, response);
177 assert!(!encoded_response
178 .headers
179 .iter()
180 .any(|(header_name, _)| header_name == "Content-Encoding")); let mut encoded_content = vec![];
182 encoded_response
183 .data
184 .into_reader_and_size()
185 .0
186 .read_to_end(&mut encoded_content)
187 .unwrap();
188 assert_eq!(
189 String::from_utf8(encoded_content).unwrap(),
190 "<p>Hello world</p>"
191 ); }
193
194 #[test]
195 fn empty_req_encodings() {
196 let request = {
197 let h = vec![("Accept-Encoding".to_owned(), "".to_owned())];
198 Request::fake_http("GET", "/", h, vec![])
199 };
200 let response = Response::html("<p>Hello world</p>");
201
202 let encoded_response = content_encoding::apply(&request, response);
203 assert!(!encoded_response
204 .headers
205 .iter()
206 .any(|(header_name, _)| header_name == "Content-Encoding")); let mut encoded_content = vec![];
208 encoded_response
209 .data
210 .into_reader_and_size()
211 .0
212 .read_to_end(&mut encoded_content)
213 .unwrap();
214 assert_eq!(
215 String::from_utf8(encoded_content).unwrap(),
216 "<p>Hello world</p>"
217 ); }
219
220 #[test]
221 fn multi_req_encoding() {
222 let request = {
223 let h = vec![("Accept-Encoding".to_owned(), "foo".to_owned())];
224 Request::fake_http("GET", "/", h, vec![])
225 };
226 let response = Response::html("<p>Hello world</p>");
227
228 let encoded_response = content_encoding::apply(&request, response);
229 assert!(!encoded_response
230 .headers
231 .iter()
232 .any(|(header_name, _)| header_name == "Content-Encoding")); let mut encoded_content = vec![];
234 encoded_response
235 .data
236 .into_reader_and_size()
237 .0
238 .read_to_end(&mut encoded_content)
239 .unwrap();
240 assert_eq!(
241 String::from_utf8(encoded_content).unwrap(),
242 "<p>Hello world</p>"
243 ); }
245
246 #[test]
247 fn unknown_req_encoding() {
248 let request = {
249 let h = vec![("Accept-Encoding".to_owned(), "x-gzip, br".to_owned())];
250 Request::fake_http("GET", "/", h, vec![])
251 };
252 let response = Response::html("<p>Hello world</p>");
253
254 let encoded_response = content_encoding::apply(&request, response);
255 assert!(encoded_response
256 .headers
257 .contains(&("Content-Encoding".into(), "br".into()))); }
259
260 #[test]
261 fn brotli_encoding() {
262 let request = {
263 let h = vec![("Accept-Encoding".to_owned(), "br".to_owned())];
264 Request::fake_http("GET", "/", h, vec![])
265 };
266 let response = Response::html(
267 "<html><head><title>Hello world</title><body><p>Hello world</p></body></html>",
268 );
269
270 let encoded_response = content_encoding::apply(&request, response);
271 assert!(encoded_response
272 .headers
273 .contains(&("Content-Encoding".into(), "br".into()))); let mut encoded_content = vec![];
275 encoded_response
276 .data
277 .into_reader_and_size()
278 .0
279 .read_to_end(&mut encoded_content)
280 .unwrap();
281 assert_eq!(
282 encoded_content,
283 vec![
284 27, 75, 0, 0, 4, 28, 114, 164, 129, 5, 210, 206, 25, 30, 90, 114, 224, 114, 73,
285 109, 45, 196, 23, 126, 240, 144, 77, 40, 26, 211, 228, 67, 73, 40, 236, 55, 101,
286 254, 127, 147, 194, 129, 132, 65, 130, 120, 152, 249, 68, 56, 93, 2
287 ]
288 ); }
290
291 #[test]
292 fn gzip_encoding() {
293 let request = {
294 let h = vec![("Accept-Encoding".to_owned(), "gzip".to_owned())];
295 Request::fake_http("GET", "/", h, vec![])
296 };
297 let response = Response::html(
298 "<html><head><title>Hello world</title><body><p>Hello world</p></body></html>",
299 );
300
301 let encoded_response = content_encoding::apply(&request, response);
302 assert!(encoded_response
303 .headers
304 .contains(&("Content-Encoding".into(), "gzip".into()))); let mut encoded_content = vec![];
306 encoded_response
307 .data
308 .into_reader_and_size()
309 .0
310 .read_to_end(&mut encoded_content)
311 .unwrap();
312
313 assert_eq!(
318 encoded_content[10..],
319 vec![
320 179, 201, 40, 201, 205, 177, 179, 201, 72, 77, 76, 177, 179, 41, 201, 44, 201, 73,
321 181, 243, 72, 205, 201, 201, 87, 40, 207, 47, 202, 73, 177, 209, 135, 8, 217, 36,
322 229, 167, 84, 218, 217, 20, 160, 202, 21, 216, 217, 232, 67, 36, 244, 193, 166, 0,
323 0, 202, 239, 44, 120, 76, 0, 0, 0
324 ]
325 ); }
327}