use crate::{application, frame::Tag, varint::VarInt};
use s2n_codec::{decoder_parameterized_value, Encoder, EncoderValue};
macro_rules! connection_close_tag {
() => {
0x1cu8..=0x1du8
};
}
const QUIC_ERROR_TAG: u8 = 0x1c;
const APPLICATION_ERROR_TAG: u8 = 0x1d;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ConnectionClose<'a> {
pub error_code: VarInt,
pub frame_type: Option<VarInt>,
pub reason: Option<&'a [u8]>,
}
impl ConnectionClose<'_> {
#[inline]
pub fn tag(&self) -> u8 {
if self.frame_type.is_some() {
QUIC_ERROR_TAG
} else {
APPLICATION_ERROR_TAG
}
}
}
impl application::error::TryInto for ConnectionClose<'_> {
#[inline]
fn application_error(&self) -> Option<application::Error> {
if self.frame_type.is_none() {
Some(self.error_code.into())
} else {
None
}
}
}
decoder_parameterized_value!(
impl<'a> ConnectionClose<'a> {
fn decode(tag: Tag, buffer: Buffer) -> Result<Self> {
let (error_code, buffer) = buffer.decode()?;
let (frame_type, buffer) = if tag == QUIC_ERROR_TAG {
let (frame_type, buffer) = buffer.decode()?;
(Some(frame_type), buffer)
} else {
(None, buffer)
};
let (reason, buffer) = buffer.decode_slice_with_len_prefix::<VarInt>()?;
let reason = if reason.is_empty() {
None
} else {
#[allow(clippy::all)]
Some(&reason.into_less_safe_slice()[..])
};
let frame = ConnectionClose {
error_code,
frame_type,
reason,
};
Ok((frame, buffer))
}
}
);
impl EncoderValue for ConnectionClose<'_> {
#[inline]
fn encode<E: Encoder>(&self, buffer: &mut E) {
buffer.encode(&self.tag());
buffer.encode(&self.error_code);
if let Some(frame_type) = &self.frame_type {
buffer.encode(frame_type);
}
if let Some(reason) = &self.reason {
buffer.encode_with_len_prefix::<VarInt, _>(reason);
} else {
buffer.encode(&0u8);
}
}
}