use std::{
future::Future,
pin::Pin,
sync::Arc,
task::{Context, Poll},
time::Duration,
};
use web_transport_trait::Stats;
use crate::{
Error, Version, bandwidth,
util::{MaybeBoxedExt, MaybeSendBox},
};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct ConnectionStats {
pub rtt: Option<Duration>,
pub estimated_send_rate: Option<u64>,
pub estimated_recv_rate: Option<u64>,
pub bytes_sent: Option<u64>,
pub bytes_received: Option<u64>,
pub bytes_lost: Option<u64>,
pub packets_sent: Option<u64>,
pub packets_received: Option<u64>,
pub packets_lost: Option<u64>,
}
#[derive(Clone)]
pub struct Session {
shared: Arc<SessionShared>,
version: Version,
send_bandwidth: Option<bandwidth::Consumer>,
recv_bandwidth: Option<bandwidth::Consumer>,
}
impl Session {
pub fn version(&self) -> Version {
self.version
}
pub fn send_bandwidth(&self) -> Option<bandwidth::Consumer> {
self.send_bandwidth.clone()
}
pub fn recv_bandwidth(&self) -> Option<bandwidth::Consumer> {
self.recv_bandwidth.clone()
}
pub fn stats(&self) -> ConnectionStats {
let mut stats = self.shared.inner.stats();
stats.estimated_recv_rate = self.recv_bandwidth.as_ref().and_then(bandwidth::Consumer::peek);
stats
}
pub fn abort(&self, err: Error) {
self.shared.close(err.to_code(), err.to_string().as_ref());
}
pub async fn closed(&self) -> Error {
Error::Transport(self.shared.inner.closed().await)
}
}
pub struct Driver {
protocol: MaybeSendBox<'static, Result<(), Error>>,
maintenance: Option<MaybeSendBox<'static, ()>>,
result: Option<Result<(), Error>>,
waiter: Option<kio::Waiter>,
}
impl Driver {
pub fn poll(&mut self, waiter: &kio::Waiter) -> Poll<Result<(), Error>> {
if let Some(result) = &self.result {
return Poll::Ready(result.clone());
}
if let Some(maintenance) = &mut self.maintenance
&& waiter.poll_future(maintenance.as_mut()).is_ready()
{
self.maintenance = None;
}
let result = std::task::ready!(waiter.poll_future(self.protocol.as_mut()));
self.result = Some(result.clone());
self.maintenance = None;
Poll::Ready(result)
}
pub(super) async fn wait_ready(&mut self, ready: impl Future<Output = ()>) {
let mut ready = std::pin::pin!(ready);
kio::wait(|waiter| {
if waiter.poll_future(ready.as_mut()).is_ready() {
return Poll::Ready(());
}
let _ = self.poll(waiter);
Poll::Pending
})
.await
}
}
impl Future for Driver {
type Output = Result<(), Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
let waiter = kio::Waiter::new(cx.waker().clone());
let result = this.poll(&waiter);
this.waiter = Some(waiter);
result
}
}
struct SessionShared {
inner: Box<dyn SessionInner>,
closed: std::sync::atomic::AtomicBool,
}
impl SessionShared {
fn close(&self, code: u32, reason: &str) {
if !self.closed.swap(true, std::sync::atomic::Ordering::SeqCst) {
self.inner.close(code, reason);
}
}
}
impl Drop for SessionShared {
fn drop(&mut self) {
self.close(Error::Cancel.to_code(), "dropped");
}
}
impl Session {
pub(super) fn new<S: web_transport_trait::Session>(
session: S,
version: Version,
recv_bandwidth: Option<bandwidth::Consumer>,
protocol: MaybeSendBox<'static, Result<(), Error>>,
) -> (Self, Driver) {
let (send_bandwidth, maintenance) = if session.stats().estimated_send_rate().is_some() {
let producer = bandwidth::Producer::new();
let consumer = producer.consume();
let mut monitor = SendBandwidth::new(session.clone(), producer);
let maintenance = async move { kio::wait(|waiter| monitor.poll(waiter)).await }.maybe_boxed();
(Some(consumer), Some(maintenance))
} else {
(None, None)
};
let session = Self {
shared: Arc::new(SessionShared {
inner: Box::new(session),
closed: std::sync::atomic::AtomicBool::new(false),
}),
version,
send_bandwidth,
recv_bandwidth,
};
let driver = Driver {
protocol,
maintenance,
result: None,
waiter: None,
};
(session, driver)
}
}
struct SendBandwidth<S> {
session: S,
producer: bandwidth::Producer,
closed: MaybeSendBox<'static, ()>,
mode: SendBandwidthMode,
}
enum SendBandwidthMode {
Idle,
Polling { sleep: MaybeSendBox<'static, ()> },
}
impl<S: web_transport_trait::Session> SendBandwidth<S> {
const POLL_INTERVAL: Duration = Duration::from_millis(100);
fn new(session: S, producer: bandwidth::Producer) -> Self {
let closed = {
let session = session.clone();
async move {
session.closed().await;
}
}
.maybe_boxed();
Self {
session,
producer,
closed,
mode: SendBandwidthMode::Idle,
}
}
fn sample(&mut self) -> Result<(), Error> {
let bitrate = self.session.stats().estimated_send_rate();
self.producer.set(bitrate)?;
self.mode = SendBandwidthMode::Polling {
sleep: web_async::time::sleep(Self::POLL_INTERVAL).maybe_boxed(),
};
Ok(())
}
fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
if waiter.poll_future(self.closed.as_mut()).is_ready() {
return Poll::Ready(());
}
loop {
match &mut self.mode {
SendBandwidthMode::Idle => {
match self.producer.poll_used(waiter) {
Poll::Ready(Ok(())) => {}
Poll::Ready(Err(_)) => return Poll::Ready(()),
Poll::Pending => return Poll::Pending,
}
if self.sample().is_err() {
return Poll::Ready(());
}
}
SendBandwidthMode::Polling { sleep } => {
match self.producer.poll_unused(waiter) {
Poll::Ready(Ok(())) => {
self.mode = SendBandwidthMode::Idle;
continue;
}
Poll::Ready(Err(_)) => return Poll::Ready(()),
Poll::Pending => {}
}
if waiter.poll_future(sleep.as_mut()).is_pending() {
return Poll::Pending;
}
if self.sample().is_err() {
return Poll::Ready(());
}
}
}
}
}
}
trait SessionInner: web_transport_trait::MaybeSend + web_transport_trait::MaybeSync {
fn close(&self, code: u32, reason: &str);
fn closed(&self) -> MaybeSendBox<'_, String>;
fn stats(&self) -> ConnectionStats;
}
impl<S: web_transport_trait::Session> SessionInner for S {
fn close(&self, code: u32, reason: &str) {
S::close(self, code, reason);
}
fn closed(&self) -> MaybeSendBox<'_, String> {
Box::pin(async move { S::closed(self).await.to_string() })
}
fn stats(&self) -> ConnectionStats {
let stats = S::stats(self);
ConnectionStats {
rtt: stats.rtt(),
estimated_send_rate: stats.estimated_send_rate(),
bytes_sent: stats.bytes_sent(),
bytes_received: stats.bytes_received(),
bytes_lost: stats.bytes_lost(),
packets_sent: stats.packets_sent(),
packets_received: stats.packets_received(),
packets_lost: stats.packets_lost(),
..Default::default()
}
}
}