use std::net::{SocketAddr, TcpListener};
use frame_host::FrameConfig;
use crate::error::CliError;
const PAGE_COHERENCE: &str = "Pick a free port for [frame].bind, and because the page's origin \
(http://HOST:PORT) is echoed in [bus.websocket].allowed_origins, change allowed_origins to \
match or the served page is Origin-refused by the bus. Keep all four frame.toml ports \
distinct: [frame].bind, [bus].listen_address, [bus].health_listen_address, and \
[bus.websocket].listen_address. Or omit [frame].bind entirely to let the host prefer 4190 \
and walk forward to a free port.";
const BUS_COHERENCE: &str = "Pick a free port and keep all four frame.toml ports distinct: \
[frame].bind, [bus].listen_address, [bus].health_listen_address, and \
[bus.websocket].listen_address. If you move [frame].bind, also change \
[bus.websocket].allowed_origins to its new origin. Or omit the whole [bus] section to let \
the host bind OS-assigned free ports and advertise them via /frame/config.json.";
struct HostSocket {
role: &'static str,
key: &'static str,
addr: SocketAddr,
coherence: &'static str,
}
fn stated_sockets(config: &FrameConfig) -> Vec<HostSocket> {
let mut sockets = Vec::new();
if let Some(addr) = config.frame.bind {
sockets.push(HostSocket {
role: "page server",
key: "[frame].bind",
addr,
coherence: PAGE_COHERENCE,
});
}
if config.bus.listen_address.port() != 0 {
sockets.push(HostSocket {
role: "bus",
key: "[bus].listen_address",
addr: config.bus.listen_address,
coherence: BUS_COHERENCE,
});
}
if config.bus.health_listen_address.port() != 0 {
sockets.push(HostSocket {
role: "bus health",
key: "[bus].health_listen_address",
addr: config.bus.health_listen_address,
coherence: BUS_COHERENCE,
});
}
if let Some(websocket) = config.bus.websocket.as_ref()
&& websocket.listen_address.port() != 0
{
sockets.push(HostSocket {
role: "websocket",
key: "[bus.websocket].listen_address",
addr: websocket.listen_address,
coherence: BUS_COHERENCE,
});
}
sockets
}
pub fn ensure_ports_available(config: &FrameConfig) -> Result<(), CliError> {
for socket in stated_sockets(config) {
match TcpListener::bind(socket.addr) {
Ok(listener) => drop(listener),
Err(source) => {
return Err(CliError::PortUnavailable {
role: socket.role,
key: socket.key,
addr: socket.addr,
coherence: socket.coherence,
source,
});
}
}
}
Ok(())
}