1use web_transport_trait::{MaybeSend, MaybeSync};
5
6use crate::origin;
7use crate::time::{Clock, Instant};
8use crate::{
9 ALPN_14, ALPN_15, ALPN_16, ALPN_17, ALPN_18, ALPN_19, ALPN_20, ALPN_21, ALPN_22, ALPN_LITE, ALPN_LITE_03,
10 ALPN_LITE_04, ALPN_LITE_05, ALPN_LITE_06, Consume, Error, NEGOTIATED, Role, Session, SessionError, Version,
11 Versions,
12 coding::{Decode, Encode, Stream},
13 ietf, lite, setup, stats,
14};
15
16#[derive(Default, Clone)]
18pub struct Server {
19 publish: Option<origin::Consumer>,
20 subscribe: Option<origin::Producer>,
21 stats: stats::Session,
22 versions: Versions,
23}
24
25impl Server {
26 pub fn new() -> Self {
28 Default::default()
29 }
30
31 pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
36 self.publish = Some(publish.consume());
37 self
38 }
39
40 pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
43 self.subscribe = Some(subscribe);
44 self
45 }
46
47 pub fn with_stats(mut self, stats: stats::Session) -> Self {
52 self.stats = stats;
53 self
54 }
55
56 pub fn with_origin(self, origin: origin::Producer) -> Self {
58 self.with_publisher(&origin).with_subscriber(origin)
59 }
60
61 pub fn with_versions(mut self, versions: Versions) -> Self {
64 self.versions = versions;
65 self
66 }
67
68 fn stat_tagged_origins(&self) -> (Option<origin::Consumer>, Option<origin::Producer>) {
73 let publish = self.publish.clone().map(|origin| origin.with_stats(self.stats.clone()));
74 let subscribe = self
75 .subscribe
76 .clone()
77 .map(|origin| origin.with_stats(self.stats.clone()));
78 (publish, subscribe)
79 }
80
81 fn start_lite<S>(
84 &self,
85 runtime: Clock,
86 session: S,
87 version: lite::Version,
88 client_setup: Option<lite::Setup>,
89 peer_hop: Option<crate::Hop>,
90 ) -> Result<(Session, crate::Driver<S>), Error>
91 where
92 S: crate::transport::poll::Session,
93 {
94 let (publish, subscribe) = self.stat_tagged_origins();
95
96 let our_setup = if version.has_setup_stream() {
100 lite::Setup {
101 probe: lite::ProbeLevel::detect(&session),
102 path: None,
103 role: None,
104 cost: None,
105 hop: None,
107 }
108 } else {
109 lite::Setup::default()
110 };
111
112 let start = lite::start(lite::Config {
113 runtime: runtime.clone(),
114 session: session.clone(),
115 setup_stream: None,
116 publish,
117 subscribe,
118 peer_hop,
119 version,
120 our_setup,
121 peer_setup: client_setup,
122 })?;
123
124 Ok(Session::new(
125 runtime,
126 session,
127 version.into(),
128 start.recv_bandwidth,
129 crate::driver::Protocol::Lite(Box::new(start.driver)),
130 start.goaway,
131 ))
132 }
133
134 pub async fn accept_lite<S>(&self, now: Instant, session: S) -> Result<(Session, crate::Driver<S>), Error>
143 where
144 S: crate::transport::poll::Session,
145 {
146 self.accept_request_lite(now, session).await?.ok().await
147 }
148
149 pub async fn accept_request_lite<S>(&self, now: Instant, mut session: S) -> Result<Handshake<S>, Error>
155 where
156 S: crate::transport::poll::Session,
157 {
158 let runtime = Clock::new(now);
159 let (path, role, origin, handshake) = match session.protocol() {
160 Some(alpn @ (ALPN_LITE_05 | ALPN_LITE_06)) => {
161 let version = match alpn {
162 ALPN_LITE_06 => lite::Version::Lite06,
163 _ => lite::Version::Lite05,
164 };
165 self.versions.select(Version::Lite(version)).ok_or(Error::Version)?;
166 let client_setup = lite::accept_setup(&mut session, version).await?;
171 (
172 client_setup.path.clone(),
173 client_setup.role,
174 client_setup.hop,
175 PausedHandshake::LiteSetup {
176 session,
177 version,
178 client_setup,
179 },
180 )
181 }
182 Some(ALPN_LITE_04) => {
183 self.versions
184 .select(Version::Lite(lite::Version::Lite04))
185 .ok_or(Error::Version)?;
186 (
187 None,
188 None,
189 None,
190 PausedHandshake::LiteBare {
191 session,
192 version: lite::Version::Lite04,
193 },
194 )
195 }
196 Some(ALPN_LITE_03) => {
197 self.versions
198 .select(Version::Lite(lite::Version::Lite03))
199 .ok_or(Error::Version)?;
200 (
201 None,
202 None,
203 None,
204 PausedHandshake::LiteBare {
205 session,
206 version: lite::Version::Lite03,
207 },
208 )
209 }
210 _ => return Err(Error::Version),
211 };
212
213 Ok(Handshake {
214 path,
215 role,
216 origin,
217 assigned_hop: crate::Hop::random(),
218 inner: Some(RequestInner {
219 server: self.clone(),
220 runtime,
221 handshake,
222 }),
223 })
224 }
225
226 pub async fn accept<S>(&self, now: Instant, session: S) -> Result<(Session, crate::Driver<S>), Error>
234 where
235 S: crate::transport::poll::Boxable,
236 S::SendStream: MaybeSync,
237 S::RecvStream: MaybeSync,
238 {
239 self.accept_request(now, session).await?.ok().await
240 }
241
242 pub async fn accept_request<S>(&self, now: Instant, mut session: S) -> Result<Handshake<S>, Error>
253 where
254 S: crate::transport::poll::Boxable,
255 S::SendStream: MaybeSync,
256 S::RecvStream: MaybeSync,
257 {
258 let runtime = Clock::new(now);
259 let (encoding, supported) = match session.protocol() {
260 Some(alpn @ (ALPN_22 | ALPN_21 | ALPN_20 | ALPN_19 | ALPN_18 | ALPN_17)) => {
261 let draft = match alpn {
262 ALPN_22 => ietf::Version::Draft22,
263 ALPN_21 => ietf::Version::Draft21,
264 ALPN_20 => ietf::Version::Draft20,
265 ALPN_19 => ietf::Version::Draft19,
266 ALPN_18 => ietf::Version::Draft18,
267 _ => ietf::Version::Draft17,
268 };
269
270 self.versions.select(Version::Ietf(draft)).ok_or(Error::Version)?;
271 return self.accept_ietf_modern(runtime, session, draft).await;
272 }
273 Some(ALPN_16) => {
274 let v = self
275 .versions
276 .select(Version::Ietf(ietf::Version::Draft16))
277 .ok_or(Error::Version)?;
278 (v, v.into())
279 }
280 Some(ALPN_15) => {
281 let v = self
282 .versions
283 .select(Version::Ietf(ietf::Version::Draft15))
284 .ok_or(Error::Version)?;
285 (v, v.into())
286 }
287 Some(ALPN_14) => {
288 let v = self
289 .versions
290 .select(Version::Ietf(ietf::Version::Draft14))
291 .ok_or(Error::Version)?;
292 (v, v.into())
293 }
294 Some(ALPN_LITE_05 | ALPN_LITE_06 | ALPN_LITE_04 | ALPN_LITE_03) => {
297 return self.accept_request_lite(now, session).await;
298 }
299 Some(ALPN_LITE) | None => {
300 let supported = self.versions.filter(&NEGOTIATED.into()).ok_or(Error::Version)?;
301 (Version::Ietf(ietf::Version::Draft14), supported)
302 }
303 Some(p) => return Err(Error::UnknownAlpn(p.to_string())),
304 };
305
306 let mut stream = Stream::accept(&mut session, encoding).await?;
309 let mut client: setup::Client = stream.reader.decode().await?;
310
311 let version = client
312 .versions
313 .iter()
314 .flat_map(|v| Version::try_from(*v).ok())
315 .find(|v| supported.contains(v))
316 .ok_or(Error::Version)?;
317
318 let (path, request_id_max, peer_declared) = match version {
322 Version::Ietf(v) => {
323 let params = ietf::Parameters::decode(&mut client.parameters, v)?;
324 let path = match params.get_bytes(ietf::ParameterBytes::Path) {
325 Some(bytes) => Some(
326 std::str::from_utf8(bytes)
327 .map_err(|_| Error::Decode(crate::DecodeError::InvalidValue))?
328 .to_owned(),
329 ),
330 None => None,
331 };
332 let request_id_max = params
333 .get_varint(ietf::ParameterVarInt::MaxRequestId)
334 .map(ietf::RequestId);
335 let peer_declared = ietf::peer::Peer {
336 solicit: ietf::solicit::from_setup(¶ms, v)?,
337 ..Default::default()
338 };
339 (path, request_id_max, peer_declared)
340 }
341 Version::Lite(_) => (None, None, ietf::peer::Peer::default()),
342 };
343
344 Ok(Handshake {
345 path,
346 role: None,
347 origin: None,
348 assigned_hop: crate::Hop::random(),
349 inner: Some(RequestInner {
350 server: self.clone(),
351 runtime,
352 handshake: PausedHandshake::Boxed(Box::new(PausedLegacy {
353 session,
354 stream,
355 version,
356 request_id_max,
357 peer_declared,
358 })),
359 }),
360 })
361 }
362
363 async fn accept_ietf_modern<S>(
366 &self,
367 runtime: Clock,
368 mut session: S,
369 version: ietf::Version,
370 ) -> Result<Handshake<S>, Error>
371 where
372 S: crate::transport::poll::Boxable,
373 S::SendStream: MaybeSync,
374 S::RecvStream: MaybeSync,
375 {
376 let peer_setup = ietf::accept_setup(&mut session, version).await?;
377 Ok(Handshake {
378 path: peer_setup.path.clone(),
379 role: None,
380 origin: peer_setup.declared.cluster.hop.filter(|h| *h != crate::Hop::UNKNOWN),
383 assigned_hop: crate::Hop::random(),
384 inner: Some(RequestInner {
385 server: self.clone(),
386 runtime,
387 handshake: PausedHandshake::Boxed(Box::new(PausedIetfModern {
388 session,
389 version,
390 peer_setup,
391 })),
392 }),
393 })
394 }
395}
396
397pub struct Handshake<S: crate::transport::poll::Session> {
405 path: Option<String>,
406 role: Option<Role>,
407 origin: Option<crate::Hop>,
408 assigned_hop: crate::Hop,
412 inner: Option<RequestInner<S>>,
414}
415
416struct RequestInner<S: crate::transport::poll::Session> {
418 server: Server,
419 runtime: Clock,
421 handshake: PausedHandshake<S>,
422}
423
424enum PausedHandshake<S: crate::transport::poll::Session> {
427 LiteBare { session: S, version: lite::Version },
429 LiteSetup {
432 session: S,
433 version: lite::Version,
434 client_setup: lite::Setup,
435 },
436 Boxed(Box<dyn Paused<S>>),
443}
444
445type Accept<S> = crate::util::MaybeSendBox<'static, Result<(Session, crate::Driver<S>), Error>>;
446
447trait Paused<S: crate::transport::poll::Session>: MaybeSend + MaybeSync {
454 fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S>;
456
457 fn close(self: Box<Self>, err: Error);
459}
460
461struct PausedIetfModern<S: crate::transport::poll::Session> {
465 session: S,
466 version: ietf::Version,
467 peer_setup: ietf::PeerSetup<S>,
468}
469
470impl<S> Paused<S> for PausedIetfModern<S>
471where
472 S: crate::transport::poll::Boxable,
473 S::SendStream: MaybeSync,
474 S::RecvStream: MaybeSync,
475{
476 fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S> {
477 use crate::util::MaybeBoxedExt as _;
478 async move {
479 let Self {
480 session,
481 version,
482 peer_setup,
483 } = *self;
484 let (publish, subscribe) = server.stat_tagged_origins();
485
486 let (protocol, goaway) = ietf::start(ietf::Config {
489 runtime: runtime.clone(),
490 session: session.clone(),
491 setup: None,
492 request_id_max: None,
493 client: false,
494 publish,
495 subscribe,
496 peer_hop,
497 cost: None,
499 version,
500 path: None,
501 peer_setup_stream: Some(peer_setup.stream),
502 peer_declared: Some(peer_setup.declared),
503 })?;
504 tracing::debug!(?version, "connected");
505 Ok(Session::new(
506 runtime,
507 session,
508 version.into(),
509 None,
510 crate::driver::Protocol::Ietf(protocol),
511 goaway,
512 ))
513 }
514 .maybe_boxed()
515 }
516
517 fn close(self: Box<Self>, err: Error) {
518 let mut session = self.session;
519 session.close(SessionError::from(&err).to_code(), &err.to_string());
520 }
521}
522
523struct PausedLegacy<S: crate::transport::poll::Session> {
527 session: S,
528 stream: Stream<S, Version>,
529 version: Version,
530 request_id_max: Option<ietf::RequestId>,
531 peer_declared: ietf::peer::Peer,
533}
534
535impl<S> Paused<S> for PausedLegacy<S>
536where
537 S: crate::transport::poll::Boxable,
538 S::SendStream: MaybeSync,
539 S::RecvStream: MaybeSync,
540{
541 fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S> {
542 use crate::util::MaybeBoxedExt as _;
543 async move {
544 let Self {
545 session,
546 mut stream,
547 version,
548 request_id_max,
549 peer_declared,
550 } = *self;
551 let (publish, subscribe) = server.stat_tagged_origins();
552
553 let parameters = match version {
555 Version::Ietf(v) => {
556 let mut parameters = ietf::Parameters::default();
557 parameters.set_varint(ietf::ParameterVarInt::MaxRequestId, u32::MAX as u64);
558 parameters.set_bytes(ietf::ParameterBytes::Implementation, b"moq-lite-rs".to_vec());
559 ietf::solicit::into_setup(&mut parameters, v);
560 parameters.encode_bytes(v)?
561 }
562 Version::Lite(v) => lite::Parameters::default().encode_bytes(v)?,
563 };
564
565 let server_setup = setup::Server {
566 version: version.into(),
567 parameters,
568 };
569 stream.writer.encode(&server_setup).await?;
570
571 let (recv_bw, protocol, goaway) = match version {
572 Version::Lite(v) => {
573 let stream = stream.with_version(v);
574 let start = lite::start(lite::Config {
576 runtime: runtime.clone(),
577 session: session.clone(),
578 setup_stream: Some(stream),
579 publish,
580 subscribe,
581 peer_hop,
582 version: v,
583 our_setup: lite::Setup::default(),
584 peer_setup: None,
585 })?;
586 (
587 start.recv_bandwidth,
588 crate::driver::Protocol::Lite(Box::new(start.driver)),
589 start.goaway,
590 )
591 }
592 Version::Ietf(v) => {
593 let stream = stream.with_version(v);
594 let (protocol, goaway) = ietf::start(ietf::Config {
596 runtime: runtime.clone(),
597 session: session.clone(),
598 setup: Some(stream),
599 request_id_max,
600 client: false,
601 publish,
602 subscribe,
603 peer_hop,
604 cost: None,
605 version: v,
606 path: None,
607 peer_setup_stream: None,
608 peer_declared: Some(peer_declared),
609 })?;
610 (None, crate::driver::Protocol::Ietf(protocol), goaway)
611 }
612 };
613
614 Ok(Session::new(runtime, session, version, recv_bw, protocol, goaway))
615 }
616 .maybe_boxed()
617 }
618
619 fn close(self: Box<Self>, err: Error) {
620 let mut session = self.session;
621 session.close(SessionError::from(&err).to_code(), &err.to_string());
622 }
623}
624
625impl<S> Handshake<S>
626where
627 S: crate::transport::poll::Session,
628{
629 pub fn path(&self) -> &str {
636 self.path.as_deref().unwrap_or("")
637 }
638
639 pub fn role(&self) -> Option<Role> {
648 self.role
649 }
650
651 pub fn peer_hop(&self) -> Option<crate::Hop> {
660 self.origin
661 }
662
663 pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
666 self.inner_mut().server.publish = Some(publish.consume());
667 self
668 }
669
670 pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
672 self.inner_mut().server.subscribe = Some(subscribe);
673 self
674 }
675
676 pub fn with_peer_hop(mut self, hop: crate::Hop) -> Self {
689 self.assigned_hop = hop;
690 self
691 }
692
693 pub fn with_stats(mut self, stats: stats::Session) -> Self {
696 self.inner_mut().server.stats = stats;
697 self
698 }
699
700 fn inner_mut(&mut self) -> &mut RequestInner<S> {
701 self.inner.as_mut().expect("request already responded")
702 }
703
704 pub async fn ok(mut self) -> Result<(Session, crate::Driver<S>), Error> {
708 let peer_hop = Some(self.assigned_hop);
709 let RequestInner {
710 server,
711 runtime,
712 handshake,
713 } = self.inner.take().expect("request already responded");
714
715 match handshake {
716 PausedHandshake::LiteBare { session, version } => {
717 server.start_lite(runtime, session, version, None, peer_hop)
718 }
719 PausedHandshake::LiteSetup {
720 session,
721 version,
722 client_setup,
723 } => server.start_lite(runtime, session, version, Some(client_setup), peer_hop),
724 PausedHandshake::Boxed(paused) => paused.ok(server, runtime, peer_hop).await,
725 }
726 }
727
728 pub fn close(mut self, err: Error) {
730 let inner = self.inner.take().expect("request already responded");
731 inner.close(err);
732 }
733}
734
735impl<S: crate::transport::poll::Session> RequestInner<S> {
736 fn close(self, err: Error) {
737 let mut session = match self.handshake {
738 PausedHandshake::LiteBare { session, .. } => session,
739 PausedHandshake::LiteSetup { session, .. } => session,
740 PausedHandshake::Boxed(paused) => return paused.close(err),
741 };
742 session.close(SessionError::from(&err).to_code(), &err.to_string());
743 }
744}
745
746impl<S: crate::transport::poll::Session> Drop for Handshake<S> {
747 fn drop(&mut self) {
750 if let Some(inner) = self.inner.take() {
751 tracing::warn!("Handshake dropped without ok() or close(); rejecting the session");
752 inner.close(Error::Cancel);
753 }
754 }
755}
756
757#[cfg(test)]
758mod tests {
759 use super::*;
760 use crate::Hop;
761 use crate::model::ProduceTest;
762 use std::{
763 collections::VecDeque,
764 sync::{Arc, Mutex},
765 };
766
767 use crate::ALPN_LITE_05;
768 use bytes::Bytes;
769
770 fn occurrences(log: &crate::lite::test_transport::Log, needle: &[u8]) -> usize {
771 let writes = log.writes.lock().unwrap();
772 writes.windows(needle.len()).filter(|window| *window == needle).count()
773 }
774
775 #[derive(Debug, Clone, Default)]
776 struct FakeError;
777 impl std::fmt::Display for FakeError {
778 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
779 write!(f, "fake transport error")
780 }
781 }
782 impl std::error::Error for FakeError {}
783 impl web_transport_trait::Error for FakeError {
784 fn session_error(&self) -> Option<(u32, String)> {
785 Some((0, "closed".to_string()))
786 }
787 }
788
789 #[derive(Clone)]
792 struct FakeSession {
793 protocol: Option<&'static str>,
794 uni: Arc<Mutex<VecDeque<Vec<u8>>>>,
795 }
796
797 impl FakeSession {
798 fn new(protocol: &'static str, uni: impl IntoIterator<Item = Vec<u8>>) -> Self {
799 Self {
800 protocol: Some(protocol),
801 uni: Arc::new(Mutex::new(uni.into_iter().collect())),
802 }
803 }
804 }
805
806 impl web_transport_trait::poll::Session for FakeSession {
807 type SendStream = FakeSend;
808 type RecvStream = FakeRecv;
809 type Error = FakeError;
810
811 fn poll_accept_uni(
812 &mut self,
813 _cx: &mut std::task::Context<'_>,
814 ) -> std::task::Poll<Result<Self::RecvStream, Self::Error>> {
815 match self.uni.lock().unwrap().pop_front() {
816 Some(data) => std::task::Poll::Ready(Ok(FakeRecv { data: data.into() })),
817 None => std::task::Poll::Pending,
818 }
819 }
820 fn poll_accept_bi(
821 &mut self,
822 _cx: &mut std::task::Context<'_>,
823 ) -> std::task::Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
824 std::task::Poll::Pending
825 }
826 fn poll_open_bi(
827 &mut self,
828 _cx: &mut std::task::Context<'_>,
829 ) -> std::task::Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
830 std::task::Poll::Pending
831 }
832 fn poll_open_uni(
833 &mut self,
834 _cx: &mut std::task::Context<'_>,
835 ) -> std::task::Poll<Result<Self::SendStream, Self::Error>> {
836 std::task::Poll::Pending
837 }
838 fn poll_send_datagram(
839 &mut self,
840 _cx: &mut std::task::Context<'_>,
841 _payload: &[u8],
842 ) -> std::task::Poll<Result<(), Self::Error>> {
843 std::task::Poll::Ready(Ok(()))
844 }
845 fn poll_recv_datagram(
846 &mut self,
847 _cx: &mut std::task::Context<'_>,
848 ) -> std::task::Poll<Result<Bytes, Self::Error>> {
849 std::task::Poll::Pending
850 }
851 fn max_datagram_size(&self) -> usize {
852 1200
853 }
854 fn protocol(&self) -> Option<&str> {
855 self.protocol
856 }
857 fn close(&mut self, _code: u32, _reason: &str) {}
858 fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Error> {
859 std::task::Poll::Pending
860 }
861 fn stats(&self) -> impl web_transport_trait::Stats {
862 web_transport_trait::StatsUnavailable
863 }
864 }
865
866 #[derive(Clone, Default)]
867 struct FakeSend;
868 impl web_transport_trait::poll::SendStream for FakeSend {
869 type Error = FakeError;
870 fn poll_write(
871 &mut self,
872 _cx: &mut std::task::Context<'_>,
873 buf: &[u8],
874 ) -> std::task::Poll<Result<usize, Self::Error>> {
875 std::task::Poll::Ready(Ok(buf.len()))
876 }
877 fn set_priority(&mut self, _order: u8) {}
878 fn finish(&mut self) -> Result<(), Self::Error> {
879 Ok(())
880 }
881 fn reset(&mut self, _code: u32) {}
882 fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
883 std::task::Poll::Ready(Ok(()))
884 }
885 }
886
887 struct FakeRecv {
888 data: VecDeque<u8>,
889 }
890 impl web_transport_trait::poll::RecvStream for FakeRecv {
891 type Error = FakeError;
892 fn poll_read(
893 &mut self,
894 _cx: &mut std::task::Context<'_>,
895 dst: &mut [u8],
896 ) -> std::task::Poll<Result<Option<usize>, Self::Error>> {
897 if self.data.is_empty() {
898 return std::task::Poll::Ready(Ok(None));
899 }
900 let size = dst.len().min(self.data.len());
901 for slot in dst.iter_mut().take(size) {
902 *slot = self.data.pop_front().unwrap();
903 }
904 std::task::Poll::Ready(Ok(Some(size)))
905 }
906 fn stop(&mut self, _code: u32) {}
907 fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
908 std::task::Poll::Ready(Ok(()))
909 }
910 }
911
912 fn lite05_setup(path: Option<&str>, role: Option<Role>, hop: Option<Hop>) -> Vec<u8> {
914 let v = lite::Version::Lite05;
915 let mut buf = Vec::new();
916 lite::DataType::Setup.encode(&mut buf, v).unwrap();
917 lite::Setup {
918 probe: lite::ProbeLevel::None,
919 path: path.map(str::to_string),
920 role,
921 cost: None,
922 hop,
923 }
924 .encode(&mut buf, v)
925 .unwrap();
926 buf
927 }
928
929 fn ietf_setup(version: ietf::Version, path: Option<&str>) -> Vec<u8> {
932 let mut params = ietf::Parameters::default();
933 if let Some(path) = path {
934 params.set_bytes(ietf::ParameterBytes::Path, path.as_bytes().to_vec());
935 }
936 let parameters = params.encode_bytes(version).unwrap();
937
938 let mut buf = Vec::new();
939 setup::Setup { parameters }
940 .encode(&mut buf, crate::Version::Ietf(version))
941 .unwrap();
942 buf
943 }
944
945 #[tokio::test(start_paused = true)]
946 async fn accept_request_reads_ietf_path() {
947 for (alpn, version) in [
950 (ALPN_17, ietf::Version::Draft17),
951 (ALPN_18, ietf::Version::Draft18),
952 (ALPN_19, ietf::Version::Draft19),
953 ] {
954 let session = FakeSession::new(alpn, [ietf_setup(version, Some("/team/room"))]);
955 let request = Server::new()
956 .accept_request(tokio::time::Instant::now().into_std(), session)
957 .await
958 .unwrap();
959 assert_eq!(request.path(), "/team/room", "{alpn}");
960 }
961 }
962
963 #[tokio::test(start_paused = true)]
964 async fn accept_request_ietf_without_path_is_empty() {
965 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, None)]);
966 let request = Server::new()
967 .accept_request(tokio::time::Instant::now().into_std(), session)
968 .await
969 .unwrap();
970 assert_eq!(request.path(), "");
971 }
972
973 #[tokio::test(start_paused = true)]
974 async fn accept_request_ietf_empty_path_is_accepted() {
975 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, Some(""))]);
976 let request = Server::new()
977 .accept_request(tokio::time::Instant::now().into_std(), session)
978 .await
979 .unwrap();
980 assert_eq!(request.path(), "");
981 }
982
983 fn lite05_group() -> Vec<u8> {
985 let mut buf = Vec::new();
986 lite::DataType::Group.encode(&mut buf, lite::Version::Lite05).unwrap();
987 buf
988 }
989
990 #[tokio::test(start_paused = true)]
991 async fn accept_request_reads_lite05_path() {
992 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some("/team/room"), None, None)]);
993 let request = Server::new()
994 .accept_request(tokio::time::Instant::now().into_std(), session)
995 .await
996 .unwrap();
997 assert_eq!(request.path(), "/team/room");
998 assert_eq!(request.role(), None, "a client that omits the role is bidirectional");
999 }
1000
1001 #[tokio::test(start_paused = true)]
1002 async fn accept_request_lite05_without_path_is_empty() {
1003 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, None)]);
1004 let request = Server::new()
1005 .accept_request(tokio::time::Instant::now().into_std(), session)
1006 .await
1007 .unwrap();
1008 assert_eq!(request.path(), "");
1009 }
1010
1011 #[tokio::test(start_paused = true)]
1012 async fn accept_request_lite05_empty_path_is_accepted() {
1013 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some(""), None, None)]);
1016 let request = Server::new()
1017 .accept_request(tokio::time::Instant::now().into_std(), session)
1018 .await
1019 .unwrap();
1020 assert_eq!(request.path(), "");
1021 }
1022
1023 #[tokio::test(start_paused = true)]
1024 async fn accept_request_reads_lite05_role() {
1025 let session = FakeSession::new(
1026 ALPN_LITE_05,
1027 [lite05_setup(Some("/team/room"), Some(Role::Publisher), None)],
1028 );
1029 let request = Server::new()
1030 .accept_request(tokio::time::Instant::now().into_std(), session)
1031 .await
1032 .unwrap();
1033 assert_eq!(request.role(), Some(Role::Publisher));
1034 }
1035
1036 #[tokio::test(start_paused = true)]
1037 async fn accept_request_skips_uni_stream_before_setup() {
1038 let session = FakeSession::new(
1041 ALPN_LITE_05,
1042 [lite05_group(), lite05_setup(Some("/team/room"), None, None)],
1043 );
1044 let request = Server::new()
1045 .accept_request(tokio::time::Instant::now().into_std(), session)
1046 .await
1047 .unwrap();
1048 assert_eq!(request.path(), "/team/room");
1049 }
1050
1051 #[tokio::test(start_paused = true)]
1052 async fn accept_request_reads_lite05_peer_hop() {
1053 let hop = Hop::new(42).unwrap();
1054 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, Some(hop))]);
1055 let request = Server::new()
1056 .accept_request(tokio::time::Instant::now().into_std(), session)
1057 .await
1058 .unwrap();
1059 assert_eq!(request.peer_hop(), Some(hop));
1060 }
1061
1062 #[tokio::test(start_paused = true)]
1063 async fn anonymous_peer_hop_filters_routes_from_server_session() {
1064 let other = Hop::new(778).unwrap();
1065 let origin = crate::origin::Config::new(Hop::new(1).unwrap()).produce();
1066
1067 let gate = kio::Producer::new(true);
1068 let transport = crate::lite::test_transport::SinkSession::gated_bi(gate.consume());
1069 let log = transport.log.clone();
1070 let version = ietf::Version::Draft18;
1071 let request = Handshake {
1072 path: None,
1073 role: None,
1074 origin: None,
1075 assigned_hop: Hop::random(),
1076 inner: Some(RequestInner {
1077 server: Server::new().with_publisher(&origin),
1078 runtime: Clock::new(tokio::time::Instant::now().into_std()),
1079 handshake: PausedHandshake::Boxed(Box::new(PausedIetfModern {
1080 session: transport,
1081 version,
1082 peer_setup: ietf::PeerSetup {
1083 stream: crate::coding::Reader::new(
1084 crate::lite::test_transport::PendingRecv,
1085 Version::Ietf(version),
1086 ),
1087 path: None,
1088 declared: ietf::peer::Peer::default(),
1089 },
1090 })),
1091 }),
1092 };
1093 let assigned = request.assigned_hop;
1094
1095 let mut echoed_hops = crate::Hops::new();
1096 echoed_hops.push(crate::Hop::UNKNOWN).unwrap();
1097 let _echoed = origin
1098 .announce(
1099 "echoed-route",
1100 crate::origin::Route::default()
1101 .with_hops(echoed_hops)
1102 .with_via(assigned),
1103 )
1104 .unwrap();
1105
1106 let mut local_hops = crate::Hops::new();
1107 local_hops.push(other).unwrap();
1108 let _local = origin
1109 .announce("local-route", crate::origin::Route::default().with_hops(local_hops))
1110 .unwrap();
1111
1112 let (session, driver) = request.ok().await.unwrap();
1113 tokio::spawn(crate::time::run(driver));
1114
1115 for _ in 0..100 {
1116 if occurrences(&log, b"local-route") > 0 {
1117 break;
1118 }
1119 tokio::time::sleep(std::time::Duration::from_millis(1)).await;
1120 }
1121
1122 assert_eq!(occurrences(&log, b"echoed-route"), 0);
1123 assert_eq!(occurrences(&log, b"local-route"), 1);
1124 drop(session);
1125 }
1126}