Skip to main content

net_wire/http2/builder/
data.rs

1//! Atomic caller-buffer construction of standard HTTP/2 DATA, HEADERS, PUSH_PROMISE, and CONTINUATION frames.
2
3use super::super::data::Http2Data;
4use super::super::error::Http2BuildError;
5use super::super::frame::Http2Frame;
6use super::super::headers::{Http2Continuation, Http2Headers, Http2PushPromise};
7use super::super::priority::Http2Priority;
8use super::super::types::{Http2FrameType, Http2StreamId};
9use super::raw::{FRAME_HEADER_LENGTH, MAXIMUM_PAYLOAD, write_frame_envelope};
10
11const DATA_PADDED: u8 = 0x08;
12const HEADERS_PRIORITY: u8 = 0x20;
13const PUSH_PROMISE_PADDED: u8 = 0x08;
14
15/// Builds a typed HTTP/2 DATA frame in caller-owned storage.
16pub struct Http2DataBuilder<'a, 'b> {
17    buffer: &'a mut [u8],
18    stream_id: Http2StreamId,
19    flags: u8,
20    data: &'b [u8],
21    padding_length: Option<u8>,
22}
23
24impl<'a, 'b> Http2DataBuilder<'a, 'b> {
25    /// Creates a builder that copies DATA and emits zero-valued padding when requested.
26    pub fn new(
27        buffer: &'a mut [u8],
28        stream_id: Http2StreamId,
29        flags: u8,
30        data: &'b [u8],
31        padding_length: Option<u8>,
32    ) -> Self {
33        Self {
34            buffer,
35            stream_id,
36            flags,
37            data,
38            padding_length,
39        }
40    }
41
42    /// Validates HTTP/2's representable payload length and capacity before writing the frame.
43    pub fn build(self) -> Result<Http2Data<'a>, Http2BuildError> {
44        self.build_with_maximum(MAXIMUM_PAYLOAD)
45    }
46
47    /// Validates the caller-provided payload maximum and capacity before writing the frame.
48    pub fn build_with_maximum(
49        self,
50        maximum_payload: usize,
51    ) -> Result<Http2Data<'a>, Http2BuildError> {
52        if self.flags & DATA_PADDED != 0 {
53            return Err(Http2BuildError::DataPaddedFlagSet);
54        }
55        validate_stream_id(self.stream_id)?;
56        let padding_length = self.padding_length;
57        let flags = self.flags
58            | if padding_length.is_some() {
59                DATA_PADDED
60            } else {
61                0
62            };
63        let payload_length = self
64            .data
65            .len()
66            .checked_add(usize::from(padding_length.is_some()))
67            .and_then(|length| length.checked_add(usize::from(padding_length.unwrap_or(0))))
68            .ok_or(Http2BuildError::PayloadTooLarge {
69                maximum: maximum_payload.min(MAXIMUM_PAYLOAD),
70                actual: self.data.len(),
71            })?;
72        let bytes = write_frame_envelope(
73            self.buffer,
74            payload_length,
75            Http2FrameType::DATA,
76            flags,
77            self.stream_id,
78            maximum_payload,
79        )?;
80        let mut offset = FRAME_HEADER_LENGTH;
81        if let Some(padding_length) = padding_length {
82            bytes[offset] = padding_length;
83            offset += 1;
84        }
85        let data_end = offset + self.data.len();
86        bytes[offset..data_end].copy_from_slice(self.data);
87        bytes[data_end..].fill(0);
88        Ok(Http2Data::from_validated(
89            Http2Frame::from_validated(bytes),
90            padding_length,
91        ))
92    }
93}
94
95/// Builds a typed HTTP/2 HEADERS frame in caller-owned storage.
96pub struct Http2HeadersBuilder<'a, 'b> {
97    buffer: &'a mut [u8],
98    stream_id: Http2StreamId,
99    flags: u8,
100    field_block_fragment: &'b [u8],
101    priority: Option<Http2Priority>,
102    padding_length: Option<u8>,
103}
104
105impl<'a, 'b> Http2HeadersBuilder<'a, 'b> {
106    /// Creates a builder that copies the field block fragment and optional layout sections.
107    pub fn new(
108        buffer: &'a mut [u8],
109        stream_id: Http2StreamId,
110        flags: u8,
111        field_block_fragment: &'b [u8],
112        priority: Option<Http2Priority>,
113        padding_length: Option<u8>,
114    ) -> Self {
115        Self {
116            buffer,
117            stream_id,
118            flags,
119            field_block_fragment,
120            priority,
121            padding_length,
122        }
123    }
124
125    /// Validates HTTP/2's representable payload length and capacity before writing the frame.
126    pub fn build(self) -> Result<Http2Headers<'a>, Http2BuildError> {
127        self.build_with_maximum(MAXIMUM_PAYLOAD)
128    }
129
130    /// Validates the caller-provided payload maximum and capacity before writing the frame.
131    pub fn build_with_maximum(
132        self,
133        maximum_payload: usize,
134    ) -> Result<Http2Headers<'a>, Http2BuildError> {
135        if self.flags & DATA_PADDED != 0 {
136            return Err(Http2BuildError::HeadersPaddedFlagSet);
137        }
138        if self.flags & HEADERS_PRIORITY != 0 {
139            return Err(Http2BuildError::HeadersPriorityFlagSet);
140        }
141        validate_stream_id(self.stream_id)?;
142        let flags = self.flags
143            | if self.padding_length.is_some() {
144                DATA_PADDED
145            } else {
146                0
147            }
148            | if self.priority.is_some() {
149                HEADERS_PRIORITY
150            } else {
151                0
152            };
153        let payload_length = self
154            .field_block_fragment
155            .len()
156            .checked_add(5 * usize::from(self.priority.is_some()))
157            .and_then(|length| length.checked_add(usize::from(self.padding_length.is_some())))
158            .and_then(|length| length.checked_add(usize::from(self.padding_length.unwrap_or(0))))
159            .ok_or(Http2BuildError::PayloadTooLarge {
160                maximum: maximum_payload.min(MAXIMUM_PAYLOAD),
161                actual: self.field_block_fragment.len(),
162            })?;
163        let bytes = write_frame_envelope(
164            self.buffer,
165            payload_length,
166            Http2FrameType::HEADERS,
167            flags,
168            self.stream_id,
169            maximum_payload,
170        )?;
171        let mut offset = FRAME_HEADER_LENGTH;
172        if let Some(padding_length) = self.padding_length {
173            bytes[offset] = padding_length;
174            offset += 1;
175        }
176        if let Some(priority) = self.priority {
177            bytes[offset..offset + 4].copy_from_slice(&priority.raw_dependency().to_be_bytes());
178            bytes[offset + 4] = priority.weight();
179            offset += 5;
180        }
181        let fragment_end = offset + self.field_block_fragment.len();
182        bytes[offset..fragment_end].copy_from_slice(self.field_block_fragment);
183        bytes[fragment_end..].fill(0);
184        Ok(Http2Headers::from_validated(
185            Http2Frame::from_validated(bytes),
186            self.padding_length,
187            self.priority,
188        ))
189    }
190}
191
192/// Builds a typed HTTP/2 PUSH_PROMISE frame in caller-owned storage.
193pub struct Http2PushPromiseBuilder<'a, 'b> {
194    buffer: &'a mut [u8],
195    stream_id: Http2StreamId,
196    flags: u8,
197    promised_stream_id: Http2StreamId,
198    field_block_fragment: &'b [u8],
199    padding_length: Option<u8>,
200}
201
202impl<'a, 'b> Http2PushPromiseBuilder<'a, 'b> {
203    /// Creates a builder that copies the promised stream ID, field block fragment, and padding.
204    pub fn new(
205        buffer: &'a mut [u8],
206        stream_id: Http2StreamId,
207        flags: u8,
208        promised_stream_id: Http2StreamId,
209        field_block_fragment: &'b [u8],
210        padding_length: Option<u8>,
211    ) -> Self {
212        Self {
213            buffer,
214            stream_id,
215            flags,
216            promised_stream_id,
217            field_block_fragment,
218            padding_length,
219        }
220    }
221
222    /// Validates HTTP/2's representable payload length and capacity before writing the frame.
223    pub fn build(self) -> Result<Http2PushPromise<'a>, Http2BuildError> {
224        self.build_with_maximum(MAXIMUM_PAYLOAD)
225    }
226
227    /// Validates the caller-provided payload maximum and capacity before writing the frame.
228    pub fn build_with_maximum(
229        self,
230        maximum_payload: usize,
231    ) -> Result<Http2PushPromise<'a>, Http2BuildError> {
232        if self.flags & PUSH_PROMISE_PADDED != 0 {
233            return Err(Http2BuildError::PushPromisePaddedFlagSet);
234        }
235        if self.promised_stream_id.value() == 0 {
236            return Err(Http2BuildError::ZeroPromisedStreamId);
237        }
238        if self.promised_stream_id.has_reserved_bit() {
239            return Err(Http2BuildError::ReservedPromisedStreamId);
240        }
241        validate_stream_id(self.stream_id)?;
242        let flags = self.flags
243            | if self.padding_length.is_some() {
244                PUSH_PROMISE_PADDED
245            } else {
246                0
247            };
248        let payload_length = self
249            .field_block_fragment
250            .len()
251            .checked_add(4)
252            .and_then(|length| length.checked_add(usize::from(self.padding_length.is_some())))
253            .and_then(|length| length.checked_add(usize::from(self.padding_length.unwrap_or(0))))
254            .ok_or(Http2BuildError::PayloadTooLarge {
255                maximum: maximum_payload.min(MAXIMUM_PAYLOAD),
256                actual: self.field_block_fragment.len(),
257            })?;
258        let bytes = write_frame_envelope(
259            self.buffer,
260            payload_length,
261            Http2FrameType::PUSH_PROMISE,
262            flags,
263            self.stream_id,
264            maximum_payload,
265        )?;
266        let mut offset = FRAME_HEADER_LENGTH;
267        if let Some(padding_length) = self.padding_length {
268            bytes[offset] = padding_length;
269            offset += 1;
270        }
271        bytes[offset..offset + 4].copy_from_slice(&self.promised_stream_id.raw().to_be_bytes());
272        offset += 4;
273        let fragment_end = offset + self.field_block_fragment.len();
274        bytes[offset..fragment_end].copy_from_slice(self.field_block_fragment);
275        bytes[fragment_end..].fill(0);
276        Ok(Http2PushPromise::from_validated(
277            Http2Frame::from_validated(bytes),
278            self.padding_length,
279            self.promised_stream_id,
280        ))
281    }
282}
283
284/// Builds a typed HTTP/2 CONTINUATION frame in caller-owned storage.
285pub struct Http2ContinuationBuilder<'a, 'b> {
286    buffer: &'a mut [u8],
287    stream_id: Http2StreamId,
288    flags: u8,
289    field_block_fragment: &'b [u8],
290}
291
292impl<'a, 'b> Http2ContinuationBuilder<'a, 'b> {
293    /// Creates a builder that copies the complete field block fragment.
294    pub fn new(
295        buffer: &'a mut [u8],
296        stream_id: Http2StreamId,
297        flags: u8,
298        field_block_fragment: &'b [u8],
299    ) -> Self {
300        Self {
301            buffer,
302            stream_id,
303            flags,
304            field_block_fragment,
305        }
306    }
307
308    /// Validates HTTP/2's representable payload length and capacity before writing the frame.
309    pub fn build(self) -> Result<Http2Continuation<'a>, Http2BuildError> {
310        self.build_with_maximum(MAXIMUM_PAYLOAD)
311    }
312
313    /// Validates the caller-provided payload maximum and capacity before writing the frame.
314    pub fn build_with_maximum(
315        self,
316        maximum_payload: usize,
317    ) -> Result<Http2Continuation<'a>, Http2BuildError> {
318        validate_stream_id(self.stream_id)?;
319        let bytes = write_frame_envelope(
320            self.buffer,
321            self.field_block_fragment.len(),
322            Http2FrameType::CONTINUATION,
323            self.flags,
324            self.stream_id,
325            maximum_payload,
326        )?;
327        bytes[FRAME_HEADER_LENGTH..].copy_from_slice(self.field_block_fragment);
328        Ok(Http2Continuation::from_validated(
329            Http2Frame::from_validated(bytes),
330        ))
331    }
332}
333
334fn validate_stream_id(stream_id: Http2StreamId) -> Result<(), Http2BuildError> {
335    if stream_id.value() == 0 {
336        return Err(Http2BuildError::ZeroStreamId);
337    }
338    if stream_id.has_reserved_bit() {
339        return Err(Http2BuildError::ReservedStreamId);
340    }
341    Ok(())
342}