Skip to main content

net_wire/http2/builder/
control.rs

1//! Atomic caller-buffer construction of HTTP/2 control frames.
2
3use super::super::control::{
4    Http2Goaway, Http2Ping, Http2PriorityFrame, Http2RstStream, Http2WindowIncrement,
5    Http2WindowUpdate,
6};
7use super::super::error::Http2BuildError;
8use super::super::frame::Http2Frame;
9use super::super::priority::Http2Priority;
10use super::super::settings::{Http2Setting, Http2Settings};
11use super::super::types::{Http2ErrorCode, Http2FrameType, Http2StreamId};
12use super::raw::{FRAME_HEADER_LENGTH, MAXIMUM_PAYLOAD, write_frame_envelope};
13
14/// Builds a typed HTTP/2 SETTINGS frame in caller-owned storage.
15pub struct Http2SettingsBuilder<'a, 'b> {
16    buffer: &'a mut [u8],
17    flags: u8,
18    settings: &'b [Http2Setting],
19}
20
21impl<'a, 'b> Http2SettingsBuilder<'a, 'b> {
22    /// Creates a builder that copies settings in the supplied order.
23    pub fn new(buffer: &'a mut [u8], flags: u8, settings: &'b [Http2Setting]) -> Self {
24        Self {
25            buffer,
26            flags,
27            settings,
28        }
29    }
30
31    /// Builds the frame after validating its payload and caller buffer.
32    pub fn build(self) -> Result<Http2Settings<'a>, Http2BuildError> {
33        self.build_with_maximum(MAXIMUM_PAYLOAD)
34    }
35
36    /// Builds the frame after validating the caller-provided payload maximum and capacity.
37    pub fn build_with_maximum(
38        self,
39        maximum_payload: usize,
40    ) -> Result<Http2Settings<'a>, Http2BuildError> {
41        for setting in self.settings.iter().copied() {
42            if !setting.has_valid_intrinsic_value() {
43                return Err(Http2BuildError::InvalidSettingValue {
44                    id: setting.id(),
45                    value: setting.value(),
46                });
47            }
48        }
49        let payload_length =
50            self.settings
51                .len()
52                .checked_mul(6)
53                .ok_or(Http2BuildError::PayloadTooLarge {
54                    maximum: maximum_payload.min(MAXIMUM_PAYLOAD),
55                    actual: self.settings.len(),
56                })?;
57        if self.flags & 0x1 != 0 && payload_length != 0 {
58            return Err(Http2BuildError::SettingsAckPayload {
59                actual: payload_length,
60            });
61        }
62        let bytes = write_frame_envelope(
63            self.buffer,
64            payload_length,
65            Http2FrameType::SETTINGS,
66            self.flags,
67            Http2StreamId::new(0),
68            maximum_payload,
69        )?;
70        for (index, setting) in self.settings.iter().copied().enumerate() {
71            let offset = FRAME_HEADER_LENGTH + index * 6;
72            bytes[offset..offset + 2].copy_from_slice(&setting.id().raw().to_be_bytes());
73            bytes[offset + 2..offset + 6].copy_from_slice(&setting.value().to_be_bytes());
74        }
75        Ok(Http2Settings::from_validated(Http2Frame::from_validated(
76            bytes,
77        )))
78    }
79}
80
81/// Builds a typed HTTP/2 GOAWAY frame in caller-owned storage.
82pub struct Http2GoawayBuilder<'a, 'b> {
83    buffer: &'a mut [u8],
84    flags: u8,
85    last_stream_id: Http2StreamId,
86    error_code: Http2ErrorCode,
87    additional_debug_data: &'b [u8],
88}
89
90impl<'a, 'b> Http2GoawayBuilder<'a, 'b> {
91    /// Creates a builder that copies the fixed prefix and opaque debug data.
92    pub fn new(
93        buffer: &'a mut [u8],
94        flags: u8,
95        last_stream_id: Http2StreamId,
96        error_code: Http2ErrorCode,
97        additional_debug_data: &'b [u8],
98    ) -> Self {
99        Self {
100            buffer,
101            flags,
102            last_stream_id,
103            error_code,
104            additional_debug_data,
105        }
106    }
107
108    /// Builds the frame after validating its payload and caller buffer.
109    pub fn build(self) -> Result<Http2Goaway<'a>, Http2BuildError> {
110        self.build_with_maximum(MAXIMUM_PAYLOAD)
111    }
112
113    /// Builds the frame after validating the caller-provided payload maximum and capacity.
114    pub fn build_with_maximum(
115        self,
116        maximum_payload: usize,
117    ) -> Result<Http2Goaway<'a>, Http2BuildError> {
118        if self.last_stream_id.has_reserved_bit() {
119            return Err(Http2BuildError::ReservedLastStreamId);
120        }
121        let payload_length = self.additional_debug_data.len().checked_add(8).ok_or(
122            Http2BuildError::PayloadTooLarge {
123                maximum: maximum_payload.min(MAXIMUM_PAYLOAD),
124                actual: self.additional_debug_data.len(),
125            },
126        )?;
127        let bytes = write_frame_envelope(
128            self.buffer,
129            payload_length,
130            Http2FrameType::GOAWAY,
131            self.flags,
132            Http2StreamId::new(0),
133            maximum_payload,
134        )?;
135        bytes[FRAME_HEADER_LENGTH..FRAME_HEADER_LENGTH + 4]
136            .copy_from_slice(&self.last_stream_id.raw().to_be_bytes());
137        bytes[FRAME_HEADER_LENGTH + 4..FRAME_HEADER_LENGTH + 8]
138            .copy_from_slice(&self.error_code.raw().to_be_bytes());
139        bytes[FRAME_HEADER_LENGTH + 8..].copy_from_slice(self.additional_debug_data);
140        Ok(Http2Goaway::from_validated(
141            Http2Frame::from_validated(bytes),
142            self.last_stream_id,
143            self.error_code,
144        ))
145    }
146}
147
148/// Builds a typed HTTP/2 PRIORITY frame in caller-owned storage.
149pub struct Http2PriorityFrameBuilder<'a> {
150    buffer: &'a mut [u8],
151    stream_id: Http2StreamId,
152    flags: u8,
153    priority: Http2Priority,
154}
155
156impl<'a> Http2PriorityFrameBuilder<'a> {
157    /// Creates a builder for an exact five-byte priority section.
158    pub fn new(
159        buffer: &'a mut [u8],
160        stream_id: Http2StreamId,
161        flags: u8,
162        priority: Http2Priority,
163    ) -> Self {
164        Self {
165            buffer,
166            stream_id,
167            flags,
168            priority,
169        }
170    }
171
172    /// Builds the frame after validating its fixed payload and caller buffer.
173    pub fn build(self) -> Result<Http2PriorityFrame<'a>, Http2BuildError> {
174        self.build_with_maximum(MAXIMUM_PAYLOAD)
175    }
176
177    /// Builds the frame after validating the caller-provided payload maximum and capacity.
178    pub fn build_with_maximum(
179        self,
180        maximum_payload: usize,
181    ) -> Result<Http2PriorityFrame<'a>, Http2BuildError> {
182        validate_required_stream_id(self.stream_id)?;
183        let bytes = write_frame_envelope(
184            self.buffer,
185            5,
186            Http2FrameType::PRIORITY,
187            self.flags,
188            self.stream_id,
189            maximum_payload,
190        )?;
191        bytes[FRAME_HEADER_LENGTH..FRAME_HEADER_LENGTH + 4]
192            .copy_from_slice(&self.priority.raw_dependency().to_be_bytes());
193        bytes[FRAME_HEADER_LENGTH + 4] = self.priority.weight();
194        Ok(Http2PriorityFrame::from_validated(
195            Http2Frame::from_validated(bytes),
196            self.priority,
197        ))
198    }
199}
200
201/// Builds a typed HTTP/2 RST_STREAM frame in caller-owned storage.
202pub struct Http2RstStreamBuilder<'a> {
203    buffer: &'a mut [u8],
204    stream_id: Http2StreamId,
205    flags: u8,
206    error_code: Http2ErrorCode,
207}
208
209impl<'a> Http2RstStreamBuilder<'a> {
210    /// Creates a builder for an exact four-byte error code.
211    pub fn new(
212        buffer: &'a mut [u8],
213        stream_id: Http2StreamId,
214        flags: u8,
215        error_code: Http2ErrorCode,
216    ) -> Self {
217        Self {
218            buffer,
219            stream_id,
220            flags,
221            error_code,
222        }
223    }
224
225    /// Builds the frame after validating its fixed payload and caller buffer.
226    pub fn build(self) -> Result<Http2RstStream<'a>, Http2BuildError> {
227        self.build_with_maximum(MAXIMUM_PAYLOAD)
228    }
229
230    /// Builds the frame after validating the caller-provided payload maximum and capacity.
231    pub fn build_with_maximum(
232        self,
233        maximum_payload: usize,
234    ) -> Result<Http2RstStream<'a>, Http2BuildError> {
235        validate_required_stream_id(self.stream_id)?;
236        let bytes = write_frame_envelope(
237            self.buffer,
238            4,
239            Http2FrameType::RST_STREAM,
240            self.flags,
241            self.stream_id,
242            maximum_payload,
243        )?;
244        bytes[FRAME_HEADER_LENGTH..].copy_from_slice(&self.error_code.raw().to_be_bytes());
245        Ok(Http2RstStream::from_validated(
246            Http2Frame::from_validated(bytes),
247            self.error_code,
248        ))
249    }
250}
251
252/// Builds a typed HTTP/2 PING frame in caller-owned storage.
253pub struct Http2PingBuilder<'a, 'b> {
254    buffer: &'a mut [u8],
255    flags: u8,
256    opaque_data: &'b [u8; 8],
257}
258
259impl<'a, 'b> Http2PingBuilder<'a, 'b> {
260    /// Creates a builder for eight opaque payload bytes on the connection stream.
261    pub fn new(buffer: &'a mut [u8], flags: u8, opaque_data: &'b [u8; 8]) -> Self {
262        Self {
263            buffer,
264            flags,
265            opaque_data,
266        }
267    }
268
269    /// Builds the frame after validating its fixed payload and caller buffer.
270    pub fn build(self) -> Result<Http2Ping<'a>, Http2BuildError> {
271        self.build_with_maximum(MAXIMUM_PAYLOAD)
272    }
273
274    /// Builds the frame after validating the caller-provided payload maximum and capacity.
275    pub fn build_with_maximum(
276        self,
277        maximum_payload: usize,
278    ) -> Result<Http2Ping<'a>, Http2BuildError> {
279        let bytes = write_frame_envelope(
280            self.buffer,
281            8,
282            Http2FrameType::PING,
283            self.flags,
284            Http2StreamId::new(0),
285            maximum_payload,
286        )?;
287        bytes[FRAME_HEADER_LENGTH..].copy_from_slice(self.opaque_data);
288        let opaque_data = bytes[FRAME_HEADER_LENGTH..].try_into().map_err(|_| {
289            Http2BuildError::BufferTooShort {
290                required: FRAME_HEADER_LENGTH + 8,
291                available: bytes.len(),
292            }
293        })?;
294        Ok(Http2Ping::from_validated(
295            Http2Frame::from_validated(bytes),
296            opaque_data,
297        ))
298    }
299}
300
301/// Builds a typed HTTP/2 WINDOW_UPDATE frame in caller-owned storage.
302pub struct Http2WindowUpdateBuilder<'a> {
303    buffer: &'a mut [u8],
304    stream_id: Http2StreamId,
305    flags: u8,
306    increment: Http2WindowIncrement,
307}
308
309impl<'a> Http2WindowUpdateBuilder<'a> {
310    /// Creates a builder for an exact four-byte window increment.
311    pub fn new(
312        buffer: &'a mut [u8],
313        stream_id: Http2StreamId,
314        flags: u8,
315        increment: Http2WindowIncrement,
316    ) -> Self {
317        Self {
318            buffer,
319            stream_id,
320            flags,
321            increment,
322        }
323    }
324
325    /// Builds the frame after validating its fixed payload and caller buffer.
326    pub fn build(self) -> Result<Http2WindowUpdate<'a>, Http2BuildError> {
327        self.build_with_maximum(MAXIMUM_PAYLOAD)
328    }
329
330    /// Builds the frame after validating the caller-provided payload maximum and capacity.
331    pub fn build_with_maximum(
332        self,
333        maximum_payload: usize,
334    ) -> Result<Http2WindowUpdate<'a>, Http2BuildError> {
335        if self.stream_id.has_reserved_bit() {
336            return Err(Http2BuildError::ReservedStreamId);
337        }
338        if self.increment.has_reserved_bit() {
339            return Err(Http2BuildError::ReservedWindowIncrement);
340        }
341        if self.increment.value() == 0 {
342            return Err(Http2BuildError::ZeroWindowIncrement);
343        }
344        let bytes = write_frame_envelope(
345            self.buffer,
346            4,
347            Http2FrameType::WINDOW_UPDATE,
348            self.flags,
349            self.stream_id,
350            maximum_payload,
351        )?;
352        bytes[FRAME_HEADER_LENGTH..].copy_from_slice(&self.increment.raw().to_be_bytes());
353        Ok(Http2WindowUpdate::from_validated(
354            Http2Frame::from_validated(bytes),
355            self.increment,
356        ))
357    }
358}
359
360fn validate_required_stream_id(stream_id: Http2StreamId) -> Result<(), Http2BuildError> {
361    if stream_id.value() == 0 {
362        return Err(Http2BuildError::ZeroStreamId);
363    }
364    if stream_id.has_reserved_bit() {
365        return Err(Http2BuildError::ReservedStreamId);
366    }
367    Ok(())
368}