use qbase::{
packet::{
DataHeader, Packet,
header::{long, short},
},
util::BoundQueue,
};
use crate::component::route::{CipherPacket, Way};
type PacketQueue<P> = BoundQueue<(CipherPacket<P>, Way)>;
#[derive(Debug)]
pub struct RcvdPacketQueue {
initial: PacketQueue<long::InitialHeader>,
handshake: PacketQueue<long::HandshakeHeader>,
zero_rtt: PacketQueue<long::ZeroRttHeader>,
one_rtt: PacketQueue<short::OneRttHeader>,
}
impl Default for RcvdPacketQueue {
fn default() -> Self {
Self::new()
}
}
impl RcvdPacketQueue {
pub fn new() -> Self {
Self {
initial: BoundQueue::new(8),
handshake: BoundQueue::new(8),
zero_rtt: BoundQueue::new(8),
one_rtt: BoundQueue::new(128),
}
}
pub fn initial(&self) -> &PacketQueue<long::InitialHeader> {
&self.initial
}
pub fn handshake(&self) -> &PacketQueue<long::HandshakeHeader> {
&self.handshake
}
pub fn zero_rtt(&self) -> &PacketQueue<long::ZeroRttHeader> {
&self.zero_rtt
}
pub fn one_rtt(&self) -> &PacketQueue<short::OneRttHeader> {
&self.one_rtt
}
pub fn close_all(&self) {
self.initial.close();
self.handshake.close();
self.zero_rtt.close();
self.one_rtt.close();
}
pub async fn deliver(&self, packet: Packet, way: Way) {
match packet {
Packet::Data(packet) => match packet.header {
DataHeader::Long(long::DataHeader::Initial(header)) => {
let packet = CipherPacket::new(header, packet.bytes, packet.offset);
_ = self.initial.try_send((packet, way));
}
DataHeader::Long(long::DataHeader::Handshake(header)) => {
let packet = CipherPacket::new(header, packet.bytes, packet.offset);
_ = self.handshake.try_send((packet, way));
}
DataHeader::Long(long::DataHeader::ZeroRtt(header)) => {
let packet = CipherPacket::new(header, packet.bytes, packet.offset);
_ = self.zero_rtt.try_send((packet, way));
}
DataHeader::Short(header) => {
let packet = CipherPacket::new(header, packet.bytes, packet.offset);
_ = self.one_rtt.try_send((packet, way));
}
},
Packet::VN(_vn) => {}
Packet::Retry(_retry) => {}
}
}
}
#[cfg(test)]
mod tests {
use std::{net::SocketAddr, time::Duration};
use bytes::BytesMut;
use qbase::{
cid::ConnectionId,
net::route::{Link, Pathway},
packet::{DataPacket, LongHeaderBuilder},
};
use super::*;
use crate::bind_uri::BindUri;
fn initial_packet() -> Packet {
let header = LongHeaderBuilder::with_cid(
ConnectionId::from_slice(b"destination"),
ConnectionId::from_slice(b"source"),
)
.initial(Vec::new());
Packet::Data(DataPacket {
header: DataHeader::Long(long::DataHeader::Initial(header)),
bytes: BytesMut::new(),
offset: 0,
})
}
fn way() -> Way {
let local = SocketAddr::from(([127, 0, 0, 1], 4433));
let remote = SocketAddr::from(([192, 0, 2, 1], 50000));
let link = Link::new(local, remote);
(BindUri::from(local), Pathway::from(link), link)
}
#[tokio::test]
async fn deliver_enqueues_when_capacity_is_available() {
let queue = RcvdPacketQueue::new();
queue.deliver(initial_packet(), way()).await;
assert!(queue.initial().recv().await.is_some());
}
#[tokio::test]
async fn deliver_does_not_wait_for_a_full_connection_queue() {
let queue = RcvdPacketQueue::new();
loop {
let Packet::Data(packet) = initial_packet() else {
unreachable!()
};
let DataHeader::Long(long::DataHeader::Initial(header)) = packet.header else {
unreachable!()
};
let packet = CipherPacket::new(header, packet.bytes, packet.offset);
match queue.initial.try_send((packet, way())) {
Ok(()) => {}
Err(error) if error.is_full() => break,
Err(error) => panic!("queue closed while filling it: {error}"),
}
}
tokio::time::timeout(
Duration::from_millis(25),
queue.deliver(initial_packet(), way()),
)
.await
.expect("routing must not wait for one connection's full queue");
}
}