1pub(crate) trait SupportedEncodings: Copy {
2 fn gzip(&self) -> bool;
3 fn deflate(&self) -> bool;
4 fn br(&self) -> bool;
5 fn zstd(&self) -> bool;
6}
7
8#[derive(Copy, Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
10pub(crate) enum Encoding {
11 #[allow(dead_code)]
12 Identity,
13 #[cfg(any(feature = "fs", feature = "compression-deflate"))]
14 Deflate,
15 #[cfg(any(feature = "fs", feature = "compression-gzip"))]
16 Gzip,
17 #[cfg(any(feature = "fs", feature = "compression-br"))]
18 Brotli,
19 #[cfg(any(feature = "fs", feature = "compression-zstd"))]
20 Zstd,
21}
22
23impl Encoding {
24 #[allow(dead_code)]
25 fn to_str(self) -> &'static str {
26 match self {
27 #[cfg(any(feature = "fs", feature = "compression-gzip"))]
28 Encoding::Gzip => "gzip",
29 #[cfg(any(feature = "fs", feature = "compression-deflate"))]
30 Encoding::Deflate => "deflate",
31 #[cfg(any(feature = "fs", feature = "compression-br"))]
32 Encoding::Brotli => "br",
33 #[cfg(any(feature = "fs", feature = "compression-zstd"))]
34 Encoding::Zstd => "zstd",
35 Encoding::Identity => "identity",
36 }
37 }
38
39 #[cfg(feature = "fs")]
40 pub(crate) fn to_file_extension(self) -> Option<&'static std::ffi::OsStr> {
41 match self {
42 Encoding::Gzip => Some(std::ffi::OsStr::new(".gz")),
43 Encoding::Deflate => Some(std::ffi::OsStr::new(".zz")),
44 Encoding::Brotli => Some(std::ffi::OsStr::new(".br")),
45 Encoding::Zstd => Some(std::ffi::OsStr::new(".zst")),
46 Encoding::Identity => None,
47 }
48 }
49
50 #[allow(dead_code)]
51 pub(crate) fn into_header_value(self) -> http::HeaderValue {
52 http::HeaderValue::from_static(self.to_str())
53 }
54
55 #[cfg(any(
56 feature = "compression-gzip",
57 feature = "compression-br",
58 feature = "compression-deflate",
59 feature = "compression-zstd",
60 feature = "fs",
61 ))]
62 fn parse(s: &str, _supported_encoding: impl SupportedEncodings) -> Option<Encoding> {
63 #[cfg(any(feature = "fs", feature = "compression-gzip"))]
64 if (s.eq_ignore_ascii_case("gzip") || s.eq_ignore_ascii_case("x-gzip"))
65 && _supported_encoding.gzip()
66 {
67 return Some(Encoding::Gzip);
68 }
69
70 #[cfg(any(feature = "fs", feature = "compression-deflate"))]
71 if s.eq_ignore_ascii_case("deflate") && _supported_encoding.deflate() {
72 return Some(Encoding::Deflate);
73 }
74
75 #[cfg(any(feature = "fs", feature = "compression-br"))]
76 if s.eq_ignore_ascii_case("br") && _supported_encoding.br() {
77 return Some(Encoding::Brotli);
78 }
79
80 #[cfg(any(feature = "fs", feature = "compression-zstd"))]
81 if s.eq_ignore_ascii_case("zstd") && _supported_encoding.zstd() {
82 return Some(Encoding::Zstd);
83 }
84
85 if s.eq_ignore_ascii_case("identity") {
86 return Some(Encoding::Identity);
87 }
88
89 None
90 }
91
92 #[cfg(any(
93 feature = "compression-gzip",
94 feature = "compression-br",
95 feature = "compression-zstd",
96 feature = "compression-deflate",
97 ))]
98 pub(crate) fn from_headers(
103 headers: &http::HeaderMap,
104 supported_encoding: impl SupportedEncodings,
105 ) -> Option<Self> {
106 preferred_encoding_with_wildcard(headers, supported_encoding)
107 }
108
109 #[cfg(any(
110 feature = "compression-gzip",
111 feature = "compression-br",
112 feature = "compression-zstd",
113 feature = "compression-deflate",
114 feature = "fs",
115 ))]
116 pub(crate) fn preferred_encoding(
117 accepted_encodings: impl Iterator<Item = (Encoding, QValue)>,
118 ) -> Option<Self> {
119 accepted_encodings
120 .filter(|(_, qvalue)| qvalue.0 > 0)
121 .max_by_key(|&(encoding, qvalue)| (qvalue, encoding))
122 .map(|(encoding, _)| encoding)
123 }
124}
125
126#[cfg(any(
129 feature = "compression-gzip",
130 feature = "compression-br",
131 feature = "compression-zstd",
132 feature = "compression-deflate",
133 feature = "fs",
134))]
135#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
136pub(crate) struct QValue(u16);
137
138#[cfg(any(
139 feature = "compression-gzip",
140 feature = "compression-br",
141 feature = "compression-zstd",
142 feature = "compression-deflate",
143 feature = "fs",
144))]
145impl QValue {
146 #[inline]
147 pub(crate) fn one() -> Self {
148 Self(1000)
149 }
150
151 fn parse(s: &str) -> Option<Self> {
153 let mut c = s.chars();
154 match c.next() {
156 Some('q' | 'Q') => (),
157 _ => return None,
158 };
159 match c.next() {
160 Some('=') => (),
161 _ => return None,
162 };
163
164 let mut value = match c.next() {
167 Some('0') => 0,
168 Some('1') => 1000,
169 _ => return None,
170 };
171
172 match c.next() {
174 Some('.') => (),
175 None => return Some(Self(value)),
176 _ => return None,
177 };
178
179 let mut factor = 100;
183 loop {
184 match c.next() {
185 Some(n @ '0'..='9') => {
186 if factor < 1 {
189 return None;
190 }
191 value += factor * (n as u16 - '0' as u16);
193 }
194 None => {
195 return if value <= 1000 {
198 Some(Self(value))
199 } else {
200 None
201 };
202 }
203 _ => return None,
204 };
205 factor /= 10;
206 }
207 }
208}
209
210#[cfg(any(
211 feature = "compression-gzip",
212 feature = "compression-br",
213 feature = "compression-zstd",
214 feature = "compression-deflate",
215 feature = "fs",
216))]
217pub(crate) fn encodings<'a>(
219 headers: &'a http::HeaderMap,
220 supported_encoding: impl SupportedEncodings + 'a,
221) -> impl Iterator<Item = (Encoding, QValue)> + 'a {
222 headers
223 .get_all(http::header::ACCEPT_ENCODING)
224 .iter()
225 .filter_map(|hval| hval.to_str().ok())
226 .flat_map(|s| s.split(','))
227 .filter_map(move |v| {
228 let mut v = v.splitn(2, ';');
229
230 let encoding = match Encoding::parse(v.next().unwrap().trim(), supported_encoding) {
231 Some(encoding) => encoding,
232 None => return None, };
234
235 let qval = if let Some(qval) = v.next() {
236 QValue::parse(qval.trim())?
237 } else {
238 QValue::one()
239 };
240
241 Some((encoding, qval))
242 })
243}
244
245#[cfg(any(
248 feature = "compression-gzip",
249 feature = "compression-br",
250 feature = "compression-zstd",
251 feature = "compression-deflate",
252))]
253fn wildcard_qvalue(headers: &http::HeaderMap) -> Option<QValue> {
254 headers
255 .get_all(http::header::ACCEPT_ENCODING)
256 .iter()
257 .filter_map(|hval| hval.to_str().ok())
258 .flat_map(|s| s.split(','))
259 .find_map(|v| {
260 let mut v = v.splitn(2, ';');
261 let coding = v.next().unwrap().trim();
262 if coding != "*" {
263 return None;
264 }
265 let qval = if let Some(qval) = v.next() {
266 QValue::parse(qval.trim())?
267 } else {
268 QValue::one()
269 };
270 Some(qval)
271 })
272}
273
274#[cfg(any(
279 feature = "compression-gzip",
280 feature = "compression-br",
281 feature = "compression-zstd",
282 feature = "compression-deflate",
283))]
284fn preferred_encoding_with_wildcard(
285 headers: &http::HeaderMap,
286 supported_encoding: impl SupportedEncodings,
287) -> Option<Encoding> {
288 let explicit: Vec<(Encoding, QValue)> = encodings(headers, supported_encoding).collect();
289 let wildcard_q = wildcard_qvalue(headers);
290
291 let wildcard_q = match wildcard_q {
295 Some(q) => q,
296 None => {
297 let identity_rejected = explicit
298 .iter()
299 .any(|(enc, q)| *enc == Encoding::Identity && q.0 == 0);
300 return match Encoding::preferred_encoding(explicit.into_iter()) {
301 Some(enc) => Some(enc),
302 None => {
303 if identity_rejected {
304 None
305 } else {
306 Some(Encoding::Identity)
307 }
308 }
309 };
310 }
311 };
312
313 let all_supported = all_supported_encodings(supported_encoding);
317
318 let effective = all_supported.iter().filter_map(|e| *e).map(|enc| {
319 let q = explicit
320 .iter()
321 .find(|(e, _)| *e == enc)
322 .map(|(_, q)| *q)
323 .unwrap_or(wildcard_q);
324 (enc, q)
325 });
326
327 Encoding::preferred_encoding(effective)
328}
329
330#[cfg(any(
332 feature = "compression-gzip",
333 feature = "compression-br",
334 feature = "compression-zstd",
335 feature = "compression-deflate",
336))]
337fn all_supported_encodings(supported_encoding: impl SupportedEncodings) -> [Option<Encoding>; 5] {
338 let mut out: [Option<Encoding>; 5] = [None; 5];
339 let mut n = 0;
340
341 macro_rules! push {
342 ($enc:expr) => {
343 out[n] = Some($enc);
344 n += 1;
345 };
346 }
347
348 push!(Encoding::Identity);
349
350 #[cfg(any(feature = "fs", feature = "compression-gzip"))]
351 if supported_encoding.gzip() {
352 push!(Encoding::Gzip);
353 }
354
355 #[cfg(any(feature = "fs", feature = "compression-deflate"))]
356 if supported_encoding.deflate() {
357 push!(Encoding::Deflate);
358 }
359
360 #[cfg(any(feature = "fs", feature = "compression-br"))]
361 if supported_encoding.br() {
362 push!(Encoding::Brotli);
363 }
364
365 #[cfg(any(feature = "fs", feature = "compression-zstd"))]
366 if supported_encoding.zstd() {
367 push!(Encoding::Zstd);
368 }
369
370 let _ = n;
371 out
372}
373
374#[cfg(all(
375 test,
376 feature = "compression-gzip",
377 feature = "compression-deflate",
378 feature = "compression-br",
379 feature = "compression-zstd",
380))]
381mod tests {
382 use super::*;
383
384 #[derive(Copy, Clone, Default)]
385 struct SupportedEncodingsAll;
386
387 impl SupportedEncodings for SupportedEncodingsAll {
388 fn gzip(&self) -> bool {
389 true
390 }
391
392 fn deflate(&self) -> bool {
393 true
394 }
395
396 fn br(&self) -> bool {
397 true
398 }
399
400 fn zstd(&self) -> bool {
401 true
402 }
403 }
404
405 #[test]
406 fn no_accept_encoding_header() {
407 let encoding = Encoding::from_headers(&http::HeaderMap::new(), SupportedEncodingsAll);
408 assert_eq!(Some(Encoding::Identity), encoding);
409 }
410
411 #[test]
412 fn accept_encoding_header_single_encoding() {
413 let mut headers = http::HeaderMap::new();
414 headers.append(
415 http::header::ACCEPT_ENCODING,
416 http::HeaderValue::from_static("gzip"),
417 );
418 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
419 assert_eq!(Some(Encoding::Gzip), encoding);
420 }
421
422 #[test]
423 fn accept_encoding_header_two_encodings() {
424 let mut headers = http::HeaderMap::new();
425 headers.append(
426 http::header::ACCEPT_ENCODING,
427 http::HeaderValue::from_static("gzip,br"),
428 );
429 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
430 assert_eq!(Some(Encoding::Brotli), encoding);
431 }
432
433 #[test]
434 fn accept_encoding_header_gzip_x_gzip() {
435 let mut headers = http::HeaderMap::new();
436 headers.append(
437 http::header::ACCEPT_ENCODING,
438 http::HeaderValue::from_static("gzip,x-gzip"),
439 );
440 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
441 assert_eq!(Some(Encoding::Gzip), encoding);
442 }
443
444 #[test]
445 fn accept_encoding_header_x_gzip_deflate() {
446 let mut headers = http::HeaderMap::new();
447 headers.append(
448 http::header::ACCEPT_ENCODING,
449 http::HeaderValue::from_static("deflate,x-gzip"),
450 );
451 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
452 assert_eq!(Some(Encoding::Gzip), encoding);
453 }
454
455 #[test]
456 fn accept_encoding_header_three_encodings() {
457 let mut headers = http::HeaderMap::new();
458 headers.append(
459 http::header::ACCEPT_ENCODING,
460 http::HeaderValue::from_static("gzip,deflate,br"),
461 );
462 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
463 assert_eq!(Some(Encoding::Brotli), encoding);
464 }
465
466 #[test]
467 fn accept_encoding_header_two_encodings_with_one_qvalue() {
468 let mut headers = http::HeaderMap::new();
469 headers.append(
470 http::header::ACCEPT_ENCODING,
471 http::HeaderValue::from_static("gzip;q=0.5,br"),
472 );
473 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
474 assert_eq!(Some(Encoding::Brotli), encoding);
475 }
476
477 #[test]
478 fn accept_encoding_header_three_encodings_with_one_qvalue() {
479 let mut headers = http::HeaderMap::new();
480 headers.append(
481 http::header::ACCEPT_ENCODING,
482 http::HeaderValue::from_static("gzip;q=0.5,deflate,br"),
483 );
484 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
485 assert_eq!(Some(Encoding::Brotli), encoding);
486 }
487
488 #[test]
489 fn two_accept_encoding_headers_with_one_qvalue() {
490 let mut headers = http::HeaderMap::new();
491 headers.append(
492 http::header::ACCEPT_ENCODING,
493 http::HeaderValue::from_static("gzip;q=0.5"),
494 );
495 headers.append(
496 http::header::ACCEPT_ENCODING,
497 http::HeaderValue::from_static("br"),
498 );
499 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
500 assert_eq!(Some(Encoding::Brotli), encoding);
501 }
502
503 #[test]
504 fn two_accept_encoding_headers_three_encodings_with_one_qvalue() {
505 let mut headers = http::HeaderMap::new();
506 headers.append(
507 http::header::ACCEPT_ENCODING,
508 http::HeaderValue::from_static("gzip;q=0.5,deflate"),
509 );
510 headers.append(
511 http::header::ACCEPT_ENCODING,
512 http::HeaderValue::from_static("br"),
513 );
514 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
515 assert_eq!(Some(Encoding::Brotli), encoding);
516 }
517
518 #[test]
519 fn three_accept_encoding_headers_with_one_qvalue() {
520 let mut headers = http::HeaderMap::new();
521 headers.append(
522 http::header::ACCEPT_ENCODING,
523 http::HeaderValue::from_static("gzip;q=0.5"),
524 );
525 headers.append(
526 http::header::ACCEPT_ENCODING,
527 http::HeaderValue::from_static("deflate"),
528 );
529 headers.append(
530 http::header::ACCEPT_ENCODING,
531 http::HeaderValue::from_static("br"),
532 );
533 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
534 assert_eq!(Some(Encoding::Brotli), encoding);
535 }
536
537 #[test]
538 fn accept_encoding_header_two_encodings_with_two_qvalues() {
539 let mut headers = http::HeaderMap::new();
540 headers.append(
541 http::header::ACCEPT_ENCODING,
542 http::HeaderValue::from_static("gzip;q=0.5,br;q=0.8"),
543 );
544 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
545 assert_eq!(Some(Encoding::Brotli), encoding);
546
547 let mut headers = http::HeaderMap::new();
548 headers.append(
549 http::header::ACCEPT_ENCODING,
550 http::HeaderValue::from_static("gzip;q=0.8,br;q=0.5"),
551 );
552 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
553 assert_eq!(Some(Encoding::Gzip), encoding);
554
555 let mut headers = http::HeaderMap::new();
556 headers.append(
557 http::header::ACCEPT_ENCODING,
558 http::HeaderValue::from_static("gzip;q=0.995,br;q=0.999"),
559 );
560 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
561 assert_eq!(Some(Encoding::Brotli), encoding);
562 }
563
564 #[test]
565 fn accept_encoding_header_three_encodings_with_three_qvalues() {
566 let mut headers = http::HeaderMap::new();
567 headers.append(
568 http::header::ACCEPT_ENCODING,
569 http::HeaderValue::from_static("gzip;q=0.5,deflate;q=0.6,br;q=0.8"),
570 );
571 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
572 assert_eq!(Some(Encoding::Brotli), encoding);
573
574 let mut headers = http::HeaderMap::new();
575 headers.append(
576 http::header::ACCEPT_ENCODING,
577 http::HeaderValue::from_static("gzip;q=0.8,deflate;q=0.6,br;q=0.5"),
578 );
579 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
580 assert_eq!(Some(Encoding::Gzip), encoding);
581
582 let mut headers = http::HeaderMap::new();
583 headers.append(
584 http::header::ACCEPT_ENCODING,
585 http::HeaderValue::from_static("gzip;q=0.6,deflate;q=0.8,br;q=0.5"),
586 );
587 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
588 assert_eq!(Some(Encoding::Deflate), encoding);
589
590 let mut headers = http::HeaderMap::new();
591 headers.append(
592 http::header::ACCEPT_ENCODING,
593 http::HeaderValue::from_static("gzip;q=0.995,deflate;q=0.997,br;q=0.999"),
594 );
595 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
596 assert_eq!(Some(Encoding::Brotli), encoding);
597 }
598
599 #[test]
600 fn accept_encoding_header_invalid_encdoing() {
601 let mut headers = http::HeaderMap::new();
602 headers.append(
603 http::header::ACCEPT_ENCODING,
604 http::HeaderValue::from_static("invalid,gzip"),
605 );
606 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
607 assert_eq!(Some(Encoding::Gzip), encoding);
608 }
609
610 #[test]
611 fn accept_encoding_header_with_qvalue_zero() {
612 let mut headers = http::HeaderMap::new();
613 headers.append(
614 http::header::ACCEPT_ENCODING,
615 http::HeaderValue::from_static("gzip;q=0"),
616 );
617 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
618 assert_eq!(Some(Encoding::Identity), encoding);
619
620 let mut headers = http::HeaderMap::new();
621 headers.append(
622 http::header::ACCEPT_ENCODING,
623 http::HeaderValue::from_static("gzip;q=0."),
624 );
625 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
626 assert_eq!(Some(Encoding::Identity), encoding);
627
628 let mut headers = http::HeaderMap::new();
629 headers.append(
630 http::header::ACCEPT_ENCODING,
631 http::HeaderValue::from_static("gzip;q=0,br;q=0.5"),
632 );
633 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
634 assert_eq!(Some(Encoding::Brotli), encoding);
635 }
636
637 #[test]
638 fn accept_encoding_header_with_uppercase_letters() {
639 let mut headers = http::HeaderMap::new();
640 headers.append(
641 http::header::ACCEPT_ENCODING,
642 http::HeaderValue::from_static("gZiP"),
643 );
644 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
645 assert_eq!(Some(Encoding::Gzip), encoding);
646
647 let mut headers = http::HeaderMap::new();
648 headers.append(
649 http::header::ACCEPT_ENCODING,
650 http::HeaderValue::from_static("gzip;q=0.5,br;Q=0.8"),
651 );
652 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
653 assert_eq!(Some(Encoding::Brotli), encoding);
654 }
655
656 #[test]
657 fn accept_encoding_header_with_allowed_spaces() {
658 let mut headers = http::HeaderMap::new();
659 headers.append(
660 http::header::ACCEPT_ENCODING,
661 http::HeaderValue::from_static(" gzip\t; q=0.5 ,\tbr ;\tq=0.8\t"),
662 );
663 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
664 assert_eq!(Some(Encoding::Brotli), encoding);
665 }
666
667 #[test]
668 fn accept_encoding_header_with_invalid_spaces() {
669 let mut headers = http::HeaderMap::new();
670 headers.append(
671 http::header::ACCEPT_ENCODING,
672 http::HeaderValue::from_static("gzip;q =0.5"),
673 );
674 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
675 assert_eq!(Some(Encoding::Identity), encoding);
676
677 let mut headers = http::HeaderMap::new();
678 headers.append(
679 http::header::ACCEPT_ENCODING,
680 http::HeaderValue::from_static("gzip;q= 0.5"),
681 );
682 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
683 assert_eq!(Some(Encoding::Identity), encoding);
684 }
685
686 #[test]
687 fn accept_encoding_header_with_invalid_quvalues() {
688 let mut headers = http::HeaderMap::new();
689 headers.append(
690 http::header::ACCEPT_ENCODING,
691 http::HeaderValue::from_static("gzip;q=-0.1"),
692 );
693 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
694 assert_eq!(Some(Encoding::Identity), encoding);
695
696 let mut headers = http::HeaderMap::new();
697 headers.append(
698 http::header::ACCEPT_ENCODING,
699 http::HeaderValue::from_static("gzip;q=00.5"),
700 );
701 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
702 assert_eq!(Some(Encoding::Identity), encoding);
703
704 let mut headers = http::HeaderMap::new();
705 headers.append(
706 http::header::ACCEPT_ENCODING,
707 http::HeaderValue::from_static("gzip;q=0.5000"),
708 );
709 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
710 assert_eq!(Some(Encoding::Identity), encoding);
711
712 let mut headers = http::HeaderMap::new();
713 headers.append(
714 http::header::ACCEPT_ENCODING,
715 http::HeaderValue::from_static("gzip;q=.5"),
716 );
717 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
718 assert_eq!(Some(Encoding::Identity), encoding);
719
720 let mut headers = http::HeaderMap::new();
721 headers.append(
722 http::header::ACCEPT_ENCODING,
723 http::HeaderValue::from_static("gzip;q=1.01"),
724 );
725 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
726 assert_eq!(Some(Encoding::Identity), encoding);
727
728 let mut headers = http::HeaderMap::new();
729 headers.append(
730 http::header::ACCEPT_ENCODING,
731 http::HeaderValue::from_static("gzip;q=1.001"),
732 );
733 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
734 assert_eq!(Some(Encoding::Identity), encoding);
735 }
736
737 #[test]
738 fn wildcard_alone_picks_best_supported() {
739 let mut headers = http::HeaderMap::new();
740 headers.append(
741 http::header::ACCEPT_ENCODING,
742 http::HeaderValue::from_static("*"),
743 );
744 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
745 assert_eq!(Some(Encoding::Zstd), encoding);
747 }
748
749 #[test]
750 fn wildcard_q_zero_with_nothing_else_returns_not_satisfiable() {
751 let mut headers = http::HeaderMap::new();
752 headers.append(
753 http::header::ACCEPT_ENCODING,
754 http::HeaderValue::from_static("*;q=0"),
755 );
756 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
757 assert_eq!(None, encoding);
759 }
760
761 #[test]
762 fn wildcard_q_zero_with_gzip_picks_gzip() {
763 let mut headers = http::HeaderMap::new();
764 headers.append(
765 http::header::ACCEPT_ENCODING,
766 http::HeaderValue::from_static("*;q=0,gzip"),
767 );
768 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
769 assert_eq!(Some(Encoding::Gzip), encoding);
770 }
771
772 #[test]
773 fn identity_q_zero_alone_returns_not_satisfiable() {
774 let mut headers = http::HeaderMap::new();
775 headers.append(
776 http::header::ACCEPT_ENCODING,
777 http::HeaderValue::from_static("identity;q=0"),
778 );
779 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
780 assert_eq!(None, encoding);
783 }
784
785 #[test]
786 fn identity_q_zero_with_gzip_picks_gzip() {
787 let mut headers = http::HeaderMap::new();
788 headers.append(
789 http::header::ACCEPT_ENCODING,
790 http::HeaderValue::from_static("identity;q=0,gzip"),
791 );
792 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
793 assert_eq!(Some(Encoding::Gzip), encoding);
794 }
795
796 #[test]
797 fn wildcard_q_zero_identity_q_zero_no_compression_returns_not_satisfiable() {
798 let mut headers = http::HeaderMap::new();
800 headers.append(
801 http::header::ACCEPT_ENCODING,
802 http::HeaderValue::from_static("*;q=0,identity;q=0"),
803 );
804 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
805 assert_eq!(None, encoding);
807 }
808
809 #[test]
810 fn wildcard_with_low_qvalue() {
811 let mut headers = http::HeaderMap::new();
812 headers.append(
813 http::header::ACCEPT_ENCODING,
814 http::HeaderValue::from_static("*;q=0.5,gzip;q=1"),
815 );
816 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
817 assert_eq!(Some(Encoding::Gzip), encoding);
819 }
820
821 #[test]
822 fn wildcard_q_zero_with_identity_picks_identity() {
823 let mut headers = http::HeaderMap::new();
824 headers.append(
825 http::header::ACCEPT_ENCODING,
826 http::HeaderValue::from_static("*;q=0,identity"),
827 );
828 let encoding = Encoding::from_headers(&headers, SupportedEncodingsAll);
829 assert_eq!(Some(Encoding::Identity), encoding);
831 }
832
833 #[derive(Copy, Clone)]
834 struct SupportedGzipOnly;
835
836 impl SupportedEncodings for SupportedGzipOnly {
837 fn gzip(&self) -> bool {
838 true
839 }
840 fn deflate(&self) -> bool {
841 false
842 }
843 fn br(&self) -> bool {
844 false
845 }
846 fn zstd(&self) -> bool {
847 false
848 }
849 }
850
851 #[test]
852 fn wildcard_with_partial_server_support_picks_best_available() {
853 let mut headers = http::HeaderMap::new();
854 headers.append(
855 http::header::ACCEPT_ENCODING,
856 http::HeaderValue::from_static("*"),
857 );
858 let encoding = Encoding::from_headers(&headers, SupportedGzipOnly);
859 assert_eq!(Some(Encoding::Gzip), encoding);
861 }
862
863 #[test]
864 fn wildcard_q_zero_with_unsupported_encoding_returns_not_satisfiable() {
865 let mut headers = http::HeaderMap::new();
866 headers.append(
867 http::header::ACCEPT_ENCODING,
868 http::HeaderValue::from_static("*;q=0,br"),
869 );
870 let encoding = Encoding::from_headers(&headers, SupportedGzipOnly);
871 assert_eq!(None, encoding);
875 }
876}