iota/web-ui/src/server.rs
Alex Emmet 1ea0b97f6d ...
2026-04-05 00:03:19 +02:00

159 lines
5.1 KiB
Rust

use crate::api::api_config;
use crate::web_path_parser;
use actix_web::{App, Error, HttpRequest, HttpServer, Responder, dev::ServerHandle, web};
use iota_logger::log;
use iota_state::{ACTIVE_TASKS, SHUTDOWN};
use iota_util::file_util::load_file_buf;
use rustls::ServerConfig;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use std::{
error::Error as StdError,
io::{self, BufReader, ErrorKind},
net::IpAddr,
sync::Arc,
time::Duration,
};
use tokio::sync::oneshot;
pub async fn start(port: u16) -> bool {
let (tx, rx) = oneshot::channel::<ServerHandle>();
let _ = tokio::spawn(async move {
let server = match load_tls_config() {
Ok(Some(tls_config)) => {
log!("HTTPS (HTTP/2) Server running on 0.0.0.0:{}", port);
let _config = (*tls_config).clone();
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(true))
.configure(api_config)
.default_service(web::to(web_path_parser::handle))
})
.bind(("0.0.0.0", port))
.unwrap()
.run()
}
Ok(_) => {
log!("HTTP Server running on 0.0.0.0:{}", port);
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(false))
.configure(api_config)
.default_service(web::to(web_path_parser::handle))
})
.bind(("0.0.0.0", port))
.unwrap()
.run()
}
Err(e) => {
log!("TLS config error: {}", e);
return;
}
};
let server_handle = server.handle();
tx.send(server_handle).unwrap();
ACTIVE_TASKS.insert("WebServer".into());
server.await.unwrap();
ACTIVE_TASKS.remove("WebServer");
log!("Web Server shutdown complete.");
});
if let Ok(server_handle) = rx.await {
tokio::spawn(async move {
wait_for_shutdown(server_handle).await;
});
true
} else {
false
}
}
async fn wait_for_shutdown(server_handle: ServerHandle) {
loop {
if *SHUTDOWN.read().await {
log!("Shutdown signal received.");
server_handle.stop(true).await;
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
fn load_tls_config() -> Result<Option<Arc<ServerConfig>>, Box<dyn StdError>> {
let cert_file_res = load_file_buf("certs", "cert.pem");
let key_file_res = load_file_buf("certs", "cert.key");
let cert_file_buf = match cert_file_res {
Ok(b) => b,
Err(e) if e.kind() == ErrorKind::NotFound => {
log!("TLS certificate 'certs/cert.pem' not found.");
return Ok(None);
}
Err(e) => return Err(e.into()), // Other IO error
};
let key_file_buf = match key_file_res {
Ok(b) => b,
Err(e) if e.kind() == ErrorKind::NotFound => {
log!("TLS key 'certs/cert.key' not found.");
return Ok(None);
}
Err(e) => return Err(e.into()), // Other IO error
};
let cert_chain = rustls_pemfile::certs(&mut BufReader::new(cert_file_buf))
.collect::<Result<Vec<CertificateDer>, _>>()?;
// PKCS8
let mut key_reader = BufReader::new(key_file_buf);
let mut key_ders = rustls_pemfile::pkcs8_private_keys(&mut key_reader)
.map(|r| r.map(Into::into))
.collect::<Result<Vec<PrivateKeyDer>, _>>()?;
if key_ders.is_empty() {
// RSA
key_reader = BufReader::new(load_file_buf("certs", "cert.key")?); // Re-read key file
key_ders = rustls_pemfile::rsa_private_keys(&mut key_reader)
.map(|r| r.map(Into::into))
.collect::<Result<Vec<PrivateKeyDer>, _>>()?;
}
if key_ders.is_empty() {
// EC
key_reader = BufReader::new(load_file_buf("certs", "cert.key")?); // Re-read key file
key_ders = rustls_pemfile::ec_private_keys(&mut key_reader)
.map(|r| r.map(Into::into))
.collect::<Result<Vec<PrivateKeyDer>, _>>()?;
}
if key_ders.is_empty() {
return Err("No private keys found in key file. (Tried PKCS8, RSA, and EC)".into());
}
let config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, key_ders.remove(0))
.map_err(|e| io::Error::new(ErrorKind::Other, e.to_string()))?;
Ok(Some(Arc::new(config)))
}
pub fn is_local_network(addr: IpAddr) -> bool {
match addr {
IpAddr::V4(v4) => {
let o = v4.octets();
o[0] == 10
|| (o[0] == 172 && (16..=31).contains(&o[1]))
|| (o[0] == 192 && o[1] == 168)
|| o[0] == 127
|| (o[0] == 169 && o[1] == 254)
}
IpAddr::V6(v6) => {
let s = v6.segments();
(s[0] & 0xfe00) == 0xfc00 || (s[0] & 0xffc0) == 0xfe80 || v6.is_loopback()
}
}
}