1use crate::Error;
20use crate::hpack::dynamic::{DynamicTable, lookup};
21use crate::hpack::huffman;
22use crate::hpack::static_table;
23
24pub const MAX_STRING: usize = 512;
29
30pub struct Header {
32 pub name: heapless::String<MAX_STRING>,
33 pub value: heapless::String<MAX_STRING>,
34}
35
36pub struct Decoder {
42 table: DynamicTable,
43}
44
45impl Decoder {
46 pub fn new(table_size: usize) -> Self {
47 Self {
48 table: DynamicTable::new(table_size),
49 }
50 }
51
52 pub fn table(&self) -> &DynamicTable {
53 &self.table
54 }
55
56 pub fn decode(
61 &mut self,
62 mut input: &[u8],
63 mut on_header: impl FnMut(&str, &str),
64 ) -> Result<(), Error> {
65 while !input.is_empty() {
66 let first = input[0];
67
68 if first & 0x80 != 0 {
69 let (index, rest) = decode_integer(input, 7)?;
71 input = rest;
72 let (name, value) = lookup(&self.table, index as usize)?;
73 on_header(name, value);
74 continue;
75 }
76
77 if first & 0xe0 == 0x20 {
78 let (size, rest) = decode_integer(input, 5)?;
82 input = rest;
83 self.table.set_capacity(size as usize);
84 continue;
85 }
86
87 let (prefix, indexing) = if first & 0xc0 == 0x40 {
89 (6, true)
90 } else {
91 (4, false)
95 };
96
97 let (index, rest) = decode_integer(input, prefix)?;
98 input = rest;
99
100 let mut name = heapless::String::<MAX_STRING>::new();
101 if index == 0 {
102 input = decode_string(input, &mut name)?;
103 } else {
104 let (existing, _) = lookup(&self.table, index as usize)?;
105 name.push_str(existing).map_err(|_| Error::BufferTooSmall)?;
106 }
107
108 let mut value = heapless::String::<MAX_STRING>::new();
109 input = decode_string(input, &mut value)?;
110
111 on_header(&name, &value);
112 if indexing {
113 self.table.insert(&name, &value);
114 }
115 }
116 Ok(())
117 }
118}
119
120pub fn decode_integer(input: &[u8], prefix_bits: u32) -> Result<(u64, &[u8]), Error> {
125 let mask = (1u64 << prefix_bits) - 1;
126 let first = *input.first().ok_or(Error::Incomplete)? as u64;
127 let value = first & mask;
128 if value < mask {
129 return Ok((value, &input[1..]));
130 }
131
132 let mut value = mask;
133 let mut shift = 0;
134 let mut rest = &input[1..];
135 loop {
136 let byte = *rest.first().ok_or(Error::Incomplete)?;
137 rest = &rest[1..];
138 if shift > 63 {
141 return Err(Error::Hpack);
142 }
143 value = value
144 .checked_add(((byte & 0x7f) as u64) << shift)
145 .ok_or(Error::Hpack)?;
146 if byte & 0x80 == 0 {
147 return Ok((value, rest));
148 }
149 shift += 7;
150 }
151}
152
153fn decode_string<'a>(
155 input: &'a [u8],
156 out: &mut heapless::String<MAX_STRING>,
157) -> Result<&'a [u8], Error> {
158 let huffman_coded = input.first().ok_or(Error::Incomplete)? & 0x80 != 0;
159 let (len, rest) = decode_integer(input, 7)?;
160 let len = len as usize;
161 let bytes = rest.get(..len).ok_or(Error::Incomplete)?;
162
163 let mut buffer = [0u8; MAX_STRING];
164 let decoded: &[u8] = if huffman_coded {
165 let n = huffman::decode(bytes, &mut buffer)?;
166 &buffer[..n]
167 } else {
168 bytes
169 };
170
171 let text = core::str::from_utf8(decoded).map_err(|_| Error::Hpack)?;
172 out.push_str(text).map_err(|_| Error::BufferTooSmall)?;
173 Ok(&rest[len..])
174}
175
176pub use static_table::{DYNAMIC_BASE, ENTRIES as STATIC_ENTRIES};
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 fn hex(text: &str) -> heapless::Vec<u8, 256> {
185 let mut bytes = heapless::Vec::new();
186 for pair in text.as_bytes().chunks(2) {
187 bytes
188 .push(u8::from_str_radix(core::str::from_utf8(pair).unwrap(), 16).unwrap())
189 .unwrap();
190 }
191 bytes
192 }
193
194 fn decode_all(decoder: &mut Decoder, block: &str) -> heapless::Vec<heapless::String<256>, 16> {
196 let mut headers = heapless::Vec::new();
197 decoder
198 .decode(&hex(block), |name, value| {
199 let mut entry = heapless::String::<256>::new();
200 entry.push_str(name).unwrap();
201 entry.push_str(": ").unwrap();
202 entry.push_str(value).unwrap();
203 headers.push(entry).unwrap();
204 })
205 .unwrap();
206 headers
207 }
208
209 #[test]
210 fn decodes_integers_as_the_rfc_section_5_1_examples_do() {
211 assert_eq!(decode_integer(&[0x0a], 5).unwrap().0, 10);
213 assert_eq!(decode_integer(&[0x1f, 0x9a, 0x0a], 5).unwrap().0, 1337);
215 assert_eq!(decode_integer(&[0x2a], 8).unwrap().0, 42);
217 assert_eq!(decode_integer(&[0x1f], 5), Err(Error::Incomplete));
219 }
220
221 #[test]
225 fn decodes_the_rfc_appendix_c_3_request_sequence() {
226 let mut decoder = Decoder::new(4096);
227
228 let first = decode_all(&mut decoder, "828684410f7777772e6578616d706c652e636f6d");
229 assert_eq!(first[0].as_str(), ":method: GET");
230 assert_eq!(first[1].as_str(), ":scheme: http");
231 assert_eq!(first[2].as_str(), ":path: /");
232 assert_eq!(first[3].as_str(), ":authority: www.example.com");
233 assert_eq!(decoder.table().len(), 1);
234
235 let second = decode_all(&mut decoder, "828684be58086e6f2d6361636865");
237 assert_eq!(second[3].as_str(), ":authority: www.example.com");
238 assert_eq!(second[4].as_str(), "cache-control: no-cache");
239 assert_eq!(decoder.table().len(), 2);
240
241 let third = decode_all(
242 &mut decoder,
243 "828785bf400a637573746f6d2d6b65790c637573746f6d2d76616c7565",
244 );
245 assert_eq!(third[3].as_str(), ":authority: www.example.com");
246 assert_eq!(third[4].as_str(), "custom-key: custom-value");
247 assert_eq!(decoder.table().len(), 3);
248 }
249
250 #[test]
252 fn decodes_the_rfc_appendix_c_4_huffman_request_sequence() {
253 let mut decoder = Decoder::new(4096);
254
255 let first = decode_all(&mut decoder, "828684418cf1e3c2e5f23a6ba0ab90f4ff");
256 assert_eq!(first[3].as_str(), ":authority: www.example.com");
257
258 let second = decode_all(&mut decoder, "828684be5886a8eb10649cbf");
259 assert_eq!(second[4].as_str(), "cache-control: no-cache");
260
261 let third = decode_all(
262 &mut decoder,
263 "828785bf408825a849e95ba97d7f8925a849e95bb8e8b4bf",
264 );
265 assert_eq!(third[4].as_str(), "custom-key: custom-value");
266 }
267
268 #[test]
272 fn decodes_the_rfc_appendix_c_5_response_sequence_with_eviction() {
273 let mut decoder = Decoder::new(256);
274
275 let first = decode_all(
276 &mut decoder,
277 "4803333032580770726976617465611d4d6f6e2c203231204f637420323031332032303a31333a323120474d546e1768747470733a2f2f7777772e6578616d706c652e636f6d",
278 );
279 assert_eq!(first[0].as_str(), ":status: 302");
280 assert_eq!(first[3].as_str(), "location: https://www.example.com");
281
282 let second = decode_all(&mut decoder, "4803333037c1c0bf");
283 assert_eq!(second[0].as_str(), ":status: 307");
284 assert_eq!(second[1].as_str(), "cache-control: private");
285 assert_eq!(second[3].as_str(), "location: https://www.example.com");
286
287 let third = decode_all(
288 &mut decoder,
289 "88c1611d4d6f6e2c203231204f637420323031332032303a31333a323220474d54c05a04677a69707738666f6f3d4153444a4b48514b425a584f5157454f50495541585157454f49553b206d61782d6167653d333630303b2076657273696f6e3d31",
290 );
291 assert_eq!(third[0].as_str(), ":status: 200");
292 assert_eq!(third[4].as_str(), "content-encoding: gzip");
293 assert!(
294 third[5]
295 .as_str()
296 .starts_with("set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU")
297 );
298 }
299
300 #[test]
301 fn a_dynamic_table_size_update_is_applied_and_is_not_a_header() {
302 let mut decoder = Decoder::new(4096);
303 let headers = decode_all(&mut decoder, "2082");
305 assert_eq!(headers.len(), 1);
306 assert_eq!(headers[0].as_str(), ":method: GET");
307 assert_eq!(decoder.table().capacity(), 0);
308 }
309
310 #[test]
311 fn an_index_the_peer_never_defined_is_an_error() {
312 let mut decoder = Decoder::new(4096);
316 assert_eq!(decoder.decode(&hex("be"), |_, _| {}), Err(Error::Hpack));
317 assert_eq!(decoder.decode(&hex("ff00"), |_, _| {}), Err(Error::Hpack));
319 }
320
321 #[test]
322 fn a_truncated_block_is_incomplete_rather_than_a_short_header() {
323 let mut decoder = Decoder::new(4096);
324 assert_eq!(
326 decoder.decode(&hex("400a637573746f6d"), |_, _| {}),
327 Err(Error::Incomplete)
328 );
329 }
330
331 #[test]
332 fn never_indexed_headers_do_not_enter_the_table() {
333 let mut decoder = Decoder::new(4096);
334 let headers = decode_all(&mut decoder, "10012d0131");
336 assert_eq!(headers[0].as_str(), "-: 1");
337 assert!(
338 decoder.table().is_empty(),
339 "a never-indexed header must not be remembered"
340 );
341 }
342}