cellos-supervisor 0.5.1

CellOS execution-cell runner — boots cells in Firecracker microVMs or gVisor, enforces narrow typed authority, emits signed CloudEvents.
Documentation
//! Error types produced by the h2 frame parser + HPACK decoder.

use std::fmt;

/// Errors produced by `extract_h2_authority` and `HpackDecoder::decode_block`.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum H2ParseError {
    /// A frame's declared length exceeds [`super::DEFAULT_MAX_FRAME_SIZE`].
    OversizedFrame { declared: u32, max: u32 },
    /// First non-SETTINGS frame is not a HEADERS frame.
    UnexpectedFirstFrame { frame_type: u8 },
    /// HEADERS frame flagged something other than END_HEADERS — Phase 3g
    /// reassembles via CONTINUATION but this variant is preserved for
    /// callers of the *stateless* one-shot API who set the END_HEADERS
    /// requirement explicitly.
    ContinuationFragmentation,
    /// HPACK encoding requires features we don't decode (legacy P3c
    /// variant — Phase 3g supports dynamic-table state and Huffman, so
    /// this is now only emitted for representation classes outside the
    /// four RFC 7541 §6 cases).
    HpackUnsupported,
    /// HEADERS payload is malformed — padding length exceeds payload
    /// length, integer overflow in HPACK varint, etc.
    MalformedHeaders,
    /// A header literal contains a non-ASCII byte. RFC 3986 hosts are
    /// ASCII; rejecting protects downstream `make_ascii_lowercase` from
    /// altering UTF-8 byte sequences.
    NonAsciiAuthority,
    /// Padded HEADERS payload declared a pad length larger than the
    /// remaining payload.
    PaddingOverflow,
    /// HPACK indexed reference points outside the static + current
    /// dynamic tables.
    HpackInvalidIndex { index: u64 },
    /// `SETTINGS_HEADER_TABLE_SIZE` (HPACK §6.3 dynamic-table size update)
    /// requested a table size larger than our defensive ceiling.
    HpackTableSizeOversized { requested: usize, max: usize },
    /// CONTINUATION reassembly observed a non-CONTINUATION frame
    /// (DATA, SETTINGS, etc.) between the initial HEADERS and the
    /// END_HEADERS-bearing CONTINUATION (RFC 7540 §4.3).
    InterleavedFrame { frame_type: u8 },
    /// Aggregate header block (HEADERS + all CONTINUATION payloads)
    /// exceeded the defensive cap.
    HpackOversizedHeaderBlock { total: usize, max: usize },
    /// Huffman bitstream contained the EOS symbol or had >7 bits of
    /// padding (RFC 7541 §5.2).
    HuffmanInvalid,
    /// Huffman-decoded output exceeded our decoded-length bound.
    HuffmanOversized { max: usize },
    /// scope: per-stream HEADERS+CONTINUATION reassembler exceeded a
    /// defensive memory bound (per-stream block, total in-flight bytes
    /// across all streams, or maximum concurrent in-flight streams).
    /// Caller emits `l7_h2_unparseable_headers` for the offending stream
    /// and RST_STREAMs only that stream — other streams continue.
    ReassemblerOverflow {
        /// Which bound tripped. Stored as an enum below so error matchers
        /// can distinguish operator-actionable cases (raise the bound,
        /// see runbook) from defence-in-depth trips.
        kind: ReassemblerOverflowKind,
    },
}

/// Which [`H2ParseError::ReassemblerOverflow`] limit was exceeded.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ReassemblerOverflowKind {
    /// One stream's accumulated HEADERS + CONTINUATION bytes exceeded
    /// 64 KiB (matches `MAX_HEADER_BLOCK_SIZE`).
    PerStreamBlock,
    /// Sum of in-flight bytes across all open reassemblies exceeded
    /// 256 KiB.
    TotalInFlight,
    /// Concurrent in-flight reassemblies exceeded 64.
    ConcurrentStreams,
}

impl fmt::Display for H2ParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            H2ParseError::OversizedFrame { declared, max } => {
                write!(
                    f,
                    "HTTP/2 frame length {declared} exceeds max-frame-size {max}"
                )
            }
            H2ParseError::UnexpectedFirstFrame { frame_type } => write!(
                f,
                "HTTP/2 first frame type 0x{frame_type:02x} is not HEADERS or SETTINGS"
            ),
            H2ParseError::ContinuationFragmentation => write!(
                f,
                "HTTP/2 HEADERS fragmented across CONTINUATION frames; \
                 stateless one-shot API does not reassemble"
            ),
            H2ParseError::HpackUnsupported => write!(
                f,
                "HPACK encoding requires representation classes outside RFC 7541 §6"
            ),
            H2ParseError::MalformedHeaders => write!(f, "HEADERS payload is malformed"),
            H2ParseError::NonAsciiAuthority => {
                write!(f, ":authority literal contains non-ASCII bytes")
            }
            H2ParseError::PaddingOverflow => {
                write!(f, "HEADERS padding length exceeds payload length")
            }
            H2ParseError::HpackInvalidIndex { index } => {
                write!(f, "HPACK indexed reference {index} out of range")
            }
            H2ParseError::HpackTableSizeOversized { requested, max } => write!(
                f,
                "HPACK SETTINGS_HEADER_TABLE_SIZE update {requested} exceeds defensive cap {max}"
            ),
            H2ParseError::InterleavedFrame { frame_type } => write!(
                f,
                "HTTP/2 frame type 0x{frame_type:02x} interleaved between HEADERS and END_HEADERS"
            ),
            H2ParseError::HpackOversizedHeaderBlock { total, max } => write!(
                f,
                "aggregate HPACK header block {total} octets exceeds defensive cap {max}"
            ),
            H2ParseError::HuffmanInvalid => write!(
                f,
                "Huffman bitstream contained EOS symbol or invalid padding (RFC 7541 §5.2)"
            ),
            H2ParseError::HuffmanOversized { max } => {
                write!(
                    f,
                    "Huffman-decoded output exceeded defensive cap of {max} octets"
                )
            }
            H2ParseError::ReassemblerOverflow { kind } => match kind {
                ReassemblerOverflowKind::PerStreamBlock => {
                    write!(f, "per-stream HEADERS+CONTINUATION block exceeded 64 KiB")
                }
                ReassemblerOverflowKind::TotalInFlight => write!(
                    f,
                    "aggregate HEADERS+CONTINUATION reassembler in-flight bytes exceeded 256 KiB"
                ),
                ReassemblerOverflowKind::ConcurrentStreams => write!(
                    f,
                    "concurrent in-flight HEADERS+CONTINUATION reassemblies exceeded 64"
                ),
            },
        }
    }
}

impl std::error::Error for H2ParseError {}