1 #![feature(proc_macro_hygiene)]
2 
3 #[macro_use]
4 #[cfg(all(feature = "brotli_compression", feature = "gzip_compression"))]
5 extern crate rocket;
6 
7 #[cfg(all(feature = "brotli_compression", feature = "gzip_compression"))]
8 mod compression_fairing_tests {
9     use rocket::config::{Config, Environment};
10     use rocket::http::hyper::header::{ContentEncoding, Encoding};
11     use rocket::http::Status;
12     use rocket::http::{ContentType, Header};
13     use rocket::local::Client;
14     use rocket::response::{Content, Response};
15     use rocket_contrib::compression::Compression;
16 
17     use std::io::Cursor;
18     use std::io::Read;
19 
20     use flate2::read::{GzDecoder, GzEncoder};
21 
22     const HELLO: &str = r"This is a message to hello with more than 100 bytes \
23         in order to have to read more than one buffer when gzipping. こんにちは!";
24 
25     #[get("/")]
index() -> String26     pub fn index() -> String {
27         String::from(HELLO)
28     }
29 
30     #[get("/font")]
font() -> Content<&'static str>31     pub fn font() -> Content<&'static str> {
32         Content(ContentType::WOFF, HELLO)
33     }
34 
35     #[get("/image")]
image() -> Content<&'static str>36     pub fn image() -> Content<&'static str> {
37         Content(ContentType::PNG, HELLO)
38     }
39 
40     #[get("/tar")]
tar() -> Content<&'static str>41     pub fn tar() -> Content<&'static str> {
42         Content(ContentType::TAR, HELLO)
43     }
44 
45     #[get("/already_encoded")]
already_encoded() -> Response<'static>46     pub fn already_encoded() -> Response<'static> {
47         let mut encoder = GzEncoder::new(
48             Cursor::new(String::from(HELLO)),
49             flate2::Compression::default(),
50         );
51         let mut encoded = Vec::new();
52         encoder.read_to_end(&mut encoded).unwrap();
53         Response::build()
54             .header(ContentEncoding(vec![Encoding::Gzip]))
55             .sized_body(Cursor::new(encoded))
56             .finalize()
57     }
58 
59     #[get("/identity")]
identity() -> Response<'static>60     pub fn identity() -> Response<'static> {
61         Response::build()
62             .header(ContentEncoding(vec![Encoding::Identity]))
63             .sized_body(Cursor::new(String::from(HELLO)))
64             .finalize()
65     }
66 
rocket() -> rocket::Rocket67     fn rocket() -> rocket::Rocket {
68         rocket::ignite()
69             .mount(
70                 "/",
71                 routes![index, font, image, tar, already_encoded, identity],
72             )
73             .attach(Compression::fairing())
74     }
75 
rocket_tar_exception() -> rocket::Rocket76     fn rocket_tar_exception() -> rocket::Rocket {
77         let mut table = std::collections::BTreeMap::new();
78         table.insert("exclude".to_string(), vec!["application/x-tar"]);
79         let config = Config::build(Environment::Development)
80             .extra("compress", table)
81             .expect("valid configuration");
82 
83         rocket::custom(config)
84             .mount("/", routes![image, tar])
85             .attach(Compression::fairing())
86     }
87 
88     #[test]
test_prioritizes_brotli()89     fn test_prioritizes_brotli() {
90         let client = Client::new(rocket()).expect("valid rocket instance");
91         let mut response = client
92             .get("/")
93             .header(Header::new("Accept-Encoding", "deflate, gzip, br"))
94             .dispatch();
95         assert_eq!(response.status(), Status::Ok);
96         assert!(response
97             .headers()
98             .get("Content-Encoding")
99             .any(|x| x == "br"));
100         let mut body_plain = Cursor::new(Vec::<u8>::new());
101         brotli::BrotliDecompress(
102             &mut Cursor::new(response.body_bytes().unwrap()),
103             &mut body_plain,
104         )
105         .expect("decompress response");
106         assert_eq!(
107             String::from_utf8(body_plain.get_mut().to_vec()).unwrap(),
108             String::from(HELLO)
109         );
110     }
111 
112     #[test]
test_br_font()113     fn test_br_font() {
114         let client = Client::new(rocket()).expect("valid rocket instance");
115         let mut response = client
116             .get("/font")
117             .header(Header::new("Accept-Encoding", "deflate, gzip, br"))
118             .dispatch();
119         assert_eq!(response.status(), Status::Ok);
120         assert!(response
121             .headers()
122             .get("Content-Encoding")
123             .any(|x| x == "br"));
124         let mut body_plain = Cursor::new(Vec::<u8>::new());
125         brotli::BrotliDecompress(
126             &mut Cursor::new(response.body_bytes().unwrap()),
127             &mut body_plain,
128         )
129         .expect("decompress response");
130         assert_eq!(
131             String::from_utf8(body_plain.get_mut().to_vec()).unwrap(),
132             String::from(HELLO)
133         );
134     }
135 
136     #[test]
test_fallback_gzip()137     fn test_fallback_gzip() {
138         let client = Client::new(rocket()).expect("valid rocket instance");
139         let mut response = client
140             .get("/")
141             .header(Header::new("Accept-Encoding", "deflate, gzip"))
142             .dispatch();
143         assert_eq!(response.status(), Status::Ok);
144         assert!(!response
145             .headers()
146             .get("Content-Encoding")
147             .any(|x| x == "br"));
148         assert!(response
149             .headers()
150             .get("Content-Encoding")
151             .any(|x| x == "gzip"));
152         let mut s = String::new();
153         GzDecoder::new(&response.body_bytes().unwrap()[..])
154             .read_to_string(&mut s)
155             .expect("decompress response");
156         assert_eq!(s, String::from(HELLO));
157     }
158 
159     #[test]
test_does_not_recompress()160     fn test_does_not_recompress() {
161         let client = Client::new(rocket()).expect("valid rocket instance");
162         let mut response = client
163             .get("/already_encoded")
164             .header(Header::new("Accept-Encoding", "deflate, gzip, br"))
165             .dispatch();
166         assert_eq!(response.status(), Status::Ok);
167         assert!(!response
168             .headers()
169             .get("Content-Encoding")
170             .any(|x| x == "br"));
171         assert!(response
172             .headers()
173             .get("Content-Encoding")
174             .any(|x| x == "gzip"));
175         let mut s = String::new();
176         GzDecoder::new(&response.body_bytes().unwrap()[..])
177             .read_to_string(&mut s)
178             .expect("decompress response");
179         assert_eq!(s, String::from(HELLO));
180     }
181 
182     #[test]
test_does_not_compress_explicit_identity()183     fn test_does_not_compress_explicit_identity() {
184         let client = Client::new(rocket()).expect("valid rocket instance");
185         let mut response = client
186             .get("/identity")
187             .header(Header::new("Accept-Encoding", "deflate, gzip, br"))
188             .dispatch();
189         assert_eq!(response.status(), Status::Ok);
190         assert!(!response
191             .headers()
192             .get("Content-Encoding")
193             .any(|x| x != "identity"));
194         assert_eq!(
195             String::from_utf8(response.body_bytes().unwrap()).unwrap(),
196             String::from(HELLO)
197         );
198     }
199 
200     #[test]
test_does_not_compress_image()201     fn test_does_not_compress_image() {
202         let client = Client::new(rocket()).expect("valid rocket instance");
203         let mut response = client
204             .get("/image")
205             .header(Header::new("Accept-Encoding", "deflate, gzip, br"))
206             .dispatch();
207         assert_eq!(response.status(), Status::Ok);
208         assert!(!response
209             .headers()
210             .get("Content-Encoding")
211             .any(|x| x != "identity"));
212         assert_eq!(
213             String::from_utf8(response.body_bytes().unwrap()).unwrap(),
214             String::from(HELLO)
215         );
216     }
217 
218     #[test]
test_ignores_unimplemented_encodings()219     fn test_ignores_unimplemented_encodings() {
220         let client = Client::new(rocket()).expect("valid rocket instance");
221         let mut response = client
222             .get("/")
223             .header(Header::new("Accept-Encoding", "deflate"))
224             .dispatch();
225         assert_eq!(response.status(), Status::Ok);
226         assert!(!response
227             .headers()
228             .get("Content-Encoding")
229             .any(|x| x != "identity"));
230         assert_eq!(
231             String::from_utf8(response.body_bytes().unwrap()).unwrap(),
232             String::from(HELLO)
233         );
234     }
235 
236     #[test]
test_respects_identity_only()237     fn test_respects_identity_only() {
238         let client = Client::new(rocket()).expect("valid rocket instance");
239         let mut response = client
240             .get("/")
241             .header(Header::new("Accept-Encoding", "identity"))
242             .dispatch();
243         assert_eq!(response.status(), Status::Ok);
244         assert!(!response
245             .headers()
246             .get("Content-Encoding")
247             .any(|x| x != "identity"));
248         assert_eq!(
249             String::from_utf8(response.body_bytes().unwrap()).unwrap(),
250             String::from(HELLO)
251         );
252     }
253 
254     #[test]
test_does_not_compress_custom_exception()255     fn test_does_not_compress_custom_exception() {
256         let client = Client::new(rocket_tar_exception()).expect("valid rocket instance");
257         let mut response = client
258             .get("/tar")
259             .header(Header::new("Accept-Encoding", "deflate, gzip, br"))
260             .dispatch();
261         assert_eq!(response.status(), Status::Ok);
262         assert!(!response
263             .headers()
264             .get("Content-Encoding")
265             .any(|x| x != "identity"));
266         assert_eq!(
267             String::from_utf8(response.body_bytes().unwrap()).unwrap(),
268             String::from(HELLO)
269         );
270     }
271 
272     #[test]
test_compress_custom_removed_exception()273     fn test_compress_custom_removed_exception() {
274         let client = Client::new(rocket_tar_exception()).expect("valid rocket instance");
275         let mut response = client
276             .get("/image")
277             .header(Header::new("Accept-Encoding", "deflate, gzip, br"))
278             .dispatch();
279         assert_eq!(response.status(), Status::Ok);
280         assert!(response
281             .headers()
282             .get("Content-Encoding")
283             .any(|x| x == "br"));
284         let mut body_plain = Cursor::new(Vec::<u8>::new());
285         brotli::BrotliDecompress(
286             &mut Cursor::new(response.body_bytes().unwrap()),
287             &mut body_plain,
288         )
289         .expect("decompress response");
290         assert_eq!(
291             String::from_utf8(body_plain.get_mut().to_vec()).unwrap(),
292             String::from(HELLO)
293         );
294     }
295 }
296