use std::{
pin::Pin,
sync::{Arc, Mutex, MutexGuard, Weak},
time::Instant,
};
use sciparse::{address::ip_socket_addr::ScionSocketIpAddr, identifier::isd_asn::IsdAsn};
use squiche::{Connection, SendInfo};
use tokio::sync::Notify;
use crate::{
app::{NoApp, QuicScionApplication, Wakeups},
socket::{BoxedSocketError, GenericScionUdpSocket},
};
pub struct QuicScionConnDriver<A = NoApp> {
conn_handle: ConnectionHandle<A>,
socket: Arc<dyn GenericScionUdpSocket>,
}
impl<A: QuicScionApplication> QuicScionConnDriver<A> {
pub fn new(conn_handle: ConnectionHandle<A>, socket: Arc<dyn GenericScionUdpSocket>) -> Self {
Self {
conn_handle,
socket,
}
}
pub async fn run(&self) -> Result<(), BoxedSocketError> {
self.conn_handle.notify();
let mut closed = false;
let mut send_buf = Box::new([0u8; 65535]);
let mut transmit_size = 0usize;
let mut send_to =
ScionSocketIpAddr::new(IsdAsn::from_u64(0), "0.0.0.0".parse().unwrap(), 0);
let start_time = Instant::now();
let timeout = tokio::time::sleep_until(start_time.into());
tokio::pin!(timeout);
let mut timeout_fired = false;
let mut timeout_inst: Option<Instant> = None;
{
let handle = self.conn_handle.clone();
let mut conn = handle.lock();
conn.app.bind(&handle);
Self::set_timeout(
&mut timeout_inst,
&mut timeout,
conn.inner.timeout_instant(),
);
}
let mut wakeups = Wakeups::default();
while !closed {
let send = async {
self.socket
.send_to(&send_buf[..transmit_size], send_to)
.await
};
let notified = self.conn_handle.notified();
tokio::select! {
biased;
res = send, if transmit_size > 0 => {
res?;
transmit_size = 0;
},
_ = (&mut timeout), if timeout_inst.is_some() => {
timeout_fired = true;
},
_ = notified => {},
}
{
let mut conn = self.conn_handle.lock();
if timeout_fired {
conn.inner.on_timeout();
timeout_fired = false;
}
conn.update_app(&mut wakeups);
if transmit_size == 0 {
closed = conn.inner.is_closed();
if closed {
conn.on_closed(&mut wakeups);
}
match conn.send(send_buf.as_mut()) {
Ok((n, s)) => {
transmit_size = n;
send_to = s.to;
}
Err(squiche::Error::Done) => {}
Err(err) => {
tracing::error!(?err, "error on calling send on connection");
}
}
}
Self::set_timeout(
&mut timeout_inst,
&mut timeout,
conn.inner.timeout_instant(),
);
}
wakeups.fire();
}
Ok(())
}
#[inline]
fn set_timeout(
timeout_inst: &mut Option<Instant>,
timeout: &mut Pin<&mut tokio::time::Sleep>,
new_timeout: Option<Instant>,
) {
*timeout_inst = new_timeout;
if let Some(t) = &timeout_inst {
timeout.as_mut().reset((*t).into());
}
}
}
pub struct ConnectionHandle<A = NoApp> {
conn: Arc<Mutex<QuicScionConn<A>>>,
notify: Arc<Notify>,
}
impl<A> Clone for ConnectionHandle<A> {
fn clone(&self) -> Self {
Self {
conn: self.conn.clone(),
notify: self.notify.clone(),
}
}
}
impl<A> ConnectionHandle<A> {
pub fn new(notify: Notify, conn: QuicScionConn<A>) -> Self {
Self {
conn: Arc::new(Mutex::new(conn)),
notify: Arc::new(notify),
}
}
pub fn lock(&self) -> MutexGuard<'_, QuicScionConn<A>> {
self.conn.lock().unwrap()
}
pub fn lock_recovering(&self) -> MutexGuard<'_, QuicScionConn<A>> {
self.conn
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
pub fn notify(&self) {
self.notify.notify_one();
}
pub fn notified(&self) -> tokio::sync::futures::Notified<'_> {
self.notify.notified()
}
pub fn downgrade(&self) -> WeakConnectionHandle<A> {
WeakConnectionHandle {
conn: Arc::downgrade(&self.conn),
notify: self.notify.clone(),
}
}
}
pub struct WeakConnectionHandle<A = NoApp> {
conn: Weak<Mutex<QuicScionConn<A>>>,
notify: Arc<Notify>,
}
impl<A> Clone for WeakConnectionHandle<A> {
fn clone(&self) -> Self {
Self {
conn: self.conn.clone(),
notify: self.notify.clone(),
}
}
}
impl<A> WeakConnectionHandle<A> {
pub fn upgrade(&self) -> Option<ConnectionHandle<A>> {
Some(ConnectionHandle {
conn: self.conn.upgrade()?,
notify: self.notify.clone(),
})
}
}
pub struct QuicScionConn<A = NoApp> {
pub asn_pair: IsdAsnPair,
pub inner: Connection,
pub app: A,
}
impl<A> QuicScionConn<A> {
pub fn send(&mut self, send_buf: &mut [u8]) -> squiche::Result<(usize, ScionSendInfo)> {
self.inner
.send(send_buf)
.map(|(n, s)| (n, ScionSendInfo::from_squiche(s, self.asn_pair.clone())))
}
}
impl<A: QuicScionApplication> QuicScionConn<A> {
fn update_app(&mut self, wakeups: &mut Wakeups) {
self.app.update(&mut self.inner, wakeups);
}
fn on_closed(&mut self, wakeups: &mut Wakeups) {
self.app.on_closed(wakeups);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IsdAsnPair {
pub from: IsdAsn,
pub to: IsdAsn,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScionSendInfo {
pub from: ScionSocketIpAddr,
pub to: ScionSocketIpAddr,
pub at: Instant,
}
impl ScionSendInfo {
pub fn new(from: ScionSocketIpAddr, to: ScionSocketIpAddr, at: Instant) -> Self {
Self { from, to, at }
}
pub fn from_squiche(send_info: SendInfo, asn_pair: IsdAsnPair) -> Self {
ScionSendInfo {
from: ScionSocketIpAddr::new(asn_pair.from, send_info.from.ip(), send_info.from.port()),
to: ScionSocketIpAddr::new(asn_pair.to, send_info.to.ip(), send_info.to.port()),
at: send_info.at,
}
}
}