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