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 {
445 probe: lite::ProbeLevel::detect(&session),
446 path: None,
447 role: None,
448 cost: None,
450 origin: None,
452 };
453 let start = lite::start(lite::Config {
454 session: session.clone(),
455 setup_stream: None,
456 publish,
457 subscribe,
458 peer_origin: None,
459 version,
460 our_setup,
461 peer_setup: Some(client_setup),
462 })?;
463 return Ok(Session::new(
464 session,
465 version.into(),
466 start.recv_bandwidth,
467 start.driver,
468 ));
469 }
470 Handshake::Legacy {
471 session,
472 stream,
473 version,
474 request_id_max,
475 peer_declared,
476 } => (session, stream, version, request_id_max, peer_declared),
477 };
478
479 let parameters = match version {
481 Version::Ietf(v) => {
482 let mut parameters = ietf::Parameters::default();
483 parameters.set_varint(ietf::ParameterVarInt::MaxRequestId, u32::MAX as u64);
484 parameters.set_bytes(ietf::ParameterBytes::Implementation, b"moq-lite-rs".to_vec());
485 ietf::solicit::into_setup(&mut parameters, v);
486 parameters.encode_bytes(v)?
487 }
488 Version::Lite(v) => lite::Parameters::default().encode_bytes(v)?,
489 };
490
491 let server_setup = setup::Server {
492 version: version.into(),
493 parameters,
494 };
495 stream.writer.encode(&server_setup).await?;
496
497 let (recv_bw, protocol) = match version {
498 Version::Lite(v) => {
499 let stream = stream.with_version(v);
500 let start = lite::start(lite::Config {
502 session: session.clone(),
503 setup_stream: Some(stream),
504 publish,
505 subscribe,
506 peer_origin: None,
507 version: v,
508 our_setup: lite::Setup::default(),
509 peer_setup: None,
510 })?;
511 (start.recv_bandwidth, start.driver)
512 }
513 Version::Ietf(v) => {
514 let stream = stream.with_version(v);
515 let protocol = ietf::start(ietf::Config {
517 session: session.clone(),
518 setup: Some(stream),
519 request_id_max,
520 client: false,
521 publish,
522 subscribe,
523 peer_origin: None,
524 cost: None,
525 version: v,
526 path: None,
527 peer_setup_stream: None,
528 peer_declared: Some(peer_declared),
529 })?;
530 (None, protocol)
531 }
532 };
533
534 Ok(Session::new(session, version, recv_bw, protocol))
535 }
536
537 pub fn close(mut self, err: Error) {
539 let inner = self.inner.take().expect("request already responded");
540 inner.close(err);
541 }
542}
543
544impl<S: web_transport_trait::Session> RequestInner<S> {
545 fn close(self, err: Error) {
546 let session = match self.handshake {
547 Handshake::IetfModern { session, .. } => session,
548 Handshake::LiteBare { session, .. } => session,
549 Handshake::Legacy { session, .. } => session,
550 Handshake::LiteSetup { session, .. } => session,
551 };
552 session.close(err.to_code(), &err.to_string());
553 }
554}
555
556impl<S: web_transport_trait::Session> Drop for Request<S> {
557 fn drop(&mut self) {
560 if let Some(inner) = self.inner.take() {
561 tracing::warn!("Request dropped without ok() or close(); rejecting the session");
562 inner.close(Error::Cancel);
563 }
564 }
565}
566
567#[cfg(test)]
568mod tests {
569 use super::*;
570 use crate::Origin;
571 use std::{
572 collections::VecDeque,
573 sync::{Arc, Mutex},
574 };
575
576 use crate::ALPN_LITE_05;
577 use bytes::Bytes;
578
579 #[derive(Debug, Clone, Default)]
580 struct FakeError;
581 impl std::fmt::Display for FakeError {
582 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
583 write!(f, "fake transport error")
584 }
585 }
586 impl std::error::Error for FakeError {}
587 impl web_transport_trait::Error for FakeError {
588 fn session_error(&self) -> Option<(u32, String)> {
589 Some((0, "closed".to_string()))
590 }
591 }
592
593 #[derive(Clone)]
596 struct FakeSession {
597 protocol: Option<&'static str>,
598 uni: Arc<Mutex<VecDeque<Vec<u8>>>>,
599 }
600
601 impl FakeSession {
602 fn new(protocol: &'static str, uni: impl IntoIterator<Item = Vec<u8>>) -> Self {
603 Self {
604 protocol: Some(protocol),
605 uni: Arc::new(Mutex::new(uni.into_iter().collect())),
606 }
607 }
608 }
609
610 impl web_transport_trait::Session for FakeSession {
611 type SendStream = FakeSend;
612 type RecvStream = FakeRecv;
613 type Error = FakeError;
614
615 async fn accept_uni(&self) -> Result<Self::RecvStream, Self::Error> {
616 let data = self.uni.lock().unwrap().pop_front();
618 match data {
619 Some(data) => Ok(FakeRecv { data: data.into() }),
620 None => std::future::pending().await,
621 }
622 }
623 async fn accept_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error> {
624 std::future::pending().await
625 }
626 async fn open_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error> {
627 std::future::pending().await
628 }
629 async fn open_uni(&self) -> Result<Self::SendStream, Self::Error> {
630 std::future::pending().await
631 }
632 fn send_datagram(&self, _payload: Bytes) -> Result<(), Self::Error> {
633 Ok(())
634 }
635 async fn recv_datagram(&self) -> Result<Bytes, Self::Error> {
636 std::future::pending().await
637 }
638 fn max_datagram_size(&self) -> usize {
639 1200
640 }
641 fn protocol(&self) -> Option<&str> {
642 self.protocol
643 }
644 fn close(&self, _code: u32, _reason: &str) {}
645 async fn closed(&self) -> Self::Error {
646 std::future::pending().await
647 }
648 }
649
650 #[derive(Clone, Default)]
651 struct FakeSend;
652 impl web_transport_trait::SendStream for FakeSend {
653 type Error = FakeError;
654 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
655 Ok(buf.len())
656 }
657 fn set_priority(&mut self, _order: u8) {}
658 fn finish(&mut self) -> Result<(), Self::Error> {
659 Ok(())
660 }
661 fn reset(&mut self, _code: u32) {}
662 async fn closed(&mut self) -> Result<(), Self::Error> {
663 Ok(())
664 }
665 }
666
667 struct FakeRecv {
668 data: VecDeque<u8>,
669 }
670 impl web_transport_trait::RecvStream for FakeRecv {
671 type Error = FakeError;
672 async fn read(&mut self, dst: &mut [u8]) -> Result<Option<usize>, Self::Error> {
673 if self.data.is_empty() {
674 return Ok(None);
675 }
676 let size = dst.len().min(self.data.len());
677 for slot in dst.iter_mut().take(size) {
678 *slot = self.data.pop_front().unwrap();
679 }
680 Ok(Some(size))
681 }
682 fn stop(&mut self, _code: u32) {}
683 async fn closed(&mut self) -> Result<(), Self::Error> {
684 Ok(())
685 }
686 }
687
688 fn lite05_setup(path: Option<&str>, role: Option<Role>, origin: Option<Origin>) -> Vec<u8> {
690 let v = lite::Version::Lite05;
691 let mut buf = Vec::new();
692 lite::DataType::Setup.encode(&mut buf, v).unwrap();
693 lite::Setup {
694 probe: lite::ProbeLevel::None,
695 path: path.map(str::to_string),
696 role,
697 cost: None,
698 origin,
699 }
700 .encode(&mut buf, v)
701 .unwrap();
702 buf
703 }
704
705 fn ietf_setup(version: ietf::Version, path: Option<&str>) -> Vec<u8> {
708 let mut params = ietf::Parameters::default();
709 if let Some(path) = path {
710 params.set_bytes(ietf::ParameterBytes::Path, path.as_bytes().to_vec());
711 }
712 let parameters = params.encode_bytes(version).unwrap();
713
714 let mut buf = Vec::new();
715 setup::Setup { parameters }
716 .encode(&mut buf, crate::Version::Ietf(version))
717 .unwrap();
718 buf
719 }
720
721 #[tokio::test(start_paused = true)]
722 async fn accept_request_reads_ietf_path() {
723 for (alpn, version) in [
726 (ALPN_17, ietf::Version::Draft17),
727 (ALPN_18, ietf::Version::Draft18),
728 (ALPN_19, ietf::Version::Draft19),
729 ] {
730 let session = FakeSession::new(alpn, [ietf_setup(version, Some("/team/room"))]);
731 let request = Server::new().accept_request(session).await.unwrap();
732 assert_eq!(request.path(), "/team/room", "{alpn}");
733 }
734 }
735
736 #[tokio::test(start_paused = true)]
737 async fn accept_request_ietf_without_path_is_empty() {
738 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, None)]);
739 let request = Server::new().accept_request(session).await.unwrap();
740 assert_eq!(request.path(), "");
741 }
742
743 #[tokio::test(start_paused = true)]
744 async fn accept_request_ietf_empty_path_is_accepted() {
745 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, Some(""))]);
746 let request = Server::new().accept_request(session).await.unwrap();
747 assert_eq!(request.path(), "");
748 }
749
750 fn lite05_group() -> Vec<u8> {
752 let mut buf = Vec::new();
753 lite::DataType::Group.encode(&mut buf, lite::Version::Lite05).unwrap();
754 buf
755 }
756
757 #[tokio::test(start_paused = true)]
758 async fn accept_request_reads_lite05_path() {
759 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some("/team/room"), None, None)]);
760 let request = Server::new().accept_request(session).await.unwrap();
761 assert_eq!(request.path(), "/team/room");
762 assert_eq!(request.role(), None, "a client that omits the role is bidirectional");
763 }
764
765 #[tokio::test(start_paused = true)]
766 async fn accept_request_lite05_without_path_is_empty() {
767 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, None)]);
768 let request = Server::new().accept_request(session).await.unwrap();
769 assert_eq!(request.path(), "");
770 }
771
772 #[tokio::test(start_paused = true)]
773 async fn accept_request_lite05_empty_path_is_accepted() {
774 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some(""), None, None)]);
777 let request = Server::new().accept_request(session).await.unwrap();
778 assert_eq!(request.path(), "");
779 }
780
781 #[tokio::test(start_paused = true)]
782 async fn accept_request_reads_lite05_role() {
783 let session = FakeSession::new(
784 ALPN_LITE_05,
785 [lite05_setup(Some("/team/room"), Some(Role::Publisher), None)],
786 );
787 let request = Server::new().accept_request(session).await.unwrap();
788 assert_eq!(request.role(), Some(Role::Publisher));
789 }
790
791 #[tokio::test(start_paused = true)]
792 async fn accept_request_skips_uni_stream_before_setup() {
793 let session = FakeSession::new(
796 ALPN_LITE_05,
797 [lite05_group(), lite05_setup(Some("/team/room"), None, None)],
798 );
799 let request = Server::new().accept_request(session).await.unwrap();
800 assert_eq!(request.path(), "/team/room");
801 }
802
803 #[tokio::test(start_paused = true)]
804 async fn accept_request_reads_lite05_peer_origin() {
805 let origin = Origin::new(42).unwrap();
806 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, Some(origin))]);
807 let request = Server::new().accept_request(session).await.unwrap();
808 assert_eq!(request.peer_origin(), Some(origin));
809 }
810}