1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #![rustfmt::skip]
15 
16 use std::collections::{HashMap, VecDeque};
17 
18 /// The [`Dynamic Table`][dynamic_table] implementation of [QPACK].
19 ///
20 /// [dynamic_table]: https://www.rfc-editor.org/rfc/rfc9204.html#name-dynamic-table
21 /// [QPACK]: https://www.rfc-editor.org/rfc/rfc9204.html
22 /// # Introduction
23 /// The dynamic table consists of a list of field lines maintained in first-in, first-out order.
24 /// A QPACK encoder and decoder share a dynamic table that is initially empty.
25 /// The encoder adds entries to the dynamic table and sends them to the decoder via instructions on
26 /// the encoder stream
27 ///
28 /// The dynamic table can contain duplicate entries (i.e., entries with the same name and same value).
29 /// Therefore, duplicate entries MUST NOT be treated as an error by the decoder.
30 ///
31 /// Dynamic table entries can have empty values.
32 
33 pub(crate) struct TableSearcher<'a> {
34     dynamic: &'a DynamicTable,
35 }
36 
37 impl<'a> TableSearcher<'a> {
new(dynamic: &'a DynamicTable) -> Self38     pub(crate) fn new(dynamic: &'a DynamicTable) -> Self {
39         Self { dynamic }
40     }
41 
42     /// Searches index in static and dynamic tables.
find_index_static(&self, header: &Field, value: &str) -> Option<TableIndex>43     pub(crate) fn find_index_static(&self, header: &Field, value: &str) -> Option<TableIndex> {
44         match StaticTable::index(header, value) {
45             x @ Some(TableIndex::Field(_)) => x,
46             _ => Some(TableIndex::None),
47         }
48     }
49 
find_index_name_static(&self, header: &Field, value: &str) -> Option<TableIndex>50     pub(crate) fn find_index_name_static(&self, header: &Field, value: &str) -> Option<TableIndex> {
51         match StaticTable::index(header, value) {
52             x @ Some(TableIndex::FieldName(_)) => x,
53             _ => Some(TableIndex::None),
54         }
55     }
56 
find_index_dynamic(&self, header: &Field, value: &str) -> Option<TableIndex>57     pub(crate) fn find_index_dynamic(&self, header: &Field, value: &str) -> Option<TableIndex> {
58         match self.dynamic.index(header, value) {
59             x @ Some(TableIndex::Field(_)) => x,
60             _ => Some(TableIndex::None),
61         }
62     }
63 
find_index_name_dynamic( &self, header: &Field, value: &str, ) -> Option<TableIndex>64     pub(crate) fn find_index_name_dynamic(
65         &self,
66         header: &Field,
67         value: &str,
68     ) -> Option<TableIndex> {
69         match self.dynamic.index_name(header, value) {
70             x @ Some(TableIndex::FieldName(_)) => x,
71             _ => Some(TableIndex::None),
72         }
73     }
74 
find_field_static(&self, index: usize) -> Option<(Field, String)>75     pub(crate) fn find_field_static(&self, index: usize) -> Option<(Field, String)> {
76         match StaticTable::field(index) {
77             x @ Some((_, _)) => x,
78             _ => None,
79         }
80     }
81 
find_field_name_static(&self, index: usize) -> Option<Field>82     pub(crate) fn find_field_name_static(&self, index: usize) -> Option<Field> {
83         StaticTable::field_name(index)
84     }
85 
find_field_dynamic(&self, index: usize) -> Option<(Field, String)>86     pub(crate) fn find_field_dynamic(&self, index: usize) -> Option<(Field, String)> {
87         self.dynamic.field(index)
88     }
89 
find_field_name_dynamic(&self, index: usize) -> Option<Field>90     pub(crate) fn find_field_name_dynamic(&self, index: usize) -> Option<Field> {
91         self.dynamic.field_name(index)
92     }
93 }
94 
95 pub struct DynamicTable {
96     queue: VecDeque<(Field, String)>,
97     // The size of the dynamic table is the sum of the size of its entries
98     size: usize,
99     capacity: usize,
100     pub(crate) insert_count: usize,
101     remove_count: usize,
102     pub(crate) known_received_count: usize,
103 }
104 
105 impl DynamicTable {
with_empty() -> Self106     pub fn with_empty() -> Self {
107         Self {
108             queue: VecDeque::new(),
109             size: 0,
110             capacity: 0,
111             insert_count: 0,
112             remove_count: 0,
113             known_received_count: 0,
114         }
115     }
116 
size(&self) -> usize117     pub(crate) fn size(&self) -> usize {
118         self.size
119     }
120 
capacity(&self) -> usize121     pub(crate) fn capacity(&self) -> usize {
122         self.capacity
123     }
124 
max_entries(&self) -> usize125     pub(crate) fn max_entries(&self) -> usize {
126         self.capacity / 32
127     }
128     /// Updates `DynamicTable` by a given `Header` and value pair.
update(&mut self, field: Field, value: String) -> Option<TableIndex>129     pub(crate) fn update(&mut self, field: Field, value: String) -> Option<TableIndex> {
130         self.insert_count += 1;
131         self.size += field.len() + value.len() + 32;
132         self.queue.push_back((field.clone(), value.clone()));
133         self.fit_size();
134         self.index(&field, &value)
135     }
136 
have_enough_space( &self, field: &Field, value: &String, insert_length: &usize, ) -> bool137     pub(crate) fn have_enough_space(
138         &self,
139         field: &Field,
140         value: &String,
141         insert_length: &usize,
142     ) -> bool {
143         if self.size + field.len() + value.len() + 32 <= self.capacity - insert_length {
144             return true;
145         } else {
146             let mut eviction_space = 0;
147             for (i, (h, v)) in self.queue.iter().enumerate() {
148                 if i <= self.known_received_count {
149                     eviction_space += h.len() + v.len() + 32;
150                 } else {
151                     if eviction_space - insert_length >= field.len() + value.len() + 32 {
152                         return true;
153                     }
154                     return false;
155                 }
156                 if eviction_space - insert_length >= field.len() + value.len() + 32 {
157                     return true;
158                 }
159             }
160         }
161         false
162     }
163 
164     /// Updates `DynamicTable`'s size.
update_size(&mut self, max_size: usize)165     pub(crate) fn update_size(&mut self, max_size: usize) {
166         self.capacity = max_size;
167         self.fit_size();
168     }
169 
170     /// Adjusts dynamic table content to fit its size.
fit_size(&mut self)171     fn fit_size(&mut self) {
172         while self.size > self.capacity && !self.queue.is_empty() {
173             let (key, string) = self.queue.pop_front().unwrap();
174             self.remove_count += 1;
175             self.capacity -= key.len() + string.len() + 32;
176         }
177     }
178 
179     /// Tries get the index of a `Header`.
index(&self, header: &Field, value: &str) -> Option<TableIndex>180     fn index(&self, header: &Field, value: &str) -> Option<TableIndex> {
181         // find latest
182         let mut index = None;
183         for (n, (h, v)) in self.queue.iter().enumerate() {
184             if let (true, true, _) = (header == h, value == v, &index) {
185                 index = Some(TableIndex::Field(n + self.remove_count))
186             }
187         }
188         index
189     }
190 
index_name(&self, header: &Field, value: &str) -> Option<TableIndex>191     fn index_name(&self, header: &Field, value: &str) -> Option<TableIndex> {
192         // find latest
193         let mut index = None;
194         for (n, (h, v)) in self.queue.iter().enumerate() {
195             if let (true, _, _) = (header == h, value == v, &index) {
196                 index = Some(TableIndex::FieldName(n + self.remove_count))
197             }
198         }
199         index
200     }
201 
field(&self, index: usize) -> Option<(Field, String)>202     pub(crate) fn field(&self, index: usize) -> Option<(Field, String)> {
203         self.queue.get(index - self.remove_count).cloned()
204     }
205 
field_name(&self, index: usize) -> Option<Field>206     pub(crate) fn field_name(&self, index: usize) -> Option<Field> {
207         self.queue
208             .get(index - self.remove_count)
209             .map(|(field, _)| field.clone())
210     }
211 }
212 
213 #[derive(PartialEq, Clone)]
214 pub(crate) enum TableIndex {
215     Field(usize),
216     FieldName(usize),
217     None,
218 }
219 
220 /// The [`Static Table`][static_table] implementation of [QPACK].
221 ///
222 /// [static_table]: https://www.rfc-editor.org/rfc/rfc9204.html#static-table
223 /// [QPACK]: https://www.rfc-editor.org/rfc/rfc9204.html
224 ///
225 /// # Introduction
226 /// The static table consists of a predefined list of field lines,
227 /// each of which has a fixed index over time.
228 /// All entries in the static table have a name and a value.
229 /// However, values can be empty (that is, have a length of 0). Each entry is
230 /// identified by a unique index.
231 /// Note that the QPACK static table is indexed from 0,
232 /// whereas the HPACK static table is indexed from 1.
233 /// When the decoder encounters an invalid static table
234 /// index in a field line format, it MUST treat this
235 /// as a connection error of type QpackDecompressionFailed.
236 /// If this index is received on the encoder stream,
237 /// this MUST be treated as a connection error of type QpackEncoderStreamError.
238 ///
239 
240 struct StaticTable;
241 
242 impl StaticTable {
243     /// Gets a `Field` by the given index.
field_name(index: usize) -> Option<Field>244     fn field_name(index: usize) -> Option<Field> {
245         match index {
246             0 => Some(Field::Authority),
247             1 => Some(Field::Path),
248             2 => Some(Field::Other(String::from("age"))),
249             3 => Some(Field::Other(String::from("content-disposition"))),
250             4 => Some(Field::Other(String::from("content-length"))),
251             5 => Some(Field::Other(String::from("cookie"))),
252             6 => Some(Field::Other(String::from("date"))),
253             7 => Some(Field::Other(String::from("etag"))),
254             8 => Some(Field::Other(String::from("if-modified-since"))),
255             9 => Some(Field::Other(String::from("if-none-match"))),
256             10 => Some(Field::Other(String::from("last-modified"))),
257             11 => Some(Field::Other(String::from("link"))),
258             12 => Some(Field::Other(String::from("location"))),
259             13 => Some(Field::Other(String::from("referer"))),
260             14 => Some(Field::Other(String::from("set-cookie"))),
261             15..=21 => Some(Field::Method),
262             22..=23 => Some(Field::Scheme),
263             24..=28 => Some(Field::Status),
264             29..=30 => Some(Field::Other(String::from("accept"))),
265             31 => Some(Field::Other(String::from("accept-encoding"))),
266             32 => Some(Field::Other(String::from("accept-ranges"))),
267             33..=34 => Some(Field::Other(String::from("access-control-allow-headers"))),
268             35 => Some(Field::Other(String::from("access-control-allow-origin"))),
269             36..=41 => Some(Field::Other(String::from("cache-control"))),
270             42..=43 => Some(Field::Other(String::from("content-encoding"))),
271             44..=54 => Some(Field::Other(String::from("content-type"))),
272             55 => Some(Field::Other(String::from("range"))),
273             56..=58 => Some(Field::Other(String::from("strict-transport-security"))),
274             59..=60 => Some(Field::Other(String::from("vary"))),
275             61 => Some(Field::Other(String::from("x-content-type-options"))),
276             62 => Some(Field::Other(String::from("x-xss-protection"))),
277             63..=71 => Some(Field::Status),
278             72 => Some(Field::Other(String::from("accept-language"))),
279             73..=74 => Some(Field::Other(String::from(
280                 "access-control-allow-credentials",
281             ))),
282             75 => Some(Field::Other(String::from("access-control-allow-headers"))),
283             76..=78 => Some(Field::Other(String::from("access-control-allow-methods"))),
284             79 => Some(Field::Other(String::from("access-control-expose-headers"))),
285             80 => Some(Field::Other(String::from("access-control-request-headers"))),
286             81..=82 => Some(Field::Other(String::from("access-control-request-method"))),
287             83 => Some(Field::Other(String::from("alt-svc"))),
288             84 => Some(Field::Other(String::from("authorization"))),
289             85 => Some(Field::Other(String::from("content-security-policy"))),
290             86 => Some(Field::Other(String::from("early-data"))),
291             87 => Some(Field::Other(String::from("expect-ct"))),
292             88 => Some(Field::Other(String::from("forwarded"))),
293             89 => Some(Field::Other(String::from("if-range"))),
294             90 => Some(Field::Other(String::from("origin"))),
295             91 => Some(Field::Other(String::from("purpose"))),
296             92 => Some(Field::Other(String::from("server"))),
297             93 => Some(Field::Other(String::from("timing-allow-origin"))),
298             94 => Some(Field::Other(String::from("upgrade-insecure-requests"))),
299             95 => Some(Field::Other(String::from("user-agent"))),
300             96 => Some(Field::Other(String::from("x-forwarded-for"))),
301             97..=98 => Some(Field::Other(String::from("x-frame-options"))),
302             _ => None,
303         }
304     }
305 
306     /// Tries to get a `Field` and a value by the given index.
field(index: usize) -> Option<(Field, String)>307     fn field(index: usize) -> Option<(Field, String)> {
308         match index {
309             1 => Some((Field::Path, String::from("/"))),
310             2 => Some((Field::Other(String::from("age")), String::from("0"))),
311             4 => Some((
312                 Field::Other(String::from("content-length")),
313                 String::from("0"),
314             )),
315             15 => Some((Field::Method, String::from("CONNECT"))),
316             16 => Some((Field::Method, String::from("DELETE"))),
317             17 => Some((Field::Method, String::from("GET"))),
318             18 => Some((Field::Method, String::from("HEAD"))),
319             19 => Some((Field::Method, String::from("OPTIONS"))),
320             20 => Some((Field::Method, String::from("POST"))),
321             21 => Some((Field::Method, String::from("PUT"))),
322             22 => Some((Field::Scheme, String::from("http"))),
323             23 => Some((Field::Scheme, String::from("https"))),
324             24 => Some((Field::Status, String::from("103"))),
325             25 => Some((Field::Status, String::from("200"))),
326             26 => Some((Field::Status, String::from("304"))),
327             27 => Some((Field::Status, String::from("404"))),
328             28 => Some((Field::Status, String::from("503"))),
329             29 => Some((Field::Other(String::from("accept")), String::from("*/*"))),
330             30 => Some((
331                 Field::Other(String::from("accept")),
332                 String::from("application/dns-message"),
333             )),
334             31 => Some((
335                 Field::Other(String::from("accept-encoding")),
336                 String::from("gzip, deflate, br"),
337             )),
338             32 => Some((
339                 Field::Other(String::from("accept-ranges")),
340                 String::from("bytes"),
341             )),
342             33 => Some((
343                 Field::Other(String::from("access-control-allow-headers")),
344                 String::from("cache-control"),
345             )),
346             34 => Some((
347                 Field::Other(String::from("access-control-allow-headers")),
348                 String::from("content-type"),
349             )),
350             35 => Some((
351                 Field::Other(String::from("access-control-allow-origin")),
352                 String::from("*"),
353             )),
354             36 => Some((
355                 Field::Other(String::from("cache-control")),
356                 String::from("max-age=0"),
357             )),
358             37 => Some((
359                 Field::Other(String::from("cache-control")),
360                 String::from("max-age=2592000"),
361             )),
362             38 => Some((
363                 Field::Other(String::from("cache-control")),
364                 String::from("max-age=604800"),
365             )),
366             39 => Some((
367                 Field::Other(String::from("cache-control")),
368                 String::from("no-cache"),
369             )),
370             40 => Some((
371                 Field::Other(String::from("cache-control")),
372                 String::from("no-store"),
373             )),
374             41 => Some((
375                 Field::Other(String::from("cache-control")),
376                 String::from("public, max-age=31536000"),
377             )),
378             42 => Some((
379                 Field::Other(String::from("content-encoding")),
380                 String::from("br"),
381             )),
382             43 => Some((
383                 Field::Other(String::from("content-encoding")),
384                 String::from("gzip"),
385             )),
386             44 => Some((
387                 Field::Other(String::from("content-type")),
388                 String::from("application/dns-message"),
389             )),
390             45 => Some((
391                 Field::Other(String::from("content-type")),
392                 String::from("application/javascript"),
393             )),
394             46 => Some((
395                 Field::Other(String::from("content-type")),
396                 String::from("application/json"),
397             )),
398             47 => Some((
399                 Field::Other(String::from("content-type")),
400                 String::from("application/x-www-form-urlencoded"),
401             )),
402             48 => Some((
403                 Field::Other(String::from("content-type")),
404                 String::from("image/gif"),
405             )),
406             49 => Some((
407                 Field::Other(String::from("content-type")),
408                 String::from("image/jpeg"),
409             )),
410             50 => Some((
411                 Field::Other(String::from("content-type")),
412                 String::from("image/png"),
413             )),
414             51 => Some((
415                 Field::Other(String::from("content-type")),
416                 String::from("text/css"),
417             )),
418             52 => Some((
419                 Field::Other(String::from("content-type")),
420                 String::from("text/html; charset=utf-8"),
421             )),
422             53 => Some((
423                 Field::Other(String::from("content-type")),
424                 String::from("text/plain"),
425             )),
426             54 => Some((
427                 Field::Other(String::from("content-type")),
428                 String::from("text/plain;charset=utf-8"),
429             )),
430             55 => Some((
431                 Field::Other(String::from("range")),
432                 String::from("bytes=0-"),
433             )),
434             56 => Some((
435                 Field::Other(String::from("strict-transport-security")),
436                 String::from("max-age=31536000"),
437             )),
438             57 => Some((
439                 Field::Other(String::from("strict-transport-security")),
440                 String::from("max-age=31536000; includesubdomains"),
441             )),
442             58 => Some((
443                 Field::Other(String::from("strict-transport-security")),
444                 String::from("max-age=31536000; includesubdomains; preload"),
445             )),
446             59 => Some((
447                 Field::Other(String::from("vary")),
448                 String::from("accept-encoding"),
449             )),
450             60 => Some((Field::Other(String::from("vary")), String::from("origin"))),
451             61 => Some((
452                 Field::Other(String::from("x-content-type-options")),
453                 String::from("nosniff"),
454             )),
455             62 => Some((
456                 Field::Other(String::from("x-xss-protection")),
457                 String::from("1; mode=block"),
458             )),
459             63 => Some((Field::Status, String::from("100"))),
460             64 => Some((Field::Status, String::from("204"))),
461             65 => Some((Field::Status, String::from("206"))),
462             66 => Some((Field::Status, String::from("302"))),
463             67 => Some((Field::Status, String::from("400"))),
464             68 => Some((Field::Status, String::from("403"))),
465             69 => Some((Field::Status, String::from("421"))),
466             70 => Some((Field::Status, String::from("425"))),
467             71 => Some((Field::Status, String::from("500"))),
468             73 => Some((
469                 Field::Other(String::from("access-control-allow-credentials")),
470                 String::from("FALSE"),
471             )),
472             74 => Some((
473                 Field::Other(String::from("access-control-allow-credentials")),
474                 String::from("TRUE"),
475             )),
476             75 => Some((
477                 Field::Other(String::from("access-control-allow-headers")),
478                 String::from("*"),
479             )),
480             76 => Some((
481                 Field::Other(String::from("access-control-allow-methods")),
482                 String::from("get"),
483             )),
484             77 => Some((
485                 Field::Other(String::from("access-control-allow-methods")),
486                 String::from("get, post, options"),
487             )),
488             78 => Some((
489                 Field::Other(String::from("access-control-allow-methods")),
490                 String::from("options"),
491             )),
492             79 => Some((
493                 Field::Other(String::from("access-control-expose-headers")),
494                 String::from("content-length"),
495             )),
496             80 => Some((
497                 Field::Other(String::from("access-control-request-headers")),
498                 String::from("content-type"),
499             )),
500             81 => Some((
501                 Field::Other(String::from("access-control-request-method")),
502                 String::from("get"),
503             )),
504             82 => Some((
505                 Field::Other(String::from("access-control-request-method")),
506                 String::from("post"),
507             )),
508             83 => Some((Field::Other(String::from("alt-svc")), String::from("clear"))),
509             85 => Some((
510                 Field::Other(String::from("content-security-policy")),
511                 String::from("script-src 'none'; object-src 'none'; base-uri 'none'"),
512             )),
513             86 => Some((Field::Other(String::from("early-data")), String::from("1"))),
514             91 => Some((
515                 Field::Other(String::from("purpose")),
516                 String::from("prefetch"),
517             )),
518             93 => Some((
519                 Field::Other(String::from("timing-allow-origin")),
520                 String::from("*"),
521             )),
522             94 => Some((
523                 Field::Other(String::from("upgrade-insecure-requests")),
524                 String::from("1"),
525             )),
526             97 => Some((
527                 Field::Other(String::from("x-frame-options")),
528                 String::from("deny"),
529             )),
530             98 => Some((
531                 Field::Other(String::from("x-frame-options")),
532                 String::from("sameorigin"),
533             )),
534             _ => None,
535         }
536     }
537 
538     /// Tries to get a `Index` by the given field and value.
index(field: &Field, value: &str) -> Option<TableIndex>539     fn index(field: &Field, value: &str) -> Option<TableIndex> {
540         match (field, value) {
541             (Field::Authority, _) => Some(TableIndex::FieldName(0)),
542             (Field::Path, "/") => Some(TableIndex::Field(1)),
543             (Field::Path, _) => Some(TableIndex::FieldName(1)),
544             (Field::Method, "CONNECT") => Some(TableIndex::Field(15)),
545             (Field::Method, "DELETE") => Some(TableIndex::Field(16)),
546             (Field::Method, "GET") => Some(TableIndex::Field(17)),
547             (Field::Method, "HEAD") => Some(TableIndex::Field(18)),
548             (Field::Method, "OPTIONS") => Some(TableIndex::Field(19)),
549             (Field::Method, "POST") => Some(TableIndex::Field(20)),
550             (Field::Method, "PUT") => Some(TableIndex::Field(21)),
551             (Field::Method, _) => Some(TableIndex::FieldName(15)),
552             (Field::Scheme, "http") => Some(TableIndex::Field(22)),
553             (Field::Scheme, "https") => Some(TableIndex::Field(23)),
554             (Field::Scheme, _) => Some(TableIndex::FieldName(22)),
555             (Field::Status, "103") => Some(TableIndex::Field(24)),
556             (Field::Status, "200") => Some(TableIndex::Field(25)),
557             (Field::Status, "304") => Some(TableIndex::Field(26)),
558             (Field::Status, "404") => Some(TableIndex::Field(27)),
559             (Field::Status, "503") => Some(TableIndex::Field(28)),
560             (Field::Status, "100") => Some(TableIndex::Field(63)),
561             (Field::Status, "204") => Some(TableIndex::Field(64)),
562             (Field::Status, "206") => Some(TableIndex::Field(65)),
563             (Field::Status, "302") => Some(TableIndex::Field(66)),
564             (Field::Status, "400") => Some(TableIndex::Field(67)),
565             (Field::Status, "403") => Some(TableIndex::Field(68)),
566             (Field::Status, "421") => Some(TableIndex::Field(69)),
567             (Field::Status, "425") => Some(TableIndex::Field(70)),
568             (Field::Status, "500") => Some(TableIndex::Field(71)),
569             (Field::Status, _) => Some(TableIndex::FieldName(24)),
570             (Field::Other(s), v) => match (s.as_str(), v) {
571                 ("age", "0") => Some(TableIndex::Field(2)),
572                 ("age", _) => Some(TableIndex::FieldName(2)),
573                 ("content-disposition", _) => Some(TableIndex::FieldName(3)),
574                 ("content-length", "0") => Some(TableIndex::Field(4)),
575                 ("content-length", _) => Some(TableIndex::FieldName(4)),
576                 ("cookie", _) => Some(TableIndex::FieldName(5)),
577                 ("date", _) => Some(TableIndex::FieldName(6)),
578                 ("etag", _) => Some(TableIndex::FieldName(7)),
579                 ("if-modified-since", _) => Some(TableIndex::FieldName(8)),
580                 ("if-none-match", _) => Some(TableIndex::FieldName(9)),
581                 ("last-modified", _) => Some(TableIndex::FieldName(10)),
582                 ("link", _) => Some(TableIndex::FieldName(11)),
583                 ("location", _) => Some(TableIndex::FieldName(12)),
584                 ("referer", _) => Some(TableIndex::FieldName(13)),
585                 ("set-cookie", _) => Some(TableIndex::FieldName(14)),
586                 ("accept", "*/*") => Some(TableIndex::Field(29)),
587                 ("accept", "application/dns-message") => Some(TableIndex::Field(30)),
588                 ("accept", _) => Some(TableIndex::FieldName(29)),
589                 ("accept-encoding", "gzip, deflate, br") => Some(TableIndex::Field(31)),
590                 ("accept-encoding", _) => Some(TableIndex::FieldName(31)),
591                 ("accept-ranges", "bytes") => Some(TableIndex::Field(32)),
592                 ("accept-ranges", _) => Some(TableIndex::FieldName(32)),
593                 ("access-control-allow-headers", "cache-control") => Some(TableIndex::Field(33)),
594                 ("access-control-allow-headers", "content-type") => Some(TableIndex::Field(34)),
595                 ("access-control-allow-origin", "*") => Some(TableIndex::Field(35)),
596                 ("access-control-allow-origin", _) => Some(TableIndex::FieldName(35)),
597                 ("cache-control", "max-age=0") => Some(TableIndex::Field(36)),
598                 ("cache-control", "max-age=2592000") => Some(TableIndex::Field(37)),
599                 ("cache-control", "max-age=604800") => Some(TableIndex::Field(38)),
600                 ("cache-control", "no-cache") => Some(TableIndex::Field(39)),
601                 ("cache-control", "no-store") => Some(TableIndex::Field(40)),
602                 ("cache-control", "public, max-age=31536000") => Some(TableIndex::Field(41)),
603                 ("cache-control", _) => Some(TableIndex::FieldName(36)),
604                 ("content-encoding", "br") => Some(TableIndex::Field(42)),
605                 ("content-encoding", "gzip") => Some(TableIndex::Field(43)),
606                 ("content-encoding", _) => Some(TableIndex::FieldName(42)),
607                 ("content-type", "application/dns-message") => Some(TableIndex::Field(44)),
608                 ("content-type", "application/javascript") => Some(TableIndex::Field(45)),
609                 ("content-type", "application/json") => Some(TableIndex::Field(46)),
610                 ("content-type", "application/x-www-form-urlencoded") => {
611                     Some(TableIndex::Field(47))
612                 }
613                 ("content-type", "image/gif") => Some(TableIndex::Field(48)),
614                 ("content-type", "image/jpeg") => Some(TableIndex::Field(49)),
615                 ("content-type", "image/png") => Some(TableIndex::Field(50)),
616                 ("content-type", "text/css") => Some(TableIndex::Field(51)),
617                 ("content-type", "text/html; charset=utf-8") => Some(TableIndex::Field(52)),
618                 ("content-type", "text/plain") => Some(TableIndex::Field(53)),
619                 ("content-type", "text/plain;charset=utf-8") => Some(TableIndex::Field(54)),
620                 ("content-type", _) => Some(TableIndex::FieldName(44)),
621                 ("range", "bytes=0-") => Some(TableIndex::Field(55)),
622                 ("range", _) => Some(TableIndex::FieldName(55)),
623                 ("strict-transport-security", "max-age=31536000") => Some(TableIndex::Field(56)),
624                 ("strict-transport-security", "max-age=31536000; includesubdomains") => {
625                     Some(TableIndex::Field(57))
626                 }
627                 ("strict-transport-security", "max-age=31536000; includesubdomains; preload") => {
628                     Some(TableIndex::Field(58))
629                 }
630                 ("strict-transport-security", _) => Some(TableIndex::FieldName(56)),
631                 ("vary", "accept-encoding") => Some(TableIndex::Field(59)),
632                 ("vary", "origin") => Some(TableIndex::Field(60)),
633                 ("vary", _) => Some(TableIndex::FieldName(59)),
634                 ("x-content-type-options", "nosniff") => Some(TableIndex::Field(61)),
635                 ("x-content-type-options", _) => Some(TableIndex::FieldName(61)),
636                 ("x-xss-protection", "1; mode=block") => Some(TableIndex::Field(62)),
637                 ("x-xss-protection", _) => Some(TableIndex::FieldName(62)),
638                 ("accept-language", _) => Some(TableIndex::FieldName(72)),
639                 ("access-control-allow-credentials", "FALSE") => Some(TableIndex::Field(73)),
640                 ("access-control-allow-credentials", "TRUE") => Some(TableIndex::Field(74)),
641                 ("access-control-allow-credentials", _) => Some(TableIndex::FieldName(73)),
642                 ("access-control-allow-headers", "*") => Some(TableIndex::Field(75)),
643                 ("access-control-allow-headers", _) => Some(TableIndex::FieldName(75)),
644                 ("access-control-allow-methods", "get") => Some(TableIndex::Field(76)),
645                 ("access-control-allow-methods", "get, post, options") => {
646                     Some(TableIndex::Field(77))
647                 }
648                 ("access-control-allow-methods", "options") => Some(TableIndex::Field(78)),
649                 ("access-control-allow-methods", _) => Some(TableIndex::FieldName(76)),
650                 ("access-control-expose-headers", "content-length") => Some(TableIndex::Field(79)),
651                 ("access-control-expose-headers", _) => Some(TableIndex::FieldName(79)),
652                 ("access-control-request-headers", "content-type") => Some(TableIndex::Field(80)),
653                 ("access-control-request-headers", _) => Some(TableIndex::FieldName(80)),
654                 ("access-control-request-method", "get") => Some(TableIndex::Field(81)),
655                 ("access-control-request-method", "post") => Some(TableIndex::Field(82)),
656                 ("access-control-request-method", _) => Some(TableIndex::FieldName(81)),
657                 ("alt-svc", "clear") => Some(TableIndex::Field(83)),
658                 ("alt-svc", _) => Some(TableIndex::FieldName(83)),
659                 ("authorization", _) => Some(TableIndex::FieldName(84)),
660                 (
661                     "content-security-policy",
662                     "script-src 'none'; object-src 'none'; base-uri 'none'",
663                 ) => Some(TableIndex::Field(85)),
664                 ("content-security-policy", _) => Some(TableIndex::FieldName(85)),
665                 ("early-data", "1") => Some(TableIndex::Field(86)),
666                 ("early-data", _) => Some(TableIndex::FieldName(86)),
667                 ("expect-ct", _) => Some(TableIndex::FieldName(87)),
668                 ("forwarded", _) => Some(TableIndex::FieldName(88)),
669                 ("if-range", _) => Some(TableIndex::FieldName(89)),
670                 ("origin", _) => Some(TableIndex::FieldName(90)),
671                 ("purpose", "prefetch") => Some(TableIndex::Field(91)),
672                 ("purpose", _) => Some(TableIndex::FieldName(91)),
673                 ("server", _) => Some(TableIndex::FieldName(92)),
674                 ("timing-allow-origin", "*") => Some(TableIndex::Field(93)),
675                 ("timing-allow-origin", _) => Some(TableIndex::FieldName(93)),
676                 ("upgrade-insecure-requests", "1") => Some(TableIndex::Field(94)),
677                 ("upgrade-insecure-requests", _) => Some(TableIndex::FieldName(94)),
678                 ("user-agent", _) => Some(TableIndex::FieldName(95)),
679                 ("x-forwarded-for", _) => Some(TableIndex::FieldName(96)),
680                 ("x-frame-options", "deny") => Some(TableIndex::Field(97)),
681                 ("x-frame-options", "sameorigin") => Some(TableIndex::Field(98)),
682                 ("x-frame-options", _) => Some(TableIndex::FieldName(97)),
683                 _ => None,
684             },
685         }
686     }
687 }
688 
689 #[derive(Clone, PartialEq, Eq, Debug)]
690 pub enum Field {
691     Authority,
692     Method,
693     Path,
694     Scheme,
695     Status,
696     Other(String),
697 }
698 
699 impl Field {
len(&self) -> usize700     pub(crate) fn len(&self) -> usize {
701         match self {
702             Field::Authority => 10, // 10 is the length of ":authority".
703             Field::Method => 7,     // 7 is the length of ":method".
704             Field::Path => 5,       // 5 is the length of ":path".
705             Field::Scheme => 7,     // 7 is the length of "scheme".
706             Field::Status => 7,     // 7 is the length of "status".
707             Field::Other(s) => s.len(),
708         }
709     }
710 
into_string(self) -> String711     pub(crate) fn into_string(self) -> String {
712         match self {
713             Field::Authority => String::from(":authority"),
714             Field::Method => String::from(":method"),
715             Field::Path => String::from(":path"),
716             Field::Scheme => String::from(":scheme"),
717             Field::Status => String::from(":status"),
718             Field::Other(s) => s,
719         }
720     }
721 }
722