Skip to main content

net_wire/http3/
qpack.rs

1//! HTTP/3 handoff from typed field-section frames to the QPACK decoder.
2//!
3//! This module deliberately owns only the typed HTTP/3-to-QPACK boundary. Stream state,
4//! blocked-section retention, and QPACK decoder feedback remain caller responsibilities.
5
6use core::fmt;
7
8use crate::qpack::field::section::decode::{
9    QpackFieldSectionDecodeError, QpackFieldSectionDecodeOutcome, QpackFieldSectionDecoder,
10    QpackFieldSectionOutput,
11};
12use crate::qpack::table::dynamic::QpackDynamicTable;
13
14use super::frame::{Http3Headers, Http3PushPromise};
15
16/// Failure while decoding a QPACK field section carried by an HTTP/3 frame.
17///
18/// [`Self::DecompressionFailed`] is a peer-caused HTTP/3 connection error.
19/// [`Self::OutputProvisioning`] reports insufficient caller output storage and is local, so it
20/// must not be sent as an HTTP/3 peer error.
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub enum Http3QpackFieldSectionError {
23    /// The peer's field section failed QPACK decompression.
24    DecompressionFailed(QpackFieldSectionDecodeError),
25    /// Caller-provided decoded-field output was insufficient.
26    OutputProvisioning(QpackFieldSectionDecodeError),
27}
28
29impl Http3QpackFieldSectionError {
30    /// Returns the HTTP/3 connection error code when this failure is peer-caused.
31    pub const fn error_code(self) -> Option<super::codepoints::Http3ErrorCode> {
32        match self {
33            Self::DecompressionFailed(_) => {
34                Some(super::codepoints::Http3ErrorCode::QPACK_DECOMPRESSION_FAILED)
35            }
36            Self::OutputProvisioning(_) => None,
37        }
38    }
39}
40
41impl From<QpackFieldSectionDecodeError> for Http3QpackFieldSectionError {
42    fn from(error: QpackFieldSectionDecodeError) -> Self {
43        match error.is_decompression_failed() {
44            true => Self::DecompressionFailed(error),
45            false => Self::OutputProvisioning(error),
46        }
47    }
48}
49
50impl fmt::Display for Http3QpackFieldSectionError {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        match self {
53            Self::DecompressionFailed(error) => {
54                write!(f, "HTTP/3 QPACK decompression failed: {error}")
55            }
56            Self::OutputProvisioning(error) => {
57                write!(
58                    f,
59                    "HTTP/3 QPACK output provisioning failed locally: {error}"
60                )
61            }
62        }
63    }
64}
65
66impl core::error::Error for Http3QpackFieldSectionError {
67    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
68        match self {
69            Self::DecompressionFailed(error) | Self::OutputProvisioning(error) => Some(error),
70        }
71    }
72}
73
74impl Http3Headers<'_> {
75    /// Decodes this HEADERS frame's exact encoded field section into caller-owned output.
76    ///
77    /// The QPACK decoder's transactional output and table guarantees are preserved. A
78    /// [`QpackFieldSectionDecodeOutcome::Blocked`] result is successful; retry this same field
79    /// section after the dynamic table's insert count advances. Output-provisioning failures are
80    /// local caller errors, not HTTP/3 peer errors.
81    pub fn decode_field_section<'output>(
82        &self,
83        decoder: &QpackFieldSectionDecoder,
84        table: &QpackDynamicTable<'_, '_>,
85        output: &'output mut QpackFieldSectionOutput<'_, '_>,
86    ) -> Result<QpackFieldSectionDecodeOutcome<'output>, Http3QpackFieldSectionError> {
87        decode_field_section(self.encoded_field_section(), decoder, table, output)
88    }
89}
90
91impl Http3PushPromise<'_> {
92    /// Decodes this PUSH_PROMISE frame's exact encoded field section into caller-owned output.
93    ///
94    /// The QPACK decoder's transactional output and table guarantees are preserved. A
95    /// [`QpackFieldSectionDecodeOutcome::Blocked`] result is successful; retry this same field
96    /// section after the dynamic table's insert count advances. Output-provisioning failures are
97    /// local caller errors, not HTTP/3 peer errors.
98    pub fn decode_field_section<'output>(
99        &self,
100        decoder: &QpackFieldSectionDecoder,
101        table: &QpackDynamicTable<'_, '_>,
102        output: &'output mut QpackFieldSectionOutput<'_, '_>,
103    ) -> Result<QpackFieldSectionDecodeOutcome<'output>, Http3QpackFieldSectionError> {
104        decode_field_section(self.encoded_field_section(), decoder, table, output)
105    }
106}
107
108fn decode_field_section<'output>(
109    encoded_field_section: &[u8],
110    decoder: &QpackFieldSectionDecoder,
111    table: &QpackDynamicTable<'_, '_>,
112    output: &'output mut QpackFieldSectionOutput<'_, '_>,
113) -> Result<QpackFieldSectionDecodeOutcome<'output>, Http3QpackFieldSectionError> {
114    decoder
115        .decode(encoded_field_section, table, output)
116        .map_err(Http3QpackFieldSectionError::from)
117}