specter/transport/h2/hpack_impl/
decoder.rs1use super::dynamic_table::DynamicTable;
4use super::error::HpackError;
5use super::huffman::huffman_decode;
6use super::integer::decode_integer;
7use super::static_table::get_static_entry;
8
9const STATIC_TABLE_SIZE: usize = 61;
10
11pub struct Decoder {
13 dynamic_table: DynamicTable,
14}
15
16impl Decoder {
17 pub fn new() -> Self {
19 Self {
20 dynamic_table: DynamicTable::new(4096),
21 }
22 }
23
24 pub fn set_max_table_size(&mut self, size: usize) {
26 self.dynamic_table.set_max_size(size);
27 }
28
29 pub fn decode_with_cb<F>(&mut self, data: &[u8], mut cb: F) -> Result<(), HpackError>
33 where
34 F: FnMut(&[u8], &[u8]),
35 {
36 let mut pos = 0;
37
38 while pos < data.len() {
39 let byte = data[pos];
40
41 if (byte & 0xE0) == 0x20 {
44 let (size, consumed) = decode_integer(&data[pos..], 5, 0x1F)?;
45 self.dynamic_table.set_max_size(size);
46 pos += consumed + 1;
47 continue;
48 }
49
50 if (byte & 0x80) != 0 {
53 let (index, consumed) = decode_integer(&data[pos..], 7, 0x7F)?;
54 pos += consumed + 1;
55
56 let (name, value) = self.get_entry(index)?;
57 cb(name, value);
58 continue;
59 }
60
61 if (byte & 0xC0) == 0x40 {
64 let (name_idx, consumed) = decode_integer(&data[pos..], 6, 0x3F)?;
65 pos += consumed + 1;
66
67 let name = if name_idx == 0 {
68 let (name_bytes, name_consumed) = self.decode_string_literal(&data[pos..])?;
70 pos += name_consumed;
71 name_bytes
72 } else {
73 let (name_bytes, _) = self.get_entry(name_idx)?;
75 name_bytes.to_vec()
76 };
77
78 let (value_bytes, value_consumed) = self.decode_string_literal(&data[pos..])?;
79 pos += value_consumed;
80
81 cb(&name, &value_bytes);
82
83 self.dynamic_table.add(name, value_bytes);
85 continue;
86 }
87
88 if (byte & 0xF0) == 0x00 {
91 let (name_idx, consumed) = decode_integer(&data[pos..], 4, 0x0F)?;
92 pos += consumed + 1;
93
94 let name = if name_idx == 0 {
95 let (name_bytes, name_consumed) = self.decode_string_literal(&data[pos..])?;
96 pos += name_consumed;
97 name_bytes
98 } else {
99 let (name_bytes, _) = self.get_entry(name_idx)?;
100 name_bytes.to_vec()
101 };
102
103 let (value_bytes, value_consumed) = self.decode_string_literal(&data[pos..])?;
104 pos += value_consumed;
105
106 cb(&name, &value_bytes);
107 continue;
108 }
109
110 if (byte & 0xF0) == 0x10 {
113 let (name_idx, consumed) = decode_integer(&data[pos..], 4, 0x0F)?;
114 pos += consumed + 1;
115
116 let name = if name_idx == 0 {
117 let (name_bytes, name_consumed) = self.decode_string_literal(&data[pos..])?;
118 pos += name_consumed;
119 name_bytes
120 } else {
121 let (name_bytes, _) = self.get_entry(name_idx)?;
122 name_bytes.to_vec()
123 };
124
125 let (value_bytes, value_consumed) = self.decode_string_literal(&data[pos..])?;
126 pos += value_consumed;
127
128 cb(&name, &value_bytes);
129 continue;
130 }
131
132 return Err(HpackError::Decode(format!(
133 "Invalid header field representation: 0x{:02x}",
134 byte
135 )));
136 }
137
138 Ok(())
139 }
140
141 fn get_entry(&self, index: usize) -> Result<(&[u8], &[u8]), HpackError> {
143 if index == 0 {
144 return Err(HpackError::InvalidIndex(0));
145 }
146
147 if index <= STATIC_TABLE_SIZE {
148 get_static_entry(index).ok_or(HpackError::InvalidIndex(index))
150 } else {
151 let dynamic_idx = index - STATIC_TABLE_SIZE;
153 self.dynamic_table
154 .get(dynamic_idx)
155 .map(|e| (e.name(), e.value()))
156 .ok_or(HpackError::InvalidIndex(index))
157 }
158 }
159
160 fn decode_string_literal(&self, data: &[u8]) -> Result<(Vec<u8>, usize), HpackError> {
162 if data.is_empty() {
163 return Err(HpackError::UnexpectedEof);
164 }
165
166 let h_flag = (data[0] & 0x80) != 0;
167 let (length, length_consumed) = decode_integer(data, 7, 0x7F)?;
168
169 let data_start = length_consumed + 1;
170 if data_start + length > data.len() {
171 return Err(HpackError::UnexpectedEof);
172 }
173
174 let string_data = &data[data_start..data_start + length];
175
176 let decoded = if h_flag {
177 huffman_decode(string_data)?
179 } else {
180 string_data.to_vec()
182 };
183
184 Ok((decoded, data_start + length))
185 }
186}
187
188impl Default for Decoder {
189 fn default() -> Self {
190 Self::new()
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::transport::h2::hpack_impl::encoder::Encoder;
198
199 #[test]
200 fn test_decode_indexed_header() {
201 let mut decoder = Decoder::new();
202 let data = [0x82];
204 let mut headers = Vec::new();
205 decoder
206 .decode_with_cb(&data, |name, value| {
207 headers.push((name.to_vec(), value.to_vec()));
208 })
209 .unwrap();
210
211 assert_eq!(headers.len(), 1);
212 assert_eq!(headers[0].0, b":method");
213 assert_eq!(headers[0].1, b"GET");
214 }
215
216 #[test]
217 fn test_decode_literal() {
218 let mut encoder = Encoder::new();
219 let headers = [(b"custom-key".as_slice(), b"custom-value".as_slice())];
220 let encoded = encoder.encode(&headers);
221
222 let mut decoder = Decoder::new();
223 let mut headers = Vec::new();
224 decoder
225 .decode_with_cb(&encoded, |name, value| {
226 headers.push((name.to_vec(), value.to_vec()));
227 })
228 .unwrap();
229
230 assert_eq!(headers.len(), 1);
231 assert_eq!(headers[0].0, b"custom-key");
232 assert_eq!(headers[0].1, b"custom-value");
233 }
234
235 #[test]
236 fn test_round_trip() {
237 let headers = [
238 (b":method".as_slice(), b"GET".as_slice()),
239 (b":scheme".as_slice(), b"http".as_slice()),
240 (b":path".as_slice(), b"/".as_slice()),
241 (b":authority".as_slice(), b"www.example.com".as_slice()),
242 ];
243
244 let mut encoder = Encoder::new();
245 let encoded = encoder.encode(&headers);
246
247 let mut decoder = Decoder::new();
248 let mut decoded = Vec::new();
249 decoder
250 .decode_with_cb(&encoded, |name, value| {
251 decoded.push((name.to_vec(), value.to_vec()));
252 })
253 .unwrap();
254
255 assert_eq!(decoded.len(), 4);
256 assert_eq!(decoded[0].0, b":method");
257 assert_eq!(decoded[0].1, b"GET");
258 }
259}