diff --git a/ylong_http/src/body/mime/mimetype.rs b/ylong_http/src/body/mime/mimetype.rs index 0dfc9834db862749b6b45b33c269e59572cbd900..8c995ebdb09a41044b496d06f1496d26f4c64e5a 100644 --- a/ylong_http/src/body/mime/mimetype.rs +++ b/ylong_http/src/body/mime/mimetype.rs @@ -27,7 +27,7 @@ use crate::error::{ErrorKind, HttpError}; /// between: /// /// ```type/subtype``` -/// +/// /// It is case-insensitive but are traditionally written in lowercase, such as: /// ```application/octet-stream```. /// diff --git a/ylong_http/src/h3/mod.rs b/ylong_http/src/h3/mod.rs index b75d4c37d868748dffb0a012954444647527d202..d89ded094f9d595963db0ed03804de4dacfb273a 100644 --- a/ylong_http/src/h3/mod.rs +++ b/ylong_http/src/h3/mod.rs @@ -12,3 +12,8 @@ // limitations under the License. // TODO: `HTTP/3` Module. + +pub mod parts; +pub mod qpack; + +pub mod pseudo; diff --git a/ylong_http/src/h3/parts.rs b/ylong_http/src/h3/parts.rs new file mode 100644 index 0000000000000000000000000000000000000000..c1f1018ff6e7167f5b8b9dbff9f4a828434afa89 --- /dev/null +++ b/ylong_http/src/h3/parts.rs @@ -0,0 +1,72 @@ +// 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. + +use crate::h3::qpack::table::Field; +use crate::h3::pseudo::PseudoHeaders; +use crate::headers::Headers; + +/// HTTP2 HEADERS frame payload implementation. +#[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() + } +} diff --git a/ylong_http/src/h3/pseudo.rs b/ylong_http/src/h3/pseudo.rs new file mode 100644 index 0000000000000000000000000000000000000000..bbccdf5c5ea347a74a069c6f7d36342ad50b0151 --- /dev/null +++ b/ylong_http/src/h3/pseudo.rs @@ -0,0 +1,512 @@ +// 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, + method: Option, + path: Option, + scheme: Option, + status: Option, +} + +// 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 0000000000000000000000000000000000000000..e35185870f306fea22dde4a450a9dd6c677e13d7 --- /dev/null +++ b/ylong_http/src/h3/qpack/decoder.rs @@ -0,0 +1,971 @@ +// 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. + +use crate::h3::parts::Parts; +use crate::h3::qpack::error::ErrorCode::{DecompressionFailed, EncoderStreamError}; +use crate::h3::qpack::error::H3errorQpack; +use crate::h3::qpack::{ + DeltaBase, EncoderInstPrefixBit, EncoderInstruction, MidBit, ReprPrefixBit, Representation, + RequireInsertCount, +}; +use std::mem::take; + +use crate::h3::qpack::format::decoder::{ + EncInstDecoder, InstDecodeState, Name, ReprDecodeState, ReprDecoder, +}; +use crate::h3::qpack::integer::Integer; +use crate::h3::qpack::table::Field::Path; +use crate::h3::qpack::table::{DynamicTable, Field, TableSearcher}; + +/// An decoder is used to de-compress field in a compression format for efficiently representing +/// HTTP fields that is to be used in HTTP/3. This is a variation of HPACK compression that seeks +/// to reduce head-of-line blocking. +/// +/// # Examples(not run) +// ```no_run +// use crate::ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use crate::ylong_http::h3::qpack::decoder::QpackDecoder; +// use crate::ylong_http::test_util::decode; +// const MAX_HEADER_LIST_SIZE: usize = 16 << 20; +// +// // Required content: +// let mut dynamic_table = DynamicTable::with_empty(); +// let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); +// +// //decode instruction +// // convert hex string to dec-array +// let mut inst = decode("3fbd01c00f7777772e6578616d706c652e636f6dc10c2f73616d706c652f70617468").unwrap().as_slice().to_vec(); +// decoder.decode_ins(&mut inst); +// +// //decode field section +// // convert hex string to dec-array +// let mut repr = decode("03811011").unwrap().as_slice().to_vec(); +// decoder.decode_repr(&mut repr); +// +// ``` + +pub(crate) struct FiledLines { + parts: Parts, + header_size: usize, +} + +pub struct QpackDecoder<'a> { + field_list_size: usize, + // max header list size + table: &'a mut DynamicTable, + // dynamic table + repr_state: Option, + // field decode state + inst_state: Option, + // instruction decode state + lines: FiledLines, + // field lines, which is used to store the decoded field lines + base: usize, + // RFC required, got from field section prefix + require_insert_count: usize, // RFC required, got from field section prefix +} + +impl<'a> QpackDecoder<'a> { + + /// create a new decoder + /// # Examples(not run) + /// +// ```no_run +// use crate::ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use crate::ylong_http::h3::qpack::decoder::QpackDecoder; +// use crate::ylong_http::test_util::decode; +// const MAX_HEADER_LIST_SIZE: usize = 16 << 20; +// // Required content: +// let mut dynamic_table = DynamicTable::with_empty(); +// let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); +// ``` + pub fn new(field_list_size: usize, table: &'a mut DynamicTable) -> Self { + Self { + field_list_size, + table, + repr_state: None, + inst_state: None, + lines: FiledLines { + parts: Parts::new(), + header_size: 0, + }, + base: 0, + require_insert_count: 0, + } + } + + /// Users can call `decode_ins` multiple times to decode decoder instructions. + /// # Examples(not run) +// ```no_run +// use crate::ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use crate::ylong_http::h3::qpack::decoder::QpackDecoder; +// use crate::ylong_http::test_util::decode; +// const MAX_HEADER_LIST_SIZE: usize = 16 << 20; +// // Required content: +// let mut dynamic_table = DynamicTable::with_empty(); +// let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); +// //decode instruction +// // convert hex string to dec-array +// let mut inst = decode("3fbd01c00f7777772e6578616d706c652e636f6dc10c2f73616d706c652f70617468").unwrap().as_slice().to_vec(); +// decoder.decode_ins(&mut inst); +// ``` + pub fn decode_ins(&mut self, buf: &[u8]) -> Result<(), H3errorQpack> { + let mut decoder = EncInstDecoder::new(); + let mut updater = Updater::new(self.table); + let mut cnt = 0; + loop { + match decoder.decode(&buf[cnt..], &mut self.inst_state)? { + Some(inst) => match inst { + (offset, EncoderInstruction::SetCap { capacity }) => { + println!("set cap"); + cnt += offset; + updater.update_capacity(capacity)?; + } + ( + offset, + EncoderInstruction::InsertWithIndex { + mid_bit, + name, + value, + }, + ) => { + cnt += offset; + updater.update_table(mid_bit, name, value)?; + } + ( + offset, + EncoderInstruction::InsertWithLiteral { + mid_bit, + name, + value, + }, + ) => { + cnt += offset; + updater.update_table(mid_bit, name, value)?; + } + (offset, EncoderInstruction::Duplicate { index }) => { + cnt += offset; + updater.duplicate(index)?; + } + }, + None => return Result::Ok(()), + } + } + } + + /// 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 ... + /// +-------------------------------+ + /// # Examples(not run) +// ```no_run +// use crate::ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use crate::ylong_http::h3::qpack::decoder::QpackDecoder; +// use crate::ylong_http::test_util::decode; +// const MAX_HEADER_LIST_SIZE: usize = 16 << 20; +// // Required content: +// let mut dynamic_table = DynamicTable::with_empty(); +// let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); +// //decode field section +// // convert hex string to dec-array +// let mut repr = decode("03811011").unwrap().as_slice().to_vec(); +// decoder.decode_repr(&mut repr); +// ``` + pub fn decode_repr(&mut self, buf: &[u8]) -> Result<(), H3errorQpack> { + let mut decoder = ReprDecoder::new(); + let mut searcher = Searcher::new(self.field_list_size, self.table, &mut self.lines); + let mut cnt = 0; + loop { + match decoder.decode(&buf[cnt..], &mut self.repr_state)? { + Some(( + offset, + Representation::FieldSectionPrefix { + require_insert_count, + signal, + delta_base, + }, + )) => { + cnt += offset; + 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; + } + } + if signal { + self.base = self.require_insert_count - delta_base.0 - 1; + } else { + self.base = self.require_insert_count + delta_base.0; + } + searcher.base = self.base; + if self.require_insert_count > self.table.insert_count { + //todo:block + } + } + Some((offset, Representation::Indexed { mid_bit, index })) => { + cnt += offset; + searcher.search(Representation::Indexed { mid_bit, index })?; + } + Some((offset, Representation::IndexedWithPostIndex { index })) => { + cnt += offset; + searcher.search(Representation::IndexedWithPostIndex { index })?; + } + Some(( + offset, + Representation::LiteralWithIndexing { + mid_bit, + name, + value, + }, + )) => { + println!("offset:{}", offset); + cnt += offset; + searcher.search_literal_with_indexing(mid_bit, name, value)?; + } + Some(( + offset, + Representation::LiteralWithPostIndexing { + mid_bit, + name, + value, + }, + )) => { + cnt += offset; + searcher.search_literal_with_post_indexing(mid_bit, name, value)?; + } + Some(( + offset, + Representation::LiteralWithLiteralName { + mid_bit, + name, + value, + }, + )) => { + cnt += offset; + searcher.search_listeral_with_literal(mid_bit, name, value)?; + } + + None => { + return Result::Ok(()); + } + } + } + } + + /// 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+) | + /// +---+---------------------------+ + /// # Examples(not run) +// ```no_run +// use crate::ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use crate::ylong_http::h3::qpack::decoder::QpackDecoder; +// use crate::ylong_http::test_util::decode; +// const MAX_HEADER_LIST_SIZE: usize = 16 << 20; +// // Required content: +// let mut dynamic_table = DynamicTable::with_empty(); +// let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); +// //decode field section +// // convert hex string to dec-array +// let mut repr = decode("03811011").unwrap().as_slice().to_vec(); +// decoder.decode_repr(&mut repr); +// //finish +// let mut qpack_decoder_buf = [0u8;20]; +// decoder.finish(1,&mut qpack_decoder_buf); +// ``` + pub fn finish( + &mut self, + stream_id: usize, + buf: &mut [u8], + ) -> Result<(Parts, Option), H3errorQpack> { + if self.repr_state.is_some() { + return Err(H3errorQpack::ConnectionError(DecompressionFailed)); + } + self.lines.header_size = 0; + if self.require_insert_count > 0 { + let 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+) | + /// +---+---+-----------------------+ + /// # Examples(not run) +// ```no_run +// use crate::ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use crate::ylong_http::h3::qpack::decoder::QpackDecoder; +// use crate::ylong_http::test_util::decode; +// const MAX_HEADER_LIST_SIZE: usize = 16 << 20; +// // Required content: +// let mut dynamic_table = DynamicTable::with_empty(); +// let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); +// //decode field section +// // convert hex string to dec-array +// let mut repr = decode("03811011").unwrap().as_slice().to_vec(); +// decoder.decode_repr(&mut repr); +// //stream_cancel +// let mut qpack_decoder_buf = [0u8;20]; +// decoder.stream_cancel(1,&mut qpack_decoder_buf); +// ``` + pub fn stream_cancel( + &mut self, + stream_id: usize, + buf: &mut [u8], + ) -> Result { + let ack = Integer::index(0x40, stream_id, 0x3f); + let size = ack.encode(buf); + if let Ok(size) = size { + return Ok(size); + } + Err(H3errorQpack::ConnectionError(DecompressionFailed)) + } +} + +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<(), H3errorQpack> { + self.table.update_size(capacity); + Ok(()) + } + + fn update_table( + &mut self, + mid_bit: MidBit, + name: Name, + value: Vec, + ) -> Result<(), H3errorQpack> { + 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<(), H3errorQpack> { + let table_searcher = TableSearcher::new(self.table); + let (f, v) = table_searcher + .find_field_dynamic(self.table.insert_count - index - 1) + .ok_or(H3errorQpack::ConnectionError(EncoderStreamError))?; + 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), H3errorQpack> { + 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(H3errorQpack::ConnectionError(EncoderStreamError))? + } else { + searcher + .find_field_name_dynamic(insert_count - index - 1) + .ok_or(H3errorQpack::ConnectionError(EncoderStreamError))? + } + } + Name::Literal(octets) => Field::Other( + String::from_utf8(octets) + .map_err(|_| H3errorQpack::ConnectionError(EncoderStreamError))?, + ), + }; + let v = String::from_utf8(value) + .map_err(|_| H3errorQpack::ConnectionError(EncoderStreamError))?; + 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(field_list_size: usize, table: &'a DynamicTable, lines: &'a mut FiledLines) -> Self { + Self { + field_list_size, + table, + lines, + base: 0, + } + } + + fn search(&mut self, repr: Representation) -> Result<(), H3errorQpack> { + match repr { + 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: MidBit, index: usize) -> Result<(), H3errorQpack> { + 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(H3errorQpack::ConnectionError(DecompressionFailed))?; + + self.lines.parts.update(f, v); + Ok(()) + } else { + let (f, v) = table_searcher + .find_field_dynamic(self.base - index - 1) + .ok_or(H3errorQpack::ConnectionError(DecompressionFailed))?; + + self.lines.parts.update(f, v); + Ok(()) + } + } + + fn search_post_indexed(&mut self, index: usize) -> Result<(), H3errorQpack> { + let table_searcher = TableSearcher::new(self.table); + let (f, v) = table_searcher + .find_field_dynamic(self.base + index) + .ok_or(H3errorQpack::ConnectionError(DecompressionFailed))?; + self.check_field_list_size(&f, &v)?; + self.lines.parts.update(f, v); + Ok(()) + } + + fn search_literal_with_indexing( + &mut self, + mid_bit: MidBit, + name: Name, + value: Vec, + ) -> Result<(), H3errorQpack> { + 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<(), H3errorQpack> { + 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<(), H3errorQpack> { + 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), H3errorQpack> { + 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(H3errorQpack::ConnectionError(DecompressionFailed))? + } else { + searcher + .find_field_name_dynamic(self.base - index - 1) + .ok_or(H3errorQpack::ConnectionError(DecompressionFailed))? + } + } else { + let searcher = TableSearcher::new(self.table); + searcher + .find_field_name_dynamic(self.base + index) + .ok_or(H3errorQpack::ConnectionError(DecompressionFailed))? + } + } + Name::Literal(octets) => Field::Other( + String::from_utf8(octets) + .map_err(|_| H3errorQpack::ConnectionError(DecompressionFailed))?, + ), + }; + let v = String::from_utf8(value) + .map_err(|_| H3errorQpack::ConnectionError(DecompressionFailed))?; + 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<(), H3errorQpack> { + 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(H3errorQpack::ConnectionError(DecompressionFailed)) + } else { + Ok(()) + } + } +} + +fn field_line_length(key_size: usize, value_size: usize) -> usize { + key_size + value_size + 32 +} + +#[cfg(test)] +mod ut_qpack_decoder { + use crate::h3::qpack::format::decoder::ReprDecodeState; + use crate::h3::qpack::table::{DynamicTable, Field}; + use crate::h3::qpack::QpackDecoder; + use crate::test_util::decode; + + const MAX_HEADER_LIST_SIZE: usize = 16 << 20; + + #[test] + fn ut_qpack_decoder() { + rfc9204_test_cases(); + test_need_more(); + 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(); + decode_long_field(); + + fn get_state(state: &Option) { + match state { + Some(ReprDecodeState::FiledSectionPrefix(_)) => { + println!("FiledSectionPrefix"); + } + Some(ReprDecodeState::ReprIndex(_)) => { + println!("Indexed"); + } + Some(ReprDecodeState::ReprValueString(_)) => { + println!("ReprValueString"); + } + Some(ReprDecodeState::ReprNameAndValue(_)) => { + println!("ReprNameAndValue"); + } + None => { + println!("None"); + } + } + } + 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); + }; + } + + macro_rules! get_parts { + ($qpack: expr $(, $input: literal)*) => {{ + $( + let mut text = decode($input).unwrap().as_slice().to_vec(); + assert!($qpack.decode_repr(&mut text).is_ok()); + )* + let mut ack = [0u8; 20]; + match $qpack.finish(1,&mut ack) { + Ok((parts,_)) => parts, + Err(_) => panic!("QpackDecoder::finish() failed!"), + } + }}; + } + 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)*, + { $a: expr, $m: expr, $p: expr, $sc: expr, $st: expr }, + { $size: expr $(, $($k2: literal)? $($k3: ident)? => $v2: literal)* } $(,)? + ) => { + let mut _qpack = $qpack; + let (pseudo, _) = get_parts!(_qpack $(, $input)*).into_parts(); + check_pseudo!(pseudo, { $a, $m, $p, $sc, $st }); + }; + ( + $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)* }); + }; + } + + fn rfc9204_test_cases() { + literal_field_line_with_name_reference(); + dynamic_table(); + speculative_insert(); + duplicate_instruction_stream_cancellation(); + dynamic_table_insert_eviction(); + fn literal_field_line_with_name_reference() { + println!("run literal_field_line_with_name_reference"); + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(4096); + let decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + qpack_test_case!( + decoder, + "0000510b2f696e6465782e68746d6c", + { None, None, Some("/index.html"), None, None }, + { 0 } + ); + println!("passed"); + } + fn dynamic_table() { + println!("dynamic_table"); + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(4096); + let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let mut ins = + decode("3fbd01c00f7777772e6578616d706c652e636f6dc10c2f73616d706c652f70617468") + .unwrap() + .as_slice() + .to_vec(); + let _ = decoder.decode_ins(&mut ins); + get_state(&decoder.repr_state); + qpack_test_case!( + decoder, + "03811011", + { Some("www.example.com"), None, Some("/sample/path"), None, None }, + { 0 } + ); + } + fn speculative_insert() { + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(4096); + let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let mut ins = decode("4a637573746f6d2d6b65790c637573746f6d2d76616c7565") + .unwrap() + .as_slice() + .to_vec(); + let _ = decoder.decode_ins(&mut ins); + qpack_test_case!( + decoder, + "028010", + { "custom-key"=>"custom-value" }, + { 0 } + ); + } + fn duplicate_instruction_stream_cancellation() { + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(4096); + dynamic_table.update(Field::Authority, String::from("www.example.com")); + dynamic_table.update(Field::Path, String::from("/sample/path")); + dynamic_table.update( + Field::Other(String::from("custom-key")), + String::from("custom-value"), + ); + dynamic_table.known_received_count = 3; + let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let mut ins = decode("02").unwrap().as_slice().to_vec(); + let _ = decoder.decode_ins(&mut ins); + qpack_test_case!( + decoder, + "058010c180", + { Some("www.example.com"), None, Some("/"), None, None }, + { "custom-key"=>"custom-value" }, + { 0 } + ); + } + fn dynamic_table_insert_eviction() { + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(4096); + dynamic_table.update(Field::Authority, String::from("www.example.com")); + dynamic_table.update(Field::Path, String::from("/sample/path")); + dynamic_table.update( + Field::Other(String::from("custom-key")), + String::from("custom-value"), + ); + dynamic_table.update(Field::Authority, String::from("www.example.com")); + dynamic_table.known_received_count = 3; + let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let mut ins = decode("810d637573746f6d2d76616c756532") + .unwrap() + .as_slice() + .to_vec(); + let _ = decoder.decode_ins(&mut ins); + qpack_test_case!( + decoder, + "068111", + { "custom-key"=>"custom-value2" }, + { 0 } + ); + } + } + + fn test_need_more() { + println!("test_need_more"); + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(4096); + let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let mut text = decode("00").unwrap().as_slice().to_vec(); //510b2f696e6465782e68746d6c + println!("text={:?}", text); + let _ = decoder.decode_repr(&mut text); + get_state(&decoder.repr_state); + let mut text2 = decode("00510b2f696e6465782e68746d6c") + .unwrap() + .as_slice() + .to_vec(); + println!("text2={:?}", text2); + let _ = decoder.decode_repr(&mut text2); + } + + fn test_indexed_static() { + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(4096); + + qpack_test_case!( + QpackDecoder::new(MAX_HEADER_LIST_SIZE,&mut dynamic_table), + "0000d1", + { None, Some("GET"), None, None, None }, + { 0 } + ); + } + fn test_indexed_dynamic() { + // Test index "custom-field"=>"custom-value" in dynamic table + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(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) + "030081", + {"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_empty(); + dynamic_table.update_size(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) + "048211", + {"custom2-field"=>"custom2-value"}, + { 0 } + ); + } + fn test_literal_indexing_static() { + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(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_empty(); + dynamic_table.update_size(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) + "0300610d637573746f6d312d76616c7565", + {"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_empty(); + dynamic_table.update_size(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) + "0482010d637573746f6d312d76616c7565", + {"custom2-field"=>"custom1-value"}, + { 0 } + ); + } + + fn test_literal_with_literal_name() { + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(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_empty(); + dynamic_table.update_size(4096); + let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let mut ins = decode("3fbd01").unwrap().as_slice().to_vec(); + let _ = decoder.decode_ins(&mut ins); + assert_eq!(decoder.table.capacity(), 220); + } + + fn decode_long_field() { + let mut dynamic_table = DynamicTable::with_empty(); + dynamic_table.update_size(4096); + let mut decoder = QpackDecoder::new(MAX_HEADER_LIST_SIZE, &mut dynamic_table); + let mut repr = decode("ffffff01ffff01037fffff01") + .unwrap() + .as_slice() + .to_vec(); + let _ = decoder.decode_repr(&mut repr); + assert_eq!(decoder.base, 32382); + } + } +} diff --git a/ylong_http/src/h3/qpack/encoder.rs b/ylong_http/src/h3/qpack/encoder.rs new file mode 100644 index 0000000000000000000000000000000000000000..f5a1b305315db5121aa2e40f7e15a91f33595249 --- /dev/null +++ b/ylong_http/src/h3/qpack/encoder.rs @@ -0,0 +1,588 @@ +// 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. + +use crate::h3::parts::Parts; +use crate::h3::qpack::error::ErrorCode::DecoderStreamError; +use crate::h3::qpack::error::H3errorQpack; +use crate::h3::qpack::format::encoder::{ + DecInstDecoder, InstDecodeState, PartsIter, ReprEncodeState, SetCap, +}; +use crate::h3::qpack::format::ReprEncoder; +use crate::h3::qpack::integer::{Integer, IntegerEncoder}; +use crate::h3::qpack::table::{DynamicTable, Field}; +use crate::h3::qpack::{DecoderInstruction, PrefixMask}; +use std::collections::{HashMap, VecDeque}; + +/// An encoder is used to compress field in a compression format for efficiently representing +/// HTTP fields that is to be used in HTTP/3. This is a variation of HPACK compression that seeks +/// to reduce head-of-line blocking. +/// +/// # Examples +// ```no_run +// use crate::ylong_http::h3::qpack::encoder::QpackEncoder; +// use crate::ylong_http::h3::parts::Parts; +// use crate::ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use crate::ylong_http::test_util::decode; +// +// +// +// // the (field, value) is: ("custom-key", "custom-value2") +// // Required content: +// let mut encoder_buf = [0u8; 1024]; // QPACK stream providing control commands. +// let mut stream_buf = [0u8; 1024]; // Field section encoded in QPACK format. +// let mut encoder_cur = 0; // index of encoder_buf. +// let mut stream_cur = 0; // index of stream_buf. +// let mut table = DynamicTable::with_empty(); +// +// // create a new encoder. +// let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); +// +// // set dynamic table capacity. +// encoder_cur += encoder.set_capacity(220, &mut encoder_buf[encoder_buf..]); +// +// // set field section. +// let mut field = Parts::new(); +// field.update(Field::Other(String::from("custom-key")), String::from("custom-value")); +// encoder.set_parts(field); +// +// // encode field section. +// let (cur1, cur2, _) = encoder.encode(&mut encoder_buf[encoder_cur..], &mut stream_buf[stream_cur..]); +// encoder_cur += cur1; +// stream_cur += cur2; +// +// assert_eq!(stream_buf[..encoder_cur].to_vec().as_slice(), decode("028010").unwrap().as_slice()); +// assert_eq!(stream_buf[..stream_cur].to_vec().as_slice(), decode("4a637573746f6d2d6b65790c637573746f6d2d76616c7565").unwrap().as_slice()); +// +// +// ``` + +pub struct QpackEncoder<'a> { + table: &'a mut DynamicTable, + // Headers to be encode. + field_iter: Option, + // save the state of encoding field. + field_state: Option, + // save the state of decoding instructions. + inst_state: Option, + // list of fields to be inserted. + insert_list: VecDeque<(Field, String)>, + insert_length: usize, + // `RFC`: the number of insertions that the decoder needs to receive before it can decode the field section. + required_insert_count: usize, + + stream_id: usize, + // allow reference to the inserting field default is false. + allow_post: bool, + // RFC9204-2.1.1.1. if index QpackEncoder<'a> { + + /// create a new encoder. + /// #Examples +// ```no_run +// use ylong_http::h3::qpack::encoder::QpackEncoder; +// use ylong_http::h3::qpack::table::DynamicTable; +// let mut encoder_buf = [0u8; 1024]; // QPACK stream providing control commands. +// let mut stream_buf = [0u8; 1024]; // Field section encoded in QPACK format. +// let mut encoder_cur = 0; // index of encoder_buf. +// let mut stream_cur = 0; // index of stream_buf. +// let mut table = DynamicTable::with_empty(); +// +// // create a new encoder. +// let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); +// ``` + pub fn new( + table: &'a mut DynamicTable, + stream_id: usize, + allow_post: bool, + draining_index: usize, + ) -> QpackEncoder { + Self { + table, + field_iter: None, + field_state: None, + inst_state: None, + insert_list: VecDeque::new(), + insert_length: 0, + required_insert_count: 0, + stream_id, + allow_post, + draining_index, + } + } + + /// Set the maximum dynamic table size. + /// # Examples + /// ```no_run + /// use ylong_http::h3::qpack::encoder::QpackEncoder; + /// use ylong_http::h3::qpack::table::DynamicTable; + /// let mut encoder_buf = [0u8; 1024]; // QPACK stream providing control commands. + /// let mut stream_buf = [0u8; 1024]; // Field section encoded in QPACK format. + /// let mut encoder_cur = 0; // index of encoder_buf. + /// let mut stream_cur = 0; // index of stream_buf. + /// let mut table = DynamicTable::with_empty(); + /// let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); + /// let mut encoder_cur = encoder.set_capacity(220, &mut encoder_buf[..]); + /// ``` + pub fn set_capacity(&mut self, max_size: usize, encoder_buf: &mut [u8]) -> usize { + self.table.update_size(max_size); + if let Ok(cur) = SetCap::new(max_size).encode(&mut encoder_buf[..]) { + return cur; + } + 0 + } + + + /// Set the field section to be encoded. + /// # Examples +// ```no_run +// use ylong_http::h3::qpack::encoder::QpackEncoder; +// use ylong_http::h3::parts::Parts; +// use ylong_http::h3::qpack::table::{DynamicTable, Field}; +// let mut table = DynamicTable::with_empty(); +// let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); +// let mut parts = Parts::new(); +// parts.update(Field::Other(String::from("custom-key")), String::from("custom-value")); +// encoder.set_parts(parts); +// ``` + pub fn set_parts(&mut self, parts: Parts) { + self.field_iter = Some(PartsIter::new(parts)); + } + + fn ack(&mut self, stream_id: usize) -> Result, H3errorQpack> { + assert_eq!(stream_id, self.stream_id); + + if self.table.known_received_count < self.required_insert_count { + self.table.known_received_count = self.required_insert_count; + } else { + return Err(H3errorQpack::ConnectionError(DecoderStreamError)); + } + + Ok(Some(DecoderInst::Ack)) + } + + /// Users can call `decode_ins` multiple times to decode decoder instructions. + /// # Return + /// `Ok(None)` means that the decoder instruction is not complete. + /// # Examples +// ```no_run +// use ylong_http::h3::qpack::encoder::QpackEncoder; +// use ylong_http::h3::parts::Parts; +// use ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use ylong_http::test_util::decode; +// let mut table = DynamicTable::with_empty(); +// let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); +// let _ = encoder.decode_ins(&mut decode("80").unwrap().as_slice()); +// ``` + pub fn decode_ins(&mut self, buf: &[u8]) -> Result, H3errorQpack> { + let mut decoder = DecInstDecoder::new(buf); + + match decoder.decode(&mut self.inst_state)? { + Some(DecoderInstruction::Ack { stream_id }) => self.ack(stream_id), + //todo: stream cancel + Some(DecoderInstruction::StreamCancel { stream_id }) => { + assert_eq!(stream_id, self.stream_id); + Ok(Some(DecoderInst::StreamCancel)) + } + //todo: insert count increment + Some(DecoderInstruction::InsertCountIncrement { increment }) => { + self.table.known_received_count += increment; + Ok(Some(DecoderInst::InsertCountIncrement)) + } + None => Ok(None), + } + } + + fn get_prefix(&self, prefix_buf: &mut [u8]) -> usize { + let mut cur_prefix = 0; + let mut wire_ric = 0; + if self.required_insert_count != 0 { + wire_ric = self.required_insert_count % (2 * self.table.max_entries()) + 1; + } + + cur_prefix += Integer::index(0x00, wire_ric, 0xff) + .encode(&mut prefix_buf[..]) + .unwrap_or(0); + let base = self.table.insert_count; + println!("base: {}", base); + println!("required_insert_count: {}", self.required_insert_count); + if base >= self.required_insert_count { + cur_prefix += Integer::index(0x00, base - self.required_insert_count, 0x7f) + .encode(&mut prefix_buf[cur_prefix..]) + .unwrap_or(0); + } else { + cur_prefix += Integer::index(0x80, self.required_insert_count - base - 1, 0x7f) + .encode(&mut prefix_buf[cur_prefix..]) + .unwrap_or(0); + } + cur_prefix + } + /// Users can call `encode` multiple times to encode multiple complete field sections. + /// # Examples +// ```no_run +// use ylong_http::h3::qpack::encoder::QpackEncoder; +// use ylong_http::h3::parts::Parts; +// use ylong_http::h3::qpack::table::{DynamicTable, Field}; +// use ylong_http::test_util::decode; +// let mut encoder_buf = [0u8; 1024]; // QPACK stream providing control commands. +// let mut stream_buf = [0u8; 1024]; // Field section encoded in QPACK format. +// let mut encoder_cur = 0; // index of encoder_buf. +// let mut stream_cur = 0; // index of stream_buf. +// let mut table = DynamicTable::with_empty(); +// let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); +// let mut parts = Parts::new(); +// parts.update(Field::Other(String::from("custom-key")), String::from("custom-value")); +// encoder.set_parts(parts); +// let (cur1, cur2, _) = encoder.encode(&mut encoder_buf[encoder_cur..], &mut stream_buf[stream_cur..]); +// encoder_cur += cur1; +// stream_cur += cur2; +// ``` + pub fn encode( + &mut self, + encoder_buf: &mut [u8], //instructions encoded results + stream_buf: &mut [u8], //headers encoded results + ) -> (usize, usize, Option<([u8; 1024], usize)>) { + let (mut cur_encoder, mut cur_stream) = (0, 0); + if self.is_finished() { + // denote an end of field section + // self.stream_reference.push_back(None); + //todo: size of prefix_buf + let mut prefix_buf = [0u8; 1024]; + let cur_prefix = self.get_prefix(&mut prefix_buf[0..]); + for (field, value) in self.insert_list.iter() { + self.table.update(field.clone(), value.clone()); + } + (cur_encoder, cur_stream, Some((prefix_buf, cur_prefix))) + } else { + let mut encoder = ReprEncoder::new( + self.table, + self.draining_index, + self.allow_post, + &mut self.insert_length, + ); + (cur_encoder, cur_stream) = encoder.encode( + &mut self.field_iter, + &mut self.field_state, + &mut encoder_buf[0..], + &mut stream_buf[0..], + &mut self.insert_list, + &mut self.required_insert_count, + ); + (cur_encoder, cur_stream, None) + } + } + + /// Check the previously set `Parts` if encoding is complete. + pub(crate) fn is_finished(&self) -> bool { + self.field_iter.is_none() && self.field_state.is_none() + } +} + +pub enum DecoderInst { + Ack, + StreamCancel, + InsertCountIncrement, +} + +#[cfg(test)] +mod ut_qpack_encoder { + use crate::h3::parts::Parts; + use crate::h3::qpack::encoder; + use crate::h3::qpack::encoder::QpackEncoder; + use crate::h3::qpack::table::{DynamicTable, Field}; + use crate::test_util::decode; + macro_rules! qpack_test_cases { + ($enc: expr,$encoder_buf:expr,$encoder_cur:expr, $len: expr, $res: literal,$encoder_res: literal, $size: expr, { $($h: expr, $v: expr $(,)?)*} $(,)?) => { + let mut _encoder = $enc; + let mut stream_buf = [0u8; $len]; + let mut stream_cur = 0; + $( + let mut parts = Parts::new(); + parts.update($h, $v); + _encoder.set_parts(parts); + let (cur1,cur2,_) = _encoder.encode(&mut $encoder_buf[$encoder_cur..],&mut stream_buf[stream_cur..]); + $encoder_cur += cur1; + stream_cur += cur2; + )* + let (cur1, cur2, prefix) = _encoder.encode(&mut $encoder_buf[$encoder_cur..],&mut stream_buf[stream_cur..]); + $encoder_cur += cur1; + stream_cur += cur2; + if let Some((prefix_buf,cur_prefix)) = prefix{ + stream_buf.copy_within(0..stream_cur,cur_prefix); + stream_buf[..cur_prefix].copy_from_slice(&prefix_buf[..cur_prefix]); + stream_cur += cur_prefix; + } + println!("stream_buf: {:#?}",stream_buf); + let result = decode($res).unwrap(); + if let Some(res) = decode($encoder_res){ + assert_eq!($encoder_buf[..$encoder_cur].to_vec().as_slice(), res.as_slice()); + } + assert_eq!(stream_cur, $len); + assert_eq!(stream_buf.as_slice(), result.as_slice()); + assert_eq!(_encoder.table.size(), $size); + } + } + #[test] + /// The encoder sends an encoded field section containing a literal representation of a field + /// with a static name reference. + fn literal_field_line_with_name_reference() { + println!("literal_field_line_with_name_reference"); + let mut encoder_buf = [0u8; 1024]; + let mut table = DynamicTable::with_empty(); + let mut encoder = QpackEncoder::new(&mut table, 0, false, 0); + let mut encoder_cur = encoder.set_capacity(0, &mut encoder_buf[..]); + qpack_test_cases!( + encoder, + encoder_buf, + encoder_cur, + 15, "0000510b2f696e6465782e68746d6c", + "20", + 0, + { + Field::Path, + String::from("/index.html"), + }, + ); + } + + #[test] + ///The encoder sets the dynamic table capacity, inserts a header with a dynamic name + /// reference, then sends a potentially blocking, encoded field section referencing + /// this new entry. The decoder acknowledges processing the encoded field section, + /// which implicitly acknowledges all dynamic table insertions up to the Required + /// Insert Count. + fn dynamic_table() { + let mut encoder_buf = [0u8; 1024]; + let mut table = DynamicTable::with_empty(); + let mut encoder = QpackEncoder::new(&mut table, 0, true, 0); + let mut encoder_cur = encoder.set_capacity(220, &mut encoder_buf[..]); + qpack_test_cases!( + encoder, + encoder_buf, + encoder_cur, + 4, "03811011", + "3fbd01c00f7777772e6578616d706c652e636f6dc10c2f73616d706c652f70617468", + 106, + { + Field::Authority, + String::from("www.example.com"), + Field::Path, + String::from("/sample/path"), + }, + ); + } + + #[test] + ///The encoder inserts a header into the dynamic table with a literal name. + /// The decoder acknowledges receipt of the entry. The encoder does not send any + /// encoded field sections. + fn speculative_insert() { + let mut encoder_buf = [0u8; 1024]; + let mut table = DynamicTable::with_empty(); + let mut encoder = QpackEncoder::new(&mut table, 0, true, 0); + let _ = encoder.set_capacity(220, &mut encoder_buf[..]); + let mut encoder_cur = 0; + qpack_test_cases!( + encoder, + encoder_buf, + encoder_cur, + 3, "028010", + "4a637573746f6d2d6b65790c637573746f6d2d76616c7565", + 54, + { + Field::Other(String::from("custom-key")), + String::from("custom-value"), + }, + ); + } + + #[test] + fn duplicate_instruction_stream_cancellation() { + let mut encoder_buf = [0u8; 1024]; + let mut table = DynamicTable::with_empty(); + let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); + let _ = encoder.set_capacity(4096, &mut encoder_buf[..]); + encoder + .table + .update(Field::Authority, String::from("www.example.com")); + encoder + .table + .update(Field::Path, String::from("/sample/path")); + encoder.table.update( + Field::Other(String::from("custom-key")), + String::from("custom-value"), + ); + encoder.required_insert_count = 3; + let mut encoder_cur = 0; + qpack_test_cases!( + encoder, + encoder_buf, + encoder_cur, + 5, "050080c181", + "02", + 274, + { + Field::Authority, + String::from("www.example.com"), + Field::Path, + String::from("/"), + Field::Other(String::from("custom-key")), + String::from("custom-value") + }, + ); + } + + #[test] + fn dynamic_table_insert_eviction() { + let mut encoder_buf = [0u8; 1024]; + let mut table = DynamicTable::with_empty(); + let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); + let _ = encoder.set_capacity(4096, &mut encoder_buf[..]); + encoder + .table + .update(Field::Authority, String::from("www.example.com")); + encoder + .table + .update(Field::Path, String::from("/sample/path")); + encoder.table.update( + Field::Other(String::from("custom-key")), + String::from("custom-value"), + ); + encoder + .table + .update(Field::Authority, String::from("www.example.com")); + encoder.required_insert_count = 3; //acked + let mut encoder_cur = 0; + qpack_test_cases!( + encoder, + encoder_buf, + encoder_cur, + 3, "040183", + "810d637573746f6d2d76616c756532", + 272, + { + Field::Other(String::from("custom-key")), + String::from("custom-value2") + }, + ); + } + + #[test] + fn test_ack() { + let mut encoder_buf = [0u8; 1024]; + let mut table = DynamicTable::with_empty(); + let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); + let mut encoder_cur = encoder.set_capacity(4096, &mut encoder_buf[..]); + + let field_list: [(Field, String); 3] = [ + (Field::Authority, String::from("www.example.com")), + (Field::Path, String::from("/sample/path")), + ( + Field::Other(String::from("custom-key")), + String::from("custom-value"), + ), + ]; + let mut stream_cur = 0; + for (field, value) in field_list.iter() { + let mut parts = Parts::new(); + parts.update(field.clone(), value.clone()); + encoder.set_parts(parts); + let mut stream_buf = [0u8; 1024]; + let (cur1, cur2, _) = encoder.encode( + &mut encoder_buf[encoder_cur..], + &mut stream_buf[stream_cur..], + ); + encoder_cur += cur1; + stream_cur += cur2; + } + let _ = encoder.decode_ins(decode("80").unwrap().as_slice()); + assert_eq!(encoder.table.known_received_count, 3); + } + + #[test] + fn encode_post_name() { + let mut encoder_buf = [0u8; 1024]; + let mut table = DynamicTable::with_empty(); + let mut encoder = QpackEncoder::new(&mut table, 0, true, 1); + let _ = encoder.set_capacity(60, &mut encoder_buf[..]); + let mut encoder_cur = 0; + let mut stream_buf = [0u8; 100]; + let mut stream_cur = 0; + let mut parts = Parts::new(); + parts.update( + Field::Other(String::from("custom-key")), + String::from("custom-value1"), + ); + encoder.set_parts(parts); + let (cur1, cur2, _) = encoder.encode( + &mut encoder_buf[encoder_cur..], + &mut stream_buf[stream_cur..], + ); + encoder_cur += cur1; + stream_cur += cur2; + let mut parts = Parts::new(); + parts.update( + Field::Other(String::from("custom-key")), + String::from("custom-value2"), + ); + encoder.set_parts(parts); + let (cur1, cur2, _) = encoder.encode( + &mut encoder_buf[encoder_cur..], + &mut stream_buf[stream_cur..], + ); + encoder_cur += cur1; + stream_cur += cur2; + assert_eq!( + [16, 0, 13, 99, 117, 115, 116, 111, 109, 45, 118, 97, 108, 117, 101, 50], + stream_buf[..stream_cur] + ); + assert_eq!( + [ + 74, 99, 117, 115, 116, 111, 109, 45, 107, 101, 121, 13, 99, 117, 115, 116, 111, + 109, 45, 118, 97, 108, 117, 101, 49 + ], + encoder_buf[..encoder_cur] + ) + } + #[test] + fn test_indexing_with_litreal() { + let mut encoder_buf = [0u8; 1024]; + let mut table = DynamicTable::with_empty(); + let mut encoder = QpackEncoder::new(&mut table, 0, false, 1); + let _ = encoder.set_capacity(60, &mut encoder_buf[..]); + let encoder_cur = 0; + let mut stream_buf = [0u8; 100]; + let mut stream_cur = 0; + let mut parts = Parts::new(); + parts.update( + Field::Other(String::from("custom-key")), + String::from("custom-value1"), + ); + encoder.set_parts(parts); + let (_, cur2, _) = encoder.encode( + &mut encoder_buf[encoder_cur..], + &mut stream_buf[stream_cur..], + ); + stream_cur += cur2; + + assert_eq!( + [ + 39, 3, 99, 117, 115, 116, 111, 109, 45, 107, 101, 121, 13, 99, 117, 115, 116, 111, + 109, 45, 118, 97, 108, 117, 101, 49 + ], + stream_buf[..stream_cur] + ); + } +} diff --git a/ylong_http/src/h3/qpack/error.rs b/ylong_http/src/h3/qpack/error.rs new file mode 100644 index 0000000000000000000000000000000000000000..aa3029ff61d4488e80a492a3504b1d87d915f097 --- /dev/null +++ b/ylong_http/src/h3/qpack/error.rs @@ -0,0 +1,24 @@ +// 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. +pub enum H3errorQpack { + ConnectionError(ErrorCode), +} + +#[derive(Debug, Eq, PartialEq, Clone)] +pub enum ErrorCode { + DecompressionFailed = 0x0200, + + EncoderStreamError = 0x0201, + + DecoderStreamError = 0x0202, +} diff --git a/ylong_http/src/h3/qpack/format/decoder.rs b/ylong_http/src/h3/qpack/format/decoder.rs new file mode 100644 index 0000000000000000000000000000000000000000..fff4f8d77f6a7b5d3983a6a8ccfb78a09c6ba863 --- /dev/null +++ b/ylong_http/src/h3/qpack/format/decoder.rs @@ -0,0 +1,858 @@ +// 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. +use crate::h3::qpack::error::ErrorCode::DecompressionFailed; +use crate::h3::qpack::error::{ErrorCode, H3errorQpack}; +use crate::h3::qpack::format::decoder::DecResult::Error; +use crate::h3::qpack::integer::IntegerDecoder; +use crate::h3::qpack::{ + DeltaBase, EncoderInstPrefixBit, EncoderInstruction, MidBit, PrefixMask, ReprPrefixBit, + Representation, RequireInsertCount, +}; +use crate::huffman::HuffmanDecoder; +use std::cmp::Ordering; +use std::marker::PhantomData; + +pub(crate) struct EncInstDecoder; + +impl EncInstDecoder { + pub(crate) fn new() -> Self { + Self {} + } + + /// Decodes `buf`. Every time users call `decode`, it will try to + /// decode a `EncoderInstruction`. + /// # Example +// ```no_run +// use crate::h3::qpack::format::decoder::EncInstDecoder; +// let mut decoder = EncInstDecoder::new(); +// +// ``` + pub(crate) fn decode( + &mut self, + buf: &[u8], + inst_state: &mut Option, + ) -> Result, H3errorQpack> { + if buf.is_empty() { + return Ok(None); + } + + match inst_state + .take() + .unwrap_or_else(|| InstDecodeState::EncInstIndex(EncInstIndex::new())) + .decode(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!("need more"); + *inst_state = Some(state); + Ok(None) + } + DecResult::Decoded((buf_index, repr)) => Ok(Some((buf_index, repr))), + + DecResult::Error(error) => Err(error), + } + } +} + +pub(crate) struct ReprDecoder; + +impl ReprDecoder { + /// Creates a new `ReprDecoder` whose `state` is `None`. + pub(crate) fn new() -> Self { + Self {} + } + + /// Decodes `self.buf`. Every time users call `decode`, it will try to + /// decode a `Representation`. + pub(crate) fn decode( + &mut self, + buf: &[u8], + repr_state: &mut Option, + ) -> Result, H3errorQpack> { + // If buf is empty, leave the state unchanged. + let buf_len = buf.len(); + if buf.is_empty() { + return Ok(None); + } + match repr_state + .take() + .unwrap_or_else(|| ReprDecodeState::FiledSectionPrefix(FiledSectionPrefix::new())) + .decode(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!("need more"); + *repr_state = Some(state); + Ok(None) + } + DecResult::Decoded((buf_index, repr)) => { + if buf_index >= buf_len { + return Ok(Some((buf_index, repr))); + } + *repr_state = Some(ReprDecodeState::ReprIndex(ReprIndex::new())); + Ok(Some((buf_index, repr))) + } + + DecResult::Error(error) => Err(error), + } + } +} +macro_rules! return_res { + ($res: expr ,$buf_index: expr) => { + if let DecResult::Decoded((cnt, repr)) = $res { + return DecResult::Decoded((cnt + $buf_index, repr)); + } else { + $res + } + }; +} + +macro_rules! state_def { + ($name: ident, $decoded: ty, $($state: ident),* $(,)?) => { + pub(crate) enum $name { + $( + $state($state), + )* + } + + impl $name { + fn decode(self, buf: & [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!( + FSPInner, + (usize, RequireInsertCount, bool, DeltaBase), + FSPTwoIntergers, +); +state_def!( + InstDecodeState, + (usize, EncoderInstruction), + EncInstIndex, + InstValueString, + InstNameAndValue, +); + +state_def!( + ReprDecodeState, + (usize, Representation), + FiledSectionPrefix, + ReprIndex, + ReprValueString, + ReprNameAndValue, +); +state_def!( + InstIndexInner, + (usize, EncoderInstPrefixBit, MidBit, usize), + InstFirstByte, + InstTrailingBytes +); +state_def!( + ReprIndexInner, + (usize, ReprPrefixBit, MidBit, usize), + ReprFirstByte, + ReprTrailingBytes +); + +state_def!( + LiteralString, + (usize, Vec), + LengthFirstByte, + LengthTrailingBytes, + AsciiStringBytes, + HuffmanStringBytes, +); + +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: &[u8]) -> DecResult<(usize, Representation), ReprDecodeState> { + match self.inner.decode(buf) { + DecResult::Decoded((buf_index, ric, signal, delta_base)) => DecResult::Decoded(( + buf_index, + Representation::FieldSectionPrefix { + require_insert_count: ric, + signal, + delta_base, + }, + )), + DecResult::NeedMore(inner) => { + DecResult::NeedMore(FiledSectionPrefix::from_inner(inner).into()) + } + DecResult::Error(e) => e.into(), + } + } +} + +pub(crate) struct EncInstIndex { + inner: InstIndexInner, +} + +impl EncInstIndex { + fn new() -> Self { + Self::from_inner(InstFirstByte.into()) + } + fn from_inner(inner: InstIndexInner) -> Self { + Self { inner } + } + fn decode(self, buf: &[u8]) -> DecResult<(usize, EncoderInstruction), InstDecodeState> { + match self.inner.decode(buf) { + DecResult::Decoded((buf_index, EncoderInstPrefixBit::SETCAP, _, index)) => { + DecResult::Decoded((buf_index, EncoderInstruction::SetCap { capacity: index })) + } + DecResult::Decoded(( + buf_index, + EncoderInstPrefixBit::INSERTWITHINDEX, + mid_bit, + index, + )) => { + let res = InstValueString::new( + EncoderInstPrefixBit::INSERTWITHINDEX, + mid_bit, + Name::Index(index), + ) + .decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + DecResult::Decoded(( + buf_index, + EncoderInstPrefixBit::INSERTWITHLITERAL, + mid_bit, + namelen, + )) => { + let res = InstNameAndValue::new( + EncoderInstPrefixBit::INSERTWITHLITERAL, + mid_bit, + namelen, + ) + .decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + DecResult::Decoded((remain_buf, EncoderInstPrefixBit::DUPLICATE, _, index)) => { + DecResult::Decoded((remain_buf, EncoderInstruction::Duplicate { index })) + } + DecResult::NeedMore(inner) => { + DecResult::NeedMore(EncInstIndex::from_inner(inner).into()) + } + DecResult::Error(e) => e.into(), + _ => DecResult::Error(H3errorQpack::ConnectionError( + ErrorCode::DecompressionFailed, + )), + } + } +} + +pub(crate) struct ReprIndex { + inner: ReprIndexInner, +} + +impl ReprIndex { + fn new() -> Self { + Self::from_inner(ReprFirstByte.into()) + } + fn from_inner(inner: ReprIndexInner) -> Self { + Self { inner } + } + fn decode(self, buf: &[u8]) -> DecResult<(usize, Representation), ReprDecodeState> { + match self.inner.decode(buf) { + DecResult::Decoded((buf_index, ReprPrefixBit::INDEXED, mid_bit, index)) => { + DecResult::Decoded((buf_index, Representation::Indexed { mid_bit, index })) + } + DecResult::Decoded((buf_index, ReprPrefixBit::INDEXEDWITHPOSTINDEX, _, index)) => { + DecResult::Decoded((buf_index, Representation::IndexedWithPostIndex { index })) + } + DecResult::Decoded((buf_index, ReprPrefixBit::LITERALWITHINDEXING, mid_bit, index)) => { + let res = ReprValueString::new( + ReprPrefixBit::LITERALWITHINDEXING, + mid_bit, + Name::Index(index), + ) + .decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + DecResult::Decoded(( + buf_index, + ReprPrefixBit::LITERALWITHPOSTINDEXING, + mid_bit, + index, + )) => { + let res = ReprValueString::new( + ReprPrefixBit::LITERALWITHPOSTINDEXING, + mid_bit, + Name::Index(index), + ) + .decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + DecResult::Decoded(( + buf_index, + ReprPrefixBit::LITERALWITHLITERALNAME, + mid_bit, + namelen, + )) => { + let res = + ReprNameAndValue::new(ReprPrefixBit::LITERALWITHLITERALNAME, mid_bit, namelen) + .decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + DecResult::NeedMore(inner) => DecResult::NeedMore(ReprIndex::from_inner(inner).into()), + DecResult::Error(e) => e.into(), + _ => DecResult::Error(H3errorQpack::ConnectionError( + ErrorCode::DecompressionFailed, + )), + } + } +} + +pub(crate) struct FSPTwoIntergers; + +impl FSPTwoIntergers { + fn decode( + self, + buf: &[u8], + ) -> DecResult<(usize, RequireInsertCount, bool, DeltaBase), FSPInner> { + let mut buf_index = 1; + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + let buf_len = buf.len(); + let mask = PrefixMask::REQUIREINSERTCOUNT; + let ric = match IntegerDecoder::first_byte(buf[buf_index - 1], mask.0) { + Ok(ric) => ric, + Err(mut int) => { + let res: usize; + + loop { + // If `buf` has been completely decoded here, return the current state. + buf_index += 1; + if buf_index >= buf_len { + return DecResult::NeedMore(self.into()); + } + // Updates trailing bytes until we get the index. + match int.next_byte(buf[buf_index - 1]) { + Ok(None) => {} + Ok(Some(index)) => { + res = index; + break; + } + Err(e) => return e.into(), + } + } + res + } + }; + buf_index += 1; + if buf_index >= buf_len { + return DecResult::NeedMore(self.into()); + } + let byte = buf[buf_index - 1]; + let signal = (byte & 0x80) != 0; + let mask = PrefixMask::DELTABASE; + let delta_base = match IntegerDecoder::first_byte(byte, mask.0) { + Ok(delta_base) => delta_base, + Err(mut int) => { + let res: usize; + loop { + // If `buf` has been completely decoded here, return the current state. + buf_index += 1; + if buf_index >= buf_len { + return DecResult::NeedMore(self.into()); + } + + // Updates trailing bytes until we get the index. + match int.next_byte(buf[buf_index - 1]) { + Ok(None) => {} + Ok(Some(index)) => { + res = index; + break; + } + Err(e) => return e.into(), + } + } + res + } + }; + + DecResult::Decoded(( + buf_index, + RequireInsertCount(ric), + signal, + DeltaBase(delta_base), + )) + } +} + +macro_rules! decode_first_byte { + ($struct_name:ident, $prefix_type:ty, $inner_type:ty,$trailing_bytes:ty) => { + pub(crate) struct $struct_name; + + impl $struct_name { + fn decode( + self, + buf: &[u8], + ) -> DecResult<(usize, $prefix_type, MidBit, usize), $inner_type> { + // If `buf` has been completely decoded here, return the current state. + let buf_index = 1; + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + + let prefix = <$prefix_type>::from_u8(buf[buf_index - 1]); + let mid_bit = prefix.prefix_midbit_value(buf[buf_index - 1]); + let mask = prefix.prefix_index_mask(); + + match IntegerDecoder::first_byte(buf[buf_index - 1], mask.0) { + // Return the PrefixBit and index part value. + Ok(idx) => DecResult::Decoded((buf_index, prefix, 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) => { + let res = + <$trailing_bytes>::new(prefix, mid_bit, int).decode(&buf[buf_index..]); + if let DecResult::Decoded((cnt, prefix, mid_bit, int)) = res { + return DecResult::Decoded((cnt + buf_index, prefix, mid_bit, int)); + } else { + return res; + } + } + } + } + } + }; +} + +decode_first_byte!( + InstFirstByte, + EncoderInstPrefixBit, + InstIndexInner, + InstTrailingBytes +); +decode_first_byte!( + ReprFirstByte, + ReprPrefixBit, + ReprIndexInner, + ReprTrailingBytes +); + +macro_rules! trailing_bytes_decoder { + ($struct_name:ident, $prefix_type:ty, $inner_type:ty) => { + pub(crate) struct $struct_name { + prefix: $prefix_type, + mid_bit: MidBit, + index: IntegerDecoder, + } + + impl $struct_name { + fn new(prefix: $prefix_type, mid_bit: MidBit, index: IntegerDecoder) -> Self { + Self { + prefix, + mid_bit, + index, + } + } + + fn decode( + mut self, + buf: &[u8], + ) -> DecResult<(usize, $prefix_type, MidBit, usize), $inner_type> { + let mut buf_index = 1; + let buf_len = buf.len(); + loop { + // If `buf` has been completely decoded here, return the current state. + if buf_index > buf_len { + return DecResult::NeedMore(self.into()); + } + + // Updates trailing bytes until we get the index. + match self.index.next_byte(buf[buf_index - 1]) { + Ok(None) => {} + Ok(Some(index)) => { + return DecResult::Decoded(( + buf_index, + self.prefix, + self.mid_bit, + index, + )); + } + Err(e) => return e.into(), + } + buf_index += 1; + } + } + } + }; +} + +trailing_bytes_decoder!(InstTrailingBytes, EncoderInstPrefixBit, InstIndexInner); +trailing_bytes_decoder!(ReprTrailingBytes, ReprPrefixBit, ReprIndexInner); + +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: &[u8]) -> DecResult<(usize, Vec), LiteralString> { + let mut buf_index = 1; + let buf_len = buf.len(); + loop { + if buf_index >= buf_len { + return DecResult::NeedMore(self.into()); + } + + match (self.length.next_byte(buf[buf_index - 1]), self.is_huffman) { + (Ok(None), _) => {} + (Err(e), _) => return e.into(), + (Ok(Some(length)), true) => { + let res = HuffmanStringBytes::new(length).decode(&buf[buf_index..]); + if let DecResult::Decoded((cnt, vec)) = res { + return DecResult::Decoded((cnt + buf_index, vec)); + } else { + return res; + } + } + (Ok(Some(length)), false) => { + let res = AsciiStringBytes::new(length).decode(&buf[buf_index..]); + if let DecResult::Decoded((cnt, vec)) = res { + return DecResult::Decoded((cnt + buf_index, vec)); + } else { + return res; + } + } + } + buf_index += 1; + } + } +} + +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: &[u8]) -> DecResult<(usize, Vec), 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]); + DecResult::Decoded((pos, self.octets)) + } + Ordering::Less => { + self.octets.extend_from_slice(buf); + // let (_, mut remain_buf) = buf.split_at_mut(buf.len()); + DecResult::NeedMore(self.into()) + } + } + } +} + +macro_rules! name_and_value_decoder { + ($struct_name:ident, $prefix_type:ty, $inner_type:ty, $output_type:ty, $state_type:ty,$value_string:ty) => { + pub(crate) struct $struct_name { + prefix: $prefix_type, + mid_bit: MidBit, + inner: $inner_type, + } + + impl $struct_name { + fn new(prefix: $prefix_type, mid_bit: MidBit, namelen: usize) -> Self { + Self::from_inner(prefix, mid_bit, AsciiStringBytes::new(namelen).into()) + } + + fn from_inner(prefix: $prefix_type, mid_bit: MidBit, inner: $inner_type) -> Self { + Self { + prefix, + mid_bit, + inner, + } + } + + fn decode(self, buf: &[u8]) -> DecResult<(usize, $output_type), $state_type> { + match self.inner.decode(buf) { + DecResult::Decoded((buf_index, octets)) => { + let res = + <$value_string>::new(self.prefix, self.mid_bit, Name::Literal(octets)) + .decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + DecResult::NeedMore(inner) => DecResult::NeedMore( + Self::from_inner(self.prefix, self.mid_bit, inner).into(), + ), + DecResult::Error(e) => e.into(), + } + } + } + }; +} + +name_and_value_decoder!( + InstNameAndValue, + EncoderInstPrefixBit, + LiteralString, + EncoderInstruction, + InstDecodeState, + InstValueString +); +name_and_value_decoder!( + ReprNameAndValue, + ReprPrefixBit, + LiteralString, + Representation, + ReprDecodeState, + ReprValueString +); + +pub(crate) struct LengthFirstByte; + +impl LengthFirstByte { + fn decode(self, buf: &[u8]) -> DecResult<(usize, Vec), LiteralString> { + let buf_index = 1; + if buf.is_empty() { + return DecResult::NeedMore(self.into()); + } + + match ( + IntegerDecoder::first_byte(buf[buf_index - 1], 0x7f), + (buf[buf_index - 1] & 0x80) == 0x80, + ) { + (Ok(len), true) => { + let res = HuffmanStringBytes::new(len).decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + (Ok(len), false) => { + let res = AsciiStringBytes::new(len).decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + (Err(int), huffman) => { + let res = LengthTrailingBytes::new(huffman, int).decode(&buf[buf_index..]); + return_res!(res, buf_index) + } + } + } +} + +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: &[u8]) -> DecResult<(usize, Vec), 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 H3errorQpack::ConnectionError(DecompressionFailed).into(); + } + // let (_, mut remain_buf) = buf.split_at_mut(pos); + match self.huffman.finish() { + Ok(vec) => DecResult::Decoded((pos, vec)), + Err(_) => H3errorQpack::ConnectionError(DecompressionFailed).into(), + } + } + Ordering::Less => { + if self.huffman.decode(buf).is_err() { + return H3errorQpack::ConnectionError(DecompressionFailed).into(); + } + self.read += buf.len(); + // let (_, mut remain_buf) = buf.split_at_mut(buf.len()); + DecResult::NeedMore(self.into()) + } + } + } +} + +#[derive(Clone)] +pub(crate) enum Name { + Index(usize), + Literal(Vec), +} + +pub(crate) struct InstValueString { + inst: EncoderInstPrefixBit, + mid_bit: MidBit, + name: Name, + inner: LiteralString, +} + +impl InstValueString { + fn new(inst: EncoderInstPrefixBit, mid_bit: MidBit, name: Name) -> Self { + Self::from_inner(inst, mid_bit, name, LengthFirstByte.into()) + } + + fn from_inner( + inst: EncoderInstPrefixBit, + mid_bit: MidBit, + name: Name, + inner: LiteralString, + ) -> Self { + Self { + inst, + mid_bit, + name, + inner, + } + } + + fn decode(self, buf: &[u8]) -> DecResult<(usize, EncoderInstruction), InstDecodeState> { + match (self.inst, self.inner.decode(buf)) { + (EncoderInstPrefixBit::INSERTWITHINDEX, DecResult::Decoded((buf_index, value))) => { + DecResult::Decoded(( + buf_index, + EncoderInstruction::InsertWithIndex { + mid_bit: self.mid_bit, + name: self.name, + value, + }, + )) + } + (EncoderInstPrefixBit::INSERTWITHLITERAL, DecResult::Decoded((buf_index, value))) => { + DecResult::Decoded(( + buf_index, + EncoderInstruction::InsertWithLiteral { + mid_bit: self.mid_bit, + name: self.name, + value, + }, + )) + } + (_, _) => Error(H3errorQpack::ConnectionError(DecompressionFailed)), + } + } +} + +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: &[u8]) -> DecResult<(usize, Representation), ReprDecodeState> { + match (self.repr, self.inner.decode(buf)) { + (ReprPrefixBit::LITERALWITHINDEXING, DecResult::Decoded((buf_index, value))) => { + DecResult::Decoded(( + buf_index, + Representation::LiteralWithIndexing { + mid_bit: self.mid_bit, + name: self.name, + value, + }, + )) + } + (ReprPrefixBit::LITERALWITHPOSTINDEXING, DecResult::Decoded((buf_index, value))) => { + DecResult::Decoded(( + buf_index, + Representation::LiteralWithPostIndexing { + mid_bit: self.mid_bit, + name: self.name, + value, + }, + )) + } + (ReprPrefixBit::LITERALWITHLITERALNAME, DecResult::Decoded((buf_index, value))) => { + DecResult::Decoded(( + buf_index, + Representation::LiteralWithLiteralName { + mid_bit: self.mid_bit, + name: self.name, + value, + }, + )) + } + (_, _) => Error(H3errorQpack::ConnectionError(DecompressionFailed)), + } + } +} + +/// Decoder's possible returns during the decoding process. +pub(crate) 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(H3errorQpack), +} + +impl From for DecResult { + fn from(e: H3errorQpack) -> Self { + DecResult::Error(e) + } +} diff --git a/ylong_http/src/h3/qpack/format/encoder.rs b/ylong_http/src/h3/qpack/format/encoder.rs new file mode 100644 index 0000000000000000000000000000000000000000..8d2b79d2c903bf5201ee58bf84a80b13b8e6f68c --- /dev/null +++ b/ylong_http/src/h3/qpack/format/encoder.rs @@ -0,0 +1,960 @@ +// 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. +use crate::h3::parts::Parts; +use crate::h3::qpack::error::ErrorCode::DecoderStreamError; +use crate::h3::qpack::error::H3errorQpack; +use crate::h3::qpack::format::decoder::DecResult; +use crate::h3::qpack::integer::{Integer, IntegerDecoder, IntegerEncoder}; +use crate::h3::qpack::table::{DynamicTable, Field, TableIndex, TableSearcher}; +use crate::h3::qpack::{DecoderInstPrefixBit, DecoderInstruction, EncoderInstruction, PrefixMask}; +use crate::headers::HeadersIntoIter; +use crate::h3::pseudo::PseudoHeaders; +use std::arch::asm; +use std::cmp::{max, Ordering}; +use std::collections::{HashMap, VecDeque}; +use std::result; +use std::sync::Arc; + +pub struct ReprEncoder<'a> { + table: &'a mut DynamicTable, + draining_index: usize, + allow_post: bool, + insert_length: &'a mut usize, +} + +impl<'a> ReprEncoder<'a> { + /// Creates a new, empty `ReprEncoder`. + /// # Examples +// ```no_run +// use ylong_http::h3::qpack::table::DynamicTable; +// use ylong_http::h3::qpack::format::encoder::ReprEncoder; +// let mut table = DynamicTable::new(4096); +// let mut insert_length = 0; +// let mut encoder = ReprEncoder::new(&mut table, 0, true, &mut insert_length); +// ``` + pub fn new( + table: &'a mut DynamicTable, + draining_index: usize, + allow_post: bool, + insert_length: &'a mut usize, + ) -> Self { + Self { + table, + draining_index, + allow_post, + insert_length, + } + } + + /// written to `buffer` and the length of the decoded content will be returned. + /// # Examples +// ```no_run +// use std::collections::VecDeque;use ylong_http::h3::qpack::table::DynamicTable; +// use ylong_http::h3::qpack::format::encoder::ReprEncoder; +// let mut table = DynamicTable::new(4096); +// let mut insert_length = 0; +// let mut encoder = ReprEncoder::new(&mut table, 0, true, &mut insert_length); +// let mut qpack_buffer = [0u8; 1024]; +// let mut stream_buffer = [0u8; 1024]; // stream buffer +// let mut insert_list = VecDeque::new(); // fileds to insert +// let mut required_insert_count = 0; // RFC required. +// let mut field_iter = None; // for field iterator +// let mut field_state = None; // for field encode state +// encoder.encode(&mut field_iter, &mut field_state, &mut qpack_buffer, &mut stream_buffer, &mut insert_list, &mut required_insert_count); + pub(crate) fn encode( + &mut self, + field_iter: &mut Option, + field_state: &mut Option, + encoder_buffer: &mut [u8], + stream_buffer: &mut [u8], + insert_list: &mut VecDeque<(Field, String)>, + required_insert_count: &mut usize, + ) -> (usize, usize) { + let mut cur_encoder = 0; + let mut cur_stream = 0; + let mut base = self.table.insert_count; + if let Some(mut iter) = field_iter.take() { + while let Some((h, v)) = iter.next() { + let searcher = TableSearcher::new(self.table); + let mut stream_result: Result = Result::Ok(0); + let mut encoder_result: Result = Result::Ok(0); + 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 { + let mut dynamic_index = searcher.find_index_dynamic(&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) || !self.should_index(&dynamic_index) + { + // if index is close to eviction, drop it and use duplicate + // let dyn_index = dynamic_index.clone(); + // dynamic_index = Some(TableIndex::None); + let mut is_duplicate = false; + if static_name_index == Some(TableIndex::None) { + dynamic_name_index = searcher.find_index_name_dynamic(&h, &v); + } + + if self.table.have_enough_space(&h, &v, self.insert_length) { + if !self.should_index(&dynamic_index) { + if let Some(TableIndex::Field(index)) = dynamic_index { + encoder_result = Duplicate::new(base - index - 1) + .encode(&mut encoder_buffer[cur_encoder..]); + self.table.update(h.clone(), v.clone()); + base = max(base, self.table.insert_count); + dynamic_index = + Some(TableIndex::Field(self.table.insert_count - 1)); + is_duplicate = true; + } + } else { + encoder_result = match ( + &static_name_index, + &dynamic_name_index, + self.should_index(&dynamic_name_index), + ) { + // insert with name reference in static table + (Some(TableIndex::FieldName(index)), _, _) => { + InsertWithName::new( + *index, + v.clone().into_bytes(), + false, + true, + ) + .encode(&mut encoder_buffer[cur_encoder..]) + } + // insert with name reference in dynamic table + (_, Some(TableIndex::FieldName(index)), true) => { + // convert abs index to rel index + InsertWithName::new( + base - index - 1, + v.clone().into_bytes(), + false, + false, + ) + .encode(&mut encoder_buffer[cur_encoder..]) + } + // Duplicate + (_, Some(TableIndex::FieldName(index)), false) => { + let res = Duplicate::new(*index) + .encode(&mut encoder_buffer[cur_encoder..]); + self.table.update(h.clone(), v.clone()); + base = max(base, self.table.insert_count); + dynamic_name_index = Some(TableIndex::FieldName( + self.table.insert_count - 1, + )); + is_duplicate = true; + res + } + // insert with literal name + (_, _, _) => InsertWithLiteral::new( + h.clone().into_string().into_bytes(), + v.clone().into_bytes(), + false, + ) + .encode(&mut encoder_buffer[cur_encoder..]), + } + }; + if self.table.size() + h.len() + v.len() + 32 >= self.table.capacity() { + self.draining_index += 1; + } + insert_list.push_back((h.clone(), v.clone())); + *self.insert_length += h.len() + v.len() + 32; + } + if self.allow_post && !is_duplicate { + for (post_index, (t_h, t_v)) in insert_list.iter().enumerate() { + if t_h == &h && t_v == &v { + dynamic_index = Some(TableIndex::Field(post_index)) + } + if t_h == &h { + dynamic_name_index = Some(TableIndex::FieldName(post_index)); + } + } + } + } + + 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 { + // use post-base index + if base <= index { + stream_result = IndexingWithPostName::new( + index - base, + v.clone().into_bytes(), + false, + false, + ) + .encode(&mut stream_buffer[cur_stream..]); + } else { + stream_result = IndexingWithName::new( + base - index - 1, + v.clone().into_bytes(), + false, + false, + false, + ) + .encode(&mut stream_buffer[cur_stream..]); + } + *required_insert_count = max(*required_insert_count, index + 1); + } + } 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 { + stream_result = IndexingWithLiteral::new( + h.into_string().into_bytes(), + v.into_bytes(), + false, + false, + ) + .encode(&mut stream_buffer[cur_stream..]); + } + } + } else { + assert!(dynamic_index != Some(TableIndex::None)); + // Encode with index in dynamic table + if let Some(TableIndex::Field(index)) = dynamic_index { + // use post-base index + if base <= index { + stream_result = IndexedWithPostName::new(index - base) + .encode(&mut stream_buffer[cur_stream..]); + } else { + stream_result = Indexed::new(base - index - 1, false) + .encode(&mut stream_buffer[cur_stream..]); + } + *required_insert_count = max(*required_insert_count, index + 1); + } + } + } + + match (encoder_result, stream_result) { + (Ok(encoder_size), Ok(stream_size)) => { + cur_stream += stream_size; + cur_encoder += encoder_size; + } + (Err(state), Ok(_)) => { + *field_iter = Some(iter); + *field_state = Some(state); + return (encoder_buffer.len(), stream_buffer.len()); + } + (Ok(_), Err(state)) => { + *field_iter = Some(iter); + *field_state = Some(state); + return (encoder_buffer.len(), stream_buffer.len()); + } + (Err(_), Err(state)) => { + *field_iter = Some(iter); + *field_state = Some(state); + return (encoder_buffer.len(), stream_buffer.len()); + } + } + } + } + + (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, index: &Option) -> bool { + match index { + Some(TableIndex::Field(x)) => { + if *x < self.draining_index { + return false; + } + true + } + Some(TableIndex::FieldName(x)) => { + if *x < self.draining_index { + return false; + } + true + } + _ => true, + } + } +} + +pub(crate) enum ReprEncodeState { + SetCap(SetCap), + Indexed(Indexed), + InsertWithName(InsertWithName), + InsertWithLiteral(InsertWithLiteral), + IndexingWithName(IndexingWithName), + IndexingWithPostName(IndexingWithPostName), + IndexingWithLiteral(IndexingWithLiteral), + IndexedWithPostName(IndexedWithPostName), + Duplicate(Duplicate), +} + +pub(crate) struct SetCap { + capacity: Integer, +} + +impl SetCap { + fn from(capacity: Integer) -> Self { + Self { capacity } + } + + pub(crate) fn new(capacity: usize) -> Self { + Self { + capacity: Integer::index(0x20, capacity, PrefixMask::SETCAP.0), + } + } + + pub(crate) fn encode(self, dst: &mut [u8]) -> Result { + self.capacity + .encode(dst) + .map_err(|e| ReprEncodeState::SetCap(SetCap::from(e))) + } +} + +pub(crate) struct Duplicate { + index: Integer, +} + +impl Duplicate { + fn from(index: Integer) -> Self { + Self { index } + } + + fn new(index: usize) -> Self { + Self { + index: Integer::index(0x00, index, PrefixMask::DUPLICATE.0), + } + } + + fn encode(self, dst: &mut [u8]) -> Result { + self.index + .encode(dst) + .map_err(|e| ReprEncodeState::Duplicate(Duplicate::from(e))) + } +} + +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::INSERTWITHINDEX.0) + .set_value(value, is_huffman), + } + } else { + Self { + inner: IndexAndValue::new() + .set_index(0x80, index, PrefixMask::INSERTWITHINDEX.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) + } +} + +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], +} + +impl<'a> DecInstDecoder<'a> { + pub(crate) fn new(buf: &'a [u8]) -> Self { + Self { buf } + } + + pub(crate) fn decode( + &mut self, + ins_state: &mut Option, + ) -> Result, H3errorQpack> { + if self.buf.is_empty() { + return Ok(None); + } + + match ins_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) => { + *ins_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(H3errorQpack::ConnectionError(DecoderStreamError)), + } + } +} + +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, +} + +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 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`. + pub(crate) 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/format/mod.rs b/ylong_http/src/h3/qpack/format/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..70358414dd86517ef4a6f329015e1c9b4dae10a6 --- /dev/null +++ b/ylong_http/src/h3/qpack/format/mod.rs @@ -0,0 +1,16 @@ +// 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. +pub(crate) mod decoder; +pub mod encoder; + +pub(crate) use encoder::ReprEncoder; diff --git a/ylong_http/src/h3/qpack/integer.rs b/ylong_http/src/h3/qpack/integer.rs new file mode 100644 index 0000000000000000000000000000000000000000..6c6b471174ef720159aaaf2f2b3dc768ccf4698b --- /dev/null +++ b/ylong_http/src/h3/qpack/integer.rs @@ -0,0 +1,145 @@ +// 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. +use crate::h3::qpack::error::{ErrorCode, H3errorQpack}; +use std::cmp::Ordering; + +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, H3errorQpack> { + 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(H3errorQpack::ConnectionError( + ErrorCode::DecompressionFailed, + ))?; //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) + } +} diff --git a/ylong_http/src/h3/qpack/mod.rs b/ylong_http/src/h3/qpack/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..f21f9d3673a61a6df3c44f685354903eefa32c93 --- /dev/null +++ b/ylong_http/src/h3/qpack/mod.rs @@ -0,0 +1,300 @@ +// 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. + +pub mod decoder; +pub mod encoder; +pub(crate) mod error; +pub mod format; +mod integer; +pub mod table; +use crate::h3::qpack::format::decoder::Name; +pub(crate) use decoder::FiledLines; +pub(crate) use decoder::QpackDecoder; +pub(crate) use encoder::DecoderInst; +pub(crate) use encoder::QpackEncoder; + +pub(crate) struct RequireInsertCount(usize); + +pub(crate) struct DeltaBase(usize); + +#[derive(Copy, Clone, PartialEq, Eq)] +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); + +/// # Prefix bit: +/// ## Encoder Instructions: +/// SETCAP: 0x20 +/// INSERTWITHINDEX: 0x80 +/// INSERTWITHLITERAL: 0x40 +/// DUPLICATE: 0x00 +/// +/// ## Decoder Instructions: +/// ACK: 0x80 +/// STREAMCANCEL: 0x40 +/// INSERTCOUNTINCREMENT: 0x00 +/// +/// ## Representation: +/// INDEXED: 0x80 +/// INDEXEDWITHPOSTINDEX: 0x10 +/// LITERALWITHINDEXING: 0x40 +/// LITERALWITHPOSTINDEXING: 0x00 +/// LITERALWITHLITERALNAME: 0x20 + +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) -> MidBit { + 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); + 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 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::LITERALWITHINDEXING, + x if x >= 0x20 => Self::LITERALWITHLITERALNAME, + x if x >= 0x10 => Self::INDEXEDWITHPOSTINDEX, + _ => Self::LITERALWITHPOSTINDEXING, + } + } + + /// 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) -> MidBit { + match self.0 { + 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 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 { + /// 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 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 + /// the entry in the dynamic table. + FieldSectionPrefix { + require_insert_count: RequireInsertCount, + signal: bool, + delta_base: DeltaBase, + }, + + 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, + }, +} + +//impl debug for Representation + +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, + //'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 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 INSERTWITHINDEX: Self = Self(0x3f); + 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); + pub(crate) const INDEXEDWITHPOSTNAME: Self = Self(0x0f); +} diff --git a/ylong_http/src/h3/qpack/table.rs b/ylong_http/src/h3/qpack/table.rs new file mode 100644 index 0000000000000000000000000000000000000000..4d1cdc109977b015ad7666414752a4cb3363ad54 --- /dev/null +++ b/ylong_http/src/h3/qpack/table.rs @@ -0,0 +1,719 @@ +// 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. + +use std::collections::{HashMap, 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 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_index_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_index_name_dynamic( + &self, + header: &Field, + value: &str, + ) -> Option { + match self.dynamic.index_name(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_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 struct DynamicTable { + queue: VecDeque<(Field, String)>, + // 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, + pub(crate) known_received_count: usize, +} + +impl DynamicTable { + pub fn with_empty() -> Self { + Self { + queue: VecDeque::new(), + size: 0, + capacity: 0, + insert_count: 0, + remove_count: 0, + known_received_count: 0, + } + } + + pub(crate) fn size(&self) -> usize { + self.size + } + + pub(crate) fn capacity(&self) -> usize { + 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) -> Option { + self.insert_count += 1; + self.size += field.len() + value.len() + 32; + self.queue.push_back((field.clone(), value.clone())); + self.fit_size(); + self.index(&field, &value) + } + + pub(crate) fn have_enough_space( + &self, + field: &Field, + value: &String, + insert_length: &usize, + ) -> bool { + if self.size + field.len() + value.len() + 32 <= self.capacity - insert_length { + return true; + } else { + let mut eviction_space = 0; + for (i, (h, v)) in self.queue.iter().enumerate() { + if i <= self.known_received_count { + eviction_space += h.len() + v.len() + 32; + } else { + if eviction_space - insert_length >= field.len() + value.len() + 32 { + return true; + } + return false; + } + if eviction_space - insert_length >= 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; + self.fit_size(); + } + + /// 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_front().unwrap(); + self.remove_count += 1; + self.capacity -= key.len() + string.len() + 32; + } + } + + /// Tries get the index of a `Header`. + fn index(&self, header: &Field, value: &str) -> Option { + // find latest + let mut index = None; + for (n, (h, v)) in self.queue.iter().enumerate() { + if let (true, true, _) = (header == h, value == v, &index) { + index = Some(TableIndex::Field(n + self.remove_count)) + } + } + index + } + + fn index_name(&self, header: &Field, value: &str) -> Option { + // find latest + let mut index = None; + for (n, (h, v)) in self.queue.iter().enumerate() { + if let (true, _, _) = (header == h, value == v, &index) { + index = Some(TableIndex::FieldName(n + self.remove_count)) + } + } + index + } + + pub(crate) fn field(&self, index: usize) -> Option<(Field, String)> { + 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()) + } +} + +#[derive(PartialEq, Clone)] +pub(crate) enum TableIndex { + Field(usize), + FieldName(usize), + None, +} + +/// 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 format, it MUST treat this +/// as a connection error of type QpackDecompressionFailed. +/// If this index is received on the encoder stream, +/// this MUST be treated as a connection error of type QpackEncoderStreamError. +/// + +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::Path, _) => Some(TableIndex::FieldName(1)), + (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::Method, _) => Some(TableIndex::FieldName(15)), + (Field::Scheme, "http") => Some(TableIndex::Field(22)), + (Field::Scheme, "https") => Some(TableIndex::Field(23)), + (Field::Scheme, _) => Some(TableIndex::FieldName(22)), + (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::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::Status, _) => Some(TableIndex::FieldName(24)), + (Field::Other(s), v) => match (s.as_str(), v) { + ("age", "0") => Some(TableIndex::Field(2)), + ("age", _) => Some(TableIndex::FieldName(2)), + ("content-disposition", _) => Some(TableIndex::FieldName(3)), + ("content-length", "0") => Some(TableIndex::Field(4)), + ("content-length", _) => Some(TableIndex::FieldName(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", _) => Some(TableIndex::FieldName(29)), + ("accept-encoding", "gzip, deflate, br") => Some(TableIndex::Field(31)), + ("accept-encoding", _) => Some(TableIndex::FieldName(31)), + ("accept-ranges", "bytes") => Some(TableIndex::Field(32)), + ("accept-ranges", _) => Some(TableIndex::FieldName(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)), + ("access-control-allow-origin", _) => Some(TableIndex::FieldName(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)), + ("cache-control", _) => Some(TableIndex::FieldName(36)), + ("content-encoding", "br") => Some(TableIndex::Field(42)), + ("content-encoding", "gzip") => Some(TableIndex::Field(43)), + ("content-encoding", _) => Some(TableIndex::FieldName(42)), + ("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)), + ("content-type", _) => Some(TableIndex::FieldName(44)), + ("range", "bytes=0-") => Some(TableIndex::Field(55)), + ("range", _) => Some(TableIndex::FieldName(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)) + } + ("strict-transport-security", _) => Some(TableIndex::FieldName(56)), + ("vary", "accept-encoding") => Some(TableIndex::Field(59)), + ("vary", "origin") => Some(TableIndex::Field(60)), + ("vary", _) => Some(TableIndex::FieldName(59)), + ("x-content-type-options", "nosniff") => Some(TableIndex::Field(61)), + ("x-content-type-options", _) => Some(TableIndex::FieldName(61)), + ("x-xss-protection", "1; mode=block") => Some(TableIndex::Field(62)), + ("x-xss-protection", _) => Some(TableIndex::FieldName(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-credentials", _) => Some(TableIndex::FieldName(73)), + ("access-control-allow-headers", "*") => Some(TableIndex::Field(75)), + ("access-control-allow-headers", _) => Some(TableIndex::FieldName(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-allow-methods", _) => Some(TableIndex::FieldName(76)), + ("access-control-expose-headers", "content-length") => Some(TableIndex::Field(79)), + ("access-control-expose-headers", _) => Some(TableIndex::FieldName(79)), + ("access-control-request-headers", "content-type") => Some(TableIndex::Field(80)), + ("access-control-request-headers", _) => Some(TableIndex::FieldName(80)), + ("access-control-request-method", "get") => Some(TableIndex::Field(81)), + ("access-control-request-method", "post") => Some(TableIndex::Field(82)), + ("access-control-request-method", _) => Some(TableIndex::FieldName(81)), + ("alt-svc", "clear") => Some(TableIndex::Field(83)), + ("alt-svc", _) => Some(TableIndex::FieldName(83)), + ("authorization", _) => Some(TableIndex::FieldName(84)), + ( + "content-security-policy", + "script-src 'none'; object-src 'none'; base-uri 'none'", + ) => Some(TableIndex::Field(85)), + ("content-security-policy", _) => Some(TableIndex::FieldName(85)), + ("early-data", "1") => Some(TableIndex::Field(86)), + ("early-data", _) => Some(TableIndex::FieldName(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)), + ("purpose", _) => Some(TableIndex::FieldName(91)), + ("server", _) => Some(TableIndex::FieldName(92)), + ("timing-allow-origin", "*") => Some(TableIndex::Field(93)), + ("timing-allow-origin", _) => Some(TableIndex::FieldName(93)), + ("upgrade-insecure-requests", "1") => Some(TableIndex::Field(94)), + ("upgrade-insecure-requests", _) => Some(TableIndex::FieldName(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)), + ("x-frame-options", _) => Some(TableIndex::FieldName(97)), + _ => None, + }, + } + } +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub 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, + } + } +} diff --git a/ylong_http/src/test_util.rs b/ylong_http/src/test_util.rs index 7d9c9a7953c0e375b9b9de9a87aae48e70f4d296..0d554802b60f2524b93e701a72d2a2ab943e4e51 100644 --- a/ylong_http/src/test_util.rs +++ b/ylong_http/src/test_util.rs @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub(crate) fn decode(str: &str) -> Option> { +pub fn decode(str: &str) -> Option> { if str.len() % 2 != 0 { return None; } diff --git a/ylong_http_client/src/async_impl/conn/http2.rs b/ylong_http_client/src/async_impl/conn/http2.rs index 68dc833bb74f749f03d0d46b782933ca63480530..d00f546063208cf91d434e0d8220ffa5567ff9bf 100644 --- a/ylong_http_client/src/async_impl/conn/http2.rs +++ b/ylong_http_client/src/async_impl/conn/http2.rs @@ -40,9 +40,9 @@ pub(crate) async fn request( request: &mut Request, retryable: &mut Retryable, ) -> Result, HttpClientError> -where - T: Body, - S: AsyncRead + AsyncWrite + Sync + Send + Unpin + 'static, + where + T: Body, + S: AsyncRead + AsyncWrite + Sync + Send + Unpin + 'static, { let part = request.part().clone(); let body = request.body_mut(); @@ -83,8 +83,8 @@ fn frame_2_response( headers_frame: Frame, retryable: &mut Retryable, ) -> Result, HttpClientError> -where - S: AsyncRead + AsyncWrite + Sync + Send + Unpin + 'static, + where + S: AsyncRead + AsyncWrite + Sync + Send + Unpin + 'static, { let part = match headers_frame.payload() { Payload::Headers(headers) => { diff --git a/ylong_http_client/src/async_impl/http_body.rs b/ylong_http_client/src/async_impl/http_body.rs index b13e105a7777059363ac1d12241ca01231556044..60b1f00aefeca031bf19758291931559549f0a96 100644 --- a/ylong_http_client/src/async_impl/http_body.rs +++ b/ylong_http_client/src/async_impl/http_body.rs @@ -568,11 +568,11 @@ impl Chunk { #[cfg(test)] mod ut_async_http_body { - use ylong_http::body::{async_impl, ChunkBodyDecoder}; use crate::async_impl::http_body::Chunk; use crate::async_impl::HttpBody; use crate::util::normalizer::BodyLength; use crate::ErrorKind; + use ylong_http::body::{async_impl, ChunkBodyDecoder}; /// UT test cases for `Chunk::get_trailers`. ///