Coverage Report

Created: 2025-07-12 06:22

/src/h2/src/hpack/table.rs
Line
Count
Source (jump to first uncovered line)
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};
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 {
75
12.9k
    pub fn new(max_size: usize, capacity: usize) -> Table {
76
12.9k
        if capacity == 0 {
77
12.9k
            Table {
78
12.9k
                mask: 0,
79
12.9k
                indices: vec![],
80
12.9k
                slots: VecDeque::new(),
81
12.9k
                inserted: 0,
82
12.9k
                size: 0,
83
12.9k
                max_size,
84
12.9k
            }
85
        } else {
86
0
            let capacity = cmp::max(to_raw_capacity(capacity).next_power_of_two(), 8);
87
0
88
0
            Table {
89
0
                mask: capacity.wrapping_sub(1),
90
0
                indices: vec![None; capacity],
91
0
                slots: VecDeque::with_capacity(usable_capacity(capacity)),
92
0
                inserted: 0,
93
0
                size: 0,
94
0
                max_size,
95
0
            }
96
        }
97
12.9k
    }
98
99
    #[inline]
100
182k
    pub fn capacity(&self) -> usize {
101
182k
        usable_capacity(self.indices.len())
102
182k
    }
103
104
1.02k
    pub fn max_size(&self) -> usize {
105
1.02k
        self.max_size
106
1.02k
    }
107
108
    /// Gets the header stored in the table
109
5.14k
    pub fn resolve<'a>(&'a self, index: &'a Index) -> &'a Header {
110
        use self::Index::*;
111
112
5.14k
        match *index {
113
0
            Indexed(_, ref h) => h,
114
3.56k
            Name(_, ref h) => h,
115
0
            Inserted(idx) => &self.slots[idx].header,
116
1.58k
            InsertedValue(_, idx) => &self.slots[idx].header,
117
0
            NotIndexed(ref h) => h,
118
        }
119
5.14k
    }
120
121
0
    pub fn resolve_idx(&self, index: &Index) -> usize {
122
        use self::Index::*;
123
124
0
        match *index {
125
0
            Indexed(idx, ..) => idx,
126
0
            Name(idx, ..) => idx,
127
0
            Inserted(idx) => idx + DYN_OFFSET,
128
0
            InsertedValue(_name_idx, slot_idx) => slot_idx + DYN_OFFSET,
129
0
            NotIndexed(_) => panic!("cannot resolve index"),
130
        }
131
0
    }
132
133
    /// Index the header in the HPACK table.
134
743k
    pub fn index(&mut self, header: Header) -> Index {
135
743k
        // Check the static table
136
743k
        let statik = index_static(&header);
137
743k
138
743k
        // Don't index certain headers. This logic is borrowed from nghttp2.
139
743k
        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
185k
            debug_assert!(statik.is_some(), "skip_value_index requires a static name",);
144
185k
            return Index::new(statik, header);
145
558k
        }
146
147
        // If the header is already indexed by the static table, return that
148
558k
        if let Some((n, true)) = statik {
149
371k
            return Index::Indexed(n, header);
150
186k
        }
151
186k
152
186k
        // Don't index large headers
153
186k
        if header.len() * 4 > self.max_size * 3 {
154
3.56k
            return Index::new(statik, header);
155
182k
        }
156
182k
157
182k
        self.index_dynamic(header, statik)
158
743k
    }
159
160
182k
    fn index_dynamic(&mut self, header: Header, statik: Option<(usize, bool)>) -> Index {
161
182k
        debug_assert!(self.assert_valid_state("one"));
162
163
182k
        if header.len() + self.size < self.max_size || !header.is_sensitive() {
164
182k
            // Only grow internal storage if needed
165
182k
            self.reserve_one();
166
182k
        }
167
168
182k
        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
0
            return Index::new(statik, header);
173
182k
        }
174
182k
175
182k
        let hash = hash_header(&header);
176
182k
177
182k
        let desired_pos = desired_pos(self.mask, hash);
178
182k
        let mut probe = desired_pos;
179
182k
        let mut dist = 0;
180
182k
181
182k
        // Start at the ideal position, checking all slots
