v2 / vlib / veb / tests / static_compression_test.v
629 lines · 506 sloc · 21.55 KB · 344b9afcfe67902dd9660bd3b077f18464d1d114
Raw
1// vtest build: !docker-ubuntu-musl && !windows // !windows: fasthttp.Server.run not implemented; !docker-ubuntu-musl: readonly-permission assertions can fail there
2import veb
3import net.http
4import os
5import time
6import compress.gzip
7import compress.zstd
8
9const port = 14013
10const port_no_auto = 14014 // Port for static_compression_max_size = 0 test
11const port_gzip_only = 14015 // Port for enable_static_gzip only test
12const port_zstd_only = 14016 // Port for enable_static_zstd only test
13const port_filtered_mimes = 14017 // Port for static_compression_mime_types test
14
15const localserver = 'http://127.0.0.1:${port}'
16const localserver_no_auto = 'http://127.0.0.1:${port_no_auto}'
17const localserver_gzip_only = 'http://127.0.0.1:${port_gzip_only}'
18const localserver_zstd_only = 'http://127.0.0.1:${port_zstd_only}'
19const localserver_filtered_mimes = 'http://127.0.0.1:${port_filtered_mimes}'
20
21const exit_after = time.second * 30
22
23const test_file_content = 'This is a test file for gzip compression. It contains enough text to make compression worthwhile. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
24const filtered_css_content = 'body{margin:0;padding:0;color:#123456;background:#fafafa;}'
25const filtered_svg_content = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><rect width="2" height="2" fill="#fff"/></svg>'
26
27pub struct App {
28 veb.StaticHandler
29 veb.Middleware[Context]
30mut:
31 started chan bool
32}
33
34pub fn (mut app App) before_accept_loop() {
35 app.started <- true
36}
37
38pub fn (mut app App) index(mut ctx Context) veb.Result {
39 return ctx.text('Hello V!')
40}
41
42pub struct Context {
43 veb.Context
44}
45
46fn sanitize_cache_path_component(component string) string {
47 mut sanitized := component.trim_space()
48 if sanitized == '' {
49 return 'unknown'
50 }
51 for invalid_char in ['/', '\\', ':', '*', '?', '"', '<', '>', '|'] {
52 sanitized = sanitized.replace(invalid_char, '_')
53 }
54 return sanitized
55}
56
57fn static_cache_root_for_tests() string {
58 app_dir_name := sanitize_cache_path_component(os.base(os.getwd()))
59 return os.join_path(os.cache_dir(), 'veb', 'static_compression', app_dir_name)
60}
61
62fn reset_test_static_cache() {
63 os.rmdir_all(static_cache_root_for_tests()) or {}
64}
65
66fn find_cached_static_file(file_name string, ext string) string {
67 cache_root := static_cache_root_for_tests()
68 if !os.exists(cache_root) {
69 return ''
70 }
71 for path in os.walk_ext(cache_root, ext) {
72 normalized := path.replace('\\', '/')
73 if normalized.contains('/${file_name}.') {
74 return path
75 }
76 }
77 return ''
78}
79
80fn testsuite_begin() {
81 os.chdir(os.dir(@FILE))!
82 reset_test_static_cache()
83
84 // Create test directory and files
85 os.mkdir_all('testdata_compression')!
86 os.write_file('testdata_compression/test.txt', test_file_content)!
87 os.write_file('testdata_compression/large.txt', test_file_content.repeat(100))!
88
89 // Create readonly directory and file for readonly filesystem test
90 os.mkdir_all('testdata_compression/readonly')!
91 os.write_file('testdata_compression/readonly/readonly.txt', 'This is a readonly file test')!
92
93 // Create pre-compressed file for manual .gz test
94 large_content := 'X'.repeat(2000)
95 os.write_file('testdata_compression/precompressed.txt', large_content)!
96 compressed_gz := gzip.compress(large_content.bytes()) or { panic(err) }
97 os.write_file('testdata_compression/precompressed.txt.gz', compressed_gz.bytestr())!
98
99 // Create pre-compressed file for manual .zst test
100 os.write_file('testdata_compression/precompressed_zstd.txt', large_content)!
101 compressed_zst := zstd.compress(large_content.bytes()) or { panic(err) }
102 os.write_file('testdata_compression/precompressed_zstd.txt.zst', compressed_zst.bytestr())!
103
104 // Create test file for zstd auto-compression
105 os.write_file('testdata_compression/zstd_test.txt', test_file_content)!
106
107 // Create file for testing max_size = 0 (no auto-compression)
108 os.write_file('testdata_compression/no_auto.txt', 'This file should not be auto-compressed')!
109
110 // Create files for gzip-only and zstd-only tests
111 os.write_file('testdata_compression/gzip_only_test.txt', test_file_content)!
112 os.write_file('testdata_compression/zstd_only_test.txt', test_file_content)!
113 os.write_file('testdata_compression/filtered.css', filtered_css_content)!
114 os.write_file('testdata_compression/filtered.svg', filtered_svg_content)!
115 filtered_svg_gz := gzip.compress(filtered_svg_content.bytes()) or { panic(err) }
116 os.write_file('testdata_compression/filtered.svg.gz', filtered_svg_gz.bytestr())!
117
118 spawn fn () {
119 time.sleep(exit_after)
120 assert true == false, 'timeout reached!'
121 exit(1)
122 }()
123
124 run_app_test()
125 run_no_auto_compression_test()
126 run_gzip_only_test()
127 run_zstd_only_test()
128 run_filtered_mime_test()
129}
130
131fn testsuite_end() {
132 // Clean up test files
133 os.rmdir_all('testdata_compression') or {}
134 reset_test_static_cache()
135}
136
137fn run_app_test() {
138 mut app := &App{}
139
140 // Enable static compression (zstd/gzip)
141 app.enable_static_compression = true
142 app.static_compression_max_size = 1048576 // 1MB
143
144 app.handle_static('testdata_compression', true) or { panic(err) }
145
146 // Add compression middleware (gzip for this test app)
147 app.use(veb.encode_gzip[Context]())
148
149 spawn veb.run_at[App, Context](mut app, port: port, timeout_in_seconds: 25, family: .ip)
150 _ := <-app.started
151}
152
153fn run_no_auto_compression_test() {
154 mut app := &App{}
155
156 // Enable static compression but disable auto-compression (max_size = 0)
157 app.enable_static_compression = true
158 app.static_compression_max_size = 0 // Disable auto-compression
159
160 app.handle_static('testdata_compression', true) or { panic(err) }
161
162 // Add compression middleware (gzip for this test app)
163 app.use(veb.encode_gzip[Context]())
164
165 spawn veb.run_at[App, Context](mut app,
166 port: port_no_auto
167 timeout_in_seconds: 25
168 family: .ip
169 )
170 _ := <-app.started
171}
172
173fn run_gzip_only_test() {
174 mut app := &App{}
175
176 // Enable ONLY gzip compression (not zstd, not auto)
177 app.enable_static_gzip = true
178 app.static_compression_max_size = 1048576 // 1MB
179
180 app.handle_static('testdata_compression', true) or { panic(err) }
181
182 spawn veb.run_at[App, Context](mut app,
183 port: port_gzip_only
184 timeout_in_seconds: 25
185 family: .ip
186 )
187 _ := <-app.started
188}
189
190fn run_zstd_only_test() {
191 mut app := &App{}
192
193 // Enable ONLY zstd compression (not gzip, not auto)
194 app.enable_static_zstd = true
195 app.static_compression_max_size = 1048576 // 1MB
196
197 app.handle_static('testdata_compression', true) or { panic(err) }
198
199 spawn veb.run_at[App, Context](mut app,
200 port: port_zstd_only
201 timeout_in_seconds: 25
202 family: .ip
203 )
204 _ := <-app.started
205}
206
207fn run_filtered_mime_test() {
208 mut app := &App{}
209
210 app.enable_static_compression = true
211 app.static_compression_max_size = 1048576
212 app.static_compression_mime_types = [veb.mime_types['.css'], veb.mime_types['.js']]
213
214 app.handle_static('testdata_compression', true) or { panic(err) }
215
216 spawn veb.run_at[App, Context](mut app,
217 port: port_filtered_mimes
218 timeout_in_seconds: 25
219 family: .ip
220 )
221 _ := <-app.started
222}
223
224fn test_gzip_compression_with_accept_encoding() {
225 // Request with Accept-Encoding: gzip
226 mut req := http.new_request(.get, '${localserver}/test.txt', '')
227 req.add_header(.accept_encoding, 'gzip')
228 x := req.do()!
229
230 assert x.status() == .ok
231 assert x.header.get(.content_encoding)! == 'gzip'
232 assert x.header.get(.vary)! == 'Accept-Encoding'
233
234 // HTTP client auto-decompresses gzip, so verify the content directly
235 assert x.body == test_file_content
236}
237
238fn test_no_compression_without_accept_encoding() {
239 // Request without Accept-Encoding header
240 x := http.get('${localserver}/test.txt')!
241
242 assert x.status() == .ok
243 // Should not have content-encoding header when client doesn't accept gzip
244 _ := x.header.get(.content_encoding) or {
245 // Expected: no content-encoding header
246 assert x.body == test_file_content
247 return
248 }
249 assert false, 'should not compress without Accept-Encoding: gzip'
250}
251
252fn test_gz_file_cache_creation() {
253 // First request creates a veb-managed .gz cache file.
254 mut req := http.new_request(.get, '${localserver}/test.txt', '')
255 req.add_header(.accept_encoding, 'gzip')
256 _ := req.do()!
257
258 // Cache files should not be created beside the source file.
259 source_gz_path := 'testdata_compression/test.txt.gz'
260 assert !os.exists(source_gz_path), '.gz cache file should not be created beside the source file'
261 gz_path := find_cached_static_file('test.txt', '.gz')
262 assert gz_path != '', '.gz cache file should be created in veb cache dir'
263
264 // Second request should use cached .gz file
265 y := req.do()!
266 assert y.status() == .ok
267 assert y.header.get(.content_encoding)! == 'gzip'
268
269 // Verify Content-Length matches .gz file size (tests os.file_size() code path)
270 gz_file_size := os.file_size(gz_path)
271 content_length := y.header.get(.content_length)!.u64()
272 assert content_length == gz_file_size, 'Content-Length should match .gz file size'
273}
274
275fn test_large_file_not_auto_compressed() {
276 // Configure app with very small max size to test threshold
277 // The large.txt file is ~20KB (200 chars * 100), which exceeds a 1KB threshold
278 // But we set it to 1MB, so it should still be compressed
279 // Let's test by checking if it gets compressed
280
281 mut req := http.new_request(.get, '${localserver}/large.txt', '')
282 req.add_header(.accept_encoding, 'gzip')
283 x := req.do()!
284
285 assert x.status() == .ok
286 // File should be compressed as it's under 1MB threshold
287 assert x.header.get(.content_encoding)! == 'gzip'
288}
289
290fn test_already_compressed_flag() {
291 // Request a file that will be compressed and cached
292 mut req := http.new_request(.get, '${localserver}/test.txt', '')
293 req.add_header(.accept_encoding, 'gzip')
294 x := req.do()!
295
296 assert x.status() == .ok
297 // The file should be compressed only once (in send_file, not by middleware)
298 // HTTP client auto-decompresses gzip, so verify the content directly
299 assert x.body == test_file_content
300}
301
302fn test_readonly_filesystem_fallback() {
303 // Test that compression works when source files are in readonly directories.
304 // Skip on Windows as readonly permissions work differently (ACL vs chmod)
305 $if windows {
306 eprintln('Skipping readonly filesystem test on Windows')
307 return
308 }
309
310 // Make readonly directory readonly (no write permissions)
311 readonly_dir := 'testdata_compression/readonly'
312 readonly_file := '${readonly_dir}/readonly.txt'
313
314 os.chmod(readonly_dir, 0o555)! // r-xr-xr-x
315
316 mut req := http.new_request(.get, '${localserver}/readonly/readonly.txt', '')
317 req.add_header(.accept_encoding, 'gzip')
318 x := req.do()!
319
320 // Restore permissions before assertions (for cleanup)
321 os.chmod(readonly_dir, 0o755) or {} // rwxr-xr-x
322
323 assert x.status() == .ok
324 // Should still be compressed.
325 assert x.header.get(.content_encoding)! == 'gzip'
326
327 // Verify that .gz file was NOT created beside the source file.
328 gz_path := '${readonly_file}.gz'
329 assert !os.exists(gz_path), '.gz cache file should not be created beside readonly source files'
330
331 // HTTP client auto-decompresses gzip, so verify the content directly
332 assert x.body == 'This is a readonly file test'
333}
334
335fn test_readonly_filesystem_fallback_zstd() {
336 // Test that zstd compression works when source files are in readonly directories.
337 // Skip on Windows as readonly permissions work differently (ACL vs chmod)
338 $if windows {
339 eprintln('Skipping readonly filesystem test on Windows')
340 return
341 }
342
343 // Make readonly directory readonly (no write permissions)
344 readonly_dir := 'testdata_compression/readonly'
345 readonly_file := '${readonly_dir}/readonly.txt'
346
347 os.chmod(readonly_dir, 0o555)! // r-xr-xr-x
348
349 mut req := http.new_request(.get, '${localserver}/readonly/readonly.txt', '')
350 req.add_header(.accept_encoding, 'zstd')
351 x := req.do()!
352
353 // Restore permissions before assertions (for cleanup)
354 os.chmod(readonly_dir, 0o755) or {} // rwxr-xr-x
355
356 assert x.status() == .ok
357 // Should still be compressed.
358 assert x.header.get(.content_encoding)! == 'zstd'
359
360 // Verify that .zst file was NOT created beside the source file.
361 zst_path := '${readonly_file}.zst'
362 assert !os.exists(zst_path), '.zst cache file should not be created beside readonly source files'
363
364 // Verify content is valid zstd
365 decompressed := zstd.decompress(x.body.bytes()) or {
366 assert false, 'response should be valid zstd even on readonly fs: ${err}'
367 return
368 }
369 assert decompressed.bytestr() == 'This is a readonly file test'
370}
371
372fn test_precompressed_gz_file_served() {
373 // Test that manually pre-compressed .gz files are always served
374 // This validates the manual pre-compression workflow (useful with static_compression_max_size = 0)
375
376 // Request the pre-compressed file
377 mut req := http.new_request(.get, '${localserver}/precompressed.txt', '')
378 req.add_header(.accept_encoding, 'gzip')
379 x := req.do()!
380
381 assert x.status() == .ok
382 // Should serve the manually pre-compressed .gz file
383 assert x.header.get(.content_encoding)! == 'gzip'
384 assert x.header.get(.vary)! == 'Accept-Encoding'
385
386 // HTTP client auto-decompresses gzip, so verify the content directly
387 large_content := 'X'.repeat(2000)
388 assert x.body == large_content
389}
390
391fn test_no_auto_compression_with_max_size_zero() {
392 // Test that static_compression_max_size = 0 disables auto-compression
393 // but still serves manually pre-compressed .gz files
394
395 // 1. Verify manually pre-compressed .gz files are still served
396 mut req1 := http.new_request(.get, '${localserver_no_auto}/precompressed.txt', '')
397 req1.add_header(.accept_encoding, 'gzip')
398 x := req1.do()!
399
400 assert x.status() == .ok
401 // Should serve the manually pre-compressed .gz file
402 assert x.header.get(.content_encoding)! == 'gzip'
403 assert x.header.get(.vary)! == 'Accept-Encoding'
404
405 // HTTP client auto-decompresses gzip, so verify the content directly
406 large_content := 'X'.repeat(2000)
407 assert x.body == large_content
408
409 // 2. Verify auto-compression is disabled for files without .gz
410 mut req2 := http.new_request(.get, '${localserver_no_auto}/no_auto.txt', '')
411 req2.add_header(.accept_encoding, 'gzip')
412 y := req2.do()!
413
414 assert y.status() == .ok
415 // Should NOT have content-encoding header (no auto-compression)
416 _ := y.header.get(.content_encoding) or {
417 // Expected: no content-encoding header
418 assert y.body == 'This file should not be auto-compressed'
419 return
420 }
421 assert false, 'should not auto-compress with static_compression_max_size = 0'
422
423 // 3. Verify that .gz file was NOT created
424 gz_path := 'testdata_compression/no_auto.txt.gz'
425 assert !os.exists(gz_path), '.gz cache file should not be created with max_size = 0'
426 cache_gz_path := find_cached_static_file('no_auto.txt', '.gz')
427 assert cache_gz_path == '', '.gz cache file should not be created in veb cache with max_size = 0'
428}
429
430// Zstd tests
431
432fn test_zstd_preferred_over_gzip() {
433 // When client supports both zstd and gzip, zstd should be preferred
434 mut req := http.new_request(.get, '${localserver}/zstd_test.txt', '')
435 req.add_header(.accept_encoding, 'gzip, zstd, br')
436 x := req.do()!
437
438 assert x.status() == .ok
439 assert x.header.get(.content_encoding)! == 'zstd', 'zstd should be preferred over gzip'
440 assert x.header.get(.vary)! == 'Accept-Encoding'
441
442 // Verify the body is valid zstd
443 decompressed := zstd.decompress(x.body.bytes()) or {
444 assert false, 'failed to decompress zstd response: ${err}'
445 return
446 }
447 assert decompressed.bytestr() == test_file_content
448}
449
450fn test_zst_file_cache_creation() {
451 // First request should create a veb-managed .zst cache file.
452 mut req := http.new_request(.get, '${localserver}/zstd_test.txt', '')
453 req.add_header(.accept_encoding, 'zstd')
454 _ := req.do()!
455
456 // Cache files should not be created beside the source file.
457 source_zst_path := 'testdata_compression/zstd_test.txt.zst'
458 assert !os.exists(source_zst_path), '.zst cache file should not be created beside the source file'
459 zst_path := find_cached_static_file('zstd_test.txt', '.zst')
460 assert zst_path != '', '.zst cache file should be created in veb cache dir'
461
462 // Second request should use cached .zst file
463 y := req.do()!
464 assert y.status() == .ok
465 assert y.header.get(.content_encoding)! == 'zstd'
466
467 // Verify Content-Length matches .zst file size
468 zst_file_size := os.file_size(zst_path)
469 content_length := y.header.get(.content_length)!.u64()
470 assert content_length == zst_file_size, 'Content-Length should match .zst file size'
471}
472
473fn test_precompressed_zst_file_served() {
474 // Test that manually pre-compressed .zst files are served
475 mut req := http.new_request(.get, '${localserver}/precompressed_zstd.txt', '')
476 req.add_header(.accept_encoding, 'zstd')
477 x := req.do()!
478
479 assert x.status() == .ok
480 assert x.header.get(.content_encoding)! == 'zstd'
481 assert x.header.get(.vary)! == 'Accept-Encoding'
482
483 // Verify it's the pre-compressed content
484 large_content := 'X'.repeat(2000)
485 decompressed := zstd.decompress(x.body.bytes()) or {
486 assert false, 'manual .zst should be valid: ${err}'
487 return
488 }
489 assert decompressed.bytestr() == large_content
490}
491
492fn test_gzip_fallback_when_zstd_not_supported() {
493 // When client only supports gzip, gzip should be used
494 mut req := http.new_request(.get, '${localserver}/zstd_test.txt', '')
495 req.add_header(.accept_encoding, 'gzip')
496 x := req.do()!
497
498 assert x.status() == .ok
499 assert x.header.get(.content_encoding)! == 'gzip', 'should fallback to gzip when zstd not supported'
500
501 // HTTP client auto-decompresses gzip, so verify the content directly
502 assert x.body == test_file_content
503}
504
505// Tests for enable_static_gzip only (backward compatibility)
506
507fn test_gzip_only_serves_gzip() {
508 // Test that enable_static_gzip alone works (backward compatibility)
509 mut req := http.new_request(.get, '${localserver_gzip_only}/gzip_only_test.txt', '')
510 req.add_header(.accept_encoding, 'gzip')
511 x := req.do()!
512
513 assert x.status() == .ok
514 assert x.header.get(.content_encoding)! == 'gzip', 'gzip-only mode should serve gzip'
515 assert x.header.get(.vary)! == 'Accept-Encoding'
516
517 // HTTP client auto-decompresses gzip, so verify the content directly
518 assert x.body == test_file_content
519}
520
521fn test_gzip_only_ignores_zstd_request() {
522 // Test that enable_static_gzip does NOT serve zstd even if client supports it
523 mut req := http.new_request(.get, '${localserver_gzip_only}/gzip_only_test.txt', '')
524 req.add_header(.accept_encoding, 'zstd, gzip')
525 x := req.do()!
526
527 assert x.status() == .ok
528 // Should serve gzip, NOT zstd (because only enable_static_gzip is set)
529 assert x.header.get(.content_encoding)! == 'gzip', 'gzip-only mode should serve gzip even when client supports zstd'
530
531 // HTTP client auto-decompresses gzip, so verify the content directly
532 assert x.body == test_file_content
533}
534
535fn test_gzip_only_no_compression_without_gzip_header() {
536 // Test that enable_static_gzip does not compress when client doesn't accept gzip
537 mut req := http.new_request(.get, '${localserver_gzip_only}/gzip_only_test.txt', '')
538 req.add_header(.accept_encoding, 'zstd') // Only zstd, no gzip
539 x := req.do()!
540
541 assert x.status() == .ok
542 // Should not have content-encoding header (no compression)
543 _ := x.header.get(.content_encoding) or {
544 // Expected: no content-encoding header
545 assert x.body == test_file_content
546 return
547 }
548 assert false, 'gzip-only mode should not compress when client only accepts zstd'
549}
550
551// Tests for enable_static_zstd only
552
553fn test_zstd_only_serves_zstd() {
554 // Test that enable_static_zstd alone works
555 mut req := http.new_request(.get, '${localserver_zstd_only}/zstd_only_test.txt', '')
556 req.add_header(.accept_encoding, 'zstd')
557 x := req.do()!
558
559 assert x.status() == .ok
560 assert x.header.get(.content_encoding)! == 'zstd', 'zstd-only mode should serve zstd'
561 assert x.header.get(.vary)! == 'Accept-Encoding'
562
563 // Verify the body is valid zstd
564 decompressed := zstd.decompress(x.body.bytes()) or {
565 assert false, 'failed to decompress zstd response: ${err}'
566 return
567 }
568 assert decompressed.bytestr() == test_file_content
569}
570
571fn test_zstd_only_ignores_gzip_request() {
572 // Test that enable_static_zstd does not serve gzip even if client supports it
573 mut req := http.new_request(.get, '${localserver_zstd_only}/zstd_only_test.txt', '')
574 req.add_header(.accept_encoding, 'gzip, zstd')
575 x := req.do()!
576
577 assert x.status() == .ok
578 // Should serve zstd, not gzip (because only enable_static_zstd is set)
579 assert x.header.get(.content_encoding)! == 'zstd', 'zstd-only mode should serve zstd even when client supports gzip'
580
581 decompressed := zstd.decompress(x.body.bytes()) or {
582 assert false, 'failed to decompress zstd response: ${err}'
583 return
584 }
585 assert decompressed.bytestr() == test_file_content
586}
587
588fn test_zstd_only_no_compression_without_zstd_header() {
589 // Test that enable_static_zstd does not compress when client doesn't accept zstd
590 mut req := http.new_request(.get, '${localserver_zstd_only}/zstd_only_test.txt', '')
591 req.add_header(.accept_encoding, 'gzip') // Only gzip, no zstd
592 x := req.do()!
593
594 assert x.status() == .ok
595 // Should not have content-encoding header (no compression)
596 _ := x.header.get(.content_encoding) or {
597 // Expected: no content-encoding header
598 assert x.body == test_file_content
599 return
600 }
601 assert false, 'zstd-only mode should not compress when client only accepts gzip'
602}
603
604fn test_static_compression_mime_filter_allows_matching_types() {
605 mut req := http.new_request(.get, '${localserver_filtered_mimes}/filtered.css', '')
606 req.add_header(.accept_encoding, 'gzip')
607 x := req.do()!
608
609 assert x.status() == .ok
610 assert x.header.get(.content_encoding)! == 'gzip'
611 assert x.header.get(.vary)! == 'Accept-Encoding'
612 assert x.body == filtered_css_content
613
614 gz_path := find_cached_static_file('filtered.css', '.gz')
615 assert gz_path != '', 'allowed MIME type should create a cached compressed file'
616}
617
618fn test_static_compression_mime_filter_skips_excluded_types() {
619 mut req := http.new_request(.get, '${localserver_filtered_mimes}/filtered.svg', '')
620 req.add_header(.accept_encoding, 'gzip')
621 x := req.do()!
622
623 assert x.status() == .ok
624 _ := x.header.get(.content_encoding) or {
625 assert x.body == filtered_svg_content
626 return
627 }
628 assert false, 'excluded MIME type should not be served from compressed content'
629}
630