mumble_server_runtime_gateway/
serve.rs1use std::sync::Arc;
8
9use anyhow::{Context, Result};
10use tokio::net::{TcpListener, UdpSocket};
11use tokio_rustls::TlsAcceptor;
12
13use crate::config::GatewayConfig;
14use crate::connection;
15use crate::metrics::VoiceMetrics;
16use crate::router::ConnectionRouter;
17use crate::runtime::{Runtime, RuntimeHandle};
18use crate::tls::{self, Identity};
19use crate::voice::VoicePlane;
20
21pub struct Gateway {
27 runtime: Runtime,
28 config: Arc<GatewayConfig>,
29 listener: TcpListener,
30 udp: Arc<UdpSocket>,
31 acceptor: TlsAcceptor,
32 voice_metrics: Arc<VoiceMetrics>,
33}
34
35impl Gateway {
36 pub async fn bind(config: GatewayConfig, identity: Identity) -> Result<Gateway> {
42 tls::install_crypto_provider();
43 let acceptor = TlsAcceptor::from(tls::server_config(identity)?);
44 let (listener, udp) = bind_both(config.bind).await?;
45
46 let mut config = config;
47 config.bind = listener.local_addr().context("TCP local address")?;
48
49 Ok(Gateway {
50 runtime: Runtime::start(),
51 config: Arc::new(config),
52 listener,
53 udp: Arc::new(udp),
54 acceptor,
55 voice_metrics: Arc::new(VoiceMetrics::default()),
56 })
57 }
58
59 #[must_use]
61 pub fn address(&self) -> std::net::SocketAddr {
62 self.config.bind
63 }
64
65 #[must_use]
66 pub fn runtime(&self) -> RuntimeHandle {
67 self.runtime.handle()
68 }
69
70 #[must_use]
71 pub fn voice_metrics(&self) -> Arc<VoiceMetrics> {
72 Arc::clone(&self.voice_metrics)
73 }
74
75 pub async fn serve<R: ConnectionRouter>(self, router: R) -> Result<()> {
82 let router = Arc::new(router);
83 let runtime = self.runtime.handle();
84 let voice = Arc::new(VoicePlane::new_with_metrics(
85 Arc::clone(&self.udp),
86 Arc::clone(runtime.peers()),
87 (*self.config).clone(),
88 Arc::clone(&self.voice_metrics),
89 ));
90
91 let plane = {
94 let voice = Arc::clone(&voice);
95 tokio::spawn(async move {
96 if let Err(error) = voice.run().await {
97 eprintln!("mumble-server-runtime-gateway: the voice plane stopped: {error}");
98 }
99 })
100 };
101
102 let accepting = async {
103 loop {
104 let (tcp, from) = self.listener.accept().await.context("TCP accept")?;
105 let acceptor = self.acceptor.clone();
106 let runtime = runtime.clone();
107 let router = Arc::clone(&router);
108 let voice = Arc::clone(&voice);
109 let udp = Arc::clone(&self.udp);
110 let config = Arc::clone(&self.config);
111
112 tokio::spawn(async move {
116 if let Err(error) =
117 connection::serve(tcp, acceptor, runtime, router, voice, udp, config).await
118 {
119 eprintln!(
120 "mumble-server-runtime-gateway: connection from {from} ended: {error:#}"
121 );
122 }
123 });
124 }
125 };
126
127 let outcome: Result<()> = accepting.await;
128 plane.abort();
129 outcome
130 }
131}
132
133const EPHEMERAL_ATTEMPTS: u32 = 16;
138
139async fn bind_both(address: std::net::SocketAddr) -> Result<(TcpListener, UdpSocket)> {
148 let mut attempts = if address.port() == 0 {
149 EPHEMERAL_ATTEMPTS
150 } else {
151 1
152 };
153
154 loop {
155 attempts = attempts.saturating_sub(1);
156 let listener = TcpListener::bind(address)
157 .await
158 .with_context(|| format!("binding TCP {address}"))?;
159 let bound = listener.local_addr().context("TCP local address")?;
160
161 match UdpSocket::bind(bound).await {
162 Ok(udp) => return Ok((listener, udp)),
163 Err(error) if attempts > 0 => {
164 drop(listener);
167 eprintln!(
168 "mumble-server-runtime-gateway: UDP {bound} was taken ({error}), trying another port"
169 );
170 }
171 Err(error) => {
172 return Err(anyhow::Error::from(error))
173 .with_context(|| format!("binding UDP {bound}"));
174 }
175 }
176 }
177}
178
179pub async fn serve<R: ConnectionRouter>(
190 config: GatewayConfig,
191 identity: Identity,
192 build: impl FnOnce(&RuntimeHandle) -> R,
193) -> Result<()> {
194 let gateway = Gateway::bind(config, identity).await?;
195 let router = build(&gateway.runtime());
196 gateway.serve(router).await
197}