1use crate::http;
2use crate::http_common::{HeaderSource, HttpCookie, HttpHeader, ParsingMetadata};
3use hpack_patched::Decoder;
4use std::cell::RefCell;
5use std::collections::HashMap;
6use std::time::Instant;
7
8pub const HTTP2_CONNECTION_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
9
10#[derive(Debug, Clone, PartialEq)]
11#[repr(u8)]
12pub enum Http2FrameType {
13 Data = 0x0,
14 Headers = 0x1,
15 Priority = 0x2,
16 RstStream = 0x3,
17 Settings = 0x4,
18 PushPromise = 0x5,
19 Ping = 0x6,
20 GoAway = 0x7,
21 WindowUpdate = 0x8,
22 Continuation = 0x9,
23 Unknown(u8),
24}
25
26impl From<u8> for Http2FrameType {
27 fn from(frame_type: u8) -> Self {
28 match frame_type {
29 0x0 => Http2FrameType::Data,
30 0x1 => Http2FrameType::Headers,
31 0x2 => Http2FrameType::Priority,
32 0x3 => Http2FrameType::RstStream,
33 0x4 => Http2FrameType::Settings,
34 0x5 => Http2FrameType::PushPromise,
35 0x6 => Http2FrameType::Ping,
36 0x7 => Http2FrameType::GoAway,
37 0x8 => Http2FrameType::WindowUpdate,
38 0x9 => Http2FrameType::Continuation,
39 other => Http2FrameType::Unknown(other),
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
45pub struct Http2Frame {
46 pub frame_type: Http2FrameType,
47 pub stream_id: u32,
48 pub flags: u8,
49 pub payload: Vec<u8>,
50 pub length: u32,
51}
52
53#[derive(Debug, Clone, Default)]
54pub struct Http2Settings {
55 pub header_table_size: Option<u32>,
56 pub enable_push: Option<bool>,
57 pub max_concurrent_streams: Option<u32>,
58 pub initial_window_size: Option<u32>,
59 pub max_frame_size: Option<u32>,
60 pub max_header_list_size: Option<u32>,
61}
62
63#[derive(Debug, Clone)]
64pub struct Http2Stream {
65 pub stream_id: u32,
66 pub headers: Vec<HttpHeader>,
67 pub method: Option<String>,
68 pub path: Option<String>,
69 pub authority: Option<String>,
70 pub scheme: Option<String>,
71 pub status: Option<u16>,
72}
73
74pub struct Http2Config {
75 pub max_frame_size: u32,
76 pub max_streams: u32,
77 pub enable_hpack: bool,
78 pub strict_parsing: bool,
79}
80
81impl Default for Http2Config {
82 fn default() -> Self {
83 Self {
84 max_frame_size: 16384,
85 max_streams: 100,
86 enable_hpack: false,
87 strict_parsing: false,
88 }
89 }
90}
91
92#[derive(Debug, Clone)]
93pub struct Http2Request {
94 pub method: String,
95 pub path: String,
96 pub authority: Option<String>,
97 pub scheme: Option<String>,
98 pub version: http::Version,
99 pub headers: Vec<HttpHeader>,
100 pub cookies: Vec<HttpCookie>,
101 pub referer: Option<String>,
102 pub stream_id: u32,
103 pub parsing_metadata: ParsingMetadata,
104 pub frame_sequence: Vec<Http2FrameType>,
105 pub settings: Http2Settings,
106}
107
108#[derive(Debug, Clone)]
109pub struct Http2Response {
110 pub status: u16,
111 pub version: http::Version,
112 pub headers: Vec<HttpHeader>,
113 pub stream_id: u32,
114 pub parsing_metadata: ParsingMetadata,
115 pub frame_sequence: Vec<Http2FrameType>,
116 pub server: Option<String>,
117 pub content_type: Option<String>,
118}
119
120#[derive(Debug, Clone)]
121pub enum Http2ParseError {
122 InvalidPreface,
123 InvalidFrameHeader,
124 InvalidFrameLength(u32),
125 InvalidStreamId(u32),
126 FrameTooLarge(u32),
127 MissingRequiredHeaders,
128 InvalidPseudoHeader(String),
129 IncompleteFrame,
130 InvalidUtf8,
131 UnsupportedFeature(String),
132 HpackDecodingFailed,
133}
134
135impl std::fmt::Display for Http2ParseError {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 match self {
138 Self::InvalidPreface => write!(f, "Invalid HTTP/2 connection preface"),
139 Self::InvalidFrameHeader => write!(f, "Invalid HTTP/2 frame header"),
140 Self::InvalidFrameLength(len) => write!(f, "Invalid frame length: {len}"),
141 Self::InvalidStreamId(id) => write!(f, "Invalid stream ID: {id}"),
142 Self::FrameTooLarge(size) => write!(f, "Frame too large: {size} bytes"),
143 Self::MissingRequiredHeaders => write!(f, "Missing required pseudo-headers"),
144 Self::InvalidPseudoHeader(name) => write!(f, "Invalid pseudo-header: {name}"),
145 Self::IncompleteFrame => write!(f, "Incomplete HTTP/2 frame"),
146 Self::InvalidUtf8 => write!(f, "Invalid UTF-8 in HTTP/2 data"),
147 Self::UnsupportedFeature(feature) => write!(f, "Unsupported feature: {feature}"),
148 Self::HpackDecodingFailed => write!(f, "HPACK decoding failed"),
149 }
150 }
151}
152
153impl std::error::Error for Http2ParseError {}
154
155pub struct Http2Parser<'a> {
165 config: Http2Config,
166 hpack_decoder: RefCell<Decoder<'a>>,
167}
168
169impl<'a> Default for Http2Parser<'a> {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175impl<'a> Http2Parser<'a> {
176 pub fn new() -> Self {
177 Self {
178 config: Http2Config::default(),
179 hpack_decoder: RefCell::new(Decoder::new()),
180 }
181 }
182
183 pub fn parse_request(&self, data: &[u8]) -> Result<Option<Http2Request>, Http2ParseError> {
185 let start_time = Instant::now();
186
187 if !self.has_http2_preface(data) {
188 return Err(Http2ParseError::InvalidPreface);
189 }
190
191 let frame_data = &data[HTTP2_CONNECTION_PREFACE.len()..];
192 let frames = self.parse_frames(frame_data)?;
193
194 if frames.is_empty() {
195 return Ok(None);
196 }
197
198 let Some(stream_id) = self.find_primary_stream(&frames) else {
199 return Ok(None);
200 };
201 let stream = self.build_stream(stream_id, &frames)?;
202
203 let method = stream
204 .method
205 .ok_or(Http2ParseError::MissingRequiredHeaders)?;
206 let path = stream.path.ok_or(Http2ParseError::MissingRequiredHeaders)?;
207
208 let parsing_time = start_time.elapsed().as_nanos() as u64;
209 let frame_sequence: Vec<Http2FrameType> =
210 frames.iter().map(|f| f.frame_type.clone()).collect();
211
212 let mut headers = Vec::new();
213 let mut headers_map = HashMap::new();
214 let mut referer: Option<String> = None;
215 let mut cookie_headers: Vec<&HttpHeader> = Vec::new();
216
217 for header in &stream.headers {
218 let header_name_lower = header.name.to_lowercase();
219
220 if header_name_lower == "cookie" {
221 cookie_headers.push(header);
222 } else if header_name_lower == "referer" {
223 if let Some(ref value) = header.value {
224 referer = Some(value.clone());
225 }
226 } else {
227 if let Some(ref value) = header.value {
228 headers_map.insert(header_name_lower, value.clone());
229 }
230 headers.push(header.clone());
232 }
233 }
234
235 let cookies = self.parse_cookies_from_headers(&cookie_headers);
236
237 let metadata = ParsingMetadata {
238 header_count: headers.len(),
239 duplicate_headers: Vec::new(),
240 case_variations: HashMap::new(),
241 parsing_time_ns: parsing_time,
242 has_malformed_headers: false,
243 request_line_length: 0,
244 total_headers_length: headers
245 .iter()
246 .map(|h| {
247 h.name
248 .len()
249 .saturating_add(h.value.as_ref().map_or(0, |v| v.len()))
250 })
251 .sum(),
252 };
253
254 Ok(Some(Http2Request {
255 method,
256 path,
257 authority: stream.authority,
258 scheme: stream.scheme,
259 version: http::Version::V20,
260 headers,
261 cookies,
262 referer,
263 stream_id,
264 parsing_metadata: metadata,
265 frame_sequence,
266 settings: self.extract_settings(&frames),
267 }))
268 }
269
270 pub fn parse_response(&self, data: &[u8]) -> Result<Option<Http2Response>, Http2ParseError> {
272 let start_time = Instant::now();
273
274 let frames = self.parse_frames(data)?;
275
276 if frames.is_empty() {
277 return Ok(None);
278 }
279
280 let Some(stream_id) = self.find_primary_stream(&frames) else {
281 return Ok(None);
282 };
283 let stream = self.build_stream(stream_id, &frames)?;
284
285 let status = stream
286 .status
287 .ok_or(Http2ParseError::MissingRequiredHeaders)?;
288
289 let parsing_time = start_time.elapsed().as_nanos() as u64;
290 let frame_sequence: Vec<Http2FrameType> =
291 frames.iter().map(|f| f.frame_type.clone()).collect();
292
293 let mut headers_map = HashMap::new();
294 for header in &stream.headers {
295 if let Some(ref value) = header.value {
296 headers_map.insert(header.name.to_lowercase(), value.clone());
297 }
298 }
299
300 let metadata = ParsingMetadata {
301 header_count: stream.headers.len(),
302 duplicate_headers: Vec::new(),
303 case_variations: HashMap::new(),
304 parsing_time_ns: parsing_time,
305 has_malformed_headers: false,
306 request_line_length: 0,
307 total_headers_length: stream
308 .headers
309 .iter()
310 .map(|h| {
311 h.name
312 .len()
313 .saturating_add(h.value.as_ref().map_or(0, |v| v.len()))
314 })
315 .sum(),
316 };
317
318 Ok(Some(Http2Response {
319 status,
320 version: http::Version::V20,
321 headers: stream.headers,
322 stream_id,
323 parsing_metadata: metadata,
324 frame_sequence,
325 server: headers_map.get("server").cloned(),
326 content_type: headers_map.get("content-type").cloned(),
327 }))
328 }
329
330 fn has_http2_preface(&self, data: &[u8]) -> bool {
331 data.starts_with(HTTP2_CONNECTION_PREFACE)
332 }
333
334 fn parse_frames(&self, data: &[u8]) -> Result<Vec<Http2Frame>, Http2ParseError> {
335 let mut frames = Vec::new();
336 let mut remaining = data;
337
338 while remaining.len() >= 9 {
339 let frame_length = u32::from_be_bytes([0, remaining[0], remaining[1], remaining[2]]);
341 let frame_total_size = match usize::try_from(9_u32.saturating_add(frame_length)) {
342 Ok(size) => size,
343 Err(_) => break, };
345
346 if remaining.len() < frame_total_size {
347 break;
349 }
350
351 match self.parse_single_frame(remaining) {
352 Ok((rest, frame)) => {
353 frames.push(frame);
354 remaining = rest;
355 }
356 Err(_) => {
357 break;
359 }
360 }
361 }
362
363 Ok(frames)
364 }
365
366 fn parse_single_frame<'b>(
367 &self,
368 data: &'b [u8],
369 ) -> Result<(&'b [u8], Http2Frame), Http2ParseError> {
370 if data.len() < 9 {
371 return Err(Http2ParseError::IncompleteFrame);
372 }
373
374 let length = u32::from_be_bytes([0, data[0], data[1], data[2]]);
375 let frame_type_byte = data[3];
376 let flags = data[4];
377 let stream_id = u32::from_be_bytes([data[5], data[6], data[7], data[8]]) & 0x7FFF_FFFF;
378
379 if length > self.config.max_frame_size {
380 return Err(Http2ParseError::FrameTooLarge(length));
381 }
382
383 let frame_total_size = match usize::try_from(9_u32.saturating_add(length)) {
384 Ok(size) => size,
385 Err(_) => return Err(Http2ParseError::FrameTooLarge(length)),
386 };
387
388 if data.len() < frame_total_size {
389 return Err(Http2ParseError::IncompleteFrame);
390 }
391
392 let payload_start = 9;
393 let payload_end = frame_total_size;
394 let payload = data[payload_start..payload_end].to_vec();
395
396 let frame = Http2Frame {
397 frame_type: Http2FrameType::from(frame_type_byte),
398 stream_id,
399 flags,
400 payload,
401 length,
402 };
403
404 Ok((&data[payload_end..], frame))
405 }
406
407 fn find_primary_stream(&self, frames: &[Http2Frame]) -> Option<u32> {
408 for frame in frames {
409 if frame.stream_id > 0 && frame.frame_type == Http2FrameType::Headers {
410 return Some(frame.stream_id);
411 }
412 }
413 None
414 }
415
416 fn build_stream(
417 &self,
418 stream_id: u32,
419 frames: &[Http2Frame],
420 ) -> Result<Http2Stream, Http2ParseError> {
421 let mut headers = Vec::new();
422 let mut method = None;
423 let mut path = None;
424 let mut authority = None;
425 let mut scheme = None;
426 let mut status = None;
427
428 let stream_frames: Vec<&Http2Frame> =
429 frames.iter().filter(|f| f.stream_id == stream_id).collect();
430
431 for frame in stream_frames {
432 match frame.frame_type {
433 Http2FrameType::Headers | Http2FrameType::Continuation => {
434 let frame_headers = self.parse_headers_payload(&frame.payload)?;
435 for header in frame_headers {
436 match header.name.as_str() {
437 ":method" => method = Some(header.value.clone().unwrap_or_default()),
438 ":path" => path = Some(header.value.clone().unwrap_or_default()),
439 ":authority" => {
440 authority = Some(header.value.clone().unwrap_or_default())
441 }
442 ":scheme" => scheme = Some(header.value.clone().unwrap_or_default()),
443 ":status" => {
444 status = header.value.as_ref().and_then(|v| v.parse().ok())
445 }
446 _ => headers.push(header),
447 }
448 }
449 }
450 _ => {}
451 }
452 }
453
454 Ok(Http2Stream {
455 stream_id,
456 headers,
457 method,
458 path,
459 authority,
460 scheme,
461 status,
462 })
463 }
464
465 fn parse_headers_payload(&self, payload: &[u8]) -> Result<Vec<HttpHeader>, Http2ParseError> {
466 let headers = self
467 .hpack_decoder
468 .borrow_mut()
469 .decode(payload)
470 .map_err(|_| Http2ParseError::HpackDecodingFailed)?;
471
472 let mut http_headers = Vec::new();
473
474 for (position, (name, value)) in headers.iter().enumerate() {
475 let name_str = String::from_utf8_lossy(name).to_string();
476 let value_str = String::from_utf8_lossy(value);
477 let value_opt = if value_str.is_empty() {
478 None
479 } else {
480 Some(value_str.to_string())
481 };
482
483 http_headers.push(HttpHeader {
484 name: name_str,
485 value: value_opt,
486 position,
487 source: HeaderSource::Http2Header,
488 });
489 }
490
491 Ok(http_headers)
492 }
493
494 fn extract_settings(&self, frames: &[Http2Frame]) -> Http2Settings {
495 let mut settings = Http2Settings::default();
496
497 for frame in frames {
498 if frame.frame_type == Http2FrameType::Settings {
499 let payload = &frame.payload;
500 for chunk in payload.chunks_exact(6) {
501 if chunk.len() == 6 {
502 let id = u16::from_be_bytes([chunk[0], chunk[1]]);
503 let value = u32::from_be_bytes([chunk[2], chunk[3], chunk[4], chunk[5]]);
504
505 match id {
506 1 => settings.header_table_size = Some(value),
507 2 => settings.enable_push = Some(value != 0),
508 3 => settings.max_concurrent_streams = Some(value),
509 4 => settings.initial_window_size = Some(value),
510 5 => settings.max_frame_size = Some(value),
511 6 => settings.max_header_list_size = Some(value),
512 _ => {}
513 }
514 }
515 }
516 }
517 }
518
519 settings
520 }
521
522 fn parse_cookies_from_headers(&self, cookie_headers: &[&HttpHeader]) -> Vec<HttpCookie> {
524 let mut cookies = Vec::new();
525 let mut position = 0;
526
527 for header in cookie_headers {
528 if let Some(ref cookie_value) = header.value {
529 for cookie_str in cookie_value.split(';') {
531 let cookie_str = cookie_str.trim();
532 if cookie_str.is_empty() {
533 continue;
534 }
535
536 if let Some(eq_pos) = cookie_str.find('=') {
537 let name = cookie_str[..eq_pos].trim().to_string();
538 let value = Some(
539 cookie_str
540 .get(eq_pos.saturating_add(1)..)
541 .unwrap_or("")
542 .trim()
543 .to_string(),
544 );
545 cookies.push(HttpCookie {
546 name,
547 value,
548 position,
549 });
550 } else {
551 cookies.push(HttpCookie {
552 name: cookie_str.to_string(),
553 value: None,
554 position,
555 });
556 }
557 position = position.saturating_add(1);
558 }
559 }
560 }
561
562 cookies
563 }
564}
565
566pub fn is_http2_traffic(data: &[u8]) -> bool {
567 data.starts_with(HTTP2_CONNECTION_PREFACE)
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573
574 fn assert_parser_error<T>(
575 result: Result<Option<T>, Http2ParseError>,
576 expected_discriminant: Http2ParseError,
577 ) {
578 match result {
579 Err(actual) => assert_eq!(
580 std::mem::discriminant(&actual),
581 std::mem::discriminant(&expected_discriminant),
582 "Expected error type {expected_discriminant:?} but got {actual:?}"
583 ),
584 Ok(_) => panic!("Expected error {expected_discriminant:?} but got Ok"),
585 }
586 }
587
588 fn create_http2_frame(frame_type: u8, stream_id: u32, payload: &[u8]) -> Vec<u8> {
589 let mut frame = Vec::new();
590
591 let length = payload.len() as u32;
593 frame.push(((length >> 16) & 0xFF) as u8);
594 frame.push(((length >> 8) & 0xFF) as u8);
595 frame.push((length & 0xFF) as u8);
596
597 frame.push(frame_type);
599
600 frame.push(0x00);
602
603 frame.extend_from_slice(&(stream_id & 0x7FFFFFFF).to_be_bytes());
605
606 frame.extend_from_slice(payload);
608
609 frame
610 }
611
612 fn create_http2_request_with_preface(frames: &[Vec<u8>]) -> Vec<u8> {
613 let mut data = Vec::from(HTTP2_CONNECTION_PREFACE);
614 for frame in frames {
615 data.extend_from_slice(frame);
616 }
617 data
618 }
619
620 #[test]
621 fn test_http2_preface_detection() {
622 let http2_data = HTTP2_CONNECTION_PREFACE;
623 assert!(is_http2_traffic(http2_data));
624
625 let http1_data = b"GET / HTTP/1.1\r\n";
626 assert!(!is_http2_traffic(http1_data));
627
628 let partial_preface = &HTTP2_CONNECTION_PREFACE[..10];
630 assert!(!is_http2_traffic(partial_preface));
631
632 assert!(!is_http2_traffic(&[]));
634 }
635
636 #[test]
637 fn test_frame_type_conversion() {
638 assert_eq!(Http2FrameType::from(0x0), Http2FrameType::Data);
639 assert_eq!(Http2FrameType::from(0x1), Http2FrameType::Headers);
640 assert_eq!(Http2FrameType::from(0x2), Http2FrameType::Priority);
641 assert_eq!(Http2FrameType::from(0x3), Http2FrameType::RstStream);
642 assert_eq!(Http2FrameType::from(0x4), Http2FrameType::Settings);
643 assert_eq!(Http2FrameType::from(0x5), Http2FrameType::PushPromise);
644 assert_eq!(Http2FrameType::from(0x6), Http2FrameType::Ping);
645 assert_eq!(Http2FrameType::from(0x7), Http2FrameType::GoAway);
646 assert_eq!(Http2FrameType::from(0x8), Http2FrameType::WindowUpdate);
647 assert_eq!(Http2FrameType::from(0x9), Http2FrameType::Continuation);
648 assert_eq!(Http2FrameType::from(0xFF), Http2FrameType::Unknown(0xFF));
649 }
650
651 #[test]
652 fn test_invalid_preface() {
653 let parser = Http2Parser::new();
654 let invalid_data = b"GET / HTTP/1.1\r\n\r\n";
655
656 assert_parser_error(
657 parser.parse_request(invalid_data),
658 Http2ParseError::InvalidPreface,
659 );
660 }
661
662 #[test]
663 fn test_empty_data() {
664 let parser = Http2Parser::new();
665
666 assert_parser_error(parser.parse_request(&[]), Http2ParseError::InvalidPreface);
668
669 let result = parser.parse_request(HTTP2_CONNECTION_PREFACE);
671 match result {
672 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request without frames"),
674 Err(e) => panic!("Should not error: {e:?}"),
675 }
676 }
677
678 #[test]
679 fn test_incomplete_frame_header() {
680 let parser = Http2Parser::new();
681
682 let mut data = Vec::from(HTTP2_CONNECTION_PREFACE);
684 data.extend_from_slice(&[0x00, 0x00, 0x00, 0x01]); let result = parser.parse_request(&data);
687 match result {
688 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request with incomplete data"),
690 Err(e) => panic!("Should not error: {e:?}"),
691 }
692 }
693
694 #[test]
695 fn test_frame_too_large() {
696 let parser = Http2Parser::new();
697
698 let large_length = 20000u32;
700 let mut frame = Vec::new();
701
702 frame.push(((large_length >> 16) & 0xFF) as u8);
704 frame.push(((large_length >> 8) & 0xFF) as u8);
705 frame.push((large_length & 0xFF) as u8);
706
707 frame.extend_from_slice(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x01]); let data = create_http2_request_with_preface(&[frame]);
711 let result = parser.parse_request(&data);
712 match result {
713 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request with frame too large"),
715 Err(e) => panic!("Should not error: {e:?}"),
716 }
717 }
718
719 #[test]
720 fn test_incomplete_frame_payload() {
721 let parser = Http2Parser::new();
722
723 let mut frame = Vec::new();
725 frame.extend_from_slice(&[0x00, 0x00, 0x64]); frame.extend_from_slice(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x01]); frame.extend_from_slice(&[0x00; 50]); let data = create_http2_request_with_preface(&[frame]);
730 let result = parser.parse_request(&data);
731 match result {
732 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request with incomplete payload"),
734 Err(e) => panic!("Should not error: {e:?}"),
735 }
736 }
737
738 #[test]
739 fn test_zero_length_frame() {
740 let parser = Http2Parser::new();
741
742 let frame = create_http2_frame(0x04, 0, &[]); let data = create_http2_request_with_preface(&[frame]);
745
746 let result = parser.parse_request(&data);
747 match result {
748 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request without headers frame"),
750 Err(e) => panic!("Should not error: {e:?}"),
751 }
752 }
753
754 #[test]
755 fn test_maximum_valid_frame_size() {
756 let parser = Http2Parser::new();
757
758 let max_payload = vec![0x00; 16384];
760 let frame = create_http2_frame(0x00, 1, &max_payload); let data = create_http2_request_with_preface(&[frame]);
762
763 let result = parser.parse_request(&data);
764 match result {
765 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request without headers frame"),
767 Err(e) => panic!("Should not error: {e:?}"),
768 }
769 }
770
771 #[test]
772 fn test_invalid_stream_id_zero_for_headers() {
773 let parser = Http2Parser::new();
774
775 let frame = create_http2_frame(0x01, 0, &[0x00]); let data = create_http2_request_with_preface(&[frame]);
778
779 let result = parser.parse_request(&data);
780 match result {
781 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request with invalid stream ID"),
783 Err(e) => panic!("Should not error: {e:?}"),
784 }
785 }
786
787 #[test]
788 fn test_multiple_frames_parsing() {
789 let parser = Http2Parser::new();
790
791 let settings_frame = create_http2_frame(0x04, 0, &[]); let headers_frame = create_http2_frame(0x01, 1, &[0x00]); let data = create_http2_request_with_preface(&[settings_frame, headers_frame]);
796 let result = parser.parse_request(&data);
797
798 match result {
800 Ok(None) => {
801 }
803 Err(Http2ParseError::HpackDecodingFailed) => {
804 }
806 other => {
807 panic!("Unexpected result: {other:?}");
808 }
809 }
810 }
811
812 #[test]
813 fn test_arithmetic_overflow_protection() {
814 let parser = Http2Parser::new();
815
816 let mut frame = Vec::new();
818 frame.extend_from_slice(&[0xFF, 0xFF, 0xFF]); frame.extend_from_slice(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x01]); let data = create_http2_request_with_preface(&[frame]);
822 let result = parser.parse_request(&data);
823
824 assert!(result.is_ok());
826 }
827
828 #[test]
829 fn test_hpack_decoding_failure() {
830 let parser = Http2Parser::new();
831
832 let invalid_hpack = vec![0xFF; 10]; let frame = create_http2_frame(0x01, 1, &invalid_hpack);
835 let data = create_http2_request_with_preface(&[frame]);
836
837 let result = parser.parse_request(&data);
838 match result {
840 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request with HPACK decoding failure"),
842 Err(_) => {} }
844 }
845
846 #[test]
847 fn test_missing_required_headers() {
848 let parser = Http2Parser::new();
849
850 let frame = create_http2_frame(0x01, 1, &[0x00]);
853 let data = create_http2_request_with_preface(&[frame]);
854
855 let result = parser.parse_request(&data);
856 match result {
858 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request with missing required headers"),
860 Err(_) => {} }
862 }
863
864 #[test]
865 fn test_response_parsing_without_preface() {
866 let parser = Http2Parser::new();
867
868 let frame = create_http2_frame(0x01, 1, &[0x00]); let result = parser.parse_response(&frame);
872 match result {
874 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a response with HPACK failure"),
876 Err(_) => {} }
878 }
879
880 #[test]
881 fn test_frame_parsing_edge_cases() {
882 let parser = Http2Parser::new();
883
884 let test_cases = [
886 {
888 let mut frame = create_http2_frame(0x01, 1, &[0x00]);
889 frame[5] |= 0x80;
891 frame
892 },
893 create_http2_frame(0x09, 1, &[0x00]),
895 create_http2_frame(0xFF, 1, &[0x00]),
897 ];
898
899 for (i, frame) in test_cases.iter().enumerate() {
900 let data = create_http2_request_with_preface(std::slice::from_ref(frame));
901 let result = parser.parse_request(&data);
902
903 assert!(result.is_ok() || result.is_err(), "Test case {i} failed");
905 }
906 }
907
908 #[test]
909 fn test_settings_frame_parsing() {
910 let parser = Http2Parser::new();
911
912 let mut settings_payload = Vec::new();
914
915 settings_payload.extend_from_slice(&[0x00, 0x01]); settings_payload.extend_from_slice(&[0x00, 0x00, 0x10, 0x00]); settings_payload.extend_from_slice(&[0x00, 0x02]); settings_payload.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); let settings_frame = create_http2_frame(0x04, 0, &settings_payload);
924 let headers_frame = create_http2_frame(0x01, 1, &[0x00]);
925
926 let data = create_http2_request_with_preface(&[settings_frame, headers_frame]);
927 let result = parser.parse_request(&data);
928
929 assert!(result.is_ok() || result.is_err());
931 }
932
933 #[test]
934 fn test_cookie_parsing_edge_cases() {
935 use crate::http_common::HttpHeader;
936 let parser = Http2Parser::new();
937
938 let test_cases = vec![
939 ("", 0),
940 ("name=value", 1),
941 ("name=", 1),
942 ("name", 1),
943 ("name=value; other=test", 2),
944 (" name = value ", 1),
945 ("name=value;", 1),
946 (";name=value", 1),
947 ("name=value;;other=test", 2),
948 ];
949
950 for (cookie_str, expected_count) in test_cases {
951 let headers = [HttpHeader {
952 name: "cookie".to_string(),
953 value: if cookie_str.is_empty() {
954 None
955 } else {
956 Some(cookie_str.to_string())
957 },
958 position: 0,
959 source: crate::http_common::HeaderSource::Http2Header,
960 }];
961
962 let cookie_headers: Vec<&HttpHeader> = headers
963 .iter()
964 .filter(|h| h.name.to_lowercase() == "cookie")
965 .collect();
966 let cookies = parser.parse_cookies_from_headers(&cookie_headers);
967 assert_eq!(
968 cookies.len(),
969 expected_count,
970 "Failed for case: '{cookie_str}'"
971 );
972
973 match cookie_str {
974 "" => {
975 assert!(cookies.is_empty());
976 }
977 "name=value" => {
978 assert_eq!(cookies[0].name, "name");
979 assert_eq!(cookies[0].value, Some("value".to_string()));
980 }
981 "name=" => {
982 assert_eq!(cookies[0].name, "name");
983 assert_eq!(cookies[0].value, Some("".to_string()));
984 }
985 "name" => {
986 assert_eq!(cookies[0].name, "name");
987 assert_eq!(cookies[0].value, None);
988 }
989 "name=value; other=test" => {
990 assert_eq!(cookies[0].name, "name");
991 assert_eq!(cookies[0].value, Some("value".to_string()));
992 assert_eq!(cookies[1].name, "other");
993 assert_eq!(cookies[1].value, Some("test".to_string()));
994 }
995 " name = value " => {
996 assert_eq!(cookies[0].name, "name");
997 assert_eq!(cookies[0].value, Some("value".to_string()));
998 }
999 "name=value;" => {
1000 assert_eq!(cookies[0].name, "name");
1001 assert_eq!(cookies[0].value, Some("value".to_string()));
1002 }
1003 ";name=value" => {
1004 assert_eq!(cookies[0].name, "name");
1005 assert_eq!(cookies[0].value, Some("value".to_string()));
1006 }
1007 "name=value;;other=test" => {
1008 assert_eq!(cookies[0].name, "name");
1009 assert_eq!(cookies[0].value, Some("value".to_string()));
1010 assert_eq!(cookies[1].name, "other");
1011 assert_eq!(cookies[1].value, Some("test".to_string()));
1012 }
1013 _ => {}
1014 }
1015 }
1016 }
1017
1018 #[test]
1019 fn test_multiple_cookie_headers_http2() {
1020 use crate::http_common::HttpHeader;
1021 let parser = Http2Parser::new();
1022
1023 let headers = [
1025 HttpHeader {
1026 name: "cookie".to_string(),
1027 value: Some("session_id=abc123".to_string()),
1028 position: 0,
1029 source: crate::http_common::HeaderSource::Http2Header,
1030 },
1031 HttpHeader {
1032 name: "cookie".to_string(),
1033 value: Some("user_id=456".to_string()),
1034 position: 1,
1035 source: crate::http_common::HeaderSource::Http2Header,
1036 },
1037 HttpHeader {
1038 name: "cookie".to_string(),
1039 value: Some("theme=dark; lang=en".to_string()),
1040 position: 2,
1041 source: crate::http_common::HeaderSource::Http2Header,
1042 },
1043 ];
1044
1045 let cookie_headers: Vec<&HttpHeader> = headers
1046 .iter()
1047 .filter(|h| h.name.to_lowercase() == "cookie")
1048 .collect();
1049 let cookies = parser.parse_cookies_from_headers(&cookie_headers);
1050
1051 assert_eq!(cookies.len(), 4);
1052 assert_eq!(cookies[0].name, "session_id");
1053 assert_eq!(cookies[0].value, Some("abc123".to_string()));
1054 assert_eq!(cookies[1].name, "user_id");
1055 assert_eq!(cookies[1].value, Some("456".to_string()));
1056 assert_eq!(cookies[2].name, "theme");
1057 assert_eq!(cookies[2].value, Some("dark".to_string()));
1058 assert_eq!(cookies[3].name, "lang");
1059 assert_eq!(cookies[3].value, Some("en".to_string()));
1060 }
1061
1062 #[test]
1063 fn test_security_malformed_frames() {
1064 let parser = Http2Parser::new();
1065
1066 let malicious_cases = [
1068 vec![0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01],
1070 vec![0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF],
1072 vec![0x7F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
1074 vec![0x00, 0x10, 0x00, 0xFE, 0xFF, 0x80, 0x00, 0x00, 0x01],
1076 ];
1077
1078 for malicious_frame in malicious_cases.iter() {
1079 let data = create_http2_request_with_preface(std::slice::from_ref(malicious_frame));
1080 let result = parser.parse_request(&data);
1081
1082 match result {
1084 Ok(_) | Err(_) => {
1085 }
1087 }
1088
1089 let response_result = parser.parse_response(malicious_frame);
1091 match response_result {
1092 Ok(_) | Err(_) => {
1093 }
1095 }
1096 }
1097 }
1098
1099 #[test]
1100 fn test_memory_exhaustion_protection() {
1101 let parser = Http2Parser::new();
1102
1103 let mut frames = Vec::new();
1105 for i in 0..1000 {
1106 let frame = create_http2_frame(0x00, (i % 100) + 1, &[0x00]); frames.push(frame);
1108 }
1109
1110 let data = create_http2_request_with_preface(&frames);
1111 let result = parser.parse_request(&data);
1112
1113 assert!(result.is_ok());
1115 }
1116
1117 #[test]
1118 fn test_stream_id_edge_cases() {
1119 let parser = Http2Parser::new();
1120
1121 let test_cases = vec![
1122 (0x00000001, true), (0x00000003, true), (0x00000002, true), (0x7FFFFFFF, true), (0x80000001, true), (0x00000000, false), ];
1129
1130 for (stream_id, should_find_stream) in test_cases {
1131 let frame = create_http2_frame(0x01, stream_id, &[0x00]); let data = create_http2_request_with_preface(&[frame]);
1133 let result = parser.parse_request(&data);
1134
1135 if should_find_stream {
1136 match result {
1138 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request with HPACK failure"),
1140 Err(_) => {} }
1142 } else {
1143 match result {
1145 Ok(None) => {} Ok(Some(_)) => panic!("Should not return a request with invalid stream"),
1147 Err(e) => panic!("Should not error for invalid stream: {e:?}"),
1148 }
1149 }
1150 }
1151 }
1152
1153 #[test]
1154 fn test_frame_flag_handling() {
1155 let parser = Http2Parser::new();
1156
1157 let flag_cases = vec![
1159 0x00, 0x01, 0x04, 0x05, 0x08, 0x20, 0xFF, ];
1167
1168 for flags in flag_cases {
1169 let mut frame = create_http2_frame(0x01, 1, &[0x00]); frame[4] = flags; let data = create_http2_request_with_preface(&[frame]);
1173 let result = parser.parse_request(&data);
1174
1175 assert!(result.is_ok() || result.is_err());
1177 }
1178 }
1179
1180 #[test]
1181 fn test_utf8_validation() {
1182 let parser = Http2Parser::new();
1183
1184 let invalid_utf8_cases = vec![
1186 vec![0xFF, 0xFE, 0xFD], vec![0x80, 0x80, 0x80], vec![0xC0, 0x80], vec![0xED, 0xA0, 0x80], ];
1191
1192 for invalid_utf8 in invalid_utf8_cases {
1193 let frame = create_http2_frame(0x01, 1, &invalid_utf8);
1194 let data = create_http2_request_with_preface(&[frame]);
1195 let result = parser.parse_request(&data);
1196
1197 match result {
1199 Ok(_) => {
1200 }
1202 Err(Http2ParseError::HpackDecodingFailed) => {
1203 }
1205 Err(Http2ParseError::InvalidUtf8) => {
1206 }
1208 Err(_) => {
1209 }
1211 }
1212 }
1213 }
1214
1215 #[test]
1216 fn test_error_display_formatting() {
1217 let errors = vec![
1219 Http2ParseError::InvalidPreface,
1220 Http2ParseError::InvalidFrameHeader,
1221 Http2ParseError::InvalidFrameLength(12345),
1222 Http2ParseError::InvalidStreamId(67890),
1223 Http2ParseError::FrameTooLarge(999999),
1224 Http2ParseError::MissingRequiredHeaders,
1225 Http2ParseError::InvalidPseudoHeader(":invalid".to_string()),
1226 Http2ParseError::IncompleteFrame,
1227 Http2ParseError::InvalidUtf8,
1228 Http2ParseError::UnsupportedFeature("test".to_string()),
1229 Http2ParseError::HpackDecodingFailed,
1230 ];
1231
1232 for error in errors {
1233 let formatted = format!("{error}");
1234 assert!(!formatted.is_empty());
1235 assert!(!formatted.contains("Debug")); }
1237 }
1238
1239 #[test]
1240 fn test_config_edge_cases() {
1241 let config = Http2Config {
1243 max_frame_size: 1, strict_parsing: true,
1245 ..Default::default()
1246 };
1247
1248 let parser = Http2Parser {
1249 config,
1250 hpack_decoder: RefCell::new(Decoder::new()),
1251 };
1252
1253 let frame = create_http2_frame(0x01, 1, &[0x00, 0x00]); let data = create_http2_request_with_preface(&[frame]);
1256 let result = parser.parse_request(&data);
1257
1258 assert!(result.is_ok());
1260 }
1261
1262 #[test]
1263 fn test_multiple_streams_handling() {
1264 let parser = Http2Parser::new();
1265
1266 let frame1 = create_http2_frame(0x01, 1, &[0x00]); let frame2 = create_http2_frame(0x01, 3, &[0x00]); let frame3 = create_http2_frame(0x00, 1, &[0x48, 0x65, 0x6c, 0x6c, 0x6f]); let data = create_http2_request_with_preface(&[frame1, frame2, frame3]);
1272 let result = parser.parse_request(&data);
1273
1274 match result {
1276 Ok(Some(req)) => {
1277 assert_eq!(req.stream_id, 1); }
1279 Ok(None) => {}
1280 Err(_) => {}
1281 }
1282 }
1283
1284 #[test]
1285 fn test_continuation_frames() {
1286 let parser = Http2Parser::new();
1287
1288 let headers_frame = create_http2_frame(0x01, 1, &[0x00, 0x01]); let continuation_frame = create_http2_frame(0x09, 1, &[0x02, 0x03]); let data = create_http2_request_with_preface(&[headers_frame, continuation_frame]);
1293 let result = parser.parse_request(&data);
1294
1295 match result {
1297 Ok(_) | Err(_) => {} }
1299 }
1300
1301 #[test]
1302 fn test_settings_frame_with_invalid_payload() {
1303 let parser = Http2Parser::new();
1304
1305 let invalid_settings = create_http2_frame(0x04, 0, &[0x00, 0x01, 0x00, 0x00, 0x10]); let headers_frame = create_http2_frame(0x01, 1, &[0x00]);
1308
1309 let data = create_http2_request_with_preface(&[invalid_settings, headers_frame]);
1310 let result = parser.parse_request(&data);
1311
1312 match result {
1314 Ok(_) | Err(_) => {} }
1316 }
1317
1318 #[test]
1319 fn test_priority_frame_handling() {
1320 let parser = Http2Parser::new();
1321
1322 let priority_frame = create_http2_frame(0x02, 1, &[0x00, 0x00, 0x00, 0x02, 0x10]); let headers_frame = create_http2_frame(0x01, 1, &[0x00]);
1325
1326 let data = create_http2_request_with_preface(&[priority_frame, headers_frame]);
1327 let result = parser.parse_request(&data);
1328
1329 match result {
1331 Ok(_) | Err(_) => {} }
1333 }
1334
1335 #[test]
1336 fn test_window_update_frame() {
1337 let parser = Http2Parser::new();
1338
1339 let window_update = create_http2_frame(0x08, 0, &[0x00, 0x00, 0x10, 0x00]); let headers_frame = create_http2_frame(0x01, 1, &[0x00]);
1342
1343 let data = create_http2_request_with_preface(&[window_update, headers_frame]);
1344 let result = parser.parse_request(&data);
1345
1346 match result {
1348 Ok(_) | Err(_) => {} }
1350 }
1351
1352 #[test]
1353 fn test_data_frame_without_headers() {
1354 let parser = Http2Parser::new();
1355
1356 let data_frame = create_http2_frame(0x00, 1, &[0x48, 0x65, 0x6c, 0x6c, 0x6f]); let data = create_http2_request_with_preface(&[data_frame]);
1360 let result = parser.parse_request(&data);
1361
1362 match result {
1364 Ok(None) => {} Ok(Some(_)) => panic!("Should not return request without HEADERS frame"),
1366 Err(_) => {} }
1368 }
1369
1370 #[test]
1371 fn test_stream_id_zero_for_headers() {
1372 let parser = Http2Parser::new();
1373
1374 let invalid_headers = create_http2_frame(0x01, 0, &[0x00]); let data = create_http2_request_with_preface(&[invalid_headers]);
1378 let result = parser.parse_request(&data);
1379
1380 match result {
1382 Ok(None) => {} Ok(Some(_)) => panic!("Should not return request with invalid stream ID"),
1384 Err(_) => {} }
1386 }
1387
1388 #[test]
1389 fn test_mixed_frame_types_sequence() {
1390 let parser = Http2Parser::new();
1391
1392 let settings_frame = create_http2_frame(0x04, 0, &[0x00, 0x02, 0x00, 0x00, 0x00, 0x01]); let window_update = create_http2_frame(0x08, 0, &[0x00, 0x00, 0x10, 0x00]); let headers_frame = create_http2_frame(0x01, 1, &[0x00]); let data_frame = create_http2_frame(0x00, 1, &[0x48, 0x65, 0x6c, 0x6c, 0x6f]); let data = create_http2_request_with_preface(&[
1399 settings_frame,
1400 window_update,
1401 headers_frame,
1402 data_frame,
1403 ]);
1404 let result = parser.parse_request(&data);
1405
1406 match result {
1408 Ok(Some(req)) => {
1409 assert_eq!(req.stream_id, 1);
1410 assert!(req.frame_sequence.len() >= 2); }
1412 Ok(None) => {} Err(_) => {} }
1415 }
1416
1417 #[test]
1418 fn test_response_with_invalid_status() {
1419 let parser = Http2Parser::new();
1420
1421 let headers_frame = create_http2_frame(0x01, 1, &[0x00]); let result = parser.parse_response(&headers_frame);
1425
1426 match result {
1428 Ok(None) => {} Err(Http2ParseError::MissingRequiredHeaders) => {} Err(Http2ParseError::HpackDecodingFailed) => {} other => panic!("Unexpected result: {other:?}"),
1432 }
1433 }
1434
1435 #[test]
1436 fn test_frame_flags_handling() {
1437 let parser = Http2Parser::new();
1438
1439 let mut frame = create_http2_frame(0x01, 1, &[0x00]);
1441 frame[4] = 0x05; let data = create_http2_request_with_preface(&[frame]);
1444 let result = parser.parse_request(&data);
1445
1446 match result {
1448 Ok(_) | Err(_) => {} }
1450 }
1451
1452 #[test]
1453 fn test_large_stream_id() {
1454 let parser = Http2Parser::new();
1455
1456 let max_stream_id = 0x7FFFFFFF;
1458 let headers_frame = create_http2_frame(0x01, max_stream_id, &[0x00]);
1459
1460 let data = create_http2_request_with_preface(&[headers_frame]);
1461 let result = parser.parse_request(&data);
1462
1463 match result {
1465 Ok(Some(req)) => {
1466 assert_eq!(req.stream_id, max_stream_id);
1467 }
1468 Ok(None) => {} Err(_) => {} }
1471 }
1472
1473 #[test]
1474 fn test_empty_payload_frames() {
1475 let parser = Http2Parser::new();
1476
1477 let empty_headers = create_http2_frame(0x01, 1, &[]); let empty_data = create_http2_frame(0x00, 1, &[]); let data = create_http2_request_with_preface(&[empty_headers, empty_data]);
1482 let result = parser.parse_request(&data);
1483
1484 match result {
1486 Ok(_) | Err(_) => {} }
1488 }
1489
1490 #[test]
1491 fn test_cookie_and_referer_excluded_from_headers_list_http2() {
1492 use crate::http_common::HttpHeader;
1493 let parser = Http2Parser::new();
1494
1495 let headers = [
1497 HttpHeader {
1498 name: ":method".to_string(),
1499 value: Some("GET".to_string()),
1500 position: 0,
1501 source: crate::http_common::HeaderSource::Http2Header,
1502 },
1503 HttpHeader {
1504 name: ":path".to_string(),
1505 value: Some("/page".to_string()),
1506 position: 1,
1507 source: crate::http_common::HeaderSource::Http2Header,
1508 },
1509 HttpHeader {
1510 name: ":authority".to_string(),
1511 value: Some("example.com".to_string()),
1512 position: 2,
1513 source: crate::http_common::HeaderSource::Http2Header,
1514 },
1515 HttpHeader {
1516 name: "cookie".to_string(),
1517 value: Some("session=abc123".to_string()),
1518 position: 3,
1519 source: crate::http_common::HeaderSource::Http2Header,
1520 },
1521 HttpHeader {
1522 name: "referer".to_string(),
1523 value: Some("https://google.com".to_string()),
1524 position: 4,
1525 source: crate::http_common::HeaderSource::Http2Header,
1526 },
1527 HttpHeader {
1528 name: "user-agent".to_string(),
1529 value: Some("test-browser".to_string()),
1530 position: 5,
1531 source: crate::http_common::HeaderSource::Http2Header,
1532 },
1533 HttpHeader {
1534 name: "accept".to_string(),
1535 value: Some("text/html".to_string()),
1536 position: 6,
1537 source: crate::http_common::HeaderSource::Http2Header,
1538 },
1539 ];
1540
1541 let cookie_headers: Vec<&HttpHeader> = headers
1542 .iter()
1543 .filter(|h| h.name.to_lowercase() == "cookie")
1544 .collect();
1545 let cookies = parser.parse_cookies_from_headers(&cookie_headers);
1546
1547 assert_eq!(cookies.len(), 1);
1548 assert_eq!(cookies[0].name, "session");
1549 assert_eq!(cookies[0].value, Some("abc123".to_string()));
1550
1551 let mut filtered_headers = Vec::new();
1552 let mut referer_found: Option<String> = None;
1553
1554 for header in headers {
1555 let header_name_lower = header.name.to_lowercase();
1556
1557 if header_name_lower == "cookie" {
1558 continue;
1559 } else if header_name_lower == "referer" {
1560 if let Some(ref value) = header.value {
1561 referer_found = Some(value.clone());
1562 }
1563 } else {
1564 filtered_headers.push(header);
1565 }
1566 }
1567
1568 assert_eq!(referer_found, Some("https://google.com".to_string()));
1569
1570 let header_names: Vec<String> = filtered_headers
1571 .iter()
1572 .map(|h| h.name.to_lowercase())
1573 .collect();
1574 assert!(
1575 !header_names.contains(&"cookie".to_string()),
1576 "Cookie header should not be in headers list"
1577 );
1578 assert!(
1579 !header_names.contains(&"referer".to_string()),
1580 "Referer header should not be in headers list"
1581 );
1582
1583 assert!(header_names.contains(&":method".to_string()));
1584 assert!(header_names.contains(&":path".to_string()));
1585 assert!(header_names.contains(&":authority".to_string()));
1586 assert!(header_names.contains(&"user-agent".to_string()));
1587 assert!(header_names.contains(&"accept".to_string()));
1588
1589 assert_eq!(filtered_headers.len(), 5);
1590 }
1591}