use std::{
ops::{Deref, DerefMut},
time::{Duration, Instant},
};
use parking_lot::{Condvar, Mutex};
use quiche::{ConnectionId, RecvInfo, SendInfo};
use crate::{
Error, Event, Result, Token,
conn::LocKind,
registration::Registration,
utils::{min_of_some, release_time},
};
static DEFAULT_RELEASE_TIMER_THRESHOLD: Duration = Duration::from_micros(250);
struct ConnGuard<'a> {
token: Token,
conn_state_guard: crate::conn::ConnGuard,
group: &'a Group,
send_done: bool,
}
impl<'a> Drop for ConnGuard<'a> {
fn drop(&mut self) {
let mut registration = self.group.registration.lock();
unsafe {
assert!(
registration
.unlock_conn(
self.send_done,
DEFAULT_RELEASE_TIMER_THRESHOLD,
self.token,
self.conn_state_guard.lock_count,
)
.is_ok(),
"unlock connection {:?}",
self.token
);
}
self.group.condvar.notify_one();
}
}
impl<'a> Deref for ConnGuard<'a> {
type Target = quiche::Connection;
fn deref(&self) -> &Self::Target {
self.conn_state_guard.conn
}
}
impl<'a> DerefMut for ConnGuard<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.conn_state_guard.conn
}
}
#[derive(Default)]
pub struct Group {
registration: Mutex<Registration>,
condvar: Condvar,
}
impl Group {
pub fn new() -> Self {
Self::default()
}
pub fn register(&self, conn: quiche::Connection) -> Result<Token> {
let mut state = self.registration.lock();
state.register(conn, DEFAULT_RELEASE_TIMER_THRESHOLD)
}
pub fn deregister(&self, token: Token) -> Result<quiche::Connection> {
let mut state = self.registration.lock();
state.deregister(token)
}
fn lock_conn(&self, token: Token, kind: LocKind) -> Result<ConnGuard<'_>> {
let mut state = self.registration.lock();
Ok(ConnGuard {
token,
conn_state_guard: state.try_lock_conn(token, kind)?,
group: self,
send_done: false,
})
}
fn lock_conn_with(&self, scid: &ConnectionId<'_>, kind: LocKind) -> Result<ConnGuard<'_>> {
let mut state = self.registration.lock();
let (token, conn_state_guard) = state.try_lock_conn_by(scid, kind)?;
Ok(ConnGuard {
token,
conn_state_guard,
group: self,
send_done: false,
})
}
pub fn send(&self, token: Token, buf: &mut [u8]) -> Result<(usize, SendInfo)> {
let mut conn = self.lock_conn(token, LocKind::Send)?;
if let Some(release_time) =
release_time(&conn, Instant::now(), DEFAULT_RELEASE_TIMER_THRESHOLD)
{
log::trace!(
"connection send, scid={:?}, next_release_time={:?}",
conn.trace_id(),
release_time,
);
return Err(Error::Retry);
}
conn.on_timeout();
match conn.send(buf) {
Ok((send_size, send_info)) => {
log::trace!(
"connection send, scid={:?}, send_size={}, send_info={:?}",
conn.trace_id(),
send_size,
send_info
);
return Ok((send_size, send_info));
}
Err(quiche::Error::Done) => {
log::trace!("connection send, scid={:?}, done", conn.trace_id());
conn.send_done = true;
return Err(Error::Retry);
}
Err(err) => {
log::error!("connection send, scid={:?}, err={}", conn.trace_id(), err);
return Err(Error::Quiche(err));
}
}
}
pub fn recv(&self, buf: &mut [u8], info: RecvInfo) -> Result<usize> {
let header =
quiche::Header::from_slice(buf, quiche::MAX_CONN_ID_LEN).map_err(Error::Quiche)?;
self.recv_with_(&header.dcid, buf, info, false)
.map(|(_, recv_size)| recv_size)
}
pub(crate) fn recv_with_(
&self,
scid: &ConnectionId<'_>,
buf: &mut [u8],
info: RecvInfo,
is_server: bool,
) -> Result<(Token, usize)> {
let mut conn = self.lock_conn_with(scid, LocKind::Recv)?;
if is_server && !conn.is_server() {
panic!("dispatch packet to non-server connection.");
}
match conn.recv(buf, info) {
Ok(recv_size) => {
log::trace!(
"Connection recv, scid={:?}, len={}",
conn.source_id(),
recv_size
);
Ok((conn.token, recv_size))
}
Err(err) => {
log::error!("Connection recv, scid={:?}, err={}", conn.source_id(), err);
Err(Error::Quiche(err))
}
}
}
pub fn stream_send(
&self,
token: Token,
stream_id: u64,
buf: &[u8],
fin: bool,
) -> Result<usize> {
let mut conn = self.lock_conn(token, LocKind::StreamSend(stream_id, buf.len()))?;
match conn.stream_send(stream_id, buf, fin) {
Ok(send_size) => {
log::trace!(
"stream send, scid={:?}, stream_id={}, len={}, fin={}",
conn.source_id(),
stream_id,
send_size,
fin
);
return Ok(send_size);
}
Err(quiche::Error::Done) => {
log::trace!(
"stream send, scid={:?}, stream_id={}, fin={}, Done",
conn.source_id(),
stream_id,
fin
);
return Err(Error::Retry);
}
Err(err) => {
log::error!(
"stream send, scid={:?}, stream_id={}, fin={}, err={}",
conn.source_id(),
stream_id,
fin,
err
);
return Err(Error::Quiche(err));
}
}
}
pub fn stream_recv(
&self,
token: Token,
stream_id: u64,
buf: &mut [u8],
) -> Result<(usize, bool)> {
let mut conn = self.lock_conn(token, LocKind::StreamRecv(stream_id))?;
match conn.stream_recv(stream_id, buf) {
Ok((recv_size, fin)) => {
log::trace!(
"stream recv, scid={:?}, stream_id={}, len={}, fin={}",
conn.source_id(),
stream_id,
recv_size,
fin
);
return Ok((recv_size, fin));
}
Err(quiche::Error::Done) => {
log::trace!(
"stream recv, scid={:?}, stream_id={}, Done",
conn.source_id(),
stream_id,
);
return Err(Error::Retry);
}
Err(err) => {
log::error!(
"stream recv, scid={:?}, stream_id={}, err={}",
conn.source_id(),
stream_id,
err
);
return Err(Error::Quiche(err));
}
}
}
pub fn stream_open(&self, token: Token) -> Result<u64> {
self.registration
.lock()
.stream_open(token, DEFAULT_RELEASE_TIMER_THRESHOLD)
}
pub fn stream_close(&self, token: Token, stream_id: u64) -> Result<()> {
self.registration
.lock()
.stream_close(token, stream_id, DEFAULT_RELEASE_TIMER_THRESHOLD)
}
pub fn poll(&self, events: &mut Vec<Event>, timeout: Option<Duration>) -> Result<()> {
let timeout = timeout.map(|timeout| Instant::now() + timeout);
let mut state = self.registration.lock();
let readiness = unsafe { state.readiness() };
loop {
let next_release = readiness.poll(events);
if !events.is_empty()
|| timeout
.filter(|timeout| !(Instant::now() < *timeout))
.is_some()
{
return Ok(());
}
let timeout = min_of_some(timeout, next_release);
if let Some(timeout) = timeout {
self.condvar.wait_until(&mut state, timeout);
} else {
self.condvar.wait(&mut state);
}
}
}
pub fn non_blocking_poll(&self, events: &mut Vec<Event>) -> Option<Instant> {
let mut state = self.registration.lock();
let readiness = unsafe { state.readiness() };
readiness.poll(events)
}
}