Skip to main content

net_wire/http2/
headers.rs

1use super::error::Http2ParseError;
2use super::frame::Http2Frame;
3use super::layout::{PADDED, padding_length, validate_frame, validate_padding};
4use super::priority::Http2Priority;
5use super::types::{Http2FrameType, Http2StreamId};
6
7const PRIORITY: u8 = 0x20;
8
9/// A validated borrowed HTTP/2 HEADERS frame.
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub struct Http2Headers<'a> {
12    frame: Http2Frame<'a>,
13    field_block_start: usize,
14    field_block_end: usize,
15    priority: Option<Http2Priority>,
16}
17
18impl<'a> Http2Headers<'a> {
19    pub(super) fn from_validated(
20        frame: Http2Frame<'a>,
21        padding_length: Option<u8>,
22        priority: Option<Http2Priority>,
23    ) -> Self {
24        let field_block_start =
25            usize::from(padding_length.is_some()) + 5 * usize::from(priority.is_some());
26        let field_block_end = frame.payload().len() - usize::from(padding_length.unwrap_or(0));
27        Self {
28            frame,
29            field_block_start,
30            field_block_end,
31            priority,
32        }
33    }
34
35    /// Parses the first complete HEADERS frame, enforcing the caller-provided payload maximum.
36    pub fn parse(bytes: &'a [u8], maximum_payload: usize) -> Result<Self, Http2ParseError> {
37        Self::from_frame(Http2Frame::parse(bytes, maximum_payload)?)
38    }
39
40    /// Validates a raw HEADERS frame's intrinsic layout.
41    pub fn from_frame(frame: Http2Frame<'a>) -> Result<Self, Http2ParseError> {
42        validate_frame(frame, Http2FrameType::HEADERS)?;
43        let payload = frame.payload();
44        let padded = frame.flags() & PADDED != 0;
45        if padded {
46            validate_padding(payload)?;
47        }
48        let padding_length = padding_length(frame);
49        let mut field_block_start = usize::from(padded);
50        let field_block_end = payload.len() - padding_length;
51        let priority = if frame.flags() & PRIORITY != 0 {
52            let available = field_block_end - field_block_start;
53            if available < 5 {
54                return Err(Http2ParseError::MalformedPayload {
55                    required: 5,
56                    available,
57                });
58            }
59            let priority = Http2Priority::parse(&payload[field_block_start..field_block_start + 5]);
60            field_block_start += 5;
61            Some(priority)
62        } else {
63            None
64        };
65        Ok(Self {
66            frame,
67            field_block_start,
68            field_block_end,
69            priority,
70        })
71    }
72
73    /// Returns the validated raw frame.
74    pub const fn frame(&self) -> Http2Frame<'a> {
75        self.frame
76    }
77
78    /// Returns the exact represented frame bytes.
79    pub fn as_bytes(&self) -> &'a [u8] {
80        self.frame.as_bytes()
81    }
82
83    /// Returns the field block fragment excluding pad metadata, priority, and padding.
84    pub fn field_block_fragment(&self) -> &'a [u8] {
85        &self.frame.payload()[self.field_block_start..self.field_block_end]
86    }
87
88    /// Returns the trailing padding bytes.
89    pub fn padding(&self) -> &'a [u8] {
90        &self.frame.payload()[self.field_block_end..]
91    }
92
93    /// Returns the encoded padding length when PADDED is set.
94    pub fn pad_length(&self) -> Option<u8> {
95        (self.frame.flags() & PADDED != 0).then(|| self.frame.payload()[0])
96    }
97
98    /// Returns the optional priority section.
99    pub const fn priority(&self) -> Option<Http2Priority> {
100        self.priority
101    }
102}
103
104/// A validated borrowed HTTP/2 PUSH_PROMISE frame.
105#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106pub struct Http2PushPromise<'a> {
107    frame: Http2Frame<'a>,
108    field_block_start: usize,
109    field_block_end: usize,
110    promised_stream_id: Http2StreamId,
111}
112
113impl<'a> Http2PushPromise<'a> {
114    pub(super) fn from_validated(
115        frame: Http2Frame<'a>,
116        padding_length: Option<u8>,
117        promised_stream_id: Http2StreamId,
118    ) -> Self {
119        let field_block_start = usize::from(padding_length.is_some()) + 4;
120        let field_block_end = frame.payload().len() - usize::from(padding_length.unwrap_or(0));
121        Self {
122            frame,
123            field_block_start,
124            field_block_end,
125            promised_stream_id,
126        }
127    }
128
129    /// Parses the first complete PUSH_PROMISE frame, enforcing the caller-provided payload maximum.
130    pub fn parse(bytes: &'a [u8], maximum_payload: usize) -> Result<Self, Http2ParseError> {
131        Self::from_frame(Http2Frame::parse(bytes, maximum_payload)?)
132    }
133
134    /// Validates a raw PUSH_PROMISE frame's intrinsic layout.
135    pub fn from_frame(frame: Http2Frame<'a>) -> Result<Self, Http2ParseError> {
136        validate_frame(frame, Http2FrameType::PUSH_PROMISE)?;
137        let payload = frame.payload();
138        let padded = frame.flags() & PADDED != 0;
139        if padded {
140            validate_padding(payload)?;
141        }
142        let field_block_start = usize::from(padded);
143        let field_block_end = payload.len() - padding_length(frame);
144        let available = field_block_end - field_block_start;
145        if available < 4 {
146            return Err(Http2ParseError::MalformedPayload {
147                required: 4,
148                available,
149            });
150        }
151        let promised_stream_id = Http2StreamId::new(u32::from_be_bytes([
152            payload[field_block_start],
153            payload[field_block_start + 1],
154            payload[field_block_start + 2],
155            payload[field_block_start + 3],
156        ]));
157        if promised_stream_id.value() == 0 {
158            return Err(Http2ParseError::ZeroPromisedStreamId);
159        }
160        Ok(Self {
161            frame,
162            field_block_start: field_block_start + 4,
163            field_block_end,
164            promised_stream_id,
165        })
166    }
167
168    /// Returns the validated raw frame.
169    pub const fn frame(&self) -> Http2Frame<'a> {
170        self.frame
171    }
172
173    /// Returns the exact represented frame bytes.
174    pub fn as_bytes(&self) -> &'a [u8] {
175        self.frame.as_bytes()
176    }
177
178    /// Returns the raw promised stream identifier, including its reserved bit.
179    pub const fn promised_stream_id(&self) -> Http2StreamId {
180        self.promised_stream_id
181    }
182
183    /// Returns the field block fragment excluding pad metadata, promised stream ID, and padding.
184    pub fn field_block_fragment(&self) -> &'a [u8] {
185        &self.frame.payload()[self.field_block_start..self.field_block_end]
186    }
187
188    /// Returns the trailing padding bytes.
189    pub fn padding(&self) -> &'a [u8] {
190        &self.frame.payload()[self.field_block_end..]
191    }
192
193    /// Returns the encoded padding length when PADDED is set.
194    pub fn pad_length(&self) -> Option<u8> {
195        (self.frame.flags() & PADDED != 0).then(|| self.frame.payload()[0])
196    }
197}
198
199/// A validated borrowed HTTP/2 CONTINUATION frame.
200#[derive(Clone, Copy, Debug, Eq, PartialEq)]
201pub struct Http2Continuation<'a> {
202    frame: Http2Frame<'a>,
203}
204
205impl<'a> Http2Continuation<'a> {
206    pub(super) fn from_validated(frame: Http2Frame<'a>) -> Self {
207        Self { frame }
208    }
209
210    /// Parses the first complete CONTINUATION frame, enforcing the caller-provided payload maximum.
211    pub fn parse(bytes: &'a [u8], maximum_payload: usize) -> Result<Self, Http2ParseError> {
212        Self::from_frame(Http2Frame::parse(bytes, maximum_payload)?)
213    }
214
215    /// Validates a raw CONTINUATION frame's intrinsic layout.
216    pub fn from_frame(frame: Http2Frame<'a>) -> Result<Self, Http2ParseError> {
217        validate_frame(frame, Http2FrameType::CONTINUATION)?;
218        Ok(Self { frame })
219    }
220
221    /// Returns the validated raw frame.
222    pub const fn frame(&self) -> Http2Frame<'a> {
223        self.frame
224    }
225
226    /// Returns the exact represented frame bytes.
227    pub fn as_bytes(&self) -> &'a [u8] {
228        self.frame.as_bytes()
229    }
230
231    /// Returns the complete CONTINUATION payload as a field block fragment.
232    pub fn field_block_fragment(&self) -> &'a [u8] {
233        self.frame.payload()
234    }
235}