182
182k
        probe_loop!(probe < self.indices.len(), {
183
182k
            if let Some(pos) = self.indices[probe] {
184
                // The slot is already occupied, but check if it has a lower
185
                // displacement.
186
181k
                let their_dist = probe_distance(self.mask, pos.hash, probe);
187
181k
188
181k
                let slot_idx = pos.index.wrapping_add(self.inserted);
189
181k
190
181k
                if their_dist < dist {
191
                    // Index robinhood
192
0
                    return self.index_vacant(header, hash, dist, probe, statik);
193
181k
                } else if pos.hash == hash && self.slots[slot_idx].header.name() == header.name() {
194
                    // Matching name, check values
195
181k
                    return self.index_occupied(header, hash, pos.index, statik.map(|(n, _)| n));
196
0
                }
197
            } else {
198
1.58k
                return self.index_vacant(header, hash, dist, probe, statik);
199
            }
200
201
0
            dist += 1;
202
        });
203
182k
    }
204
205
181k
    fn index_occupied(
206
181k
        &mut self,
207
181k
        header: Header,
208
181k
        hash: HashValue,
209
181k
        mut index: usize,
210
181k
        statik: Option<usize>,
211
181k
    ) -> Index {
212
181k
        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
181k
            let real_idx = index.wrapping_add(self.inserted);
220
181k
221
181k
            if self.slots[real_idx].header.value_eq(&header) {
222
                // We have a full match!
223
181k
                return Index::Indexed(real_idx + DYN_OFFSET, header);
224
0
            }
225
226
0
            if let Some(next) = self.slots[real_idx].next {
227
0
                index = next;
228
0
                continue;
229
0
            }
230
0
231
0
            if header.is_sensitive() {
232
                // Should we assert this?
233
                // debug_assert!(statik.is_none());
234
0
                return Index::Name(real_idx + DYN_OFFSET, header);
235
0
            }
236
0
237
0
            self.update_size(header.len(), Some(index));
238
0
239
0
            // Insert the new header
240
0
            self.insert(header, hash);
241
0
242
0
            // Recompute real_idx as it just changed.
243
0
            let new_real_idx = index.wrapping_add(self.inserted);
244
0
245
0
            // The previous node in the linked list may have gotten evicted
246
0
            // while making room for this header.
247
0
            if new_real_idx < self.slots.len() {
248
0
                let idx = 0usize.wrapping_sub(self.inserted);
249
0
250
0
                self.slots[new_real_idx].next = Some(idx);
251
0
            }
252
253
0
            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
0
            return if let Some(n) = statik {
258
                // If name is in static table, use it instead
259
0
                Index::InsertedValue(n, 0)
260
            } else {
261
0
                Index::InsertedValue(real_idx + DYN_OFFSET, 0)
262
            };
263
        }
264
181k
    }
265
266
1.58k
    fn index_vacant(
267
1.58k
        &mut self,
268
1.58k
        header: Header,
269
1.58k
        hash: HashValue,
270
1.58k
        mut dist: usize,
271
1.58k
        mut probe: usize,
272
1.58k
        statik: Option<(usize, bool)>,
273
1.58k
    ) -> Index {
274
1.58k
        if header.is_sensitive() {
275
0
            return Index::new(statik, header);
276
1.58k
        }
277
1.58k
278
1.58k
        debug_assert!(self.assert_valid_state("top"));
279
1.58k
        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
1.58k
        if self.update_size(header.len(), None) {
284
0
            while dist != 0 {
285
0
                let back = probe.wrapping_sub(1) & self.mask;
286
287
0
                if let Some(pos) = self.indices[back] {
288
0
                    let their_dist = probe_distance(self.mask, pos.hash, back);
289
0
290
0
                    if their_dist < (dist - 1) {
291
0
                        probe = back;
292
0
                        dist -= 1;
293
0
                    } else {
294
0
                        break;
295
                    }
296
0
                } else {
297
0
                    probe = back;
298
0
                    dist -= 1;
299
0
                }
300
            }
301
1.58k
        }
302
303
1.58k
        debug_assert!(self.assert_valid_state("after update"));
304
305
1.58k
        self.insert(header, hash);
306
1.58k
307
1.58k
        let pos_idx = 0usize.wrapping_sub(self.inserted);
308
1.58k
309
1.58k
        let prev = mem::replace(
310
1.58k
            &mut self.indices[probe],
311
1.58k
            Some(Pos {
312
1.58k
                index: pos_idx,
313
1.58k
                hash,
314
1.58k
            }),
315
1.58k
        );
316
317
1.58k
        if let Some(mut prev) = prev {
318
            // Shift forward
319
0
            let mut probe = probe + 1;
320
0
321
0
            probe_loop!(probe < self.indices.len(), {
322
0
                let pos = &mut self.indices[probe];
323
0
324
0
                prev = match mem::replace(pos, Some(prev)) {
325
0
                    Some(p) => p,
326
0
                    None => break,
327
                };
328
            });
329
1.58k
        }
330
331
1.58k
        debug_assert!(self.assert_valid_state("bottom"));
332
333
1.58k
        if let Some((n, _)) = statik {
334
1.58k
            Index::InsertedValue(n, 0)
335
        } else {
336
0
            Index::Inserted(0)
337
        }
338
1.58k
    }
