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 #[arg(id = "server-quic-lb-id", long = "server-quic-lb-id", env = "MOQ_SERVER_QUIC_LB_ID")]
55 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub quic_lb_id: Option<ServerId>,
57
58 #[arg(
61 id = "server-quic-lb-nonce",
62 long = "server-quic-lb-nonce",
63 requires = "server-quic-lb-id",
64 env = "MOQ_SERVER_QUIC_LB_NONCE"
65 )]
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub quic_lb_nonce: Option<usize>,
68
69 #[arg(
77 id = "server-preferred-v4",
78 long = "server-preferred-v4",
79 env = "MOQ_SERVER_PREFERRED_V4"
80 )]
81 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub preferred_v4: Option<net::SocketAddrV4>,
83
84 #[arg(
88 id = "server-preferred-v6",
89 long = "server-preferred-v6",
90 env = "MOQ_SERVER_PREFERRED_V6"
91 )]
92 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub preferred_v6: Option<net::SocketAddrV6>,
94
95 #[serde(skip_serializing_if = "Option::is_none")]
97 #[arg(
98 id = "server-max-streams",
99 long = "server-max-streams",
100 env = "MOQ_SERVER_MAX_STREAMS"
101 )]
102 pub max_streams: Option<u64>,
103
104 #[serde(default, skip_serializing_if = "Vec::is_empty")]
112 #[arg(id = "server-version", long = "server-version", env = "MOQ_SERVER_VERSION")]
113 pub version: Vec<moq_net::Version>,
114
115 #[command(flatten)]
116 #[serde(default)]
117 pub tls: crate::tls::Server,
118}
119
120#[cfg(feature = "tcp")]
126#[derive(clap::Args, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
127#[serde(deny_unknown_fields, default)]
128#[non_exhaustive]
129pub struct TcpConfig {
130 #[arg(long = "server-tcp-bind", id = "server-tcp-bind", env = "MOQ_SERVER_TCP_BIND")]
132 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub bind: Option<net::SocketAddr>,
134}
135
136#[cfg(all(feature = "uds", unix))]
139#[derive(clap::Args, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
140#[serde(deny_unknown_fields, default)]
141#[non_exhaustive]
142pub struct UnixConfig {
143 #[arg(long = "server-unix-bind", id = "server-unix-bind", env = "MOQ_SERVER_UNIX_BIND")]
145 #[serde(default, skip_serializing_if = "Option::is_none")]
146 pub bind: Option<PathBuf>,
147
148 #[command(flatten)]
151 #[serde(default, skip_serializing_if = "Option::is_none")]
152 pub allow: Option<UnixAllow>,
153}
154
155#[cfg(all(feature = "uds", unix))]
161#[derive(clap::Args, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
162#[serde(deny_unknown_fields, default)]
163#[non_exhaustive]
164pub struct UnixAllow {
165 #[arg(
167 long = "server-unix-allow-uid",
168 env = "MOQ_SERVER_UNIX_ALLOW_UID",
169 value_delimiter = ','
170 )]
171 #[serde(default, skip_serializing_if = "Vec::is_empty")]
172 pub uid: Vec<u32>,
173
174 #[arg(
176 long = "server-unix-allow-gid",
177 env = "MOQ_SERVER_UNIX_ALLOW_GID",
178 value_delimiter = ','
179 )]
180 #[serde(default, skip_serializing_if = "Vec::is_empty")]
181 pub gid: Vec<u32>,
182
183 #[arg(
186 long = "server-unix-allow-pid",
187 env = "MOQ_SERVER_UNIX_ALLOW_PID",
188 value_delimiter = ','
189 )]
190 #[serde(default, skip_serializing_if = "Vec::is_empty")]
191 pub pid: Vec<i32>,
192}
193
194#[cfg(all(feature = "uds", unix))]
195impl UnixAllow {
196 fn is_empty(&self) -> bool {
198 self.uid.is_empty() && self.gid.is_empty() && self.pid.is_empty()
199 }
200
201 fn permits(&self, cred: &crate::unix::PeerCred) -> bool {
205 let uid_ok = self.uid.is_empty() || self.uid.contains(&cred.uid);
206 let gid_ok = self.gid.is_empty() || self.gid.contains(&cred.gid);
207 let pid_ok = self.pid.is_empty() || cred.pid.is_some_and(|pid| self.pid.contains(&pid));
208 uid_ok && gid_ok && pid_ok
209 }
210}
211
212impl ServerConfig {
213 pub fn init(self) -> crate::Result<Server> {
214 Server::new(self)
215 }
216
217 pub fn versions(&self) -> moq_net::Versions {
219 if self.version.is_empty() {
220 moq_net::Versions::all()
221 } else {
222 moq_net::Versions::from(self.version.clone())
223 }
224 }
225
226 #[allow(unused_mut)]
231 fn has_stream_listener(&self) -> bool {
232 let mut has = false;
233 #[cfg(feature = "tcp")]
234 {
235 has |= self.tcp.bind.is_some();
236 }
237 #[cfg(all(feature = "uds", unix))]
238 {
239 has |= self.unix.bind.is_some();
240 }
241 has
242 }
243}
244
245pub(crate) const DEFAULT_BIND: &str = "[::]:443";
247
248pub struct Server {
254 moq: moq_net::Server,
255 versions: moq_net::Versions,
256 accept: FuturesUnordered<BoxFuture<'static, crate::Result<Request>>>,
257 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
258 streams: StreamListeners,
259 #[cfg(feature = "iroh")]
260 iroh: Option<iroh::Endpoint>,
261 #[cfg(feature = "noq")]
262 noq: Option<crate::noq::NoqServer>,
263 #[cfg(feature = "quinn")]
264 quinn: Option<crate::quinn::QuinnServer>,
265 #[cfg(feature = "quiche")]
266 quiche: Option<crate::quiche::QuicheServer>,
267 #[cfg(feature = "websocket")]
268 websocket: Option<crate::websocket::Listener>,
269}
270
271impl Server {
272 pub fn new(config: ServerConfig) -> crate::Result<Self> {
273 let backend = config.backend.clone().unwrap_or({
274 #[cfg(feature = "quinn")]
275 {
276 QuicBackend::Quinn
277 }
278 #[cfg(all(feature = "noq", not(feature = "quinn")))]
279 {
280 QuicBackend::Noq
281 }
282 #[cfg(all(feature = "quiche", not(feature = "quinn"), not(feature = "noq")))]
283 {
284 QuicBackend::Quiche
285 }
286 #[cfg(all(not(feature = "quiche"), not(feature = "quinn"), not(feature = "noq")))]
287 panic!("no QUIC backend compiled; enable noq, quinn, or quiche feature");
288 });
289
290 let versions = config.versions();
291
292 let build_quic = config.bind.is_some() || !config.has_stream_listener();
296
297 if build_quic && !config.tls.root.is_empty() {
298 let mtls_supported = match backend {
299 #[cfg(feature = "quinn")]
300 QuicBackend::Quinn => true,
301 #[cfg(feature = "noq")]
302 QuicBackend::Noq => true,
303 #[allow(unreachable_patterns)]
304 _ => false,
305 };
306 if !mtls_supported {
307 return Err(Error::MtlsUnsupported);
308 }
309 }
310
311 #[cfg(feature = "noq")]
312 #[allow(unreachable_patterns)]
313 let noq = match backend {
314 QuicBackend::Noq if build_quic => Some(crate::noq::NoqServer::new(config.clone())?),
315 _ => None,
316 };
317
318 #[cfg(feature = "quinn")]
319 #[allow(unreachable_patterns)]
320 let quinn = match backend {
321 QuicBackend::Quinn if build_quic => Some(crate::quinn::QuinnServer::new(config.clone())?),
322 _ => None,
323 };
324
325 #[cfg(feature = "quiche")]
326 let quiche = match backend {
327 QuicBackend::Quiche if build_quic => Some(crate::quiche::QuicheServer::new(config.clone())?),
328 _ => None,
329 };
330
331 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
333 let mut stream_binds = Vec::new();
334 #[cfg(feature = "tcp")]
335 if let Some(addr) = config.tcp.bind {
336 stream_binds.push(StreamBind::Tcp(addr));
337 }
338 #[cfg(all(feature = "uds", unix))]
339 if let Some(path) = config.unix.bind.clone() {
340 stream_binds.push(StreamBind::Unix(path));
341 }
342 #[cfg(all(feature = "uds", unix))]
344 let unix_allow = config.unix.allow.clone().filter(|allow| !allow.is_empty());
345 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
346 let streams = StreamListeners::new(
347 stream_binds,
348 stream_versions(&versions),
349 #[cfg(all(feature = "uds", unix))]
350 unix_allow,
351 );
352
353 Ok(Server {
354 accept: Default::default(),
355 moq: moq_net::Server::new().with_versions(versions.clone()),
356 versions,
357 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
358 streams,
359 #[cfg(feature = "iroh")]
360 iroh: None,
361 #[cfg(feature = "noq")]
362 noq,
363 #[cfg(feature = "quinn")]
364 quinn,
365 #[cfg(feature = "quiche")]
366 quiche,
367 #[cfg(feature = "websocket")]
368 websocket: None,
369 })
370 }
371
372 #[cfg(feature = "websocket")]
378 pub fn with_websocket(mut self, websocket: Option<crate::websocket::Listener>) -> Self {
379 self.websocket = websocket;
380 self
381 }
382
383 #[cfg(feature = "iroh")]
384 pub fn with_iroh(mut self, iroh: Option<iroh::Endpoint>) -> Self {
385 self.iroh = iroh;
386 self
387 }
388
389 pub fn with_publish(mut self, publish: impl Into<Option<moq_net::OriginConsumer>>) -> Self {
390 self.moq = self.moq.with_publish(publish);
391 self
392 }
393
394 pub fn with_consume(mut self, consume: impl Into<Option<moq_net::OriginProducer>>) -> Self {
395 self.moq = self.moq.with_consume(consume);
396 self
397 }
398
399 pub fn with_stats(mut self, stats: moq_net::StatsHandle) -> Self {
401 self.moq = self.moq.with_stats(stats);
402 self
403 }
404
405 pub async fn serve_publish(self, origin: moq_net::OriginConsumer) -> crate::Result<()> {
412 self.with_publish(origin).serve().await
413 }
414
415 pub async fn serve_consume(self, origin: moq_net::OriginProducer) -> crate::Result<()> {
419 self.with_consume(origin).serve().await
420 }
421
422 async fn serve(mut self) -> crate::Result<()> {
425 if let Ok(addr) = self.local_addr() {
426 tracing::info!(%addr, "listening");
427 }
428 while let Some(request) = self.accept().await {
429 tokio::spawn(async move {
430 if let Err(err) = serve_session(request).await {
431 tracing::warn!(%err, "session ended with error");
432 }
433 });
434 }
435 Ok(())
436 }
437
438 pub fn tls_info(&self) -> Arc<RwLock<crate::tls::Info>> {
440 #[cfg(feature = "noq")]
441 if let Some(noq) = self.noq.as_ref() {
442 return noq.tls_info();
443 }
444 #[cfg(feature = "quinn")]
445 if let Some(quinn) = self.quinn.as_ref() {
446 return quinn.tls_info();
447 }
448 #[cfg(feature = "quiche")]
449 if let Some(quiche) = self.quiche.as_ref() {
450 return quiche.tls_info();
451 }
452 Arc::new(RwLock::new(crate::tls::Info::empty()))
454 }
455
456 #[cfg(not(any(
457 feature = "noq",
458 feature = "quinn",
459 feature = "quiche",
460 feature = "iroh",
461 feature = "tcp",
462 all(feature = "uds", unix)
463 )))]
464 pub async fn accept(&mut self) -> Option<Request> {
465 unreachable!("no transport compiled; enable a QUIC backend, tcp, or uds feature");
466 }
467
468 #[cfg(any(
475 feature = "noq",
476 feature = "quinn",
477 feature = "quiche",
478 feature = "iroh",
479 feature = "tcp",
480 all(feature = "uds", unix)
481 ))]
482 pub async fn accept(&mut self) -> Option<Request> {
483 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
486 if let Err(err) = self.streams.ensure_started().await {
487 tracing::error!(%err, "failed to bind stream listener");
488 return None;
489 }
490
491 loop {
492 #[cfg(feature = "noq")]
494 let noq_accept = async {
495 #[cfg(feature = "noq")]
496 if let Some(noq) = self.noq.as_mut() {
497 return noq.accept().await;
498 }
499 None
500 };
501 #[cfg(not(feature = "noq"))]
502 let noq_accept = async { None::<()> };
503
504 #[cfg(feature = "iroh")]
505 let iroh_accept = async {
506 #[cfg(feature = "iroh")]
507 if let Some(endpoint) = self.iroh.as_mut() {
508 return endpoint.accept().await;
509 }
510 None
511 };
512 #[cfg(not(feature = "iroh"))]
513 let iroh_accept = async { None::<()> };
514
515 #[cfg(feature = "quinn")]
516 let quinn_accept = async {
517 #[cfg(feature = "quinn")]
518 if let Some(quinn) = self.quinn.as_mut() {
519 return quinn.accept().await;
520 }
521 None
522 };
523 #[cfg(not(feature = "quinn"))]
524 let quinn_accept = async { None::<()> };
525
526 #[cfg(feature = "quiche")]
527 let quiche_accept = async {
528 #[cfg(feature = "quiche")]
529 if let Some(quiche) = self.quiche.as_mut() {
530 return quiche.accept().await;
531 }
532 None
533 };
534 #[cfg(not(feature = "quiche"))]
535 let quiche_accept = async { None::<()> };
536
537 #[cfg(feature = "websocket")]
538 let ws_ref = self.websocket.as_ref();
539 #[cfg(feature = "websocket")]
540 let ws_accept = async {
541 match ws_ref {
542 Some(ws) => ws.accept().await,
543 None => std::future::pending().await,
544 }
545 };
546 #[cfg(not(feature = "websocket"))]
547 let ws_accept = std::future::pending::<Option<crate::Result<()>>>();
548
549 #[allow(unused_variables)]
550 let server = self.moq.clone();
551 #[allow(unused_variables)]
552 let versions = self.versions.clone();
553
554 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
556 let stream_accept = self.streams.recv();
557 #[cfg(not(any(feature = "tcp", all(feature = "uds", unix))))]
558 let stream_accept = std::future::pending::<Option<Request>>();
559
560 tokio::select! {
561 Some(request) = stream_accept => {
562 return Some(request);
563 }
564 Some(_conn) = noq_accept => {
565 #[cfg(feature = "noq")]
566 {
567 let alpns = versions.alpns();
568 self.accept.push(async move {
569 let noq = super::noq::NoqRequest::accept(_conn, alpns).await?;
570 Ok(Request {
571 server,
572 kind: RequestKind::Noq(Box::new(noq)),
573 })
574 }.boxed());
575 }
576 }
577 Some(_conn) = quinn_accept => {
578 #[cfg(feature = "quinn")]
579 {
580 let alpns = versions.alpns();
581 self.accept.push(async move {
582 let quinn = super::quinn::QuinnRequest::accept(_conn, alpns).await?;
583 Ok(Request {
584 server,
585 kind: RequestKind::Quinn(Box::new(quinn)),
586 })
587 }.boxed());
588 }
589 }
590 Some(_conn) = quiche_accept => {
591 #[cfg(feature = "quiche")]
592 {
593 let alpns = versions.alpns();
594 self.accept.push(async move {
595 let quiche = super::quiche::QuicheRequest::accept(_conn, alpns).await?;
596 Ok(Request {
597 server,
598 kind: RequestKind::Quiche(Box::new(quiche)),
599 })
600 }.boxed());
601 }
602 }
603 Some(_conn) = iroh_accept => {
604 #[cfg(feature = "iroh")]
605 self.accept.push(async move {
606 let iroh = super::iroh::Request::accept(_conn).await?;
607 Ok(Request {
608 server,
609 kind: RequestKind::Iroh(Box::new(iroh)),
610 })
611 }.boxed());
612 }
613 Some(_res) = ws_accept => {
614 #[cfg(feature = "websocket")]
615 match _res {
616 Ok(session) => {
617 return Some(Request {
618 server,
619 kind: RequestKind::WebSocket(Box::new(session)),
620 });
621 }
622 Err(err) => tracing::debug!(%err, "failed to accept WebSocket session"),
623 }
624 }
625 Some(res) = self.accept.next() => {
626 match res {
627 Ok(session) => return Some(session),
628 Err(err) => tracing::debug!(%err, "failed to accept session"),
629 }
630 }
631 _ = tokio::signal::ctrl_c() => {
632 self.close().await;
633 return None;
634 }
635 }
636 }
637 }
638
639 #[cfg(feature = "iroh")]
640 pub fn iroh_endpoint(&self) -> Option<&iroh::Endpoint> {
641 self.iroh.as_ref()
642 }
643
644 pub fn local_addr(&self) -> crate::Result<net::SocketAddr> {
645 #[cfg(feature = "noq")]
646 if let Some(noq) = self.noq.as_ref() {
647 return Ok(noq.local_addr()?);
648 }
649 #[cfg(feature = "quinn")]
650 if let Some(quinn) = self.quinn.as_ref() {
651 return Ok(quinn.local_addr()?);
652 }
653 #[cfg(feature = "quiche")]
654 if let Some(quiche) = self.quiche.as_ref() {
655 return Ok(quiche.local_addr()?);
656 }
657 Err(Error::NoBackend("no QUIC listener configured"))
659 }
660
661 #[cfg(feature = "websocket")]
662 pub fn websocket_local_addr(&self) -> Option<net::SocketAddr> {
663 self.websocket.as_ref().and_then(|ws| ws.local_addr().ok())
664 }
665
666 pub async fn close(&mut self) {
667 #[cfg(feature = "noq")]
668 if let Some(noq) = self.noq.as_mut() {
669 noq.close();
670 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
671 }
672 #[cfg(feature = "quinn")]
673 if let Some(quinn) = self.quinn.as_mut() {
674 quinn.close();
675 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
676 }
677 #[cfg(feature = "quiche")]
678 if let Some(quiche) = self.quiche.as_mut() {
679 quiche.close();
680 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
681 }
682 #[cfg(feature = "iroh")]
683 if let Some(iroh) = self.iroh.take() {
684 iroh.close().await;
685 }
686 #[cfg(feature = "websocket")]
687 {
688 let _ = self.websocket.take();
689 }
690 #[cfg(not(any(feature = "noq", feature = "quinn", feature = "quiche", feature = "iroh")))]
691 unreachable!("no QUIC backend compiled");
692 }
693}
694
695async fn serve_session(request: Request) -> crate::Result<()> {
697 let session = request.ok().await?;
698 session.closed().await?;
699 Ok(())
700}
701
702#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
709fn stream_versions(base: &moq_net::Versions) -> moq_net::Versions {
710 let mut versions: Vec<moq_net::Version> = base.iter().copied().collect();
711 if let Ok(lite05) = "moq-lite-05-wip".parse::<moq_net::Version>() {
712 if !versions.contains(&lite05) {
713 versions.push(lite05);
714 }
715 }
716 moq_net::Versions::from(versions)
717}
718
719#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
721enum StreamBind {
722 #[cfg(feature = "tcp")]
723 Tcp(net::SocketAddr),
724 #[cfg(all(feature = "uds", unix))]
725 Unix(PathBuf),
726}
727
728#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
735struct StreamListeners {
736 binds: Vec<StreamBind>,
737 versions: moq_net::Versions,
738 #[cfg(all(feature = "uds", unix))]
739 unix_allow: Option<UnixAllow>,
740 rx: Option<tokio::sync::mpsc::Receiver<Request>>,
741 tasks: Vec<tokio::task::JoinHandle<()>>,
742}
743
744#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
745impl StreamListeners {
746 fn new(
747 binds: Vec<StreamBind>,
748 versions: moq_net::Versions,
749 #[cfg(all(feature = "uds", unix))] unix_allow: Option<UnixAllow>,
750 ) -> Self {
751 Self {
752 binds,
753 versions,
754 #[cfg(all(feature = "uds", unix))]
755 unix_allow,
756 rx: None,
757 tasks: Vec::new(),
758 }
759 }
760
761 async fn ensure_started(&mut self) -> crate::Result<()> {
763 if self.rx.is_some() || self.binds.is_empty() {
764 return Ok(());
765 }
766
767 let (tx, rx) = tokio::sync::mpsc::channel(16);
768 for bind in self.binds.drain(..) {
769 let versions = self.versions.clone();
770 match bind {
771 #[cfg(feature = "tcp")]
772 StreamBind::Tcp(addr) => {
773 if !addr.ip().is_loopback() {
774 tracing::warn!(%addr, "tcp listener bound to a non-loopback address; qmux is UNENCRYPTED, ensure the network is trusted");
775 }
776 let listener = crate::tcp::Listener::bind(addr).await?.with_protocols(versions.alpns());
777 tracing::info!(%addr, "listening (tcp)");
778 self.tasks.push(spawn_tcp_loop(listener, versions, tx.clone()));
779 }
780 #[cfg(all(feature = "uds", unix))]
781 StreamBind::Unix(path) => {
782 let listener = crate::unix::Listener::bind(&path)
783 .await?
784 .with_protocols(versions.alpns());
785 listener.set_mode(0o666)?;
788 tracing::info!(path = %path.display(), allow = ?self.unix_allow, "listening (unix)");
789 self.tasks
790 .push(spawn_unix_loop(listener, versions, self.unix_allow.clone(), tx.clone()));
791 }
792 }
793 }
794
795 self.rx = Some(rx);
796 Ok(())
797 }
798
799 async fn recv(&mut self) -> Option<Request> {
801 match self.rx.as_mut() {
802 Some(rx) => rx.recv().await,
803 None => std::future::pending().await,
804 }
805 }
806}
807
808#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
809impl Drop for StreamListeners {
810 fn drop(&mut self) {
811 for task in &self.tasks {
813 task.abort();
814 }
815 }
816}
817
818#[cfg(feature = "tcp")]
819fn spawn_tcp_loop(
820 listener: crate::tcp::Listener,
821 versions: moq_net::Versions,
822 tx: tokio::sync::mpsc::Sender<Request>,
823) -> tokio::task::JoinHandle<()> {
824 tokio::spawn(async move {
825 loop {
826 match listener.accept().await {
827 Some(Ok(session)) => spawn_stream_request(session, "tcp", versions.clone(), tx.clone()),
828 Some(Err(err)) => tracing::warn!(%err, "tcp listener accept failed"),
829 None => break,
830 }
831 }
832 })
833}
834
835#[cfg(all(feature = "uds", unix))]
836fn spawn_unix_loop(
837 listener: crate::unix::Listener,
838 versions: moq_net::Versions,
839 allow: Option<UnixAllow>,
840 tx: tokio::sync::mpsc::Sender<Request>,
841) -> tokio::task::JoinHandle<()> {
842 tokio::spawn(async move {
843 loop {
844 match listener.accept().await {
845 Some(Ok((session, cred))) => {
846 if let Some(allow) = &allow
848 && !allow.permits(&cred)
849 {
850 tracing::warn!(uid = cred.uid, gid = cred.gid, pid = ?cred.pid, "unix connection rejected by allow list");
851 continue;
852 }
853 spawn_stream_request(session, "unix", versions.clone(), tx.clone());
854 }
855 Some(Err(err)) => tracing::warn!(%err, "unix listener accept failed"),
856 None => break,
857 }
858 }
859 })
860}
861
862#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
865fn spawn_stream_request(
866 session: qmux::Session,
867 transport: &'static str,
868 versions: moq_net::Versions,
869 tx: tokio::sync::mpsc::Sender<Request>,
870) {
871 tokio::spawn(async move {
872 let server = moq_net::Server::new().with_versions(versions);
873 match server.accept_request(session).await {
874 Ok(request) => {
875 let request = Request {
876 server: moq_net::Server::new(),
877 kind: RequestKind::Stream(Box::new(StreamRequest { request, transport })),
878 };
879 let _ = tx.send(request).await;
880 }
881 Err(err) => tracing::debug!(%err, "stream SETUP handshake failed"),
882 }
883 });
884}
885
886#[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
889pub(crate) struct StreamRequest {
890 request: moq_net::Request<qmux::Session>,
891 transport: &'static str,
892}
893
894pub(crate) enum RequestKind {
896 #[cfg(feature = "noq")]
897 Noq(Box<crate::noq::NoqRequest>),
898 #[cfg(feature = "quinn")]
899 Quinn(Box<crate::quinn::QuinnRequest>),
900 #[cfg(feature = "quiche")]
901 Quiche(Box<crate::quiche::QuicheRequest>),
902 #[cfg(feature = "iroh")]
903 Iroh(Box<crate::iroh::Request>),
904 #[cfg(feature = "websocket")]
905 WebSocket(Box<qmux::Session>),
906 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
907 Stream(Box<StreamRequest>),
908}
909
910pub struct Request {
915 server: moq_net::Server,
916 kind: RequestKind,
917}
918
919impl Request {
920 pub async fn close(self, _code: u16) -> crate::Result<()> {
922 match self.kind {
923 #[cfg(feature = "noq")]
924 RequestKind::Noq(request) => {
925 let status =
926 web_transport_noq::http::StatusCode::from_u16(_code).map_err(|_| Error::InvalidStatusCode)?;
927 request.close(status).await.map_err(crate::noq::Error::Server)?;
928 Ok(())
929 }
930 #[cfg(feature = "quinn")]
931 RequestKind::Quinn(request) => {
932 let status =
933 web_transport_quinn::http::StatusCode::from_u16(_code).map_err(|_| Error::InvalidStatusCode)?;
934 request.close(status).await.map_err(crate::quinn::Error::Server)?;
935 Ok(())
936 }
937 #[cfg(feature = "quiche")]
938 RequestKind::Quiche(request) => {
939 let status =
940 web_transport_quiche::http::StatusCode::from_u16(_code).map_err(|_| Error::InvalidStatusCode)?;
941 request.reject(status).await.map_err(crate::quiche::Error::Reject)?;
942 Ok(())
943 }
944 #[cfg(feature = "iroh")]
945 RequestKind::Iroh(request) => {
946 let status =
947 web_transport_iroh::http::StatusCode::from_u16(_code).map_err(|_| Error::InvalidStatusCode)?;
948 request.close(status).await.map_err(crate::iroh::Error::Server)?;
949 Ok(())
950 }
951 #[cfg(feature = "websocket")]
952 RequestKind::WebSocket(_session) => {
953 Ok(())
955 }
956 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
957 RequestKind::Stream(stream) => {
958 let err = match _code {
961 401 | 403 => moq_net::Error::Unauthorized,
962 other => moq_net::Error::App(other),
963 };
964 stream.request.close(err);
965 Ok(())
966 }
967 }
968 }
969
970 pub fn with_publish(self, publish: impl Into<Option<moq_net::OriginConsumer>>) -> Self {
972 let Request { server, kind } = self;
973 match kind {
974 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
975 RequestKind::Stream(stream) => {
976 let StreamRequest { request, transport } = *stream;
977 Request {
978 server,
979 kind: RequestKind::Stream(Box::new(StreamRequest {
980 request: request.with_publish(publish),
981 transport,
982 })),
983 }
984 }
985 kind => Request {
986 server: server.with_publish(publish),
987 kind,
988 },
989 }
990 }
991
992 pub fn with_consume(self, consume: impl Into<Option<moq_net::OriginProducer>>) -> Self {
994 let Request { server, kind } = self;
995 match kind {
996 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
997 RequestKind::Stream(stream) => {
998 let StreamRequest { request, transport } = *stream;
999 Request {
1000 server,
1001 kind: RequestKind::Stream(Box::new(StreamRequest {
1002 request: request.with_consume(consume),
1003 transport,
1004 })),
1005 }
1006 }
1007 kind => Request {
1008 server: server.with_consume(consume),
1009 kind,
1010 },
1011 }
1012 }
1013
1014 pub fn with_stats(self, stats: moq_net::StatsHandle) -> Self {
1016 let Request { server, kind } = self;
1017 match kind {
1018 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1019 RequestKind::Stream(stream) => {
1020 let StreamRequest { request, transport } = *stream;
1021 Request {
1022 server,
1023 kind: RequestKind::Stream(Box::new(StreamRequest {
1024 request: request.with_stats(stats),
1025 transport,
1026 })),
1027 }
1028 }
1029 kind => Request {
1030 server: server.with_stats(stats),
1031 kind,
1032 },
1033 }
1034 }
1035
1036 pub async fn ok(self) -> crate::Result<Session> {
1038 match self.kind {
1039 #[cfg(feature = "noq")]
1040 RequestKind::Noq(request) => Ok(self
1041 .server
1042 .accept(request.ok().await.map_err(crate::noq::Error::Server)?)
1043 .await?),
1044 #[cfg(feature = "quinn")]
1045 RequestKind::Quinn(request) => Ok(self
1046 .server
1047 .accept(request.ok().await.map_err(crate::quinn::Error::Server)?)
1048 .await?),
1049 #[cfg(feature = "quiche")]
1050 RequestKind::Quiche(request) => {
1051 let conn = request.ok().await.map_err(crate::quiche::Error::Accept)?;
1052 Ok(self.server.accept(conn).await?)
1053 }
1054 #[cfg(feature = "iroh")]
1055 RequestKind::Iroh(request) => Ok(self
1056 .server
1057 .accept(request.ok().await.map_err(crate::iroh::Error::Server)?)
1058 .await?),
1059 #[cfg(feature = "websocket")]
1060 RequestKind::WebSocket(session) => Ok(self.server.accept(*session).await?),
1061 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1062 RequestKind::Stream(stream) => Ok(stream.request.ok().await?),
1063 }
1064 }
1065
1066 pub fn transport(&self) -> &'static str {
1068 match self.kind {
1069 #[cfg(feature = "noq")]
1070 RequestKind::Noq(_) => "quic",
1071 #[cfg(feature = "quinn")]
1072 RequestKind::Quinn(_) => "quic",
1073 #[cfg(feature = "quiche")]
1074 RequestKind::Quiche(_) => "quic",
1075 #[cfg(feature = "iroh")]
1076 RequestKind::Iroh(_) => "iroh",
1077 #[cfg(feature = "websocket")]
1078 RequestKind::WebSocket(_) => "websocket",
1079 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1080 RequestKind::Stream(ref stream) => stream.transport,
1081 }
1082 }
1083
1084 pub fn url(&self) -> Option<&Url> {
1089 #[cfg(not(any(
1090 feature = "noq",
1091 feature = "quinn",
1092 feature = "quiche",
1093 feature = "iroh",
1094 feature = "tcp",
1095 all(feature = "uds", unix)
1096 )))]
1097 unreachable!("no transport compiled; enable a QUIC backend, tcp, or uds feature");
1098
1099 #[allow(unreachable_code)]
1100 match self.kind {
1101 #[cfg(feature = "noq")]
1102 RequestKind::Noq(ref request) => request.url(),
1103 #[cfg(feature = "quinn")]
1104 RequestKind::Quinn(ref request) => request.url(),
1105 #[cfg(feature = "quiche")]
1106 RequestKind::Quiche(ref request) => request.url(),
1107 #[cfg(feature = "iroh")]
1108 RequestKind::Iroh(ref request) => request.url(),
1109 #[cfg(feature = "websocket")]
1110 RequestKind::WebSocket(_) => None,
1111 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1112 RequestKind::Stream(_) => None,
1113 }
1114 }
1115
1116 pub fn path(&self) -> Option<&str> {
1119 match self.kind {
1120 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1121 RequestKind::Stream(ref stream) => stream.request.path(),
1122 #[allow(unreachable_patterns)]
1123 _ => None,
1124 }
1125 }
1126
1127 pub fn peer_identity(&self) -> Option<crate::tls::PeerIdentity> {
1134 match self.kind {
1135 #[cfg(feature = "quinn")]
1136 RequestKind::Quinn(ref request) => request.peer_identity(),
1137 #[cfg(feature = "noq")]
1138 RequestKind::Noq(ref request) => request.peer_identity(),
1139 #[cfg(feature = "quiche")]
1140 RequestKind::Quiche(_) => None,
1141 #[cfg(feature = "iroh")]
1142 RequestKind::Iroh(_) => None,
1143 #[cfg(feature = "websocket")]
1144 RequestKind::WebSocket(_) => None,
1145 #[cfg(any(feature = "tcp", all(feature = "uds", unix)))]
1146 RequestKind::Stream(_) => None,
1147 #[cfg(not(any(
1148 feature = "noq",
1149 feature = "quinn",
1150 feature = "quiche",
1151 feature = "iroh",
1152 feature = "websocket",
1153 feature = "tcp",
1154 all(feature = "uds", unix)
1155 )))]
1156 _ => None,
1157 }
1158 }
1159
1160 #[deprecated(note = "use `peer_identity` instead")]
1162 pub fn has_peer_certificate(&self) -> bool {
1163 self.peer_identity().is_some()
1164 }
1165}
1166
1167#[serde_with::serde_as]
1169#[derive(Clone, serde::Serialize, serde::Deserialize)]
1170pub struct ServerId(#[serde_as(as = "serde_with::hex::Hex")] pub(crate) Vec<u8>);
1171
1172impl ServerId {
1173 #[allow(dead_code)]
1174 pub(crate) fn len(&self) -> usize {
1175 self.0.len()
1176 }
1177}
1178
1179impl std::fmt::Debug for ServerId {
1180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1181 f.debug_tuple("QuicLbServerId").field(&hex::encode(&self.0)).finish()
1182 }
1183}
1184
1185impl std::str::FromStr for ServerId {
1186 type Err = hex::FromHexError;
1187
1188 fn from_str(s: &str) -> Result<Self, Self::Err> {
1189 hex::decode(s).map(Self)
1190 }
1191}
1192
1193#[cfg(test)]
1194mod tests {
1195 use super::*;
1196
1197 #[test]
1198 fn test_tls_string_or_array() {
1199 let single = r#"
1201 cert = "cert.pem"
1202 key = "key.pem"
1203 "#;
1204 let config: crate::tls::Server = toml::from_str(single).unwrap();
1205 assert_eq!(config.cert, vec![PathBuf::from("cert.pem")]);
1206 assert_eq!(config.key, vec![PathBuf::from("key.pem")]);
1207
1208 let array = r#"
1210 cert = ["a.pem", "b.pem"]
1211 key = ["a.key", "b.key"]
1212 generate = ["localhost"]
1213 root = ["ca.pem"]
1214 "#;
1215 let config: crate::tls::Server = toml::from_str(array).unwrap();
1216 assert_eq!(config.cert, vec![PathBuf::from("a.pem"), PathBuf::from("b.pem")]);
1217 assert_eq!(config.key, vec![PathBuf::from("a.key"), PathBuf::from("b.key")]);
1218 assert_eq!(config.generate, vec!["localhost".to_string()]);
1219 assert_eq!(config.root, vec![PathBuf::from("ca.pem")]);
1220 }
1221
1222 #[test]
1223 fn bind_string_or_listen_alias() {
1224 let bind: ServerConfig = toml::from_str(r#"bind = "[::]:443""#).unwrap();
1226 assert_eq!(bind.bind.as_deref(), Some("[::]:443"));
1227
1228 let alias: ServerConfig = toml::from_str(r#"listen = "0.0.0.0:4443""#).unwrap();
1229 assert_eq!(alias.bind.as_deref(), Some("0.0.0.0:4443"));
1230 }
1231
1232 #[cfg(all(feature = "uds", unix))]
1233 #[test]
1234 fn stream_listener_config_parses() {
1235 let config: ServerConfig = toml::from_str(
1236 r#"
1237bind = "[::]:443"
1238
1239[unix]
1240bind = "/run/moq.sock"
1241
1242[unix.allow]
1243uid = [1001, 1002]
1244"#,
1245 )
1246 .unwrap();
1247 assert_eq!(config.bind.as_deref(), Some("[::]:443"));
1248 assert_eq!(config.unix.bind.as_deref(), Some(std::path::Path::new("/run/moq.sock")));
1249 assert_eq!(config.unix.allow.as_ref().expect("allow").uid, vec![1001, 1002]);
1250 assert!(config.has_stream_listener());
1251 }
1252
1253 #[cfg(all(feature = "uds", unix))]
1254 #[test]
1255 fn stream_only_config_has_no_quic() {
1256 let mut config = ServerConfig::default();
1258 config.unix.bind = Some(PathBuf::from("/run/moq.sock"));
1259 assert!(config.has_stream_listener());
1260 assert!(config.bind.is_none());
1261
1262 assert!(!ServerConfig::default().has_stream_listener());
1264 }
1265}