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