use std::future::Future;
use futures::future::BoxFuture;
use crate::{
codec::{BoxReadStream, BoxWriteStream},
quic::{ReadStream, WriteStream},
varint::VarInt,
};
mod error;
mod protocol;
mod session;
pub use error::{Closed, DatagramError, OpenSnafu, OpenStreamError, RegisterError};
pub use protocol::{
WT_BIDI_SIGNAL, WT_UNI_SIGNAL, WebTransportProtocol, WebTransportProtocolFactory,
};
pub use session::WebTransportSession;
pub trait Session: Send + Sync {
type StreamReader: ReadStream + Unpin;
type StreamWriter: WriteStream + Unpin;
fn session_id(&self) -> VarInt;
fn open_bi(
&self,
) -> impl Future<Output = Result<(Self::StreamReader, Self::StreamWriter), OpenStreamError>>
+ Send
+ '_;
fn open_uni(
&self,
) -> impl Future<Output = Result<Self::StreamWriter, OpenStreamError>> + Send + '_;
fn accept_bi(
&self,
) -> impl Future<Output = Result<(Self::StreamReader, Self::StreamWriter), Closed>> + Send + '_;
fn accept_uni(&self) -> impl Future<Output = Result<Self::StreamReader, Closed>> + Send + '_;
}
pub trait DynSession: Send + Sync {
fn session_id(&self) -> VarInt;
#[allow(clippy::type_complexity)]
fn open_bi(&self) -> BoxFuture<'_, Result<(BoxReadStream, BoxWriteStream), OpenStreamError>>;
fn open_uni(&self) -> BoxFuture<'_, Result<BoxWriteStream, OpenStreamError>>;
#[allow(clippy::type_complexity)]
fn accept_bi(&self) -> BoxFuture<'_, Result<(BoxReadStream, BoxWriteStream), Closed>>;
fn accept_uni(&self) -> BoxFuture<'_, Result<BoxReadStream, Closed>>;
}
impl<T: Session> DynSession for T {
fn session_id(&self) -> VarInt {
Session::session_id(self)
}
fn open_bi(&self) -> BoxFuture<'_, Result<(BoxReadStream, BoxWriteStream), OpenStreamError>> {
Box::pin(async {
let (r, w) = Session::open_bi(self).await?;
Ok((Box::pin(r) as BoxReadStream, Box::pin(w) as BoxWriteStream))
})
}
fn open_uni(&self) -> BoxFuture<'_, Result<BoxWriteStream, OpenStreamError>> {
Box::pin(async {
let w = Session::open_uni(self).await?;
Ok(Box::pin(w) as BoxWriteStream)
})
}
fn accept_bi(&self) -> BoxFuture<'_, Result<(BoxReadStream, BoxWriteStream), Closed>> {
Box::pin(async {
let (r, w) = Session::accept_bi(self).await?;
Ok((Box::pin(r) as BoxReadStream, Box::pin(w) as BoxWriteStream))
})
}
fn accept_uni(&self) -> BoxFuture<'_, Result<BoxReadStream, Closed>> {
Box::pin(async {
let r = Session::accept_uni(self).await?;
Ok(Box::pin(r) as BoxReadStream)
})
}
}
impl Session for WebTransportSession {
type StreamReader = BoxReadStream;
type StreamWriter = BoxWriteStream;
fn session_id(&self) -> VarInt {
WebTransportSession::session_id(self)
}
async fn open_bi(&self) -> Result<(BoxReadStream, BoxWriteStream), OpenStreamError> {
WebTransportSession::open_bi(self).await
}
async fn open_uni(&self) -> Result<BoxWriteStream, OpenStreamError> {
WebTransportSession::open_uni(self).await
}
async fn accept_bi(&self) -> Result<(BoxReadStream, BoxWriteStream), Closed> {
WebTransportSession::accept_bi(self).await
}
async fn accept_uni(&self) -> Result<BoxReadStream, Closed> {
WebTransportSession::accept_uni(self).await
}
}
#[cfg(feature = "rpc")]
mod lifecycle_ext {
use std::future::Future;
use snafu::ResultExt;
use super::{Closed, OpenSnafu, OpenStreamError};
use crate::{
quic::{self, ConnectionError},
rpc::lifecycle::LifecycleExt,
};
#[allow(async_fn_in_trait)]
pub trait WtLifecycleExt: LifecycleExt {
fn check_open(&self) -> Result<(), OpenStreamError> {
quic::Lifecycle::check(self).context(OpenSnafu)
}
fn check_accept(&self) -> Result<(), Closed> {
quic::Lifecycle::check(self).map_err(|_| Closed)
}
async fn guard_open<T>(
&self,
fut: impl Future<Output = Result<T, OpenStreamError>>,
) -> Result<T, OpenStreamError> {
self.check_open()?;
match fut.await {
Ok(v) => Ok(v),
Err(OpenStreamError::Open { source }) => Err(OpenStreamError::Open {
source: self.latch().latch_with(|| source),
}),
Err(other) => Err(other),
}
}
async fn guard_open_with<T, E, M>(
&self,
fut: impl Future<Output = Result<T, E>>,
map_err: M,
) -> Result<T, OpenStreamError>
where
M: FnOnce(E) -> OpenStreamError,
{
self.check_open()?;
match fut.await {
Ok(v) => Ok(v),
Err(e) => {
if let Some(existing) = self.latch().peek() {
return Err(OpenStreamError::Open { source: existing });
}
Err(match map_err(e) {
OpenStreamError::Open { source } => OpenStreamError::Open {
source: self.latch().latch_with(|| source),
},
other => other,
})
}
}
}
async fn guard_accept<T>(
&self,
fut: impl Future<Output = Result<T, Closed>>,
) -> Result<T, Closed> {
self.check_accept()?;
fut.await
}
async fn guard_accept_err<T, E, M>(
&self,
fut: impl Future<Output = Result<T, E>>,
map_err: M,
) -> Result<T, Closed>
where
M: FnOnce(E) -> Option<ConnectionError>,
{
self.check_accept()?;
match fut.await {
Ok(v) => Ok(v),
Err(e) => {
if self.latch().peek().is_none()
&& let Some(error) = map_err(e)
{
let _ = self.latch().latch_with(|| error);
}
Err(Closed)
}
}
}
}
impl<T: LifecycleExt + ?Sized> WtLifecycleExt for T {}
}
#[cfg(feature = "rpc")]
pub use lifecycle_ext::WtLifecycleExt;