1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use crate::h2::hpack::representation::{ReprEncStateHolder, ReprEncodeState, ReprEncoder};
15 use crate::h2::hpack::table::{DynamicTable, Header};
16 use crate::h2::{Parts, PseudoHeaders};
17 
18 /// Decoder implementation of [`HPACK`].
19 ///
20 /// [`HPACK`]: https://httpwg.org/specs/rfc7541.html
21 // TODO: 增加 SizeUpdate 字段以支持用户动态变化 DynamicTable 大小。
22 pub(crate) struct HpackEncoder {
23     table: DynamicTable,
24     holder: ReprEncStateHolder,
25     use_huffman: bool,
26 }
27 
28 impl HpackEncoder {
29     /// Create a `HpackEncoder` with the given max dynamic table size and
30     /// huffman usage.
new(max_size: usize, use_huffman: bool) -> Self31     pub(crate) fn new(max_size: usize, use_huffman: bool) -> Self {
32         Self {
33             table: DynamicTable::with_max_size(max_size),
34             holder: ReprEncStateHolder::new(),
35             use_huffman,
36         }
37     }
38 
39     // TODO enable update header_table_size
update_max_dynamic_table_size(&self, _max_size: usize)40     pub(crate) fn update_max_dynamic_table_size(&self, _max_size: usize) {}
41 
42     /// Set the `Parts` to be encoded.
set_parts(&mut self, parts: Parts)43     pub(crate) fn set_parts(&mut self, parts: Parts) {
44         self.holder.set_parts(parts)
45     }
46 
47     /// Users can call `encode` multiple times to encode the previously set
48     /// `Parts` in segments.
encode(&mut self, dst: &mut [u8]) -> usize49     pub(crate) fn encode(&mut self, dst: &mut [u8]) -> usize {
50         let mut encoder = ReprEncoder::new(&mut self.table);
51         encoder.load(&mut self.holder);
52         let size = encoder.encode(dst, self.use_huffman);
53         if size == dst.len() {
54             encoder.save(&mut self.holder);
55         }
56         size
57     }
58 
59     /// Check the previously set `Parts` if encoding is complete.
is_finished(&self) -> bool60     pub(crate) fn is_finished(&self) -> bool {
61         self.holder.is_empty()
62     }
63 }
64 
65 #[cfg(test)]
66 mod ut_hpack_encoder {
67     use crate::h2::hpack::table::Header;
68     use crate::h2::hpack::HpackEncoder;
69     use crate::h2::Parts;
70     use crate::util::test_util::decode;
71 
72     #[test]
ut_hpack_encoder()73     fn ut_hpack_encoder() {
74         rfc7541_test_cases();
75 
76         // In order to ensure that Header and Value are added in the order of
77         // `RFC`, each time a Parts is generated separately and passed in
78         macro_rules! hpack_test_cases {
79             ($enc: expr, $len: expr, $res: literal, $size: expr , { $($h: expr, $v: expr $(,)?)*} $(,)?) => {
80                 let mut _encoder = $enc;
81                 let mut vec = [0u8; $len];
82                 let mut cur = 0;
83                 $(
84                     let mut parts = Parts::new();
85                     parts.update($h, $v);
86                     _encoder.set_parts(parts);
87                     cur += _encoder.encode(&mut vec[cur..]);
88                 )*
89                 assert_eq!(cur, $len);
90                 let result = decode($res).unwrap();
91                 assert_eq!(vec.as_slice(), result.as_slice());
92                 assert_eq!(_encoder.table.curr_size(), $size);
93             }
94         }
95 
96         /// The following test cases are from RFC7541.
97         fn rfc7541_test_cases() {
98             // C.2.1.  Literal Header Field with Indexing
99             hpack_test_cases!(
100                 HpackEncoder::new(4096, false),
101                 26, "400a637573746f6d2d6b65790d637573746f6d2d686561646572", 55,
102                 {
103                     Header::Other(String::from("custom-key")),
104                     String::from("custom-header"),
105                 },
106             );
107 
108             // TODO: C.2.2.  Literal Header Field without Indexing
109             // TODO: C.2.3.  Literal Header Field Never Indexed
110 
111             // C.2.4.  Indexed Header Field
112             hpack_test_cases!(
113                 HpackEncoder::new(4096, false),
114                 1, "82", 0,
115                 {
116                     Header::Method,
117                     String::from("GET"),
118                 },
119             );
120 
121             // C.3.  Request Examples without Huffman Coding
122             {
123                 let mut encoder = HpackEncoder::new(4096, false);
124                 // C.3.1.  First Request
125                 hpack_test_cases!(
126                     &mut encoder,
127                     20, "828684410f7777772e6578616d706c652e636f6d", 57,
128                     {
129                         Header::Method,
130                         String::from("GET"),
131                         Header::Scheme,
132                         String::from("http"),
133                         Header::Path,
134                         String::from("/"),
135                         Header::Authority,
136                         String::from("www.example.com"),
137                     },
138                 );
139 
140                 // C.3.2.  Second Request
141                 hpack_test_cases!(
142                     &mut encoder,
143                     14, "828684be58086e6f2d6361636865", 110,
144                     {
145                         Header::Method,
146                         String::from("GET"),
147                         Header::Scheme,
148                         String::from("http"),
149                         Header::Path,
150                         String::from("/"),
151                         Header::Authority,
152                         String::from("www.example.com"),
153                         Header::Other(String::from("cache-control")),
154                         String::from("no-cache"),
155                     },
156                 );
157 
158                 // C.3.3.  Third Request
159                 hpack_test_cases!(
160                     &mut encoder,
161                     29, "828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565", 164,
162                     {
163                         Header::Method,
164                         String::from("GET"),
165                         Header::Scheme,
166                         String::from("https"),
167                         Header::Path,
168                         String::from("/index.html"),
169                         Header::Authority,
170                         String::from("www.example.com"),
171                         Header::Other(String::from("custom-key")),
172                         String::from("custom-value"),
173                     },
174                 );
175             }
176 
177             // TODO: C.4.  Request Examples with Huffman Coding
178 
179             // C.5.  Response Examples without Huffman Coding
180             {
181                 let mut encoder = HpackEncoder::new(256, false);
182                 // C.5.1.  First Response
183                 hpack_test_cases!(
184                     &mut encoder,
185                     70,
186                     "4803333032580770726976617465611d\
187                     4d6f6e2c203231204f63742032303133\
188                     2032303a31333a323120474d546e1768\
189                     747470733a2f2f7777772e6578616d70\
190                     6c652e636f6d",
191                     222,
192                     {
193                         Header::Status,
194                         String::from("302"),
195                         Header::Other(String::from("cache-control")),
196                         String::from("private"),
197                         Header::Other(String::from("date")),
198                         String::from("Mon, 21 Oct 2013 20:13:21 GMT"),
199                         Header::Other(String::from("location")),
200                         String::from("https://www.example.com"),
201                     },
202                 );
203 
204                 // C.5.2.  Second Response
205                 hpack_test_cases!(
206                     &mut encoder,
207                     8, "4803333037c1c0bf", 222,
208                     {
209                         Header::Status,
210                         String::from("307"),
211                         Header::Other(String::from("cache-control")),
212                         String::from("private"),
213                         Header::Other(String::from("date")),
214                         String::from("Mon, 21 Oct 2013 20:13:21 GMT"),
215                         Header::Other(String::from("location")),
216                         String::from("https://www.example.com"),
217                     },
218                 );
219 
220                 // C.5.3.  Third Response
221                 hpack_test_cases!(
222                     &mut encoder,
223                     98,
224                     "88c1611d4d6f6e2c203231204f637420\
225                     323031332032303a31333a323220474d\
226                     54c05a04677a69707738666f6f3d4153\
227                     444a4b48514b425a584f5157454f5049\
228                     5541585157454f49553b206d61782d61\
229                     67653d333630303b2076657273696f6e\
230                     3d31",
231                     215,
232                     {
233                         Header::Status,
234                         String::from("200"),
235                         Header::Other(String::from("cache-control")),
236                         String::from("private"),
237                         Header::Other(String::from("date")),
238                         String::from("Mon, 21 Oct 2013 20:13:22 GMT"),
239                         Header::Other(String::from("location")),
240                         String::from("https://www.example.com"),
241                         Header::Other(String::from("content-encoding")),
242                         String::from("gzip"),
243                         Header::Other(String::from("set-cookie")),
244                         String::from("foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
245                     },
246                 );
247             }
248 
249             // TODO: C.6.  Response Examples with Huffman Coding
250         }
251     }
252 }
253