use super::Server;
use tokio::net::TcpListener;
enum Transport {
Http(TcpListener),
#[cfg(feature = "tls")]
Https(TcpListener, rustls::ServerConfig),
#[cfg(feature = "http3")]
H3(s2n_quic::Server),
#[cfg(feature = "tor")]
Onion(super::tor::OnionConfig),
#[cfg(feature = "i2p")]
I2p(super::i2p::I2pConfig),
}
#[must_use = "MultiServer does nothing until `.serve()` is called and awaited"]
pub struct MultiServer<S> {
server: Server<S>,
transports: Vec<Transport>,
}
impl<S> std::fmt::Debug for MultiServer<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MultiServer")
.field("transports", &self.transports.len())
.finish_non_exhaustive()
}
}
impl<S> MultiServer<S>
where
S: Clone + Send + Sync + 'static,
{
pub(super) const fn new(server: Server<S>) -> Self {
Self {
server,
transports: Vec::new(),
}
}
pub fn with_http(mut self, listener: TcpListener) -> Self {
self.transports.push(Transport::Http(listener));
self
}
#[cfg(feature = "tls")]
pub fn with_https(mut self, listener: TcpListener, config: rustls::ServerConfig) -> Self {
self.transports.push(Transport::Https(listener, config));
self
}
#[cfg(feature = "http3")]
pub fn with_h3(mut self, quic_server: s2n_quic::Server) -> Self {
self.transports.push(Transport::H3(quic_server));
self
}
#[cfg(feature = "tor")]
pub fn with_onion(mut self, config: super::tor::OnionConfig) -> Self {
self.transports.push(Transport::Onion(config));
self
}
#[cfg(feature = "i2p")]
pub fn with_i2p(mut self, config: super::i2p::I2pConfig) -> Self {
self.transports.push(Transport::I2p(config));
self
}
pub async fn serve(self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
if self.transports.is_empty() {
return Err(
"MultiServer::serve called with no transports configured — call at least one \
`.with_http`/`.with_https`/`.with_h3`/`.with_onion`/`.with_i2p` first"
.into(),
);
}
let mut set = tokio::task::JoinSet::new();
for transport in self.transports {
let server = self.server.clone();
match transport {
Transport::Http(listener) => {
set.spawn(async move { server.serve_http(listener).await.map_err(Into::into) });
}
#[cfg(feature = "tls")]
Transport::Https(listener, config) => {
set.spawn(async move {
server
.serve_https_config(listener, config)
.await
.map_err(Into::into)
});
}
#[cfg(feature = "http3")]
Transport::H3(quic_server) => {
set.spawn(
async move { server.serve_h3(quic_server).await.map_err(Into::into) },
);
}
#[cfg(feature = "tor")]
Transport::Onion(config) => {
set.spawn(async move { server.serve_onion(config).await });
}
#[cfg(feature = "i2p")]
Transport::I2p(config) => {
set.spawn(async move { server.serve_i2p_config(config).await });
}
}
}
let Some(result) = set.join_next().await else {
return Err("MultiServer: no transport task was actually spawned".into());
};
set.abort_all();
match result {
Ok(outcome) => outcome,
Err(join_err) => Err(Box::new(join_err)),
}
}
}