339
340
1.58k
    fn insert(&mut self, header: Header, hash: HashValue) {
341
1.58k
        self.inserted = self.inserted.wrapping_add(1);
342
1.58k
343
1.58k
        self.slots.push_front(Slot {
344
1.58k
            hash,
345
1.58k
            header,
346
1.58k
            next: None,
347
1.58k
        });
348
1.58k
    }
349
350
325
    pub fn resize(&mut self, size: usize) {
351
325
        self.max_size = size;
352
325
353
325
        if size == 0 {
354
55
            self.size = 0;
355
356
215
            for i in &mut self.indices {
357
160
                *i = None;
358
160
            }
359
360
55
            self.slots.clear();
361
55
            self.inserted = 0;
362
270
        } else {
363
270
            self.converge(None);
364
270
        }
365
325
    }
366
367
1.58k
    fn update_size(&mut self, len: usize, prev_idx: Option<usize>) -> bool {
368
1.58k
        self.size += len;
369
1.58k
        self.converge(prev_idx)
370
1.58k
    }
371
372
1.85k
    fn converge(&mut self, prev_idx: Option<usize>) -> bool {
373
1.85k
        let mut ret = false;
374
375
1.87k
        while self.size > self.max_size {
376
13
            ret = true;
377
13
            self.evict(prev_idx);
378
13
        }
379
380
1.85k
        ret
381
1.85k
    }
382
383
13
    fn evict(&mut self, prev_idx: Option<usize>) {
384
13
        let pos_idx = (self.slots.len() - 1).wrapping_sub(self.inserted);
385
13
386
13
        debug_assert!(!self.slots.is_empty());
387
13
        debug_assert!(self.assert_valid_state("one"));
388
389
        // Remove the header
390
13
        let slot = self.slots.pop_back().unwrap();
391
13
        let mut probe = desired_pos(self.mask, slot.hash);
392
13
393
13
        // Update the size
394
13
        self.size -= slot.header.len();
395
13
396
13
        debug_assert_eq!(
397
0
            self.indices
398
0
                .iter()
399
0
                .filter_map(|p| *p)
400
0
                .filter(|p| p.index == pos_idx)
401
0
                .count(),
402
            1
403
        );
404
405
        // Find the associated position
406
13
        probe_loop!(probe < self.indices.len(), {
407
13
            debug_assert!(self.indices[probe].is_some());
408
409
13
            let mut pos = self.indices[probe].unwrap();
410
13
411
13
            if pos.index == pos_idx {
412
13
                if let Some(idx) = slot.next {
413
0
                    pos.index = idx;
414
0
                    self.indices[probe] = Some(pos);
415
13
                } else if Some(pos.index) == prev_idx {
416
0
                    pos.index = 0usize.wrapping_sub(self.inserted + 1);
417
0
                    self.indices[probe] = Some(pos);
418
13
                } else {
419
13
                    self.indices[probe] = None;
420
13
                    self.remove_phase_two(probe);
421
13
                }
422
423
13
                break;
424
0
            }
425
        });
426
427
13
        debug_assert!(self.assert_valid_state("two"));
428
13
    }
429
430
    // Shifts all indices that were displaced by the header that has just been
431
    // removed.
432
13
    fn remove_phase_two(&mut self, probe: usize) {
433
13
        let mut last_probe = probe;
434
13
        let mut probe = probe + 1;
435
13
436
13
        probe_loop!(probe < self.indices.len(), {
437
13
            if let Some(pos) = self.indices[probe] {
438
0
                if probe_distance(self.mask, pos.hash, probe) > 0 {
439
0
                    self.indices[last_probe] = self.indices[probe].take();
440
0
                } else {
441
0
                    break;
442
                }
443
            } else {
444
13
                break;
445
            }
446
447
0
            last_probe = probe;
448
        });
449
450
13
        debug_assert!(self.assert_valid_state("two"));
451
13
    }
