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) = 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 (path, request_id_max)
214 }
215 Version::Lite(_) => (None, None),
216 };
217
218 Ok(Request {
219 path,
220 role: None,
221 origin: None,
222 inner: Some(RequestInner {
223 server: self.clone(),
224 handshake: Handshake::Legacy {
225 session,
226 stream,
227 version,
228 request_id_max,
229 },
230 }),
231 })
232 }
233
234 async fn accept_ietf_modern<S: web_transport_trait::Session>(
237 &self,
238 session: S,
239 version: ietf::Version,
240 ) -> Result<Request<S>, Error> {
241 let peer_setup = ietf::accept_setup(&session, version).await?;
242 Ok(Request {
243 path: peer_setup.path.clone(),
244 role: None,
245 origin: peer_setup.cluster.origin.filter(|o| *o != crate::Origin::UNKNOWN),
248 inner: Some(RequestInner {
249 server: self.clone(),
250 handshake: Handshake::IetfModern {
251 session,
252 version,
253 peer_setup,
254 },
255 }),
256 })
257 }
258}
259
260pub struct Request<S: web_transport_trait::Session> {
268 path: Option<String>,
269 role: Option<Role>,
270 origin: Option<crate::Origin>,
271 inner: Option<RequestInner<S>>,
273}
274
275struct RequestInner<S: web_transport_trait::Session> {
277 server: Server,
278 handshake: Handshake<S>,
279}
280
281enum Handshake<S: web_transport_trait::Session> {
284 IetfModern {
288 session: S,
289 version: ietf::Version,
290 peer_setup: ietf::PeerSetup<S>,
291 },
292 LiteBare { session: S, version: lite::Version },
294 Legacy {
298 session: S,
299 stream: Stream<S, Version>,
300 version: Version,
301 request_id_max: Option<ietf::RequestId>,
302 },
303 LiteSetup {
306 session: S,
307 version: lite::Version,
308 client_setup: lite::Setup,
309 },
310}
311
312impl<S: web_transport_trait::Session> Request<S> {
313 pub fn path(&self) -> &str {
320 self.path.as_deref().unwrap_or("")
321 }
322
323 pub fn role(&self) -> Option<Role> {
332 self.role
333 }
334
335 pub fn peer_origin(&self) -> Option<crate::Origin> {
344 self.origin
345 }
346
347 pub fn with_publisher(mut self, publish: impl Consume<origin::Consumer>) -> Self {
350 self.inner_mut().server.publish = Some(publish.consume());
351 self
352 }
353
354 pub fn with_subscriber(mut self, subscribe: origin::Producer) -> Self {
356 self.inner_mut().server.subscribe = Some(subscribe);
357 self
358 }
359
360 pub fn with_stats(mut self, stats: stats::Session) -> Self {
363 self.inner_mut().server.stats = stats;
364 self
365 }
366
367 fn inner_mut(&mut self) -> &mut RequestInner<S> {
368 self.inner.as_mut().expect("request already responded")
369 }
370
371 pub async fn ok(mut self) -> Result<(Session, Driver), Error> {
374 let RequestInner { server, handshake } = self.inner.take().expect("request already responded");
375
376 let publish = server.publish.map(|origin| origin.with_stats(server.stats.clone()));
380 let subscribe = server.subscribe.map(|origin| origin.with_stats(server.stats.clone()));
381
382 let (session, mut stream, version, request_id_max) = match handshake {
383 Handshake::IetfModern {
384 session,
385 version,
386 peer_setup,
387 } => {
388 let protocol = ietf::start(ietf::Config {
391 session: session.clone(),
392 setup: None,
393 request_id_max: None,
394 client: false,
395 publish,
396 subscribe,
397 peer_origin: None,
398 cost: None,
400 version,
401 path: None,
402 peer_setup_stream: Some(peer_setup.stream),
403 peer_cluster: Some(peer_setup.cluster),
404 })?;
405 tracing::debug!(?version, "connected");
406 return Ok(Session::new(session, version.into(), None, protocol));
407 }
408 Handshake::LiteBare { session, version } => {
409 let start = lite::start(
410 session.clone(),
411 None,
412 publish,
413 subscribe,
414 None,
415 version,
416 lite::Setup::default(),
417 None,
418 )?;
419 return Ok(Session::new(
420 session,
421 version.into(),
422 start.recv_bandwidth,
423 start.driver,
424 ));
425 }
426 Handshake::LiteSetup {
427 session,
428 version,
429 client_setup,
430 } => {
431 let our_setup = lite::Setup {
433 probe: lite::ProbeLevel::Report,
434 path: None,
435 role: None,
436 cost: None,
438 origin: None,
440 };
441 let start = lite::start(
442 session.clone(),
443 None,
444 publish,
445 subscribe,
446 None,
447 version,
448 our_setup,
449 Some(client_setup),
450 )?;
451 return Ok(Session::new(
452 session,
453 version.into(),
454 start.recv_bandwidth,
455 start.driver,
456 ));
457 }
458 Handshake::Legacy {
459 session,
460 stream,
461 version,
462 request_id_max,
463 } => (session, stream, version, request_id_max),
464 };
465
466 let parameters = match version {
468 Version::Ietf(v) => {
469 let mut parameters = ietf::Parameters::default();
470 parameters.set_varint(ietf::ParameterVarInt::MaxRequestId, u32::MAX as u64);
471 parameters.set_bytes(ietf::ParameterBytes::Implementation, b"moq-lite-rs".to_vec());
472 parameters.encode_bytes(v)?
473 }
474 Version::Lite(v) => lite::Parameters::default().encode_bytes(v)?,
475 };
476
477 let server_setup = setup::Server {
478 version: version.into(),
479 parameters,
480 };
481 stream.writer.encode(&server_setup).await?;
482
483 let (recv_bw, protocol) = match version {
484 Version::Lite(v) => {
485 let stream = stream.with_version(v);
486 let start = lite::start(
488 session.clone(),
489 Some(stream),
490 publish,
491 subscribe,
492 None,
493 v,
494 lite::Setup::default(),
495 None,
496 )?;
497 (start.recv_bandwidth, start.driver)
498 }
499 Version::Ietf(v) => {
500 let stream = stream.with_version(v);
501 let protocol = ietf::start(ietf::Config {
503 session: session.clone(),
504 setup: Some(stream),
505 request_id_max,
506 client: false,
507 publish,
508 subscribe,
509 peer_origin: None,
510 cost: None,
511 version: v,
512 path: None,
513 peer_setup_stream: None,
514 peer_cluster: None,
515 })?;
516 (None, protocol)
517 }
518 };
519
520 Ok(Session::new(session, version, recv_bw, protocol))
521 }
522
523 pub fn close(mut self, err: Error) {
525 let inner = self.inner.take().expect("request already responded");
526 inner.close(err);
527 }
528}
529
530impl<S: web_transport_trait::Session> RequestInner<S> {
531 fn close(self, err: Error) {
532 let session = match self.handshake {
533 Handshake::IetfModern { session, .. } => session,
534 Handshake::LiteBare { session, .. } => session,
535 Handshake::Legacy { session, .. } => session,
536 Handshake::LiteSetup { session, .. } => session,
537 };
538 session.close(err.to_code(), &err.to_string());
539 }
540}
541
542impl<S: web_transport_trait::Session> Drop for Request<S> {
543 fn drop(&mut self) {
546 if let Some(inner) = self.inner.take() {
547 tracing::warn!("Request dropped without ok() or close(); rejecting the session");
548 inner.close(Error::Cancel);
549 }
550 }
551}
552
553#[cfg(test)]
554mod tests {
555 use super::*;
556 use crate::Origin;
557 use std::{
558 collections::VecDeque,
559 sync::{Arc, Mutex},
560 };
561
562 use crate::ALPN_LITE_05;
563 use bytes::Bytes;
564
565 #[derive(Debug, Clone, Default)]
566 struct FakeError;
567 impl std::fmt::Display for FakeError {
568 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
569 write!(f, "fake transport error")
570 }
571 }
572 impl std::error::Error for FakeError {}
573 impl web_transport_trait::Error for FakeError {
574 fn session_error(&self) -> Option<(u32, String)> {
575 Some((0, "closed".to_string()))
576 }
577 }
578
579 #[derive(Clone)]
582 struct FakeSession {
583 protocol: Option<&'static str>,
584 uni: Arc<Mutex<VecDeque<Vec<u8>>>>,
585 }
586
587 impl FakeSession {
588 fn new(protocol: &'static str, uni: impl IntoIterator<Item = Vec<u8>>) -> Self {
589 Self {
590 protocol: Some(protocol),
591 uni: Arc::new(Mutex::new(uni.into_iter().collect())),
592 }
593 }
594 }
595
596 impl web_transport_trait::Session for FakeSession {
597 type SendStream = FakeSend;
598 type RecvStream = FakeRecv;
599 type Error = FakeError;
600
601 async fn accept_uni(&self) -> Result<Self::RecvStream, Self::Error> {
602 let data = self.uni.lock().unwrap().pop_front();
604 match data {
605 Some(data) => Ok(FakeRecv { data: data.into() }),
606 None => std::future::pending().await,
607 }
608 }
609 async fn accept_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error> {
610 std::future::pending().await
611 }
612 async fn open_bi(&self) -> Result<(Self::SendStream, Self::RecvStream), Self::Error> {
613 std::future::pending().await
614 }
615 async fn open_uni(&self) -> Result<Self::SendStream, Self::Error> {
616 std::future::pending().await
617 }
618 fn send_datagram(&self, _payload: Bytes) -> Result<(), Self::Error> {
619 Ok(())
620 }
621 async fn recv_datagram(&self) -> Result<Bytes, Self::Error> {
622 std::future::pending().await
623 }
624 fn max_datagram_size(&self) -> usize {
625 1200
626 }
627 fn protocol(&self) -> Option<&str> {
628 self.protocol
629 }
630 fn close(&self, _code: u32, _reason: &str) {}
631 async fn closed(&self) -> Self::Error {
632 std::future::pending().await
633 }
634 }
635
636 #[derive(Clone, Default)]
637 struct FakeSend;
638 impl web_transport_trait::SendStream for FakeSend {
639 type Error = FakeError;
640 async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
641 Ok(buf.len())
642 }
643 fn set_priority(&mut self, _order: u8) {}
644 fn finish(&mut self) -> Result<(), Self::Error> {
645 Ok(())
646 }
647 fn reset(&mut self, _code: u32) {}
648 async fn closed(&mut self) -> Result<(), Self::Error> {
649 Ok(())
650 }
651 }
652
653 struct FakeRecv {
654 data: VecDeque<u8>,
655 }
656 impl web_transport_trait::RecvStream for FakeRecv {
657 type Error = FakeError;
658 async fn read(&mut self, dst: &mut [u8]) -> Result<Option<usize>, Self::Error> {
659 if self.data.is_empty() {
660 return Ok(None);
661 }
662 let size = dst.len().min(self.data.len());
663 for slot in dst.iter_mut().take(size) {
664 *slot = self.data.pop_front().unwrap();
665 }
666 Ok(Some(size))
667 }
668 fn stop(&mut self, _code: u32) {}
669 async fn closed(&mut self) -> Result<(), Self::Error> {
670 Ok(())
671 }
672 }
673
674 fn lite05_setup(path: Option<&str>, role: Option<Role>, origin: Option<Origin>) -> Vec<u8> {
676 let v = lite::Version::Lite05;
677 let mut buf = Vec::new();
678 lite::DataType::Setup.encode(&mut buf, v).unwrap();
679 lite::Setup {
680 probe: lite::ProbeLevel::None,
681 path: path.map(str::to_string),
682 role,
683 cost: None,
684 origin,
685 }
686 .encode(&mut buf, v)
687 .unwrap();
688 buf
689 }
690
691 fn ietf_setup(version: ietf::Version, path: Option<&str>) -> Vec<u8> {
694 let mut params = ietf::Parameters::default();
695 if let Some(path) = path {
696 params.set_bytes(ietf::ParameterBytes::Path, path.as_bytes().to_vec());
697 }
698 let parameters = params.encode_bytes(version).unwrap();
699
700 let mut buf = Vec::new();
701 setup::Setup { parameters }
702 .encode(&mut buf, crate::Version::Ietf(version))
703 .unwrap();
704 buf
705 }
706
707 #[tokio::test(start_paused = true)]
708 async fn accept_request_reads_ietf_path() {
709 for (alpn, version) in [
712 (ALPN_17, ietf::Version::Draft17),
713 (ALPN_18, ietf::Version::Draft18),
714 (ALPN_19, ietf::Version::Draft19),
715 ] {
716 let session = FakeSession::new(alpn, [ietf_setup(version, Some("/team/room"))]);
717 let request = Server::new().accept_request(session).await.unwrap();
718 assert_eq!(request.path(), "/team/room", "{alpn}");
719 }
720 }
721
722 #[tokio::test(start_paused = true)]
723 async fn accept_request_ietf_without_path_is_empty() {
724 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, None)]);
725 let request = Server::new().accept_request(session).await.unwrap();
726 assert_eq!(request.path(), "");
727 }
728
729 #[tokio::test(start_paused = true)]
730 async fn accept_request_ietf_empty_path_is_accepted() {
731 let session = FakeSession::new(ALPN_19, [ietf_setup(ietf::Version::Draft19, Some(""))]);
732 let request = Server::new().accept_request(session).await.unwrap();
733 assert_eq!(request.path(), "");
734 }
735
736 fn lite05_group() -> Vec<u8> {
738 let mut buf = Vec::new();
739 lite::DataType::Group.encode(&mut buf, lite::Version::Lite05).unwrap();
740 buf
741 }
742
743 #[tokio::test(start_paused = true)]
744 async fn accept_request_reads_lite05_path() {
745 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some("/team/room"), None, None)]);
746 let request = Server::new().accept_request(session).await.unwrap();
747 assert_eq!(request.path(), "/team/room");
748 assert_eq!(request.role(), None, "a client that omits the role is bidirectional");
749 }
750
751 #[tokio::test(start_paused = true)]
752 async fn accept_request_lite05_without_path_is_empty() {
753 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, None)]);
754 let request = Server::new().accept_request(session).await.unwrap();
755 assert_eq!(request.path(), "");
756 }
757
758 #[tokio::test(start_paused = true)]
759 async fn accept_request_lite05_empty_path_is_accepted() {
760 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(Some(""), None, None)]);
763 let request = Server::new().accept_request(session).await.unwrap();
764 assert_eq!(request.path(), "");
765 }
766
767 #[tokio::test(start_paused = true)]
768 async fn accept_request_reads_lite05_role() {
769 let session = FakeSession::new(
770 ALPN_LITE_05,
771 [lite05_setup(Some("/team/room"), Some(Role::Publisher), None)],
772 );
773 let request = Server::new().accept_request(session).await.unwrap();
774 assert_eq!(request.role(), Some(Role::Publisher));
775 }
776
777 #[tokio::test(start_paused = true)]
778 async fn accept_request_skips_uni_stream_before_setup() {
779 let session = FakeSession::new(
782 ALPN_LITE_05,
783 [lite05_group(), lite05_setup(Some("/team/room"), None, None)],
784 );
785 let request = Server::new().accept_request(session).await.unwrap();
786 assert_eq!(request.path(), "/team/room");
787 }
788
789 #[tokio::test(start_paused = true)]
790 async fn accept_request_reads_lite05_peer_origin() {
791 let origin = Origin::new(42).unwrap();
792 let session = FakeSession::new(ALPN_LITE_05, [lite05_setup(None, None, Some(origin))]);
793 let request = Server::new().accept_request(session).await.unwrap();
794 assert_eq!(request.peer_origin(), Some(origin));
795 }
796}