1use crate::transport::h2::hpack_impl::{Decoder, Encoder};
9use bytes::Bytes;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub enum PseudoHeaderOrder {
17 #[default]
19 Chrome,
20 Firefox,
22 Safari,
24 Standard,
26 Custom([u8; 4]),
28}
29
30impl PseudoHeaderOrder {
31 fn order(&self) -> [usize; 4] {
35 match self {
36 Self::Chrome => [0, 2, 1, 3], Self::Firefox => [0, 3, 1, 2], Self::Safari => [0, 2, 3, 1], Self::Standard => [0, 1, 2, 3], Self::Custom(order) => [
45 order[0] as usize,
46 order[1] as usize,
47 order[2] as usize,
48 order[3] as usize,
49 ],
50 }
51 }
52
53 pub fn akamai_string(&self) -> &'static str {
55 match self {
56 Self::Chrome => "m,s,a,p",
57 Self::Firefox => "m,p,a,s",
58 Self::Safari => "m,s,p,a",
59 Self::Standard => "m,a,s,p",
60 Self::Custom(_) => "custom",
61 }
62 }
63}
64
65pub struct HpackEncoder {
67 encoder: Encoder,
68 pseudo_order: PseudoHeaderOrder,
69}
70
71impl HpackEncoder {
72 pub fn new(pseudo_order: PseudoHeaderOrder) -> Self {
74 Self {
75 encoder: Encoder::new(),
76 pseudo_order,
77 }
78 }
79
80 pub fn chrome() -> Self {
82 Self::new(PseudoHeaderOrder::Chrome)
83 }
84
85 pub fn set_max_table_size(&mut self, size: usize) {
87 self.encoder.set_max_table_size(size);
88 }
89
90 pub fn encode_request(
95 &mut self,
96 method: &str,
97 scheme: &str,
98 authority: &str,
99 path: &str,
100 headers: &[(String, String)],
101 ) -> Bytes {
102 let pseudo_headers: [(&[u8], &[u8]); 4] = [
104 (b":method", method.as_bytes()),
105 (b":authority", authority.as_bytes()),
106 (b":scheme", scheme.as_bytes()),
107 (b":path", path.as_bytes()),
108 ];
109
110 let mut all_headers: Vec<(&[u8], &[u8])> = Vec::new();
112
113 let mut valid_headers: Vec<(String, &str)> = Vec::with_capacity(headers.len());
117
118 for (name, value) in headers {
120 if name.starts_with(':') {
122 continue;
123 }
124
125 if name.is_empty() {
127 continue;
128 }
129 if name
130 .as_bytes()
131 .iter()
132 .any(|&b| b < 0x21 || (b > 0x7E && b != 0x7F))
133 {
134 continue;
135 }
136
137 let name_lower = name.to_lowercase();
139
140 if name_lower == "connection"
142 || name_lower == "keep-alive"
143 || name_lower == "proxy-connection"
144 || name_lower == "transfer-encoding"
145 || name_lower == "upgrade"
146 {
147 continue;
148 }
149
150 if name_lower == "te" && value.to_lowercase() != "trailers" {
152 continue;
153 }
154
155 valid_headers.push((name_lower, value));
156 }
157
158 let order = self.pseudo_order.order();
160 for &idx in &order {
161 all_headers.push(pseudo_headers[idx]);
162 }
163
164 for (n, v) in &valid_headers {
166 all_headers.push((n.as_bytes(), v.as_bytes()));
167 }
168
169 let encoded = self.encoder.encode(&all_headers);
171 Bytes::from(encoded)
172 }
173
174 pub fn encode_extended_connect_websocket(
179 &mut self,
180 authority: &str,
181 scheme: &str,
182 path: &str,
183 headers: &[(String, String)],
184 ) -> Result<Bytes, String> {
185 if authority.is_empty() {
186 return Err(":authority must not be empty".to_string());
187 }
188 if scheme.is_empty() {
189 return Err(":scheme must not be empty".to_string());
190 }
191 if path.is_empty() {
192 return Err(":path must not be empty".to_string());
193 }
194
195 let pseudo_headers: [(&[u8], &[u8]); 5] = [
196 (b":method", b"CONNECT"),
197 (b":protocol", b"websocket"),
198 (b":scheme", scheme.as_bytes()),
199 (b":path", path.as_bytes()),
200 (b":authority", authority.as_bytes()),
201 ];
202
203 let mut valid_headers: Vec<(String, &str)> = Vec::with_capacity(headers.len());
204
205 for (name, value) in headers {
206 if name.starts_with(':') {
207 return Err(format!("RFC 8441 user pseudo-header rejected: {name}"));
208 }
209
210 if name.is_empty() {
211 return Err("RFC 8441 header name must not be empty".to_string());
212 }
213 if name
214 .as_bytes()
215 .iter()
216 .any(|&b| b < 0x21 || (b > 0x7E && b != 0x7F))
217 {
218 return Err(format!("RFC 8441 invalid header name rejected: {name}"));
219 }
220
221 let name_lower = name.to_lowercase();
222 if matches!(
223 name_lower.as_str(),
224 "connection"
225 | "upgrade"
226 | "host"
227 | "sec-websocket-key"
228 | "sec-websocket-accept"
229 | "sec-websocket-extensions"
230 | "keep-alive"
231 | "proxy-connection"
232 | "transfer-encoding"
233 ) {
234 return Err(format!("RFC 8441 forbidden header rejected: {name_lower}"));
235 }
236
237 if name_lower == "te" && value.to_lowercase() != "trailers" {
238 return Err("RFC 8441 forbids TE values other than trailers".to_string());
239 }
240
241 valid_headers.push((name_lower, value));
242 }
243
244 let mut all_headers: Vec<(&[u8], &[u8])> =
245 Vec::with_capacity(pseudo_headers.len() + valid_headers.len());
246 all_headers.extend_from_slice(&pseudo_headers);
247 for (name, value) in &valid_headers {
248 all_headers.push((name.as_bytes(), value.as_bytes()));
249 }
250
251 let encoded = self.encoder.encode(&all_headers);
252 Ok(Bytes::from(encoded))
253 }
254
255 pub fn chunk_encoded(encoded: Bytes, max_frame_size: usize) -> (Bytes, Vec<Bytes>) {
263 if encoded.len() <= max_frame_size {
264 return (encoded, Vec::new());
266 }
267
268 let mut chunks: Vec<Bytes> = encoded
270 .chunks(max_frame_size)
271 .map(Bytes::copy_from_slice)
272 .collect();
273
274 let first = chunks.remove(0);
275 (first, chunks)
276 }
277}
278
279pub struct HpackDecoder {
281 decoder: Decoder,
282}
283
284impl HpackDecoder {
285 pub fn new() -> Self {
287 Self {
288 decoder: Decoder::new(),
289 }
290 }
291
292 pub fn set_max_table_size(&mut self, size: usize) {
294 self.decoder.set_max_table_size(size);
295 }
296
297 pub fn decode(&mut self, data: &[u8]) -> Result<Vec<(String, String)>, String> {
299 let mut headers = Vec::new();
300
301 self.decoder
302 .decode_with_cb(data, |name, value| {
303 let name_str = String::from_utf8_lossy(name).into_owned();
304 let value_str = String::from_utf8_lossy(value).into_owned();
305 headers.push((name_str, value_str));
306 })
307 .map_err(|e| format!("HPACK decode error: {:?}", e))?;
308
309 Ok(headers)
310 }
311}
312
313impl Default for HpackDecoder {
314 fn default() -> Self {
315 Self::new()
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn test_pseudo_order_chrome() {
325 let order = PseudoHeaderOrder::Chrome;
326 assert_eq!(order.akamai_string(), "m,s,a,p");
327 }
328
329 #[test]
330 fn test_pseudo_order_standard() {
331 let order = PseudoHeaderOrder::Standard;
332 assert_eq!(order.akamai_string(), "m,a,s,p");
333 }
334
335 #[test]
336 fn test_encoder_creates_valid_block() {
337 let mut encoder = HpackEncoder::chrome();
338 let block = encoder.encode_request(
339 "GET",
340 "https",
341 "example.com",
342 "/",
343 &[("user-agent".to_string(), "test".to_string())],
344 );
345
346 assert!(!block.is_empty());
348
349 let mut decoder = HpackDecoder::new();
351 let headers = decoder.decode(&block).unwrap();
352
353 assert_eq!(headers.len(), 5);
355
356 assert_eq!(headers[0].0, ":method");
358 assert_eq!(headers[0].1, "GET");
359 assert_eq!(headers[1].0, ":scheme");
360 assert_eq!(headers[1].1, "https");
361 assert_eq!(headers[2].0, ":authority");
362 assert_eq!(headers[2].1, "example.com");
363 assert_eq!(headers[3].0, ":path");
364 assert_eq!(headers[3].1, "/");
365 assert_eq!(headers[4].0, "user-agent");
366 assert_eq!(headers[4].1, "test");
367 }
368
369 #[test]
370 fn test_encoder_standard_order() {
371 let mut encoder = HpackEncoder::new(PseudoHeaderOrder::Standard);
372 let block = encoder.encode_request("GET", "https", "example.com", "/", &[]);
373
374 let mut decoder = HpackDecoder::new();
375 let headers = decoder.decode(&block).unwrap();
376
377 assert_eq!(headers[0].0, ":method");
379 assert_eq!(headers[1].0, ":authority");
380 assert_eq!(headers[2].0, ":scheme");
381 assert_eq!(headers[3].0, ":path");
382 }
383
384 #[test]
385 fn test_encoder_filters_connection_headers() {
386 let mut encoder = HpackEncoder::chrome();
387 let block = encoder.encode_request(
388 "GET",
389 "https",
390 "example.com",
391 "/",
392 &[
393 ("connection".to_string(), "keep-alive".to_string()),
394 ("keep-alive".to_string(), "timeout=5".to_string()),
395 ("user-agent".to_string(), "test".to_string()),
396 ],
397 );
398
399 let mut decoder = HpackDecoder::new();
400 let headers = decoder.decode(&block).unwrap();
401
402 assert_eq!(headers.len(), 5);
404 assert_eq!(headers[4].0, "user-agent");
405 }
406}