s2n_quic_core/frame/
connection_close.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{application, frame::Tag, varint::VarInt};
5use s2n_codec::{decoder_parameterized_value, Encoder, EncoderValue};
6
7//= https://www.rfc-editor.org/rfc/rfc9000#section-19.19
8//# An endpoint sends a CONNECTION_CLOSE frame (type=0x1c or 0x1d) to
9//# notify its peer that the connection is being closed.  The
10//# CONNECTION_CLOSE frame with a type of 0x1c is used to signal errors
11//# at only the QUIC layer, or the absence of errors (with the NO_ERROR
12//# code).  The CONNECTION_CLOSE frame with a type of 0x1d is used to
13//# signal an error with the application that uses QUIC.
14
15macro_rules! connection_close_tag {
16    () => {
17        0x1cu8..=0x1du8
18    };
19}
20const QUIC_ERROR_TAG: u8 = 0x1c;
21const APPLICATION_ERROR_TAG: u8 = 0x1d;
22
23//= https://www.rfc-editor.org/rfc/rfc9000#section-19.19
24//# CONNECTION_CLOSE Frame {
25//#   Type (i) = 0x1c..0x1d,
26//#   Error Code (i),
27//#   [Frame Type (i)],
28//#   Reason Phrase Length (i),
29//#   Reason Phrase (..),
30//# }
31
32//= https://www.rfc-editor.org/rfc/rfc9000#section-19.19
33//# CONNECTION_CLOSE frames contain the following fields:
34//#
35//# Error Code:  A variable-length integer that indicates the reason for
36//# closing this connection.  A CONNECTION_CLOSE frame of type 0x1c
37//# uses codes from the space defined in Section 20.1.  A
38//# CONNECTION_CLOSE frame of type 0x1d uses codes defined by the
39//# application protocol; see Section 20.2.
40//#
41//# Frame Type:  A variable-length integer encoding the type of frame
42//# that triggered the error.  A value of 0 (equivalent to the mention
43//# of the PADDING frame) is used when the frame type is unknown.  The
44//# application-specific variant of CONNECTION_CLOSE (type 0x1d) does
45//# not include this field.
46//#
47//# Reason Phrase Length:  A variable-length integer specifying the
48//# length of the reason phrase in bytes.  Because a CONNECTION_CLOSE
49//# frame cannot be split between packets, any limits on packet size
50//# will also limit the space available for a reason phrase.
51//#
52//# Reason Phrase:  Additional diagnostic information for the closure.
53//# This can be zero length if the sender chooses not to give details
54//# beyond the Error Code value.  This SHOULD be a UTF-8 encoded
55//# string [RFC3629], though the frame does not carry information,
56//# such as language tags, that would aid comprehension by any entity
57//# other than the one that created the text.
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub struct ConnectionClose<'a> {
61    /// A variable length integer error code which indicates the reason
62    /// for closing this connection.
63    pub error_code: VarInt,
64
65    /// A variable-length integer encoding the type of frame that
66    /// triggered the error.
67    pub frame_type: Option<VarInt>,
68
69    /// A human-readable explanation for why the connection was closed.
70    /// This SHOULD be a UTF-8 encoded string.
71    pub reason: Option<&'a [u8]>,
72}
73
74impl ConnectionClose<'_> {
75    #[inline]
76    pub fn tag(&self) -> u8 {
77        if self.frame_type.is_some() {
78            QUIC_ERROR_TAG
79        } else {
80            APPLICATION_ERROR_TAG
81        }
82    }
83}
84
85// If a `ConnectionClose` contains no frame type it was sent by an application and contains
86// an `ApplicationErrorCode`. Otherwise it is an error on the QUIC layer.
87impl application::error::TryInto for ConnectionClose<'_> {
88    #[inline]
89    fn application_error(&self) -> Option<application::Error> {
90        if self.frame_type.is_none() {
91            Some(self.error_code.into())
92        } else {
93            None
94        }
95    }
96}
97
98decoder_parameterized_value!(
99    impl<'a> ConnectionClose<'a> {
100        fn decode(tag: Tag, buffer: Buffer) -> Result<Self> {
101            let (error_code, buffer) = buffer.decode()?;
102
103            let (frame_type, buffer) = if tag == QUIC_ERROR_TAG {
104                let (frame_type, buffer) = buffer.decode()?;
105                (Some(frame_type), buffer)
106            } else {
107                (None, buffer)
108            };
109
110            let (reason, buffer) = buffer.decode_slice_with_len_prefix::<VarInt>()?;
111
112            let reason = if reason.is_empty() {
113                None
114            } else {
115                // newer versions of clippy complain about redundant slicing
116                // but we don't know if this is a `&slice` or `&mut slice`
117                #[allow(clippy::all)]
118                Some(&reason.into_less_safe_slice()[..])
119            };
120
121            let frame = ConnectionClose {
122                error_code,
123                frame_type,
124                reason,
125            };
126
127            Ok((frame, buffer))
128        }
129    }
130);
131
132impl EncoderValue for ConnectionClose<'_> {
133    #[inline]
134    fn encode<E: Encoder>(&self, buffer: &mut E) {
135        buffer.encode(&self.tag());
136
137        buffer.encode(&self.error_code);
138        if let Some(frame_type) = &self.frame_type {
139            buffer.encode(frame_type);
140        }
141
142        if let Some(reason) = &self.reason {
143            buffer.encode_with_len_prefix::<VarInt, _>(reason);
144        } else {
145            buffer.encode(&0u8);
146        }
147    }
148}