Skip to main content

net_wire/http3/frame/
goaway.rs

1//! Typed HTTP/3 GOAWAY frame payload view and builder.
2
3use crate::http3::codepoints::Http3FrameType;
4use crate::quic::varint::{QuicVarInt, QuicVarIntLen};
5
6use super::envelope::Http3Frame;
7use super::payload::{
8    Http3FramePayloadBuildError, Http3FramePayloadField, Http3FramePayloadParseError,
9    encode_varint, envelope, parse_exact_varint, validate_type,
10};
11
12/// A validated borrowed HTTP/3 GOAWAY frame.
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub struct Http3Goaway<'a> {
15    frame: Http3Frame<'a>,
16    identifier: QuicVarInt<'a>,
17}
18
19impl<'a> Http3Goaway<'a> {
20    /// Assembles a GOAWAY view from builder-validated components.
21    pub(in crate::http3) fn from_validated(
22        frame: Http3Frame<'a>,
23        identifier: u64,
24        identifier_length: QuicVarIntLen,
25    ) -> Self {
26        let identifier = QuicVarInt::from_validated(
27            &frame.payload()[..identifier_length.byte_len()],
28            identifier,
29            identifier_length,
30        );
31        Self { frame, identifier }
32    }
33
34    /// Parses the first complete GOAWAY frame, enforcing the caller-provided payload maximum.
35    pub fn parse(
36        bytes: &'a [u8],
37        maximum_payload: usize,
38    ) -> Result<Self, Http3FramePayloadParseError> {
39        Self::from_frame(
40            Http3Frame::parse(bytes, maximum_payload)
41                .map_err(Http3FramePayloadParseError::Frame)?,
42        )
43    }
44
45    /// Validates a raw GOAWAY frame's intrinsic layout without assigning identifier role.
46    pub fn from_frame(frame: Http3Frame<'a>) -> Result<Self, Http3FramePayloadParseError> {
47        validate_type(frame, Http3FrameType::GOAWAY)?;
48        let identifier =
49            parse_exact_varint(frame.payload(), Http3FramePayloadField::GoawayIdentifier)?;
50        Ok(Self { frame, identifier })
51    }
52
53    /// Returns the validated raw frame.
54    pub const fn frame(&self) -> Http3Frame<'a> {
55        self.frame
56    }
57
58    /// Returns the exact represented frame bytes.
59    pub const fn as_bytes(&self) -> &'a [u8] {
60        self.frame.as_bytes()
61    }
62
63    /// Returns the role-neutral GOAWAY identifier value.
64    pub const fn identifier(&self) -> u64 {
65        self.identifier.value()
66    }
67
68    /// Returns the exact role-neutral GOAWAY identifier variable-length integer.
69    pub const fn identifier_varint(&self) -> QuicVarInt<'a> {
70        self.identifier
71    }
72}
73
74/// Builds a typed HTTP/3 GOAWAY frame in caller-owned storage.
75pub struct Http3GoawayBuilder<'output> {
76    destination: &'output mut [u8],
77    identifier: u64,
78}
79
80impl<'output> Http3GoawayBuilder<'output> {
81    /// Creates a role-neutral GOAWAY builder.
82    pub fn new(destination: &'output mut [u8], identifier: u64) -> Self {
83        Self {
84            destination,
85            identifier,
86        }
87    }
88
89    /// Canonically encodes and atomically writes the GOAWAY frame.
90    pub fn build(self) -> Result<Http3Goaway<'output>, Http3FramePayloadBuildError> {
91        let identifier = encode_varint(self.identifier).map_err(|error| {
92            Http3FramePayloadBuildError::PayloadVarInt {
93                field: Http3FramePayloadField::GoawayIdentifier,
94                error,
95            }
96        })?;
97        let plan = envelope(
98            Http3FrameType::GOAWAY,
99            identifier.byte_len(),
100            self.destination,
101        )?;
102        let header_length = plan.header_length();
103        let required = plan.required();
104        let bytes = &mut self.destination[..required];
105        plan.write_header(bytes);
106        bytes[header_length..].copy_from_slice(identifier.bytes());
107        let frame = plan.finish(bytes);
108        Ok(Http3Goaway::from_validated(
109            frame,
110            self.identifier,
111            identifier.len,
112        ))
113    }
114}