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