Skip to main content

stackforge_core/layer/http2/
mod.rs

1//! HTTP/2 protocol layer (RFC 7540).
2//!
3//! This module implements the HTTP/2 framing layer for Stackforge, including:
4//! - Frame parsing and building (RFC 7540)
5//! - HPACK header compression (RFC 7541)
6//! - Layer integration with the Stackforge packet model
7//!
8//! ## Architecture
9//!
10//! HTTP/2 operates over TCP as a sequence of frames. Each frame has a 9-byte
11//! header followed by a variable-length payload. A connection begins with a
12//! 24-byte "preface" string sent by the client.
13//!
14//! The `Http2Layer` represents one "segment" of HTTP/2 traffic captured within
15//! a packet buffer. It identifies whether the segment starts with the connection
16//! preface and provides access to the contained frames.
17//!
18//! ## Usage
19//!
20//! ```rust
21//! use stackforge_core::layer::http2::builder::{Http2Builder, Http2FrameBuilder};
22//! use stackforge_core::layer::http2::{Http2Layer, Http2Frame};
23//! use stackforge_core::layer::{LayerIndex, LayerKind};
24//!
25//! // Build a connection initiation
26//! let bytes = Http2Builder::new()
27//!     .frame(Http2FrameBuilder::settings())
28//!     .build();
29//!
30//! // Parse it back
31//! let layer = Http2Layer::new(0, bytes.len(), true);
32//! let summary = layer.summary(&bytes);
33//! assert!(summary.contains("HTTP/2"));
34//! ```
35
36pub mod builder;
37pub mod frames;
38pub mod hpack;
39
40pub use builder::{Http2Builder, Http2FrameBuilder, build_frame};
41pub use frames::{
42    HTTP2_FRAME_HEADER_LEN, HTTP2_PREFACE, Http2Frame, Http2FrameType, error_codes, flags,
43    parse_all_frames, parse_goaway, parse_rst_stream, parse_settings, parse_window_update,
44    settings_id,
45};
46pub use hpack::{HpackDecoder, HpackEncoder, STATIC_TABLE};
47
48use crate::layer::field::{FieldError, FieldValue};
49use crate::layer::{Layer, LayerIndex, LayerKind};
50
51// ============================================================================
52// HTTP/2 Layer
53// ============================================================================
54
55/// Field names exposed by the HTTP/2 layer.
56pub const HTTP2_FIELD_NAMES: &[&str] = &["frame_type", "flags", "stream_id", "length"];
57
58/// HTTP/2 protocol layer view.
59///
60/// Represents one or more HTTP/2 frames within a packet buffer. The `index`
61/// covers the portion of the buffer from the start of the HTTP/2 data
62/// (preface or first frame header) to the end.
63///
64/// Note: A single `Http2Layer` may contain multiple frames. Use
65/// [`frames::parse_all_frames`] or [`Http2Layer::all_frames`] to iterate them.
66#[derive(Debug, Clone)]
67pub struct Http2Layer {
68    /// Layer boundary index within the packet buffer.
69    pub index: LayerIndex,
70    /// Whether the layer begins with the HTTP/2 client connection preface.
71    pub has_preface: bool,
72}
73
74impl Http2Layer {
75    /// Create a new HTTP/2 layer from start/end offsets and a preface flag.
76    #[must_use]
77    pub fn new(start: usize, end: usize, has_preface: bool) -> Self {
78        Http2Layer {
79            index: LayerIndex::new(LayerKind::Http2, start, end),
80            has_preface,
81        }
82    }
83
84    /// Create from an existing `LayerIndex`.
85    #[must_use]
86    pub fn from_index(index: LayerIndex, has_preface: bool) -> Self {
87        Http2Layer { index, has_preface }
88    }
89
90    /// Check if `buf` starting at `offset` begins with the HTTP/2 preface.
91    #[must_use]
92    pub fn has_preface_at(buf: &[u8], offset: usize) -> bool {
93        let end = offset + HTTP2_PREFACE.len();
94        if end > buf.len() {
95            return false;
96        }
97        &buf[offset..end] == HTTP2_PREFACE
98    }
99
100    /// Get the offset where frames begin (after the preface if present).
101    #[must_use]
102    pub fn frames_start(&self) -> usize {
103        if self.has_preface {
104            self.index.start + HTTP2_PREFACE.len()
105        } else {
106            self.index.start
107        }
108    }
109
110    /// Get the first frame in this layer, skipping the preface if present.
111    ///
112    /// Returns `None` if there are no frames or the buffer is too short.
113    #[must_use]
114    pub fn first_frame(&self, buf: &[u8]) -> Option<Http2Frame> {
115        let offset = self.frames_start();
116        Http2Frame::parse_at(buf, offset)
117    }
118
119    /// Parse all frames in this layer's buffer range.
120    #[must_use]
121    pub fn all_frames(&self, buf: &[u8]) -> Vec<Http2Frame> {
122        let slice = self.index.slice(buf);
123        let preface_skip = if self.has_preface {
124            HTTP2_PREFACE.len()
125        } else {
126            0
127        };
128
129        if preface_skip > slice.len() {
130            return Vec::new();
131        }
132
133        // parse_all_frames works on the full buffer; we pass the slice and
134        // offset is relative to the slice start, so we use the offset directly.
135        // For correctness we parse directly using absolute offsets.
136        let mut frames = Vec::new();
137        let mut offset = self.index.start + preface_skip;
138
139        loop {
140            match Http2Frame::parse_at(buf, offset) {
141                Some(frame) => {
142                    // Only include frames within our layer's range
143                    if frame.payload_offset + frame.length as usize > self.index.end {
144                        break;
145                    }
146                    let total = frame.total_size;
147                    frames.push(frame);
148                    offset += total;
149                    if offset >= self.index.end {
150                        break;
151                    }
152                },
153                None => break,
154            }
155        }
156
157        frames
158    }
159
160    /// Generate a human-readable summary for this layer.
161    #[must_use]
162    pub fn summary(&self, buf: &[u8]) -> String {
163        let preface_str = if self.has_preface { "Preface + " } else { "" };
164
165        if let Some(frame) = self.first_frame(buf) {
166            let type_name = frame.frame_type.name();
167            if self.has_preface {
168                format!(
169                    "HTTP/2 {}[{}] stream={}",
170                    preface_str, type_name, frame.stream_id
171                )
172            } else {
173                format!("HTTP/2 [{}] stream={}", type_name, frame.stream_id)
174            }
175        } else if self.has_preface {
176            "HTTP/2 Preface".to_string()
177        } else {
178            "HTTP/2".to_string()
179        }
180    }
181
182    /// Get the "header length" of this layer.
183    ///
184    /// This returns the number of bytes that constitute the "header" portion:
185    /// - If only a preface: 24 bytes
186    /// - If a frame (no preface): 9 bytes (one frame header)
187    /// - If preface + at least one frame: 33 bytes (24 + 9)
188    #[must_use]
189    pub fn header_len(&self, buf: &[u8]) -> usize {
190        if self.has_preface {
191            // Check if there's at least one frame after the preface
192            let frame_start = self.index.start + HTTP2_PREFACE.len();
193            if frame_start + HTTP2_FRAME_HEADER_LEN <= buf.len() {
194                HTTP2_PREFACE.len() + HTTP2_FRAME_HEADER_LEN // 33 bytes
195            } else {
196                HTTP2_PREFACE.len() // 24 bytes, preface only
197            }
198        } else {
199            HTTP2_FRAME_HEADER_LEN // 9 bytes
200        }
201    }
202
203    /// Get a field value from the first frame by name.
204    ///
205    /// Supported fields: `frame_type`, `flags`, `stream_id`, `length`.
206    #[must_use]
207    pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
208        let frame = self.first_frame(buf)?;
209        match name {
210            "frame_type" => Some(Ok(FieldValue::U8(frame.frame_type.as_u8()))),
211            "flags" => Some(Ok(FieldValue::U8(frame.flags))),
212            "stream_id" => Some(Ok(FieldValue::U32(frame.stream_id))),
213            "length" => Some(Ok(FieldValue::U32(frame.length))),
214            _ => None,
215        }
216    }
217
218    /// Set a field value by name.
219    ///
220    /// Currently only `stream_id` supports mutation. Other fields require
221    /// rebuilding the frame.
222    pub fn set_field(
223        &self,
224        buf: &mut [u8],
225        name: &str,
226        value: FieldValue,
227    ) -> Option<Result<(), FieldError>> {
228        match name {
229            "stream_id" => {
230                let id = match value {
231                    FieldValue::U32(v) => v,
232                    FieldValue::U16(v) => u32::from(v),
233                    FieldValue::U8(v) => u32::from(v),
234                    _ => {
235                        return Some(Err(FieldError::InvalidValue(format!(
236                            "stream_id: expected integer, got {value:?}"
237                        ))));
238                    },
239                };
240
241                let offset = self.frames_start() + 5; // stream_id at bytes 5-8 of frame header
242                if offset + 4 > buf.len() {
243                    return Some(Err(FieldError::BufferTooShort {
244                        offset,
245                        need: 4,
246                        have: buf.len().saturating_sub(offset),
247                    }));
248                }
249                let bytes = (id & 0x7FFFFFFF).to_be_bytes();
250                buf[offset..offset + 4].copy_from_slice(&bytes);
251                Some(Ok(()))
252            },
253            "flags" => {
254                let flag_val = match value {
255                    FieldValue::U8(v) => v,
256                    _ => {
257                        return Some(Err(FieldError::InvalidValue(format!(
258                            "flags: expected U8, got {value:?}"
259                        ))));
260                    },
261                };
262
263                let offset = self.frames_start() + 4; // flags at byte 4 of frame header
264                if offset >= buf.len() {
265                    return Some(Err(FieldError::BufferTooShort {
266                        offset,
267                        need: 1,
268                        have: 0,
269                    }));
270                }
271                buf[offset] = flag_val;
272                Some(Ok(()))
273            },
274            _ => None,
275        }
276    }
277
278    /// Get the static list of field names for this layer type.
279    #[must_use]
280    pub fn field_names() -> &'static [&'static str] {
281        HTTP2_FIELD_NAMES
282    }
283}
284
285// ============================================================================
286// Layer trait implementation
287// ============================================================================
288
289impl Layer for Http2Layer {
290    fn kind(&self) -> LayerKind {
291        LayerKind::Http2
292    }
293
294    fn summary(&self, data: &[u8]) -> String {
295        self.summary(data)
296    }
297
298    fn header_len(&self, data: &[u8]) -> usize {
299        self.header_len(data)
300    }
301
302    fn field_names(&self) -> &'static [&'static str] {
303        HTTP2_FIELD_NAMES
304    }
305
306    fn hashret(&self, _data: &[u8]) -> Vec<u8> {
307        // HTTP/2 hashret could be based on stream ID, but for now return empty
308        vec![]
309    }
310}
311
312// ============================================================================
313// HTTP/2 detection helper
314// ============================================================================
315
316/// Check if a TCP payload looks like HTTP/2 traffic.
317///
318/// Returns true if the buffer starts with the HTTP/2 connection preface
319/// or if it begins with a valid-looking HTTP/2 frame header.
320#[must_use]
321pub fn is_http2_payload(data: &[u8]) -> bool {
322    // Check for preface
323    if data.len() >= HTTP2_PREFACE.len() && data.starts_with(HTTP2_PREFACE) {
324        return true;
325    }
326
327    // Check for a valid frame header: length(3) + type(1) + flags(1) + stream_id(4)
328    if data.len() < HTTP2_FRAME_HEADER_LEN {
329        return false;
330    }
331
332    // Length field is 24-bit; must be reasonable (< 16 MB)
333    let length = (u32::from(data[0]) << 16) | (u32::from(data[1]) << 8) | u32::from(data[2]);
334    if length > 0x00FF_FFFF {
335        return false;
336    }
337
338    // Frame type must be 0-9 (known) or we still accept it for unknown types
339    let frame_type = data[3];
340    let valid_type = frame_type <= 9;
341
342    // Stream ID must have R bit = 0 in a valid frame
343    let stream_word = u32::from_be_bytes([data[5], data[6], data[7], data[8]]);
344    let r_bit = (stream_word & 0x80000000) == 0; // R bit should be 0
345
346    valid_type && r_bit && length <= 16384 // default MAX_FRAME_SIZE
347}
348
349// ============================================================================
350// Tests
351// ============================================================================
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356    use crate::layer::http2::builder::{Http2Builder, Http2FrameBuilder, build_get_request};
357
358    #[test]
359    fn test_http2_layer_summary_with_preface() {
360        let bytes = Http2Builder::new()
361            .frame(Http2FrameBuilder::settings())
362            .build();
363
364        let layer = Http2Layer::new(0, bytes.len(), true);
365        let summary = layer.summary(&bytes);
366        assert!(summary.contains("HTTP/2"));
367        assert!(summary.contains("Preface"));
368        assert!(summary.contains("SETTINGS"));
369    }
370
371    #[test]
372    fn test_http2_layer_summary_no_preface() {
373        let bytes = Http2FrameBuilder::settings_ack().build();
374        let layer = Http2Layer::new(0, bytes.len(), false);
375        let summary = layer.summary(&bytes);
376        assert!(summary.contains("HTTP/2"));
377        assert!(summary.contains("SETTINGS"));
378    }
379
380    #[test]
381    fn test_http2_layer_header_len_preface_and_frame() {
382        let bytes = Http2Builder::new()
383            .frame(Http2FrameBuilder::settings())
384            .build();
385        let layer = Http2Layer::new(0, bytes.len(), true);
386        // 24 (preface) + 9 (first frame header)
387        assert_eq!(layer.header_len(&bytes), 33);
388    }
389
390    #[test]
391    fn test_http2_layer_header_len_no_preface() {
392        let bytes = Http2FrameBuilder::settings().build();
393        let layer = Http2Layer::new(0, bytes.len(), false);
394        assert_eq!(layer.header_len(&bytes), 9);
395    }
396
397    #[test]
398    fn test_http2_layer_get_field() {
399        // Build a HEADERS frame on stream 3
400        let bytes = Http2FrameBuilder::headers_frame(3, vec![0x82]).build();
401        let layer = Http2Layer::new(0, bytes.len(), false);
402
403        let ft = layer.get_field(&bytes, "frame_type").unwrap().unwrap();
404        assert_eq!(ft, FieldValue::U8(1)); // HEADERS = 1
405
406        let sid = layer.get_field(&bytes, "stream_id").unwrap().unwrap();
407        assert_eq!(sid, FieldValue::U32(3));
408
409        let flags = layer.get_field(&bytes, "flags").unwrap().unwrap();
410        assert_eq!(flags, FieldValue::U8(0x04)); // END_HEADERS
411
412        let len = layer.get_field(&bytes, "length").unwrap().unwrap();
413        assert_eq!(len, FieldValue::U32(1)); // 1 byte payload
414    }
415
416    #[test]
417    fn test_http2_layer_set_stream_id() {
418        let mut bytes = Http2FrameBuilder::headers_frame(1, vec![0x82]).build();
419        let layer = Http2Layer::new(0, bytes.len(), false);
420
421        layer
422            .set_field(&mut bytes, "stream_id", FieldValue::U32(5))
423            .unwrap()
424            .unwrap();
425
426        let sid = layer.get_field(&bytes, "stream_id").unwrap().unwrap();
427        assert_eq!(sid, FieldValue::U32(5));
428    }
429
430    #[test]
431    fn test_http2_layer_all_frames() {
432        let bytes = Http2Builder::new()
433            .frame(Http2FrameBuilder::settings())
434            .frame(Http2FrameBuilder::settings_ack())
435            .frame(Http2FrameBuilder::headers_frame(1, vec![0x82]))
436            .build();
437
438        let layer = Http2Layer::new(0, bytes.len(), true);
439        let frames = layer.all_frames(&bytes);
440        assert_eq!(frames.len(), 3);
441        assert_eq!(frames[0].frame_type, Http2FrameType::Settings);
442        assert_eq!(frames[1].frame_type, Http2FrameType::Settings);
443        assert_eq!(frames[2].frame_type, Http2FrameType::Headers);
444    }
445
446    #[test]
447    fn test_has_preface_at() {
448        let mut buf = Vec::new();
449        buf.extend_from_slice(HTTP2_PREFACE);
450        buf.extend_from_slice(&Http2FrameBuilder::settings().build());
451
452        assert!(Http2Layer::has_preface_at(&buf, 0));
453        assert!(!Http2Layer::has_preface_at(&buf, 1));
454        assert!(!Http2Layer::has_preface_at(&[], 0));
455    }
456
457    #[test]
458    fn test_is_http2_payload_preface() {
459        let bytes = Http2Builder::new()
460            .frame(Http2FrameBuilder::settings())
461            .build();
462        assert!(is_http2_payload(&bytes));
463    }
464
465    #[test]
466    fn test_is_http2_payload_frame() {
467        let bytes = Http2FrameBuilder::settings_ack().build();
468        assert!(is_http2_payload(&bytes));
469    }
470
471    #[test]
472    fn test_is_http2_payload_not_http2() {
473        assert!(!is_http2_payload(b"HTTP/1.1 200 OK\r\n\r\n"));
474        assert!(!is_http2_payload(b"GET / HTTP/1.1\r\n"));
475        assert!(!is_http2_payload(&[]));
476    }
477
478    #[test]
479    fn test_layer_kind() {
480        let layer = Http2Layer::new(0, 9, false);
481        assert_eq!(layer.kind(), LayerKind::Http2);
482    }
483
484    #[test]
485    fn test_layer_field_names() {
486        let layer = Http2Layer::new(0, 9, false);
487        let names = layer.field_names();
488        assert!(names.contains(&"frame_type"));
489        assert!(names.contains(&"flags"));
490        assert!(names.contains(&"stream_id"));
491        assert!(names.contains(&"length"));
492    }
493
494    #[test]
495    fn test_get_request_integration() {
496        use crate::layer::http2::hpack::HpackDecoder;
497
498        let bytes = build_get_request("example.com", "/api/v1", 1);
499        let layer = Http2Layer::new(0, bytes.len(), true);
500
501        let frames = layer.all_frames(&bytes);
502        assert!(frames.len() >= 2);
503
504        // Find the HEADERS frame
505        let headers_frame = frames
506            .iter()
507            .find(|f| f.frame_type == Http2FrameType::Headers)
508            .unwrap();
509        assert!(headers_frame.is_end_headers());
510        assert!(headers_frame.is_end_stream());
511
512        // Decode HPACK
513        let hpack_data = headers_frame_fragment(headers_frame, &bytes).unwrap();
514        let mut decoder = HpackDecoder::new();
515        let headers = decoder.decode(hpack_data).unwrap();
516
517        let method = headers
518            .iter()
519            .find(|(n, _)| n == ":method")
520            .map(|(_, v)| v.as_str());
521        let path = headers
522            .iter()
523            .find(|(n, _)| n == ":path")
524            .map(|(_, v)| v.as_str());
525
526        assert_eq!(method, Some("GET"));
527        assert_eq!(path, Some("/api/v1"));
528    }
529
530    // Helper to extract HPACK fragment from a HEADERS frame
531    fn headers_frame_fragment<'a>(frame: &Http2Frame, buf: &'a [u8]) -> Option<&'a [u8]> {
532        if frame.frame_type != Http2FrameType::Headers {
533            return None;
534        }
535        let payload = frame.payload(buf);
536        let mut start = 0;
537
538        if frame.is_padded() {
539            if payload.is_empty() {
540                return None;
541            }
542            start += payload[0] as usize + 1;
543        }
544
545        if frame.has_priority() {
546            start += 5;
547        }
548
549        Some(&payload[start..])
550    }
551}