452
453
182k
    fn reserve_one(&mut self) {
454
182k
        let len = self.slots.len();
455
182k
456
182k
        if len == self.capacity() {
457
1.57k
            if len == 0 {
458
1.57k
                let new_raw_cap = 8;
459
1.57k
                self.mask = 8 - 1;
460
1.57k
                self.indices = vec![None; new_raw_cap];
461
1.57k
            } else {
462
0
                let raw_cap = self.indices.len();
463
0
                self.grow(raw_cap << 1);
464
0
            }
465
181k
        }
466
182k
    }
467
468
    #[inline]
469
0
    fn grow(&mut self, new_raw_cap: usize) {
470
0
        // This path can never be reached when handling the first allocation in
471
0
        // the map.
472
0
473
0
        debug_assert!(self.assert_valid_state("top"));
474
475
        // find first ideally placed element -- start of cluster
476
0
        let mut first_ideal = 0;
477
478
0
        for (i, pos) in self.indices.iter().enumerate() {
479
0
            if let Some(pos) = *pos {
480
0
                if 0 == probe_distance(self.mask, pos.hash, i) {
481
0
                    first_ideal = i;
482
0
                    break;
483
0
                }
484
0
            }
485
        }
486
487
        // visit the entries in an order where we can simply reinsert them
488
        // into self.indices without any bucket stealing.
489
0
        let old_indices = mem::replace(&mut self.indices, vec![None; new_raw_cap]);
490
0
        self.mask = new_raw_cap.wrapping_sub(1);
491
492
0
        for &pos in &old_indices[first_ideal..] {
493
0
            self.reinsert_entry_in_order(pos);
494
0
        }
495
496
0
        for &pos in &old_indices[..first_ideal] {
497
0
            self.reinsert_entry_in_order(pos);
498
0
        }
499
500
0
        debug_assert!(self.assert_valid_state("bottom"));
501
0
    }
502
503
0
    fn reinsert_entry_in_order(&mut self, pos: Option<Pos>) {
504
0
        if let Some(pos) = pos {
505
            // Find first empty bucket and insert there
506
0
            let mut probe = desired_pos(self.mask, pos.hash);
507
0
508
0
            probe_loop!(probe < self.indices.len(), {
509
0
                if self.indices[probe].is_none() {
510
                    // empty bucket, insert here
511
0
                    self.indices[probe] = Some(pos);
512
0
                    return;
513
0
                }
514
0
515
0
                debug_assert!({
516
0
                    let them = self.indices[probe].unwrap();
517
0
                    let their_distance = probe_distance(self.mask, them.hash, probe);
518
0
                    let our_distance = probe_distance(self.mask, pos.hash, probe);
519
0
520
0
                    their_distance >= our_distance
521
                });
522
            });
523
0
        }
524
0
    }
525
526
    #[cfg(not(test))]
527
0
    fn assert_valid_state(&self, _: &'static str) -> bool {
528
0
        true
529
0
    }
530
531
    #[cfg(test)]
532
    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
627
    pub fn len(&self) -> usize {
628
        self.slots.len()
629
    }
630
631
    /// Returns the table size
632
    pub fn size(&self) -> usize {
633
        self.size
634
    }
