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}