Skip to main content

net_wire/quic/frame/
connection_close.rs

1//! RFC 9000 CONNECTION_CLOSE frame parsing.
2
3use super::super::varint::QuicVarInt;
4use super::parse::{QuicFrameField, QuicFrameParseError};
5
6/// A checked borrowed RFC 9000 CONNECTION_CLOSE frame view.
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct QuicConnectionCloseFrame<'a> {
9    bytes: &'a [u8],
10    frame_type: QuicVarInt<'a>,
11    error_code: QuicVarInt<'a>,
12    triggering_frame_type: Option<QuicVarInt<'a>>,
13    reason_phrase_length: QuicVarInt<'a>,
14    reason_phrase: &'a [u8],
15}
16
17impl<'a> QuicConnectionCloseFrame<'a> {
18    /// Returns the exact encoded frame bytes.
19    pub const fn as_bytes(self) -> &'a [u8] {
20        self.bytes
21    }
22
23    /// Returns the exact encoded frame-type variable integer.
24    pub const fn frame_type(self) -> QuicVarInt<'a> {
25        self.frame_type
26    }
27
28    /// Returns the exact encoded error-code variable integer.
29    pub const fn error_code(self) -> QuicVarInt<'a> {
30        self.error_code
31    }
32
33    /// Returns the triggering frame type for a transport close, if present.
34    pub const fn triggering_frame_type(self) -> Option<QuicVarInt<'a>> {
35        self.triggering_frame_type
36    }
37
38    /// Returns the exact encoded reason-phrase-length variable integer.
39    pub const fn reason_phrase_length(self) -> QuicVarInt<'a> {
40        self.reason_phrase_length
41    }
42
43    /// Returns the exact opaque reason-phrase bytes, which may be empty.
44    pub const fn reason_phrase(self) -> &'a [u8] {
45        self.reason_phrase
46    }
47}
48
49pub(super) fn parse_connection_close<'a>(
50    bytes: &'a [u8],
51    frame_type: QuicVarInt<'a>,
52    offset: usize,
53) -> Result<(QuicConnectionCloseFrame<'a>, &'a [u8]), QuicFrameParseError> {
54    let mut start = frame_type.byte_len();
55    let error_code = parse_field(
56        bytes,
57        start,
58        offset,
59        QuicFrameField::ConnectionCloseErrorCode,
60    )?;
61    start += error_code.byte_len();
62
63    let triggering_frame_type = if frame_type.value() == 0x1c {
64        let triggering_frame_type =
65            parse_field(bytes, start, offset, QuicFrameField::TriggeringFrameType)?;
66        start += triggering_frame_type.byte_len();
67        Some(triggering_frame_type)
68    } else {
69        None
70    };
71
72    let reason_phrase_length =
73        parse_field(bytes, start, offset, QuicFrameField::ReasonPhraseLength)?;
74    start += reason_phrase_length.byte_len();
75    let reason_length_value = reason_phrase_length.value();
76    let reason_length = usize::try_from(reason_length_value).map_err(|_| {
77        QuicFrameParseError::LengthNotRepresentable {
78            field: QuicFrameField::ReasonPhraseLength,
79            offset: offset + start - reason_phrase_length.byte_len(),
80            value: reason_length_value,
81        }
82    })?;
83    let reason_offset = offset + start;
84    let end = start
85        .checked_add(reason_length)
86        .ok_or(QuicFrameParseError::LengthOverflow {
87            field: QuicFrameField::ReasonPhrase,
88            offset: reason_offset,
89            length: reason_length,
90        })?;
91    let available = bytes.len() - start;
92    if available < reason_length {
93        return Err(QuicFrameParseError::IncompleteBytes {
94            field: QuicFrameField::ReasonPhrase,
95            offset: reason_offset,
96            required: reason_length,
97            available,
98        });
99    }
100
101    Ok((
102        QuicConnectionCloseFrame {
103            bytes: &bytes[..end],
104            frame_type,
105            error_code,
106            triggering_frame_type,
107            reason_phrase_length,
108            reason_phrase: &bytes[start..end],
109        },
110        &bytes[end..],
111    ))
112}
113
114fn parse_field<'a>(
115    bytes: &'a [u8],
116    start: usize,
117    offset: usize,
118    field: QuicFrameField,
119) -> Result<QuicVarInt<'a>, QuicFrameParseError> {
120    QuicVarInt::parse(&bytes[start..]).map_err(|error| QuicFrameParseError::Field {
121        field,
122        offset: offset + start,
123        error,
124    })
125}