635
}
636
637
impl Index {
638
189k
    fn new(v: Option<(usize, bool)>, e: Header) -> Index {
639
189k
        match v {
640
0
            None => Index::NotIndexed(e),
641
185k
            Some((n, true)) => Index::Indexed(n, e),
642
3.56k
            Some((n, false)) => Index::Name(n, e),
643
        }
644
189k
    }
645
}
646
647
#[inline]
648
182k
fn usable_capacity(cap: usize) -> usize {
649
182k
    cap - cap / 4
650
182k
}
651
652
#[inline]
653
0
fn to_raw_capacity(n: usize) -> usize {
654
0
    n + n / 3
655
0
}
656
657
#[inline]
658
363k
fn desired_pos(mask: usize, hash: HashValue) -> usize {
659
363k
    hash.0 & mask
660
363k
}
661
662
#[inline]
663
181k
fn probe_distance(mask: usize, hash: HashValue, current: usize) -> usize {
664
181k
    current.wrapping_sub(desired_pos(mask, hash)) & mask
665
181k
}
666
667
182k
fn hash_header(header: &Header) -> HashValue {
668
    const MASK: u64 = (MAX_SIZE as u64) - 1;
669
670
182k
    let mut h = FnvHasher::default();
671
182k
    header.name().hash(&mut h);
672
182k
    HashValue((h.finish() & MASK) as usize)
673
182k
}
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.
677
743k
fn index_static(header: &Header) -> Option<(usize, bool)> {
678
743k
    match *header {
679
        Header::Field {
680
0
            ref name,
681
0
            ref value,
682
0
        } => match *name {
683
0
            header::ACCEPT_CHARSET => Some((15, false)),
684
            header::ACCEPT_ENCODING => {
685
0
                if value == "gzip, deflate" {
686
0
                    Some((16, true))
687
                } else {
688
0
                    Some((16, false))
689
                }
690
            }
691
0
            header::ACCEPT_LANGUAGE => Some((17, false)),
692
0
            header::ACCEPT_RANGES => Some((18, false)),
693
0
            header::ACCEPT => Some((19, false)),
694
0
            header::ACCESS_CONTROL_ALLOW_ORIGIN => Some((20, false)),
695
0
            header::AGE => Some((21, false)),
696
0
            header::ALLOW => Some((22, false)),
697
0
            header::AUTHORIZATION => Some((23, false)),
698
0
            header::CACHE_CONTROL => Some((24, false)),
699
0
            header::CONTENT_DISPOSITION => Some((25, false)),
700
0
            header::CONTENT_ENCODING => Some((26, false)),
701
0
            header::CONTENT_LANGUAGE => Some((27, false)),
702
0
            header::CONTENT_LENGTH => Some((28, false)),
703
0
            header::CONTENT_LOCATION => Some((29, false)),
704
0
            header::CONTENT_RANGE => Some((30, false)),
705
0
            header::CONTENT_TYPE => Some((31, false)),
706
0
            header::COOKIE => Some((32, false)),
707
0
            header::DATE => Some((33, false)),
708
0
            header::ETAG => Some((34, false)),
709
0
            header::EXPECT => Some((35, false)),
710
0
            header::EXPIRES => Some((36, false)),
711
0
            header::FROM => Some((37, false)),
712
0
            header::HOST => Some((38, false)),
713
0
            header::IF_MATCH => Some((39, false)),
714
0
            header::IF_MODIFIED_SINCE => Some((40, false)),
715
0
            header::IF_NONE_MATCH => Some((41, false)),
716
0
            header::IF_RANGE => Some((42, false)),
717
0
            header::IF_UNMODIFIED_SINCE => Some((43, false)),
718
0
            header::LAST_MODIFIED => Some((44, false)),
719
0
            header::LINK => Some((45, false)),
720
0
            header::LOCATION => Some((46, false)),
721
0
            header::MAX_FORWARDS => Some((47, false)),
722
0
            header::PROXY_AUTHENTICATE => Some((48, false)),
723
0
            header::PROXY_AUTHORIZATION => Some((49, false)),
724
0
            header::RANGE => Some((50, false)),
725
0
            header::REFERER => Some((51, false)),
726
0
            header::REFRESH => Some((52, false)),
727
0
            header::RETRY_AFTER => Some((53, false)),
728
0
            header::SERVER => Some((54, false)),
729
0
            header::SET_COOKIE => Some((55, false)),
730
0
            header::STRICT_TRANSPORT_SECURITY => Some((56, false)),
731
0
            header::TRANSFER_ENCODING => Some((57, false)),
732
0
            header::USER_AGENT => Some((58, false)),
733
0
            header::VARY => Some((59, false)),
734
0
            header::VIA => Some((60, false)),
735
0
            header::WWW_AUTHENTICATE => Some((61, false)),
736
0
            _ => None,
737
        },
738
185k
        Header::Authority(_) => Some((1, false)),
739
186k
        Header::Method(ref v) => match *v {
740
1
            Method::GET => Some((2, true)),
741
185k
            Method::POST => Some((3, true)),
742
362
            _ => Some((2, false)),
743
        },
744
185k
        Header::Scheme(ref v) => match &**v {
745
185k
            "http" => Some((6, true)),
746
185k
            "https" => Some((7, true)),
747
0
            _ => Some((6, false)),
748
        },
749
185k
        Header::Path(ref v) => match &**v {
750
185k
            "/" => Some((4, true)),
751
0
            "/index.html" => Some((5, true)),
752
0
            _ => Some((4, false)),
753
        },
754
0
        Header::Protocol(..) => None,
755
0
        Header::Status(ref v) => match u16::from(*v) {
756
0
            200 => Some((8, true)),
757
0
            204 => Some((9, true)),
758
0
            206 => Some((10, true)),
759
0
            304 => Some((11, true)),
760
0
            400 => Some((12, true)),
761
0
            404 => Some((13, true)),
762
0
            500 => Some((14, true)),
763
0
            _ => Some((8, false)),
764
        },
765
    }
766
743k
}