1 use super::Header;
2 
3 use fnv::FnvHasher;
4 use http::header;
5 use http::method::Method;
6 
7 use std::collections::VecDeque;
8 use std::hash::{Hash, Hasher};
9 use std::{cmp, mem, usize};
10 
11 /// HPACK encoder table
12 #[derive(Debug)]
13 pub struct Table {
14     mask: usize,
15     indices: Vec<Option<Pos>>,
16     slots: VecDeque<Slot>,
17     inserted: usize,
18     // Size is in bytes
19     size: usize,
20     max_size: usize,
21 }
22 
23 #[derive(Debug)]
24 pub enum Index {
25     // The header is already fully indexed
26     Indexed(usize, Header),
27 
28     // The name is indexed, but not the value
29     Name(usize, Header),
30 
31     // The full header has been inserted into the table.
32     Inserted(usize),
33 
34     // Only the value has been inserted (hpack table idx, slots idx)
35     InsertedValue(usize, usize),
36 
37     // The header is not indexed by this table
38     NotIndexed(Header),
39 }
40 
41 #[derive(Debug)]
42 struct Slot {
43     hash: HashValue,
44     header: Header,
45     next: Option<usize>,
46 }
47 
48 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
49 struct Pos {
50     index: usize,
51     hash: HashValue,
52 }
53 
54 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
55 struct HashValue(usize);
56 
57 const MAX_SIZE: usize = 1 << 16;
58 const DYN_OFFSET: usize = 62;
59 
60 macro_rules! probe_loop {
61     ($probe_var: ident < $len: expr, $body: expr) => {
62         debug_assert!($len > 0);
63         loop {
64             if $probe_var < $len {
65                 $body
66                 $probe_var += 1;
67             } else {
68                 $probe_var = 0;
69             }
70         }
71     };
72 }
73 
74 impl Table {
new(max_size: usize, capacity: usize) -> Table75     pub fn new(max_size: usize, capacity: usize) -> Table {
76         if capacity == 0 {
77             Table {
78                 mask: 0,
79                 indices: vec![],
80                 slots: VecDeque::new(),
81                 inserted: 0,
82                 size: 0,
83                 max_size,
84             }
85         } else {
86             let capacity = cmp::max(to_raw_capacity(capacity).next_power_of_two(), 8);
87 
88             Table {
89                 mask: capacity.wrapping_sub(1),
90                 indices: vec![None; capacity],
91                 slots: VecDeque::with_capacity(usable_capacity(capacity)),
92                 inserted: 0,
93                 size: 0,
94                 max_size,
95             }
96         }
97     }
98 
99     #[inline]
capacity(&self) -> usize100     pub fn capacity(&self) -> usize {
101         usable_capacity(self.indices.len())
102     }
103 
max_size(&self) -> usize104     pub fn max_size(&self) -> usize {
105         self.max_size
106     }
107 
108     /// Gets the header stored in the table
resolve<'a>(&'a self, index: &'a Index) -> &'a Header109     pub fn resolve<'a>(&'a self, index: &'a Index) -> &'a Header {
110         use self::Index::*;
111 
112         match *index {
113             Indexed(_, ref h) => h,
114             Name(_, ref h) => h,
115             Inserted(idx) => &self.slots[idx].header,
116             InsertedValue(_, idx) => &self.slots[idx].header,
117             NotIndexed(ref h) => h,
118         }
119     }
120 
resolve_idx(&self, index: &Index) -> usize121     pub fn resolve_idx(&self, index: &Index) -> usize {
122         use self::Index::*;
123 
124         match *index {
125             Indexed(idx, ..) => idx,
126             Name(idx, ..) => idx,
127             Inserted(idx) => idx + DYN_OFFSET,
128             InsertedValue(_name_idx, slot_idx) => slot_idx + DYN_OFFSET,
129             NotIndexed(_) => panic!("cannot resolve index"),
130         }
131     }
132 
133     /// Index the header in the HPACK table.
index(&mut self, header: Header) -> Index134     pub fn index(&mut self, header: Header) -> Index {
135         // Check the static table
136         let statik = index_static(&header);
137 
138         // Don't index certain headers. This logic is borrowed from nghttp2.
139         if header.skip_value_index() {
140             // Right now, if this is true, the header name is always in the
141             // static table. At some point in the future, this might not be true
142             // and this logic will need to be updated.
143             debug_assert!(statik.is_some(), "skip_value_index requires a static name",);
144             return Index::new(statik, header);
145         }
146 
147         // If the header is already indexed by the static table, return that
148         if let Some((n, true)) = statik {
149             return Index::Indexed(n, header);
150         }
151 
152         // Don't index large headers
153         if header.len() * 4 > self.max_size * 3 {
154             return Index::new(statik, header);
155         }
156 
157         self.index_dynamic(header, statik)
158     }
159 
index_dynamic(&mut self, header: Header, statik: Option<(usize, bool)>) -> Index160     fn index_dynamic(&mut self, header: Header, statik: Option<(usize, bool)>) -> Index {
161         debug_assert!(self.assert_valid_state("one"));
162 
163         if header.len() + self.size < self.max_size || !header.is_sensitive() {
164             // Only grow internal storage if needed
165             self.reserve_one();
166         }
167 
168         if self.indices.is_empty() {
169             // If `indices` is not empty, then it is impossible for all
170             // `indices` entries to be `Some`. So, we only need to check for the
171             // empty case.
172             return Index::new(statik, header);
173         }
174 
175         let hash = hash_header(&header);
176 
177         let desired_pos = desired_pos(self.mask, hash);
178         let mut probe = desired_pos;
179         let mut dist = 0;
180 
181         // Start at the ideal position, checking all slots
182         probe_loop!(probe < self.indices.len(), {
183             if let Some(pos) = self.indices[probe] {
184                 // The slot is already occupied, but check if it has a lower
185                 // displacement.
186                 let their_dist = probe_distance(self.mask, pos.hash, probe);
187 
188                 let slot_idx = pos.index.wrapping_add(self.inserted);
189 
190                 if their_dist < dist {
191                     // Index robinhood
192                     return self.index_vacant(header, hash, dist, probe, statik);
193                 } else if pos.hash == hash && self.slots[slot_idx].header.name() == header.name() {
194                     // Matching name, check values
195                     return self.index_occupied(header, hash, pos.index, statik.map(|(n, _)| n));
196                 }
197             } else {
198                 return self.index_vacant(header, hash, dist, probe, statik);
199             }
200 
201             dist += 1;
202         });
203     }
204 
index_occupied( &mut self, header: Header, hash: HashValue, mut index: usize, statik: Option<usize>, ) -> Index205     fn index_occupied(
206         &mut self,
207         header: Header,
208         hash: HashValue,
209         mut index: usize,
210         statik: Option<usize>,
211     ) -> Index {
212         debug_assert!(self.assert_valid_state("top"));
213 
214         // There already is a match for the given header name. Check if a value
215         // matches. The header will also only be inserted if the table is not at
216         // capacity.
217         loop {
218             // Compute the real index into the VecDeque
219             let real_idx = index.wrapping_add(self.inserted);
220 
221             if self.slots[real_idx].header.value_eq(&header) {
222                 // We have a full match!
223                 return Index::Indexed(real_idx + DYN_OFFSET, header);
224             }
225 
226             if let Some(next) = self.slots[real_idx].next {
227                 index = next;
228                 continue;
229             }
230 
231             if header.is_sensitive() {
232                 // Should we assert this?
233                 // debug_assert!(statik.is_none());
234                 return Index::Name(real_idx + DYN_OFFSET, header);
235             }
236 
237             self.update_size(header.len(), Some(index));
238 
239             // Insert the new header
240             self.insert(header, hash);
241 
242             // Recompute real_idx as it just changed.
243             let new_real_idx = index.wrapping_add(self.inserted);
244 
245             // The previous node in the linked list may have gotten evicted
246             // while making room for this header.
247             if new_real_idx < self.slots.len() {
248                 let idx = 0usize.wrapping_sub(self.inserted);
249 
250                 self.slots[new_real_idx].next = Some(idx);
251             }
252 
253             debug_assert!(self.assert_valid_state("bottom"));
254 
255             // Even if the previous header was evicted, we can still reference
256             // it when inserting the new one...
257             return if let Some(n) = statik {
258                 // If name is in static table, use it instead
259                 Index::InsertedValue(n, 0)
260             } else {
261                 Index::InsertedValue(real_idx + DYN_OFFSET, 0)
262             };
263         }
264     }
265 
index_vacant( &mut self, header: Header, hash: HashValue, mut dist: usize, mut probe: usize, statik: Option<(usize, bool)>, ) -> Index266     fn index_vacant(
267         &mut self,
268         header: Header,
269         hash: HashValue,
270         mut dist: usize,
271         mut probe: usize,
272         statik: Option<(usize, bool)>,
273     ) -> Index {
274         if header.is_sensitive() {
275             return Index::new(statik, header);
276         }
277 
278         debug_assert!(self.assert_valid_state("top"));
279         debug_assert!(dist == 0 || self.indices[probe.wrapping_sub(1) & self.mask].is_some());
280 
281         // Passing in `usize::MAX` for prev_idx since there is no previous
282         // header in this case.
283         if self.update_size(header.len(), None) {
284             while dist != 0 {
285                 let back = probe.wrapping_sub(1) & self.mask;
286 
287                 if let Some(pos) = self.indices[back] {
288                     let their_dist = probe_distance(self.mask, pos.hash, back);
289 
290                     if their_dist < (dist - 1) {
291                         probe = back;
292                         dist -= 1;
293                     } else {
294                         break;
295                     }
296                 } else {
297                     probe = back;
298                     dist -= 1;
299                 }
300             }
301         }
302 
303         debug_assert!(self.assert_valid_state("after update"));
304 
305         self.insert(header, hash);
306 
307         let pos_idx = 0usize.wrapping_sub(self.inserted);
308 
309         let prev = mem::replace(
310             &mut self.indices[probe],
311             Some(Pos {
312                 index: pos_idx,
313                 hash,
314             }),
315         );
316 
317         if let Some(mut prev) = prev {
318             // Shift forward
319             let mut probe = probe + 1;
320 
321             probe_loop!(probe < self.indices.len(), {
322                 let pos = &mut self.indices[probe as usize];
323 
324                 prev = match mem::replace(pos, Some(prev)) {
325                     Some(p) => p,
326                     None => break,
327                 };
328             });
329         }
330 
331         debug_assert!(self.assert_valid_state("bottom"));
332 
333         if let Some((n, _)) = statik {
334             Index::InsertedValue(n, 0)
335         } else {
336             Index::Inserted(0)
337         }
338     }
339 
insert(&mut self, header: Header, hash: HashValue)340     fn insert(&mut self, header: Header, hash: HashValue) {
341         self.inserted = self.inserted.wrapping_add(1);
342 
343         self.slots.push_front(Slot {
344             hash,
345             header,
346             next: None,
347         });
348     }
349 
resize(&mut self, size: usize)350     pub fn resize(&mut self, size: usize) {
351         self.max_size = size;
352 
353         if size == 0 {
354             self.size = 0;
355 
356             for i in &mut self.indices {
357                 *i = None;
358             }
359 
360             self.slots.clear();
361             self.inserted = 0;
362         } else {
363             self.converge(None);
364         }
365     }
366 
update_size(&mut self, len: usize, prev_idx: Option<usize>) -> bool367     fn update_size(&mut self, len: usize, prev_idx: Option<usize>) -> bool {
368         self.size += len;
369         self.converge(prev_idx)
370     }
371 
converge(&mut self, prev_idx: Option<usize>) -> bool372     fn converge(&mut self, prev_idx: Option<usize>) -> bool {
373         let mut ret = false;
374 
375         while self.size > self.max_size {
376             ret = true;
377             self.evict(prev_idx);
378         }
379 
380         ret
381     }
382 
evict(&mut self, prev_idx: Option<usize>)383     fn evict(&mut self, prev_idx: Option<usize>) {
384         let pos_idx = (self.slots.len() - 1).wrapping_sub(self.inserted);
385 
386         debug_assert!(!self.slots.is_empty());
387         debug_assert!(self.assert_valid_state("one"));
388 
389         // Remove the header
390         let slot = self.slots.pop_back().unwrap();
391         let mut probe = desired_pos(self.mask, slot.hash);
392 
393         // Update the size
394         self.size -= slot.header.len();
395 
396         debug_assert_eq!(
397             self.indices
398                 .iter()
399                 .filter_map(|p| *p)
400                 .filter(|p| p.index == pos_idx)
401                 .count(),
402             1
403         );
404 
405         // Find the associated position
406         probe_loop!(probe < self.indices.len(), {
407             debug_assert!(!self.indices[probe].is_none());
408 
409             let mut pos = self.indices[probe].unwrap();
410 
411             if pos.index == pos_idx {
412                 if let Some(idx) = slot.next {
413                     pos.index = idx;
414                     self.indices[probe] = Some(pos);
415                 } else if Some(pos.index) == prev_idx {
416                     pos.index = 0usize.wrapping_sub(self.inserted + 1);
417                     self.indices[probe] = Some(pos);
418                 } else {
419                     self.indices[probe] = None;
420                     self.remove_phase_two(probe);
421                 }
422 
423                 break;
424             }
425         });
426 
427         debug_assert!(self.assert_valid_state("two"));
428     }
429 
430     // Shifts all indices that were displaced by the header that has just been
431     // removed.
remove_phase_two(&mut self, probe: usize)432     fn remove_phase_two(&mut self, probe: usize) {
433         let mut last_probe = probe;
434         let mut probe = probe + 1;
435 
436         probe_loop!(probe < self.indices.len(), {
437             if let Some(pos) = self.indices[probe] {
438                 if probe_distance(self.mask, pos.hash, probe) > 0 {
439                     self.indices[last_probe] = self.indices[probe].take();
440                 } else {
441                     break;
442                 }
443             } else {
444                 break;
445             }
446 
447             last_probe = probe;
448         });
449 
450         debug_assert!(self.assert_valid_state("two"));
451     }
452 
reserve_one(&mut self)453     fn reserve_one(&mut self) {
454         let len = self.slots.len();
455 
456         if len == self.capacity() {
457             if len == 0 {
458                 let new_raw_cap = 8;
459                 self.mask = 8 - 1;
460                 self.indices = vec![None; new_raw_cap];
461             } else {
462                 let raw_cap = self.indices.len();
463                 self.grow(raw_cap << 1);
464             }
465         }
466     }
467 
468     #[inline]
grow(&mut self, new_raw_cap: usize)469     fn grow(&mut self, new_raw_cap: usize) {
470         // This path can never be reached when handling the first allocation in
471         // the map.
472 
473         debug_assert!(self.assert_valid_state("top"));
474 
475         // find first ideally placed element -- start of cluster
476         let mut first_ideal = 0;
477 
478         for (i, pos) in self.indices.iter().enumerate() {
479             if let Some(pos) = *pos {
480                 if 0 == probe_distance(self.mask, pos.hash, i) {
481                     first_ideal = i;
482                     break;
483                 }
484             }
485         }
486 
487         // visit the entries in an order where we can simply reinsert them
488         // into self.indices without any bucket stealing.
489         let old_indices = mem::replace(&mut self.indices, vec![None; new_raw_cap]);
490         self.mask = new_raw_cap.wrapping_sub(1);
491 
492         for &pos in &old_indices[first_ideal..] {
493             self.reinsert_entry_in_order(pos);
494         }
495 
496         for &pos in &old_indices[..first_ideal] {
497             self.reinsert_entry_in_order(pos);
498         }
499 
500         debug_assert!(self.assert_valid_state("bottom"));
501     }
502 
reinsert_entry_in_order(&mut self, pos: Option<Pos>)503     fn reinsert_entry_in_order(&mut self, pos: Option<Pos>) {
504         if let Some(pos) = pos {
505             // Find first empty bucket and insert there
506             let mut probe = desired_pos(self.mask, pos.hash);
507 
508             probe_loop!(probe < self.indices.len(), {
509                 if self.indices[probe].is_none() {
510                     // empty bucket, insert here
511                     self.indices[probe] = Some(pos);
512                     return;
513                 }
514 
515                 debug_assert!({
516                     let them = self.indices[probe].unwrap();
517                     let their_distance = probe_distance(self.mask, them.hash, probe);
518                     let our_distance = probe_distance(self.mask, pos.hash, probe);
519 
520                     their_distance >= our_distance
521                 });
522             });
523         }
524     }
525 
526     #[cfg(not(test))]
assert_valid_state(&self, _: &'static str) -> bool527     fn assert_valid_state(&self, _: &'static str) -> bool {
528         true
529     }
530 
531     #[cfg(test)]
assert_valid_state(&self, _msg: &'static str) -> bool532     fn assert_valid_state(&self, _msg: &'static str) -> bool {
533         /*
534             // Checks that the internal map structure is valid
535             //
536             // Ensure all hash codes in indices match the associated slot
537             for pos in &self.indices {
538                 if let Some(pos) = *pos {
539                     let real_idx = pos.index.wrapping_add(self.inserted);
540 
541                     if real_idx.wrapping_add(1) != 0 {
542                         assert!(real_idx < self.slots.len(),
543                                 "out of index; real={}; len={}, msg={}",
544                                 real_idx, self.slots.len(), msg);
545 
546                         assert_eq!(pos.hash, self.slots[real_idx].hash,
547                                    "index hash does not match slot; msg={}", msg);
548                     }
549                 }
550             }
551 
552             // Every index is only available once
553             for i in 0..self.indices.len() {
554                 if self.indices[i].is_none() {
555                     continue;
556                 }
557 
558                 for j in i+1..self.indices.len() {
559                     assert_ne!(self.indices[i], self.indices[j],
560                                 "duplicate indices; msg={}", msg);
561                 }
562             }
563 
564             for (index, slot) in self.slots.iter().enumerate() {
565                 let mut indexed = None;
566 
567                 // First, see if the slot is indexed
568                 for (i, pos) in self.indices.iter().enumerate() {
569                     if let Some(pos) = *pos {
570                         let real_idx = pos.index.wrapping_add(self.inserted);
571                         if real_idx == index {
572                             indexed = Some(i);
573                             // Already know that there is no dup, so break
574                             break;
575                         }
576                     }
577                 }
578 
579                 if let Some(actual) = indexed {
580                     // Ensure that it is accessible..
581                     let desired = desired_pos(self.mask, slot.hash);
582                     let mut probe = desired;
583                     let mut dist = 0;
584 
585                     probe_loop!(probe < self.indices.len(), {
586                         assert!(self.indices[probe].is_some(),
587                                 "unexpected empty slot; probe={}; hash={:?}; msg={}",
588                                 probe, slot.hash, msg);
589 
590                         let pos = self.indices[probe].unwrap();
591 
592                         let their_dist = probe_distance(self.mask, pos.hash, probe);
593                         let real_idx = pos.index.wrapping_add(self.inserted);
594 
595                         if real_idx == index {
596                             break;
597                         }
598 
599                         assert!(dist <= their_dist,
600                                 "could not find entry; actual={}; desired={};" +
601                                 "probe={}, dist={}; their_dist={}; index={}; msg={}",
602                                 actual, desired, probe, dist, their_dist,
603                                 index.wrapping_sub(self.inserted), msg);
604 
605                         dist += 1;
606                     });
607                 } else {
608                     // There is exactly one next link
609                     let cnt = self.slots.iter().map(|s| s.next)
610                         .filter(|n| *n == Some(index.wrapping_sub(self.inserted)))
611                         .count();
612 
613                     assert_eq!(1, cnt, "more than one node pointing here; msg={}", msg);
614                 }
615             }
616         */
617 
618         // TODO: Ensure linked lists are correct: no cycles, etc...
619 
620         true
621     }
622 }
623 
624 #[cfg(test)]
625 impl Table {
626     /// Returns the number of headers in the table
len(&self) -> usize627     pub fn len(&self) -> usize {
628         self.slots.len()
629     }
630 
631     /// Returns the table size
size(&self) -> usize632     pub fn size(&self) -> usize {
633         self.size
634     }
635 }
636 
637 impl Index {
new(v: Option<(usize, bool)>, e: Header) -> Index638     fn new(v: Option<(usize, bool)>, e: Header) -> Index {
639         match v {
640             None => Index::NotIndexed(e),
641             Some((n, true)) => Index::Indexed(n, e),
642             Some((n, false)) => Index::Name(n, e),
643         }
644     }
645 }
646 
647 #[inline]
usable_capacity(cap: usize) -> usize648 fn usable_capacity(cap: usize) -> usize {
649     cap - cap / 4
650 }
651 
652 #[inline]
to_raw_capacity(n: usize) -> usize653 fn to_raw_capacity(n: usize) -> usize {
654     n + n / 3
655 }
656 
657 #[inline]
desired_pos(mask: usize, hash: HashValue) -> usize658 fn desired_pos(mask: usize, hash: HashValue) -> usize {
659     (hash.0 & mask) as usize
660 }
661 
662 #[inline]
probe_distance(mask: usize, hash: HashValue, current: usize) -> usize663 fn probe_distance(mask: usize, hash: HashValue, current: usize) -> usize {
664     current.wrapping_sub(desired_pos(mask, hash)) & mask as usize
665 }
666 
hash_header(header: &Header) -> HashValue667 fn hash_header(header: &Header) -> HashValue {
668     const MASK: u64 = (MAX_SIZE as u64) - 1;
669 
670     let mut h = FnvHasher::default();
671     header.name().hash(&mut h);
672     HashValue((h.finish() & MASK) as usize)
673 }
674 
675 /// Checks the static table for the header. If found, returns the index and a
676 /// boolean representing if the value matched as well.
index_static(header: &Header) -> Option<(usize, bool)>677 fn index_static(header: &Header) -> Option<(usize, bool)> {
678     match *header {
679         Header::Field {
680             ref name,
681             ref value,
682         } => match *name {
683             header::ACCEPT_CHARSET => Some((15, false)),
684             header::ACCEPT_ENCODING => {
685                 if value == "gzip, deflate" {
686                     Some((16, true))
687                 } else {
688                     Some((16, false))
689                 }
690             }
691             header::ACCEPT_LANGUAGE => Some((17, false)),
692             header::ACCEPT_RANGES => Some((18, false)),
693             header::ACCEPT => Some((19, false)),
694             header::ACCESS_CONTROL_ALLOW_ORIGIN => Some((20, false)),
695             header::AGE => Some((21, false)),
696             header::ALLOW => Some((22, false)),
697             header::AUTHORIZATION => Some((23, false)),
698             header::CACHE_CONTROL => Some((24, false)),
699             header::CONTENT_DISPOSITION => Some((25, false)),
700             header::CONTENT_ENCODING => Some((26, false)),
701             header::CONTENT_LANGUAGE => Some((27, false)),
702             header::CONTENT_LENGTH => Some((28, false)),
703             header::CONTENT_LOCATION => Some((29, false)),
704             header::CONTENT_RANGE => Some((30, false)),
705             header::CONTENT_TYPE => Some((31, false)),
706             header::COOKIE => Some((32, false)),
707             header::DATE => Some((33, false)),
708             header::ETAG => Some((34, false)),
709             header::EXPECT => Some((35, false)),
710             header::EXPIRES => Some((36, false)),
711             header::FROM => Some((37, false)),
712             header::HOST => Some((38, false)),
713             header::IF_MATCH => Some((39, false)),
714             header::IF_MODIFIED_SINCE => Some((40, false)),
715             header::IF_NONE_MATCH => Some((41, false)),
716             header::IF_RANGE => Some((42, false)),
717             header::IF_UNMODIFIED_SINCE => Some((43, false)),
718             header::LAST_MODIFIED => Some((44, false)),
719             header::LINK => Some((45, false)),
720             header::LOCATION => Some((46, false)),
721             header::MAX_FORWARDS => Some((47, false)),
722             header::PROXY_AUTHENTICATE => Some((48, false)),
723             header::PROXY_AUTHORIZATION => Some((49, false)),
724             header::RANGE => Some((50, false)),
725             header::REFERER => Some((51, false)),
726             header::REFRESH => Some((52, false)),
727             header::RETRY_AFTER => Some((53, false)),
728             header::SERVER => Some((54, false)),
729             header::SET_COOKIE => Some((55, false)),
730             header::STRICT_TRANSPORT_SECURITY => Some((56, false)),
731             header::TRANSFER_ENCODING => Some((57, false)),
732             header::USER_AGENT => Some((58, false)),
733             header::VARY => Some((59, false)),
734             header::VIA => Some((60, false)),
735             header::WWW_AUTHENTICATE => Some((61, false)),
736             _ => None,
737         },
738         Header::Authority(_) => Some((1, false)),
739         Header::Method(ref v) => match *v {
740             Method::GET => Some((2, true)),
741             Method::POST => Some((3, true)),
742             _ => Some((2, false)),
743         },
744         Header::Scheme(ref v) => match &**v {
745             "http" => Some((6, true)),
746             "https" => Some((7, true)),
747             _ => Some((6, false)),
748         },
749         Header::Path(ref v) => match &**v {
750             "/" => Some((4, true)),
751             "/index.html" => Some((5, true)),
752             _ => Some((4, false)),
753         },
754         Header::Status(ref v) => match u16::from(*v) {
755             200 => Some((8, true)),
756             204 => Some((9, true)),
757             206 => Some((10, true)),
758             304 => Some((11, true)),
759             400 => Some((12, true)),
760             404 => Some((13, true)),
761             500 => Some((14, true)),
762             _ => Some((8, false)),
763         },
764     }
765 }
766