From a08a6a723119805141fd1b70ccb50d39001d5777 Mon Sep 17 00:00:00 2001 From: wengshihao <173874942@qq.com> Date: Wed, 19 Jul 2023 17:33:05 +0800 Subject: [PATCH] Partially implemented QPACK table --- ylong_http/src/h3/mod.rs | 2 + ylong_http/src/h3/qpack/mod.rs | 3 + ylong_http/src/h3/qpack/table.rs | 472 +++++++++++++++++++++++++++++++ 3 files changed, 477 insertions(+) create mode 100644 ylong_http/src/h3/qpack/mod.rs create mode 100644 ylong_http/src/h3/qpack/table.rs diff --git a/ylong_http/src/h3/mod.rs b/ylong_http/src/h3/mod.rs index b75d4c3..6b3cd47 100644 --- a/ylong_http/src/h3/mod.rs +++ b/ylong_http/src/h3/mod.rs @@ -12,3 +12,5 @@ // limitations under the License. // TODO: `HTTP/3` Module. + +mod qpack; \ No newline at end of file diff --git a/ylong_http/src/h3/qpack/mod.rs b/ylong_http/src/h3/qpack/mod.rs new file mode 100644 index 0000000..97985c5 --- /dev/null +++ b/ylong_http/src/h3/qpack/mod.rs @@ -0,0 +1,3 @@ + + +pub(crate) mod table; \ No newline at end of file diff --git a/ylong_http/src/h3/qpack/table.rs b/ylong_http/src/h3/qpack/table.rs new file mode 100644 index 0000000..2727b62 --- /dev/null +++ b/ylong_http/src/h3/qpack/table.rs @@ -0,0 +1,472 @@ + +use std::collections::VecDeque; + +/// The [`Dynamic Table`][dynamic_table] implementation of [QPACK]. +/// +/// [dynamic_table]: https://www.rfc-editor.org/rfc/rfc9204.html#name-dynamic-table +/// [QPACK]: https://www.rfc-editor.org/rfc/rfc9204.html +/// # Introduction +/// The dynamic table consists of a list of field lines maintained in first-in, first-out order. +/// A QPACK encoder and decoder share a dynamic table that is initially empty. +/// The encoder adds entries to the dynamic table and sends them to the decoder via instructions on +/// the encoder stream +/// +/// The dynamic table can contain duplicate entries (i.e., entries with the same name and same value). +/// Therefore, duplicate entries MUST NOT be treated as an error by the decoder. +/// +/// Dynamic table entries can have empty values. + + +pub(crate) struct DynamicTable { + queue: VecDeque, + // The size of the dynamic table is the sum of the size of its entries + size: usize, + capacity: usize, + abs_idx: usize, +} + +impl DynamicTable { + pub(crate) fn with_capacity(capacity: usize) -> Self { + Self { + queue: VecDeque::new(), + size: 0, + capacity, + abs_idx: 0, + } + } + + pub(crate) fn size(&self) -> usize { + self.size + } + + pub(crate) fn capacity(&self) -> usize { + self.capacity + } + + /// ## RFC9204-2.1.1: + /// Inserting entries into the dynamic table might not be possible if the table contains entries + /// that cannot be evicted. + /// + /// A dynamic table entry cannot be evicted immediately after insertion, even if it has never + /// been referenced. Once the insertion of a dynamic table entry has been acknowledged and there + /// are no outstanding references to the entry in unacknowledged representations, + /// the entry becomes evictable. Note that references on the encoder stream never preclude + /// the eviction of an entry, because those references are guaranteed to be processed before + /// the instruction evicting the entry. + pub(crate) fn update(&mut self, name: Field, value: String, refences: Vec) { + // Check if the new entry is evictable + let is_evictable = self.queue.len() > 0 && self.queue.back().unwrap().is_evictable; + // Calculate the size of the new entry + let entry_size = name.size() + value.len() + 32; + + /// ## RFC9204-3.2.2: + /// Before a new entry is added to the dynamic table, entries are evicted from the end of + /// the dynamic table until the size of the dynamic table is less than or equal to + /// (table capacity - size of new entry). The encoder MUST NOT cause a dynamic table entry + /// to be evicted unless that entry is evictable; see Section 2.1.1. + /// The new entry is then added to the table. + + // Evict entries if necessary + while self.size + entry_size > self.capacity && !self.queue.is_empty() { + if let Some(entry) = self.queue.pop_back() { + /// ## RFC9204-3.2.2: + /// A new entry can reference an entry in the dynamic table that will be evicted + /// when adding this new entry into the dynamic table. Implementations + /// are cautioned to avoid deleting the referenced name or value if the referenced + /// entry is evicted from the dynamic table prior to inserting the new entry + if entry.is_evictable && !entry.referenced(&refences){ + self.size -= entry.name.size() + entry.value.len() + 32; + } else { + // Put the non-evictable or referenced entry back in the queue + self.queue.push_back(entry); + break; + } + } else { + break; // No more entries to evict + } + } + + // Add the new entry to the table + self.queue.push_front(TableEntry { + name, + value, + /// ## RFC9204-3.2.4: + /// Each entry possesses an absolute index that is fixed for the lifetime of that entry. + /// The first entry inserted has an absolute index of 0; indices increase by one with + /// each insertion. + abs_idx: self.abs_idx, + ack: false, + is_evictable: false, + refences: refences, + }); + self.abs_idx += 1; + + /// ## RFC9204-3.2.1: + /// The size of the dynamic table is the sum of the size of its entries. + /// + /// The size of an entry is the sum of its name's length in bytes, its value's length in bytes, + /// and 32 additional bytes. The size of an entry is calculated using the length of its name + /// and value without Huffman encoding applied. + self.size += entry_size; + } + + pub(crate) fn update_capacity(&mut self, capacity: usize) { + /// ## RFC9204-3.2.2: + /// Whenever the dynamic table capacity is reduced by the encoder (Section 4.3.1), + /// entries are evicted from the end of the dynamic table until the size of the dynamic + /// table is less than or equal to the new table capacity. This mechanism can + /// be used to completely clear entries from the dynamic table by setting a + /// capacity of 0, which can subsequently be restored. + self.capacity = capacity; + while self.size > self.capacity && !self.queue.is_empty() { + if let Some(entry) = self.queue.pop_back() { + + /// ## RFC9204-4.3.1: + /// Reducing the dynamic table capacity can cause entries to be evicted; + /// This MUST NOT cause the eviction of entries that are not evictable; + if entry.is_evictable { + self.size -= entry.name.size() + entry.value.len() + 32; + } else { + self.queue.push_back(entry); + break; + } + } else { + break; + } + } + } +} + +struct TableEntry { + name: Field, + value: String, + abs_idx: usize, + ack: bool, //TODO: to be modified + is_evictable: bool, + refences: Vec //TODO: to be modified +} + +impl TableEntry { + //check if the entry is referenced by the new entry + fn referenced(&self, refences: &Vec) -> bool { + todo!() + } + +} + + +pub(crate) enum TableIndex { + Field(usize), + FieldName(usize), +} + + +/// The [`Static Table`][static_table] implementation of [QPACK]. +/// +/// [static_table]: https://www.rfc-editor.org/rfc/rfc9204.html#static-table +/// [QPACK]: https://www.rfc-editor.org/rfc/rfc9204.html +/// +/// # Introduction +/// The static table consists of a predefined list of field lines, +/// each of which has a fixed index over time. +/// All entries in the static table have a name and a value. +/// However, values can be empty (that is, have a length of 0). Each entry is +/// identified by a unique index. +/// Note that the QPACK static table is indexed from 0, +/// whereas the HPACK static table is indexed from 1. +/// When the decoder encounters an invalid static table +/// index in a field line representation, it MUST treat this +/// as a connection error of type QPACK_DECOMPRESSION_FAILED. +/// If this index is received on the encoder stream, +/// this MUST be treated as a connection error of type QPACK_ENCODER_STREAM_ERROR. +/// + +struct StaticTable; + +impl StaticTable { + /// Gets a `Field` by the given index. + fn field_name(index: usize) -> Option { + match index { + 0 => Some(Field::Authority), + 1 => Some(Field::Path), + 2 => Some(Field::Other(String::from("age"))), + 3 => Some(Field::Other(String::from("content-disposition"))), + 4 => Some(Field::Other(String::from("content-length"))), + 5 => Some(Field::Other(String::from("cookie"))), + 6 => Some(Field::Other(String::from("date"))), + 7 => Some(Field::Other(String::from("etag"))), + 8 => Some(Field::Other(String::from("if-modified-since"))), + 9 => Some(Field::Other(String::from("if-none-match"))), + 10 => Some(Field::Other(String::from("last-modified"))), + 11 => Some(Field::Other(String::from("link"))), + 12 => Some(Field::Other(String::from("location"))), + 13 => Some(Field::Other(String::from("referer"))), + 14 => Some(Field::Other(String::from("set-cookie"))), + 15..=21 => Some(Field::Method), + 22..=23 => Some(Field::Scheme), + 24..=28 => Some(Field::Status), + 29..=30 => Some(Field::Other(String::from("accept"))), + 31 => Some(Field::Other(String::from("accept-encoding"))), + 32 => Some(Field::Other(String::from("accept-ranges"))), + 33..=34 => Some(Field::Other(String::from("access-control-allow-headers"))), + 35 => Some(Field::Other(String::from("access-control-allow-origin"))), + 36..=41 => Some(Field::Other(String::from("cache-control"))), + 42..=43 => Some(Field::Other(String::from("content-encoding"))), + 44..=54 => Some(Field::Other(String::from("content-type"))), + 55 => Some(Field::Other(String::from("range"))), + 56..=58 => Some(Field::Other(String::from("strict-transport-security"))), + 59..=60 => Some(Field::Other(String::from("vary"))), + 61 => Some(Field::Other(String::from("x-content-type-options"))), + 62 => Some(Field::Other(String::from("x-xss-protection"))), + 63..=71 => Some(Field::Status), + 72 => Some(Field::Other(String::from("accept-language"))), + 73..=74 => Some(Field::Other(String::from("access-control-allow-credentials"))), + 75 => Some(Field::Other(String::from("access-control-allow-headers"))), + 76..=78 => Some(Field::Other(String::from("access-control-allow-methods"))), + 79 => Some(Field::Other(String::from("access-control-expose-headers"))), + 80 => Some(Field::Other(String::from("access-control-request-headers"))), + 81..=82 => Some(Field::Other(String::from("access-control-request-method"))), + 83 => Some(Field::Other(String::from("alt-svc"))), + 84 => Some(Field::Other(String::from("authorization"))), + 85 => Some(Field::Other(String::from("content-security-policy"))), + 86 => Some(Field::Other(String::from("early-data"))), + 87 => Some(Field::Other(String::from("expect-ct"))), + 88 => Some(Field::Other(String::from("forwarded"))), + 89 => Some(Field::Other(String::from("if-range"))), + 90 => Some(Field::Other(String::from("origin"))), + 91 => Some(Field::Other(String::from("purpose"))), + 92 => Some(Field::Other(String::from("server"))), + 93 => Some(Field::Other(String::from("timing-allow-origin"))), + 94 => Some(Field::Other(String::from("upgrade-insecure-requests"))), + 95 => Some(Field::Other(String::from("user-agent"))), + 96 => Some(Field::Other(String::from("x-forwarded-for"))), + 97..=98 => Some(Field::Other(String::from("x-frame-options"))), + _ => None, + } + } + + /// Tries to get a `Field` and a value by the given index. + fn field(index: usize) -> Option<(Field, String)> { + match index { + 1 => Some((Field::Path, String::from("/"))), + 2 => Some((Field::Other(String::from("age")), String::from("0"))), + 4 => Some((Field::Other(String::from("content-length")), String::from("0"))), + 15 => Some((Field::Method, String::from("CONNECT"))), + 16 => Some((Field::Method, String::from("DELETE"))), + 17 => Some((Field::Method, String::from("GET"))), + 18 => Some((Field::Method, String::from("HEAD"))), + 19 => Some((Field::Method, String::from("OPTIONS"))), + 20 => Some((Field::Method, String::from("POST"))), + 21 => Some((Field::Method, String::from("PUT"))), + 22 => Some((Field::Scheme, String::from("http"))), + 23 => Some((Field::Scheme, String::from("https"))), + 24 => Some((Field::Status, String::from("103"))), + 25 => Some((Field::Status, String::from("200"))), + 26 => Some((Field::Status, String::from("304"))), + 27 => Some((Field::Status, String::from("404"))), + 28 => Some((Field::Status, String::from("503"))), + 29 => Some((Field::Other(String::from("accept")), String::from("*/*"))), + 30 => Some((Field::Other(String::from("accept")), String::from("application/dns-message"))), + 31 => Some((Field::Other(String::from("accept-encoding")), String::from("gzip, deflate, br"))), + 32 => Some((Field::Other(String::from("accept-ranges")), String::from("bytes"))), + 33 => Some((Field::Other(String::from("access-control-allow-headers")), String::from("cache-control"))), + 34 => Some((Field::Other(String::from("access-control-allow-headers")), String::from("content-type"))), + 35 => Some((Field::Other(String::from("access-control-allow-origin")), String::from("*"))), + 36 => Some((Field::Other(String::from("cache-control")), String::from("max-age=0"))), + 37 => Some((Field::Other(String::from("cache-control")), String::from("max-age=2592000"))), + 38 => Some((Field::Other(String::from("cache-control")), String::from("max-age=604800"))), + 39 => Some((Field::Other(String::from("cache-control")), String::from("no-cache"))), + 40 => Some((Field::Other(String::from("cache-control")), String::from("no-store"))), + 41 => Some((Field::Other(String::from("cache-control")), String::from("public, max-age=31536000"))), + 42 => Some((Field::Other(String::from("content-encoding")), String::from("br"))), + 43 => Some((Field::Other(String::from("content-encoding")), String::from("gzip"))), + 44 => Some((Field::Other(String::from("content-type")), String::from("application/dns-message"))), + 45 => Some((Field::Other(String::from("content-type")), String::from("application/javascript"))), + 46 => Some((Field::Other(String::from("content-type")), String::from("application/json"))), + 47 => Some((Field::Other(String::from("content-type")), String::from("application/x-www-form-urlencoded"))), + 48 => Some((Field::Other(String::from("content-type")), String::from("image/gif"))), + 49 => Some((Field::Other(String::from("content-type")), String::from("image/jpeg"))), + 50 => Some((Field::Other(String::from("content-type")), String::from("image/png"))), + 51 => Some((Field::Other(String::from("content-type")), String::from("text/css"))), + 52 => Some((Field::Other(String::from("content-type")), String::from("text/html; charset=utf-8"))), + 53 => Some((Field::Other(String::from("content-type")), String::from("text/plain"))), + 54 => Some((Field::Other(String::from("content-type")), String::from("text/plain;charset=utf-8"))), + 55 => Some((Field::Other(String::from("range")), String::from("bytes=0-"))), + 56 => Some((Field::Other(String::from("strict-transport-security")), String::from("max-age=31536000"))), + 57 => Some((Field::Other(String::from("strict-transport-security")), String::from("max-age=31536000; includesubdomains"))), + 58 => Some((Field::Other(String::from("strict-transport-security")), String::from("max-age=31536000; includesubdomains; preload"))), + 59 => Some((Field::Other(String::from("vary")), String::from("accept-encoding"))), + 60 => Some((Field::Other(String::from("vary")), String::from("origin"))), + 61 => Some((Field::Other(String::from("x-content-type-options")), String::from("nosniff"))), + 62 => Some((Field::Other(String::from("x-xss-protection")), String::from("1; mode=block"))), + 63 => Some((Field::Status, String::from("100"))), + 64 => Some((Field::Status, String::from("204"))), + 65 => Some((Field::Status, String::from("206"))), + 66 => Some((Field::Status, String::from("302"))), + 67 => Some((Field::Status, String::from("400"))), + 68 => Some((Field::Status, String::from("403"))), + 69 => Some((Field::Status, String::from("421"))), + 70 => Some((Field::Status, String::from("425"))), + 71 => Some((Field::Status, String::from("500"))), + 73 => Some((Field::Other(String::from("access-control-allow-credentials")), String::from("FALSE"))), + 74 => Some((Field::Other(String::from("access-control-allow-credentials")), String::from("TRUE"))), + 75 => Some((Field::Other(String::from("access-control-allow-headers")), String::from("*"))), + 76 => Some((Field::Other(String::from("access-control-allow-methods")), String::from("get"))), + 77 => Some((Field::Other(String::from("access-control-allow-methods")), String::from("get, post, options"))), + 78 => Some((Field::Other(String::from("access-control-allow-methods")), String::from("options"))), + 79 => Some((Field::Other(String::from("access-control-expose-headers")), String::from("content-length"))), + 80 => Some((Field::Other(String::from("access-control-request-headers")), String::from("content-type"))), + 81 => Some((Field::Other(String::from("access-control-request-method")), String::from("get"))), + 82 => Some((Field::Other(String::from("access-control-request-method")), String::from("post"))), + 83 => Some((Field::Other(String::from("alt-svc")), String::from("clear"))), + 85 => Some((Field::Other(String::from("content-security-policy")), String::from("script-src 'none'; object-src 'none'; base-uri 'none'"))), + 86 => Some((Field::Other(String::from("early-data")), String::from("1"))), + 91 => Some((Field::Other(String::from("purpose")), String::from("prefetch"))), + 93 => Some((Field::Other(String::from("timing-allow-origin")), String::from("*"))), + 94 => Some((Field::Other(String::from("upgrade-insecure-requests")), String::from("1"))), + 97 => Some((Field::Other(String::from("x-frame-options")), String::from("deny"))), + 98 => Some((Field::Other(String::from("x-frame-options")), String::from("sameorigin"))), + _ => None, + } + } + + /// Tries to get a `Index` by the given field and value. + fn index(field: &Field, value: &str) -> Option { + match (field, value) { + (Field::Authority, _) => Some(TableIndex::FieldName(0)), + (Field::Path, "/") => Some(TableIndex::Field(1)), + (Field::Other(String::from("age")), "0") => Some(TableIndex::Field(2)), + (Field::Other(String::from("content-disposition")), _) => Some(TableIndex::FieldName(3)), + (Field::Other(String::from("content-length")), "0") => Some(TableIndex::Field(4)), + (Field::Other(String::from("cookie")), _) => Some(TableIndex::FieldName(5)), + (Field::Other(String::from("date")), _) => Some(TableIndex::FieldName(6)), + (Field::Other(String::from("etag")), _) => Some(TableIndex::FieldName(7)), + (Field::Other(String::from("if-modified-since")), _) => Some(TableIndex::FieldName(8)), + (Field::Other(String::from("if-none-match")), _) => Some(TableIndex::FieldName(9)), + (Field::Other(String::from("last-modified")), _) => Some(TableIndex::FieldName(10)), + (Field::Other(String::from("link")), _) => Some(TableIndex::FieldName(11)), + (Field::Other(String::from("location")), _) => Some(TableIndex::FieldName(12)), + (Field::Other(String::from("referer")), _) => Some(TableIndex::FieldName(13)), + (Field::Other(String::from("set-cookie")), _) => Some(TableIndex::FieldName(14)), + (Field::Method, "CONNECT") => Some(TableIndex::Field(15)), + (Field::Method, "DELETE") => Some(TableIndex::Field(16)), + (Field::Method, "GET") => Some(TableIndex::Field(17)), + (Field::Method, "HEAD") => Some(TableIndex::Field(18)), + (Field::Method, "OPTIONS") => Some(TableIndex::Field(19)), + (Field::Method, "POST") => Some(TableIndex::Field(20)), + (Field::Method, "PUT") => Some(TableIndex::Field(21)), + (Field::Scheme, "http") => Some(TableIndex::Field(22)), + (Field::Scheme, "https") => Some(TableIndex::Field(23)), + (Field::Status, "103") => Some(TableIndex::Field(24)), + (Field::Status, "200") => Some(TableIndex::Field(25)), + (Field::Status, "304") => Some(TableIndex::Field(26)), + (Field::Status, "404") => Some(TableIndex::Field(27)), + (Field::Status, "503") => Some(TableIndex::Field(28)), + (Field::Other(String::from("accept")), "*/*") => Some(TableIndex::Field(29)), + (Field::Other(String::from("accept")), "application/dns-message") => Some(TableIndex::Field(30)), + (Field::Other(String::from("accept-encoding")), "gzip, deflate, br") => Some(TableIndex::Field(31)), + (Field::Other(String::from("accept-ranges")), "bytes") => Some(TableIndex::Field(32)), + (Field::Other(String::from("access-control-allow-headers")), "cache-control") => Some(TableIndex::Field(33)), + (Field::Other(String::from("access-control-allow-headers")), "content-type") => Some(TableIndex::Field(34)), + (Field::Other(String::from("access-control-allow-origin")), "*") => Some(TableIndex::Field(35)), + (Field::Other(String::from("cache-control")), "max-age=0") => Some(TableIndex::Field(36)), + (Field::Other(String::from("cache-control")), "max-age=2592000") => Some(TableIndex::Field(37)), + (Field::Other(String::from("cache-control")), "max-age=604800") => Some(TableIndex::Field(38)), + (Field::Other(String::from("cache-control")), "no-cache") => Some(TableIndex::Field(39)), + (Field::Other(String::from("cache-control")), "no-store") => Some(TableIndex::Field(40)), + (Field::Other(String::from("cache-control")), "public, max-age=31536000") => Some(TableIndex::Field(41)), + (Field::Other(String::from("content-encoding")), "br") => Some(TableIndex::Field(42)), + (Field::Other(String::from("content-encoding")), "gzip") => Some(TableIndex::Field(43)), + (Field::Other(String::from("content-type")), "application/dns-message") => Some(TableIndex::Field(44)), + (Field::Other(String::from("content-type")), "application/javascript") => Some(TableIndex::Field(45)), + (Field::Other(String::from("content-type")), "application/json") => Some(TableIndex::Field(46)), + (Field::Other(String::from("content-type")), "application/x-www-form-urlencoded") => Some(TableIndex::Field(47)), + (Field::Other(String::from("content-type")), "image/gif") => Some(TableIndex::Field(48)), + (Field::Other(String::from("content-type")), "image/jpeg") => Some(TableIndex::Field(49)), + (Field::Other(String::from("content-type")), "image/png") => Some(TableIndex::Field(50)), + (Field::Other(String::from("content-type")), "text/css") => Some(TableIndex::Field(51)), + (Field::Other(String::from("content-type")), "text/html; charset=utf-8") => Some(TableIndex::Field(52)), + (Field::Other(String::from("content-type")), "text/plain") => Some(TableIndex::Field(53)), + (Field::Other(String::from("content-type")), "text/plain;charset=utf-8") => Some(TableIndex::Field(54)), + (Field::Other(String::from("range")), "bytes=0-") => Some(TableIndex::Field(55)), + (Field::Other(String::from("strict-transport-security")), "max-age=31536000") => Some(TableIndex::Field(56)), + (Field::Other(String::from("strict-transport-security")), "max-age=31536000; includesubdomains") => Some(TableIndex::Field(57)), + (Field::Other(String::from("strict-transport-security")), "max-age=31536000; includesubdomains; preload") => Some(TableIndex::Field(58)), + (Field::Other(String::from("vary")), "accept-encoding") => Some(TableIndex::Field(59)), + (Field::Other(String::from("vary")), "origin") => Some(TableIndex::Field(60)), + (Field::Other(String::from("x-content-type-options")), "nosniff") => Some(TableIndex::Field(61)), + (Field::Other(String::from("x-xss-protection")), "1; mode=block") => Some(TableIndex::Field(62)), + (Field::Status, "100") => Some(TableIndex::Field(63)), + (Field::Status, "204") => Some(TableIndex::Field(64)), + (Field::Status, "206") => Some(TableIndex::Field(65)), + (Field::Status, "302") => Some(TableIndex::Field(66)), + (Field::Status, "400") => Some(TableIndex::Field(67)), + (Field::Status, "403") => Some(TableIndex::Field(68)), + (Field::Status, "421") => Some(TableIndex::Field(69)), + (Field::Status, "425") => Some(TableIndex::Field(70)), + (Field::Status, "500") => Some(TableIndex::Field(71)), + (Field::Other(String::from("accept-language")), _) => Some(TableIndex::FieldName(72)), + (Field::Other(String::from("access-control-allow-credentials")), "FALSE") => Some(TableIndex::Field(73)), + (Field::Other(String::from("access-control-allow-credentials")), "TRUE") => Some(TableIndex::Field(74)), + (Field::Other(String::from("access-control-allow-headers")), "*") => Some(TableIndex::Field(75)), + (Field::Other(String::from("access-control-allow-methods")), "get") => Some(TableIndex::Field(76)), + (Field::Other(String::from("access-control-allow-methods")), "get, post, options") => Some(TableIndex::Field(77)), + (Field::Other(String::from("access-control-allow-methods")), "options") => Some(TableIndex::Field(78)), + (Field::Other(String::from("access-control-expose-headers")), "content-length") => Some(TableIndex::Field(79)), + (Field::Other(String::from("access-control-request-headers")), "content-type") => Some(TableIndex::Field(80)), + (Field::Other(String::from("access-control-request-method")), "get") => Some(TableIndex::Field(81)), + (Field::Other(String::from("access-control-request-method")), "post") => Some(TableIndex::Field(82)), + (Field::Other(String::from("alt-svc")), "clear") => Some(TableIndex::Field(83)), + (Field::Other(String::from("authorization")), _) => Some(TableIndex::FieldName(84)), + (Field::Other(String::from("content-security-policy")), "script-src 'none'; object-src 'none'; base-uri 'none'") => Some(TableIndex::Field(85)), + (Field::Other(String::from("early-data")), "1") => Some(TableIndex::Field(86)), + (Field::Other(String::from("expect-ct")), _) => Some(TableIndex::FieldName(87)), + (Field::Other(String::from("forwarded")), _) => Some(TableIndex::FieldName(88)), + (Field::Other(String::from("if-range")), _) => Some(TableIndex::FieldName(89)), + (Field::Other(String::from("origin")), _) => Some(TableIndex::FieldName(90)), + (Field::Other(String::from("purpose")), "prefetch") => Some(TableIndex::Field(91)), + (Field::Other(String::from("server")), _) => Some(TableIndex::FieldName(92)), + (Field::Other(String::from("timing-allow-origin")), "*") => Some(TableIndex::Field(93)), + (Field::Other(String::from("upgrade-insecure-requests")), "1") => Some(TableIndex::Field(94)), + (Field::Other(String::from("user-agent")), _) => Some(TableIndex::FieldName(95)), + (Field::Other(String::from("x-forwarded-for")), _) => Some(TableIndex::FieldName(96)), + (Field::Other(String::from("x-frame-options")), "deny") => Some(TableIndex::Field(97)), + (Field::Other(String::from("x-frame-options")), "sameorigin") => Some(TableIndex::Field(98)), + _ => None, + } + } +} + +#[derive(Clone, PartialEq, Eq)] +pub(crate) enum Field { + Authority, + Method, + Path, + Scheme, + Status, + Other(String), +} + +impl Field { + pub(crate) fn len(&self) -> usize { + match self { + Field::Authority => 10, // 10 is the length of ":authority". + Field::Method => 7, // 7 is the length of ":method". + Field::Path => 5, // 5 is the length of ":path". + Field::Scheme => 7, // 7 is the length of "scheme". + Field::Status => 7, // 7 is the length of "status". + Field::Other(s) => s.len(), + } + } + + pub(crate) fn into_string(self) -> String { + match self { + Field::Authority => String::from(":authority"), + Field::Method => String::from(":method"), + Field::Path => String::from(":path"), + Field::Scheme => String::from(":scheme"), + Field::Status => String::from(":status"), + Field::Other(s) => s, + } + } +} \ No newline at end of file -- Gitee