use web_transport_trait::{MaybeSend, MaybeSync};
use crate::origin;
use crate::time::{Clock, Instant};
use crate::{
ALPN_14, ALPN_15, ALPN_16, ALPN_17, ALPN_18, ALPN_19, ALPN_20, ALPN_21, ALPN_22, ALPN_LITE, ALPN_LITE_03,
ALPN_LITE_04, ALPN_LITE_05, ALPN_LITE_06, ALPN_LITE_07, Consume, Error, NEGOTIATED, Role, Session, SessionError,
Version, Versions,
coding::{Decode, Encode, Stream},
ietf, lite, setup, stats,
};
#[derive(Default, Clone)]
pub struct Server {
publish: Option<origin::Consumer>,
subscribe: Option<origin::Producer>,
stats: stats::Session,
versions: Versions,
}
impl Server {
pub fn new() -> Self {
Default::default()
}
pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
self.publish = Some(publish.consume());
self
}
pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
self.subscribe = Some(subscribe);
self
}
pub fn with_stats(mut self, stats: stats::Session) -> Self {
self.stats = stats;
self
}
pub fn with_origin(self, origin: origin::Producer) -> Self {
self.with_publisher(&origin).with_subscriber(origin)
}
pub fn with_versions(mut self, versions: Versions) -> Self {
self.versions = versions;
self
}
fn stat_tagged_origins(&self) -> (Option<origin::Consumer>, Option<origin::Producer>) {
let publish = self.publish.clone().map(|origin| origin.with_stats(self.stats.clone()));
let subscribe = self
.subscribe
.clone()
.map(|origin| origin.with_stats(self.stats.clone()));
(publish, subscribe)
}
fn start_lite<S>(
&self,
runtime: Clock,
session: S,
version: lite::Version,
client_setup: Option<lite::Setup>,
peer_hop: Option<crate::Hop>,
) -> Result<(Session, crate::Driver<S>), Error>
where
S: crate::transport::poll::Session,
{
let (publish, subscribe) = self.stat_tagged_origins();
let our_setup = if version.has_setup_stream() {
lite::Setup {
probe: lite::ProbeLevel::detect(&session),
path: None,
role: None,
cost: None,
hop: None,
}
} else {
lite::Setup::default()
};
let start = lite::start(lite::Config {
runtime: runtime.clone(),
session: session.clone(),
setup_stream: None,
publish,
subscribe,
peer_hop,
version,
our_setup,
peer_setup: client_setup,
})?;
Ok(Session::new(
runtime,
session,
version.into(),
start.recv_bandwidth,
crate::driver::Protocol::Lite(Box::new(start.driver)),
start.goaway,
))
}
pub async fn accept_lite<S>(&self, now: Instant, session: S) -> Result<(Session, crate::Driver<S>), Error>
where
S: crate::transport::poll::Session,
{
self.accept_request_lite(now, session).await?.ok().await
}
pub async fn accept_request_lite<S>(&self, now: Instant, mut session: S) -> Result<Handshake<S>, Error>
where
S: crate::transport::poll::Session,
{
let runtime = Clock::new(now);
let (path, role, origin, handshake) = match session.protocol() {
Some(alpn @ (ALPN_LITE_05 | ALPN_LITE_06 | ALPN_LITE_07)) => {
let version = match alpn {
ALPN_LITE_07 => lite::Version::Lite07,
ALPN_LITE_06 => lite::Version::Lite06,
_ => lite::Version::Lite05,
};
self.versions.select(Version::Lite(version)).ok_or(Error::Version)?;
let client_setup = lite::accept_setup(&mut session, version).await?;
(
client_setup.path.clone(),
client_setup.role,
client_setup.hop,
PausedHandshake::LiteSetup {
session,
version,
client_setup,
},
)
}
Some(ALPN_LITE_04) => {
self.versions
.select(Version::Lite(lite::Version::Lite04))
.ok_or(Error::Version)?;
(
None,
None,
None,
PausedHandshake::LiteBare {
session,
version: lite::Version::Lite04,
},
)
}
Some(ALPN_LITE_03) => {
self.versions
.select(Version::Lite(lite::Version::Lite03))
.ok_or(Error::Version)?;
(
None,
None,
None,
PausedHandshake::LiteBare {
session,
version: lite::Version::Lite03,
},
)
}
_ => return Err(Error::Version),
};
Ok(Handshake {
path,
role,
origin,
assigned_hop: crate::Hop::random(),
inner: Some(RequestInner {
server: self.clone(),
runtime,
handshake,
}),
})
}
pub async fn accept<S>(&self, now: Instant, session: S) -> Result<(Session, crate::Driver<S>), Error>
where
S: crate::transport::poll::Boxable,
S::SendStream: MaybeSync,
S::RecvStream: MaybeSync,
{
self.accept_request(now, session).await?.ok().await
}
pub async fn accept_request<S>(&self, now: Instant, mut session: S) -> Result<Handshake<S>, Error>
where
S: crate::transport::poll::Boxable,
S::SendStream: MaybeSync,
S::RecvStream: MaybeSync,
{
let runtime = Clock::new(now);
let (encoding, supported) = match session.protocol() {
Some(alpn @ (ALPN_22 | ALPN_21 | ALPN_20 | ALPN_19 | ALPN_18 | ALPN_17)) => {
let draft = match alpn {
ALPN_22 => ietf::Version::Draft22,
ALPN_21 => ietf::Version::Draft21,
ALPN_20 => ietf::Version::Draft20,
ALPN_19 => ietf::Version::Draft19,
ALPN_18 => ietf::Version::Draft18,
_ => ietf::Version::Draft17,
};
self.versions.select(Version::Ietf(draft)).ok_or(Error::Version)?;
return self.accept_ietf_modern(runtime, session, draft).await;
}
Some(ALPN_16) => {
let v = self
.versions
.select(Version::Ietf(ietf::Version::Draft16))
.ok_or(Error::Version)?;
(v, v.into())
}
Some(ALPN_15) => {
let v = self
.versions
.select(Version::Ietf(ietf::Version::Draft15))
.ok_or(Error::Version)?;
(v, v.into())
}
Some(ALPN_14) => {
let v = self
.versions
.select(Version::Ietf(ietf::Version::Draft14))
.ok_or(Error::Version)?;
(v, v.into())
}
Some(ALPN_LITE_07 | ALPN_LITE_06 | ALPN_LITE_05 | ALPN_LITE_04 | ALPN_LITE_03) => {
return self.accept_request_lite(now, session).await;
}
Some(ALPN_LITE) | None => {
let supported = self.versions.filter(&NEGOTIATED.into()).ok_or(Error::Version)?;
(Version::Ietf(ietf::Version::Draft14), supported)
}
Some(p) => return Err(Error::UnknownAlpn(p.to_string())),
};
let mut stream = Stream::accept(&mut session, encoding).await?;
let mut client: setup::Client = stream.reader.decode().await?;
let version = client
.versions
.iter()
.flat_map(|v| Version::try_from(*v).ok())
.find(|v| supported.contains(v))
.ok_or(Error::Version)?;
let (path, request_id_max, peer_declared) = match version {
Version::Ietf(v) => {
let params = ietf::Parameters::decode(&mut client.parameters, v)?;
let path = match params.get_bytes(ietf::ParameterBytes::Path) {
Some(bytes) => Some(
std::str::from_utf8(bytes)
.map_err(|_| Error::Decode(crate::DecodeError::InvalidValue))?
.to_owned(),
),
None => None,
};
let request_id_max = params
.get_varint(ietf::ParameterVarInt::MaxRequestId)
.map(ietf::RequestId);
let peer_declared = ietf::peer::Peer {
solicit: ietf::solicit::from_setup(¶ms, v)?,
hidden: ietf::hidden::from_setup(¶ms, v),
..Default::default()
};
(path, request_id_max, peer_declared)
}
Version::Lite(_) => (None, None, ietf::peer::Peer::default()),
};
Ok(Handshake {
path,
role: None,
origin: None,
assigned_hop: crate::Hop::random(),
inner: Some(RequestInner {
server: self.clone(),
runtime,
handshake: PausedHandshake::Boxed(Box::new(PausedLegacy {
session,
stream,
version,
request_id_max,
peer_declared,
})),
}),
})
}
async fn accept_ietf_modern<S>(
&self,
runtime: Clock,
mut session: S,
version: ietf::Version,
) -> Result<Handshake<S>, Error>
where
S: crate::transport::poll::Boxable,
S::SendStream: MaybeSync,
S::RecvStream: MaybeSync,
{
let peer_setup = ietf::accept_setup(&mut session, version).await?;
Ok(Handshake {
path: peer_setup.path.clone(),
role: None,
origin: peer_setup.declared.cluster.hop.filter(|h| *h != crate::Hop::UNKNOWN),
assigned_hop: crate::Hop::random(),
inner: Some(RequestInner {
server: self.clone(),
runtime,
handshake: PausedHandshake::Boxed(Box::new(PausedIetfModern {
session,
version,
peer_setup,
})),
}),
})
}
}
pub struct Handshake<S: crate::transport::poll::Session> {
path: Option<String>,
role: Option<Role>,
origin: Option<crate::Hop>,
assigned_hop: crate::Hop,
inner: Option<RequestInner<S>>,
}
struct RequestInner<S: crate::transport::poll::Session> {
server: Server,
runtime: Clock,
handshake: PausedHandshake<S>,
}
enum PausedHandshake<S: crate::transport::poll::Session> {
LiteBare { session: S, version: lite::Version },
LiteSetup {
session: S,
version: lite::Version,
client_setup: lite::Setup,
},
Boxed(Box<dyn Paused<S>>),
}
type Accept<S> = crate::util::MaybeSendBox<'static, Result<(Session, crate::Driver<S>), Error>>;
trait Paused<S: crate::transport::poll::Session>: MaybeSend + MaybeSync {
fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S>;
fn close(self: Box<Self>, err: Error);
}
struct PausedIetfModern<S: crate::transport::poll::Session> {
session: S,
version: ietf::Version,
peer_setup: ietf::PeerSetup<S>,
}
impl<S> Paused<S> for PausedIetfModern<S>
where
S: crate::transport::poll::Boxable,
S::SendStream: MaybeSync,
S::RecvStream: MaybeSync,
{
fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S> {
use crate::util::MaybeBoxedExt as _;
async move {
let Self {
session,
version,
peer_setup,
} = *self;
let (publish, subscribe) = server.stat_tagged_origins();
let (protocol, goaway) = ietf::start(ietf::Config {
runtime: runtime.clone(),
session: session.clone(),
setup: None,
request_id_max: None,
client: false,
publish,
subscribe,
peer_hop,
cost: None,
version,
path: None,
peer_setup_stream: Some(peer_setup.stream),
peer_declared: Some(peer_setup.declared),
})?;
tracing::debug!(?version, "connected");
Ok(Session::new(
runtime,
session,
version.into(),
None,
crate::driver::Protocol::Ietf(protocol),
goaway,
))
}
.maybe_boxed()
}
fn close(self: Box<Self>, err: Error) {
let mut session = self.session;
session.close(SessionError::from(&err).to_code(), &err.to_string());
}
}
struct PausedLegacy<S: crate::transport::poll::Session> {
session: S,
stream: Stream<S, Version>,
version: Version,
request_id_max: Option<ietf::RequestId>,
peer_declared: ietf::peer::Peer,
}
impl<S> Paused<S> for PausedLegacy<S>
where
S: crate::transport::poll::Boxable,
S::SendStream: MaybeSync,
S::RecvStream: MaybeSync,
{
fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S> {
use crate::util::MaybeBoxedExt as _;
async move {
let Self {
session,
mut stream,
version,
request_id_max,
peer_declared,
} = *self;
let (publish, subscribe) = server.stat_tagged_origins();
let parameters = match version {
Version::Ietf(v) => {
let mut parameters = ietf::Parameters::default();
parameters.set_varint(ietf::ParameterVarInt::MaxRequestId, u32::MAX as u64);
parameters.set_bytes(ietf::ParameterBytes::Implementation, b"moq-lite-rs".to_vec());
ietf::solicit::into_setup(&mut parameters, v);
ietf::hidden::into_setup(&mut parameters, v);
parameters.encode_bytes(v)?
}
Version::Lite(v) => lite::Parameters::default().encode_bytes(v)?,
};
let server_setup = setup::Server {
version: version.into(),
parameters,
};
stream.writer.encode(&server_setup).await?;
let (recv_bw, protocol, goaway) = match version {
Version::Lite(v) => {
let stream = stream.with_version(v);
let start = lite::start(lite::Config {
runtime: runtime.clone(),
session: session.clone(),
setup_stream: Some(stream),
publish,
subscribe,
peer_hop,
version: v,
our_setup: lite::Setup::default(),
peer_setup: None,
})?;
(
start.recv_bandwidth,
crate::driver::Protocol::Lite(Box::new(start.driver)),
start.goaway,
)
}
Version::Ietf(v) => {
let stream = stream.with_version(v);
let (protocol, goaway) = ietf::start(ietf::Config {
runtime: runtime.clone(),
session: session.clone(),
setup: Some(stream),
request_id_max,
client: false,
publish,
subscribe,
peer_hop,
cost: None,
version: v,
path: None,
peer_setup_stream: None,
peer_declared: Some(peer_declared),
})?;
(None, crate::driver::Protocol::Ietf(protocol), goaway)
}
};
Ok(Session::new(runtime, session, version, recv_bw, protocol, goaway))
}
.maybe_boxed()
}
fn close(self: Box<Self>, err: Error) {
let mut session = self.session;
session.close(SessionError::from(&err).to_code(), &err.to_string());
}
}
impl<S> Handshake<S>
where
S: crate::transport::poll::Session,
{
pub fn path(&self) -> &str {
self.path.as_deref().unwrap_or("")
}
pub fn role(&self) -> Option<Role> {
self.role
}
pub fn peer_hop(&self) -> Option<crate::Hop> {
self.origin
}
pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
self.inner_mut().server.publish = Some(publish.consume());
self
}
pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
self.inner_mut().server.subscribe = Some(subscribe);
self
}
pub fn with_peer_hop(mut self, hop: crate::Hop) -> Self {
self.assigned_hop = hop;
self
}
pub fn with_stats(mut self, stats: stats::Session) -> Self {
self.inner_mut().server.stats = stats;
self
}
fn inner_mut(&mut self) -> &mut RequestInner<S> {
self.inner.as_mut().expect("request already responded")
}
pub async fn ok(mut self) -> Result<(Session, crate::Driver<S>), Error> {
let peer_hop = Some(self.assigned_hop);
let RequestInner {
server,
runtime,
handshake,
} = self.inner.take().expect("request already responded");
match handshake {
PausedHandshake::LiteBare { session, version } => {
server.start_lite(runtime, session, version, None, peer_hop)
}
PausedHandshake::LiteSetup {
session,
version,
client_setup,
} => server.start_lite(runtime, session, version, Some(client_setup), peer_hop),
PausedHandshake::Boxed(paused) => paused.ok(server, runtime, peer_hop).await,
}
}
pub fn close(mut self, err: Error) {
let inner = self.inner.take().expect("request already responded");
inner.close(err);
}
}
impl<S: crate::transport::poll::Session> RequestInner<S> {
fn close(self, err: Error) {
let mut session = match self.handshake {
PausedHandshake::LiteBare { session, .. } => session,
PausedHandshake::LiteSetup { session, .. } => session,
PausedHandshake::Boxed(paused) => return paused.close(err),
};
session.close(SessionError::from(&err).to_code(), &err.to_string());
}
}
impl<S: crate::transport::poll::Session> Drop for Handshake<S> {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
tracing::warn!("Handshake dropped without ok() or close(); rejecting the session");
inner.close(Error::Cancel);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Hop;
use crate::model::ProduceTest;
use std::{
collections::VecDeque,
sync::{Arc, Mutex},
};
use crate::ALPN_LITE_05;
use bytes::Bytes;
fn occurrences(log: &crate::lite::test_transport::Log, needle: &[u8]) -> usize {
let writes = log.writes.lock().unwrap();
writes.windows(needle.len()).filter(|window| *window == needle).count()
}
#[derive(Debug, Clone, Default)]
struct FakeError;
impl std::fmt::Display for FakeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "fake transport error")
}
}
impl std::error::Error for FakeError {}
impl web_transport_trait::Error for FakeError {
fn session_error(&self) -> Option<(u32, String)> {
Some((0, "closed".to_string()))
}
}
#[derive(Clone)]
struct FakeSession {
protocol: Option<&'static str>,
uni: Arc<Mutex<VecDeque<Vec<u8>>>>,
}
impl FakeSession {
fn new(protocol: &'static str, uni: impl IntoIterator<Item = Vec<u8>>) -> Self {
Self {
protocol: Some(protocol),
uni: Arc::new(Mutex::new(uni.into_iter().collect())),
}
}
}
impl web_transport_trait::poll::Session for FakeSession {
type SendStream = FakeSend;
type RecvStream = FakeRecv;
type Error = FakeError;
fn poll_accept_uni(
&mut self,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<Self::RecvStream, Self::Error>> {
match self.uni.lock().unwrap().pop_front() {
Some(data) => std::task::Poll::Ready(Ok(FakeRecv { data: data.into() })),
None => std::task::Poll::Pending,
}
}
fn poll_accept_bi(
&mut self,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
std::task::Poll::Pending
}
fn poll_open_bi(
&mut self,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
std::task::Poll::Pending
}
fn poll_open_uni(
&mut self,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<Self::SendStream, Self::Error>> {
std::task::Poll::Pending
}
fn poll_send_datagram(
&mut self,
_cx: &mut std::task::Context<'_>,
_payload: &[u8],
) -> std::task::Poll<Result<(), Self::Error>> {
std::task::Poll::Ready(Ok(()))
}
fn poll_recv_datagram(
&mut self,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<Bytes, Self::Error>> {
std::task::Poll::Pending
}
fn max_datagram_size(&self) -> usize {
1200
}
fn protocol(&self) -> Option<&str> {
self.protocol
}
fn close(&mut self, _code: u32, _reason: &str) {}
fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Error> {
std::task::Poll::Pending
}
fn stats(&self) -> impl web_transport_trait::Stats {
web_transport_trait::StatsUnavailable
}
}
#[derive(Clone, Default)]
struct FakeSend;
impl web_transport_trait::poll::SendStream for FakeSend {
type Error = FakeError;
fn poll_write(
&mut self,
_cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> std::task::Poll<Result<usize, Self::Error>> {
std::task::Poll::Ready(Ok(buf.len()))
}
fn set_priority(&mut self, _order: u8) {}
fn finish(&mut self) -> Result<(), Self::Error> {
Ok(())
}
fn reset(&mut self, _code: u32) {}
fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
std::task::Poll::Ready(Ok(()))
}
}
struct FakeRecv {
data: VecDeque<u8>,
}
impl web_transport_trait::poll::RecvStream for FakeRecv {
type Error = FakeError;
fn poll_read(
&mut self,
_cx: &mut std::task::Context<'_>,
dst: &mut [u8],
) -> std::task::Poll<Result<Option<usize>, Self::Error>> {
if self.data.is_empty() {
return std::task::Poll::Ready(Ok(None));
}
let size = dst.len().min(self.data.len());
for slot in dst.iter_mut().take(size) {
*slot = self.data.pop_front().unwrap();
}
std::task::Poll::Ready(Ok(Some(size)))
}
fn stop(&mut self, _code: u32) {}
fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
std::task::Poll::Ready(Ok(()))
}
}
fn lite05_setup(path: Option<&str>, role: Option<Role>, hop: Option<Hop>) -> Vec<u8> {
let v = lite::Version::Lite05;
let mut buf = Vec::new();
lite::DataType::Setup.encode(&mut buf, v).unwrap();
lite::Setup {
probe: lite::ProbeLevel::None,
path: path.map(str::to_string),
role,
cost: None,
hop,
}
.encode(&mut buf, v)
.unwrap();
buf
}
fn ietf_setup(version: ietf::Version, path: Option<&str>) -> Vec<u8> {
let mut params = ietf::Parameters::default();
if let Some(path) = path {
params.set_bytes(ietf::ParameterBytes::Path, path.as_bytes().to_vec());
}
let parameters = params.encode_bytes(version).unwrap();
let mut buf = Vec::new();
setup::Setup { parameters }
.encode(&mut buf, crate::Version::Ietf(version))
.unwrap();
buf
}
#[tokio::test(start_paused = true)]
async fn accept_request_reads_ietf_path() {
for (alpn, version) in [
(ALPN_17, ietf::Version::Draft17),
(ALPN_18, ietf::Version::Draft18),
(ALPN_19, ietf::Version::Draft19),
] {
let session = FakeSession::new(alpn, [ietf_setup(version, Some("/team/room"))]);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.path(), "/team/room", "{alpn}");
}
}
#[tokio::test(start_paused = true)]
async fn accept_request_ietf_without_path_is_empty() {
let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, None)]);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.path(), "");
}
#[tokio::test(start_paused = true)]
async fn accept_request_ietf_empty_path_is_accepted() {
let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, Some(""))]);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.path(), "");
}
fn lite05_group() -> Vec<u8> {
let mut buf = Vec::new();
lite::DataType::Group.encode(&mut buf, lite::Version::Lite05).unwrap();
buf
}
#[tokio::test(start_paused = true)]
async fn accept_request_reads_lite05_path() {
let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some("/team/room"), None, None)]);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.path(), "/team/room");
assert_eq!(request.role(), None, "a client that omits the role is bidirectional");
}
#[tokio::test(start_paused = true)]
async fn accept_request_lite05_without_path_is_empty() {
let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, None)]);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.path(), "");
}
#[tokio::test(start_paused = true)]
async fn accept_request_lite05_empty_path_is_accepted() {
let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some(""), None, None)]);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.path(), "");
}
#[tokio::test(start_paused = true)]
async fn accept_request_reads_lite05_role() {
let session = FakeSession::new(
ALPN_LITE_05,
[lite05_setup(Some("/team/room"), Some(Role::Publisher), None)],
);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.role(), Some(Role::Publisher));
}
#[tokio::test(start_paused = true)]
async fn accept_request_skips_uni_stream_before_setup() {
let session = FakeSession::new(
ALPN_LITE_05,
[lite05_group(), lite05_setup(Some("/team/room"), None, None)],
);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.path(), "/team/room");
}
#[tokio::test(start_paused = true)]
async fn accept_request_reads_lite05_peer_hop() {
let hop = Hop::new(42).unwrap();
let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, Some(hop))]);
let request = Server::new()
.accept_request(tokio::time::Instant::now().into_std(), session)
.await
.unwrap();
assert_eq!(request.peer_hop(), Some(hop));
}
#[tokio::test(start_paused = true)]
async fn anonymous_peer_hop_filters_routes_from_server_session() {
let other = Hop::new(778).unwrap();
let origin = crate::origin::Config::new(Hop::new(1).unwrap()).produce();
let gate = kio::Producer::new(true);
let transport = crate::lite::test_transport::SinkSession::gated_bi(gate.consume());
let log = transport.log.clone();
let version = ietf::Version::Draft18;
let request = Handshake {
path: None,
role: None,
origin: None,
assigned_hop: Hop::random(),
inner: Some(RequestInner {
server: Server::new().with_publisher(&origin),
runtime: Clock::new(tokio::time::Instant::now().into_std()),
handshake: PausedHandshake::Boxed(Box::new(PausedIetfModern {
session: transport,
version,
peer_setup: ietf::PeerSetup {
stream: crate::coding::Reader::new(
crate::lite::test_transport::PendingRecv,
Version::Ietf(version),
),
path: None,
declared: ietf::peer::Peer::default(),
},
})),
}),
};
let assigned = request.assigned_hop;
let mut echoed_hops = crate::Hops::new();
echoed_hops.push(crate::Hop::UNKNOWN).unwrap();
let _echoed = origin
.announce(
"echoed-route",
crate::origin::Route::default()
.with_hops(echoed_hops)
.with_via(assigned),
)
.unwrap();
let mut local_hops = crate::Hops::new();
local_hops.push(other).unwrap();
let _local = origin
.announce("local-route", crate::origin::Route::default().with_hops(local_hops))
.unwrap();
let (session, driver) = request.ok().await.unwrap();
tokio::spawn(crate::time::run(driver));
for _ in 0..100 {
if occurrences(&log, b"local-route") > 0 {
break;
}
tokio::time::sleep(std::time::Duration::from_millis(1)).await;
}
assert_eq!(occurrences(&log, b"echoed-route"), 0);
assert_eq!(occurrences(&log, b"local-route"), 1);
drop(session);
}
}