#![allow(dead_code)]
use alloc::collections::VecDeque;
use alloc::vec::Vec;
use crate::quic::varint;
use crate::tls::Error;
pub(crate) const MAX_INBOUND_DATAGRAMS: usize = 64;
pub(crate) const MAX_INBOUND_DATAGRAM_BYTES: usize = 1024 * 1024;
pub(crate) struct DatagramQueues {
pub(crate) outbound: VecDeque<Vec<u8>>,
pub(crate) inbound: VecDeque<Vec<u8>>,
inbound_bytes: usize,
pub(crate) peer_max_frame_size: u64,
pub(crate) our_max_frame_size: u64,
}
impl DatagramQueues {
pub(crate) fn new(peer_param: Option<u64>, our_param: Option<u64>) -> Self {
Self {
outbound: VecDeque::new(),
inbound: VecDeque::new(),
inbound_bytes: 0,
peer_max_frame_size: peer_param.unwrap_or(0),
our_max_frame_size: our_param.unwrap_or(0),
}
}
pub(crate) fn peer_accepts(&self) -> bool {
self.peer_max_frame_size > 0
}
pub(crate) fn send(&mut self, data: &[u8]) -> Result<(), Error> {
if !self.peer_accepts() {
return Err(Error::InappropriateState);
}
let frame_len = 1 + varint::encoded_len(data.len() as u64) + data.len();
if (frame_len as u64) > self.peer_max_frame_size {
return Err(Error::IllegalParameter);
}
self.outbound.push_back(data.to_vec());
Ok(())
}
pub(crate) fn recv(&mut self) -> Option<Vec<u8>> {
let d = self.inbound.pop_front()?;
self.inbound_bytes -= d.len();
Some(d)
}
pub(crate) fn pop_outbound(&mut self, budget: usize) -> Option<Vec<u8>> {
let head = self.outbound.front()?;
let frame_len = 1 + varint::encoded_len(head.len() as u64) + head.len();
if frame_len > budget {
return None;
}
self.outbound.pop_front()
}
pub(crate) fn enqueue_inbound(&mut self, data: Vec<u8>) -> bool {
if self.inbound.len() >= MAX_INBOUND_DATAGRAMS
|| self.inbound_bytes.saturating_add(data.len()) > MAX_INBOUND_DATAGRAM_BYTES
{
return false;
}
self.inbound_bytes += data.len();
self.inbound.push_back(data);
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn send_refused_if_peer_didnt_advertise() {
let mut q = DatagramQueues::new(None, Some(1200));
let r = q.send(b"hi");
assert!(matches!(r, Err(Error::InappropriateState)));
let mut q2 = DatagramQueues::new(Some(0), Some(1200));
let r2 = q2.send(b"hi");
assert!(matches!(r2, Err(Error::InappropriateState)));
}
#[test]
fn send_rejects_payload_exceeding_peer_max() {
let mut q = DatagramQueues::new(Some(100), Some(1200));
let big = alloc::vec![0xABu8; 200];
let r = q.send(&big);
assert!(matches!(r, Err(Error::IllegalParameter)));
assert!(q.send(b"ok").is_ok());
}
#[test]
fn send_then_pop_round_trips() {
let mut q = DatagramQueues::new(Some(1200), Some(1200));
q.send(b"hello").expect("send");
q.send(b"world").expect("send");
let popped = q.pop_outbound(7).expect("pop");
assert_eq!(popped, b"hello".to_vec());
let no = q.pop_outbound(1);
assert!(no.is_none());
let popped2 = q.pop_outbound(1024).expect("pop second");
assert_eq!(popped2, b"world".to_vec());
assert!(q.pop_outbound(1024).is_none());
}
#[test]
fn recv_round_trips() {
let mut q = DatagramQueues::new(Some(1200), Some(1200));
q.enqueue_inbound(b"a".to_vec());
q.enqueue_inbound(b"b".to_vec());
assert_eq!(q.recv().unwrap(), b"a".to_vec());
assert_eq!(q.recv().unwrap(), b"b".to_vec());
assert!(q.recv().is_none());
}
#[test]
fn inbound_queue_caps_by_count_and_drops() {
let mut q = DatagramQueues::new(Some(1200), Some(1200));
for _ in 0..MAX_INBOUND_DATAGRAMS {
assert!(q.enqueue_inbound(b"x".to_vec()));
}
assert_eq!(q.inbound.len(), MAX_INBOUND_DATAGRAMS);
assert!(!q.enqueue_inbound(b"y".to_vec()));
assert_eq!(q.inbound.len(), MAX_INBOUND_DATAGRAMS);
assert_eq!(q.recv().unwrap(), b"x".to_vec());
assert!(q.enqueue_inbound(b"z".to_vec()));
}
#[test]
fn inbound_queue_caps_by_bytes_and_drops() {
let mut q = DatagramQueues::new(Some(1200), Some(1200));
let big = alloc::vec![0u8; MAX_INBOUND_DATAGRAM_BYTES - 1];
assert!(q.enqueue_inbound(big));
assert!(!q.enqueue_inbound(b"no".to_vec()));
assert!(q.enqueue_inbound(b"k".to_vec()));
let _ = q.recv().unwrap();
assert!(q.enqueue_inbound(alloc::vec![0u8; 1024]));
}
#[test]
fn peer_accepts_reflects_advertised_value() {
assert!(!DatagramQueues::new(None, None).peer_accepts());
assert!(!DatagramQueues::new(Some(0), None).peer_accepts());
assert!(DatagramQueues::new(Some(1), None).peer_accepts());
assert!(DatagramQueues::new(Some(1200), None).peer_accepts());
}
}