mod announce;
mod announced;
mod error;
mod publisher;
mod reader;
mod subscribe;
mod subscribed;
mod subscriber;
mod track_status_requested;
mod writer;
pub use announce::*;
pub use announced::*;
pub use error::*;
pub use publisher::*;
pub use subscribe::*;
pub use subscribed::*;
pub use subscriber::*;
pub use track_status_requested::*;
use reader::*;
use writer::*;
use futures::{stream::FuturesUnordered, StreamExt};
use std::sync::{atomic, Arc, Mutex};
use crate::coding::KeyValuePairs;
use crate::message::Message;
use crate::mlog;
use crate::watch::Queue;
use crate::{message, setup};
use std::path::PathBuf;
#[must_use = "run() must be called"]
pub struct Session {
webtransport: web_transport::Session,
sender: Writer, recver: Reader,
publisher: Option<Publisher>, subscriber: Option<Subscriber>,
outgoing: Queue<Message>,
mlog: Option<Arc<Mutex<mlog::MlogWriter>>>,
}
impl Session {
fn largest_common<T: Ord + Clone + Eq>(a: &[T], b: &[T]) -> Option<T> {
a.iter()
.filter(|x| b.contains(x)) .cloned() .max() }
fn new(
webtransport: web_transport::Session,
sender: Writer,
recver: Reader,
first_requestid: u64,
mlog: Option<mlog::MlogWriter>,
) -> (Self, Option<Publisher>, Option<Subscriber>) {
let next_requestid = Arc::new(atomic::AtomicU64::new(first_requestid));
let outgoing = Queue::default().split();
let mlog_shared = mlog.map(|m| Arc::new(Mutex::new(m)));
let publisher = Some(Publisher::new(
outgoing.0.clone(),
webtransport.clone(),
next_requestid.clone(),
mlog_shared.clone(),
));
let subscriber = Some(Subscriber::new(
outgoing.0,
next_requestid,
mlog_shared.clone(),
));
let session = Self {
webtransport,
sender,
recver,
publisher: publisher.clone(),
subscriber: subscriber.clone(),
outgoing: outgoing.1,
mlog: mlog_shared,
};
(session, publisher, subscriber)
}
pub async fn connect(
mut session: web_transport::Session,
mlog_path: Option<PathBuf>,
) -> Result<(Session, Publisher, Subscriber), SessionError> {
let mlog = mlog_path.and_then(|path| {
mlog::MlogWriter::new(path)
.map_err(|e| log::warn!("Failed to create mlog: {}", e))
.ok()
});
let control = session.open_bi().await?;
let mut sender = Writer::new(control.0);
let mut recver = Reader::new(control.1);
let versions: setup::Versions = [setup::Version::DRAFT_14].into();
let mut params = KeyValuePairs::default();
params.set_intvalue(setup::ParameterType::MaxRequestId.into(), 100);
let client = setup::Client {
versions: versions.clone(),
params,
};
log::debug!("sending CLIENT_SETUP: {:?}", client);
sender.encode(&client).await?;
let server: setup::Server = recver.decode().await?;
log::debug!("received SERVER_SETUP: {:?}", server);
let session = Session::new(session, sender, recver, 0, mlog);
Ok((session.0, session.1.unwrap(), session.2.unwrap()))
}
pub async fn accept(
mut session: web_transport::Session,
mlog_path: Option<PathBuf>,
) -> Result<(Session, Option<Publisher>, Option<Subscriber>), SessionError> {
let mut mlog = mlog_path.and_then(|path| {
mlog::MlogWriter::new(path)
.map_err(|e| log::warn!("Failed to create mlog: {}", e))
.ok()
});
let control = session.accept_bi().await?;
let mut sender = Writer::new(control.0);
let mut recver = Reader::new(control.1);
let client: setup::Client = recver.decode().await?;
log::debug!("received CLIENT_SETUP: {:?}", client);
if let Some(ref mut mlog) = mlog {
let event = mlog::events::client_setup_parsed(mlog.elapsed_ms(), 0, &client);
let _ = mlog.add_event(event);
}
let server_versions = setup::Versions(vec![setup::Version::DRAFT_14]);
if let Some(largest_common_version) =
Self::largest_common(&server_versions, &client.versions)
{
let mut params = KeyValuePairs::default();
params.set_intvalue(setup::ParameterType::MaxRequestId.into(), 100);
let server = setup::Server {
version: largest_common_version,
params,
};
log::debug!("sending SERVER_SETUP: {:?}", server);
if let Some(ref mut mlog) = mlog {
let event = mlog::events::server_setup_created(mlog.elapsed_ms(), 0, &server);
let _ = mlog.add_event(event);
}
sender.encode(&server).await?;
Ok(Session::new(session, sender, recver, 1, mlog))
} else {
Err(SessionError::Version(client.versions, server_versions))
}
}
pub async fn run(self) -> Result<(), SessionError> {
tokio::select! {
res = Self::run_recv(self.recver, self.publisher, self.subscriber.clone(), self.mlog.clone()) => res,
res = Self::run_send(self.sender, self.outgoing, self.mlog.clone()) => res,
res = Self::run_streams(self.webtransport.clone(), self.subscriber.clone()) => res,
res = Self::run_datagrams(self.webtransport, self.subscriber) => res,
}
}
async fn run_send(
mut sender: Writer,
mut outgoing: Queue<message::Message>,
mlog: Option<Arc<Mutex<mlog::MlogWriter>>>,
) -> Result<(), SessionError> {
while let Some(msg) = outgoing.pop().await {
log::debug!("sending message: {:?}", msg);
if let Some(ref mlog) = mlog {
if let Ok(mut mlog_guard) = mlog.lock() {
let time = mlog_guard.elapsed_ms();
let stream_id = 0;
let event = match &msg {
Message::Subscribe(m) => {
Some(mlog::events::subscribe_created(time, stream_id, m))
}
Message::SubscribeOk(m) => {
Some(mlog::events::subscribe_ok_created(time, stream_id, m))
}
Message::SubscribeError(m) => {
Some(mlog::events::subscribe_error_created(time, stream_id, m))
}
Message::Unsubscribe(m) => {
Some(mlog::events::unsubscribe_created(time, stream_id, m))
}
Message::PublishNamespace(m) => {
Some(mlog::events::publish_namespace_created(time, stream_id, m))
}
Message::PublishNamespaceOk(m) => Some(
mlog::events::publish_namespace_ok_created(time, stream_id, m),
),
Message::PublishNamespaceError(m) => Some(
mlog::events::publish_namespace_error_created(time, stream_id, m),
),
Message::GoAway(m) => {
Some(mlog::events::go_away_created(time, stream_id, m))
}
_ => None, };
if let Some(event) = event {
let _ = mlog_guard.add_event(event);
}
}
}
sender.encode(&msg).await?;
}
Ok(())
}
async fn run_recv(
mut recver: Reader,
mut publisher: Option<Publisher>,
mut subscriber: Option<Subscriber>,
mlog: Option<Arc<Mutex<mlog::MlogWriter>>>,
) -> Result<(), SessionError> {
loop {
let msg: message::Message = recver.decode().await?;
log::debug!("received message: {:?}", msg);
if let Some(ref mlog) = mlog {
if let Ok(mut mlog_guard) = mlog.lock() {
let time = mlog_guard.elapsed_ms();
let stream_id = 0;
let event = match &msg {
Message::Subscribe(m) => {
Some(mlog::events::subscribe_parsed(time, stream_id, m))
}
Message::SubscribeOk(m) => {
Some(mlog::events::subscribe_ok_parsed(time, stream_id, m))
}
Message::SubscribeError(m) => {
Some(mlog::events::subscribe_error_parsed(time, stream_id, m))
}
Message::Unsubscribe(m) => {
Some(mlog::events::unsubscribe_parsed(time, stream_id, m))
}
Message::PublishNamespace(m) => {
Some(mlog::events::publish_namespace_parsed(time, stream_id, m))
}
Message::PublishNamespaceOk(m) => Some(
mlog::events::publish_namespace_ok_parsed(time, stream_id, m),
),
Message::PublishNamespaceError(m) => Some(
mlog::events::publish_namespace_error_parsed(time, stream_id, m),
),
Message::GoAway(m) => {
Some(mlog::events::go_away_parsed(time, stream_id, m))
}
_ => None, };
if let Some(event) = event {
let _ = mlog_guard.add_event(event);
}
}
}
let msg = match TryInto::<message::Publisher>::try_into(msg) {
Ok(msg) => {
subscriber
.as_mut()
.ok_or(SessionError::RoleViolation)?
.recv_message(msg)?;
continue;
}
Err(msg) => msg,
};
let msg = match TryInto::<message::Subscriber>::try_into(msg) {
Ok(msg) => {
publisher
.as_mut()
.ok_or(SessionError::RoleViolation)?
.recv_message(msg)?;
continue;
}
Err(msg) => msg,
};
log::warn!("Unimplemented message type received: {:?}", msg);
return Err(SessionError::unimplemented(&format!(
"message type {:?}",
msg
)));
}
}
async fn run_streams(
mut webtransport: web_transport::Session,
subscriber: Option<Subscriber>,
) -> Result<(), SessionError> {
let mut tasks = FuturesUnordered::new();
loop {
tokio::select! {
res = webtransport.accept_uni() => {
let stream = res?;
let subscriber = subscriber.clone().ok_or(SessionError::RoleViolation)?;
tasks.push(async move {
if let Err(err) = Subscriber::recv_stream(subscriber, stream).await {
log::warn!("failed to serve stream: {}", err);
};
});
},
_ = tasks.next(), if !tasks.is_empty() => {},
};
}
}
async fn run_datagrams(
mut webtransport: web_transport::Session,
mut subscriber: Option<Subscriber>,
) -> Result<(), SessionError> {
loop {
let datagram = webtransport.recv_datagram().await?;
subscriber
.as_mut()
.ok_or(SessionError::RoleViolation)?
.recv_datagram(datagram)
.await?;
}
}
}