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 chunk_encoded(encoded: Bytes, max_frame_size: usize) -> (Bytes, Vec<Bytes>) {
182 if encoded.len() <= max_frame_size {
183 return (encoded, Vec::new());
185 }
186
187 let mut chunks: Vec<Bytes> = encoded
189 .chunks(max_frame_size)
190 .map(Bytes::copy_from_slice)
191 .collect();
192
193 let first = chunks.remove(0);
194 (first, chunks)
195 }
196}
197
198pub struct HpackDecoder {
200 decoder: Decoder,
201}
202
203impl HpackDecoder {
204 pub fn new() -> Self {
206 Self {
207 decoder: Decoder::new(),
208 }
209 }
210
211 pub fn set_max_table_size(&mut self, size: usize) {
213 self.decoder.set_max_table_size(size);
214 }
215
216 pub fn decode(&mut self, data: &[u8]) -> Result<Vec<(String, String)>, String> {
218 let mut headers = Vec::new();
219
220 self.decoder
221 .decode_with_cb(data, |name, value| {
222 let name_str = String::from_utf8_lossy(name).into_owned();
223 let value_str = String::from_utf8_lossy(value).into_owned();
224 headers.push((name_str, value_str));
225 })
226 .map_err(|e| format!("HPACK decode error: {:?}", e))?;
227
228 Ok(headers)
229 }
230}
231
232impl Default for HpackDecoder {
233 fn default() -> Self {
234 Self::new()
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 #[test]
243 fn test_pseudo_order_chrome() {
244 let order = PseudoHeaderOrder::Chrome;
245 assert_eq!(order.akamai_string(), "m,s,a,p");
246 }
247
248 #[test]
249 fn test_pseudo_order_standard() {
250 let order = PseudoHeaderOrder::Standard;
251 assert_eq!(order.akamai_string(), "m,a,s,p");
252 }
253
254 #[test]
255 fn test_encoder_creates_valid_block() {
256 let mut encoder = HpackEncoder::chrome();
257 let block = encoder.encode_request(
258 "GET",
259 "https",
260 "example.com",
261 "/",
262 &[("user-agent".to_string(), "test".to_string())],
263 );
264
265 assert!(!block.is_empty());
267
268 let mut decoder = HpackDecoder::new();
270 let headers = decoder.decode(&block).unwrap();
271
272 assert_eq!(headers.len(), 5);
274
275 assert_eq!(headers[0].0, ":method");
277 assert_eq!(headers[0].1, "GET");
278 assert_eq!(headers[1].0, ":scheme");
279 assert_eq!(headers[1].1, "https");
280 assert_eq!(headers[2].0, ":authority");
281 assert_eq!(headers[2].1, "example.com");
282 assert_eq!(headers[3].0, ":path");
283 assert_eq!(headers[3].1, "/");
284 assert_eq!(headers[4].0, "user-agent");
285 assert_eq!(headers[4].1, "test");
286 }
287
288 #[test]
289 fn test_encoder_standard_order() {
290 let mut encoder = HpackEncoder::new(PseudoHeaderOrder::Standard);
291 let block = encoder.encode_request("GET", "https", "example.com", "/", &[]);
292
293 let mut decoder = HpackDecoder::new();
294 let headers = decoder.decode(&block).unwrap();
295
296 assert_eq!(headers[0].0, ":method");
298 assert_eq!(headers[1].0, ":authority");
299 assert_eq!(headers[2].0, ":scheme");
300 assert_eq!(headers[3].0, ":path");
301 }
302
303 #[test]
304 fn test_encoder_filters_connection_headers() {
305 let mut encoder = HpackEncoder::chrome();
306 let block = encoder.encode_request(
307 "GET",
308 "https",
309 "example.com",
310 "/",
311 &[
312 ("connection".to_string(), "keep-alive".to_string()),
313 ("keep-alive".to_string(), "timeout=5".to_string()),
314 ("user-agent".to_string(), "test".to_string()),
315 ],
316 );
317
318 let mut decoder = HpackDecoder::new();
319 let headers = decoder.decode(&block).unwrap();
320
321 assert_eq!(headers.len(), 5);
323 assert_eq!(headers[4].0, "user-agent");
324 }
325}