1use std::net;
2#[cfg(any(test, all(feature = "uds", unix)))]
3use std::path::PathBuf;
4
5use crate::{Error, QuicBackend};
6use moq_net::Session;
7use std::sync::{Arc, RwLock};
8use url::Url;
9#[cfg(feature = "iroh")]
10use web_transport_iroh::iroh;
11
12use futures::FutureExt;
13use futures::future::BoxFuture;
14use futures::stream::FuturesUnordered;
15use futures::stream::StreamExt;
16
17#[derive(clap::Args, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
19#[serde(deny_unknown_fields, default)]
20#[non_exhaustive]
21pub struct ServerConfig {
22 #[serde(alias = "listen")]
30 #[arg(id = "server-bind", long = "server-bind", alias = "listen", env = "MOQ_SERVER_BIND")]
31 pub bind: Option<String>,
32
33 #[cfg(feature = "tcp")]
36 #[command(flatten)]
37 #[serde(default)]
38 pub tcp: TcpConfig,
39
40 #[cfg(all(feature = "uds", unix))]
43 #[command(flatten)]
44 #[serde(default)]
45 pub unix: UnixConfig,
46
47 #[arg(id = "server-backend", long = "server-backend", env = "MOQ_SERVER_BACKEND")]
50 pub backend: Option<QuicBackend>,
51
52 #[command(flatten)]
55 #[serde(default)]
56 pub quic: crate::quic::Server,
57
58 #[serde(default, skip_serializing_if = "Vec::is_empty")]
66 #[arg(id = "server-version", long = "server-version", env = "MOQ_SERVER_VERSION")]
67 pub version: Vec<moq_net::Version>,
68
69 #[command(flatten)]
70 #[serde(default)]
71 pub tls: crate::tls::Server,
72}
73
74#[cfg(feature = "tcp")]
80#[derive(clap::Args, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
81#[serde(deny_unknown_fields, default)]
82#[non_exhaustive]
83pub struct TcpConfig {
84 #[arg(long = "server-tcp-bind", id = "server-tcp-bind", env = "MOQ_SERVER_TCP_BIND")]
86 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub bind: Option<net::SocketAddr>,
88}
89
90#[cfg(all(feature = "uds", unix))]
93#[derive(clap::Args, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
94#[serde(deny_unknown_fields, default)]
95#[non_exhaustive]
96pub struct UnixConfig {
97 #[arg(long = "server-unix-bind", id = "server-unix-bind", env = "MOQ_SERVER_UNIX_BIND")]
99 #[serde(default, skip_serializing_if = "Option::is_none")]
100 pub bind: Option<PathBuf>,
101
102 #[command(flatten)]
105 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub allow: Option<UnixAllow>,
107}
108
109#[cfg(all(feature = "uds", unix))]
115#[derive(clap::Args, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
116#[serde(deny_unknown_fields, default)]
117#[non_exhaustive]
118pub struct UnixAllow {
119 #[arg(
121 long = "server-unix-allow-uid",
122 env = "MOQ_SERVER_UNIX_ALLOW_UID",
123 value_delimiter = ','
124 )]
125 #[serde(default, skip_serializing_if = "Vec::is_empty")]
126 pub uid: Vec<u32>,
127
128 #[arg(
130 long = "server-unix-allow-gid",
131 env = "MOQ_SERVER_UNIX_ALLOW_GID",
132 value_delimiter = ','
133 )]
134 #[serde(default, skip_serializing_if = "Vec::is_empty")]
135 pub gid: Vec<u32>,
136
137 #[arg(
140 long = "server-unix-allow-pid",
141 env = "MOQ_SERVER_UNIX_ALLOW_PID",
142 value_delimiter = ','
143 )]
144 #[serde(default, skip_serializing_if = "Vec::is_empty")]
145 pub pid: Vec<i32>,
146}
147
148#[cfg(all(feature = "uds", unix))]
149impl UnixAllow {
150 fn is_empty(&self) -> bool {
152 self.uid.is_empty() && self.gid.is_empty() && self.pid.is_empty()
153 }
154
155 fn permits(&self, cred: &crate::unix::PeerCred) -> bool {
159 let uid_ok = self.uid.is_empty() || self.uid.contains(&cred.uid);
160 let gid_ok = self.gid.is_empty() || self.gid.contains(&cred.gid);
161 let pid_ok = self.pid.is_empty() || cred.pid.is_some_and(|pid| self.pid.contains(&pid));
162 uid_ok && gid_ok && pid_ok
163 }
164}
165
166impl ServerConfig {
167 pub fn init(self) -> crate::Result<Server> {
168 Server::new(self)
169 }
170
171 pub fn versions(&self) -> moq_net::Versions {
173 if self.version.is_empty() {
174 moq_net::Versions::all()
175 } else {
176 moq_net::Versions::from(self.version.clone())
177 }
178 }
179
180 #[allow(unused_mut)]
185 fn has_stream_listener(&self) -> bool {
186 let mut has = false;
187 #[cfg(feature = "tcp")]
188 {
189 has |= self.tcp.bind.is_some();
190 }
191 #[cfg(all(feature = "uds", unix))]
192 {
193 has |= self.unix.bind.is_some();
194 }
195 has
196 }
197}
198
199pub(crate) const DEFAULT_BIND: &str = "[::]:443";
201
202pub struct Server {
208 moq: moq_net::Server,
209 versions: moq_net::Versions,
210 accept: FuturesUnordered<BoxFuture<'static, crate::Result<Request>>>,
211 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
212 streams: StreamListeners,
213 #[cfg(feature = "iroh")]
214 iroh: Option<iroh::Endpoint>,
215 #[cfg(feature = "noq")]
216 noq: Option<crate::noq::NoqServer>,
217 #[cfg(feature = "quinn")]
218 quinn: Option<crate::quinn::QuinnServer>,
219 #[cfg(feature = "quiche")]
220 quiche: Option<crate::quiche::QuicheServer>,
221 #[cfg(feature = "websocket")]
222 websocket: Option<crate::websocket::Listener>,
223}
224
225impl Server {
226 pub fn new(config: ServerConfig) -> crate::Result<Self> {
227 let backend = config.backend.clone().unwrap_or({
228 #[cfg(feature = "quinn")]
229 {
230 QuicBackend::Quinn
231 }
232 #[cfg(all(feature = "noq", not(feature = "quinn")))]
233 {
234 QuicBackend::Noq
235 }
236 #[cfg(all(feature = "quiche", not(feature = "quinn"), not(feature = "noq")))]
237 {
238 QuicBackend::Quiche
239 }
240 #[cfg(all(not(feature = "quiche"), not(feature = "quinn"), not(feature = "noq")))]
241 panic!("no QUIC backend compiled; enable noq, quinn, or quiche feature");
242 });
243
244 let versions = config.versions();
245
246 let build_quic = config.bind.is_some() || !config.has_stream_listener();
250
251 if build_quic && !config.tls.root.is_empty() {
252 let mtls_supported = match backend {
253 #[cfg(feature = "quinn")]
254 QuicBackend::Quinn => true,
255 #[cfg(feature = "noq")]
256 QuicBackend::Noq => true,
257 #[allow(unreachable_patterns)]
258 _ => false,
259 };
260 if !mtls_supported {
261 return Err(Error::MtlsUnsupported);
262 }
263 }
264
265 #[cfg(feature = "noq")]
266 #[allow(unreachable_patterns)]
267 let noq = match backend {
268 QuicBackend::Noq if build_quic => Some(crate::noq::NoqServer::new(config.clone())?),
269 _ => None,
270 };
271
272 #[cfg(feature = "quinn")]
273 #[allow(unreachable_patterns)]
274 let quinn = match backend {
275 QuicBackend::Quinn if build_quic => Some(crate::quinn::QuinnServer::new(config.clone())?),
276 _ => None,
277 };
278
279 #[cfg(feature = "quiche")]
280 let quiche = match backend {
281 QuicBackend::Quiche if build_quic => Some(crate::quiche::QuicheServer::new(config.clone())?),
282 _ => None,
283 };
284
285 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
287 let mut stream_binds = Vec::new();
288 #[cfg(feature = "tcp")]
289 if let Some(addr) = config.tcp.bind {
290 stream_binds.push(StreamBind::Tcp(addr));
291 }
292 #[cfg(all(feature = "uds", unix))]
293 if let Some(path) = config.unix.bind.clone() {
294 stream_binds.push(StreamBind::Unix(path));
295 }
296 #[cfg(all(feature = "uds", unix))]
298 let unix_allow = config.unix.allow.clone().filter(|allow| !allow.is_empty());
299 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
300 let streams = StreamListeners::new(
301 stream_binds,
302 stream_versions(&versions),
303 #[cfg(all(feature = "uds", unix))]
304 unix_allow,
305 );
306
307 Ok(Server {
308 accept: Default::default(),
309 moq: moq_net::Server::new().with_versions(versions.clone()),
310 versions,
311 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
312 streams,
313 #[cfg(feature = "iroh")]
314 iroh: None,
315 #[cfg(feature = "noq")]
316 noq,
317 #[cfg(feature = "quinn")]
318 quinn,
319 #[cfg(feature = "quiche")]
320 quiche,
321 #[cfg(feature = "websocket")]
322 websocket: None,
323 })
324 }
325
326 #[cfg(feature = "websocket")]
332 pub fn with_websocket(mut self, websocket: Option<crate::websocket::Listener>) -> Self {
333 self.websocket = websocket;
334 self
335 }
336
337 #[cfg(feature = "iroh")]
338 pub fn with_iroh(mut self, iroh: Option<iroh::Endpoint>) -> Self {
339 self.iroh = iroh;
340 self
341 }
342
343 pub fn with_publish(mut self, publish: impl Into<Option<moq_net::OriginConsumer>>) -> Self {
344 self.moq = self.moq.with_publish(publish);
345 self
346 }
347
348 pub fn with_consume(mut self, consume: impl Into<Option<moq_net::OriginProducer>>) -> Self {
349 self.moq = self.moq.with_consume(consume);
350 self
351 }
352
353 pub fn with_stats(mut self, stats: moq_net::StatsHandle) -> Self {
355 self.moq = self.moq.with_stats(stats);
356 self
357 }
358
359 pub async fn serve_publish(self, origin: moq_net::OriginConsumer) -> crate::Result<()> {
366 self.with_publish(origin).serve().await
367 }
368
369 pub async fn serve_consume(self, origin: moq_net::OriginProducer) -> crate::Result<()> {
373 self.with_consume(origin).serve().await
374 }
375
376 async fn serve(mut self) -> crate::Result<()> {
379 if let Ok(addr) = self.local_addr() {
380 tracing::info!(%addr, "listening");
381 }
382 while let Some(request) = self.accept().await {
383 tokio::spawn(async move {
384 if let Err(err) = serve_session(request).await {
385 tracing::warn!(%err, "session ended with error");
386 }
387 });
388 }
389 Ok(())
390 }
391
392 pub fn tls_info(&self) -> Arc<RwLock<crate::tls::Info>> {
394 #[cfg(feature = "noq")]
395 if let Some(noq) = self.noq.as_ref() {
396 return noq.tls_info();
397 }
398 #[cfg(feature = "quinn")]
399 if let Some(quinn) = self.quinn.as_ref() {
400 return quinn.tls_info();
401 }
402 #[cfg(feature = "quiche")]
403 if let Some(quiche) = self.quiche.as_ref() {
404 return quiche.tls_info();
405 }
406 Arc::new(RwLock::new(crate::tls::Info::empty()))
408 }
409
410 #[cfg(not(any(
411 feature = "noq",
412 feature = "quinn",
413 feature = "quiche",
414 feature = "iroh",
415 feature = "tcp",
416 all(feature = "uds", unix)
417 )))]
418 pub async fn accept(&mut self) -> Option<Request> {
419 unreachable!("no transport compiled; enable a QUIC backend, tcp, or uds feature");
420 }
421
422 #[cfg(any(
429 feature = "noq",
430 feature = "quinn",
431 feature = "quiche",
432 feature = "iroh",
433 feature = "tcp",
434 all(feature = "uds", unix)
435 ))]
436 pub async fn accept(&mut self) -> Option<Request> {
437 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
440 if let Err(err) = self.streams.ensure_started().await {
441 tracing::error!(%err, "failed to bind stream listener");
442 return None;
443 }
444
445 loop {
446 #[cfg(feature = "noq")]
448 let noq_accept = async {
449 #[cfg(feature = "noq")]
450 if let Some(noq) = self.noq.as_mut() {
451 return noq.accept().await;
452 }
453 None
454 };
455 #[cfg(not(feature = "noq"))]
456 let noq_accept = async { None::<()> };
457
458 #[cfg(feature = "iroh")]
459 let iroh_accept = async {
460 #[cfg(feature = "iroh")]
461 if let Some(endpoint) = self.iroh.as_mut() {
462 return endpoint.accept().await;
463 }
464 None
465 };
466 #[cfg(not(feature = "iroh"))]
467 let iroh_accept = async { None::<()> };
468
469 #[cfg(feature = "quinn")]
470 let quinn_accept = async {
471 #[cfg(feature = "quinn")]
472 if let Some(quinn) = self.quinn.as_mut() {
473 return quinn.accept().await;
474 }
475 None
476 };
477 #[cfg(not(feature = "quinn"))]
478 let quinn_accept = async { None::<()> };
479
480 #[cfg(feature = "quiche")]
481 let quiche_accept = async {
482 #[cfg(feature = "quiche")]
483 if let Some(quiche) = self.quiche.as_mut() {
484 return quiche.accept().await;
485 }
486 None
487 };
488 #[cfg(not(feature = "quiche"))]
489 let quiche_accept = async { None::<()> };
490
491 #[cfg(feature = "websocket")]
492 let ws_ref = self.websocket.as_ref();
493 #[cfg(feature = "websocket")]
494 let ws_accept = async {
495 match ws_ref {
496 Some(ws) => ws.accept().await,
497 None => std::future::pending().await,
498 }
499 };
500 #[cfg(not(feature = "websocket"))]
501 let ws_accept = std::future::pending::<Option<crate::Result<()>>>();
502
503 #[allow(unused_variables)]
504 let server = self.moq.clone();
505 #[allow(unused_variables)]
506 let versions = self.versions.clone();
507
508 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
510 let stream_accept = self.streams.recv();
511 #[cfg(not(any(feature = "tcp", all(feature = "uds", unix))))]
512 let stream_accept = std::future::pending::<Option<Request>>();
513
514 tokio::select! {
515 Some(request) = stream_accept => {
516 return Some(request);
517 }
518 Some(_conn) = noq_accept => {
519 #[cfg(feature = "noq")]
520 {
521 let alpns = versions.alpns();
522 self.accept.push(async move {
523 let noq = super::noq::NoqRequest::accept(_conn, alpns).await?;
524 Ok(Request {
525 server,
526 kind: RequestKind::Noq(Box::new(noq)),
527 })
528 }.boxed());
529 }
530 }
531 Some(_conn) = quinn_accept => {
532 #[cfg(feature = "quinn")]
533 {
534 let alpns = versions.alpns();
535 self.accept.push(async move {
536 let quinn = super::quinn::QuinnRequest::accept(_conn, alpns).await?;
537 Ok(Request {
538 server,
539 kind: RequestKind::Quinn(Box::new(quinn)),
540 })
541 }.boxed());
542 }
543 }
544 Some(_conn) = quiche_accept => {
545 #[cfg(feature = "quiche")]
546 {
547 let alpns = versions.alpns();
548 self.accept.push(async move {
549 let quiche = super::quiche::QuicheRequest::accept(_conn, alpns).await?;
550 Ok(Request {
551 server,
552 kind: RequestKind::Quiche(Box::new(quiche)),
553 })
554 }.boxed());
555 }
556 }
557 Some(_conn) = iroh_accept => {
558 #[cfg(feature = "iroh")]
559 self.accept.push(async move {
560 let iroh = super::iroh::Request::accept(_conn).await?;
561 Ok(Request {
562 server,
563 kind: RequestKind::Iroh(Box::new(iroh)),
564 })
565 }.boxed());
566 }
567 Some(_res) = ws_accept => {
568 #[cfg(feature = "websocket")]
569 match _res {
570 Ok(session) => {
571 return Some(Request {
572 server,
573 kind: RequestKind::WebSocket(Box::new(session)),
574 });
575 }
576 Err(err) => tracing::debug!(%err, "failed to accept WebSocket session"),
577 }
578 }
579 Some(res) = self.accept.next() => {
580 match res {
581 Ok(session) => return Some(session),
582 Err(err) => tracing::debug!(%err, "failed to accept session"),
583 }
584 }
585 _ = tokio::signal::ctrl_c() => {
586 self.close().await;
587 return None;
588 }
589 }
590 }
591 }
592
593 #[cfg(feature = "iroh")]
594 pub fn iroh_endpoint(&self) -> Option<&iroh::Endpoint> {
595 self.iroh.as_ref()
596 }
597
598 pub fn local_addr(&self) -> crate::Result<net::SocketAddr> {
599 #[cfg(feature = "noq")]
600 if let Some(noq) = self.noq.as_ref() {
601 return Ok(noq.local_addr()?);
602 }
603 #[cfg(feature = "quinn")]
604 if let Some(quinn) = self.quinn.as_ref() {
605 return Ok(quinn.local_addr()?);
606 }
607 #[cfg(feature = "quiche")]
608 if let Some(quiche) = self.quiche.as_ref() {
609 return Ok(quiche.local_addr()?);
610 }
611 Err(Error::NoBackend("no QUIC listener configured"))
613 }
614
615 #[cfg(feature = "websocket")]
616 pub fn websocket_local_addr(&self) -> Option<net::SocketAddr> {
617 self.websocket.as_ref().and_then(|ws| ws.local_addr().ok())
618 }
619
620 pub async fn close(&mut self) {
621 #[cfg(feature = "noq")]
622 if let Some(noq) = self.noq.as_mut() {
623 noq.close();
624 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
625 }
626 #[cfg(feature = "quinn")]
627 if let Some(quinn) = self.quinn.as_mut() {
628 quinn.close();
629 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
630 }
631 #[cfg(feature = "quiche")]
632 if let Some(quiche) = self.quiche.as_mut() {
633 quiche.close();
634 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
635 }
636 #[cfg(feature = "iroh")]
637 if let Some(iroh) = self.iroh.take() {
638 iroh.close().await;
639 }
640 #[cfg(feature = "websocket")]
641 {
642 let _ = self.websocket.take();
643 }
644 #[cfg(not(any(feature = "noq", feature = "quinn", feature = "quiche", feature = "iroh")))]
645 unreachable!("no QUIC backend compiled");
646 }
647}
648
649async fn serve_session(request: Request) -> crate::Result<()> {
651 let session = request.ok().await?;
652 session.closed().await?;
653 Ok(())
654}
655
656#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
663fn stream_versions(base: &moq_net::Versions) -> moq_net::Versions {
664 let mut versions: Vec<moq_net::Version> = base.iter().copied().collect();
665 if let Ok(lite05) = "moq-lite-05-wip".parse::<moq_net::Version>() {
666 if !versions.contains(&lite05) {
667 versions.push(lite05);
668 }
669 }
670 moq_net::Versions::from(versions)
671}
672
673#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
675enum StreamBind {
676 #[cfg(feature = "tcp")]
677 Tcp(net::SocketAddr),
678 #[cfg(all(feature = "uds", unix))]
679 Unix(PathBuf),
680}
681
682#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
689struct StreamListeners {
690 binds: Vec<StreamBind>,
691 versions: moq_net::Versions,
692 #[cfg(all(feature = "uds", unix))]
693 unix_allow: Option<UnixAllow>,
694 rx: Option<tokio::sync::mpsc::Receiver<Request>>,
695 tasks: Vec<tokio::task::JoinHandle<()>>,
696}
697
698#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
699impl StreamListeners {
700 fn new(
701 binds: Vec<StreamBind>,
702 versions: moq_net::Versions,
703 #[cfg(all(feature = "uds", unix))] unix_allow: Option<UnixAllow>,
704 ) -> Self {
705 Self {
706 binds,
707 versions,
708 #[cfg(all(feature = "uds", unix))]
709 unix_allow,
710 rx: None,
711 tasks: Vec::new(),
712 }
713 }
714
715 async fn ensure_started(&mut self) -> crate::Result<()> {
717 if self.rx.is_some() || self.binds.is_empty() {
718 return Ok(());
719 }
720
721 let (tx, rx) = tokio::sync::mpsc::channel(16);
722 for bind in self.binds.drain(..) {
723 let versions = self.versions.clone();
724 match bind {
725 #[cfg(feature = "tcp")]
726 StreamBind::Tcp(addr) => {
727 if !addr.ip().is_loopback() {
728 tracing::warn!(%addr, "tcp listener bound to a non-loopback address; qmux is UNENCRYPTED, ensure the network is trusted");
729 }
730 let listener = crate::tcp::Listener::bind(addr).await?.with_protocols(versions.alpns());
731 tracing::info!(%addr, "listening (tcp)");
732 self.tasks.push(spawn_tcp_loop(listener, versions, tx.clone()));
733 }
734 #[cfg(all(feature = "uds", unix))]
735 StreamBind::Unix(path) => {
736 let listener = crate::unix::Listener::bind(&path)
737 .await?
738 .with_protocols(versions.alpns());
739 listener.set_mode(0o666)?;
742 tracing::info!(path = %path.display(), allow = ?self.unix_allow, "listening (unix)");
743 self.tasks
744 .push(spawn_unix_loop(listener, versions, self.unix_allow.clone(), tx.clone()));
745 }
746 }
747 }
748
749 self.rx = Some(rx);
750 Ok(())
751 }
752
753 async fn recv(&mut self) -> Option<Request> {
755 match self.rx.as_mut() {
756 Some(rx) => rx.recv().await,
757 None => std::future::pending().await,
758 }
759 }
760}
761
762#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
763impl Drop for StreamListeners {
764 fn drop(&mut self) {
765 for task in &self.tasks {
767 task.abort();
768 }
769 }
770}
771
772#[cfg(feature = "tcp")]
773fn spawn_tcp_loop(
774 listener: crate::tcp::Listener,
775 versions: moq_net::Versions,
776 tx: tokio::sync::mpsc::Sender<Request>,
777) -> tokio::task::JoinHandle<()> {
778 tokio::spawn(async move {
779 loop {
780 match listener.accept().await {
781 Some(Ok(session)) => spawn_stream_request(session, "tcp", versions.clone(), tx.clone()),
782 Some(Err(err)) => tracing::warn!(%err, "tcp listener accept failed"),
783 None => break,
784 }
785 }
786 })
787}
788
789#[cfg(all(feature = "uds", unix))]
790fn spawn_unix_loop(
791 listener: crate::unix::Listener,
792 versions: moq_net::Versions,
793 allow: Option<UnixAllow>,
794 tx: tokio::sync::mpsc::Sender<Request>,
795) -> tokio::task::JoinHandle<()> {
796 tokio::spawn(async move {
797 loop {
798 match listener.accept().await {
799 Some(Ok((session, cred))) => {
800 if let Some(allow) = &allow
802 && !allow.permits(&cred)
803 {
804 tracing::warn!(uid = cred.uid, gid = cred.gid, pid = ?cred.pid, "unix connection rejected by allow list");
805 continue;
806 }
807 spawn_stream_request(session, "unix", versions.clone(), tx.clone());
808 }
809 Some(Err(err)) => tracing::warn!(%err, "unix listener accept failed"),
810 None => break,
811 }
812 }
813 })
814}
815
816#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
819fn spawn_stream_request(
820 session: qmux::Session,
821 transport: &'static str,
822 versions: moq_net::Versions,
823 tx: tokio::sync::mpsc::Sender<Request>,
824) {
825 tokio::spawn(async move {
826 let server = moq_net::Server::new().with_versions(versions);
827 match server.accept_request(session).await {
828 Ok(request) => {
829 let request = Request {
830 server: moq_net::Server::new(),
831 kind: RequestKind::Stream(Box::new(StreamRequest { request, transport })),
832 };
833 let _ = tx.send(request).await;
834 }
835 Err(err) => tracing::debug!(%err, "stream SETUP handshake failed"),
836 }
837 });
838}
839
840#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
843pub(crate) struct StreamRequest {
844 request: moq_net::Request<qmux::Session>,
845 transport: &'static str,
846}
847
848pub(crate) enum RequestKind {
850 #[cfg(feature = "noq")]
851 Noq(Box<crate::noq::NoqRequest>),
852 #[cfg(feature = "quinn")]
853 Quinn(Box<crate::quinn::QuinnRequest>),
854 #[cfg(feature = "quiche")]
855 Quiche(Box<crate::quiche::QuicheRequest>),
856 #[cfg(feature = "iroh")]
857 Iroh(Box<crate::iroh::Request>),
858 #[cfg(feature = "websocket")]
859 WebSocket(Box<qmux::Session>),
860 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
861 Stream(Box<StreamRequest>),
862}
863
864pub struct Request {
869 server: moq_net::Server,
870 kind: RequestKind,
871}
872
873impl Request {
874 pub async fn close(self, _code: u16) -> crate::Result<()> {
876 match self.kind {
877 #[cfg(feature = "noq")]
878 RequestKind::Noq(request) => {
879 let status =
880 web_transport_noq::http::StatusCode::from_u16(_code).map_err(|_| Error::InvalidStatusCode)?;
881 request.close(status).await.map_err(crate::noq::Error::Server)?;
882 Ok(())
883 }
884 #[cfg(feature = "quinn")]
885 RequestKind::Quinn(request) => {
886 let status =
887 web_transport_quinn::http::StatusCode::from_u16(_code).map_err(|_| Error::InvalidStatusCode)?;
888 request.close(status).await.map_err(crate::quinn::Error::Server)?;
889 Ok(())
890 }
891 #[cfg(feature = "quiche")]
892 RequestKind::Quiche(request) => {
893 let status =
894 web_transport_quiche::http::StatusCode::from_u16(_code).map_err(|_| Error::InvalidStatusCode)?;
895 request.reject(status).await.map_err(crate::quiche::Error::Reject)?;
896 Ok(())
897 }
898 #[cfg(feature = "iroh")]
899 RequestKind::Iroh(request) => {
900 let status =
901 web_transport_iroh::http::StatusCode::from_u16(_code).map_err(|_| Error::InvalidStatusCode)?;
902 request.close(status).await.map_err(crate::iroh::Error::Server)?;
903 Ok(())
904 }
905 #[cfg(feature = "websocket")]
906 RequestKind::WebSocket(_session) => {
907 Ok(())
909 }
910 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
911 RequestKind::Stream(stream) => {
912 let err = match _code {
915 401 | 403 => moq_net::Error::Unauthorized,
916 other => moq_net::Error::App(other),
917 };
918 stream.request.close(err);
919 Ok(())
920 }
921 }
922 }
923
924 pub fn with_publish(self, publish: impl Into<Option<moq_net::OriginConsumer>>) -> Self {
926 let Request { server, kind } = self;
927 match kind {
928 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
929 RequestKind::Stream(stream) => {
930 let StreamRequest { request, transport } = *stream;
931 Request {
932 server,
933 kind: RequestKind::Stream(Box::new(StreamRequest {
934 request: request.with_publish(publish),
935 transport,
936 })),
937 }
938 }
939 kind => Request {
940 server: server.with_publish(publish),
941 kind,
942 },
943 }
944 }
945
946 pub fn with_consume(self, consume: impl Into<Option<moq_net::OriginProducer>>) -> Self {
948 let Request { server, kind } = self;
949 match kind {
950 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
951 RequestKind::Stream(stream) => {
952 let StreamRequest { request, transport } = *stream;
953 Request {
954 server,
955 kind: RequestKind::Stream(Box::new(StreamRequest {
956 request: request.with_consume(consume),
957 transport,
958 })),
959 }
960 }
961 kind => Request {
962 server: server.with_consume(consume),
963 kind,
964 },
965 }
966 }
967
968 pub fn with_stats(self, stats: moq_net::StatsHandle) -> Self {
970 let Request { server, kind } = self;
971 match kind {
972 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
973 RequestKind::Stream(stream) => {
974 let StreamRequest { request, transport } = *stream;
975 Request {
976 server,
977 kind: RequestKind::Stream(Box::new(StreamRequest {
978 request: request.with_stats(stats),
979 transport,
980 })),
981 }
982 }
983 kind => Request {
984 server: server.with_stats(stats),
985 kind,
986 },
987 }
988 }
989
990 pub async fn ok(self) -> crate::Result<Session> {
992 match self.kind {
993 #[cfg(feature = "noq")]
994 RequestKind::Noq(request) => Ok(self
995 .server
996 .accept(request.ok().await.map_err(crate::noq::Error::Server)?)
997 .await?),
998 #[cfg(feature = "quinn")]
999 RequestKind::Quinn(request) => Ok(self
1000 .server
1001 .accept(request.ok().await.map_err(crate::quinn::Error::Server)?)
1002 .await?),
1003 #[cfg(feature = "quiche")]
1004 RequestKind::Quiche(request) => {
1005 let conn = request.ok().await.map_err(crate::quiche::Error::Accept)?;
1006 Ok(self.server.accept(conn).await?)
1007 }
1008 #[cfg(feature = "iroh")]
1009 RequestKind::Iroh(request) => Ok(self
1010 .server
1011 .accept(request.ok().await.map_err(crate::iroh::Error::Server)?)
1012 .await?),
1013 #[cfg(feature = "websocket")]
1014 RequestKind::WebSocket(session) => Ok(self.server.accept(*session).await?),
1015 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1016 RequestKind::Stream(stream) => Ok(stream.request.ok().await?),
1017 }
1018 }
1019
1020 pub fn transport(&self) -> &'static str {
1022 match self.kind {
1023 #[cfg(feature = "noq")]
1024 RequestKind::Noq(_) => "quic",
1025 #[cfg(feature = "quinn")]
1026 RequestKind::Quinn(_) => "quic",
1027 #[cfg(feature = "quiche")]
1028 RequestKind::Quiche(_) => "quic",
1029 #[cfg(feature = "iroh")]
1030 RequestKind::Iroh(_) => "iroh",
1031 #[cfg(feature = "websocket")]
1032 RequestKind::WebSocket(_) => "websocket",
1033 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1034 RequestKind::Stream(ref stream) => stream.transport,
1035 }
1036 }
1037
1038 pub fn url(&self) -> Option<&Url> {
1043 #[cfg(not(any(
1044 feature = "noq",
1045 feature = "quinn",
1046 feature = "quiche",
1047 feature = "iroh",
1048 feature = "tcp",
1049 all(feature = "uds", unix)
1050 )))]
1051 unreachable!("no transport compiled; enable a QUIC backend, tcp, or uds feature");
1052
1053 #[allow(unreachable_code)]
1054 match self.kind {
1055 #[cfg(feature = "noq")]
1056 RequestKind::Noq(ref request) => request.url(),
1057 #[cfg(feature = "quinn")]
1058 RequestKind::Quinn(ref request) => request.url(),
1059 #[cfg(feature = "quiche")]
1060 RequestKind::Quiche(ref request) => request.url(),
1061 #[cfg(feature = "iroh")]
1062 RequestKind::Iroh(ref request) => request.url(),
1063 #[cfg(feature = "websocket")]
1064 RequestKind::WebSocket(_) => None,
1065 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1066 RequestKind::Stream(_) => None,
1067 }
1068 }
1069
1070 pub fn path(&self) -> Option<&str> {
1073 match self.kind {
1074 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1075 RequestKind::Stream(ref stream) => stream.request.path(),
1076 #[allow(unreachable_patterns)]
1077 _ => None,
1078 }
1079 }
1080
1081 pub fn peer_identity(&self) -> Option<crate::tls::PeerIdentity> {
1088 match self.kind {
1089 #[cfg(feature = "quinn")]
1090 RequestKind::Quinn(ref request) => request.peer_identity(),
1091 #[cfg(feature = "noq")]
1092 RequestKind::Noq(ref request) => request.peer_identity(),
1093 #[cfg(feature = "quiche")]
1094 RequestKind::Quiche(_) => None,
1095 #[cfg(feature = "iroh")]
1096 RequestKind::Iroh(_) => None,
1097 #[cfg(feature = "websocket")]
1098 RequestKind::WebSocket(_) => None,
1099 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1100 RequestKind::Stream(_) => None,
1101 #[cfg(not(any(
1102 feature = "noq",
1103 feature = "quinn",
1104 feature = "quiche",
1105 feature = "iroh",
1106 feature = "websocket",
1107 feature = "tcp",
1108 all(feature = "uds", unix)
1109 )))]
1110 _ => None,
1111 }
1112 }
1113
1114 #[deprecated(note = "use `peer_identity` instead")]
1116 pub fn has_peer_certificate(&self) -> bool {
1117 self.peer_identity().is_some()
1118 }
1119}
1120
1121#[serde_with::serde_as]
1123#[derive(Clone, serde::Serialize, serde::Deserialize)]
1124pub struct ServerId(#[serde_as(as = "serde_with::hex::Hex")] pub(crate) Vec<u8>);
1125
1126impl ServerId {
1127 #[allow(dead_code)]
1128 pub(crate) fn len(&self) -> usize {
1129 self.0.len()
1130 }
1131}
1132
1133impl std::fmt::Debug for ServerId {
1134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1135 f.debug_tuple("QuicLbServerId").field(&hex::encode(&self.0)).finish()
1136 }
1137}
1138
1139impl std::str::FromStr for ServerId {
1140 type Err = hex::FromHexError;
1141
1142 fn from_str(s: &str) -> Result<Self, Self::Err> {
1143 hex::decode(s).map(Self)
1144 }
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149 use super::*;
1150
1151 #[test]
1152 fn test_tls_string_or_array() {
1153 let single = r#"
1155 cert = "cert.pem"
1156 key = "key.pem"
1157 "#;
1158 let config: crate::tls::Server = toml::from_str(single).unwrap();
1159 assert_eq!(config.cert, vec![PathBuf::from("cert.pem")]);
1160 assert_eq!(config.key, vec![PathBuf::from("key.pem")]);
1161
1162 let array = r#"
1164 cert = ["a.pem", "b.pem"]
1165 key = ["a.key", "b.key"]
1166 generate = ["localhost"]
1167 root = ["ca.pem"]
1168 "#;
1169 let config: crate::tls::Server = toml::from_str(array).unwrap();
1170 assert_eq!(config.cert, vec![PathBuf::from("a.pem"), PathBuf::from("b.pem")]);
1171 assert_eq!(config.key, vec![PathBuf::from("a.key"), PathBuf::from("b.key")]);
1172 assert_eq!(config.generate, vec!["localhost".to_string()]);
1173 assert_eq!(config.root, vec![PathBuf::from("ca.pem")]);
1174 }
1175
1176 #[test]
1177 fn bind_string_or_listen_alias() {
1178 let bind: ServerConfig = toml::from_str(r#"bind = "[::]:443""#).unwrap();
1180 assert_eq!(bind.bind.as_deref(), Some("[::]:443"));
1181
1182 let alias: ServerConfig = toml::from_str(r#"listen = "0.0.0.0:4443""#).unwrap();
1183 assert_eq!(alias.bind.as_deref(), Some("0.0.0.0:4443"));
1184 }
1185
1186 #[cfg(all(feature = "uds", unix))]
1187 #[test]
1188 fn stream_listener_config_parses() {
1189 let config: ServerConfig = toml::from_str(
1190 r#"
1191bind = "[::]:443"
1192
1193[unix]
1194bind = "/run/moq.sock"
1195
1196[unix.allow]
1197uid = [1001, 1002]
1198"#,
1199 )
1200 .unwrap();
1201 assert_eq!(config.bind.as_deref(), Some("[::]:443"));
1202 assert_eq!(config.unix.bind.as_deref(), Some(std::path::Path::new("/run/moq.sock")));
1203 assert_eq!(config.unix.allow.as_ref().expect("allow").uid, vec![1001, 1002]);
1204 assert!(config.has_stream_listener());
1205 }
1206
1207 #[cfg(all(feature = "uds", unix))]
1208 #[test]
1209 fn stream_only_config_has_no_quic() {
1210 let mut config = ServerConfig::default();
1212 config.unix.bind = Some(PathBuf::from("/run/moq.sock"));
1213 assert!(config.has_stream_listener());
1214 assert!(config.bind.is_none());
1215
1216 assert!(!ServerConfig::default().has_stream_listener());
1218 }
1219}