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, ALPN_LITE_07_WIP, Consume, Error, NEGOTIATED, Role, Session,
11 SessionError, Version, 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, session: S) -> Result<Handshake<S>, Error>
155 where
156 S: crate::transport::poll::Session,
157 {
158 let mut refused = session.clone();
159 self.handshake_lite(now, session)
160 .await
161 .inspect_err(|err| close(&mut refused, err))
162 }
163
164 async fn handshake_lite<S>(&self, now: Instant, mut session: S) -> Result<Handshake<S>, Error>
165 where
166 S: crate::transport::poll::Session,
167 {
168 let runtime = Clock::new(now);
169 let (path, role, origin, handshake) = match session.protocol() {
170 Some(alpn @ (ALPN_LITE_05 | ALPN_LITE_06 | ALPN_LITE_07_WIP)) => {
171 let version = match alpn {
172 ALPN_LITE_07_WIP => lite::Version::Lite07,
173 ALPN_LITE_06 => lite::Version::Lite06,
174 _ => lite::Version::Lite05,
175 };
176 self.versions.select(Version::Lite(version)).ok_or(Error::Version)?;
177 let client_setup = lite::accept_setup(&mut session, version).await?;
182 (
183 client_setup.path.clone(),
184 client_setup.role,
185 client_setup.hop,
186 PausedHandshake::LiteSetup {
187 session,
188 version,
189 client_setup,
190 },
191 )
192 }
193 Some(ALPN_LITE_04) => {
194 self.versions
195 .select(Version::Lite(lite::Version::Lite04))
196 .ok_or(Error::Version)?;
197 (
198 None,
199 None,
200 None,
201 PausedHandshake::LiteBare {
202 session,
203 version: lite::Version::Lite04,
204 },
205 )
206 }
207 Some(ALPN_LITE_03) => {
208 self.versions
209 .select(Version::Lite(lite::Version::Lite03))
210 .ok_or(Error::Version)?;
211 (
212 None,
213 None,
214 None,
215 PausedHandshake::LiteBare {
216 session,
217 version: lite::Version::Lite03,
218 },
219 )
220 }
221 _ => return Err(Error::Version),
222 };
223
224 Ok(Handshake {
225 path,
226 role,
227 origin,
228 token: None,
230 assigned_hop: crate::Hop::random(),
231 inner: Some(RequestInner {
232 server: self.clone(),
233 runtime,
234 handshake,
235 }),
236 })
237 }
238
239 pub async fn accept<S>(&self, now: Instant, session: S) -> Result<(Session, crate::Driver<S>), Error>
247 where
248 S: crate::transport::poll::Boxable,
249 S::SendStream: MaybeSync,
250 S::RecvStream: MaybeSync,
251 {
252 self.accept_request(now, session).await?.ok().await
253 }
254
255 pub async fn accept_request<S>(&self, now: Instant, session: S) -> Result<Handshake<S>, Error>
269 where
270 S: crate::transport::poll::Boxable,
271 S::SendStream: MaybeSync,
272 S::RecvStream: MaybeSync,
273 {
274 let mut refused = session.clone();
275 self.handshake(now, session)
276 .await
277 .inspect_err(|err| close(&mut refused, err))
278 }
279
280 async fn handshake<S>(&self, now: Instant, mut session: S) -> Result<Handshake<S>, Error>
281 where
282 S: crate::transport::poll::Boxable,
283 S::SendStream: MaybeSync,
284 S::RecvStream: MaybeSync,
285 {
286 let runtime = Clock::new(now);
287 let (encoding, supported) = match session.protocol() {
288 Some(alpn @ (ALPN_22 | ALPN_21 | ALPN_20 | ALPN_19 | ALPN_18 | ALPN_17)) => {
289 let draft = match alpn {
290 ALPN_22 => ietf::Version::Draft22,
291 ALPN_21 => ietf::Version::Draft21,
292 ALPN_20 => ietf::Version::Draft20,
293 ALPN_19 => ietf::Version::Draft19,
294 ALPN_18 => ietf::Version::Draft18,
295 _ => ietf::Version::Draft17,
296 };
297
298 self.versions.select(Version::Ietf(draft)).ok_or(Error::Version)?;
299 return self.accept_ietf_modern(runtime, session, draft).await;
300 }
301 Some(ALPN_16) => {
302 let v = self
303 .versions
304 .select(Version::Ietf(ietf::Version::Draft16))
305 .ok_or(Error::Version)?;
306 (v, v.into())
307 }
308 Some(ALPN_15) => {
309 let v = self
310 .versions
311 .select(Version::Ietf(ietf::Version::Draft15))
312 .ok_or(Error::Version)?;
313 (v, v.into())
314 }
315 Some(ALPN_14) => {
316 let v = self
317 .versions
318 .select(Version::Ietf(ietf::Version::Draft14))
319 .ok_or(Error::Version)?;
320 (v, v.into())
321 }
322 Some(ALPN_LITE_07_WIP | ALPN_LITE_06 | ALPN_LITE_05 | ALPN_LITE_04 | ALPN_LITE_03) => {
325 return self.handshake_lite(now, session).await;
326 }
327 Some(ALPN_LITE) | None => {
328 let supported = self.versions.filter(&NEGOTIATED.into()).ok_or(Error::Version)?;
329 (Version::Ietf(ietf::Version::Draft14), supported)
330 }
331 Some(p) => return Err(Error::UnknownAlpn(p.to_string())),
332 };
333
334 let mut stream = Stream::accept(&mut session, encoding).await?;
337 let mut client: setup::Client = stream.reader.decode().await?;
338
339 let version = client
340 .versions
341 .iter()
342 .flat_map(|v| Version::try_from(*v).ok())
343 .find(|v| supported.contains(v))
344 .ok_or(Error::Version)?;
345
346 let (path, token, request_id_max, peer_declared) = match version {
350 Version::Ietf(v) => {
351 let params = ietf::Parameters::decode(&mut client.parameters, v)?;
352 let path = match params.get_bytes(ietf::ParameterBytes::Path) {
353 Some(bytes) => Some(
354 std::str::from_utf8(bytes)
355 .map_err(|_| Error::Decode(crate::DecodeError::InvalidValue))?
356 .to_owned(),
357 ),
358 None => None,
359 };
360 let token = ietf::token::from_setup(¶ms, v)?;
361 let request_id_max = params
362 .get_varint(ietf::ParameterVarInt::MaxRequestId)
363 .map(ietf::RequestId);
364 let peer_declared = ietf::peer::Peer {
365 solicit: ietf::solicit::from_setup(¶ms, v)?,
366 hidden: ietf::hidden::from_setup(¶ms, v),
367 ..Default::default()
368 };
369 (path, token, request_id_max, peer_declared)
370 }
371 Version::Lite(_) => (None, None, None, ietf::peer::Peer::default()),
372 };
373
374 Ok(Handshake {
375 path,
376 role: None,
377 origin: None,
378 token,
379 assigned_hop: crate::Hop::random(),
380 inner: Some(RequestInner {
381 server: self.clone(),
382 runtime,
383 handshake: PausedHandshake::Boxed(Box::new(PausedLegacy {
384 session,
385 stream,
386 version,
387 request_id_max,
388 peer_declared,
389 })),
390 }),
391 })
392 }
393
394 async fn accept_ietf_modern<S>(
397 &self,
398 runtime: Clock,
399 mut session: S,
400 version: ietf::Version,
401 ) -> Result<Handshake<S>, Error>
402 where
403 S: crate::transport::poll::Boxable,
404 S::SendStream: MaybeSync,
405 S::RecvStream: MaybeSync,
406 {
407 let peer_setup = ietf::accept_setup(&mut session, version).await?;
408 Ok(Handshake {
409 path: peer_setup.path.clone(),
410 role: None,
411 origin: peer_setup.declared.cluster.hop.filter(|h| *h != crate::Hop::UNKNOWN),
414 token: peer_setup.token.clone(),
415 assigned_hop: crate::Hop::random(),
416 inner: Some(RequestInner {
417 server: self.clone(),
418 runtime,
419 handshake: PausedHandshake::Boxed(Box::new(PausedIetfModern {
420 session,
421 version,
422 peer_setup,
423 })),
424 }),
425 })
426 }
427}
428
429pub struct Handshake<S: crate::transport::poll::Session> {
437 path: Option<String>,
438 role: Option<Role>,
439 origin: Option<crate::Hop>,
440 token: Option<setup::Token>,
441 assigned_hop: crate::Hop,
445 inner: Option<RequestInner<S>>,
447}
448
449struct RequestInner<S: crate::transport::poll::Session> {
451 server: Server,
452 runtime: Clock,
454 handshake: PausedHandshake<S>,
455}
456
457enum PausedHandshake<S: crate::transport::poll::Session> {
460 LiteBare { session: S, version: lite::Version },
462 LiteSetup {
465 session: S,
466 version: lite::Version,
467 client_setup: lite::Setup,
468 },
469 Boxed(Box<dyn Paused<S>>),
476}
477
478type Accept<S> = crate::util::MaybeSendBox<'static, Result<(Session, crate::Driver<S>), Error>>;
479
480trait Paused<S: crate::transport::poll::Session>: MaybeSend + MaybeSync {
487 fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S>;
489
490 fn close(self: Box<Self>, err: Error);
492}
493
494struct PausedIetfModern<S: crate::transport::poll::Session> {
498 session: S,
499 version: ietf::Version,
500 peer_setup: ietf::PeerSetup<S>,
501}
502
503impl<S> Paused<S> for PausedIetfModern<S>
504where
505 S: crate::transport::poll::Boxable,
506 S::SendStream: MaybeSync,
507 S::RecvStream: MaybeSync,
508{
509 fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S> {
510 use crate::util::MaybeBoxedExt as _;
511 async move {
512 let Self {
513 session,
514 version,
515 peer_setup,
516 } = *self;
517 let (publish, subscribe) = server.stat_tagged_origins();
518
519 let (protocol, goaway) = ietf::start(ietf::Config {
522 runtime: runtime.clone(),
523 session: session.clone(),
524 setup: None,
525 request_id_max: None,
526 client: false,
527 publish,
528 subscribe,
529 peer_hop,
530 cost: None,
532 version,
533 path: None,
534 peer_setup_stream: Some(peer_setup.stream),
535 peer_declared: Some(peer_setup.declared),
536 })?;
537 tracing::debug!(?version, "connected");
538 Ok(Session::new(
539 runtime,
540 session,
541 version.into(),
542 None,
543 crate::driver::Protocol::Ietf(protocol),
544 goaway,
545 ))
546 }
547 .maybe_boxed()
548 }
549
550 fn close(mut self: Box<Self>, err: Error) {
551 close(&mut self.session, &err);
552 }
553}
554
555struct PausedLegacy<S: crate::transport::poll::Session> {
559 session: S,
560 stream: Stream<S, Version>,
561 version: Version,
562 request_id_max: Option<ietf::RequestId>,
563 peer_declared: ietf::peer::Peer,
565}
566
567impl<S> Paused<S> for PausedLegacy<S>
568where
569 S: crate::transport::poll::Boxable,
570 S::SendStream: MaybeSync,
571 S::RecvStream: MaybeSync,
572{
573 fn ok(self: Box<Self>, server: Server, runtime: Clock, peer_hop: Option<crate::Hop>) -> Accept<S> {
574 use crate::util::MaybeBoxedExt as _;
575 async move {
576 let Self {
577 session,
578 mut stream,
579 version,
580 request_id_max,
581 peer_declared,
582 } = *self;
583 let (publish, subscribe) = server.stat_tagged_origins();
584
585 let parameters = match version {
587 Version::Ietf(v) => {
588 let mut parameters = ietf::Parameters::default();
589 parameters.set_varint(ietf::ParameterVarInt::MaxRequestId, u32::MAX as u64);
590 parameters.set_bytes(ietf::ParameterBytes::Implementation, b"moq-lite-rs".to_vec());
591 ietf::solicit::into_setup(&mut parameters, v);
592 ietf::hidden::into_setup(&mut parameters, v);
593 parameters.encode_bytes(v)?
594 }
595 Version::Lite(v) => lite::Parameters::default().encode_bytes(v)?,
596 };
597
598 let server_setup = setup::Server {
599 version: version.into(),
600 parameters,
601 };
602 stream.writer.encode(&server_setup).await?;
603
604 let (recv_bw, protocol, goaway) = match version {
605 Version::Lite(v) => {
606 let stream = stream.with_version(v);
607 let start = lite::start(lite::Config {
609 runtime: runtime.clone(),
610 session: session.clone(),
611 setup_stream: Some(stream),
612 publish,
613 subscribe,
614 peer_hop,
615 version: v,
616 our_setup: lite::Setup::default(),
617 peer_setup: None,
618 })?;
619 (
620 start.recv_bandwidth,
621 crate::driver::Protocol::Lite(Box::new(start.driver)),
622 start.goaway,
623 )
624 }
625 Version::Ietf(v) => {
626 let stream = stream.with_version(v);
627 let (protocol, goaway) = ietf::start(ietf::Config {
629 runtime: runtime.clone(),
630 session: session.clone(),
631 setup: Some(stream),
632 request_id_max,
633 client: false,
634 publish,
635 subscribe,
636 peer_hop,
637 cost: None,
638 version: v,
639 path: None,
640 peer_setup_stream: None,
641 peer_declared: Some(peer_declared),
642 })?;
643 (None, crate::driver::Protocol::Ietf(protocol), goaway)
644 }
645 };
646
647 Ok(Session::new(runtime, session, version, recv_bw, protocol, goaway))
648 }
649 .maybe_boxed()
650 }
651
652 fn close(mut self: Box<Self>, err: Error) {
653 close(&mut self.session, &err);
654 }
655}
656
657impl<S> Handshake<S>
658where
659 S: crate::transport::poll::Session,
660{
661 pub fn path(&self) -> &str {
668 self.path.as_deref().unwrap_or("")
669 }
670
671 pub fn role(&self) -> Option<Role> {
680 self.role
681 }
682
683 pub fn peer_hop(&self) -> Option<crate::Hop> {
692 self.origin
693 }
694
695 pub fn token(&self) -> Option<&setup::Token> {
700 self.token.as_ref()
701 }
702
703 pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
706 self.inner_mut().server.publish = Some(publish.consume());
707 self
708 }
709
710 pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
712 self.inner_mut().server.subscribe = Some(subscribe);
713 self
714 }
715
716 pub fn with_peer_hop(mut self, hop: crate::Hop) -> Self {
729 self.assigned_hop = hop;
730 self
731 }
732
733 pub fn with_stats(mut self, stats: stats::Session) -> Self {
736 self.inner_mut().server.stats = stats;
737 self
738 }
739
740 fn inner_mut(&mut self) -> &mut RequestInner<S> {
741 self.inner.as_mut().expect("request already responded")
742 }
743
744 pub async fn ok(mut self) -> Result<(Session, crate::Driver<S>), Error> {
748 let peer_hop = Some(self.assigned_hop);
749 let RequestInner {
750 server,
751 runtime,
752 handshake,
753 } = self.inner.take().expect("request already responded");
754
755 match handshake {
756 PausedHandshake::LiteBare { session, version } => {
757 server.start_lite(runtime, session, version, None, peer_hop)
758 }
759 PausedHandshake::LiteSetup {
760 session,
761 version,
762 client_setup,
763 } => server.start_lite(runtime, session, version, Some(client_setup), peer_hop),
764 PausedHandshake::Boxed(paused) => paused.ok(server, runtime, peer_hop).await,
765 }
766 }
767
768 pub fn close(mut self, err: Error) {
770 let inner = self.inner.take().expect("request already responded");
771 inner.close(err);
772 }
773}
774
775impl<S: crate::transport::poll::Session> RequestInner<S> {
776 fn close(self, err: Error) {
777 let mut session = match self.handshake {
778 PausedHandshake::LiteBare { session, .. } => session,
779 PausedHandshake::LiteSetup { session, .. } => session,
780 PausedHandshake::Boxed(paused) => return paused.close(err),
781 };
782 close(&mut session, &err);
783 }
784}
785
786fn close<S: crate::transport::poll::Session>(session: &mut S, err: &Error) {
788 session.close(SessionError::from(err).to_code(), &err.to_string());
789}
790
791impl<S: crate::transport::poll::Session> Drop for Handshake<S> {
792 fn drop(&mut self) {
795 if let Some(inner) = self.inner.take() {
796 tracing::warn!("Handshake dropped without ok() or close(); rejecting the session");
797 inner.close(Error::Cancel);
798 }
799 }
800}
801
802#[cfg(test)]
803mod tests {
804 use super::*;
805 use crate::Hop;
806 use crate::model::ProduceTest;
807 use std::{
808 collections::VecDeque,
809 sync::{Arc, Mutex},
810 };
811
812 use crate::ALPN_LITE_05;
813 use bytes::Bytes;
814
815 fn occurrences(log: &crate::lite::test_transport::Log, needle: &[u8]) -> usize {
816 let writes = log.writes.lock().unwrap();
817 writes.windows(needle.len()).filter(|window| *window == needle).count()
818 }
819
820 #[derive(Debug, Clone, Default)]
821 struct FakeError;
822 impl std::fmt::Display for FakeError {
823 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
824 write!(f, "fake transport error")
825 }
826 }
827 impl std::error::Error for FakeError {}
828 impl web_transport_trait::Error for FakeError {
829 fn session_error(&self) -> Option<(u32, String)> {
830 Some((0, "closed".to_string()))
831 }
832 }
833
834 #[derive(Clone)]
838 struct FakeSession {
839 protocol: Option<&'static str>,
840 uni: Arc<Mutex<VecDeque<Vec<u8>>>>,
841 bi: Arc<Mutex<VecDeque<Vec<u8>>>>,
842 closed: Arc<Mutex<Option<u32>>>,
843 }
844
845 impl FakeSession {
846 fn new(protocol: &'static str, uni: impl IntoIterator<Item = Vec<u8>>) -> Self {
847 Self {
848 protocol: Some(protocol),
849 uni: Arc::new(Mutex::new(uni.into_iter().collect())),
850 bi: Default::default(),
851 closed: Default::default(),
852 }
853 }
854
855 fn with_bi(self, bi: Vec<u8>) -> Self {
856 self.bi.lock().unwrap().push_back(bi);
857 self
858 }
859
860 fn closed(&self) -> Option<u32> {
861 *self.closed.lock().unwrap()
862 }
863 }
864
865 impl web_transport_trait::poll::Session for FakeSession {
866 type SendStream = FakeSend;
867 type RecvStream = FakeRecv;
868 type Error = FakeError;
869
870 fn poll_accept_uni(
871 &mut self,
872 _cx: &mut std::task::Context<'_>,
873 ) -> std::task::Poll<Result<Self::RecvStream, Self::Error>> {
874 match self.uni.lock().unwrap().pop_front() {
875 Some(data) => std::task::Poll::Ready(Ok(FakeRecv { data: data.into() })),
876 None => std::task::Poll::Pending,
877 }
878 }
879 fn poll_accept_bi(
880 &mut self,
881 _cx: &mut std::task::Context<'_>,
882 ) -> std::task::Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
883 match self.bi.lock().unwrap().pop_front() {
884 Some(data) => std::task::Poll::Ready(Ok((FakeSend, FakeRecv { data: data.into() }))),
885 None => std::task::Poll::Pending,
886 }
887 }
888 fn poll_open_bi(
889 &mut self,
890 _cx: &mut std::task::Context<'_>,
891 ) -> std::task::Poll<Result<(Self::SendStream, Self::RecvStream), Self::Error>> {
892 std::task::Poll::Pending
893 }
894 fn poll_open_uni(
895 &mut self,
896 _cx: &mut std::task::Context<'_>,
897 ) -> std::task::Poll<Result<Self::SendStream, Self::Error>> {
898 std::task::Poll::Pending
899 }
900 fn poll_send_datagram(
901 &mut self,
902 _cx: &mut std::task::Context<'_>,
903 _payload: &[u8],
904 ) -> std::task::Poll<Result<(), Self::Error>> {
905 std::task::Poll::Ready(Ok(()))
906 }
907 fn poll_recv_datagram(
908 &mut self,
909 _cx: &mut std::task::Context<'_>,
910 ) -> std::task::Poll<Result<Bytes, Self::Error>> {
911 std::task::Poll::Pending
912 }
913 fn max_datagram_size(&self) -> usize {
914 1200
915 }
916 fn protocol(&self) -> Option<&str> {
917 self.protocol
918 }
919 fn close(&mut self, code: u32, _reason: &str) {
920 self.closed.lock().unwrap().get_or_insert(code);
921 }
922 fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Error> {
923 std::task::Poll::Pending
924 }
925 fn stats(&self) -> impl web_transport_trait::Stats {
926 web_transport_trait::StatsUnavailable
927 }
928 }
929
930 #[derive(Clone, Default)]
931 struct FakeSend;
932 impl web_transport_trait::poll::SendStream for FakeSend {
933 type Error = FakeError;
934 fn poll_write(
935 &mut self,
936 _cx: &mut std::task::Context<'_>,
937 buf: &[u8],
938 ) -> std::task::Poll<Result<usize, Self::Error>> {
939 std::task::Poll::Ready(Ok(buf.len()))
940 }
941 fn set_priority(&mut self, _order: u8) {}
942 fn finish(&mut self) -> Result<(), Self::Error> {
943 Ok(())
944 }
945 fn reset(&mut self, _code: u32) {}
946 fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
947 std::task::Poll::Ready(Ok(()))
948 }
949 }
950
951 struct FakeRecv {
952 data: VecDeque<u8>,
953 }
954 impl web_transport_trait::poll::RecvStream for FakeRecv {
955 type Error = FakeError;
956 fn poll_read(
957 &mut self,
958 _cx: &mut std::task::Context<'_>,
959 dst: &mut [u8],
960 ) -> std::task::Poll<Result<Option<usize>, Self::Error>> {
961 if self.data.is_empty() {
962 return std::task::Poll::Ready(Ok(None));
963 }
964 let size = dst.len().min(self.data.len());
965 for slot in dst.iter_mut().take(size) {
966 *slot = self.data.pop_front().unwrap();
967 }
968 std::task::Poll::Ready(Ok(Some(size)))
969 }
970 fn stop(&mut self, _code: u32) {}
971 fn poll_closed(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
972 std::task::Poll::Ready(Ok(()))
973 }
974 }
975
976 fn lite05_setup(path: Option<&str>, role: Option<Role>, hop: Option<Hop>) -> Vec<u8> {
978 let v = lite::Version::Lite05;
979 let mut buf = Vec::new();
980 lite::DataType::Setup.encode(&mut buf, v).unwrap();
981 lite::Setup {
982 probe: lite::ProbeLevel::None,
983 path: path.map(str::to_string),
984 role,
985 cost: None,
986 hop,
987 }
988 .encode(&mut buf, v)
989 .unwrap();
990 buf
991 }
992
993 fn ietf_setup(version: ietf::Version, path: Option<&str>) -> Vec<u8> {
996 let mut params = ietf::Parameters::default();
997 if let Some(path) = path {
998 params.set_bytes(ietf::ParameterBytes::Path, path.as_bytes().to_vec());
999 }
1000 ietf_setup_with(version, params)
1001 }
1002
1003 fn ietf_setup_with(version: ietf::Version, params: ietf::Parameters) -> Vec<u8> {
1004 let parameters = params.encode_bytes(version).unwrap();
1005
1006 let mut buf = Vec::new();
1007 setup::Setup { parameters }
1008 .encode(&mut buf, crate::Version::Ietf(version))
1009 .unwrap();
1010 buf
1011 }
1012
1013 fn legacy_setup(version: ietf::Version, params: ietf::Parameters) -> Vec<u8> {
1015 let mut buf = Vec::new();
1016 setup::Client {
1017 versions: crate::coding::Versions::from([crate::Version::Ietf(version).into()]),
1018 parameters: params.encode_bytes(version).unwrap(),
1019 }
1020 .encode(&mut buf, crate::Version::Ietf(version))
1021 .unwrap();
1022 buf
1023 }
1024
1025 fn setup_token() -> setup::Token {
1026 setup::Token {
1027 kind: setup::Token::OUT_OF_BAND,
1028 value: vec![0x00, 0xff, b'j', b'w', b't'],
1029 }
1030 }
1031
1032 fn token_params(version: ietf::Version) -> ietf::Parameters {
1033 let mut params = ietf::Parameters::default();
1034 ietf::token::into_setup(&mut params, &setup_token(), version).unwrap();
1035 params
1036 }
1037
1038 #[tokio::test(start_paused = true)]
1039 async fn accept_request_exposes_the_setup_token() {
1040 let modern = FakeSession::new(
1041 ALPN_19,
1042 [ietf_setup_with(
1043 ietf::Version::Draft19,
1044 token_params(ietf::Version::Draft19),
1045 )],
1046 );
1047 let legacy = FakeSession::new(ALPN_16, []).with_bi(legacy_setup(
1048 ietf::Version::Draft16,
1049 token_params(ietf::Version::Draft16),
1050 ));
1051 for (name, session) in [("draft-19", modern), ("draft-16", legacy)] {
1052 let request = Server::new()
1053 .accept_request(tokio::time::Instant::now().into_std(), session)
1054 .await
1055 .unwrap();
1056 assert_eq!(request.token(), Some(&setup_token()), "{name}");
1057 }
1058 }
1059
1060 #[tokio::test(start_paused = true)]
1061 async fn accept_request_without_a_token_reports_none() {
1062 let ietf = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, None)]);
1063 let lite = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, None)]);
1064 for (name, session) in [("draft-19", ietf), ("lite-05", lite)] {
1065 let request = Server::new()
1066 .accept_request(tokio::time::Instant::now().into_std(), session)
1067 .await
1068 .unwrap();
1069 assert_eq!(request.token(), None, "{name}");
1070 }
1071 }
1072
1073 #[tokio::test(start_paused = true)]
1076 async fn a_refused_setup_token_closes_with_its_code() {
1077 let delete = [0x0, 0x7]; let truncated = [0x3]; for (raw, code) in [
1080 (&delete[..], SessionError::ProtocolViolation),
1081 (&truncated[..], SessionError::KeyValueFormatting),
1082 ] {
1083 let mut params = ietf::Parameters::default();
1084 params.set_bytes(ietf::ParameterBytes::AuthorizationToken, raw.to_vec());
1085
1086 let modern = FakeSession::new(ALPN_19, [ietf_setup_with(ietf::Version::Draft19, params.clone())]);
1087 let legacy = FakeSession::new(ALPN_16, []).with_bi(legacy_setup(ietf::Version::Draft16, params));
1088 for (name, session) in [("draft-19", modern), ("draft-16", legacy)] {
1089 let result = Server::new()
1090 .accept_request(tokio::time::Instant::now().into_std(), session.clone())
1091 .await;
1092 assert!(result.is_err(), "{name}");
1093 assert_eq!(session.closed(), Some(code.to_code()), "{name} {code}");
1094 }
1095 }
1096 }
1097
1098 #[tokio::test(start_paused = true)]
1099 async fn accept_request_reads_ietf_path() {
1100 for (alpn, version) in [
1103 (ALPN_17, ietf::Version::Draft17),
1104 (ALPN_18, ietf::Version::Draft18),
1105 (ALPN_19, ietf::Version::Draft19),
1106 ] {
1107 let session = FakeSession::new(alpn, [ietf_setup(version, Some("/team/room"))]);
1108 let request = Server::new()
1109 .accept_request(tokio::time::Instant::now().into_std(), session)
1110 .await
1111 .unwrap();
1112 assert_eq!(request.path(), "/team/room", "{alpn}");
1113 }
1114 }
1115
1116 #[tokio::test(start_paused = true)]
1117 async fn accept_request_ietf_without_path_is_empty() {
1118 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, None)]);
1119 let request = Server::new()
1120 .accept_request(tokio::time::Instant::now().into_std(), session)
1121 .await
1122 .unwrap();
1123 assert_eq!(request.path(), "");
1124 }
1125
1126 #[tokio::test(start_paused = true)]
1127 async fn accept_request_ietf_empty_path_is_accepted() {
1128 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, Some(""))]);
1129 let request = Server::new()
1130 .accept_request(tokio::time::Instant::now().into_std(), session)
1131 .await
1132 .unwrap();
1133 assert_eq!(request.path(), "");
1134 }
1135
1136 fn lite05_group() -> Vec<u8> {
1138 let mut buf = Vec::new();
1139 lite::DataType::Group.encode(&mut buf, lite::Version::Lite05).unwrap();
1140 buf
1141 }
1142
1143 #[tokio::test(start_paused = true)]
1144 async fn accept_request_reads_lite05_path() {
1145 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some("/team/room"), None, None)]);
1146 let request = Server::new()
1147 .accept_request(tokio::time::Instant::now().into_std(), session)
1148 .await
1149 .unwrap();
1150 assert_eq!(request.path(), "/team/room");
1151 assert_eq!(request.role(), None, "a client that omits the role is bidirectional");
1152 }
1153
1154 #[tokio::test(start_paused = true)]
1155 async fn accept_request_lite05_without_path_is_empty() {
1156 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, None)]);
1157 let request = Server::new()
1158 .accept_request(tokio::time::Instant::now().into_std(), session)
1159 .await
1160 .unwrap();
1161 assert_eq!(request.path(), "");
1162 }
1163
1164 #[tokio::test(start_paused = true)]
1165 async fn accept_request_lite05_empty_path_is_accepted() {
1166 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some(""), None, None)]);
1169 let request = Server::new()
1170 .accept_request(tokio::time::Instant::now().into_std(), session)
1171 .await
1172 .unwrap();
1173 assert_eq!(request.path(), "");
1174 }
1175
1176 #[tokio::test(start_paused = true)]
1177 async fn accept_request_reads_lite05_role() {
1178 let session = FakeSession::new(
1179 ALPN_LITE_05,
1180 [lite05_setup(Some("/team/room"), Some(Role::Publisher), None)],
1181 );
1182 let request = Server::new()
1183 .accept_request(tokio::time::Instant::now().into_std(), session)
1184 .await
1185 .unwrap();
1186 assert_eq!(request.role(), Some(Role::Publisher));
1187 }
1188
1189 #[tokio::test(start_paused = true)]
1190 async fn accept_request_skips_uni_stream_before_setup() {
1191 let session = FakeSession::new(
1194 ALPN_LITE_05,
1195 [lite05_group(), lite05_setup(Some("/team/room"), None, None)],
1196 );
1197 let request = Server::new()
1198 .accept_request(tokio::time::Instant::now().into_std(), session)
1199 .await
1200 .unwrap();
1201 assert_eq!(request.path(), "/team/room");
1202 }
1203
1204 #[tokio::test(start_paused = true)]
1205 async fn accept_request_reads_lite05_peer_hop() {
1206 let hop = Hop::new(42).unwrap();
1207 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, Some(hop))]);
1208 let request = Server::new()
1209 .accept_request(tokio::time::Instant::now().into_std(), session)
1210 .await
1211 .unwrap();
1212 assert_eq!(request.peer_hop(), Some(hop));
1213 }
1214
1215 #[tokio::test(start_paused = true)]
1216 async fn anonymous_peer_hop_filters_routes_from_server_session() {
1217 let other = Hop::new(778).unwrap();
1218 let origin = crate::origin::Config::new(Hop::new(1).unwrap()).produce();
1219
1220 let gate = kio::Producer::new(true);
1221 let transport = crate::lite::test_transport::SinkSession::gated_bi(gate.consume());
1222 let log = transport.log.clone();
1223 let version = ietf::Version::Draft18;
1224 let request = Handshake {
1225 path: None,
1226 role: None,
1227 origin: None,
1228 token: None,
1229 assigned_hop: Hop::random(),
1230 inner: Some(RequestInner {
1231 server: Server::new().with_publisher(&origin),
1232 runtime: Clock::new(tokio::time::Instant::now().into_std()),
1233 handshake: PausedHandshake::Boxed(Box::new(PausedIetfModern {
1234 session: transport,
1235 version,
1236 peer_setup: ietf::PeerSetup {
1237 stream: crate::coding::Reader::new(
1238 crate::lite::test_transport::PendingRecv,
1239 Version::Ietf(version),
1240 ),
1241 path: None,
1242 token: None,
1243 declared: ietf::peer::Peer::default(),
1244 },
1245 })),
1246 }),
1247 };
1248 let assigned = request.assigned_hop;
1249
1250 let mut echoed_hops = crate::Hops::new();
1251 echoed_hops.push(crate::Hop::UNKNOWN).unwrap();
1252 let _echoed = origin
1253 .announce(
1254 "echoed-route",
1255 crate::origin::Route::default()
1256 .with_hops(echoed_hops)
1257 .with_via(assigned),
1258 )
1259 .unwrap();
1260
1261 let mut local_hops = crate::Hops::new();
1262 local_hops.push(other).unwrap();
1263 let _local = origin
1264 .announce("local-route", crate::origin::Route::default().with_hops(local_hops))
1265 .unwrap();
1266
1267 let (session, driver) = request.ok().await.unwrap();
1268 tokio::spawn(crate::time::run(driver));
1269
1270 for _ in 0..100 {
1271 if occurrences(&log, b"local-route") > 0 {
1272 break;
1273 }
1274 tokio::time::sleep(std::time::Duration::from_millis(1)).await;
1275 }
1276
1277 assert_eq!(occurrences(&log, b"echoed-route"), 0);
1278 assert_eq!(occurrences(&log, b"local-route"), 1);
1279 drop(session);
1280 }
1281}