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 1/6] 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 From 780babf38bbf118c9bc391295cf37d7827a6a900 Mon Sep 17 00:00:00 2001 From: Weng Shihao Date: Wed, 9 Aug 2023 15:20:15 +0800 Subject: [PATCH 2/6] qpack --- docs/user_guide.md | 2 +- ylong_http/Cargo.toml | 2 +- ylong_http/src/h3/error.rs | 9 + ylong_http/src/h3/mod.rs | 5 +- ylong_http/src/h3/parts.rs | 61 ++ ylong_http/src/h3/pseudo.rs | 506 +++++++++++++ ylong_http/src/h3/qpack/decoder.rs | 100 +++ ylong_http/src/h3/qpack/encoder.rs | 119 +++ ylong_http/src/h3/qpack/integer.rs | 132 ++++ ylong_http/src/h3/qpack/mod.rs | 110 ++- .../src/h3/qpack/representation/decoder.rs | 201 +++++ .../src/h3/qpack/representation/encoder.rs | 706 ++++++++++++++++++ ylong_http/src/h3/qpack/representation/mod.rs | 7 + ylong_http/src/h3/qpack/table.rs | 339 ++++----- ylong_http_client/tests/file/root-ca.pem | 2 +- 15 files changed, 2115 insertions(+), 186 deletions(-) create mode 100644 ylong_http/src/h3/error.rs create mode 100644 ylong_http/src/h3/parts.rs create mode 100644 ylong_http/src/h3/pseudo.rs create mode 100644 ylong_http/src/h3/qpack/decoder.rs create mode 100644 ylong_http/src/h3/qpack/encoder.rs create mode 100644 ylong_http/src/h3/qpack/integer.rs create mode 100644 ylong_http/src/h3/qpack/representation/decoder.rs create mode 100644 ylong_http/src/h3/qpack/representation/encoder.rs create mode 100644 ylong_http/src/h3/qpack/representation/mod.rs diff --git a/docs/user_guide.md b/docs/user_guide.md index a81ac6b..db496e1 100644 --- a/docs/user_guide.md +++ b/docs/user_guide.md @@ -175,7 +175,7 @@ async fn upload_and_show_progress() { use ylong_http_client::async_impl::Client; use ylong_http_client::Request; -async fn send_request(client: Client, request: Request) { +async fn send_request(client: Client, request: Request) { // 发送请求,等待响应。 let _response = client.request(request).await.unwrap(); } diff --git a/ylong_http/Cargo.toml b/ylong_http/Cargo.toml index d6cb5df..ff0bf1c 100644 --- a/ylong_http/Cargo.toml +++ b/ylong_http/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["ylong", "http"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["http1_1", "tokio_base"] +default = ["http1_1", "tokio_base", "http2","huffman","http3"] full = [ "http1_1", "http2", diff --git a/ylong_http/src/h3/error.rs b/ylong_http/src/h3/error.rs new file mode 100644 index 0000000..eb45f2c --- /dev/null +++ b/ylong_http/src/h3/error.rs @@ -0,0 +1,9 @@ +pub enum H3Error { + //todo: add more + ConnectionError(ErrorCode), +} +#[derive(Debug, Eq, PartialEq, Clone)] +pub enum ErrorCode { + //todo: add more + TEST = 0xff, // todo: remove +} \ No newline at end of file diff --git a/ylong_http/src/h3/mod.rs b/ylong_http/src/h3/mod.rs index 6b3cd47..22272cd 100644 --- a/ylong_http/src/h3/mod.rs +++ b/ylong_http/src/h3/mod.rs @@ -13,4 +13,7 @@ // TODO: `HTTP/3` Module. -mod qpack; \ No newline at end of file +mod qpack; +mod parts; +mod pseudo; +mod error; \ No newline at end of file diff --git a/ylong_http/src/h3/parts.rs b/ylong_http/src/h3/parts.rs new file mode 100644 index 0000000..7bd4979 --- /dev/null +++ b/ylong_http/src/h3/parts.rs @@ -0,0 +1,61 @@ + + +use crate::h3::pseudo::PseudoHeaders; +use crate::headers::Headers; +use crate::h3::qpack::table::Field; +#[derive(PartialEq, Eq, Clone)] +pub struct Parts { + pub(crate) pseudo: PseudoHeaders, + pub(crate) map: Headers, +} + +impl Parts { + /// The constructor of `Parts` + pub fn new() -> Self { + Self { + pseudo: PseudoHeaders::new(), + map: Headers::new(), + } + } + + /// Sets pseudo headers for `Parts`. + pub fn set_pseudo(&mut self, pseudo: PseudoHeaders) { + self.pseudo = pseudo; + } + + /// Sets regular field lines for `Parts`. + pub fn set_header_lines(&mut self, headers: Headers) { + self.map = headers; + } + + pub(crate) fn is_empty(&self) -> bool { + self.pseudo.is_empty() && self.map.is_empty() + } + + pub(crate) fn update(&mut self, headers: Field, value: String) { + match headers { + Field::Authority => self.pseudo.set_authority(Some(value)), + Field::Method => self.pseudo.set_method(Some(value)), + Field::Path => self.pseudo.set_path(Some(value)), + Field::Scheme => self.pseudo.set_scheme(Some(value)), + Field::Status => self.pseudo.set_status(Some(value)), + Field::Other(header) => self.map.append(header.as_str(), value.as_str()).unwrap(), + } + } + + pub(crate) fn parts(&self) -> (&PseudoHeaders, &Headers) { + (&self.pseudo, &self.map) + } + + pub(crate) fn into_parts(self) -> (PseudoHeaders, Headers) { + (self.pseudo, self.map) + } +} + + + +impl Default for Parts { + fn default() -> Self { + Self::new() + } +} \ No newline at end of file diff --git a/ylong_http/src/h3/pseudo.rs b/ylong_http/src/h3/pseudo.rs new file mode 100644 index 0000000..b80ca75 --- /dev/null +++ b/ylong_http/src/h3/pseudo.rs @@ -0,0 +1,506 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// [Pseudo-Header fields] that may appear in http2 header fields. +/// +/// [Pseudo-Header fields]: https://httpwg.org/specs/rfc9113.html#PseudoHeaderFields +/// +/// # Note +/// The current structure is not responsible for checking every value. +// TODO: 考虑将 PseudoHeaders 拆分成 `RequestPseudo` 和 `ResponsePseudo`. +#[derive(Clone, PartialEq, Eq)] +pub struct PseudoHeaders { + authority: Option, // Request. + method: Option, // Request. + path: Option, // Request. + scheme: Option, // Request. + status: Option, // Response. +} + +// TODO: 去掉冗余的方法。 +impl PseudoHeaders { + /// Create a new `PseudoHeaders`. + pub(crate) fn new() -> Self { + Self { + authority: None, + method: None, + path: None, + scheme: None, + status: None, + } + } + + pub(crate) fn is_empty(&self) -> bool { + self.authority.is_none() + && self.method.is_none() + && self.path.is_none() + && self.scheme.is_none() + && self.status.is_none() + } + + /// Check if it contains `Authority`. + pub(crate) fn contains_authority(&self) -> bool { + self.authority.is_some() + } + + /// Get the `&str` value of `Authority`. + pub fn authority(&self) -> Option<&str> { + self.authority.as_deref() + } + + /// Set the value of `Authority`. + pub fn set_authority(&mut self, authority: Option) { + self.authority = authority; + } + + /// Take the `String` value of `Authority`. + pub(crate) fn take_authority(&mut self) -> Option { + self.authority.take() + } + + /// Check if it contains `Method`. + pub(crate) fn contains_method(&self) -> bool { + self.method.is_some() + } + + /// Get the `&str` value of `Method`. + pub fn method(&self) -> Option<&str> { + self.method.as_deref() + } + + /// Set the value of `Method`. + pub fn set_method(&mut self, method: Option) { + self.method = method; + } + + /// Take the `String` value of `Method`. + pub(crate) fn take_method(&mut self) -> Option { + self.method.take() + } + + /// Check if it contains `Path`. + pub(crate) fn contains_path(&self) -> bool { + self.path.is_some() + } + + /// Get the `&str` value of `Path`. + pub fn path(&self) -> Option<&str> { + self.path.as_deref() + } + + /// Set the value of `Path`. + pub fn set_path(&mut self, path: Option) { + self.path = path; + } + + /// Take the `String` value of `Path`. + pub(crate) fn take_path(&mut self) -> Option { + self.path.take() + } + + /// Check if it contains `Scheme`. + pub(crate) fn contains_scheme(&self) -> bool { + self.scheme.is_some() + } + + /// Get the `&str` value of `Scheme`. + pub fn scheme(&self) -> Option<&str> { + self.scheme.as_deref() + } + + /// Set the value of `Scheme`. + pub fn set_scheme(&mut self, scheme: Option) { + self.scheme = scheme; + } + + /// Take the `String` value of `Scheme`. + pub(crate) fn take_scheme(&mut self) -> Option { + self.scheme.take() + } + + /// Check if it contains `Status`. + pub(crate) fn contains_status(&self) -> bool { + self.status.is_some() + } + + /// Get the `&str` value of `Status`. + pub fn status(&self) -> Option<&str> { + self.status.as_deref() + } + + /// Set the value of `Status`. + pub fn set_status(&mut self, status: Option) { + self.status = status; + } + + /// Take the `String` value of `Status`. + pub(crate) fn take_status(&mut self) -> Option { + self.status.take() + } +} + +impl Default for PseudoHeaders { + fn default() -> Self { + PseudoHeaders::new() + } +} + +#[cfg(test)] +mod ut_pseudo_headers { + use crate::h3::pseudo::PseudoHeaders; + + /// UT test cases for `PseudoHeaders::new`. + /// + /// # Brief + /// 1. Calls `PseudoHeaders::new` to create a `PseudoHeaders`. + /// 2. Checks if the result has a default value. + #[test] + fn ut_pseudo_headers_new() { + let pseudo = PseudoHeaders::new(); + assert!(pseudo.authority.is_none()); + assert!(pseudo.method.is_none()); + assert!(pseudo.path.is_none()); + assert!(pseudo.scheme.is_none()); + assert!(pseudo.status.is_none()); + } + + /// UT test cases for `PseudoHeaders::contains_authority`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::contains_authority` of it. + /// 3. Calls `PseudoHeaders::contains_authority` of it after its `authority` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_contains_authority() { + let mut pseudo = PseudoHeaders::new(); + assert!(!pseudo.contains_authority()); + + pseudo.authority = Some(String::from("authority")); + assert!(pseudo.contains_authority()); + } + + /// UT test cases for `PseudoHeaders::authority`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::authority` of it. + /// 3. Calls `PseudoHeaders::authority` of it after its `authority` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_authority() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.authority().is_none()); + + pseudo.authority = Some(String::from("authority")); + assert_eq!(pseudo.authority(), Some("authority")); + } + + /// UT test cases for `PseudoHeaders::set_authority`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::set_authority` of it to set `authority` a value. + /// 3. Checks the results. + #[test] + fn ut_pseudo_headers_set_authority() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.authority().is_none()); + + pseudo.set_authority(Some(String::from("authority"))); + assert_eq!(pseudo.authority(), Some("authority")); + + pseudo.set_authority(None); + assert!(pseudo.authority().is_none()); + } + + /// UT test cases for `PseudoHeaders::take_authority`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::take_authority` of it. + /// 3. Calls `PseudoHeaders::take_authority` of it after its `authority` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_take_authority() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.take_authority().is_none()); + + pseudo.authority = Some(String::from("authority")); + assert_eq!(pseudo.take_authority(), Some(String::from("authority"))); + } + + /// UT test cases for `PseudoHeaders::contains_method`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::contains_method` of it. + /// 3. Calls `PseudoHeaders::contains_method` of it after its `method` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_contains_method() { + let mut pseudo = PseudoHeaders::new(); + assert!(!pseudo.contains_method()); + + pseudo.method = Some(String::from("method")); + assert!(pseudo.contains_method()); + } + + /// UT test cases for `PseudoHeaders::method`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::method` of it. + /// 3. Calls `PseudoHeaders::method` of it after its `method` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_method() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.method().is_none()); + + pseudo.method = Some(String::from("method")); + assert_eq!(pseudo.method(), Some("method")); + } + + /// UT test cases for `PseudoHeaders::set_method`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::set_method` of it to set `method` a value. + /// 3. Checks the results. + #[test] + fn ut_pseudo_headers_set_method() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.method().is_none()); + + pseudo.set_method(Some(String::from("method"))); + assert_eq!(pseudo.method(), Some("method")); + + pseudo.set_method(None); + assert!(pseudo.method().is_none()); + } + + /// UT test cases for `PseudoHeaders::take_method`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::take_method` of it. + /// 3. Calls `PseudoHeaders::take_method` of it after its `method` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_take_method() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.take_method().is_none()); + + pseudo.method = Some(String::from("method")); + assert_eq!(pseudo.take_method(), Some(String::from("method"))); + } + + /// UT test cases for `PseudoHeaders::contains_path`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::contains_path` of it. + /// 3. Calls `PseudoHeaders::contains_path` of it after its `path` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_contains_path() { + let mut pseudo = PseudoHeaders::new(); + assert!(!pseudo.contains_path()); + + pseudo.path = Some(String::from("path")); + assert!(pseudo.contains_path()); + } + + /// UT test cases for `PseudoHeaders::path`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::path` of it. + /// 3. Calls `PseudoHeaders::path` of it after its `path` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_path() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.path().is_none()); + + pseudo.path = Some(String::from("path")); + assert_eq!(pseudo.path(), Some("path")); + } + + /// UT test cases for `PseudoHeaders::set_path`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::set_path` of it to set `path` a value. + /// 3. Checks the results. + #[test] + fn ut_pseudo_headers_set_path() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.path().is_none()); + + pseudo.set_path(Some(String::from("path"))); + assert_eq!(pseudo.path(), Some("path")); + + pseudo.set_path(None); + assert!(pseudo.path().is_none()); + } + + /// UT test cases for `PseudoHeaders::take_path`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::take_path` of it. + /// 3. Calls `PseudoHeaders::take_path` of it after its `path` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_take_path() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.take_path().is_none()); + + pseudo.path = Some(String::from("path")); + assert_eq!(pseudo.take_path(), Some(String::from("path"))); + } + + /// UT test cases for `PseudoHeaders::contains_scheme`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::contains_scheme` of it. + /// 3. Calls `PseudoHeaders::contains_scheme` of it after its `scheme` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_contains_scheme() { + let mut pseudo = PseudoHeaders::new(); + assert!(!pseudo.contains_scheme()); + + pseudo.scheme = Some(String::from("scheme")); + assert!(pseudo.contains_scheme()); + } + + /// UT test cases for `PseudoHeaders::scheme`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::scheme` of it. + /// 3. Calls `PseudoHeaders::scheme` of it after its `scheme` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_scheme() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.scheme().is_none()); + + pseudo.scheme = Some(String::from("scheme")); + assert_eq!(pseudo.scheme(), Some("scheme")); + } + + /// UT test cases for `PseudoHeaders::set_scheme`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::set_scheme` of it to set `scheme` a value. + /// 3. Checks the results. + #[test] + fn ut_pseudo_headers_set_scheme() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.scheme().is_none()); + + pseudo.set_scheme(Some(String::from("scheme"))); + assert_eq!(pseudo.scheme(), Some("scheme")); + + pseudo.set_scheme(None); + assert!(pseudo.scheme().is_none()); + } + + /// UT test cases for `PseudoHeaders::take_scheme`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::take_scheme` of it. + /// 3. Calls `PseudoHeaders::take_scheme` of it after its `scheme` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_take_scheme() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.take_scheme().is_none()); + + pseudo.scheme = Some(String::from("scheme")); + assert_eq!(pseudo.take_scheme(), Some(String::from("scheme"))); + } + + /// UT test cases for `PseudoHeaders::contains_status`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::contains_status` of it. + /// 3. Calls `PseudoHeaders::contains_status` of it after its `status` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_contains_status() { + let mut pseudo = PseudoHeaders::new(); + assert!(!pseudo.contains_status()); + + pseudo.status = Some(String::from("status")); + assert!(pseudo.contains_status()); + } + + /// UT test cases for `PseudoHeaders::status`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::status` of it. + /// 3. Calls `PseudoHeaders::status` of it after its `status` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_status() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.status().is_none()); + + pseudo.status = Some(String::from("status")); + assert_eq!(pseudo.status(), Some("status")); + } + + /// UT test cases for `PseudoHeaders::set_status`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::set_status` of it to set `status` a value. + /// 3. Checks the results. + #[test] + fn ut_pseudo_headers_set_status() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.status().is_none()); + + pseudo.set_status(Some(String::from("status"))); + assert_eq!(pseudo.status(), Some("status")); + + pseudo.set_status(None); + assert!(pseudo.status().is_none()); + } + + /// UT test cases for `PseudoHeaders::take_status`. + /// + /// # Brief + /// 1. Creates a `PseudoHeaders`. + /// 2. Calls `PseudoHeaders::take_status` of it. + /// 3. Calls `PseudoHeaders::take_status` of it after its `status` is set. + /// 4. Checks the results. + #[test] + fn ut_pseudo_headers_take_status() { + let mut pseudo = PseudoHeaders::new(); + assert!(pseudo.take_status().is_none()); + + pseudo.status = Some(String::from("status")); + assert_eq!(pseudo.take_status(), Some(String::from("status"))); + } +} diff --git a/ylong_http/src/h3/qpack/decoder.rs b/ylong_http/src/h3/qpack/decoder.rs new file mode 100644 index 0000000..6318765 --- /dev/null +++ b/ylong_http/src/h3/qpack/decoder.rs @@ -0,0 +1,100 @@ +use crate::h3::error::H3Error; +use crate::h3::parts::Parts; + +use crate::h3::qpack::representation::decoder::{ReprDecoder, ReprDecStateHolder}; +use crate::h3::qpack::table::DynamicTable; + +struct HeaderLines { + parts: Parts, + header_size: usize, +} +pub(crate) struct QpackDncoder<'a>{ + table: &'a mut DynamicTable, + holder: ReprDecStateHolder, + lines: HeaderLines, +} + +impl<'a> QpackDncoder<'a> { + + pub(crate) fn new(table: &'a mut DynamicTable) -> Self { + Self { + table, + holder: ReprDecStateHolder::new(), + lines: HeaderLines { + parts: Parts::new(), + header_size: 0, + }, + } + } + + pub(crate) fn decode(&mut self, buf: &[u8])->Result<(), H3Error>{ + // todo: InsDecoder like ReprDecoder + let mut decoder = ReprDecoder::new(buf); + decoder.load(&mut self.holder); + + decoder.decode(); + + return Ok(()); + + } +} + + +#[cfg(test)] +mod ut_qpack_decoder{ + use crate::h3::qpack::table::{DynamicTable, Field}; + use crate::h3::qpack::QpackDncoder; + use crate::test_util::decode; + + + + #[test] + fn ut_qpack_decoder(){ + + test(); + macro_rules! get_parts { + ($qpack: expr $(, $input: literal)*) => {{ + $( + let text = decode($input).unwrap(); + println!("text: {:#?}", text); + // assert!($qpack.decode(text.as_slice()).is_ok()); + )* + // match $qpack.finish() { + // Ok(parts) => parts, + // Err(_) => panic!("HpackDecoder::finish() failed!"), + // } + }}; + } + macro_rules! qpack_test_case { + ( + $qpack: expr $(, $input: literal)*, + { $a: expr, $m: expr, $p: expr, $sc: expr, $st: expr }, + { $size: expr $(, $($k2: literal)? $($k3: ident)? => $v2: literal)* } $(,)? + ) => { + let mut _qpack = $qpack; + get_parts!(_qpack $(, $input)*); + // let (pseudo, _) = get_parts!(_hpack $(, $input)*).into_parts(); + // check_pseudo!(pseudo, { $a, $m, $p, $sc, $st }); + // check_table!(_hpack, $size, { $($($k2)? $($k3)? => $v2)* }); + }; + } + + fn test() + { + let mut dynamic_table = DynamicTable::with_capacity(4096); + qpack_test_case!( + QpackDncoder::new(&mut dynamic_table), + "82", + { None, Some("GET"), None, None, None }, + { 0 } + ); + } + } +} + + + + + + + diff --git a/ylong_http/src/h3/qpack/encoder.rs b/ylong_http/src/h3/qpack/encoder.rs new file mode 100644 index 0000000..bf732ec --- /dev/null +++ b/ylong_http/src/h3/qpack/encoder.rs @@ -0,0 +1,119 @@ +use crate::h3::parts::Parts; +use crate::h3::qpack::table::DynamicTable; +use crate::h3::qpack::representation::{ReprEncoder, ReprEncStateHolder}; +use crate::h3::qpack::PrefixMask; +use crate::h3::qpack::integer::{Integer, IntegerEncoder}; + +pub(crate) struct QpackEncoder { + table: DynamicTable, + holder: ReprEncStateHolder, + encoder_buf: [u8; N], + prefix_buf: [u8; M], + stream_buf: [u8; P], + cur_encoder_buf: usize, + cur_prefix_buf: usize, + cur_stream_buf: usize, +} + +impl QpackEncoder { + pub(crate) fn with_capacity(max_size: usize) -> QpackEncoder { + let mut s = Self { + table: DynamicTable::with_capacity(max_size), + holder: ReprEncStateHolder::new(), + encoder_buf: [0u8; N], + prefix_buf: [0u8; M], + stream_buf: [0u8; P], + cur_encoder_buf: 0, + cur_prefix_buf: 0, + cur_stream_buf: 0, + }; + let cur = InstEncode::SetCap(SetCap::new(max_size)).encode(&mut s.encoder_buf[s.cur_encoder_buf..]); + s.cur_encoder_buf += cur; + s + } + + pub(crate) fn set_parts(&mut self, parts: Parts) { + self.holder.set_parts(parts) + } + + /// Users can call `encode` multiple times to encode the previously set + /// `Parts` in segments. + pub(crate) fn encode(&mut self) -> () { + let mut encoder = ReprEncoder::new(&mut self.table); + encoder.load(&mut self.holder); + let (cur1,cur2) = encoder.encode(&mut self.encoder_buf[self.cur_encoder_buf..], &mut self.stream_buf[self.cur_stream_buf..]); + print!("encoder_buf: {:?}\n", &self.encoder_buf[..]); + print!("stream_buf: {:?}\n", &self.stream_buf[..]); + self.cur_encoder_buf += cur1; + self.cur_stream_buf += cur2; + // todo + // if size == dst.len() { + // encoder.save(&mut self.holder); + // } + // size + } + + /// Check the previously set `Parts` if encoding is complete. + pub(crate) fn is_finished(&self) -> bool { + self.holder.is_empty() + } +} + + +pub(crate) enum InstEncode { + SetCap(SetCap), + // InsertWithNameRef(InsertWithNameRef), +} + +impl InstEncode { + pub(crate) fn encode(self, encoder_buf: &mut [u8]) -> usize { + let resut = match self { + Self::SetCap(s) => s.encode(encoder_buf), + // _ => panic!("not support"), + }; + match resut { + Ok(size) => size, + Err(e) => panic!("encode error"), + } + } +} + +pub(crate) struct SetCap { + capacity: Integer, +} + +impl SetCap { + fn from(capacity: Integer) -> Self { + Self { capacity } + } + + fn new(capacity: usize) -> Self { + Self { capacity: Integer::index(0x20, capacity, PrefixMask::SETCAP.0) } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.capacity + .encode(dst) + .map_err(|e| InstEncode::SetCap(SetCap::from(e))) + } +} + + +#[cfg(test)] +mod ut_qpack_encoder { + use crate::h3::parts::Parts; + use crate::h3::qpack::encoder::{QpackEncoder}; + use crate::h3::qpack::table::Field; + + #[test] + fn test() { + // let mut vec = [0u8; 20]; + // let mut cur = 0; + let mut parts = Parts::new(); + parts.update(Field::Authority, String::from("www.example.com")); + let mut _encoder = QpackEncoder::<50, 50, 50>::with_capacity(220); + _encoder.set_parts(parts); + _encoder.encode(); + // println!("{:#?}", vec); + } +} \ No newline at end of file diff --git a/ylong_http/src/h3/qpack/integer.rs b/ylong_http/src/h3/qpack/integer.rs new file mode 100644 index 0000000..d3521dd --- /dev/null +++ b/ylong_http/src/h3/qpack/integer.rs @@ -0,0 +1,132 @@ +use std::cmp::Ordering; +use crate::h3::error::{ErrorCode, H3Error}; + +pub(crate) struct Integer { + pub(crate) int: IntegerEncoder, +} + +impl Integer { + pub(crate) fn index(pre: u8, index: usize, mask: u8) -> Self { + Self { + int: IntegerEncoder::new(pre, index, mask), + } + } + pub(crate) fn length(length: usize, is_huffman: bool) -> Self { + Self { + int: IntegerEncoder::new(u8::from(is_huffman), length, 0x7f), + } + } + pub(crate) fn encode(mut self, dst: &mut [u8]) -> Result { + let mut cur = 0; + while !self.int.is_finish() { + let dst = &mut dst[cur..]; + if dst.is_empty() { + return Err(self); + } + dst[0] = self.int.next_byte().unwrap(); + cur += 1; + } + Ok(cur) + } +} +pub(crate) struct IntegerDecoder { + index: usize, + shift: u32, +} + +impl IntegerDecoder { + /// Calculates an integer based on the incoming first byte and mask. + /// If no subsequent bytes exist, return the result directly, otherwise + /// return the decoder itself. + pub(crate) fn first_byte(byte: u8, mask: u8) -> Result { + let index = byte & mask; + match index.cmp(&mask) { + Ordering::Less => Ok(index as usize), + _ => Err(Self { + index: index as usize, + shift: 1, + }), + } + } + + /// Continues computing the integer based on the next byte of the input. + /// Returns `Ok(Some(index))` if the result is obtained, otherwise returns + /// `Ok(None)`, and returns Err in case of overflow. + pub(crate) fn next_byte(&mut self, byte: u8) -> Result, H3Error> { + self.index = 1usize + .checked_shl(self.shift - 1) + .and_then(|res| res.checked_mul((byte & 0x7f) as usize)) + .and_then(|res| res.checked_add(self.index)) + .ok_or(H3Error::ConnectionError(ErrorCode::TEST))?;//todo: modify the error code + self.shift += 7; + match (byte & 0x80) == 0x00 { + true => Ok(Some(self.index)), + false => Ok(None), + } + } +} + +pub(crate) struct IntegerEncoder { + pre: u8, + i: usize, + mask: u8, + state: IntegerEncodeState, +} + +/// Enumeration of states that the `IntegerEncoder` needs to use. +enum IntegerEncodeState { + First, + Other, + Finish, +} + +impl IntegerEncoder { + /// Creates a new `IntegerEncoder`. + pub(crate) fn new(pre: u8, i: usize, mask: u8) -> Self { + Self { + pre, + i, + mask, + state: IntegerEncodeState::First, + } + } + + + /// return the value of the integer + pub(crate) fn get_index(&self) -> usize { + self.i + } + pub(crate) fn get_pre(&self) -> u8 { + self.pre + } + + /// Gets the next byte of the integer. If no remaining bytes are calculated, + /// return `None`. + pub(crate) fn next_byte(&mut self) -> Option { + match self.state { + IntegerEncodeState::First => { + if self.i < self.mask as usize { + self.state = IntegerEncodeState::Finish; + return Some(self.pre | (self.i as u8)); + } + self.i -= self.mask as usize; + self.state = IntegerEncodeState::Other; + Some(self.pre | self.mask) + } + IntegerEncodeState::Other => Some(if self.i >= 128 { + let res = (self.i & 0x7f) as u8; + self.i >>= 7; + res | 0x80 + } else { + self.state = IntegerEncodeState::Finish; + (self.i & 0x7f) as u8 + }), + IntegerEncodeState::Finish => None, + } + } + + /// Checks if calculation is over. + pub(crate) fn is_finish(&self) -> bool { + matches!(self.state, IntegerEncodeState::Finish) + } +} \ No newline at end of file diff --git a/ylong_http/src/h3/qpack/mod.rs b/ylong_http/src/h3/qpack/mod.rs index 97985c5..44355a4 100644 --- a/ylong_http/src/h3/qpack/mod.rs +++ b/ylong_http/src/h3/qpack/mod.rs @@ -1,3 +1,111 @@ +pub(crate) mod table; +mod encoder; +mod representation; +mod integer; +mod decoder; +pub(crate) use decoder::QpackDncoder; -pub(crate) mod table; \ No newline at end of file + +pub(crate) struct ReprPrefixBit(u8); + +/// # Prefix bit: +/// ## Encoder Instructions: +/// SETCAP: 0x20 +/// INSERTWITHNAME: 0x80 +/// INSERTWITHLITERAL: 0x40 +/// +/// ## Representation: +/// INDEXED: 0x80 +/// INDEXEDWITHPOSTNAME: 0x10 +/// INDEXINGWITHNAME: 0x40 +/// INDEXINGWITHPOSTNAME: 0x00 +/// INDEXINGWITHLITERAL: 0x20 + + +impl ReprPrefixBit { + pub(crate) const INDEXED: Self = Self(0x80); + pub(crate) const INDEXEDWITHPOSTNAME: Self = Self(0x10); + pub(crate) const INDEXINGWITHNAME: Self = Self(0x40); + pub(crate) const INDEXINGWITHPOSTNAME: Self = Self(0x00); + pub(crate) const INDEXINGWITHLITERAL: Self = Self(0x20); + + /// Creates a `PrefixBit` from a byte. The interface will convert the + /// incoming byte to the most suitable prefix bit. + pub(crate) fn from_u8(byte: u8) -> Self { + match byte { + x if x >= 0x80 => Self::INDEXED, + x if x >= 0x40 => Self::INDEXINGWITHNAME, + x if x >= 0x20 => Self::INDEXINGWITHLITERAL, + x if x >= 0x10 => Self::INDEXEDWITHPOSTNAME, + _ => Self::INDEXINGWITHPOSTNAME, + } + } + + /// Returns the corresponding `PrefixIndexMask` according to the current + /// prefix bit. + pub(crate) fn prefix_index_mask(&self) -> PrefixMask { + match self.0 { + 0x80 => PrefixMask::INDEXED, + 0x40 => PrefixMask::INDEXINGWITHNAME, + 0x20 => PrefixMask::INDEXINGWITHLITERAL, + 0x10 => PrefixMask::INDEXEDWITHPOSTNAME, + _ => PrefixMask::INDEXINGWITHPOSTNAME, + } + } + + /// Unlike Hpack, QPACK has some special value for the first byte of an integer. + /// Like T indicating whether the reference is into the static or dynamic table. + pub(crate) fn prefix_midbit_value(&self, byte: u8) -> ReprMidBit { + match self.0 { + 0x80 => ReprMidBit { n: None, t: Some((byte & 0x40) != 0), h: None }, + 0x40 => ReprMidBit { n: Some((byte & 0x20) != 0), t: Some((byte & 0x10) != 0), h: None }, + 0x20 => ReprMidBit { n: Some((byte & 0x10) != 0), t: None, h: Some((byte & 0x08) != 0) }, + 0x10 => ReprMidBit { n: None, t: None, h: None }, + _ => ReprMidBit { n: Some((byte & 0x08) != 0), t: None, h: None }, + } + } +} + + +pub(crate) enum Representation { + /// An indexed field line representation identifies an entry in the static table or an entry in + /// the dynamic table with an absolute index less than the value of the Base. + /// 0 1 2 3 4 5 6 7 + /// +---+---+---+---+---+---+---+---+ + /// | 1 | T | Index (6+) | + /// +---+---+-----------------------+ + /// This representation starts with the '1' 1-bit pattern, followed by the 'T' bit, indicating + /// whether the reference is into the static or dynamic table. The 6-bit prefix integer + /// (Section 4.1.1) that follows is used to locate the table entry for the field line. When T=1, + /// the number represents the static table index; when T=0, the number is the relative index of + /// the entry in the dynamic table. + Indexed { mid_bit:ReprMidBit, index: usize }, + //todo: add more +} + +pub(crate) struct ReprMidBit { + //'N', indicates whether an intermediary is permitted to add this field line to the dynamic + // table on subsequent hops. + n: Option, + //'T', indicating whether the reference is into the static or dynamic table. + t: Option, + //'H', indicating whether is represented as a Huffman-encoded. + h: Option, + +} + +pub(crate) struct PrefixMask(u8); + + +impl PrefixMask { + pub(crate) const INDEXED: Self = Self(0x3f); + pub(crate) const DELTABASE: Self = Self(0x7f); + pub(crate) const SETCAP: Self = Self(0x1f); + pub(crate) const INSERTWITHNAME: Self = Self(0x3f); + pub(crate) const INSERTWITHLITERAL: Self = Self(0x1f); + pub(crate) const INDEXINGWITHNAME: Self = Self(0x0f); + pub(crate) const INDEXINGWITHPOSTNAME: Self = Self(0x07); + pub(crate) const INDEXINGWITHLITERAL: Self = Self(0x07); + pub(crate) const INDEXEDWITHPOSTNAME: Self = Self(0x0f); +} \ No newline at end of file diff --git a/ylong_http/src/h3/qpack/representation/decoder.rs b/ylong_http/src/h3/qpack/representation/decoder.rs new file mode 100644 index 0000000..056293f --- /dev/null +++ b/ylong_http/src/h3/qpack/representation/decoder.rs @@ -0,0 +1,201 @@ +use crate::h3::error::H3Error; +use crate::h3::qpack::{ReprMidBit, Representation, ReprPrefixBit}; +use crate::h3::qpack::integer::IntegerDecoder; + + +pub(crate) struct ReprDecoder<'a> { + /// `buf` represents the byte stream to be decoded. + buf: &'a [u8], + /// `state` represents the remaining state after the last call to `decode`. + state: Option, +} + +impl<'a> ReprDecoder<'a> { + /// Creates a new `ReprDecoder` whose `state` is `None`. + pub(crate) fn new(buf: &'a [u8]) -> Self { + Self { buf, state: None } + } + + /// Loads state from a holder. + pub(crate) fn load(&mut self, holder: &mut ReprDecStateHolder) { + self.state = holder.state.take(); + } + + + /// Decodes `self.buf`. Every time users call `decode`, it will try to + /// decode a `Representation`. + pub(crate) fn decode(&mut self) -> Result, H3Error> { + // If buf is empty, leave the state unchanged. + if self.buf.is_empty() { + return Ok(None); + } + + match self + .state + .take() + .unwrap_or_else(|| ReprDecodeState::Index(Index::new())) + .decode(&mut self.buf) + { + // If `buf` is not enough to continue decoding a complete + // `Representation`, `Ok(None)` will be returned. Users need to call + // `save` to save the current state to a `ReprDecStateHolder`. + DecResult::NeedMore(state) => { + self.state = Some(state); + Ok(None) + } + // If a `Representation` is decoded, return it. + DecResult::Decoded(repr) => Ok(Some(repr)), + DecResult::Error(error) => Err(error), + } + } +} + +pub(crate) struct ReprDecStateHolder { + state: Option, +} + +impl ReprDecStateHolder { + pub(crate) fn new() -> Self { + Self { state: None } + } + + pub(crate) fn is_empty(&self) -> bool { + self.state.is_none() + } +} + + + +macro_rules! state_def { + ($name: ident, $decoded: ty, $($state: ident),* $(,)?) => { + pub(crate) enum $name { + $( + $state($state), + )* + } + + impl $name { + fn decode(self, buf: &mut &[u8]) -> DecResult<$decoded, $name> { + match self { + $( + Self::$state(state) => state.decode(buf), + )* + } + } + } + + $( + impl From<$state> for $name { + fn from(s: $state) -> Self { + Self::$state(s) + } + } + )* + } +} + + +state_def!( + ReprDecodeState, + Representation, + Index, +); + +state_def!(IndexInner, (ReprPrefixBit, ReprMidBit, usize), FirstByte, ReprTrailingBytes); + + +pub(crate) struct Index { + inner: IndexInner, +} + +impl Index { + fn new() -> Self { + Self::from_inner(FirstByte.into()) + } + fn from_inner(inner: IndexInner) -> Self { + Self { inner } + } + fn decode(self, buf: &mut &[u8]) -> DecResult { + match self.inner.decode(buf) { + DecResult::Decoded((ReprPrefixBit::INDEXED, mid_bit, index)) => { + DecResult::Decoded(Representation::Indexed { mid_bit, index }) + } + } + } +} + + +pub(crate) struct FirstByte; + + +impl FirstByte { + fn decode(self, buf: &mut &[u8]) -> DecResult<(ReprPrefixBit, ReprMidBit, usize), IndexInner> { + // If `buf` has been completely decoded here, return the current state. + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + let byte = buf[0]; + let repr = ReprPrefixBit::from_u8(byte); + let mid_bit = repr.prefix_midbit_value(byte); + let mask = repr.prefix_index_mask(); + + // Moves the pointer of `buf` backward. + *buf = &buf[1..]; + match IntegerDecoder::first_byte(byte, mask.0) { + // Return the ReprPrefixBit and index part value. + Ok(idx) => DecResult::Decoded((repr, mid_bit, idx)), + // Index part value is longer than index(i.e. use all 1 to represent), so it needs more bytes to decode. + Err(int) => ReprTrailingBytes::new(repr, mid_bit, int).decode(buf), + } + } +} + +pub(crate) struct ReprTrailingBytes { + repr: ReprPrefixBit, + mid_bit: ReprMidBit, + index: IntegerDecoder, +} + +impl ReprTrailingBytes { + fn new(repr: ReprPrefixBit, mid_bit: ReprMidBit, index: IntegerDecoder) -> Self { + Self { repr, mid_bit, index } + } + fn decode(mut self, buf: &mut &[u8]) -> DecResult<(ReprPrefixBit, ReprMidBit, usize), IndexInner> { + loop { + // If `buf` has been completely decoded here, return the current state. + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + + let byte = buf[0]; + *buf = &buf[1..]; + // Updates trailing bytes until we get the index. + match self.index.next_byte(byte) { + Ok(None) => {} + Ok(Some(index)) => return DecResult::Decoded((self.repr, self.mid_bit, index)), + Err(e) => return e.into(), + } + } + } +} + + +/// Decoder's possible returns during the decoding process. +enum DecResult { + /// Decoder has got a `D`. Users can continue to call `encode` to try to + /// get the next `D`. + Decoded(D), + + /// Decoder needs more bytes to decode to get a `D`. Returns the current + /// decoding state `S`. + NeedMore(S), + + /// Errors that may occur when decoding. + Error(H3Error), +} + +impl From for DecResult { + fn from(e: H3Error) -> Self { + DecResult::Error(e) + } +} \ No newline at end of file diff --git a/ylong_http/src/h3/qpack/representation/encoder.rs b/ylong_http/src/h3/qpack/representation/encoder.rs new file mode 100644 index 0000000..39146ec --- /dev/null +++ b/ylong_http/src/h3/qpack/representation/encoder.rs @@ -0,0 +1,706 @@ +use std::arch::asm; +use std::cmp::{max, Ordering}; +use std::result; +use crate::h3::parts::Parts; +use crate::h3::pseudo::PseudoHeaders; +use crate::h3::qpack::integer::{Integer, IntegerEncoder}; +use crate::h3::qpack::PrefixMask; +use crate::h3::qpack::table::{DynamicTable, Field, TableIndex, TableSearcher}; +use crate::headers::HeadersIntoIter; + +pub(crate) struct ReprEncoder<'a> { + table: &'a mut DynamicTable, + iter: Option, + state: Option, + base: Base, + req_insert_count: ReqInsertCount + +} + + +impl<'a> ReprEncoder<'a> { + /// Creates a new, empty `ReprEncoder`. + pub(crate) fn new(table: &'a mut DynamicTable) -> Self { + Self { + table, + iter: None, + state: None, + base: Base::from(false,0), + req_insert_count: ReqInsertCount::new(0), + } + } + + /// Loads states from a holder. + pub(crate) fn load(&mut self, holder: &mut ReprEncStateHolder) { + self.iter = holder.iter.take(); + self.state = holder.state.take(); + } + + /// Saves current state to a holder. + pub(crate) fn save(self, holder: &mut ReprEncStateHolder) { + holder.iter = self.iter; + holder.state = self.state; + } + + /// Decodes the contents of `self.iter` and `self.state`. The result will be + /// written to `dst` and the length of the decoded content will be returned. + /// + /// If the `dst` is full, users can call this method again after preparing a + /// new piece of memory, and the subsequent decoding part will be written + /// into the new memory. + /// + /// # Note + /// Decoding is complete only when `self.iter` and `self.state` are both + /// `None`. It is recommended that users save the result to a + /// `RecEncStateHolder` immediately after using the method. + pub(crate) fn encode(&mut self, encoder_buffer: &mut [u8], stream_buffer: &mut [u8]) -> (usize, usize) { + let mut cur_encoder = 0; + let mut cur_stream = 0; + if let Some(mut iter) = self.iter.take() { + while let Some((h, v)) = iter.next() { + let searcher = TableSearcher::new(self.table); + println!("h: {:?}, v: {:?}", h, v); + let mut stream_result:Result = Result::Err(ReprEncodeState::Indexed(Indexed::new(0, false))); + let mut encoder_result:Result = Result::Err(ReprEncodeState::Indexed(Indexed::new(0, false))); + let static_index = searcher.find_index_static(&h, &v); + if static_index != Some(TableIndex::None) { + if let Some(TableIndex::Field(index)) = static_index { + stream_result = Indexed::new(index, true).encode(&mut stream_buffer[cur_stream..]); + } + } else { + let dynamic_index = searcher.find_index_dynamic(&h, &v); + let static_name_index = searcher.find_name_static(&h, &v); + let mut dynamic_name_index = Some(TableIndex::None); + if dynamic_index == Some(TableIndex::None) { + if static_name_index == Some(TableIndex::None) { + dynamic_name_index = searcher.find_name_dynamic(&h, &v); + } + if self.should_index(&h, &v) && searcher.can_index(&h, &v) { + encoder_result = match (&static_name_index, &dynamic_name_index) { + (Some(TableIndex::FieldName(index)), _) => { + InsertWithName::new(index.clone(), v.clone().into_bytes(), false, true).encode(&mut encoder_buffer[cur_encoder..]) + } + (_, Some(TableIndex::FieldName(index))) => { + InsertWithName::new(index.clone(), v.clone().into_bytes(), false, false).encode(&mut encoder_buffer[cur_encoder..]) + } + (_, _) => { + InsertWithLiteral::new(h.clone().into_string().into_bytes(), v.clone().into_bytes(), false).encode(&mut encoder_buffer[cur_encoder..]) + } + }; + self.table.update(h.clone(), v.clone()); + } + } + if dynamic_index == Some(TableIndex::None){ + if dynamic_name_index != Some(TableIndex::None){ + //Encode literal with dynamic name, possibly above Base + let base_value = self.base.get_base(&self.req_insert_count); + if let Some(TableIndex::FieldName(index)) = dynamic_name_index { + if index < base_value{ + stream_result = IndexingWithName::new(index, v.clone().into_bytes(), false, false, false).encode(&mut stream_buffer[cur_stream..]); + } + else{ + stream_result = IndexingWithPostName::new(index, v.clone().into_bytes(), false, false).encode(&mut stream_buffer[cur_stream..]); + } + } + + // requiredInsertCount = max(requiredInsertCount,dynamicNameIndex) + if let Some(TableIndex::FieldName(index)) = dynamic_name_index { + let maxindex = max(self.req_insert_count.count.int.get_index(), index); + self.req_insert_count = ReqInsertCount::new(maxindex); + } + } + else{ + // Encodes a literal with a static name or literal name + if static_name_index != Some(TableIndex::None){ + if let Some(TableIndex::FieldName(index)) = static_name_index { + stream_result = IndexingWithName::new(index, v.into_bytes(), false, true, false).encode(&mut stream_buffer[cur_stream..]); + } + } + else { + stream_result = IndexingWithLiteral::new(h.into_string().into_bytes(), v.into_bytes(), false, false).encode(&mut stream_buffer[cur_stream..]); + } + } + } + else { + // Dynamic index reference + assert!(dynamic_index != Some(TableIndex::None)); + // requiredInsertCount = max(requiredInsertCount, dynamicIndex) + if let Some(TableIndex::FieldName(index)) = dynamic_index { + let maxindex = max(self.req_insert_count.count.int.get_index(), index); + self.req_insert_count = ReqInsertCount::new(maxindex); + } + // Encode dynamicIndex, possibly above Base + let base_value = self.base.get_base(&self.req_insert_count); + if let Some(TableIndex::FieldName(index)) = dynamic_name_index { + if index < base_value{ + stream_result = Indexed::new(index, false).encode(&mut stream_buffer[cur_stream..]); + } + else{ + stream_result = IndexedWithPostName::new(index).encode(&mut stream_buffer[cur_stream..]); + } + } + } + } + + + match (encoder_result, stream_result) { + (Ok(encoder_size),Ok(stream_size)) => { + cur_stream += stream_size; + cur_encoder += encoder_size; + }, + (Err(state), Ok(stream_size)) => { + cur_stream += stream_size; + self.state = Some(state); + self.iter = Some(iter); + return (encoder_buffer.len(), stream_buffer.len()); + }, + (Ok(encoder_size), Err(state)) => { + cur_encoder += encoder_size; + self.state = Some(state); + self.iter = Some(iter); + return (encoder_buffer.len(), stream_buffer.len()); + }, + (Err(_), Err(state)) => { + self.state = Some(state); + self.iter = Some(iter); + return (encoder_buffer.len(), stream_buffer.len()); + } + } + } + } + (cur_encoder, cur_stream) + } + pub(crate) fn should_index(&self, header: &Field, value: &str) -> bool { + //todo + true + } +} + +pub(crate) struct EncFieldPrefix { + req_incert_count: ReqInsertCount, + base: Base, +} + + +pub(crate) struct ReprEncStateHolder { + iter: Option, + state: Option, +} + +impl ReprEncStateHolder { + /// Creates a new, empty `ReprEncStateHolder`. + pub(crate) fn new() -> Self { + Self { + iter: None, + state: None, + } + } + + /// Creates a state based on the `Parts` to be encoded. + pub(crate) fn set_parts(&mut self, parts: Parts) { + self.iter = Some(PartsIter::new(parts)) + } + + /// Determines whether `self.iter` and `self.state` are empty. if they are + /// empty, it means encoding is finished. + pub(crate) fn is_empty(&self) -> bool { + self.iter.is_none() && self.state.is_none() + } +} + +pub(crate) enum ReprEncodeState { + Indexed(Indexed), + InsertWithName(InsertWithName), + InsertWithLiteral(InsertWithLiteral), + IndexingWithName(IndexingWithName), + IndexingWithPostName(IndexingWithPostName), + IndexingWithLiteral(IndexingWithLiteral), + IndexedWithPostName(IndexedWithPostName), + //todo +} + + +pub(crate) struct Indexed { + index: Integer, +} + +impl Indexed { + fn from(index: Integer) -> Self { + Self { index } + } + + fn new(index: usize, is_static: bool) -> Self { + if is_static { + // in static table + Self { index: Integer::index(0xc0, index, PrefixMask::INDEXED.0) } + } else { + // in dynamic table + Self { index: Integer::index(0x80, index, PrefixMask::INDEXED.0) } + } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.index + .encode(dst) + .map_err(|e| ReprEncodeState::Indexed(Indexed::from(e))) + } +} + +pub(crate) struct IndexedWithPostName { + index: Integer, +} + +impl IndexedWithPostName { + fn from(index: Integer) -> Self { + Self { index } + } + + fn new(index: usize) -> Self { + + Self { index: Integer::index(0x10, index, PrefixMask::INDEXINGWITHPOSTNAME.0) } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.index + .encode(dst) + .map_err(|e| ReprEncodeState::IndexedWithPostName(IndexedWithPostName::from(e))) + } +} + + + +pub(crate) struct InsertWithName { + inner: IndexAndValue, +} + +impl InsertWithName { + fn from(inner: IndexAndValue) -> Self { + Self { inner } + } + + fn new(index: usize, value: Vec, is_huffman: bool, is_static: bool) -> Self { + if is_static { + Self { + inner: IndexAndValue::new() + .set_index(0xc0, index, PrefixMask::INSERTWITHNAME.0) + .set_value(value, is_huffman), + } + } else { + Self { + inner: IndexAndValue::new() + .set_index(0x80, index, PrefixMask::INSERTWITHNAME.0) + .set_value(value, is_huffman), + } + } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.inner + .encode(dst) + .map_err(|e| ReprEncodeState::InsertWithName(InsertWithName::from(e))) + } +} + + +pub(crate) struct IndexingWithName { + inner: IndexAndValue, +} + +impl IndexingWithName { + fn from(inner: IndexAndValue) -> Self { + Self { inner } + } + + fn new(index: usize, value: Vec, is_huffman: bool, is_static: bool, no_permit:bool) -> Self { + match (no_permit,is_static){ + (true, true) => { + Self { + inner: IndexAndValue::new() + .set_index(0x70, index, PrefixMask::INDEXINGWITHNAME.0) + .set_value(value, is_huffman), + } + }, + (true, false) => { + Self { + inner: IndexAndValue::new() + .set_index(0x60, index, PrefixMask::INDEXINGWITHNAME.0) + .set_value(value, is_huffman), + } + }, + (false, true) => { + Self { + inner: IndexAndValue::new() + .set_index(0x50, index, PrefixMask::INDEXINGWITHNAME.0) + .set_value(value, is_huffman), + } + }, + (false, false) => { + Self { + inner: IndexAndValue::new() + .set_index(0x40, index, PrefixMask::INDEXINGWITHNAME.0) + .set_value(value, is_huffman), + } + }, + } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.inner + .encode(dst) + .map_err(|e| ReprEncodeState::IndexingWithName(IndexingWithName::from(e))) + } +} + + + +pub(crate) struct IndexingWithPostName { + inner: IndexAndValue, +} + +impl IndexingWithPostName { + fn from(inner: IndexAndValue) -> Self { + Self { inner } + } + + fn new(index: usize, value: Vec, is_huffman: bool, no_permit:bool) -> Self { + if no_permit{ + Self { + inner: IndexAndValue::new() + .set_index(0x08, index, PrefixMask::INDEXINGWITHPOSTNAME.0) + .set_value(value, is_huffman), + } + } + else{ + Self { + inner: IndexAndValue::new() + .set_index(0x00, index, PrefixMask::INDEXINGWITHPOSTNAME.0) + .set_value(value, is_huffman), + } + } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.inner + .encode(dst) + .map_err(|e| ReprEncodeState::IndexingWithPostName(IndexingWithPostName::from(e))) + } +} + + +pub(crate) struct IndexingWithLiteral { + inner: NameAndValue, +} + +impl IndexingWithLiteral { + fn new(name: Vec, value: Vec, is_huffman: bool, no_permit: bool) -> Self { + match (no_permit,is_huffman) { + (true, true) => { + Self { + inner: NameAndValue::new() + .set_index(0x38, name.len(), PrefixMask::INDEXINGWITHLITERAL.0) + .set_name_and_value(name, value, is_huffman), + } + }, + (true, false) => { + Self { + inner: NameAndValue::new() + .set_index(0x30, name.len(), PrefixMask::INDEXINGWITHLITERAL.0) + .set_name_and_value(name, value, is_huffman), + } + }, + (false, true) => { + Self { + inner: NameAndValue::new() + .set_index(0x28, name.len(), PrefixMask::INDEXINGWITHLITERAL.0) + .set_name_and_value(name, value, is_huffman), + } + }, + (false, false) => { + Self { + inner: NameAndValue::new() + .set_index(0x20, name.len(), PrefixMask::INDEXINGWITHLITERAL.0) + .set_name_and_value(name, value, is_huffman), + } + }, + } + + } + + fn from(inner: NameAndValue) -> Self { + Self { inner } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.inner + .encode(dst) + .map_err(|e| ReprEncodeState::InsertWithLiteral(InsertWithLiteral::from(e))) + } +} + + +pub(crate) struct InsertWithLiteral { + inner: NameAndValue, +} + +impl InsertWithLiteral { + fn new(name: Vec, value: Vec, is_huffman: bool) -> Self { + if is_huffman{ + Self { + inner: NameAndValue::new() + .set_index(0x60, name.len(), PrefixMask::INSERTWITHLITERAL.0) + .set_name_and_value(name, value, is_huffman), + } + } + else{ + Self { + inner: NameAndValue::new() + .set_index(0x40, name.len(), PrefixMask::INSERTWITHLITERAL.0) + .set_name_and_value(name, value, is_huffman), + } + } + + } + + fn from(inner: NameAndValue) -> Self { + Self { inner } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.inner + .encode(dst) + .map_err(|e| ReprEncodeState::InsertWithLiteral(InsertWithLiteral::from(e))) + } +} + + +pub(crate) struct IndexAndValue { + index: Option, + value_length: Option, + value_octets: Option, +} +macro_rules! check_and_encode { + ($item: expr, $dst: expr, $cur: expr, $self: expr) => {{ + if let Some(i) = $item.take() { + match i.encode($dst) { + Ok(len) => $cur += len, + Err(e) => { + $item = Some(e); + return Err($self); + } + }; + } + }}; +} +impl IndexAndValue { + fn new() -> Self { + Self { + index: None, + value_length: None, + value_octets: None, + } + } + + fn set_index(mut self, pre: u8, index: usize, mask: u8) -> Self { + self.index = Some(Integer::index(pre, index, mask)); + self + } + + fn set_value(mut self, value: Vec, is_huffman: bool) -> Self { + self.value_length = Some(Integer::length(value.len(), is_huffman)); + self.value_octets = Some(Octets::new(value)); + self + } + + fn encode(mut self, dst: &mut [u8]) -> Result { + let mut cur = 0; + check_and_encode!(self.index, &mut dst[cur..], cur, self); + check_and_encode!(self.value_length, &mut dst[cur..], cur, self); + check_and_encode!(self.value_octets, &mut dst[cur..], cur, self); + Ok(cur) + } +} + + +pub(crate) struct NameAndValue { + index: Option, + name_length: Option, + name_octets: Option, + value_length: Option, + value_octets: Option, +} + +impl NameAndValue { + fn new() -> Self { + Self { + index: None, + name_length: None, + name_octets: None, + value_length: None, + value_octets: None, + } + } + + fn set_index(mut self, pre: u8, index: usize, mask: u8) -> Self { + self.index = Some(Integer::index(pre, index, mask)); + self + } + + fn set_name_and_value(mut self, name: Vec, value: Vec, is_huffman: bool) -> Self { + self.name_length = Some(Integer::length(name.len(), is_huffman)); + self.name_octets = Some(Octets::new(name)); + self.value_length = Some(Integer::length(value.len(), is_huffman)); + self.value_octets = Some(Octets::new(value)); + self + } + + fn encode(mut self, dst: &mut [u8]) -> Result { + let mut cur = 0; + check_and_encode!(self.index, &mut dst[cur..], cur, self); + // check_and_encode!(self.name_length, &mut dst[cur..], cur, self); //no need for qpack cause it in index. + check_and_encode!(self.name_octets, &mut dst[cur..], cur, self); + check_and_encode!(self.value_length, &mut dst[cur..], cur, self); + check_and_encode!(self.value_octets, &mut dst[cur..], cur, self); + Ok(cur) + } +} + +pub(crate) struct Octets { + src: Vec, + idx: usize, +} + +impl Octets { + fn new(src: Vec) -> Self { + Self { src, idx: 0 } + } + + fn encode(mut self, dst: &mut [u8]) -> Result { + let mut cur = 0; + + let input_len = self.src.len() - self.idx; + let output_len = dst.len(); + + if input_len == 0 { + return Ok(cur); + } + + match output_len.cmp(&input_len) { + Ordering::Greater | Ordering::Equal => { + dst[..input_len].copy_from_slice(&self.src[self.idx..]); + cur += input_len; + Ok(cur) + } + Ordering::Less => { + dst[..].copy_from_slice(&self.src[self.idx..self.idx + output_len]); + self.idx += output_len; + Err(self) + } + } + } +} + +pub(crate) struct EncInsertCount { + count: Integer, +} + +// impl EncInsertCount { +// fn from_req_insert_count(req_ins_cnt: ReqInsertCount, max_table_capacity: usize) -> Self { +// let max_entries = max_table_capacity / 32; +// if req_ins_cnt.count.int.get_index() == 0 { +// Self { count: Integer::index(0x00, 0) } +// } else { +// Self { count: Integer::index(0x00, (req_ins_cnt.count.int.get_index() % (2 * max_entries)) + 1) } +// } +// } +// } + +pub(crate) struct Base { + base: Integer, +} + +impl Base { + fn from(sign: bool, delta_base: usize) -> Self { + Self { base: Integer::index(if sign { 0x80 } else { 0x00 }, delta_base, PrefixMask::DELTABASE.0) } + } + + fn get_base(&self, req_incert_count: &ReqInsertCount) -> usize { + if self.base.int.get_pre() == 0x80 { + req_incert_count.count.int.get_index() + self.base.int.get_index() + } else { + req_incert_count.count.int.get_index() - self.base.int.get_index() - 1 + } + } +} + +pub(crate) struct ReqInsertCount { + count: Integer, +} + +impl ReqInsertCount { + fn new(index:usize) -> Self { + Self { count: Integer::index(0x00, index, 0xff) } + } +} + +struct PartsIter { + pseudo: PseudoHeaders, + map: HeadersIntoIter, + next_type: PartsIterDirection, +} + +/// `PartsIterDirection` is the `PartsIter`'s direction to get the next header. +enum PartsIterDirection { + Authority, + Method, + Path, + Scheme, + Status, + Other, +} + +impl PartsIter { + /// Creates a new `PartsIter` from the given `Parts`. + fn new(parts: Parts) -> Self { + Self { + pseudo: parts.pseudo, + map: parts.map.into_iter(), + next_type: PartsIterDirection::Method, + } + } + + /// Gets headers in the order of `Method`, `Status`, `Scheme`, `Path`, + /// `Authority` and `Other`. + fn next(&mut self) -> Option<(Field, String)> { + loop { + match self.next_type { + PartsIterDirection::Method => match self.pseudo.take_method() { + Some(value) => return Some((Field::Method, value)), + None => self.next_type = PartsIterDirection::Status, + }, + PartsIterDirection::Status => match self.pseudo.take_status() { + Some(value) => return Some((Field::Status, value)), + None => self.next_type = PartsIterDirection::Scheme, + }, + PartsIterDirection::Scheme => match self.pseudo.take_scheme() { + Some(value) => return Some((Field::Scheme, value)), + None => self.next_type = PartsIterDirection::Path, + }, + PartsIterDirection::Path => match self.pseudo.take_path() { + Some(value) => return Some((Field::Path, value)), + None => self.next_type = PartsIterDirection::Authority, + }, + PartsIterDirection::Authority => match self.pseudo.take_authority() { + Some(value) => return Some((Field::Authority, value)), + None => self.next_type = PartsIterDirection::Other, + }, + PartsIterDirection::Other => { + return self + .map + .next() + .map(|(h, v)| (Field::Other(h.to_string()), v.to_str().unwrap())); + } + } + } + } +} + diff --git a/ylong_http/src/h3/qpack/representation/mod.rs b/ylong_http/src/h3/qpack/representation/mod.rs new file mode 100644 index 0000000..15cb64a --- /dev/null +++ b/ylong_http/src/h3/qpack/representation/mod.rs @@ -0,0 +1,7 @@ +mod encoder; +pub(crate) mod decoder; + +pub(crate) use encoder::{ReprEncoder,ReprEncStateHolder}; + + + diff --git a/ylong_http/src/h3/qpack/table.rs b/ylong_http/src/h3/qpack/table.rs index 2727b62..0497939 100644 --- a/ylong_http/src/h3/qpack/table.rs +++ b/ylong_http/src/h3/qpack/table.rs @@ -1,4 +1,3 @@ - use std::collections::VecDeque; /// The [`Dynamic Table`][dynamic_table] implementation of [QPACK]. @@ -17,12 +16,57 @@ use std::collections::VecDeque; /// Dynamic table entries can have empty values. +pub(crate) struct TableSearcher<'a> { + dynamic: &'a DynamicTable, +} + +impl<'a> TableSearcher<'a> { + pub(crate) fn new(dynamic: &'a DynamicTable) -> Self { + Self { dynamic } + } + + + /// Searches index in static and dynamic tables. + pub(crate) fn find_index_static(&self, header: &Field, value: &str) -> Option { + match StaticTable::index(header, value) { + x @ Some(TableIndex::Field(_)) => x, + _ => Some(TableIndex::None), + } + } + + pub(crate) fn find_name_static(&self, header: &Field, value: &str) -> Option { + match StaticTable::index(header, value) { + x @ Some(TableIndex::FieldName(_)) => x, + _ => Some(TableIndex::None), + } + } + + pub(crate) fn find_index_dynamic(&self, header: &Field, value: &str) -> Option { + match self.dynamic.index(header, value) { + x @ Some(TableIndex::Field(_)) => x, + _ => Some(TableIndex::None), + } + } + + pub(crate) fn find_name_dynamic(&self, header: &Field, value: &str) -> Option { + match self.dynamic.index(header, value) { + x @ Some(TableIndex::FieldName(_)) => x, + _ => Some(TableIndex::None), + } + } + + pub(crate) fn can_index(&self, header: &Field, value: &str) -> bool { + //todo + true + } +} + + pub(crate) struct DynamicTable { - queue: VecDeque, + queue: VecDeque<(Field, String)>, // The size of the dynamic table is the sum of the size of its entries size: usize, capacity: usize, - abs_idx: usize, } impl DynamicTable { @@ -31,7 +75,6 @@ impl DynamicTable { queue: VecDeque::new(), size: 0, capacity, - abs_idx: 0, } } @@ -43,121 +86,52 @@ impl DynamicTable { 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; + /// Updates `DynamicTable` by a given `Header` and value pair. + pub(crate) fn update(&mut self, field: Field, value: String) { + // RFC7541-4.1: The additional 32 octets account for an estimated + // overhead associated with an entry. For example, an entry + // structure using two 64-bit pointers to reference the name and the + // value of the entry and two 64-bit integers for counting the + // number of references to the name and value would have 32 octets + // of overhead. + self.size += field.len() + value.len() + 32; + self.queue.push_front((field, value)); + self.fit_size(); + } - /// ## 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; + /// Updates `DynamicTable`'s size. + pub(crate) fn update_size(&mut self, max_size: usize) { + self.capacity = max_size; + self.fit_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; + /// Adjusts dynamic table content to fit its size. + fn fit_size(&mut self) { 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; - } + let (key, string) = self.queue.pop_back().unwrap(); + self.capacity -= key.len() + string.len() + 32; } } -} - -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!() + /// Tries get the index of a `Header`. + fn index(&self, header: &Field, value: &str) -> Option { + let mut index = None; + for (n, (h, v)) in self.queue.iter().enumerate() { + match (header == h, value == v, &index) { + (true, true, _) => return Some(TableIndex::Field(n)), + (true, false, None) => index = Some(TableIndex::FieldName(n)), + _ => {} + } + } + index } - } - +#[derive(PartialEq)] pub(crate) enum TableIndex { Field(usize), FieldName(usize), + None, } @@ -335,19 +309,6 @@ impl StaticTable { 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)), @@ -362,40 +323,6 @@ impl StaticTable { (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)), @@ -405,39 +332,89 @@ impl StaticTable { (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)), + (Field::Other(s), v) => match (s.as_str(), v) { + ("age", "0") => Some(TableIndex::Field(2)), + ("content-disposition", _) => Some(TableIndex::FieldName(3)), + ("content-length", "0") => Some(TableIndex::Field(4)), + ("cookie", _) => Some(TableIndex::FieldName(5)), + ("date", _) => Some(TableIndex::FieldName(6)), + ("etag", _) => Some(TableIndex::FieldName(7)), + ("if-modified-since", _) => Some(TableIndex::FieldName(8)), + ("if-none-match", _) => Some(TableIndex::FieldName(9)), + ("last-modified", _) => Some(TableIndex::FieldName(10)), + ("link", _) => Some(TableIndex::FieldName(11)), + ("location", _) => Some(TableIndex::FieldName(12)), + ("referer", _) => Some(TableIndex::FieldName(13)), + ("set-cookie", _) => Some(TableIndex::FieldName(14)), + ("accept", "*/*") => Some(TableIndex::Field(29)), + ("accept", "application/dns-message") => Some(TableIndex::Field(30)), + ("accept-encoding", "gzip, deflate, br") => Some(TableIndex::Field(31)), + ("accept-ranges", "bytes") => Some(TableIndex::Field(32)), + ("access-control-allow-headers", "cache-control") => Some(TableIndex::Field(33)), + ("access-control-allow-headers", "content-type") => Some(TableIndex::Field(34)), + ("access-control-allow-origin", "*") => Some(TableIndex::Field(35)), + ("cache-control", "max-age=0") => Some(TableIndex::Field(36)), + ("cache-control", "max-age=2592000") => Some(TableIndex::Field(37)), + ("cache-control", "max-age=604800") => Some(TableIndex::Field(38)), + ("cache-control", "no-cache") => Some(TableIndex::Field(39)), + ("cache-control", "no-store") => Some(TableIndex::Field(40)), + ("cache-control", "public, max-age=31536000") => Some(TableIndex::Field(41)), + ("content-encoding", "br") => Some(TableIndex::Field(42)), + ("content-encoding", "gzip") => Some(TableIndex::Field(43)), + ("content-type", "application/dns-message") => Some(TableIndex::Field(44)), + ("content-type", "application/javascript") => Some(TableIndex::Field(45)), + ("content-type", "application/json") => Some(TableIndex::Field(46)), + ("content-type", "application/x-www-form-urlencoded") => Some(TableIndex::Field(47)), + ("content-type", "image/gif") => Some(TableIndex::Field(48)), + ("content-type", "image/jpeg") => Some(TableIndex::Field(49)), + ("content-type", "image/png") => Some(TableIndex::Field(50)), + ("content-type", "text/css") => Some(TableIndex::Field(51)), + ("content-type", "text/html; charset=utf-8") => Some(TableIndex::Field(52)), + ("content-type", "text/plain") => Some(TableIndex::Field(53)), + ("content-type", "text/plain;charset=utf-8") => Some(TableIndex::Field(54)), + ("range", "bytes=0-") => Some(TableIndex::Field(55)), + ("strict-transport-security", "max-age=31536000") => Some(TableIndex::Field(56)), + ("strict-transport-security", "max-age=31536000; includesubdomains") => Some(TableIndex::Field(57)), + ("strict-transport-security", "max-age=31536000; includesubdomains; preload") => Some(TableIndex::Field(58)), + ("vary", "accept-encoding") => Some(TableIndex::Field(59)), + ("vary", "origin") => Some(TableIndex::Field(60)), + ("x-content-type-options", "nosniff") => Some(TableIndex::Field(61)), + ("x-xss-protection", "1; mode=block") => Some(TableIndex::Field(62)), + ("accept-language", _) => Some(TableIndex::FieldName(72)), + ("access-control-allow-credentials", "FALSE") => Some(TableIndex::Field(73)), + ("access-control-allow-credentials", "TRUE") => Some(TableIndex::Field(74)), + ("access-control-allow-headers", "*") => Some(TableIndex::Field(75)), + ("access-control-allow-methods", "get") => Some(TableIndex::Field(76)), + ("access-control-allow-methods", "get, post, options") => Some(TableIndex::Field(77)), + ("access-control-allow-methods", "options") => Some(TableIndex::Field(78)), + ("access-control-expose-headers", "content-length") => Some(TableIndex::Field(79)), + ("access-control-request-headers", "content-type") => Some(TableIndex::Field(80)), + ("access-control-request-method", "get") => Some(TableIndex::Field(81)), + ("access-control-request-method", "post") => Some(TableIndex::Field(82)), + ("alt-svc", "clear") => Some(TableIndex::Field(83)), + ("authorization", _) => Some(TableIndex::FieldName(84)), + ("content-security-policy", "script-src 'none'; object-src 'none'; base-uri 'none'") => Some(TableIndex::Field(85)), + ("early-data", "1") => Some(TableIndex::Field(86)), + ("expect-ct", _) => Some(TableIndex::FieldName(87)), + ("forwarded", _) => Some(TableIndex::FieldName(88)), + ("if-range", _) => Some(TableIndex::FieldName(89)), + ("origin", _) => Some(TableIndex::FieldName(90)), + ("purpose", "prefetch") => Some(TableIndex::Field(91)), + ("server", _) => Some(TableIndex::FieldName(92)), + ("timing-allow-origin", "*") => Some(TableIndex::Field(93)), + ("upgrade-insecure-requests", "1") => Some(TableIndex::Field(94)), + ("user-agent", _) => Some(TableIndex::FieldName(95)), + ("x-forwarded-for", _) => Some(TableIndex::FieldName(96)), + ("x-frame-options", "deny") => Some(TableIndex::Field(97)), + ("x-frame-options", "sameorigin") => Some(TableIndex::Field(98)), + _ => None, + }, _ => None, } } } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Debug)] pub(crate) enum Field { Authority, Method, diff --git a/ylong_http_client/tests/file/root-ca.pem b/ylong_http_client/tests/file/root-ca.pem index 4ec2f53..b095e16 100644 --- a/ylong_http_client/tests/file/root-ca.pem +++ b/ylong_http_client/tests/file/root-ca.pem @@ -12,7 +12,7 @@ aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn -BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv +BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+t+TBSpdIYtMv Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 -- Gitee From ff13c8f83217a9d455e77a61f6526371979431bc Mon Sep 17 00:00:00 2001 From: Weng Shihao Date: Mon, 14 Aug 2023 16:28:12 +0800 Subject: [PATCH 3/6] qpack --- ylong_http/src/h3/qpack/decoder.rs | 150 +++++++++++++++--- ylong_http/src/h3/qpack/mod.rs | 20 ++- .../src/h3/qpack/representation/decoder.rs | 123 +++++++++++++- .../src/h3/qpack/representation/encoder.rs | 4 +- ylong_http/src/h3/qpack/table.rs | 19 ++- 5 files changed, 276 insertions(+), 40 deletions(-) diff --git a/ylong_http/src/h3/qpack/decoder.rs b/ylong_http/src/h3/qpack/decoder.rs index 6318765..3c72c89 100644 --- a/ylong_http/src/h3/qpack/decoder.rs +++ b/ylong_http/src/h3/qpack/decoder.rs @@ -1,68 +1,161 @@ +use std::mem::take; +use crate::h3::error::ErrorCode::TEST; use crate::h3::error::H3Error; use crate::h3::parts::Parts; +use crate::h3::qpack::{Representation, ReprMidBit}; use crate::h3::qpack::representation::decoder::{ReprDecoder, ReprDecStateHolder}; -use crate::h3::qpack::table::DynamicTable; +use crate::h3::qpack::table::{DynamicTable, TableSearcher}; -struct HeaderLines { +struct FiledLines { parts: Parts, header_size: usize, } -pub(crate) struct QpackDncoder<'a>{ + +pub(crate) struct QpackDecoder<'a> { table: &'a mut DynamicTable, holder: ReprDecStateHolder, - lines: HeaderLines, + lines: FiledLines, + base: usize, } -impl<'a> QpackDncoder<'a> { - +impl<'a> QpackDecoder<'a> { pub(crate) fn new(table: &'a mut DynamicTable) -> Self { Self { table, holder: ReprDecStateHolder::new(), - lines: HeaderLines { + lines: FiledLines { parts: Parts::new(), header_size: 0, }, + base: 0, } } - pub(crate) fn decode(&mut self, buf: &[u8])->Result<(), H3Error>{ + pub(crate) fn decode(&mut self, buf: &[u8]) -> Result<(), H3Error> { // todo: InsDecoder like ReprDecoder let mut decoder = ReprDecoder::new(buf); decoder.load(&mut self.holder); - decoder.decode(); + let mut searcher = Searcher::new(&self.table, &mut self.lines); + loop { + match decoder.decode()? { + Some(repr) => { + match repr { + Representation::FieldSectionPrefix { require_insert_count, signal, delta_base } => { + println!("require_insert_count: {}", require_insert_count.0); + println!("signal: {}", signal); + println!("delta_base: {}", delta_base.0); + if signal { + self.base = require_insert_count.0 - delta_base.0 - 1; + } else { + self.base = require_insert_count.0 + delta_base.0; + } + searcher.base = self.base; + //todo:block + } + Representation::Indexed { mid_bit, index } => { + println!("T: {:#?}", mid_bit.t); + println!("N: {:#?}", mid_bit.n); + println!("H: {:#?}", mid_bit.h); + println!("index: {}", index); + searcher.search(Representation::Indexed { mid_bit, index })?; + } + } + } + None => return Result::Ok(()) + } + }; + } + pub(crate) fn finish(&mut self) -> Result { + if !self.holder.is_empty() { + return Err(H3Error::ConnectionError(TEST)); + } + self.lines.header_size = 0; + Ok(take(&mut self.lines.parts)) + } +} + + +struct Searcher<'a> { + table: &'a DynamicTable, + lines: &'a mut FiledLines, + base: usize, +} + +impl<'a> Searcher<'a> { + fn new(table: &'a DynamicTable, lines: &'a mut FiledLines) -> Self { + Self { + table, + lines, + base: 0, + } + } - return Ok(()); + fn search(&mut self, repr: Representation) -> Result<(), H3Error> { + match repr { + Representation::Indexed { mid_bit, index } => { + self.search_indexed(mid_bit, index) + } + _ => { + Ok(()) + } + } + } + fn search_indexed(&mut self, mid_bit: ReprMidBit, index: usize) -> Result<(), H3Error> { + let table_searcher = TableSearcher::new(self.table); + if let Some(true) = mid_bit.t { + let (f, v) = table_searcher + .find_field_static(index) + .ok_or(H3Error::ConnectionError(TEST))?; + self.lines.parts.update(f, v); + return Ok(()); + } else { + let (f, v) = table_searcher + .find_field_dynamic(index + self.base - 1) + .ok_or(H3Error::ConnectionError(TEST))?; + self.lines.parts.update(f, v); + return Ok(()); + } } } #[cfg(test)] -mod ut_qpack_decoder{ +mod ut_qpack_decoder { use crate::h3::qpack::table::{DynamicTable, Field}; - use crate::h3::qpack::QpackDncoder; + use crate::h3::qpack::QpackDecoder; use crate::test_util::decode; - #[test] - fn ut_qpack_decoder(){ + fn ut_qpack_decoder() { + test_indexed_static(); + macro_rules! check_pseudo { + ( + $pseudo: expr, + { $a: expr, $m: expr, $p: expr, $sc: expr, $st: expr } $(,)? + ) => { + assert_eq!($pseudo.authority(), $a); + assert_eq!($pseudo.method(), $m); + assert_eq!($pseudo.path(), $p); + assert_eq!($pseudo.scheme(), $sc); + assert_eq!($pseudo.status(), $st); + }; + } - test(); macro_rules! get_parts { ($qpack: expr $(, $input: literal)*) => {{ $( let text = decode($input).unwrap(); println!("text: {:#?}", text); - // assert!($qpack.decode(text.as_slice()).is_ok()); + assert!($qpack.decode(text.as_slice()).is_ok()); )* - // match $qpack.finish() { - // Ok(parts) => parts, - // Err(_) => panic!("HpackDecoder::finish() failed!"), - // } + match $qpack.finish() { + Ok(parts) => parts, + Err(_) => panic!("QpackDecoder::finish() failed!"), + } }}; } macro_rules! qpack_test_case { @@ -72,23 +165,28 @@ mod ut_qpack_decoder{ { $size: expr $(, $($k2: literal)? $($k3: ident)? => $v2: literal)* } $(,)? ) => { let mut _qpack = $qpack; - get_parts!(_qpack $(, $input)*); - // let (pseudo, _) = get_parts!(_hpack $(, $input)*).into_parts(); - // check_pseudo!(pseudo, { $a, $m, $p, $sc, $st }); + let (pseudo, _) = get_parts!(_qpack $(, $input)*).into_parts(); + check_pseudo!(pseudo, { $a, $m, $p, $sc, $st }); // check_table!(_hpack, $size, { $($($k2)? $($k3)? => $v2)* }); }; } - fn test() + fn test_indexed_static() { let mut dynamic_table = DynamicTable::with_capacity(4096); + qpack_test_case!( - QpackDncoder::new(&mut dynamic_table), - "82", + QpackDecoder::new(&mut dynamic_table), + "0000d1", { None, Some("GET"), None, None, None }, { 0 } ); } + fn test_indexed_dynamic() + { + let mut dynamic_table = DynamicTable::with_capacity(4096); + dynamic_table.update(Field::Other(String::from("my-field")), String::from("my-value")); + } } } diff --git a/ylong_http/src/h3/qpack/mod.rs b/ylong_http/src/h3/qpack/mod.rs index 44355a4..3260e77 100644 --- a/ylong_http/src/h3/qpack/mod.rs +++ b/ylong_http/src/h3/qpack/mod.rs @@ -4,9 +4,13 @@ mod representation; mod integer; mod decoder; -pub(crate) use decoder::QpackDncoder; +pub(crate) use decoder::QpackDecoder; +pub(crate) struct RequireInsertCount(usize); + +pub(crate) struct DeltaBase(usize); +#[derive(Copy, Clone, PartialEq, Eq)] pub(crate) struct ReprPrefixBit(u8); /// # Prefix bit: @@ -67,7 +71,6 @@ impl ReprPrefixBit { } } - pub(crate) enum Representation { /// An indexed field line representation identifies an entry in the static table or an entry in /// the dynamic table with an absolute index less than the value of the Base. @@ -80,10 +83,18 @@ pub(crate) enum Representation { /// (Section 4.1.1) that follows is used to locate the table entry for the field line. When T=1, /// the number represents the static table index; when T=0, the number is the relative index of /// the entry in the dynamic table. - Indexed { mid_bit:ReprMidBit, index: usize }, + + FieldSectionPrefix { require_insert_count: RequireInsertCount, signal: bool, delta_base: DeltaBase }, + + Indexed { mid_bit: ReprMidBit, index: usize }, + //todo: add more } + +//impl debug for Representation + + pub(crate) struct ReprMidBit { //'N', indicates whether an intermediary is permitted to add this field line to the dynamic // table on subsequent hops. @@ -99,8 +110,9 @@ pub(crate) struct PrefixMask(u8); impl PrefixMask { - pub(crate) const INDEXED: Self = Self(0x3f); + pub(crate) const REQUIREINSERTCOUNT: Self = Self(0xff); pub(crate) const DELTABASE: Self = Self(0x7f); + pub(crate) const INDEXED: Self = Self(0x3f); pub(crate) const SETCAP: Self = Self(0x1f); pub(crate) const INSERTWITHNAME: Self = Self(0x3f); pub(crate) const INSERTWITHLITERAL: Self = Self(0x1f); diff --git a/ylong_http/src/h3/qpack/representation/decoder.rs b/ylong_http/src/h3/qpack/representation/decoder.rs index 056293f..10b6766 100644 --- a/ylong_http/src/h3/qpack/representation/decoder.rs +++ b/ylong_http/src/h3/qpack/representation/decoder.rs @@ -1,5 +1,5 @@ -use crate::h3::error::H3Error; -use crate::h3::qpack::{ReprMidBit, Representation, ReprPrefixBit}; +use crate::h3::error::{ErrorCode, H3Error}; +use crate::h3::qpack::{ReprMidBit, Representation, ReprPrefixBit, RequireInsertCount, DeltaBase, PrefixMask}; use crate::h3::qpack::integer::IntegerDecoder; @@ -21,7 +21,6 @@ impl<'a> ReprDecoder<'a> { self.state = holder.state.take(); } - /// Decodes `self.buf`. Every time users call `decode`, it will try to /// decode a `Representation`. pub(crate) fn decode(&mut self) -> Result, H3Error> { @@ -33,18 +32,29 @@ impl<'a> ReprDecoder<'a> { match self .state .take() - .unwrap_or_else(|| ReprDecodeState::Index(Index::new())) + .unwrap_or_else(|| ReprDecodeState::FiledSectionPrefix(FiledSectionPrefix::new())) .decode(&mut self.buf) { // If `buf` is not enough to continue decoding a complete // `Representation`, `Ok(None)` will be returned. Users need to call // `save` to save the current state to a `ReprDecStateHolder`. DecResult::NeedMore(state) => { + println!("NeedMore"); self.state = Some(state); Ok(None) } - // If a `Representation` is decoded, return it. + // If a `Representation` is FieldSectionPrefix, give the state to the holder. + DecResult::Decoded(Representation::FieldSectionPrefix { require_insert_count, signal, delta_base }) => { + println!("FieldSectionPrefix"); + self.state = Some(ReprDecodeState::Index(Index::new())); + Ok(Some(Representation::FieldSectionPrefix { + require_insert_count, + signal, + delta_base, + })) + } DecResult::Decoded(repr) => Ok(Some(repr)), + DecResult::Error(error) => Err(error), } } @@ -98,12 +108,41 @@ macro_rules! state_def { state_def!( ReprDecodeState, Representation, + FiledSectionPrefix, Index, ); state_def!(IndexInner, (ReprPrefixBit, ReprMidBit, usize), FirstByte, ReprTrailingBytes); +state_def!(FSPInner, (RequireInsertCount, bool, DeltaBase), FSPTwoIntergers); +pub(crate) struct FiledSectionPrefix { + inner: FSPInner, +} + +impl FiledSectionPrefix { + fn new() -> Self { + Self::from_inner(FSPTwoIntergers.into()) + } + fn from_inner(inner: FSPInner) -> Self { + Self { inner } + } + + fn decode(self, buf: &mut &[u8]) -> DecResult { + match self.inner.decode(buf) { + DecResult::Decoded((ric, signal, delta_base)) => { + DecResult::Decoded(Representation::FieldSectionPrefix { + require_insert_count: ric, + signal:signal, + delta_base: delta_base, + }) + } + DecResult::NeedMore(inner) => DecResult::NeedMore(FiledSectionPrefix::from_inner(inner).into()), + DecResult::Error(e) => e.into(), + } + } +} + pub(crate) struct Index { inner: IndexInner, } @@ -120,13 +159,85 @@ impl Index { DecResult::Decoded((ReprPrefixBit::INDEXED, mid_bit, index)) => { DecResult::Decoded(Representation::Indexed { mid_bit, index }) } + DecResult::NeedMore(inner) => DecResult::NeedMore(Index::from_inner(inner).into()), + DecResult::Error(e) => e.into(), + _=>DecResult::Error(H3Error::ConnectionError(ErrorCode::TEST)), } } } +pub(crate) struct FSPTwoIntergers; -pub(crate) struct FirstByte; +impl FSPTwoIntergers { + fn decode(self, buf: &mut &[u8]) -> DecResult<(RequireInsertCount, bool, DeltaBase), FSPInner> { + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + let byte = buf[0]; + let mask = PrefixMask::REQUIREINSERTCOUNT; + *buf = &buf[1..]; + let ric = match IntegerDecoder::first_byte(byte, mask.0) { + Ok(ric) => ric, + Err(mut int) => { + let mut res: usize; + loop { + // If `buf` has been completely decoded here, return the current state. + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + + let byte = buf[0]; + *buf = &buf[1..]; + // Updates trailing bytes until we get the index. + match int.next_byte(byte) { + Ok(None) => {} + Ok(Some(index)) => { + res = index; + break; + } + Err(e) => return e.into(), + } + }; + res + } + }; + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + let byte = buf[0]; + let signal = (byte & 0x80) != 0; + let mask = PrefixMask::DELTABASE; + *buf = &buf[1..]; + let delta_base = match IntegerDecoder::first_byte(byte, mask.0) { + Ok(delta_base) => delta_base, + Err(mut int) => { + let mut res: usize; + loop { + // If `buf` has been completely decoded here, return the current state. + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + + let byte = buf[0]; + *buf = &buf[1..]; + // Updates trailing bytes until we get the index. + match int.next_byte(byte) { + Ok(None) => {} + Ok(Some(index)) => { + res = index; + break; + } + Err(e) => return e.into(), + } + }; + res + } + }; + DecResult::Decoded((RequireInsertCount(ric), signal, DeltaBase(delta_base))) + } +} +pub(crate) struct FirstByte; impl FirstByte { fn decode(self, buf: &mut &[u8]) -> DecResult<(ReprPrefixBit, ReprMidBit, usize), IndexInner> { diff --git a/ylong_http/src/h3/qpack/representation/encoder.rs b/ylong_http/src/h3/qpack/representation/encoder.rs index 39146ec..28dae04 100644 --- a/ylong_http/src/h3/qpack/representation/encoder.rs +++ b/ylong_http/src/h3/qpack/representation/encoder.rs @@ -69,11 +69,11 @@ impl<'a> ReprEncoder<'a> { } } else { let dynamic_index = searcher.find_index_dynamic(&h, &v); - let static_name_index = searcher.find_name_static(&h, &v); + let static_name_index = searcher.find_index_name_static(&h, &v); let mut dynamic_name_index = Some(TableIndex::None); if dynamic_index == Some(TableIndex::None) { if static_name_index == Some(TableIndex::None) { - dynamic_name_index = searcher.find_name_dynamic(&h, &v); + dynamic_name_index = searcher.find_index_name_dynamic(&h, &v); } if self.should_index(&h, &v) && searcher.can_index(&h, &v) { encoder_result = match (&static_name_index, &dynamic_name_index) { diff --git a/ylong_http/src/h3/qpack/table.rs b/ylong_http/src/h3/qpack/table.rs index 0497939..4fb0266 100644 --- a/ylong_http/src/h3/qpack/table.rs +++ b/ylong_http/src/h3/qpack/table.rs @@ -34,7 +34,7 @@ impl<'a> TableSearcher<'a> { } } - pub(crate) fn find_name_static(&self, header: &Field, value: &str) -> Option { + pub(crate) fn find_index_name_static(&self, header: &Field, value: &str) -> Option { match StaticTable::index(header, value) { x @ Some(TableIndex::FieldName(_)) => x, _ => Some(TableIndex::None), @@ -48,13 +48,24 @@ impl<'a> TableSearcher<'a> { } } - pub(crate) fn find_name_dynamic(&self, header: &Field, value: &str) -> Option { + pub(crate) fn find_index_name_dynamic(&self, header: &Field, value: &str) -> Option { match self.dynamic.index(header, value) { x @ Some(TableIndex::FieldName(_)) => x, _ => Some(TableIndex::None), } } + pub(crate) fn find_field_static(&self, index: usize) -> Option<(Field, String)> { + match StaticTable::field(index) { + x @ Some((_, _)) => x, + _ => None, + } + } + + pub(crate) fn find_field_dynamic(&self, index: usize) -> Option<(Field, String)> { + self.dynamic.field(index) + } + pub(crate) fn can_index(&self, header: &Field, value: &str) -> bool { //todo true @@ -125,6 +136,10 @@ impl DynamicTable { } index } + + pub(crate) fn field(&self, index: usize) -> Option<(Field, String)> { + self.queue.get(index).cloned() + } } #[derive(PartialEq)] -- Gitee From cfbaf16ab08d3607b6f5b1f947f79301fcd85c8a Mon Sep 17 00:00:00 2001 From: Weng Shihao Date: Wed, 16 Aug 2023 17:52:32 +0800 Subject: [PATCH 4/6] qpack --- ylong_http/src/body/mod.rs | 2 +- ylong_http/src/h2/hpack/integer.rs | 4 +- .../src/h2/hpack/representation/decoder.rs | 4 +- .../src/h2/hpack/representation/encoder.rs | 2 +- ylong_http/src/h2/hpack/representation/mod.rs | 44 +- ylong_http/src/h3/qpack/decoder.rs | 390 ++++++++++++++- ylong_http/src/h3/qpack/encoder.rs | 2 +- .../{representation => format}/decoder.rs | 467 ++++++++++++++++-- .../{representation => format}/encoder.rs | 4 +- .../qpack/{representation => format}/mod.rs | 0 ylong_http/src/h3/qpack/mod.rs | 103 +++- ylong_http/src/h3/qpack/table.rs | 35 +- ylong_http/src/headers.rs | 2 +- ylong_http/src/request/method.rs | 2 +- 14 files changed, 942 insertions(+), 119 deletions(-) rename ylong_http/src/h3/qpack/{representation => format}/decoder.rs (36%) rename ylong_http/src/h3/qpack/{representation => format}/encoder.rs (99%) rename ylong_http/src/h3/qpack/{representation => format}/mod.rs (100%) diff --git a/ylong_http/src/body/mod.rs b/ylong_http/src/body/mod.rs index 7cb1b32..7b164cc 100644 --- a/ylong_http/src/body/mod.rs +++ b/ylong_http/src/body/mod.rs @@ -15,7 +15,7 @@ //! //! # Introduction //! -//! HTTP messages often transfer a complete or partial representation as the +//! HTTP messages often transfer a complete or partial format as the //! message content: a stream of octets sent after the header section, as //! delineated by the message framing. //! diff --git a/ylong_http/src/h2/hpack/integer.rs b/ylong_http/src/h2/hpack/integer.rs index c668a3f..f52c12e 100644 --- a/ylong_http/src/h2/hpack/integer.rs +++ b/ylong_http/src/h2/hpack/integer.rs @@ -18,8 +18,8 @@ //! //! # Introduction //! Integers are used to represent name indexes, header field indexes, or -//! string lengths. An integer representation can start anywhere within an octet. -//! To allow for optimized processing, an integer representation always finishes +//! string lengths. An integer format can start anywhere within an octet. +//! To allow for optimized processing, an integer format always finishes //! at the end of an octet. use crate::h2::error::ErrorCode; diff --git a/ylong_http/src/h2/hpack/representation/decoder.rs b/ylong_http/src/h2/hpack/representation/decoder.rs index bf72314..afffab9 100644 --- a/ylong_http/src/h2/hpack/representation/decoder.rs +++ b/ylong_http/src/h2/hpack/representation/decoder.rs @@ -18,7 +18,7 @@ use crate::h2::H2Error; use crate::huffman::HuffmanDecoder; use core::cmp::Ordering; -/// Decoder implementation for decoding representation. Every time users call +/// Decoder implementation for decoding format. Every time users call /// `decode`, the `ReprDecoder` will try to decode a `Repr`. If `buf` has been /// fully decoded and users continue to call `decode`, it will return `None`. /// Users need to save intermediate results in time. @@ -167,7 +167,7 @@ impl Index { fn decode(self, buf: &mut &[u8]) -> DecResult { match self.inner.decode(buf) { // RFC7541-6.1: The index value of 0 is not used. It MUST be treated - // as a decoding error if found in an indexed header field representation. + // as a decoding error if found in an indexed header field format. DecResult::Decoded((PrefixBit::INDEXED, 0)) => { H2Error::ConnectionError(ErrorCode::CompressionError).into() } diff --git a/ylong_http/src/h2/hpack/representation/encoder.rs b/ylong_http/src/h2/hpack/representation/encoder.rs index 54a884d..5dec777 100644 --- a/ylong_http/src/h2/hpack/representation/encoder.rs +++ b/ylong_http/src/h2/hpack/representation/encoder.rs @@ -19,7 +19,7 @@ use crate::headers::HeadersIntoIter; use core::cmp::Ordering; use std::collections::hash_map::IntoIter; -/// Encoder implementation for decoding representation. The encode interface +/// Encoder implementation for decoding format. The encode interface /// supports segmented writing. pub(crate) struct ReprEncoder<'a> { table: &'a mut DynamicTable, diff --git a/ylong_http/src/h2/hpack/representation/mod.rs b/ylong_http/src/h2/hpack/representation/mod.rs index 9438ce8..bf8450b 100644 --- a/ylong_http/src/h2/hpack/representation/mod.rs +++ b/ylong_http/src/h2/hpack/representation/mod.rs @@ -19,27 +19,27 @@ //! # Description from RFC7541 //! An encoded header field can be represented either as an index or as a literal. //! -//! An [indexed representation] defines a header field as a reference to an +//! An [indexed format] defines a header field as a reference to an //! entry in either the static table or the dynamic table. //! -//! A [literal representation] defines a header field by specifying its +//! A [literal format] defines a header field by specifying its //! name and value. The header field name can be represented literally or as a //! reference to an entry in either the static table or the dynamic table. //! The header field value is represented literally. //! //! Three different literal representations are defined: //! -//! - A literal representation that adds the header field as a new entry at the +//! - A literal format that adds the header field as a new entry at the //! beginning of the dynamic table (see //! [Literal Header Field with Incremental Indexing]). //! -//! - A literal representation that does not add the header field to the dynamic +//! - A literal format that does not add the header field to the dynamic //! table (see [Literal Header Field without Indexing]). //! -//! - A literal representation that does not add the header field to the dynamic +//! - A literal format that does not add the header field to the dynamic //! table, with the additional stipulation that this header field always use a -//! literal representation, in particular when re-encoded by an intermediary -//! (see [Literal Header Field Never Indexed]. This representation is intended +//! literal format, in particular when re-encoded by an intermediary +//! (see [Literal Header Field Never Indexed]. This format is intended //! for protecting header field values that are not to be put at risk by //! compressing them (see [Never-Indexed Literals] for more details). //! @@ -47,7 +47,7 @@ //! security considerations, in order to protect sensitive header field values //! (see [Probing Dynamic Table State]). //! -//! The literal representation of a header field name or of a header field value +//! The literal format of a header field name or of a header field value //! can encode the sequence of octets either directly or using a static //! Huffman code (see [String Literal Representation]). //! @@ -57,8 +57,8 @@ //! [Never-Indexed Literals]: https://www.rfc-editor.org/rfc/rfc7541.html#section-7.1.3 //! [Probing Dynamic Table State]: https://www.rfc-editor.org/rfc/rfc7541.html#section-7.1 //! [String Literal Representation]: https://www.rfc-editor.org/rfc/rfc7541.html#section-5.2 -//! [indexed representation]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.1 -//! [literal representation]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.2 +//! [indexed format]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.1 +//! [literal format]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.2 mod decoder; mod encoder; @@ -73,7 +73,7 @@ pub(crate) use encoder::{ReprEncStateHolder, ReprEncodeState, ReprEncoder}; /// [header field representations]: https://www.rfc-editor.org/rfc/rfc7541.html#section-3.2 /// [dynamic table size update]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.3 pub(crate) enum Representation { - /// An [indexed header field representation] identifies an entry in either + /// An [indexed header field format] identifies an entry in either /// the static table or the dynamic table. It causes a header field to be /// added to the decoded header list. /// @@ -81,7 +81,7 @@ pub(crate) enum Representation { /// the index of the matching header field, represented as an integer with /// a 7-bit prefix. /// - /// [indexed header field representation]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.1 + /// [indexed header field format]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.1 /// /// # Binary Format /// `Indexed Header Field`: @@ -93,14 +93,14 @@ pub(crate) enum Representation { /// ``` Indexed { index: usize }, - /// A [literal header field with incremental indexing representation] results + /// A [literal header field with incremental indexing format] results /// in appending a header field to the decoded header list and inserting it /// as a new entry into the dynamic table. /// - /// A literal header field with incremental indexing representation starts + /// A literal header field with incremental indexing format starts /// with the '01' 2-bit pattern. /// - /// [literal header field with incremental indexing representation]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.2.1 + /// [literal header field with incremental indexing format]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.2.1 /// /// # Binary Format /// `Literal Header Field with Incremental Indexing -- Indexed Name`: @@ -132,14 +132,14 @@ pub(crate) enum Representation { /// ``` LiteralWithIndexing { name: Name, value: Vec }, - /// A [literal header field without indexing representation] results in + /// A [literal header field without indexing format] results in /// appending a header field to the decoded header list without altering /// the dynamic table. /// - /// A literal header field without indexing representation starts with the + /// A literal header field without indexing format starts with the /// '0000' 4-bit pattern. /// - /// [literal header field without indexing representation]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.2.2 + /// [literal header field without indexing format]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.2.2 /// /// # Binary Format /// `Literal Header Field without Indexing -- Indexed Name`: @@ -170,15 +170,15 @@ pub(crate) enum Representation { /// ``` LiteralWithoutIndexing { name: Name, value: Vec }, - /// A [literal header field never-indexed representation] results in appending + /// A [literal header field never-indexed format] results in appending /// a header field to the decoded header list without altering the dynamic - /// table. Intermediaries **MUST** use the same representation for encoding + /// table. Intermediaries **MUST** use the same format for encoding /// this header field. /// - /// A literal header field never-indexed representation starts with the + /// A literal header field never-indexed format starts with the /// '0001' 4-bit pattern. /// - /// [literal header field never-indexed representation]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.2.3 + /// [literal header field never-indexed format]: https://www.rfc-editor.org/rfc/rfc7541.html#section-6.2.3 /// /// # Binary Format /// `Literal Header Field Never Indexed -- Indexed Name`: diff --git a/ylong_http/src/h3/qpack/decoder.rs b/ylong_http/src/h3/qpack/decoder.rs index 3c72c89..07c095a 100644 --- a/ylong_http/src/h3/qpack/decoder.rs +++ b/ylong_http/src/h3/qpack/decoder.rs @@ -2,10 +2,11 @@ use std::mem::take; use crate::h3::error::ErrorCode::TEST; use crate::h3::error::H3Error; use crate::h3::parts::Parts; -use crate::h3::qpack::{Representation, ReprMidBit}; +use crate::h3::qpack::{Representation, MidBit, ReprPrefixBit, Instruction, InstPrefixBit}; + +use crate::h3::qpack::format::decoder::{InstDecoder, InstDecStateHolder, Name, ReprDecoder, ReprDecStateHolder}; +use crate::h3::qpack::table::{DynamicTable, Field, TableSearcher}; -use crate::h3::qpack::representation::decoder::{ReprDecoder, ReprDecStateHolder}; -use crate::h3::qpack::table::{DynamicTable, TableSearcher}; struct FiledLines { parts: Parts, @@ -13,17 +14,21 @@ struct FiledLines { } pub(crate) struct QpackDecoder<'a> { + field_list_size: usize, table: &'a mut DynamicTable, - holder: ReprDecStateHolder, + repr_holder: ReprDecStateHolder, + inst_holder: InstDecStateHolder, lines: FiledLines, base: usize, } impl<'a> QpackDecoder<'a> { - pub(crate) fn new(table: &'a mut DynamicTable) -> Self { + pub(crate) fn new(field_list_size: usize, table: &'a mut DynamicTable) -> Self { Self { + field_list_size, table, - holder: ReprDecStateHolder::new(), + repr_holder: ReprDecStateHolder::new(), + inst_holder: InstDecStateHolder::new(), lines: FiledLines { parts: Parts::new(), header_size: 0, @@ -32,12 +37,43 @@ impl<'a> QpackDecoder<'a> { } } - pub(crate) fn decode(&mut self, buf: &[u8]) -> Result<(), H3Error> { - // todo: InsDecoder like ReprDecoder + pub(crate) fn decode_ins(&mut self, buf: &[u8]) -> Result<(), H3Error> { + let mut decoder = InstDecoder::new(buf); + decoder.load(&mut self.inst_holder); + let mut updater = Updater::new(&mut self.table); + loop { + match decoder.decode()? { + Some(inst) => { + match inst { + Instruction::SetCap { capacity } => { + println!("capacity: {}", capacity); + updater.update_capacity(capacity)?; + } + Instruction::InsertWithIndex { mid_bit, name, value } => { + let value_str = String::from_utf8(value.clone()).unwrap(); + println!("value: {}", value_str); + updater.update_table(mid_bit, name, value)?; + } + Instruction::InsertWithLiteral { mid_bit, name, value } => { + let value_str = String::from_utf8(value.clone()).unwrap(); + println!("value: {}", value_str); + updater.update_table(mid_bit, name, value)?; + } + Instruction::Duplicate { index } => { + updater.duplicate(index)?; + } + } + } + None => return Result::Ok(()) + } + } + } + + pub(crate) fn decode_repr(&mut self, buf: &[u8]) -> Result<(), H3Error> { let mut decoder = ReprDecoder::new(buf); - decoder.load(&mut self.holder); + decoder.load(&mut self.repr_holder); - let mut searcher = Searcher::new(&self.table, &mut self.lines); + let mut searcher = Searcher::new(self.field_list_size, &self.table, &mut self.lines); loop { match decoder.decode()? { Some(repr) => { @@ -61,6 +97,32 @@ impl<'a> QpackDecoder<'a> { println!("index: {}", index); searcher.search(Representation::Indexed { mid_bit, index })?; } + Representation::IndexedWithPostIndex { index } => { + println!("post index: {}", index); + searcher.search(Representation::IndexedWithPostIndex { index })?; + } + Representation::LiteralWithIndexing { mid_bit, name, value } => { + // convert value to str according to ascii + let value_str = String::from_utf8(value.clone()).unwrap(); + println!("value: {}", value_str); + searcher.search_literal_with_indexing(mid_bit, name, value)?; + } + Representation::LiteralWithPostIndexing { mid_bit, name, value } => { + let value_str = String::from_utf8(value.clone()).unwrap(); + println!("value: {}", value_str); + searcher.search_literal_with_post_indexing(mid_bit, name, value)?; + } + Representation::LiteralWithLiteralName { mid_bit, name, value } => { + if let Name::Literal(_name) = name.clone() { + let name_str = String::from_utf8(_name.clone()).unwrap(); + println!("name: {}", name_str); + } else { + print!("error"); + } + let value_str = String::from_utf8(value.clone()).unwrap(); + println!("value: {}", value_str); + searcher.search_listeral_with_literal(mid_bit, name, value)?; + } } } None => return Result::Ok(()) @@ -68,7 +130,7 @@ impl<'a> QpackDecoder<'a> { }; } pub(crate) fn finish(&mut self) -> Result { - if !self.holder.is_empty() { + if !self.repr_holder.is_empty() { return Err(H3Error::ConnectionError(TEST)); } self.lines.header_size = 0; @@ -76,16 +138,80 @@ impl<'a> QpackDecoder<'a> { } } +struct Updater<'a> { + table: &'a mut DynamicTable, +} + +impl<'a> Updater<'a> { + fn new(table: &'a mut DynamicTable) -> Self { + Self { + table, + } + } + + fn update_capacity(&mut self, capacity: usize) -> Result<(), H3Error> { + self.table.update_size(capacity); + Ok(()) + } + + fn update_table(&mut self, mid_bit: MidBit, name: Name, value: Vec) -> Result<(), H3Error> { + let (f, v) = self.get_field_by_name_and_value(mid_bit, name, value, self.table.insert_count)?; + self.table.update(f, v); + Ok(()) + } + + fn duplicate(&mut self, index: usize) -> Result<(), H3Error> { + let table_searcher = TableSearcher::new(self.table); + let (f, v) = table_searcher + .find_field_dynamic(self.table.insert_count - index) + .ok_or(H3Error::ConnectionError(TEST))?; + self.table.update(f, v); + Ok(()) + } + + fn get_field_by_name_and_value( + &self, + mid_bit: MidBit, + name: Name, + value: Vec, + insert_count: usize, + ) -> Result<(Field, String), H3Error> { + let h = match name { + Name::Index(index) => { + let searcher = TableSearcher::new(self.table); + if let Some(true) = mid_bit.t { + searcher + .find_field_name_static(index) + .ok_or(H3Error::ConnectionError(TEST))? + } else { + searcher + .find_field_name_dynamic(insert_count - index) + .ok_or(H3Error::ConnectionError(TEST))? + } + } + Name::Literal(octets) => Field::Other( + String::from_utf8(octets) + .map_err(|_| H3Error::ConnectionError(TEST))?, + ), + }; + let v = String::from_utf8(value) + .map_err(|_| H3Error::ConnectionError(TEST))?; + Ok((h, v)) + } +} + struct Searcher<'a> { + field_list_size: usize, table: &'a DynamicTable, lines: &'a mut FiledLines, base: usize, } impl<'a> Searcher<'a> { - fn new(table: &'a DynamicTable, lines: &'a mut FiledLines) -> Self { + fn new(field_list_size: usize, table: &'a DynamicTable, lines: &'a mut FiledLines) -> Self { Self { + field_list_size, table, lines, base: 0, @@ -97,13 +223,16 @@ impl<'a> Searcher<'a> { Representation::Indexed { mid_bit, index } => { self.search_indexed(mid_bit, index) } + Representation::IndexedWithPostIndex { index } => { + self.search_post_indexed(index) + } _ => { Ok(()) } } } - fn search_indexed(&mut self, mid_bit: ReprMidBit, index: usize) -> Result<(), H3Error> { + fn search_indexed(&mut self, mid_bit: MidBit, index: usize) -> Result<(), H3Error> { let table_searcher = TableSearcher::new(self.table); if let Some(true) = mid_bit.t { let (f, v) = table_searcher @@ -113,14 +242,98 @@ impl<'a> Searcher<'a> { return Ok(()); } else { let (f, v) = table_searcher - .find_field_dynamic(index + self.base - 1) + .find_field_dynamic(self.base - index - 1) .ok_or(H3Error::ConnectionError(TEST))?; self.lines.parts.update(f, v); return Ok(()); } } + + fn search_post_indexed(&mut self, index: usize) -> Result<(), H3Error> { + let table_searcher = TableSearcher::new(self.table); + let (f, v) = table_searcher + .find_field_dynamic(self.base + index) + .ok_or(H3Error::ConnectionError(TEST))?; + self.check_field_list_size(&f, &v)?; + self.lines.parts.update(f, v); + return Ok(()); + } + + fn search_literal_with_indexing(&mut self, mid_bit: MidBit, name: Name, value: Vec) -> Result<(), H3Error> { + let (f, v) = self.get_field_by_name_and_value(mid_bit, name, value, ReprPrefixBit::LITERALWITHINDEXING)?; + self.check_field_list_size(&f, &v)?; + self.lines.parts.update(f, v); + Ok(()) + } + + fn search_literal_with_post_indexing(&mut self, mid_bit: MidBit, name: Name, value: Vec) -> Result<(), H3Error> { + let (f, v) = self.get_field_by_name_and_value(mid_bit, name, value, ReprPrefixBit::LITERALWITHPOSTINDEXING)?; + self.check_field_list_size(&f, &v)?; + self.lines.parts.update(f, v); + Ok(()) + } + + fn search_listeral_with_literal(&mut self, mid_bit: MidBit, name: Name, value: Vec) -> Result<(), H3Error> { + let (h, v) = self.get_field_by_name_and_value(mid_bit, name, value, ReprPrefixBit::LITERALWITHLITERALNAME)?; + self.check_field_list_size(&h, &v)?; + self.lines.parts.update(h, v); + Ok(()) + } + + + fn get_field_by_name_and_value( + &self, + mid_bit: MidBit, + name: Name, + value: Vec, + repr: ReprPrefixBit, + ) -> Result<(Field, String), H3Error> { + let h = match name { + Name::Index(index) => { + if repr == ReprPrefixBit::LITERALWITHINDEXING { + let searcher = TableSearcher::new(self.table); + if let Some(true) = mid_bit.t { + searcher + .find_field_name_static(index) + .ok_or(H3Error::ConnectionError(TEST))? + } else { + searcher + .find_field_name_dynamic(self.base - index - 1) + .ok_or(H3Error::ConnectionError(TEST))? + } + } else { + let searcher = TableSearcher::new(self.table); + searcher + .find_field_name_dynamic(self.base + index) + .ok_or(H3Error::ConnectionError(TEST))? + } + } + Name::Literal(octets) => Field::Other( + String::from_utf8(octets) + .map_err(|_| H3Error::ConnectionError(TEST))?, + ), + }; + let v = String::from_utf8(value) + .map_err(|_| H3Error::ConnectionError(TEST))?; + Ok((h, v)) + } + pub(crate) fn update_size(&mut self, addition: usize) { + self.lines.header_size += addition; + } + fn check_field_list_size(&mut self, key: &Field, value: &String) -> Result<(), H3Error> { + let line_size = field_line_length(key.len(), value.len()); + self.update_size(line_size); + if self.lines.header_size > self.field_list_size { + Err(H3Error::ConnectionError(TEST)) + } else { + Ok(()) + } + } } +fn field_line_length(key_size: usize, value_size: usize) -> usize { + key_size + value_size + 32 +} #[cfg(test)] mod ut_qpack_decoder { @@ -128,10 +341,19 @@ mod ut_qpack_decoder { use crate::h3::qpack::QpackDecoder; use crate::test_util::decode; + const MAX_HEADER_LIST_SIZE: usize = 16 << 20; + #[test] fn ut_qpack_decoder() { - test_indexed_static(); + // test_indexed_static(); + // test_indexed_dynamic(); + // test_post_indexed_dynamic(); + // test_literal_indexing_static(); + // test_literal_indexing_dynamic(); + // test_literal_post_indexing_dynamic(); + // test_literal_with_literal_name(); + test_setcap(); macro_rules! check_pseudo { ( $pseudo: expr, @@ -150,7 +372,7 @@ mod ut_qpack_decoder { $( let text = decode($input).unwrap(); println!("text: {:#?}", text); - assert!($qpack.decode(text.as_slice()).is_ok()); + assert!($qpack.decode_repr(text.as_slice()).is_ok()); )* match $qpack.finish() { Ok(parts) => parts, @@ -158,6 +380,13 @@ mod ut_qpack_decoder { } }}; } + macro_rules! check_map { + ($map: expr, { $($(,)? $k: literal => $v: literal)* } $(,)?) => { + $( + assert_eq!($map.get($k).unwrap().to_str().unwrap(), $v); + )* + } + } macro_rules! qpack_test_case { ( $qpack: expr $(, $input: literal)*, @@ -167,7 +396,26 @@ mod ut_qpack_decoder { let mut _qpack = $qpack; let (pseudo, _) = get_parts!(_qpack $(, $input)*).into_parts(); check_pseudo!(pseudo, { $a, $m, $p, $sc, $st }); - // check_table!(_hpack, $size, { $($($k2)? $($k3)? => $v2)* }); + }; + ( + $qpack: expr $(, $input: literal)*, + { $($(,)? $k1: literal => $v1: literal)* }, + { $size: expr $(, $($k2: literal)? $($k3: ident)? => $v2: literal)* } $(,)? + ) => { + let mut _qpack = $qpack; + let (_, map) = get_parts!(_qpack $(, $input)*).into_parts(); + check_map!(map, { $($k1 => $v1)* }); + }; + ( + $hpack: expr $(, $input: literal)*, + { $a: expr, $m: expr, $p: expr, $sc: expr, $st: expr }, + { $($(,)? $k1: literal => $v1: literal)* }, + { $size: expr $(, $($k2: literal)? $($k3: ident)? => $v2: literal)* } $(,)? + ) => { + let mut _hpack = $hpack; + let (pseudo, map) = get_parts!(_hpack $(, $input)*).into_parts(); + check_pseudo!(pseudo, { $a, $m, $p, $sc, $st }); + check_map!(map, { $($k1 => $v1)* }); }; } @@ -176,7 +424,7 @@ mod ut_qpack_decoder { let mut dynamic_table = DynamicTable::with_capacity(4096); qpack_test_case!( - QpackDecoder::new(&mut dynamic_table), + QpackDecoder::new(MAX_HEADER_LIST_SIZE,&mut dynamic_table), "0000d1", { None, Some("GET"), None, None, None }, { 0 } @@ -184,9 +432,115 @@ mod ut_qpack_decoder { } fn test_indexed_dynamic() { + // Test index "custom-field"=>"custom-value" in dynamic table let mut dynamic_table = DynamicTable::with_capacity(4096); + //abs = 0 + dynamic_table.update(Field::Other(String::from("custom-field")), String::from("custom-value")); + //abs = 1 dynamic_table.update(Field::Other(String::from("my-field")), String::from("my-value")); + qpack_test_case!( + QpackDecoder::new(MAX_HEADER_LIST_SIZE,&mut dynamic_table), + //require_insert_count=2, signal=false, delta_base=0 + //so base=2 + //rel_index=1 (abs=2(base)-1-1=0) + "020081", + {"custom-field"=>"custom-value"}, + { 0 } + ); } + fn test_post_indexed_dynamic() + { + // Test index "custom-field"=>"custom-value" in dynamic table + let mut dynamic_table = DynamicTable::with_capacity(4096); + //abs = 0 + dynamic_table.update(Field::Other(String::from("custom1-field")), String::from("custom1-value")); + //abs = 1 + dynamic_table.update(Field::Other(String::from("custom2-field")), String::from("custom2-value")); + //abs = 2 + dynamic_table.update(Field::Other(String::from("custom3-field")), String::from("custom3-value")); + qpack_test_case!( + QpackDecoder::new(MAX_HEADER_LIST_SIZE,&mut dynamic_table), + //require_insert_count=3, signal=true, delta_base=2 + //so base = 3-2-1 = 0 + //rel_index=1 (abs=0(base)+1=1) + "038211", + {"custom2-field"=>"custom2-value"}, + { 0 } + ); + } + fn test_literal_indexing_static() + { + let mut dynamic_table = DynamicTable::with_capacity(4096); + qpack_test_case!( + QpackDecoder::new(MAX_HEADER_LIST_SIZE,&mut dynamic_table), + "00007f020d637573746f6d312d76616c7565", + { None, Some("custom1-value"), None, None, None }, + { 0 } + ); + } + + fn test_literal_indexing_dynamic() + { + let mut dynamic_table = DynamicTable::with_capacity(4096); + //abs = 0 + dynamic_table.update(Field::Other(String::from("custom-field")), String::from("custom-value")); + //abs = 1 + dynamic_table.update(Field::Other(String::from("my-field")), String::from("my-value")); + qpack_test_case!( + QpackDecoder::new(MAX_HEADER_LIST_SIZE,&mut dynamic_table), + //require_insert_count=2, signal=false, delta_base=0 + //so base=2 + //rel_index=1 (abs=2(base)-1-1=0) + "0200610d637573746f6d312d76616c7565", + {"custom-field"=>"custom1-value"}, + { 0 } + ); + } + + fn test_literal_post_indexing_dynamic() + { + // Test index "custom-field"=>"custom-value" in dynamic table + let mut dynamic_table = DynamicTable::with_capacity(4096); + //abs = 0 + dynamic_table.update(Field::Other(String::from("custom1-field")), String::from("custom1-value")); + //abs = 1 + dynamic_table.update(Field::Other(String::from("custom2-field")), String::from("custom2-value")); + //abs = 2 + dynamic_table.update(Field::Other(String::from("custom3-field")), String::from("custom3-value")); + qpack_test_case!( + QpackDecoder::new(MAX_HEADER_LIST_SIZE,&mut dynamic_table), + //require_insert_count=3, signal=true, delta_base=2 + //so base = 3-2-1 = 0 + //rel_index=1 (abs=0(base)+1=1) + "0382010d637573746f6d312d76616c7565", + {"custom2-field"=>"custom1-value"}, + { 0 } + ); + } + + fn test_literal_with_literal_name() + { + let mut dynamic_table = DynamicTable::with_capacity(4096); + qpack_test_case!( + QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table), + "00003706637573746f6d322d76616c75650d637573746f6d312d76616c7565", + {"custom2-value"=>"custom1-value"}, + {0}, + ); + } + + fn test_setcap() + { + let mut dynamic_table = DynamicTable::with_capacity(4096); + let mut decoder=QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let text = decode("3f7f").unwrap(); + println!("text: {:#?}", text); + decoder.decode_ins(text.as_slice()); + print!("capacity: {}", dynamic_table.capacity()); + assert_eq!(dynamic_table.capacity(), 158); + } + + } } diff --git a/ylong_http/src/h3/qpack/encoder.rs b/ylong_http/src/h3/qpack/encoder.rs index bf732ec..04e11bd 100644 --- a/ylong_http/src/h3/qpack/encoder.rs +++ b/ylong_http/src/h3/qpack/encoder.rs @@ -1,6 +1,6 @@ use crate::h3::parts::Parts; use crate::h3::qpack::table::DynamicTable; -use crate::h3::qpack::representation::{ReprEncoder, ReprEncStateHolder}; +use crate::h3::qpack::format::{ReprEncoder, ReprEncStateHolder}; use crate::h3::qpack::PrefixMask; use crate::h3::qpack::integer::{Integer, IntegerEncoder}; diff --git a/ylong_http/src/h3/qpack/representation/decoder.rs b/ylong_http/src/h3/qpack/format/decoder.rs similarity index 36% rename from ylong_http/src/h3/qpack/representation/decoder.rs rename to ylong_http/src/h3/qpack/format/decoder.rs index 10b6766..b1621c0 100644 --- a/ylong_http/src/h3/qpack/representation/decoder.rs +++ b/ylong_http/src/h3/qpack/format/decoder.rs @@ -1,7 +1,50 @@ +use std::cmp::Ordering; use crate::h3::error::{ErrorCode, H3Error}; -use crate::h3::qpack::{ReprMidBit, Representation, ReprPrefixBit, RequireInsertCount, DeltaBase, PrefixMask}; +use crate::h3::error::ErrorCode::TEST; +use crate::h3::qpack::{MidBit, Representation, ReprPrefixBit, RequireInsertCount, DeltaBase, PrefixMask, Instruction, InstPrefixBit}; use crate::h3::qpack::integer::IntegerDecoder; +use crate::h3::qpack::format::decoder::DecResult::Error; +use crate::huffman::HuffmanDecoder; +pub(crate) struct InstDecoder<'a>{ + buf: &'a [u8], + state: Option, +} + +impl<'a> InstDecoder<'a> { + pub(crate) fn new(buf: &'a [u8]) -> Self { + Self { buf, state: None } + } + pub(crate) fn load(&mut self, holder: &mut InstDecStateHolder) { + self.state = holder.state.take(); + } + pub(crate) fn decode(&mut self) -> Result, H3Error> { + if self.buf.is_empty() { + return Ok(None); + } + + match self + .state + .take() + .unwrap_or_else(|| InstDecodeState::InstIndex(InstIndex::new())) + .decode(&mut self.buf) + { + // If `buf` is not enough to continue decoding a complete + // `Representation`, `Ok(None)` will be returned. Users need to call + // `save` to save the current state to a `ReprDecStateHolder`. + DecResult::NeedMore(state) => { + println!("NeedMore"); + self.state = Some(state); + Ok(None) + } + DecResult::Decoded(repr) => { + Ok(Some(repr)) + } + + DecResult::Error(error) => Err(error), + } + } +} pub(crate) struct ReprDecoder<'a> { /// `buf` represents the byte stream to be decoded. @@ -43,23 +86,28 @@ impl<'a> ReprDecoder<'a> { self.state = Some(state); Ok(None) } - // If a `Representation` is FieldSectionPrefix, give the state to the holder. - DecResult::Decoded(Representation::FieldSectionPrefix { require_insert_count, signal, delta_base }) => { - println!("FieldSectionPrefix"); - self.state = Some(ReprDecodeState::Index(Index::new())); - Ok(Some(Representation::FieldSectionPrefix { - require_insert_count, - signal, - delta_base, - })) + DecResult::Decoded(repr) => { + self.state = Some(ReprDecodeState::ReprIndex(ReprIndex::new())); + Ok(Some(repr)) } - DecResult::Decoded(repr) => Ok(Some(repr)), DecResult::Error(error) => Err(error), } } } +pub(crate) struct InstDecStateHolder { + state: Option, +} +impl InstDecStateHolder { + pub(crate) fn new() -> Self { + Self { state: None } + } + + pub(crate) fn is_empty(&self) -> bool { + self.state.is_none() + } +} pub(crate) struct ReprDecStateHolder { state: Option, } @@ -103,18 +151,33 @@ macro_rules! state_def { )* } } - +state_def!( + InstDecodeState, + Instruction, + InstIndex, + InstValueString, + InstNameAndValue, +); state_def!( ReprDecodeState, Representation, FiledSectionPrefix, - Index, + ReprIndex, + ReprValueString, + ReprNameAndValue, ); - -state_def!(IndexInner, (ReprPrefixBit, ReprMidBit, usize), FirstByte, ReprTrailingBytes); +state_def!(InstIndexInner, (InstPrefixBit, MidBit, usize), InstFirstByte, InstTrailingBytes); +state_def!(ReprIndexInner, (ReprPrefixBit, MidBit, usize), ReprFirstByte, ReprTrailingBytes); state_def!(FSPInner, (RequireInsertCount, bool, DeltaBase), FSPTwoIntergers); - +state_def!( + LiteralString, + Vec, + LengthFirstByte, + LengthTrailingBytes, + AsciiStringBytes, + HuffmanStringBytes, +); pub(crate) struct FiledSectionPrefix { inner: FSPInner, @@ -133,7 +196,7 @@ impl FiledSectionPrefix { DecResult::Decoded((ric, signal, delta_base)) => { DecResult::Decoded(Representation::FieldSectionPrefix { require_insert_count: ric, - signal:signal, + signal: signal, delta_base: delta_base, }) } @@ -143,15 +206,47 @@ impl FiledSectionPrefix { } } -pub(crate) struct Index { - inner: IndexInner, +pub(crate) struct InstIndex{ + inner: InstIndexInner +} + +impl InstIndex{ + fn new() -> Self { + Self::from_inner(InstFirstByte.into()) + } + fn from_inner(inner: InstIndexInner) -> Self { + Self { inner } + } + fn decode(self, buf: &mut &[u8]) -> DecResult { + match self.inner.decode(buf) { + DecResult::Decoded((InstPrefixBit::SETCAP, _, index)) => { + DecResult::Decoded(Instruction::SetCap { capacity:index }) + } + DecResult::Decoded((InstPrefixBit::INSERTWITHINDEX, mid_bit, index)) => { + InstValueString::new(InstPrefixBit::INSERTWITHINDEX, mid_bit, Name::Index(index)).decode(buf) + } + DecResult::Decoded((InstPrefixBit::INSERTWITHLITERAL, mid_bit, namelen)) => { + InstNameAndValue::new(InstPrefixBit::INSERTWITHLITERAL, mid_bit, namelen).decode(buf) + } + DecResult::Decoded((InstPrefixBit::DUPLICATE, _, index)) => { + DecResult::Decoded(Instruction::Duplicate { index }) + } + DecResult::NeedMore(inner) => DecResult::NeedMore(InstIndex::from_inner(inner).into()), + DecResult::Error(e) => e.into(), + _ => DecResult::Error(H3Error::ConnectionError(ErrorCode::TEST)), + } + } +} + +pub(crate) struct ReprIndex { + inner: ReprIndexInner, } -impl Index { +impl ReprIndex { fn new() -> Self { - Self::from_inner(FirstByte.into()) + Self::from_inner(ReprFirstByte.into()) } - fn from_inner(inner: IndexInner) -> Self { + fn from_inner(inner: ReprIndexInner) -> Self { Self { inner } } fn decode(self, buf: &mut &[u8]) -> DecResult { @@ -159,9 +254,21 @@ impl Index { DecResult::Decoded((ReprPrefixBit::INDEXED, mid_bit, index)) => { DecResult::Decoded(Representation::Indexed { mid_bit, index }) } - DecResult::NeedMore(inner) => DecResult::NeedMore(Index::from_inner(inner).into()), + DecResult::Decoded((ReprPrefixBit::INDEXEDWITHPOSTINDEX, _, index)) => { + DecResult::Decoded(Representation::IndexedWithPostIndex { index }) + } + DecResult::Decoded((ReprPrefixBit::LITERALWITHINDEXING, mid_bit, index)) => { + ReprValueString::new(ReprPrefixBit::LITERALWITHINDEXING, mid_bit, Name::Index(index)).decode(buf) + } + DecResult::Decoded((ReprPrefixBit::LITERALWITHPOSTINDEXING, mid_bit, index)) => { + ReprValueString::new(ReprPrefixBit::LITERALWITHPOSTINDEXING, mid_bit, Name::Index(index)).decode(buf) + } + DecResult::Decoded((ReprPrefixBit::LITERALWITHLITERALNAME, mid_bit, namelen)) => { + ReprNameAndValue::new(ReprPrefixBit::LITERALWITHLITERALNAME, mid_bit, namelen).decode(buf) + } + DecResult::NeedMore(inner) => DecResult::NeedMore(ReprIndex::from_inner(inner).into()), DecResult::Error(e) => e.into(), - _=>DecResult::Error(H3Error::ConnectionError(ErrorCode::TEST)), + _ => DecResult::Error(H3Error::ConnectionError(ErrorCode::TEST)), } } } @@ -236,11 +343,34 @@ impl FSPTwoIntergers { DecResult::Decoded((RequireInsertCount(ric), signal, DeltaBase(delta_base))) } } +pub(crate) struct InstFirstByte; + +impl InstFirstByte { + fn decode(self, buf: &mut &[u8]) -> DecResult<(InstPrefixBit, MidBit, usize), InstIndexInner> { + // If `buf` has been completely decoded here, return the current state. + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + let byte = buf[0]; + let inst = InstPrefixBit::from_u8(byte); + let mid_bit = inst.prefix_midbit_value(byte); + let mask = inst.prefix_index_mask(); + + // Moves the pointer of `buf` backward. + *buf = &buf[1..]; + match IntegerDecoder::first_byte(byte, mask.0) { + // Return the ReprPrefixBit and index part value. + Ok(idx) => DecResult::Decoded((inst, mid_bit, idx)), + // Index part value is longer than index(i.e. use all 1 to represent), so it needs more bytes to decode. + Err(int) => InstTrailingBytes::new(inst, mid_bit, int).decode(buf), + } + } +} -pub(crate) struct FirstByte; +pub(crate) struct ReprFirstByte; -impl FirstByte { - fn decode(self, buf: &mut &[u8]) -> DecResult<(ReprPrefixBit, ReprMidBit, usize), IndexInner> { +impl ReprFirstByte { + fn decode(self, buf: &mut &[u8]) -> DecResult<(ReprPrefixBit, MidBit, usize), ReprIndexInner> { // If `buf` has been completely decoded here, return the current state. if buf.is_empty() { return DecResult::NeedMore(self.into()); @@ -260,18 +390,45 @@ impl FirstByte { } } } +pub(crate) struct InstTrailingBytes { + inst: InstPrefixBit, + mid_bit: MidBit, + index: IntegerDecoder, +} + +impl InstTrailingBytes { + fn new(inst: InstPrefixBit, mid_bit: MidBit, index: IntegerDecoder) -> Self { + Self { inst, mid_bit, index } + } + fn decode(mut self, buf: &mut &[u8]) -> DecResult<(InstPrefixBit, MidBit, usize), InstIndexInner> { + loop { + // If `buf` has been completely decoded here, return the current state. + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + let byte = buf[0]; + *buf = &buf[1..]; + // Updates trailing bytes until we get the index. + match self.index.next_byte(byte) { + Ok(None) => {} + Ok(Some(index)) => return DecResult::Decoded((self.inst, self.mid_bit, index)), + Err(e) => return e.into(), + } + } + } +} pub(crate) struct ReprTrailingBytes { repr: ReprPrefixBit, - mid_bit: ReprMidBit, + mid_bit: MidBit, index: IntegerDecoder, } impl ReprTrailingBytes { - fn new(repr: ReprPrefixBit, mid_bit: ReprMidBit, index: IntegerDecoder) -> Self { + fn new(repr: ReprPrefixBit, mid_bit: MidBit, index: IntegerDecoder) -> Self { Self { repr, mid_bit, index } } - fn decode(mut self, buf: &mut &[u8]) -> DecResult<(ReprPrefixBit, ReprMidBit, usize), IndexInner> { + fn decode(mut self, buf: &mut &[u8]) -> DecResult<(ReprPrefixBit, MidBit, usize), ReprIndexInner> { loop { // If `buf` has been completely decoded here, return the current state. if buf.is_empty() { @@ -290,6 +447,256 @@ impl ReprTrailingBytes { } } +pub(crate) struct LengthTrailingBytes { + is_huffman: bool, + length: IntegerDecoder, +} + +impl LengthTrailingBytes { + fn new(is_huffman: bool, length: IntegerDecoder) -> Self { + Self { is_huffman, length } + } + + fn decode(mut self, buf: &mut &[u8]) -> DecResult, LiteralString> { + loop { + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + + let byte = buf[0]; + *buf = &buf[1..]; + match (self.length.next_byte(byte), self.is_huffman) { + (Ok(None), _) => {} + (Err(e), _) => return e.into(), + (Ok(Some(length)), true) => return HuffmanStringBytes::new(length).decode(buf), + (Ok(Some(length)), false) => return AsciiStringBytes::new(length).decode(buf), + } + } + } +} + +pub(crate) struct AsciiStringBytes { + octets: Vec, + length: usize, +} + +impl AsciiStringBytes { + fn new(length: usize) -> Self { + Self { + octets: Vec::new(), + length, + } + } + + fn decode(mut self, buf: &mut &[u8]) -> DecResult, LiteralString> { + match (buf.len() + self.octets.len()).cmp(&self.length) { + Ordering::Greater | Ordering::Equal => { + let pos = self.length - self.octets.len(); + self.octets.extend_from_slice(&buf[..pos]); + *buf = &buf[pos..]; + DecResult::Decoded(self.octets) + } + Ordering::Less => { + self.octets.extend_from_slice(buf); + *buf = &buf[buf.len()..]; + DecResult::NeedMore(self.into()) + } + } + } +} +pub(crate) struct InstNameAndValue { + inst: InstPrefixBit, + mid_bit: MidBit, + inner: LiteralString, +} + +impl InstNameAndValue { + fn new(inst: InstPrefixBit, mid_bit: MidBit, namelen: usize) -> Self { + Self::from_inner(inst, mid_bit, AsciiStringBytes::new(namelen).into()) + } + fn from_inner(inst: InstPrefixBit, mid_bit: MidBit, inner: LiteralString) -> Self { + Self { inst, mid_bit, inner } + } + fn decode(self, buf: &mut &[u8]) -> DecResult { + match self.inner.decode(buf) { + DecResult::Decoded(octets) => { + InstValueString::new(self.inst, self.mid_bit, Name::Literal(octets)).decode(buf) + } + DecResult::NeedMore(inner) => { + DecResult::NeedMore(Self::from_inner(self.inst, self.mid_bit, inner).into()) + } + DecResult::Error(e) => e.into(), + } + } +} +pub(crate) struct ReprNameAndValue { + repr: ReprPrefixBit, + mid_bit: MidBit, + inner: LiteralString, +} + +impl ReprNameAndValue { + fn new(repr: ReprPrefixBit, mid_bit: MidBit, namelen: usize) -> Self { + Self::from_inner(repr, mid_bit, AsciiStringBytes::new(namelen).into()) + } + fn from_inner(repr: ReprPrefixBit, mid_bit: MidBit, inner: LiteralString) -> Self { + Self { repr, mid_bit, inner } + } + fn decode(self, buf: &mut &[u8]) -> DecResult { + match self.inner.decode(buf) { + DecResult::Decoded(octets) => { + ReprValueString::new(self.repr, self.mid_bit, Name::Literal(octets)).decode(buf) + } + DecResult::NeedMore(inner) => { + DecResult::NeedMore(Self::from_inner(self.repr, self.mid_bit, inner).into()) + } + DecResult::Error(e) => e.into(), + } + } +} + +pub(crate) struct LengthFirstByte; + +impl LengthFirstByte { + fn decode(self, buf: &mut &[u8]) -> DecResult, LiteralString> { + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + + let byte = buf[0]; + *buf = &buf[1..]; + match (IntegerDecoder::first_byte(byte, 0x7f), (byte & 0x80) == 0x80) { + (Ok(len), true) => HuffmanStringBytes::new(len).decode(buf), + (Ok(len), false) => AsciiStringBytes::new(len).decode(buf), + (Err(int), huffman) => LengthTrailingBytes::new(huffman, int).decode(buf), + } + } +} + +pub(crate) struct HuffmanStringBytes { + huffman: HuffmanDecoder, + read: usize, + length: usize, +} + +impl HuffmanStringBytes { + fn new(length: usize) -> Self { + Self { + huffman: HuffmanDecoder::new(), + read: 0, + length, + } + } + + fn decode(mut self, buf: &mut &[u8]) -> DecResult, LiteralString> { + match (buf.len() + self.read).cmp(&self.length) { + Ordering::Greater | Ordering::Equal => { + let pos = self.length - self.read; + if self.huffman.decode(&buf[..pos]).is_err() { + return H3Error::ConnectionError(TEST).into(); + } + *buf = &buf[pos..]; + match self.huffman.finish() { + Ok(vec) => DecResult::Decoded(vec), + Err(_) => H3Error::ConnectionError(TEST).into(), + } + } + Ordering::Less => { + if self.huffman.decode(buf).is_err() { + return H3Error::ConnectionError(TEST).into(); + } + self.read += buf.len(); + *buf = &buf[buf.len()..]; + DecResult::NeedMore(self.into()) + } + } + } +} +#[derive(Clone)] +pub(crate) enum Name { + Index(usize), + Literal(Vec), +} +pub(crate) struct InstValueString { + inst: InstPrefixBit, + mid_bit: MidBit, + name: Name, + inner: LiteralString, +} + +impl InstValueString { + fn new(inst: InstPrefixBit, mid_bit: MidBit, name: Name) -> Self { + Self::from_inner(inst, mid_bit, name, LengthFirstByte.into()) + } + + fn from_inner(inst: InstPrefixBit, mid_bit: MidBit, name: Name, inner: LiteralString) -> Self { + Self { inst, mid_bit, name, inner } + } + + fn decode(self, buf: &mut &[u8]) -> DecResult { + match (self.inst, self.inner.decode(buf)) { + (InstPrefixBit::INSERTWITHINDEX, DecResult::Decoded(value)) => { + DecResult::Decoded(Instruction::InsertWithIndex { + mid_bit: self.mid_bit, + name: self.name, + value, + }) + } + (InstPrefixBit::INSERTWITHLITERAL, DecResult::Decoded(value)) => { + DecResult::Decoded(Instruction::InsertWithLiteral { + mid_bit: self.mid_bit, + name: self.name, + value, + }) + } + (_, _) => Error(H3Error::ConnectionError(TEST)) + } + } +} +pub(crate) struct ReprValueString { + repr: ReprPrefixBit, + mid_bit: MidBit, + name: Name, + inner: LiteralString, +} + +impl ReprValueString { + fn new(repr: ReprPrefixBit, mid_bit: MidBit, name: Name) -> Self { + Self::from_inner(repr, mid_bit, name, LengthFirstByte.into()) + } + + fn from_inner(repr: ReprPrefixBit, mid_bit: MidBit, name: Name, inner: LiteralString) -> Self { + Self { repr, mid_bit, name, inner } + } + + fn decode(self, buf: &mut &[u8]) -> DecResult { + match (self.repr, self.inner.decode(buf)) { + (ReprPrefixBit::LITERALWITHINDEXING, DecResult::Decoded(value)) => { + DecResult::Decoded(Representation::LiteralWithIndexing { + mid_bit: self.mid_bit, + name: self.name, + value, + }) + } + (ReprPrefixBit::LITERALWITHPOSTINDEXING, DecResult::Decoded(value)) => { + DecResult::Decoded(Representation::LiteralWithPostIndexing { + mid_bit: self.mid_bit, + name: self.name, + value, + }) + } + (ReprPrefixBit::LITERALWITHLITERALNAME, DecResult::Decoded(value)) => { + DecResult::Decoded(Representation::LiteralWithLiteralName { + mid_bit: self.mid_bit, + name: self.name, + value, + }) + } + (_, _) => Error(H3Error::ConnectionError(TEST)) + } + } +} + /// Decoder's possible returns during the decoding process. enum DecResult { diff --git a/ylong_http/src/h3/qpack/representation/encoder.rs b/ylong_http/src/h3/qpack/format/encoder.rs similarity index 99% rename from ylong_http/src/h3/qpack/representation/encoder.rs rename to ylong_http/src/h3/qpack/format/encoder.rs index 28dae04..045ba67 100644 --- a/ylong_http/src/h3/qpack/representation/encoder.rs +++ b/ylong_http/src/h3/qpack/format/encoder.rs @@ -282,13 +282,13 @@ impl InsertWithName { if is_static { Self { inner: IndexAndValue::new() - .set_index(0xc0, index, PrefixMask::INSERTWITHNAME.0) + .set_index(0xc0, index, PrefixMask::INSERTWITHINDEX.0) .set_value(value, is_huffman), } } else { Self { inner: IndexAndValue::new() - .set_index(0x80, index, PrefixMask::INSERTWITHNAME.0) + .set_index(0x80, index, PrefixMask::INSERTWITHINDEX.0) .set_value(value, is_huffman), } } diff --git a/ylong_http/src/h3/qpack/representation/mod.rs b/ylong_http/src/h3/qpack/format/mod.rs similarity index 100% rename from ylong_http/src/h3/qpack/representation/mod.rs rename to ylong_http/src/h3/qpack/format/mod.rs diff --git a/ylong_http/src/h3/qpack/mod.rs b/ylong_http/src/h3/qpack/mod.rs index 3260e77..7f9741f 100644 --- a/ylong_http/src/h3/qpack/mod.rs +++ b/ylong_http/src/h3/qpack/mod.rs @@ -1,48 +1,87 @@ pub(crate) mod table; mod encoder; -mod representation; +mod format; mod integer; mod decoder; pub(crate) use decoder::QpackDecoder; +use crate::h3::qpack::format::decoder::Name; pub(crate) struct RequireInsertCount(usize); pub(crate) struct DeltaBase(usize); + +#[derive(Copy, Clone, PartialEq, Eq)] +pub(crate) struct InstPrefixBit(u8); + #[derive(Copy, Clone, PartialEq, Eq)] pub(crate) struct ReprPrefixBit(u8); /// # Prefix bit: /// ## Encoder Instructions: /// SETCAP: 0x20 -/// INSERTWITHNAME: 0x80 +/// INSERTWITHINDEX: 0x80 /// INSERTWITHLITERAL: 0x40 +/// DUPLICATE: 0x00 /// /// ## Representation: /// INDEXED: 0x80 -/// INDEXEDWITHPOSTNAME: 0x10 -/// INDEXINGWITHNAME: 0x40 -/// INDEXINGWITHPOSTNAME: 0x00 -/// INDEXINGWITHLITERAL: 0x20 +/// INDEXEDWITHPOSTINDEX: 0x10 +/// LITERALWITHINDEXING: 0x40 +/// LITERALWITHPOSTINDEXING: 0x00 +/// LITERALWITHLITERALNAME: 0x20 +impl InstPrefixBit { + pub(crate) const SETCAP: Self = Self(0x20); + pub(crate) const INSERTWITHINDEX: Self = Self(0x80); + pub(crate) const INSERTWITHLITERAL: Self = Self(0x40); + pub(crate) const DUPLICATE: Self = Self(0x00); + + pub(crate) fn from_u8(byte: u8) -> Self { + match byte { + x if x >= 0x80 => Self::INSERTWITHINDEX, + x if x >= 0x40 => Self::INSERTWITHLITERAL, + x if x >= 0x20 => Self::SETCAP, + _ => Self::DUPLICATE, + } + } + + pub(crate) fn prefix_index_mask(&self) -> PrefixMask { + match self.0 { + 0x80 => PrefixMask::INSERTWITHINDEX, + 0x40 => PrefixMask::INSERTWITHLITERAL, + 0x20 => PrefixMask::SETCAP, + _ => PrefixMask::DUPLICATE, + } + } + + pub(crate) fn prefix_midbit_value(&self, byte: u8) -> MidBit { + match self.0 { + 0x80 => MidBit { n: None, t: Some((byte & 0x40) != 0), h: None }, + 0x40 => MidBit { n: None, t: None, h: Some((byte & 0x20) != 0) }, + 0x20 => MidBit { n: None, t: None, h: None }, + _ => MidBit { n: None, t: None, h: None }, + } + } +} impl ReprPrefixBit { pub(crate) const INDEXED: Self = Self(0x80); - pub(crate) const INDEXEDWITHPOSTNAME: Self = Self(0x10); - pub(crate) const INDEXINGWITHNAME: Self = Self(0x40); - pub(crate) const INDEXINGWITHPOSTNAME: Self = Self(0x00); - pub(crate) const INDEXINGWITHLITERAL: Self = Self(0x20); + pub(crate) const INDEXEDWITHPOSTINDEX: Self = Self(0x10); + pub(crate) const LITERALWITHINDEXING: Self = Self(0x40); + pub(crate) const LITERALWITHPOSTINDEXING: Self = Self(0x00); + pub(crate) const LITERALWITHLITERALNAME: Self = Self(0x20); /// Creates a `PrefixBit` from a byte. The interface will convert the /// incoming byte to the most suitable prefix bit. pub(crate) fn from_u8(byte: u8) -> Self { match byte { x if x >= 0x80 => Self::INDEXED, - x if x >= 0x40 => Self::INDEXINGWITHNAME, - x if x >= 0x20 => Self::INDEXINGWITHLITERAL, - x if x >= 0x10 => Self::INDEXEDWITHPOSTNAME, - _ => Self::INDEXINGWITHPOSTNAME, + x if x >= 0x40 => Self::LITERALWITHINDEXING, + x if x >= 0x20 => Self::LITERALWITHLITERALNAME, + x if x >= 0x10 => Self::INDEXEDWITHPOSTINDEX, + _ => Self::LITERALWITHPOSTINDEXING, } } @@ -60,25 +99,32 @@ impl ReprPrefixBit { /// Unlike Hpack, QPACK has some special value for the first byte of an integer. /// Like T indicating whether the reference is into the static or dynamic table. - pub(crate) fn prefix_midbit_value(&self, byte: u8) -> ReprMidBit { + pub(crate) fn prefix_midbit_value(&self, byte: u8) -> MidBit { match self.0 { - 0x80 => ReprMidBit { n: None, t: Some((byte & 0x40) != 0), h: None }, - 0x40 => ReprMidBit { n: Some((byte & 0x20) != 0), t: Some((byte & 0x10) != 0), h: None }, - 0x20 => ReprMidBit { n: Some((byte & 0x10) != 0), t: None, h: Some((byte & 0x08) != 0) }, - 0x10 => ReprMidBit { n: None, t: None, h: None }, - _ => ReprMidBit { n: Some((byte & 0x08) != 0), t: None, h: None }, + 0x80 => MidBit { n: None, t: Some((byte & 0x40) != 0), h: None }, + 0x40 => MidBit { n: Some((byte & 0x20) != 0), t: Some((byte & 0x10) != 0), h: None }, + 0x20 => MidBit { n: Some((byte & 0x10) != 0), t: None, h: Some((byte & 0x08) != 0) }, + 0x10 => MidBit { n: None, t: None, h: None }, + _ => MidBit { n: Some((byte & 0x08) != 0), t: None, h: None }, } } } +pub(crate) enum Instruction { + SetCap { capacity: usize }, + InsertWithIndex { mid_bit: MidBit, name: Name, value: Vec }, + InsertWithLiteral { mid_bit: MidBit, name: Name, value: Vec }, + Duplicate { index: usize }, +} + pub(crate) enum Representation { - /// An indexed field line representation identifies an entry in the static table or an entry in + /// An indexed field line format identifies an entry in the static table or an entry in /// the dynamic table with an absolute index less than the value of the Base. /// 0 1 2 3 4 5 6 7 /// +---+---+---+---+---+---+---+---+ /// | 1 | T | Index (6+) | /// +---+---+-----------------------+ - /// This representation starts with the '1' 1-bit pattern, followed by the 'T' bit, indicating + /// This format starts with the '1' 1-bit pattern, followed by the 'T' bit, indicating /// whether the reference is into the static or dynamic table. The 6-bit prefix integer /// (Section 4.1.1) that follows is used to locate the table entry for the field line. When T=1, /// the number represents the static table index; when T=0, the number is the relative index of @@ -86,16 +132,19 @@ pub(crate) enum Representation { FieldSectionPrefix { require_insert_count: RequireInsertCount, signal: bool, delta_base: DeltaBase }, - Indexed { mid_bit: ReprMidBit, index: usize }, + Indexed { mid_bit: MidBit, index: usize }, + IndexedWithPostIndex { index: usize }, + LiteralWithIndexing { mid_bit: MidBit, name: Name, value: Vec }, + LiteralWithPostIndexing { mid_bit: MidBit, name: Name, value: Vec }, + LiteralWithLiteralName { mid_bit: MidBit, name: Name, value: Vec }, - //todo: add more } //impl debug for Representation -pub(crate) struct ReprMidBit { +pub(crate) struct MidBit { //'N', indicates whether an intermediary is permitted to add this field line to the dynamic // table on subsequent hops. n: Option, @@ -114,8 +163,10 @@ impl PrefixMask { pub(crate) const DELTABASE: Self = Self(0x7f); pub(crate) const INDEXED: Self = Self(0x3f); pub(crate) const SETCAP: Self = Self(0x1f); - pub(crate) const INSERTWITHNAME: Self = Self(0x3f); + pub(crate) const INSERTWITHINDEX: Self = Self(0x3f); pub(crate) const INSERTWITHLITERAL: Self = Self(0x1f); + pub(crate) const DUPLICATE: Self = Self(0x1f); + pub(crate) const INDEXINGWITHNAME: Self = Self(0x0f); pub(crate) const INDEXINGWITHPOSTNAME: Self = Self(0x07); pub(crate) const INDEXINGWITHLITERAL: Self = Self(0x07); diff --git a/ylong_http/src/h3/qpack/table.rs b/ylong_http/src/h3/qpack/table.rs index 4fb0266..5143602 100644 --- a/ylong_http/src/h3/qpack/table.rs +++ b/ylong_http/src/h3/qpack/table.rs @@ -62,10 +62,18 @@ impl<'a> TableSearcher<'a> { } } + pub(crate) fn find_field_name_static(&self, index: usize) -> Option { + StaticTable::field_name(index) + } + pub(crate) fn find_field_dynamic(&self, index: usize) -> Option<(Field, String)> { self.dynamic.field(index) } + pub(crate) fn find_field_name_dynamic(&self, index: usize) -> Option { + self.dynamic.field_name(index) + } + pub(crate) fn can_index(&self, header: &Field, value: &str) -> bool { //todo true @@ -78,6 +86,8 @@ pub(crate) struct DynamicTable { // The size of the dynamic table is the sum of the size of its entries size: usize, capacity: usize, + pub(crate) insert_count: usize, + remove_count: usize, } impl DynamicTable { @@ -86,6 +96,8 @@ impl DynamicTable { queue: VecDeque::new(), size: 0, capacity, + insert_count: 0, + remove_count: 0, } } @@ -99,14 +111,8 @@ impl DynamicTable { /// Updates `DynamicTable` by a given `Header` and value pair. pub(crate) fn update(&mut self, field: Field, value: String) { - // RFC7541-4.1: The additional 32 octets account for an estimated - // overhead associated with an entry. For example, an entry - // structure using two 64-bit pointers to reference the name and the - // value of the entry and two 64-bit integers for counting the - // number of references to the name and value would have 32 octets - // of overhead. self.size += field.len() + value.len() + 32; - self.queue.push_front((field, value)); + self.queue.push_back((field, value)); self.fit_size(); } @@ -119,7 +125,8 @@ impl DynamicTable { /// Adjusts dynamic table content to fit its size. fn fit_size(&mut self) { while self.size > self.capacity && !self.queue.is_empty() { - let (key, string) = self.queue.pop_back().unwrap(); + let (key, string) = self.queue.pop_front().unwrap(); + self.remove_count += 1; self.capacity -= key.len() + string.len() + 32; } } @@ -129,8 +136,8 @@ impl DynamicTable { let mut index = None; for (n, (h, v)) in self.queue.iter().enumerate() { match (header == h, value == v, &index) { - (true, true, _) => return Some(TableIndex::Field(n)), - (true, false, None) => index = Some(TableIndex::FieldName(n)), + (true, true, _) => return Some(TableIndex::Field(n + self.remove_count)), + (true, false, None) => index = Some(TableIndex::FieldName(n + self.remove_count)), _ => {} } } @@ -138,7 +145,11 @@ impl DynamicTable { } pub(crate) fn field(&self, index: usize) -> Option<(Field, String)> { - self.queue.get(index).cloned() + self.queue.get(index-self.remove_count).cloned() + } + + pub(crate) fn field_name(&self, index: usize) -> Option { + self.queue.get(index-self.remove_count).map(|(field, _)| field.clone()) } } @@ -164,7 +175,7 @@ pub(crate) enum TableIndex { /// 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 +/// index in a field line format, 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. diff --git a/ylong_http/src/headers.rs b/ylong_http/src/headers.rs index 7b7f7ab..5348594 100644 --- a/ylong_http/src/headers.rs +++ b/ylong_http/src/headers.rs @@ -201,7 +201,7 @@ impl HeaderName { }) } - /// Returns a bytes representation of the `HeaderName`. + /// Returns a bytes format of the `HeaderName`. /// /// # Examples /// diff --git a/ylong_http/src/request/method.rs b/ylong_http/src/request/method.rs index db2c9bb..9fb7369 100644 --- a/ylong_http/src/request/method.rs +++ b/ylong_http/src/request/method.rs @@ -43,7 +43,7 @@ use core::convert::TryFrom; pub struct Method(Inner); impl Method { - /// Transfer a current representation of the target resource. + /// Transfer a current format of the target resource. /// /// See [`RFC9110 9.3.1`] for more details. /// -- Gitee From 6524924c883c930083828ac9bfc0877e665b5a1a Mon Sep 17 00:00:00 2001 From: Weng Shihao Date: Sun, 20 Aug 2023 23:08:47 +0800 Subject: [PATCH 5/6] qpack --- ylong_http/src/h3/error.rs | 8 +- ylong_http/src/h3/qpack/decoder.rs | 123 +++++-- ylong_http/src/h3/qpack/encoder.rs | 137 +++++--- ylong_http/src/h3/qpack/format/decoder.rs | 92 ++--- ylong_http/src/h3/qpack/format/encoder.rs | 392 ++++++++++++++-------- ylong_http/src/h3/qpack/format/mod.rs | 2 +- ylong_http/src/h3/qpack/integer.rs | 2 +- ylong_http/src/h3/qpack/mod.rs | 53 ++- ylong_http/src/h3/qpack/table.rs | 38 ++- 9 files changed, 584 insertions(+), 263 deletions(-) diff --git a/ylong_http/src/h3/error.rs b/ylong_http/src/h3/error.rs index eb45f2c..11de8bd 100644 --- a/ylong_http/src/h3/error.rs +++ b/ylong_http/src/h3/error.rs @@ -2,8 +2,12 @@ pub enum H3Error { //todo: add more ConnectionError(ErrorCode), } + #[derive(Debug, Eq, PartialEq, Clone)] pub enum ErrorCode { - //todo: add more - TEST = 0xff, // todo: remove + QPACK_DECOMPRESSION_FAILED = 0x0200, + + QPACK_ENCODER_STREAM_ERROR = 0x0201, + + QPACK_DECODER_STREAM_ERROR = 0x0202, } \ No newline at end of file diff --git a/ylong_http/src/h3/qpack/decoder.rs b/ylong_http/src/h3/qpack/decoder.rs index 07c095a..0213154 100644 --- a/ylong_http/src/h3/qpack/decoder.rs +++ b/ylong_http/src/h3/qpack/decoder.rs @@ -1,10 +1,11 @@ use std::mem::take; -use crate::h3::error::ErrorCode::TEST; +use crate::h3::error::ErrorCode::{QPACK_DECOMPRESSION_FAILED, QPACK_ENCODER_STREAM_ERROR}; use crate::h3::error::H3Error; use crate::h3::parts::Parts; -use crate::h3::qpack::{Representation, MidBit, ReprPrefixBit, Instruction, InstPrefixBit}; +use crate::h3::qpack::{Representation, MidBit, ReprPrefixBit, EncoderInstruction, EncoderInstPrefixBit}; -use crate::h3::qpack::format::decoder::{InstDecoder, InstDecStateHolder, Name, ReprDecoder, ReprDecStateHolder}; +use crate::h3::qpack::format::decoder::{EncInstDecoder, InstDecStateHolder, Name, ReprDecoder, ReprDecStateHolder}; +use crate::h3::qpack::integer::Integer; use crate::h3::qpack::table::{DynamicTable, Field, TableSearcher}; @@ -20,6 +21,7 @@ pub(crate) struct QpackDecoder<'a> { inst_holder: InstDecStateHolder, lines: FiledLines, base: usize, + require_insert_count: usize, } impl<'a> QpackDecoder<'a> { @@ -34,32 +36,33 @@ impl<'a> QpackDecoder<'a> { header_size: 0, }, base: 0, + require_insert_count: 0, } } pub(crate) fn decode_ins(&mut self, buf: &[u8]) -> Result<(), H3Error> { - let mut decoder = InstDecoder::new(buf); + let mut decoder = EncInstDecoder::new(buf); decoder.load(&mut self.inst_holder); let mut updater = Updater::new(&mut self.table); loop { match decoder.decode()? { Some(inst) => { match inst { - Instruction::SetCap { capacity } => { + EncoderInstruction::SetCap { capacity } => { println!("capacity: {}", capacity); updater.update_capacity(capacity)?; } - Instruction::InsertWithIndex { mid_bit, name, value } => { + EncoderInstruction::InsertWithIndex { mid_bit, name, value } => { let value_str = String::from_utf8(value.clone()).unwrap(); println!("value: {}", value_str); updater.update_table(mid_bit, name, value)?; } - Instruction::InsertWithLiteral { mid_bit, name, value } => { + EncoderInstruction::InsertWithLiteral { mid_bit, name, value } => { let value_str = String::from_utf8(value.clone()).unwrap(); println!("value: {}", value_str); updater.update_table(mid_bit, name, value)?; } - Instruction::Duplicate { index } => { + EncoderInstruction::Duplicate { index } => { updater.duplicate(index)?; } } @@ -69,6 +72,16 @@ impl<'a> QpackDecoder<'a> { } } + + /// User call `decoder_repr` once for decoding a complete field section, which start with the `field section prefix`: + /// 0 1 2 3 4 5 6 7 + /// +---+---+---+---+---+---+---+---+ + /// | Required Insert Count (8+) | + /// +---+---------------------------+ + /// | S | Delta Base (7+) | + /// +---+---------------------------+ + /// | Encoded Field Lines ... + /// +-------------------------------+ pub(crate) fn decode_repr(&mut self, buf: &[u8]) -> Result<(), H3Error> { let mut decoder = ReprDecoder::new(buf); decoder.load(&mut self.repr_holder); @@ -79,6 +92,21 @@ impl<'a> QpackDecoder<'a> { Some(repr) => { match repr { Representation::FieldSectionPrefix { require_insert_count, signal, delta_base } => { + if require_insert_count.0 == 0{ + self.require_insert_count = 0; + } + else { + let max_entries = self.table.max_entries(); + let full_range = 2 * max_entries; + let max_value = self.table.insert_count + max_entries; + let max_wrapped = (max_value / full_range) * full_range; + self.require_insert_count = max_wrapped + require_insert_count.0 - 1; + if self.require_insert_count > max_value{ + self.require_insert_count -= full_range; + } + + } + self.require_insert_count = require_insert_count.0; println!("require_insert_count: {}", require_insert_count.0); println!("signal: {}", signal); println!("delta_base: {}", delta_base.0); @@ -129,12 +157,46 @@ impl<'a> QpackDecoder<'a> { } }; } - pub(crate) fn finish(&mut self) -> Result { + + /// Users call `finish` to stop decoding a field section. And send an `Section Acknowledgment` to encoder: + /// After processing an encoded field section whose declared Required Insert Count is not zero, + /// the decoder emits a Section Acknowledgment instruction. The instruction starts with the + /// '1' 1-bit pattern, followed by the field section's associated stream ID encoded as + /// a 7-bit prefix integer + /// 0 1 2 3 4 5 6 7 + /// +---+---+---+---+---+---+---+---+ + /// | 1 | Stream ID (7+) | + /// +---+---------------------------+ + pub(crate) fn finish(&mut self, stream_id: usize, buf: &mut [u8]) -> Result<(Parts, Option), H3Error> { if !self.repr_holder.is_empty() { - return Err(H3Error::ConnectionError(TEST)); + return Err(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED)); } self.lines.header_size = 0; - Ok(take(&mut self.lines.parts)) + if self.require_insert_count > 0 { + let mut ack = Integer::index(0x80, stream_id, 0x7f); + let size = ack.encode(buf); + if let Ok(size) = size { + return Ok((take(&mut self.lines.parts), Some(size))); + } + } + Ok((take(&mut self.lines.parts), None)) + } + + /// Users call `stream_cancel` to stop cancel a stream. And send an `Stream Cancellation` to encoder: + /// When a stream is reset or reading is abandoned, the decoder emits a Stream Cancellation + /// instruction. The instruction starts with the '01' 2-bit pattern, + /// followed by the stream ID of the affected stream encoded as a 6-bit prefix integer. + /// 0 1 2 3 4 5 6 7 + /// +---+---+---+---+---+---+---+---+ + /// | 0 | 1 | Stream ID (6+) | + /// +---+---+-----------------------+ + pub(crate) fn stream_cancel(&mut self, stream_id: usize, buf: &mut [u8]) -> Result { + let mut ack = Integer::index(0x40, stream_id, 0x3f); + let size = ack.encode(buf); + if let Ok(size) = size { + return Ok(size); + } + Err(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED)) } } @@ -155,7 +217,7 @@ impl<'a> Updater<'a> { } fn update_table(&mut self, mid_bit: MidBit, name: Name, value: Vec) -> Result<(), H3Error> { - let (f, v) = self.get_field_by_name_and_value(mid_bit, name, value, self.table.insert_count)?; + let (f, v) = self.get_field_by_name_and_value(mid_bit, name, value, self.table.insert_count)?; self.table.update(f, v); Ok(()) } @@ -164,7 +226,7 @@ impl<'a> Updater<'a> { let table_searcher = TableSearcher::new(self.table); let (f, v) = table_searcher .find_field_dynamic(self.table.insert_count - index) - .ok_or(H3Error::ConnectionError(TEST))?; + .ok_or(H3Error::ConnectionError(QPACK_ENCODER_STREAM_ERROR))?; self.table.update(f, v); Ok(()) } @@ -182,20 +244,20 @@ impl<'a> Updater<'a> { if let Some(true) = mid_bit.t { searcher .find_field_name_static(index) - .ok_or(H3Error::ConnectionError(TEST))? + .ok_or(H3Error::ConnectionError(QPACK_ENCODER_STREAM_ERROR))? } else { searcher .find_field_name_dynamic(insert_count - index) - .ok_or(H3Error::ConnectionError(TEST))? + .ok_or(H3Error::ConnectionError(QPACK_ENCODER_STREAM_ERROR))? } } Name::Literal(octets) => Field::Other( String::from_utf8(octets) - .map_err(|_| H3Error::ConnectionError(TEST))?, + .map_err(|_| H3Error::ConnectionError(QPACK_ENCODER_STREAM_ERROR))?, ), }; let v = String::from_utf8(value) - .map_err(|_| H3Error::ConnectionError(TEST))?; + .map_err(|_| H3Error::ConnectionError(QPACK_ENCODER_STREAM_ERROR))?; Ok((h, v)) } } @@ -237,13 +299,13 @@ impl<'a> Searcher<'a> { if let Some(true) = mid_bit.t { let (f, v) = table_searcher .find_field_static(index) - .ok_or(H3Error::ConnectionError(TEST))?; + .ok_or(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED))?; self.lines.parts.update(f, v); return Ok(()); } else { let (f, v) = table_searcher .find_field_dynamic(self.base - index - 1) - .ok_or(H3Error::ConnectionError(TEST))?; + .ok_or(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED))?; self.lines.parts.update(f, v); return Ok(()); } @@ -253,7 +315,7 @@ impl<'a> Searcher<'a> { let table_searcher = TableSearcher::new(self.table); let (f, v) = table_searcher .find_field_dynamic(self.base + index) - .ok_or(H3Error::ConnectionError(TEST))?; + .ok_or(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED))?; self.check_field_list_size(&f, &v)?; self.lines.parts.update(f, v); return Ok(()); @@ -295,26 +357,26 @@ impl<'a> Searcher<'a> { if let Some(true) = mid_bit.t { searcher .find_field_name_static(index) - .ok_or(H3Error::ConnectionError(TEST))? + .ok_or(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED))? } else { searcher .find_field_name_dynamic(self.base - index - 1) - .ok_or(H3Error::ConnectionError(TEST))? + .ok_or(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED))? } } else { let searcher = TableSearcher::new(self.table); searcher .find_field_name_dynamic(self.base + index) - .ok_or(H3Error::ConnectionError(TEST))? + .ok_or(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED))? } } Name::Literal(octets) => Field::Other( String::from_utf8(octets) - .map_err(|_| H3Error::ConnectionError(TEST))?, + .map_err(|_| H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED))?, ), }; let v = String::from_utf8(value) - .map_err(|_| H3Error::ConnectionError(TEST))?; + .map_err(|_| H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED))?; Ok((h, v)) } pub(crate) fn update_size(&mut self, addition: usize) { @@ -324,7 +386,7 @@ impl<'a> Searcher<'a> { let line_size = field_line_length(key.len(), value.len()); self.update_size(line_size); if self.lines.header_size > self.field_list_size { - Err(H3Error::ConnectionError(TEST)) + Err(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED)) } else { Ok(()) } @@ -374,8 +436,9 @@ mod ut_qpack_decoder { println!("text: {:#?}", text); assert!($qpack.decode_repr(text.as_slice()).is_ok()); )* - match $qpack.finish() { - Ok(parts) => parts, + let mut ack = [0u8; 20]; + match $qpack.finish(1,&mut ack) { + Ok((parts,_)) => parts, Err(_) => panic!("QpackDecoder::finish() failed!"), } }}; @@ -532,15 +595,13 @@ mod ut_qpack_decoder { fn test_setcap() { let mut dynamic_table = DynamicTable::with_capacity(4096); - let mut decoder=QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); let text = decode("3f7f").unwrap(); println!("text: {:#?}", text); decoder.decode_ins(text.as_slice()); print!("capacity: {}", dynamic_table.capacity()); assert_eq!(dynamic_table.capacity(), 158); } - - } } diff --git a/ylong_http/src/h3/qpack/encoder.rs b/ylong_http/src/h3/qpack/encoder.rs index 04e11bd..195cb57 100644 --- a/ylong_http/src/h3/qpack/encoder.rs +++ b/ylong_http/src/h3/qpack/encoder.rs @@ -1,56 +1,104 @@ +use std::collections::{HashMap, VecDeque}; +use crate::h3::error::ErrorCode::QPACK_DECODER_STREAM_ERROR; +use crate::h3::error::H3Error; use crate::h3::parts::Parts; use crate::h3::qpack::table::DynamicTable; use crate::h3::qpack::format::{ReprEncoder, ReprEncStateHolder}; -use crate::h3::qpack::PrefixMask; +use crate::h3::qpack::format::encoder::{DecInstDecoder, InstDecodeState, InstDecStateHolder}; +use crate::h3::qpack::{DecoderInstruction, PrefixMask}; use crate::h3::qpack::integer::{Integer, IntegerEncoder}; -pub(crate) struct QpackEncoder { +pub(crate) struct QpackEncoder { table: DynamicTable, holder: ReprEncStateHolder, - encoder_buf: [u8; N], - prefix_buf: [u8; M], - stream_buf: [u8; P], - cur_encoder_buf: usize, - cur_prefix_buf: usize, - cur_stream_buf: usize, + inst_holder: InstDecStateHolder, + stream_reference: VecDeque>, + stream_id: usize, } -impl QpackEncoder { - pub(crate) fn with_capacity(max_size: usize) -> QpackEncoder { +impl QpackEncoder { + pub(crate) fn with_capacity(max_size: usize, encoder_buf: &mut [u8], stream_id: usize) -> (QpackEncoder, usize) { let mut s = Self { table: DynamicTable::with_capacity(max_size), holder: ReprEncStateHolder::new(), - encoder_buf: [0u8; N], - prefix_buf: [0u8; M], - stream_buf: [0u8; P], - cur_encoder_buf: 0, - cur_prefix_buf: 0, - cur_stream_buf: 0, + inst_holder: InstDecStateHolder::new(), + stream_reference: VecDeque::new(), + stream_id, }; - let cur = InstEncode::SetCap(SetCap::new(max_size)).encode(&mut s.encoder_buf[s.cur_encoder_buf..]); - s.cur_encoder_buf += cur; - s + let cur = EncoderInst::SetCap(SetCap::new(max_size)).encode(&mut encoder_buf[..]); + (s, cur) } + pub(crate) fn set_parts(&mut self, parts: Parts) { self.holder.set_parts(parts) } - /// Users can call `encode` multiple times to encode the previously set - /// `Parts` in segments. - pub(crate) fn encode(&mut self) -> () { + pub(crate) fn decode_ins(&mut self, buf: &[u8]) -> Result, H3Error> { + let mut decoder = DecInstDecoder::new(buf); + decoder.load(&mut self.inst_holder); + loop { + match decoder.decode()? { + Some(inst) => { + match inst { + DecoderInstruction::Ack { stream_id } => { + println!("stream_id: {}", stream_id); + assert_eq!(stream_id, self.stream_id); + loop {// ack an field section's all index + let ack_index = self.stream_reference.pop_front(); + if let Some(index) = ack_index { + if index == None { + break;// end of field section + } + if let Some(ind) = index { + if let Some(count) = self.table.ref_count.get(&ind){ + self.table.ref_count.insert(ind, count - 1); + } + } + } + else { + return Err(H3Error::ConnectionError(QPACK_DECODER_STREAM_ERROR)); + } + } + self.table.known_received_count += 1; + } + DecoderInstruction::StreamCancel { stream_id } => { + println!("stream_id: {}", stream_id); + assert_eq!(stream_id, self.stream_id); + return Ok(Some(DecoderInst::StreamCancel)); + } + DecoderInstruction::InsertCountIncrement { increment } => { + println!("increment: {}", increment); + self.table.known_received_count += increment; + } + } + } + None => return Ok(None), + } + } + } + + /// Users can call `encode` multiple times to encode multiple complete field sections. + pub(crate) fn encode(&mut self, encoder_buf: &mut [u8], stream_buf: &mut [u8]) -> (usize, usize) { let mut encoder = ReprEncoder::new(&mut self.table); encoder.load(&mut self.holder); - let (cur1,cur2) = encoder.encode(&mut self.encoder_buf[self.cur_encoder_buf..], &mut self.stream_buf[self.cur_stream_buf..]); - print!("encoder_buf: {:?}\n", &self.encoder_buf[..]); - print!("stream_buf: {:?}\n", &self.stream_buf[..]); - self.cur_encoder_buf += cur1; - self.cur_stream_buf += cur2; - // todo - // if size == dst.len() { - // encoder.save(&mut self.holder); - // } - // size + let (cur_encoder, cur_stream) = encoder.encode(&mut encoder_buf[0..], &mut stream_buf[0..], &mut self.stream_reference); + let mut cur_prefix = 0; + if self.is_finished() { + // denote an end of field section + self.stream_reference.push_back(None); + let wireRIC = self.table.insert_count % (2 * self.table.max_entries()) + 1; + let mut prefix_buf = [0u8; 1024]; + cur_prefix = Integer::index(0x00, wireRIC, 0xff).encode(&mut prefix_buf[..]).unwrap_or(0); + if self.table.known_received_count >= self.table.insert_count { + cur_prefix = Integer::index(0x00, self.table.known_received_count - self.table.insert_count, 0x7f).encode(&mut prefix_buf[cur_prefix..]).unwrap_or(0); + } else { + cur_prefix = Integer::index(0x80, self.table.insert_count - self.table.known_received_count - 1, 0x7f).encode(&mut prefix_buf[cur_prefix..]).unwrap_or(0); + } + // add prefix_buf[..cur_prefix] to the front of stream_buf + stream_buf.to_vec().splice(0..0, prefix_buf[..cur_prefix].to_vec()); + } + (cur_encoder, cur_stream + cur_prefix) } /// Check the previously set `Parts` if encoding is complete. @@ -60,12 +108,17 @@ impl QpackEncoder { } -pub(crate) enum InstEncode { +pub(crate) enum DecoderInst { + Ack, + StreamCancel, + InsertCountIncrement, +} + +pub(crate) enum EncoderInst { SetCap(SetCap), - // InsertWithNameRef(InsertWithNameRef), } -impl InstEncode { +impl EncoderInst { pub(crate) fn encode(self, encoder_buf: &mut [u8]) -> usize { let resut = match self { Self::SetCap(s) => s.encode(encoder_buf), @@ -78,6 +131,7 @@ impl InstEncode { } } + pub(crate) struct SetCap { capacity: Integer, } @@ -91,10 +145,10 @@ impl SetCap { Self { capacity: Integer::index(0x20, capacity, PrefixMask::SETCAP.0) } } - fn encode(self, dst: &mut [u8]) -> Result { + fn encode(self, dst: &mut [u8]) -> Result { self.capacity .encode(dst) - .map_err(|e| InstEncode::SetCap(SetCap::from(e))) + .map_err(|e| EncoderInst::SetCap(SetCap::from(e))) } } @@ -107,13 +161,6 @@ mod ut_qpack_encoder { #[test] fn test() { - // let mut vec = [0u8; 20]; - // let mut cur = 0; - let mut parts = Parts::new(); - parts.update(Field::Authority, String::from("www.example.com")); - let mut _encoder = QpackEncoder::<50, 50, 50>::with_capacity(220); - _encoder.set_parts(parts); - _encoder.encode(); - // println!("{:#?}", vec); + let mut encoder = QpackEncoder::with_capacity(4096, &mut [0u8; 1024], 0); } } \ No newline at end of file diff --git a/ylong_http/src/h3/qpack/format/decoder.rs b/ylong_http/src/h3/qpack/format/decoder.rs index b1621c0..5f1cc48 100644 --- a/ylong_http/src/h3/qpack/format/decoder.rs +++ b/ylong_http/src/h3/qpack/format/decoder.rs @@ -1,24 +1,24 @@ use std::cmp::Ordering; use crate::h3::error::{ErrorCode, H3Error}; -use crate::h3::error::ErrorCode::TEST; -use crate::h3::qpack::{MidBit, Representation, ReprPrefixBit, RequireInsertCount, DeltaBase, PrefixMask, Instruction, InstPrefixBit}; +use crate::h3::error::ErrorCode::QPACK_DECOMPRESSION_FAILED; +use crate::h3::qpack::{MidBit, Representation, ReprPrefixBit, RequireInsertCount, DeltaBase, PrefixMask, EncoderInstruction, EncoderInstPrefixBit}; use crate::h3::qpack::integer::IntegerDecoder; use crate::h3::qpack::format::decoder::DecResult::Error; use crate::huffman::HuffmanDecoder; -pub(crate) struct InstDecoder<'a>{ +pub(crate) struct EncInstDecoder<'a>{ buf: &'a [u8], state: Option, } -impl<'a> InstDecoder<'a> { +impl<'a> EncInstDecoder<'a> { pub(crate) fn new(buf: &'a [u8]) -> Self { Self { buf, state: None } } pub(crate) fn load(&mut self, holder: &mut InstDecStateHolder) { self.state = holder.state.take(); } - pub(crate) fn decode(&mut self) -> Result, H3Error> { + pub(crate) fn decode(&mut self) -> Result, H3Error> { if self.buf.is_empty() { return Ok(None); } @@ -26,7 +26,7 @@ impl<'a> InstDecoder<'a> { match self .state .take() - .unwrap_or_else(|| InstDecodeState::InstIndex(InstIndex::new())) + .unwrap_or_else(|| InstDecodeState::EncInstIndex(EncInstIndex::new())) .decode(&mut self.buf) { // If `buf` is not enough to continue decoding a complete @@ -153,8 +153,8 @@ macro_rules! state_def { } state_def!( InstDecodeState, - Instruction, - InstIndex, + EncoderInstruction, + EncInstIndex, InstValueString, InstNameAndValue, ); @@ -167,7 +167,7 @@ state_def!( ReprValueString, ReprNameAndValue, ); -state_def!(InstIndexInner, (InstPrefixBit, MidBit, usize), InstFirstByte, InstTrailingBytes); +state_def!(InstIndexInner, (EncoderInstPrefixBit, MidBit, usize), InstFirstByte, InstTrailingBytes); state_def!(ReprIndexInner, (ReprPrefixBit, MidBit, usize), ReprFirstByte, ReprTrailingBytes); state_def!(FSPInner, (RequireInsertCount, bool, DeltaBase), FSPTwoIntergers); state_def!( @@ -206,34 +206,34 @@ impl FiledSectionPrefix { } } -pub(crate) struct InstIndex{ +pub(crate) struct EncInstIndex { inner: InstIndexInner } -impl InstIndex{ +impl EncInstIndex { fn new() -> Self { Self::from_inner(InstFirstByte.into()) } fn from_inner(inner: InstIndexInner) -> Self { Self { inner } } - fn decode(self, buf: &mut &[u8]) -> DecResult { + fn decode(self, buf: &mut &[u8]) -> DecResult { match self.inner.decode(buf) { - DecResult::Decoded((InstPrefixBit::SETCAP, _, index)) => { - DecResult::Decoded(Instruction::SetCap { capacity:index }) + DecResult::Decoded((EncoderInstPrefixBit::SETCAP, _, index)) => { + DecResult::Decoded(EncoderInstruction::SetCap { capacity:index }) } - DecResult::Decoded((InstPrefixBit::INSERTWITHINDEX, mid_bit, index)) => { - InstValueString::new(InstPrefixBit::INSERTWITHINDEX, mid_bit, Name::Index(index)).decode(buf) + DecResult::Decoded((EncoderInstPrefixBit::INSERTWITHINDEX, mid_bit, index)) => { + InstValueString::new(EncoderInstPrefixBit::INSERTWITHINDEX, mid_bit, Name::Index(index)).decode(buf) } - DecResult::Decoded((InstPrefixBit::INSERTWITHLITERAL, mid_bit, namelen)) => { - InstNameAndValue::new(InstPrefixBit::INSERTWITHLITERAL, mid_bit, namelen).decode(buf) + DecResult::Decoded((EncoderInstPrefixBit::INSERTWITHLITERAL, mid_bit, namelen)) => { + InstNameAndValue::new(EncoderInstPrefixBit::INSERTWITHLITERAL, mid_bit, namelen).decode(buf) } - DecResult::Decoded((InstPrefixBit::DUPLICATE, _, index)) => { - DecResult::Decoded(Instruction::Duplicate { index }) + DecResult::Decoded((EncoderInstPrefixBit::DUPLICATE, _, index)) => { + DecResult::Decoded(EncoderInstruction::Duplicate { index }) } - DecResult::NeedMore(inner) => DecResult::NeedMore(InstIndex::from_inner(inner).into()), + DecResult::NeedMore(inner) => DecResult::NeedMore(EncInstIndex::from_inner(inner).into()), DecResult::Error(e) => e.into(), - _ => DecResult::Error(H3Error::ConnectionError(ErrorCode::TEST)), + _ => DecResult::Error(H3Error::ConnectionError(ErrorCode::QPACK_DECOMPRESSION_FAILED)), } } } @@ -268,7 +268,7 @@ impl ReprIndex { } DecResult::NeedMore(inner) => DecResult::NeedMore(ReprIndex::from_inner(inner).into()), DecResult::Error(e) => e.into(), - _ => DecResult::Error(H3Error::ConnectionError(ErrorCode::TEST)), + _ => DecResult::Error(H3Error::ConnectionError(ErrorCode::QPACK_DECOMPRESSION_FAILED )), } } } @@ -346,13 +346,13 @@ impl FSPTwoIntergers { pub(crate) struct InstFirstByte; impl InstFirstByte { - fn decode(self, buf: &mut &[u8]) -> DecResult<(InstPrefixBit, MidBit, usize), InstIndexInner> { + fn decode(self, buf: &mut &[u8]) -> DecResult<(EncoderInstPrefixBit, MidBit, usize), InstIndexInner> { // If `buf` has been completely decoded here, return the current state. if buf.is_empty() { return DecResult::NeedMore(self.into()); } let byte = buf[0]; - let inst = InstPrefixBit::from_u8(byte); + let inst = EncoderInstPrefixBit::from_u8(byte); let mid_bit = inst.prefix_midbit_value(byte); let mask = inst.prefix_index_mask(); @@ -391,16 +391,16 @@ impl ReprFirstByte { } } pub(crate) struct InstTrailingBytes { - inst: InstPrefixBit, + inst: EncoderInstPrefixBit, mid_bit: MidBit, index: IntegerDecoder, } impl InstTrailingBytes { - fn new(inst: InstPrefixBit, mid_bit: MidBit, index: IntegerDecoder) -> Self { + fn new(inst: EncoderInstPrefixBit, mid_bit: MidBit, index: IntegerDecoder) -> Self { Self { inst, mid_bit, index } } - fn decode(mut self, buf: &mut &[u8]) -> DecResult<(InstPrefixBit, MidBit, usize), InstIndexInner> { + fn decode(mut self, buf: &mut &[u8]) -> DecResult<(EncoderInstPrefixBit, MidBit, usize), InstIndexInner> { loop { // If `buf` has been completely decoded here, return the current state. if buf.is_empty() { @@ -505,19 +505,19 @@ impl AsciiStringBytes { } } pub(crate) struct InstNameAndValue { - inst: InstPrefixBit, + inst: EncoderInstPrefixBit, mid_bit: MidBit, inner: LiteralString, } impl InstNameAndValue { - fn new(inst: InstPrefixBit, mid_bit: MidBit, namelen: usize) -> Self { + fn new(inst: EncoderInstPrefixBit, mid_bit: MidBit, namelen: usize) -> Self { Self::from_inner(inst, mid_bit, AsciiStringBytes::new(namelen).into()) } - fn from_inner(inst: InstPrefixBit, mid_bit: MidBit, inner: LiteralString) -> Self { + fn from_inner(inst: EncoderInstPrefixBit, mid_bit: MidBit, inner: LiteralString) -> Self { Self { inst, mid_bit, inner } } - fn decode(self, buf: &mut &[u8]) -> DecResult { + fn decode(self, buf: &mut &[u8]) -> DecResult { match self.inner.decode(buf) { DecResult::Decoded(octets) => { InstValueString::new(self.inst, self.mid_bit, Name::Literal(octets)).decode(buf) @@ -593,17 +593,17 @@ impl HuffmanStringBytes { Ordering::Greater | Ordering::Equal => { let pos = self.length - self.read; if self.huffman.decode(&buf[..pos]).is_err() { - return H3Error::ConnectionError(TEST).into(); + return H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED).into(); } *buf = &buf[pos..]; match self.huffman.finish() { Ok(vec) => DecResult::Decoded(vec), - Err(_) => H3Error::ConnectionError(TEST).into(), + Err(_) => H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED).into(), } } Ordering::Less => { if self.huffman.decode(buf).is_err() { - return H3Error::ConnectionError(TEST).into(); + return H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED).into(); } self.read += buf.len(); *buf = &buf[buf.len()..]; @@ -618,38 +618,38 @@ pub(crate) enum Name { Literal(Vec), } pub(crate) struct InstValueString { - inst: InstPrefixBit, + inst: EncoderInstPrefixBit, mid_bit: MidBit, name: Name, inner: LiteralString, } impl InstValueString { - fn new(inst: InstPrefixBit, mid_bit: MidBit, name: Name) -> Self { + fn new(inst: EncoderInstPrefixBit, mid_bit: MidBit, name: Name) -> Self { Self::from_inner(inst, mid_bit, name, LengthFirstByte.into()) } - fn from_inner(inst: InstPrefixBit, mid_bit: MidBit, name: Name, inner: LiteralString) -> Self { + fn from_inner(inst: EncoderInstPrefixBit, mid_bit: MidBit, name: Name, inner: LiteralString) -> Self { Self { inst, mid_bit, name, inner } } - fn decode(self, buf: &mut &[u8]) -> DecResult { + fn decode(self, buf: &mut &[u8]) -> DecResult { match (self.inst, self.inner.decode(buf)) { - (InstPrefixBit::INSERTWITHINDEX, DecResult::Decoded(value)) => { - DecResult::Decoded(Instruction::InsertWithIndex { + (EncoderInstPrefixBit::INSERTWITHINDEX, DecResult::Decoded(value)) => { + DecResult::Decoded(EncoderInstruction::InsertWithIndex { mid_bit: self.mid_bit, name: self.name, value, }) } - (InstPrefixBit::INSERTWITHLITERAL, DecResult::Decoded(value)) => { - DecResult::Decoded(Instruction::InsertWithLiteral { + (EncoderInstPrefixBit::INSERTWITHLITERAL, DecResult::Decoded(value)) => { + DecResult::Decoded(EncoderInstruction::InsertWithLiteral { mid_bit: self.mid_bit, name: self.name, value, }) } - (_, _) => Error(H3Error::ConnectionError(TEST)) + (_, _) => Error(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED)) } } } @@ -692,14 +692,14 @@ impl ReprValueString { value, }) } - (_, _) => Error(H3Error::ConnectionError(TEST)) + (_, _) => Error(H3Error::ConnectionError(QPACK_DECOMPRESSION_FAILED)) } } } /// Decoder's possible returns during the decoding process. -enum DecResult { +pub(crate) enum DecResult { /// Decoder has got a `D`. Users can continue to call `encode` to try to /// get the next `D`. Decoded(D), diff --git a/ylong_http/src/h3/qpack/format/encoder.rs b/ylong_http/src/h3/qpack/format/encoder.rs index 045ba67..6f9ee60 100644 --- a/ylong_http/src/h3/qpack/format/encoder.rs +++ b/ylong_http/src/h3/qpack/format/encoder.rs @@ -1,10 +1,14 @@ use std::arch::asm; use std::cmp::{max, Ordering}; +use std::collections::{HashMap, VecDeque}; use std::result; +use crate::h3::error::ErrorCode::QPACK_DECODER_STREAM_ERROR; +use crate::h3::error::H3Error; use crate::h3::parts::Parts; use crate::h3::pseudo::PseudoHeaders; -use crate::h3::qpack::integer::{Integer, IntegerEncoder}; -use crate::h3::qpack::PrefixMask; +use crate::h3::qpack::integer::{Integer, IntegerDecoder, IntegerEncoder}; +use crate::h3::qpack::{DecoderInstPrefixBit, DecoderInstruction, EncoderInstruction, PrefixMask}; +use crate::h3::qpack::format::decoder::DecResult; use crate::h3::qpack::table::{DynamicTable, Field, TableIndex, TableSearcher}; use crate::headers::HeadersIntoIter; @@ -12,9 +16,6 @@ pub(crate) struct ReprEncoder<'a> { table: &'a mut DynamicTable, iter: Option, state: Option, - base: Base, - req_insert_count: ReqInsertCount - } @@ -25,8 +26,7 @@ impl<'a> ReprEncoder<'a> { table, iter: None, state: None, - base: Base::from(false,0), - req_insert_count: ReqInsertCount::new(0), + } } @@ -44,27 +44,19 @@ impl<'a> ReprEncoder<'a> { /// Decodes the contents of `self.iter` and `self.state`. The result will be /// written to `dst` and the length of the decoded content will be returned. - /// - /// If the `dst` is full, users can call this method again after preparing a - /// new piece of memory, and the subsequent decoding part will be written - /// into the new memory. - /// - /// # Note - /// Decoding is complete only when `self.iter` and `self.state` are both - /// `None`. It is recommended that users save the result to a - /// `RecEncStateHolder` immediately after using the method. - pub(crate) fn encode(&mut self, encoder_buffer: &mut [u8], stream_buffer: &mut [u8]) -> (usize, usize) { + pub(crate) fn encode(&mut self, encoder_buffer: &mut [u8], stream_buffer: &mut [u8], stream_reference: &mut VecDeque>) -> (usize, usize) { let mut cur_encoder = 0; let mut cur_stream = 0; if let Some(mut iter) = self.iter.take() { while let Some((h, v)) = iter.next() { let searcher = TableSearcher::new(self.table); println!("h: {:?}, v: {:?}", h, v); - let mut stream_result:Result = Result::Err(ReprEncodeState::Indexed(Indexed::new(0, false))); - let mut encoder_result:Result = Result::Err(ReprEncodeState::Indexed(Indexed::new(0, false))); + let mut stream_result: Result = Result::Err(ReprEncodeState::Indexed(Indexed::new(0, false))); + let mut encoder_result: Result = Result::Err(ReprEncodeState::Indexed(Indexed::new(0, false))); let static_index = searcher.find_index_static(&h, &v); if static_index != Some(TableIndex::None) { if let Some(TableIndex::Field(index)) = static_index { + // Encode as index in static table stream_result = Indexed::new(index, true).encode(&mut stream_buffer[cur_stream..]); } } else { @@ -75,91 +67,91 @@ impl<'a> ReprEncoder<'a> { if static_name_index == Some(TableIndex::None) { dynamic_name_index = searcher.find_index_name_dynamic(&h, &v); } - if self.should_index(&h, &v) && searcher.can_index(&h, &v) { + + if self.should_index(&h, &v) && self.table.have_enough_space(&h, &v) { encoder_result = match (&static_name_index, &dynamic_name_index) { + // insert with name reference in static table (Some(TableIndex::FieldName(index)), _) => { InsertWithName::new(index.clone(), v.clone().into_bytes(), false, true).encode(&mut encoder_buffer[cur_encoder..]) } + // insert with name reference in dynamic table (_, Some(TableIndex::FieldName(index))) => { - InsertWithName::new(index.clone(), v.clone().into_bytes(), false, false).encode(&mut encoder_buffer[cur_encoder..]) + // convert abs index to rel index + InsertWithName::new(self.table.insert_count - index.clone() - 1, v.clone().into_bytes(), false, false).encode(&mut encoder_buffer[cur_encoder..]) } + // insert with literal name (_, _) => { InsertWithLiteral::new(h.clone().into_string().into_bytes(), v.clone().into_bytes(), false).encode(&mut encoder_buffer[cur_encoder..]) } }; + // update dynamic table + self.table.insert_count += 1; self.table.update(h.clone(), v.clone()); } } - if dynamic_index == Some(TableIndex::None){ - if dynamic_name_index != Some(TableIndex::None){ - //Encode literal with dynamic name, possibly above Base - let base_value = self.base.get_base(&self.req_insert_count); + if dynamic_index == Some(TableIndex::None) { + if dynamic_name_index != Some(TableIndex::None) { + //Encode with name reference in dynamic table if let Some(TableIndex::FieldName(index)) = dynamic_name_index { - if index < base_value{ - stream_result = IndexingWithName::new(index, v.clone().into_bytes(), false, false, false).encode(&mut stream_buffer[cur_stream..]); + stream_reference.push_back(Some(index)); + if let Some(count) = self.table.ref_count.get(&index) { + self.table.ref_count.insert(index, count + 1); } - else{ - stream_result = IndexingWithPostName::new(index, v.clone().into_bytes(), false, false).encode(&mut stream_buffer[cur_stream..]); + // use post-base index + if self.table.known_received_count <= index { + stream_result = IndexingWithPostName::new(index - self.table.known_received_count, v.clone().into_bytes(), false, false).encode(&mut stream_buffer[cur_stream..]); + } else { + stream_result = IndexingWithName::new(self.table.known_received_count - index - 1, v.clone().into_bytes(), false, false, false).encode(&mut stream_buffer[cur_stream..]); } } - // requiredInsertCount = max(requiredInsertCount,dynamicNameIndex) - if let Some(TableIndex::FieldName(index)) = dynamic_name_index { - let maxindex = max(self.req_insert_count.count.int.get_index(), index); - self.req_insert_count = ReqInsertCount::new(maxindex); - } - } - else{ - // Encodes a literal with a static name or literal name - if static_name_index != Some(TableIndex::None){ + } else { + // Encode with name reference in static table + // or Encode as Literal + if static_name_index != Some(TableIndex::None) { if let Some(TableIndex::FieldName(index)) = static_name_index { stream_result = IndexingWithName::new(index, v.into_bytes(), false, true, false).encode(&mut stream_buffer[cur_stream..]); } - } - else { + } else { stream_result = IndexingWithLiteral::new(h.into_string().into_bytes(), v.into_bytes(), false, false).encode(&mut stream_buffer[cur_stream..]); } } - } - else { - // Dynamic index reference + } else { assert!(dynamic_index != Some(TableIndex::None)); - // requiredInsertCount = max(requiredInsertCount, dynamicIndex) - if let Some(TableIndex::FieldName(index)) = dynamic_index { - let maxindex = max(self.req_insert_count.count.int.get_index(), index); - self.req_insert_count = ReqInsertCount::new(maxindex); - } - // Encode dynamicIndex, possibly above Base - let base_value = self.base.get_base(&self.req_insert_count); + // Encode with index in dynamic table if let Some(TableIndex::FieldName(index)) = dynamic_name_index { - if index < base_value{ - stream_result = Indexed::new(index, false).encode(&mut stream_buffer[cur_stream..]); + // use post-base index + stream_reference.push_back(Some(index)); + if let Some(count) = self.table.ref_count.get(&index) { + self.table.ref_count.insert(index, count + 1); + } + if self.table.known_received_count <= index{ + stream_result = IndexedWithPostName::new(index - self.table.known_received_count).encode(&mut stream_buffer[cur_stream..]); } - else{ - stream_result = IndexedWithPostName::new(index).encode(&mut stream_buffer[cur_stream..]); + else { + stream_result = Indexed::new(self.table.known_received_count - index - 1, false).encode(&mut stream_buffer[cur_stream..]); } } } } - match (encoder_result, stream_result) { - (Ok(encoder_size),Ok(stream_size)) => { + (Ok(encoder_size), Ok(stream_size)) => { cur_stream += stream_size; cur_encoder += encoder_size; - }, + } (Err(state), Ok(stream_size)) => { cur_stream += stream_size; self.state = Some(state); self.iter = Some(iter); return (encoder_buffer.len(), stream_buffer.len()); - }, + } (Ok(encoder_size), Err(state)) => { cur_encoder += encoder_size; self.state = Some(state); self.iter = Some(iter); return (encoder_buffer.len(), stream_buffer.len()); - }, + } (Err(_), Err(state)) => { self.state = Some(state); self.iter = Some(iter); @@ -170,16 +162,39 @@ impl<'a> ReprEncoder<'a> { } (cur_encoder, cur_stream) } + /// ## 2.1.1.1. Avoiding Prohibited Insertions + /// To ensure that the encoder is not prevented from adding new entries, the encoder can + /// avoid referencing entries that are close to eviction. Rather than reference such an + /// entry, the encoder can emit a Duplicate instruction (Section 4.3.4) and reference + /// the duplicate instead. + /// + /// Determining which entries are too close to eviction to reference is an encoder preference. + /// One heuristic is to target a fixed amount of available space in the dynamic table: + /// either unused space or space that can be reclaimed by evicting non-blocking entries. + /// To achieve this, the encoder can maintain a draining index, which is the smallest + /// absolute index (Section 3.2.4) in the dynamic table that it will emit a reference for. + /// As new entries are inserted, the encoder increases the draining index to maintain the + /// section of the table that it will not reference. If the encoder does not create new + /// references to entries with an absolute index lower than the draining index, the number + /// of unacknowledged references to those entries will eventually become zero, allowing + /// them to be evicted. + /// + /// <-- Newer Entries Older Entries --> + /// (Larger Indices) (Smaller Indices) + /// +--------+---------------------------------+----------+ + /// | Unused | Referenceable | Draining | + /// | Space | Entries | Entries | + /// +--------+---------------------------------+----------+ + /// ^ ^ ^ + /// | | | + /// Insertion Point Draining Index Dropping + /// Point pub(crate) fn should_index(&self, header: &Field, value: &str) -> bool { - //todo + //todo: add condition to modify the algorithm true } } -pub(crate) struct EncFieldPrefix { - req_incert_count: ReqInsertCount, - base: Base, -} pub(crate) struct ReprEncStateHolder { @@ -216,7 +231,6 @@ pub(crate) enum ReprEncodeState { IndexingWithPostName(IndexingWithPostName), IndexingWithLiteral(IndexingWithLiteral), IndexedWithPostName(IndexedWithPostName), - //todo } @@ -256,7 +270,6 @@ impl IndexedWithPostName { } fn new(index: usize) -> Self { - Self { index: Integer::index(0x10, index, PrefixMask::INDEXINGWITHPOSTNAME.0) } } @@ -268,7 +281,6 @@ impl IndexedWithPostName { } - pub(crate) struct InsertWithName { inner: IndexAndValue, } @@ -311,36 +323,36 @@ impl IndexingWithName { Self { inner } } - fn new(index: usize, value: Vec, is_huffman: bool, is_static: bool, no_permit:bool) -> Self { - match (no_permit,is_static){ + fn new(index: usize, value: Vec, is_huffman: bool, is_static: bool, no_permit: bool) -> Self { + match (no_permit, is_static) { (true, true) => { Self { inner: IndexAndValue::new() .set_index(0x70, index, PrefixMask::INDEXINGWITHNAME.0) .set_value(value, is_huffman), } - }, + } (true, false) => { Self { inner: IndexAndValue::new() .set_index(0x60, index, PrefixMask::INDEXINGWITHNAME.0) .set_value(value, is_huffman), } - }, + } (false, true) => { Self { inner: IndexAndValue::new() .set_index(0x50, index, PrefixMask::INDEXINGWITHNAME.0) .set_value(value, is_huffman), } - }, + } (false, false) => { Self { inner: IndexAndValue::new() .set_index(0x40, index, PrefixMask::INDEXINGWITHNAME.0) .set_value(value, is_huffman), } - }, + } } } @@ -352,7 +364,6 @@ impl IndexingWithName { } - pub(crate) struct IndexingWithPostName { inner: IndexAndValue, } @@ -362,15 +373,14 @@ impl IndexingWithPostName { Self { inner } } - fn new(index: usize, value: Vec, is_huffman: bool, no_permit:bool) -> Self { - if no_permit{ + fn new(index: usize, value: Vec, is_huffman: bool, no_permit: bool) -> Self { + if no_permit { Self { inner: IndexAndValue::new() .set_index(0x08, index, PrefixMask::INDEXINGWITHPOSTNAME.0) .set_value(value, is_huffman), } - } - else{ + } else { Self { inner: IndexAndValue::new() .set_index(0x00, index, PrefixMask::INDEXINGWITHPOSTNAME.0) @@ -393,37 +403,36 @@ pub(crate) struct IndexingWithLiteral { impl IndexingWithLiteral { fn new(name: Vec, value: Vec, is_huffman: bool, no_permit: bool) -> Self { - match (no_permit,is_huffman) { + match (no_permit, is_huffman) { (true, true) => { Self { inner: NameAndValue::new() .set_index(0x38, name.len(), PrefixMask::INDEXINGWITHLITERAL.0) .set_name_and_value(name, value, is_huffman), } - }, + } (true, false) => { Self { inner: NameAndValue::new() .set_index(0x30, name.len(), PrefixMask::INDEXINGWITHLITERAL.0) .set_name_and_value(name, value, is_huffman), } - }, + } (false, true) => { Self { inner: NameAndValue::new() .set_index(0x28, name.len(), PrefixMask::INDEXINGWITHLITERAL.0) .set_name_and_value(name, value, is_huffman), } - }, + } (false, false) => { Self { inner: NameAndValue::new() .set_index(0x20, name.len(), PrefixMask::INDEXINGWITHLITERAL.0) .set_name_and_value(name, value, is_huffman), } - }, + } } - } fn from(inner: NameAndValue) -> Self { @@ -444,21 +453,19 @@ pub(crate) struct InsertWithLiteral { impl InsertWithLiteral { fn new(name: Vec, value: Vec, is_huffman: bool) -> Self { - if is_huffman{ + if is_huffman { Self { inner: NameAndValue::new() .set_index(0x60, name.len(), PrefixMask::INSERTWITHLITERAL.0) .set_name_and_value(name, value, is_huffman), } - } - else{ + } else { Self { inner: NameAndValue::new() .set_index(0x40, name.len(), PrefixMask::INSERTWITHLITERAL.0) .set_name_and_value(name, value, is_huffman), } } - } fn from(inner: NameAndValue) -> Self { @@ -564,6 +571,172 @@ impl NameAndValue { } } + + +macro_rules! state_def { + ($name: ident, $decoded: ty, $($state: ident),* $(,)?) => { + pub(crate) enum $name { + $( + $state($state), + )* + } + + impl $name { + fn decode(self, buf: &mut &[u8]) -> DecResult<$decoded, $name> { + match self { + $( + Self::$state(state) => state.decode(buf), + )* + } + } + } + + $( + impl From<$state> for $name { + fn from(s: $state) -> Self { + Self::$state(s) + } + } + )* + } +} + +state_def!( + InstDecodeState, + DecoderInstruction, + DecInstIndex +); +pub(crate) struct DecInstDecoder<'a>{ + buf: &'a [u8], + state: Option, +} +pub(crate) struct InstDecStateHolder { + state: Option, +} + +impl InstDecStateHolder { + pub(crate) fn new() -> Self { + Self { state: None } + } + + pub(crate) fn is_empty(&self) -> bool { + self.state.is_none() + } +} +impl<'a> DecInstDecoder<'a> { + pub(crate) fn new(buf: &'a [u8]) -> Self { + Self { buf, state: None } + } + pub(crate) fn load(&mut self, holder: &mut InstDecStateHolder) { + self.state = holder.state.take(); + } + pub(crate) fn decode(&mut self) -> Result, H3Error> { + if self.buf.is_empty() { + return Ok(None); + } + + match self + .state + .take() + .unwrap_or_else(|| InstDecodeState::DecInstIndex(DecInstIndex::new())) + .decode(&mut self.buf) + { + // If `buf` is not enough to continue decoding a complete + // `Representation`, `Ok(None)` will be returned. Users need to call + // `save` to save the current state to a `ReprDecStateHolder`. + DecResult::NeedMore(state) => { + println!("NeedMore"); + self.state = Some(state); + Ok(None) + } + DecResult::Decoded(repr) => { + Ok(Some(repr)) + } + + DecResult::Error(error) => Err(error), + } + } +} +state_def!(DecInstIndexInner, (DecoderInstPrefixBit, usize), InstFirstByte, InstTrailingBytes); + +pub(crate) struct DecInstIndex { + inner: DecInstIndexInner +} +impl DecInstIndex { + fn new() -> Self { + Self::from_inner(InstFirstByte.into()) + } + fn from_inner(inner: DecInstIndexInner) -> Self { + Self { inner } + } + fn decode(self, buf: &mut &[u8]) -> DecResult { + match self.inner.decode(buf) { + DecResult::Decoded((DecoderInstPrefixBit::ACK, index)) => { + DecResult::Decoded(DecoderInstruction::Ack { stream_id:index }) + } + DecResult::Decoded((DecoderInstPrefixBit::STREAMCANCEL, index)) => { + DecResult::Decoded(DecoderInstruction::StreamCancel { stream_id:index }) + } + DecResult::Decoded((DecoderInstPrefixBit::INSERTCOUNTINCREMENT, index)) => { + DecResult::Decoded(DecoderInstruction::InsertCountIncrement { increment: index }) + } + DecResult::Error(e) => e.into(), + _ => DecResult::Error(H3Error::ConnectionError(QPACK_DECODER_STREAM_ERROR)), + } + } +} + +pub(crate) struct InstFirstByte; + +impl InstFirstByte { + fn decode(self, buf: &mut &[u8]) -> DecResult<(DecoderInstPrefixBit, usize), DecInstIndexInner> { + // If `buf` has been completely decoded here, return the current state. + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + let byte = buf[0]; + let inst = DecoderInstPrefixBit::from_u8(byte); + let mask = inst.prefix_index_mask(); + + // Moves the pointer of `buf` backward. + *buf = &buf[1..]; + match IntegerDecoder::first_byte(byte, mask.0) { + // Return the ReprPrefixBit and index part value. + Ok(idx) => DecResult::Decoded((inst, idx)), + // Index part value is longer than index(i.e. use all 1 to represent), so it needs more bytes to decode. + Err(int) => InstTrailingBytes::new(inst, int).decode(buf), + } + } +} + +pub(crate) struct InstTrailingBytes { + inst: DecoderInstPrefixBit, + index: IntegerDecoder, +} + +impl InstTrailingBytes { + fn new(inst: DecoderInstPrefixBit, index: IntegerDecoder) -> Self { + Self { inst, index } + } + fn decode(mut self, buf: &mut &[u8]) -> DecResult<(DecoderInstPrefixBit, usize), DecInstIndexInner> { + loop { + // If `buf` has been completely decoded here, return the current state. + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + + let byte = buf[0]; + *buf = &buf[1..]; + // Updates trailing bytes until we get the index. + match self.index.next_byte(byte) { + Ok(None) => {} + Ok(Some(index)) => return DecResult::Decoded((self.inst, index)), + Err(e) => return e.into(), + } + } + } +} + pub(crate) struct Octets { src: Vec, idx: usize, @@ -599,49 +772,6 @@ impl Octets { } } -pub(crate) struct EncInsertCount { - count: Integer, -} - -// impl EncInsertCount { -// fn from_req_insert_count(req_ins_cnt: ReqInsertCount, max_table_capacity: usize) -> Self { -// let max_entries = max_table_capacity / 32; -// if req_ins_cnt.count.int.get_index() == 0 { -// Self { count: Integer::index(0x00, 0) } -// } else { -// Self { count: Integer::index(0x00, (req_ins_cnt.count.int.get_index() % (2 * max_entries)) + 1) } -// } -// } -// } - -pub(crate) struct Base { - base: Integer, -} - -impl Base { - fn from(sign: bool, delta_base: usize) -> Self { - Self { base: Integer::index(if sign { 0x80 } else { 0x00 }, delta_base, PrefixMask::DELTABASE.0) } - } - - fn get_base(&self, req_incert_count: &ReqInsertCount) -> usize { - if self.base.int.get_pre() == 0x80 { - req_incert_count.count.int.get_index() + self.base.int.get_index() - } else { - req_incert_count.count.int.get_index() - self.base.int.get_index() - 1 - } - } -} - -pub(crate) struct ReqInsertCount { - count: Integer, -} - -impl ReqInsertCount { - fn new(index:usize) -> Self { - Self { count: Integer::index(0x00, index, 0xff) } - } -} - struct PartsIter { pseudo: PseudoHeaders, map: HeadersIntoIter, diff --git a/ylong_http/src/h3/qpack/format/mod.rs b/ylong_http/src/h3/qpack/format/mod.rs index 15cb64a..f7ae841 100644 --- a/ylong_http/src/h3/qpack/format/mod.rs +++ b/ylong_http/src/h3/qpack/format/mod.rs @@ -1,4 +1,4 @@ -mod encoder; +pub(crate) mod encoder; pub(crate) mod decoder; pub(crate) use encoder::{ReprEncoder,ReprEncStateHolder}; diff --git a/ylong_http/src/h3/qpack/integer.rs b/ylong_http/src/h3/qpack/integer.rs index d3521dd..9551bb1 100644 --- a/ylong_http/src/h3/qpack/integer.rs +++ b/ylong_http/src/h3/qpack/integer.rs @@ -57,7 +57,7 @@ impl IntegerDecoder { .checked_shl(self.shift - 1) .and_then(|res| res.checked_mul((byte & 0x7f) as usize)) .and_then(|res| res.checked_add(self.index)) - .ok_or(H3Error::ConnectionError(ErrorCode::TEST))?;//todo: modify the error code + .ok_or(H3Error::ConnectionError(ErrorCode::QPACK_DECOMPRESSION_FAILED))?;//todo: modify the error code self.shift += 7; match (byte & 0x80) == 0x00 { true => Ok(Some(self.index)), diff --git a/ylong_http/src/h3/qpack/mod.rs b/ylong_http/src/h3/qpack/mod.rs index 7f9741f..82a2986 100644 --- a/ylong_http/src/h3/qpack/mod.rs +++ b/ylong_http/src/h3/qpack/mod.rs @@ -13,7 +13,10 @@ pub(crate) struct RequireInsertCount(usize); pub(crate) struct DeltaBase(usize); #[derive(Copy, Clone, PartialEq, Eq)] -pub(crate) struct InstPrefixBit(u8); +pub(crate) struct EncoderInstPrefixBit(u8); + +#[derive(Copy, Clone, PartialEq, Eq)] +pub(crate) struct DecoderInstPrefixBit(u8); #[derive(Copy, Clone, PartialEq, Eq)] pub(crate) struct ReprPrefixBit(u8); @@ -25,6 +28,11 @@ pub(crate) struct ReprPrefixBit(u8); /// INSERTWITHLITERAL: 0x40 /// DUPLICATE: 0x00 /// +/// ## Decoder Instructions: +/// ACK: 0x80 +/// STREAMCANCEL: 0x40 +/// INSERTCOUNTINCREMENT: 0x00 +/// /// ## Representation: /// INDEXED: 0x80 /// INDEXEDWITHPOSTINDEX: 0x10 @@ -32,7 +40,35 @@ pub(crate) struct ReprPrefixBit(u8); /// LITERALWITHPOSTINDEXING: 0x00 /// LITERALWITHLITERALNAME: 0x20 -impl InstPrefixBit { +impl DecoderInstPrefixBit { + pub(crate) const ACK: Self = Self(0x80); + pub(crate) const STREAMCANCEL: Self = Self(0x40); + pub(crate) const INSERTCOUNTINCREMENT: Self = Self(0x00); + + pub(crate) fn from_u8(byte: u8) -> Self { + match byte { + x if x >= 0x80 => Self::ACK, + x if x >= 0x40 => Self::STREAMCANCEL, + _ => Self::INSERTCOUNTINCREMENT, + } + } + + pub(crate) fn prefix_index_mask(&self) -> PrefixMask { + match self.0 { + 0x80 => PrefixMask::ACK, + 0x40 => PrefixMask::STREAMCANCEL, + _ => PrefixMask::INSERTCOUNTINCREMENT, + } + } + + pub(crate) fn prefix_midbit_value(&self, byte: u8) -> MidBit { + match self.0 { + _ => MidBit { n: None, t: None, h: None }, + } + } +} + +impl EncoderInstPrefixBit { pub(crate) const SETCAP: Self = Self(0x20); pub(crate) const INSERTWITHINDEX: Self = Self(0x80); pub(crate) const INSERTWITHLITERAL: Self = Self(0x40); @@ -110,11 +146,18 @@ impl ReprPrefixBit { } } -pub(crate) enum Instruction { +pub(crate) enum EncoderInstruction { SetCap { capacity: usize }, InsertWithIndex { mid_bit: MidBit, name: Name, value: Vec }, InsertWithLiteral { mid_bit: MidBit, name: Name, value: Vec }, Duplicate { index: usize }, + +} + +pub(crate) enum DecoderInstruction { + Ack { stream_id: usize }, + StreamCancel { stream_id: usize }, + InsertCountIncrement { increment: usize }, } pub(crate) enum Representation { @@ -167,6 +210,10 @@ impl PrefixMask { pub(crate) const INSERTWITHLITERAL: Self = Self(0x1f); pub(crate) const DUPLICATE: Self = Self(0x1f); + pub(crate) const ACK: Self = Self(0x7f); + pub(crate) const STREAMCANCEL: Self = Self(0x3f); + pub(crate) const INSERTCOUNTINCREMENT: Self = Self(0x3f); + pub(crate) const INDEXINGWITHNAME: Self = Self(0x0f); pub(crate) const INDEXINGWITHPOSTNAME: Self = Self(0x07); pub(crate) const INDEXINGWITHLITERAL: Self = Self(0x07); diff --git a/ylong_http/src/h3/qpack/table.rs b/ylong_http/src/h3/qpack/table.rs index 5143602..ae15872 100644 --- a/ylong_http/src/h3/qpack/table.rs +++ b/ylong_http/src/h3/qpack/table.rs @@ -1,4 +1,4 @@ -use std::collections::VecDeque; +use std::collections::{HashMap, VecDeque}; /// The [`Dynamic Table`][dynamic_table] implementation of [QPACK]. /// @@ -88,6 +88,8 @@ pub(crate) struct DynamicTable { capacity: usize, pub(crate) insert_count: usize, remove_count: usize, + pub(crate) known_received_count: usize, + pub(crate) ref_count: HashMap, } impl DynamicTable { @@ -98,6 +100,8 @@ impl DynamicTable { capacity, insert_count: 0, remove_count: 0, + known_received_count: 0, + ref_count: HashMap::new(), } } @@ -109,13 +113,40 @@ impl DynamicTable { self.capacity } + pub(crate) fn max_entries(&self) -> usize { + self.capacity / 32 + } /// Updates `DynamicTable` by a given `Header` and value pair. pub(crate) fn update(&mut self, field: Field, value: String) { self.size += field.len() + value.len() + 32; self.queue.push_back((field, value)); + self.ref_count.insert(self.queue.len() + self.remove_count - 1, 0); self.fit_size(); } + pub(crate) fn have_enough_space(&self, field: &Field, value: &String) -> bool { + if self.size + field.len() + value.len() + 32 <= self.capacity { + return true; + } else { + let mut eviction_space = 0; + for (i, (h, v)) in self.queue.iter().enumerate() { + if let Some(0) = self.ref_count.get(&(i + self.remove_count)){ + eviction_space += h.len() + v.len() + 32; + } + else { + if eviction_space >= field.len() + value.len() + 32 { + return true; + } + return false; + } + if eviction_space >= field.len() + value.len() + 32 { + return true; + } + } + } + false + } + /// Updates `DynamicTable`'s size. pub(crate) fn update_size(&mut self, max_size: usize) { self.capacity = max_size; @@ -145,12 +176,13 @@ impl DynamicTable { } pub(crate) fn field(&self, index: usize) -> Option<(Field, String)> { - self.queue.get(index-self.remove_count).cloned() + self.queue.get(index - self.remove_count).cloned() } pub(crate) fn field_name(&self, index: usize) -> Option { - self.queue.get(index-self.remove_count).map(|(field, _)| field.clone()) + self.queue.get(index - self.remove_count).map(|(field, _)| field.clone()) } + } #[derive(PartialEq)] -- Gitee From 043b6d5547552c1419add13fbee0323e98fc0125 Mon Sep 17 00:00:00 2001 From: Weng Shihao Date: Sun, 20 Aug 2023 23:36:02 +0800 Subject: [PATCH 6/6] qpack --- ylong_http/src/h3/qpack/decoder.rs | 1 + ylong_http/src/h3/qpack/encoder.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/ylong_http/src/h3/qpack/decoder.rs b/ylong_http/src/h3/qpack/decoder.rs index 0213154..a3b6fa3 100644 --- a/ylong_http/src/h3/qpack/decoder.rs +++ b/ylong_http/src/h3/qpack/decoder.rs @@ -40,6 +40,7 @@ impl<'a> QpackDecoder<'a> { } } + /// Users can call `decode_ins` multiple times to decode decoder instructions. pub(crate) fn decode_ins(&mut self, buf: &[u8]) -> Result<(), H3Error> { let mut decoder = EncInstDecoder::new(buf); decoder.load(&mut self.inst_holder); diff --git a/ylong_http/src/h3/qpack/encoder.rs b/ylong_http/src/h3/qpack/encoder.rs index 195cb57..c994d77 100644 --- a/ylong_http/src/h3/qpack/encoder.rs +++ b/ylong_http/src/h3/qpack/encoder.rs @@ -34,6 +34,7 @@ impl QpackEncoder { self.holder.set_parts(parts) } + /// Users can call `decode_ins` multiple times to decode decoder instructions. pub(crate) fn decode_ins(&mut self, buf: &[u8]) -> Result, H3Error> { let mut decoder = DecInstDecoder::new(buf); decoder.load(&mut self.inst_holder); -- Gitee