use anyhow::Result;
use iroh_metrics::metrics::start_metrics_server;
use tracing::info;
use crate::{
config::Config,
dns::{DnsHandler, DnsServer},
http::HttpServer,
state::AppState,
store::ZoneStore,
};
pub async fn run_with_config_until_ctrl_c(config: Config) -> Result<()> {
let mut store = ZoneStore::persistent(Config::signed_packet_store_path()?)?;
if let Some(bootstrap) = config.mainline_enabled() {
info!("mainline fallback enabled");
store = store.with_mainline_fallback(bootstrap);
};
let server = Server::spawn(config, store).await?;
tokio::signal::ctrl_c().await?;
info!("shutdown");
server.shutdown().await?;
Ok(())
}
pub struct Server {
http_server: HttpServer,
dns_server: DnsServer,
metrics_task: tokio::task::JoinHandle<anyhow::Result<()>>,
}
impl Server {
pub async fn spawn(config: Config, store: ZoneStore) -> Result<Self> {
let dns_handler = DnsHandler::new(store.clone(), &config.dns)?;
let state = AppState { store, dns_handler };
let metrics_addr = config.metrics_addr();
let metrics_task = tokio::task::spawn(async move {
if let Some(addr) = metrics_addr {
start_metrics_server(addr).await?;
}
Ok(())
});
let http_server = HttpServer::spawn(
config.http,
config.https,
config.pkarr_put_rate_limit,
state.clone(),
)
.await?;
let dns_server = DnsServer::spawn(config.dns, state.dns_handler.clone()).await?;
Ok(Self {
http_server,
dns_server,
metrics_task,
})
}
pub async fn shutdown(self) -> Result<()> {
self.metrics_task.abort();
let (res1, res2) = tokio::join!(self.dns_server.shutdown(), self.http_server.shutdown(),);
res1?;
res2?;
Ok(())
}
pub async fn run_until_error(self) -> Result<()> {
tokio::select! {
res = self.dns_server.run_until_done() => res?,
res = self.http_server.run_until_done() => res?,
}
self.metrics_task.abort();
Ok(())
}
#[cfg(test)]
pub async fn spawn_for_tests() -> Result<(Self, std::net::SocketAddr, url::Url)> {
Self::spawn_for_tests_with_mainline(None).await
}
#[cfg(test)]
pub async fn spawn_for_tests_with_mainline(
mainline: Option<crate::config::BootstrapOption>,
) -> Result<(Self, std::net::SocketAddr, url::Url)> {
use std::net::{IpAddr, Ipv4Addr};
use crate::config::MetricsConfig;
let mut config = Config::default();
config.dns.port = 0;
config.dns.bind_addr = Some(IpAddr::V4(Ipv4Addr::LOCALHOST));
config.http.as_mut().unwrap().port = 0;
config.http.as_mut().unwrap().bind_addr = Some(IpAddr::V4(Ipv4Addr::LOCALHOST));
config.https = None;
config.metrics = Some(MetricsConfig::disabled());
let mut store = ZoneStore::in_memory()?;
if let Some(bootstrap) = mainline {
info!("mainline fallback enabled");
store = store.with_mainline_fallback(bootstrap);
}
let server = Self::spawn(config, store).await?;
let dns_addr = server.dns_server.local_addr();
let http_addr = server.http_server.http_addr().expect("http is set");
let http_url = format!("http://{http_addr}").parse()?;
Ok((server, dns_addr, http_url))
}
}