use core::iter::FusedIterator;
use super::header::{
QuicLongHeader, QuicLongPacketType, QuicShortHeader, QuicShortHeaderContext, QuicVersion,
};
use super::long::QuicProtectedLongPacket;
use super::parse::QuicPacketParseError;
use super::terminal::{QuicRetryPacket, QuicVersionNegotiationPacket};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicUnknownLongPacket<'a> {
header: QuicLongHeader<'a>,
bytes: &'a [u8],
}
impl<'a> QuicUnknownLongPacket<'a> {
pub const fn header(self) -> QuicLongHeader<'a> {
self.header
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuicPacket<'a> {
ProtectedLong(QuicProtectedLongPacket<'a>),
VersionNegotiation(QuicVersionNegotiationPacket<'a>),
Retry(QuicRetryPacket<'a>),
Short(QuicShortHeader<'a>),
UnknownLong(QuicUnknownLongPacket<'a>),
}
impl<'a> QuicPacket<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
match self {
Self::ProtectedLong(packet) => packet.as_bytes(),
Self::VersionNegotiation(packet) => packet.as_bytes(),
Self::Retry(packet) => packet.as_bytes(),
Self::Short(packet) => packet.as_bytes(),
Self::UnknownLong(packet) => packet.as_bytes(),
}
}
pub const fn first_byte(self) -> u8 {
match self {
Self::ProtectedLong(packet) => packet.header().first_byte(),
Self::VersionNegotiation(packet) => packet.header().first_byte(),
Self::Retry(packet) => packet.header().first_byte(),
Self::Short(packet) => packet.first_byte(),
Self::UnknownLong(packet) => packet.header().first_byte(),
}
}
pub const fn is_long_header(self) -> bool {
!matches!(self, Self::Short(_))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicDatagram<'a> {
bytes: &'a [u8],
short_context: Option<QuicShortHeaderContext>,
}
impl<'a> QuicDatagram<'a> {
pub fn parse(
bytes: &'a [u8],
short_context: Option<QuicShortHeaderContext>,
) -> Result<Self, QuicPacketParseError> {
if bytes.is_empty() {
return Err(QuicPacketParseError::EmptyDatagram);
}
for packet in QuicPackets::new(bytes, short_context) {
packet?;
}
Ok(Self {
bytes,
short_context,
})
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn packets(self) -> QuicPackets<'a> {
QuicPackets::new(self.bytes, self.short_context)
}
}
#[derive(Clone, Debug)]
pub struct QuicPackets<'a> {
remaining: &'a [u8],
short_context: Option<QuicShortHeaderContext>,
exhausted: bool,
}
impl<'a> QuicPackets<'a> {
pub const fn new(bytes: &'a [u8], short_context: Option<QuicShortHeaderContext>) -> Self {
Self {
remaining: bytes,
short_context,
exhausted: false,
}
}
}
impl<'a> Iterator for QuicPackets<'a> {
type Item = Result<QuicPacket<'a>, QuicPacketParseError>;
fn next(&mut self) -> Option<Self::Item> {
if self.exhausted || self.remaining.is_empty() {
self.exhausted = true;
return None;
}
let bytes = self.remaining;
let result = if bytes[0] & 0x80 == 0 {
let context = match self.short_context {
Some(context) => context,
None => return self.fail(QuicPacketParseError::ShortHeaderContextRequired),
};
QuicShortHeader::parse(bytes, context).map(QuicPacket::Short)
} else {
match QuicLongHeader::parse(bytes) {
Err(error) => Err(error),
Ok(header) if header.version() == QuicVersion::NEGOTIATION => {
QuicVersionNegotiationPacket::parse(bytes).map(QuicPacket::VersionNegotiation)
}
Ok(header) => match header.long_packet_type() {
Some(QuicLongPacketType::Retry) => {
QuicRetryPacket::parse(bytes).map(QuicPacket::Retry)
}
Some(_) => QuicProtectedLongPacket::parse(bytes).map(|(packet, suffix)| {
self.remaining = suffix;
QuicPacket::ProtectedLong(packet)
}),
None => Ok(QuicPacket::UnknownLong(QuicUnknownLongPacket {
header,
bytes,
})),
},
}
};
match result {
Ok(
packet @ (QuicPacket::Short(_)
| QuicPacket::VersionNegotiation(_)
| QuicPacket::Retry(_)
| QuicPacket::UnknownLong(_)),
) => {
self.remaining = &[];
self.exhausted = true;
Some(Ok(packet))
}
Ok(packet) => Some(Ok(packet)),
Err(error) => self.fail(error),
}
}
}
impl FusedIterator for QuicPackets<'_> {}
impl<'a> QuicPackets<'a> {
fn fail(
&mut self,
error: QuicPacketParseError,
) -> Option<Result<QuicPacket<'a>, QuicPacketParseError>> {
self.remaining = &[];
self.exhausted = true;
Some(Err(error))
}
}