1use crate::origin;
2use crate::{
3 ALPN_14, ALPN_15, ALPN_16, ALPN_17, ALPN_18, ALPN_19, ALPN_LITE, ALPN_LITE_03, ALPN_LITE_04, ALPN_LITE_05,
4 ALPN_LITE_06_WIP, Consume, Driver, Error, NEGOTIATED, Role, Session, Version, Versions,
5 coding::{Decode, Encode, Stream},
6 ietf, lite, setup, stats,
7};
8
9#[derive(Default, Clone)]
11pub struct Server {
12 publish: Option<origin::Consumer>,
13 subscribe: Option<origin::Producer>,
14 stats: stats::Session,
15 versions: Versions,
16}
17
18impl Server {
19 pub fn new() -> Self {
21 Default::default()
22 }
23
24 pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
29 self.publish = Some(publish.consume());
30 self
31 }
32
33 pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
36 self.subscribe = Some(subscribe);
37 self
38 }
39
40 pub fn with_stats(mut self, stats: stats::Session) -> Self {
45 self.stats = stats;
46 self
47 }
48
49 pub fn with_origin(self, origin: origin::Producer) -> Self {
51 self.with_publisher(&origin).with_subscriber(origin)
52 }
53
54 pub fn with_versions(mut self, versions: Versions) -> Self {
57 self.versions = versions;
58 self
59 }
60
61 pub async fn accept<S: web_transport_trait::Session>(&self, session: S) -> Result<(Session, Driver), Error> {
68 self.accept_request(session).await?.ok().await
69 }
70
71 pub async fn accept_request<S: web_transport_trait::Session>(&self, session: S) -> Result<Request<S>, Error> {
82 let deferred = |handshake| Request {
85 path: None,
86 role: None,
87 origin: None,
88 inner: Some(RequestInner {
89 server: self.clone(),
90 handshake,
91 }),
92 };
93
94 let (encoding, supported) = match session.protocol() {
95 Some(ALPN_19) => {
96 self.versions
97 .select(Version::Ietf(ietf::Version::Draft19))
98 .ok_or(Error::Version)?;
99 return self.accept_ietf_modern(session, ietf::Version::Draft19).await;
100 }
101 Some(ALPN_18) => {
102 self.versions
103 .select(Version::Ietf(ietf::Version::Draft18))
104 .ok_or(Error::Version)?;
105 return self.accept_ietf_modern(session, ietf::Version::Draft18).await;
106 }
107 Some(ALPN_17) => {
108 self.versions
109 .select(Version::Ietf(ietf::Version::Draft17))
110 .ok_or(Error::Version)?;
111 return self.accept_ietf_modern(session, ietf::Version::Draft17).await;
112 }
113 Some(ALPN_16) => {
114 let v = self
115 .versions
116 .select(Version::Ietf(ietf::Version::Draft16))
117 .ok_or(Error::Version)?;
118 (v, v.into())
119 }
120 Some(ALPN_15) => {
121 let v = self
122 .versions
123 .select(Version::Ietf(ietf::Version::Draft15))
124 .ok_or(Error::Version)?;
125 (v, v.into())
126 }
127 Some(ALPN_14) => {
128 let v = self
129 .versions
130 .select(Version::Ietf(ietf::Version::Draft14))
131 .ok_or(Error::Version)?;
132 (v, v.into())
133 }
134 Some(alpn @ (ALPN_LITE_05 | ALPN_LITE_06_WIP)) => {
135 let version = match alpn {
136 ALPN_LITE_06_WIP => lite::Version::Lite06Wip,
137 _ => lite::Version::Lite05,
138 };
139 self.versions.select(Version::Lite(version)).ok_or(Error::Version)?;
140
141 let client_setup = lite::accept_setup(&session, version).await?;
145 return Ok(Request {
146 path: client_setup.path.clone(),
147 role: client_setup.role,
148 origin: client_setup.origin,
149 inner: Some(RequestInner {
150 server: self.clone(),
151 handshake: Handshake::LiteSetup {
152 session,
153 version,
154 client_setup,
155 },
156 }),
157 });
158 }
159 Some(ALPN_LITE_04) => {
160 self.versions
161 .select(Version::Lite(lite::Version::Lite04))
162 .ok_or(Error::Version)?;
163 return Ok(deferred(Handshake::LiteBare {
164 session,
165 version: lite::Version::Lite04,
166 }));
167 }
168 Some(ALPN_LITE_03) => {
169 self.versions
170 .select(Version::Lite(lite::Version::Lite03))
171 .ok_or(Error::Version)?;
172 return Ok(deferred(Handshake::LiteBare {
173 session,
174 version: lite::Version::Lite03,
175 }));
176 }
177 Some(ALPN_LITE) | None => {
178 let supported = self.versions.filter(&NEGOTIATED.into()).ok_or(Error::Version)?;
179 (Version::Ietf(ietf::Version::Draft14), supported)
180 }
181 Some(p) => return Err(Error::UnknownAlpn(p.to_string())),
182 };
183
184 let mut stream = Stream::accept(&session, encoding).await?;
187 let mut client: setup::Client = stream.reader.decode().await?;
188
189 let version = client
190 .versions
191 .iter()
192 .flat_map(|v| Version::try_from(*v).ok())
193 .find(|v| supported.contains(v))
194 .ok_or(Error::Version)?;
195
196 let (path, request_id_max, peer_declared) = match version {
200 Version::Ietf(v) => {
201 let params = ietf::Parameters::decode(&mut client.parameters, v)?;
202 let path = match params.get_bytes(ietf::ParameterBytes::Path) {
203 Some(bytes) => Some(
204 std::str::from_utf8(bytes)
205 .map_err(|_| Error::Decode(crate::DecodeError::InvalidValue))?
206 .to_owned(),
207 ),
208 None => None,
209 };
210 let request_id_max = params
211 .get_varint(ietf::ParameterVarInt::MaxRequestId)
212 .map(ietf::RequestId);
213 let peer_declared = ietf::peer::Peer {
214 solicit: ietf::solicit::from_setup(¶ms, v)?,
215 ..Default::default()
216 };
217 (path, request_id_max, peer_declared)
218 }
219 Version::Lite(_) => (None, None, ietf::peer::Peer::default()),
220 };
221
222 Ok(Request {
223 path,
224 role: None,
225 origin: None,
226 inner: Some(RequestInner {
227 server: self.clone(),
228 handshake: Handshake::Legacy {
229 session,
230 stream,
231 version,
232 request_id_max,
233 peer_declared,
234 },
235 }),
236 })
237 }
238
239 async fn accept_ietf_modern<S: web_transport_trait::Session>(
242 &self,
243 session: S,
244 version: ietf::Version,
245 ) -> Result<Request<S>, Error> {
246 let peer_setup = ietf::accept_setup(&session, version).await?;
247 Ok(Request {
248 path: peer_setup.path.clone(),
249 role: None,
250 origin: peer_setup
253 .declared
254 .cluster
255 .origin
256 .filter(|o| *o != crate::Origin::UNKNOWN),
257 inner: Some(RequestInner {
258 server: self.clone(),
259 handshake: Handshake::IetfModern {
260 session,
261 version,
262 peer_setup,
263 },
264 }),
265 })
266 }
267}
268
269pub struct Request<S: web_transport_trait::Session> {
277 path: Option<String>,
278 role: Option<Role>,
279 origin: Option<crate::Origin>,
280 inner: Option<RequestInner<S>>,
282}
283
284struct RequestInner<S: web_transport_trait::Session> {
286 server: Server,
287 handshake: Handshake<S>,
288}
289
290enum Handshake<S: web_transport_trait::Session> {
293 IetfModern {
297 session: S,
298 version: ietf::Version,
299 peer_setup: ietf::PeerSetup<S>,
300 },
301 LiteBare { session: S, version: lite::Version },
303 Legacy {
307 session: S,
308 stream: Stream<S, Version>,
309 version: Version,
310 request_id_max: Option<ietf::RequestId>,
311 peer_declared: ietf::peer::Peer,
313 },
314 LiteSetup {
317 session: S,
318 version: lite::Version,
319 client_setup: lite::Setup,
320 },
321}
322
323impl<S: web_transport_trait::Session> Request<S> {
324 pub fn path(&self) -> &str {
331 self.path.as_deref().unwrap_or("")
332 }
333
334 pub fn role(&self) -> Option<Role> {
343 self.role
344 }
345
346 pub fn peer_origin(&self) -> Option<crate::Origin> {
355 self.origin
356 }
357
358 pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
361 self.inner_mut().server.publish = Some(publish.consume());
362 self
363 }
364
365 pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
367 self.inner_mut().server.subscribe = Some(subscribe);
368 self
369 }
370
371 pub fn with_stats(mut self, stats: stats::Session) -> Self {
374 self.inner_mut().server.stats = stats;
375 self
376 }
377
378 fn inner_mut(&mut self) -> &mut RequestInner<S> {
379 self.inner.as_mut().expect("request already responded")
380 }
381
382 pub async fn ok(mut self) -> Result<(Session, Driver), Error> {
385 let RequestInner { server, handshake } = self.inner.take().expect("request already responded");
386
387 let publish = server.publish.map(|origin| origin.with_stats(server.stats.clone()));
391 let subscribe = server.subscribe.map(|origin| origin.with_stats(server.stats.clone()));
392
393 let (session, mut stream, version, request_id_max, peer_declared) = match handshake {
394 Handshake::IetfModern {
395 session,
396 version,
397 peer_setup,
398 } => {
399 let protocol = ietf::start(ietf::Config {
402 session: session.clone(),
403 setup: None,
404 request_id_max: None,
405 client: false,
406 publish,
407 subscribe,
408 peer_origin: None,
409 cost: None,
411 version,
412 path: None,
413 peer_setup_stream: Some(peer_setup.stream),
414 peer_declared: Some(peer_setup.declared),
415 })?;
416 tracing::debug!(?version, "connected");
417 return Ok(Session::new(session, version.into(), None, protocol));
418 }
419 Handshake::LiteBare { session, version } => {
420 let start = lite::start(lite::Config {
421 session: session.clone(),
422 setup_stream: None,
423 publish,
424 subscribe,
425 peer_origin: None,
426 version,
427 our_setup: lite::Setup::default(),
428 peer_setup: None,
429 })?;
430 return Ok(Session::new(
431 session,
432 version.into(),
433 start.recv_bandwidth,
434 start.driver,
435 ));
436 }
437 Handshake::LiteSetup {
438 session,
439 version,
440 client_setup,
441 } => {
442 let our_setup = lite::Setup {
444 probe: lite::ProbeLevel::Report,
445 path: None,
446 role: None,
447 cost: None,
449 origin: None,
451 };
452 let start = lite::start(lite::Config {
453 session: session.clone(),
454 setup_stream: None,
455 publish,
456 subscribe,
457 peer_origin: None,
458 version,
459 our_setup,
460 peer_setup: Some(client_setup),
461 })?;
462 return Ok(Session::new(
463 session,
464 version.into(),
465 start.recv_bandwidth,
466 start.driver,
467 ));
468 }
469 Handshake::Legacy {
470 session,
471 stream,
472 version,
473 request_id_max,
474 peer_declared,
475 } => (session, stream, version, request_id_max, peer_declared),
476 };
477
478 let parameters = match version {
480 Version::Ietf(v) => {
481 let mut parameters = ietf::Parameters::default();
482 parameters.set_varint(ietf::ParameterVarInt::MaxRequestId, u32::MAX as u64);
483 parameters.set_bytes(ietf::ParameterBytes::Implementation, b"moq-lite-rs".to_vec());
484 ietf::solicit::into_setup(&mut parameters, v);
485 parameters.encode_bytes(v)?
486 }
487 Version::Lite(v) => lite::Parameters::default().encode_bytes(v)?,
488 };
489
490 let server_setup = setup::Server {
491 version: version.into(),
492 parameters,
493 };
494 stream.writer.encode(&server_setup).await?;
495
496 let (recv_bw, protocol) = match version {
497 Version::Lite(v) => {
498 let stream = stream.with_version(v);
499 let start = lite::start(lite::Config {
501 session: session.clone(),
502 setup_stream: Some(stream),
503 publish,
504 subscribe,
505 peer_origin: None,
506 version: v,
507 our_setup: lite::Setup::default(),
508 peer_setup: None,
509 })?;
510 (start.recv_bandwidth, start.driver)
511 }
512 Version::Ietf(v) => {
513 let stream = stream.with_version(v);
514 let protocol = ietf::start(ietf::Config {
516 session: session.clone(),
517 setup: Some(stream),
518 request_id_max,
519 client: false,
520 publish,
521 subscribe,
522 peer_origin: None,
523 cost: None,
524 version: v,
525 path: None,
526 peer_setup_stream: None,
527 peer_declared: Some(peer_declared),
528 })?;
529 (None, protocol)
530 }
531 };
532
533 Ok(Session::new(session, version, recv_bw, protocol))
534 }
535
536 pub fn close(mut self, err: Error) {
538 let inner = self.inner.take().expect("request already responded");
539 inner.close(err);
540 }
541}
542
543impl<S: web_transport_trait::Session> RequestInner<S> {
544 fn close(self, err: Error) {
545 let session = match self.handshake {
546 Handshake::IetfModern { session, .. } => session,
547 Handshake::LiteBare { session, .. } => session,
548 Handshake::Legacy { session, .. } => session,
549 Handshake::LiteSetup { session, .. } => session,
550 };
551 session.close(err.to_code(), &err.to_string());
552 }
553}
554
555impl<S: web_transport_trait::Session> Drop for Request<S> {
556 fn drop(&mut self) {
559 if let Some(inner) = self.inner.take() {
560 tracing::warn!("Request dropped without ok() or close(); rejecting the session");
561 inner.close(Error::Cancel);
562 }
563 }
564}
565
566#[cfg(test)]
567mod tests {
568 use super::*;
569 use crate::Origin;
570 use std::{
571 collections::VecDeque,
572 sync::{Arc, Mutex},
573 };
574
575 use crate::ALPN_LITE_05;
576 use bytes::Bytes;
577
578 #[derive(Debug, Clone, Default)]
579 struct FakeError;
580 impl std::fmt::Display for FakeError {
581 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
582 write!(f, "fake transport error")
583 }
584 }
585 impl std::error::Error for FakeError {}
586 impl web_transport_trait::Error for FakeError {
587 fn session_error(&self) -> Option<(u32, String)> {
588 Some((0, "closed".to_string()))
589 }
590 }
591
592 #[derive(Clone)]
595 struct FakeSession {
596 protocol: Option<&'static str>,
597 uni: Arc<Mutex<VecDeque<Vec<u8>>>>,
598 }
599
600 impl FakeSession {
601 fn new(protocol: &'static str, uni: impl IntoIterator<Item = Vec<u8>>) -> Self {
602 Self {
603 protocol: Some(protocol),
604 uni: Arc::new(Mutex::new(uni.into_iter().collect())),
605 }
606 }
607 }
608
609 impl web_transport_trait::Session for FakeSession {
610 type SendStream = FakeSend;
611 type RecvStream = FakeRecv;
612 type Error = FakeError;
613
614 async fn accept_uni(&self) -> Result<Self::RecvStream, Self::Error> {
615 let data = self.uni.lock().unwrap().pop_front();
617 match data {
618 Some(data) => Ok(FakeRecv { data: data.into() }),
619 None => std::future::pending().await,
620 }
621 }
622 async fn accept_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error> {
623 std::future::pending().await
624 }
625 async fn open_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error> {
626 std::future::pending().await
627 }
628 async fn open_uni(&self) -> Result<Self::SendStream, Self::Error> {
629 std::future::pending().await
630 }
631 fn send_datagram(&self, _payload: Bytes) -> Result<(), Self::Error> {
632 Ok(())
633 }
634 async fn recv_datagram(&self) -> Result<Bytes, Self::Error> {
635 std::future::pending().await
636 }
637 fn max_datagram_size(&self) -> usize {
638 1200
639 }
640 fn protocol(&self) -> Option<&str> {
641 self.protocol
642 }
643 fn close(&self, _code: u32, _reason: &str) {}
644 async fn closed(&self) -> Self::Error {
645 std::future::pending().await
646 }
647 }
648
649 #[derive(Clone, Default)]
650 struct FakeSend;
651 impl web_transport_trait::SendStream for FakeSend {
652 type Error = FakeError;
653 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
654 Ok(buf.len())
655 }
656 fn set_priority(&mut self, _order: u8) {}
657 fn finish(&mut self) -> Result<(), Self::Error> {
658 Ok(())
659 }
660 fn reset(&mut self, _code: u32) {}
661 async fn closed(&mut self) -> Result<(), Self::Error> {
662 Ok(())
663 }
664 }
665
666 struct FakeRecv {
667 data: VecDeque<u8>,
668 }
669 impl web_transport_trait::RecvStream for FakeRecv {
670 type Error = FakeError;
671 async fn read(&mut self, dst: &mut [u8]) -> Result<Option<usize>, Self::Error> {
672 if self.data.is_empty() {
673 return Ok(None);
674 }
675 let size = dst.len().min(self.data.len());
676 for slot in dst.iter_mut().take(size) {
677 *slot = self.data.pop_front().unwrap();
678 }
679 Ok(Some(size))
680 }
681 fn stop(&mut self, _code: u32) {}
682 async fn closed(&mut self) -> Result<(), Self::Error> {
683 Ok(())
684 }
685 }
686
687 fn lite05_setup(path: Option<&str>, role: Option<Role>, origin: Option<Origin>) -> Vec<u8> {
689 let v = lite::Version::Lite05;
690 let mut buf = Vec::new();
691 lite::DataType::Setup.encode(&mut buf, v).unwrap();
692 lite::Setup {
693 probe: lite::ProbeLevel::None,
694 path: path.map(str::to_string),
695 role,
696 cost: None,
697 origin,
698 }
699 .encode(&mut buf, v)
700 .unwrap();
701 buf
702 }
703
704 fn ietf_setup(version: ietf::Version, path: Option<&str>) -> Vec<u8> {
707 let mut params = ietf::Parameters::default();
708 if let Some(path) = path {
709 params.set_bytes(ietf::ParameterBytes::Path, path.as_bytes().to_vec());
710 }
711 let parameters = params.encode_bytes(version).unwrap();
712
713 let mut buf = Vec::new();
714 setup::Setup { parameters }
715 .encode(&mut buf, crate::Version::Ietf(version))
716 .unwrap();
717 buf
718 }
719
720 #[tokio::test(start_paused = true)]
721 async fn accept_request_reads_ietf_path() {
722 for (alpn, version) in [
725 (ALPN_17, ietf::Version::Draft17),
726 (ALPN_18, ietf::Version::Draft18),
727 (ALPN_19, ietf::Version::Draft19),
728 ] {
729 let session = FakeSession::new(alpn, [ietf_setup(version, Some("/team/room"))]);
730 let request = Server::new().accept_request(session).await.unwrap();
731 assert_eq!(request.path(), "/team/room", "{alpn}");
732 }
733 }
734
735 #[tokio::test(start_paused = true)]
736 async fn accept_request_ietf_without_path_is_empty() {
737 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, None)]);
738 let request = Server::new().accept_request(session).await.unwrap();
739 assert_eq!(request.path(), "");
740 }
741
742 #[tokio::test(start_paused = true)]
743 async fn accept_request_ietf_empty_path_is_accepted() {
744 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, Some(""))]);
745 let request = Server::new().accept_request(session).await.unwrap();
746 assert_eq!(request.path(), "");
747 }
748
749 fn lite05_group() -> Vec<u8> {
751 let mut buf = Vec::new();
752 lite::DataType::Group.encode(&mut buf, lite::Version::Lite05).unwrap();
753 buf
754 }
755
756 #[tokio::test(start_paused = true)]
757 async fn accept_request_reads_lite05_path() {
758 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some("/team/room"), None, None)]);
759 let request = Server::new().accept_request(session).await.unwrap();
760 assert_eq!(request.path(), "/team/room");
761 assert_eq!(request.role(), None, "a client that omits the role is bidirectional");
762 }
763
764 #[tokio::test(start_paused = true)]
765 async fn accept_request_lite05_without_path_is_empty() {
766 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, None)]);
767 let request = Server::new().accept_request(session).await.unwrap();
768 assert_eq!(request.path(), "");
769 }
770
771 #[tokio::test(start_paused = true)]
772 async fn accept_request_lite05_empty_path_is_accepted() {
773 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some(""), None, None)]);
776 let request = Server::new().accept_request(session).await.unwrap();
777 assert_eq!(request.path(), "");
778 }
779
780 #[tokio::test(start_paused = true)]
781 async fn accept_request_reads_lite05_role() {
782 let session = FakeSession::new(
783 ALPN_LITE_05,
784 [lite05_setup(Some("/team/room"), Some(Role::Publisher), None)],
785 );
786 let request = Server::new().accept_request(session).await.unwrap();
787 assert_eq!(request.role(), Some(Role::Publisher));
788 }
789
790 #[tokio::test(start_paused = true)]
791 async fn accept_request_skips_uni_stream_before_setup() {
792 let session = FakeSession::new(
795 ALPN_LITE_05,
796 [lite05_group(), lite05_setup(Some("/team/room"), None, None)],
797 );
798 let request = Server::new().accept_request(session).await.unwrap();
799 assert_eq!(request.path(), "/team/room");
800 }
801
802 #[tokio::test(start_paused = true)]
803 async fn accept_request_reads_lite05_peer_origin() {
804 let origin = Origin::new(42).unwrap();
805 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, Some(origin))]);
806 let request = Server::new().accept_request(session).await.unwrap();
807 assert_eq!(request.peer_origin(), Some(origin));
808 }
809}