#[allow(warnings)]
#[cfg(feature = "ip_addr")]
pub mod ip_addr {
use anyhow::{Context, Result};
use std::net::{IpAddr, SocketAddr, TcpListener as StdTcpListener};
use tokio::net::TcpListener;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum BindAddr {
IpAddr(IpAddr),
#[cfg(unix)]
SocketPath(String),
}
impl BindAddr {
fn parse_addrs(addrs: &[&str]) -> Result<Vec<Self>> {
let mut bind_addrs = vec![];
#[cfg(not(unix))]
let mut invalid_addrs = vec![];
for addr in addrs {
match addr.parse::<IpAddr>() {
Ok(v) => {
bind_addrs.push(BindAddr::IpAddr(v));
}
Err(_) => {
#[cfg(unix)]
bind_addrs.push(BindAddr::SocketPath(addr.to_string()));
#[cfg(not(unix))]
invalid_addrs.push(*addr);
}
}
}
#[cfg(not(unix))]
if !invalid_addrs.is_empty() {
anyhow::bail!("Invalid bind address `{}`", invalid_addrs.join(","));
}
Ok(bind_addrs)
}
}
pub fn interface_addrs() -> Result<(Vec<BindAddr>, Vec<BindAddr>)> {
let (mut ipv4_addrs, mut ipv6_addrs) = (vec![], vec![]);
let ifaces =
if_addrs::get_if_addrs().with_context(|| "Failed to get local interface addresses")?;
for iface in ifaces.into_iter() {
let ip = iface.ip();
if ip.is_ipv4() {
ipv4_addrs.push(BindAddr::IpAddr(ip))
}
if ip.is_ipv6() {
ipv6_addrs.push(BindAddr::IpAddr(ip))
}
}
Ok((ipv4_addrs, ipv6_addrs))
}
pub async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("Failed to install CTRL+C signal handler")
}
pub fn print_listening(print_addrs: &[BindAddr], port: u16) -> Result<String> {
let mut output = String::new();
let urls = print_addrs
.iter()
.map(|bind_addr| match bind_addr {
BindAddr::IpAddr(addr) => {
let addr = match addr {
IpAddr::V4(_) => format!("{}:{}", addr, port),
IpAddr::V6(_) => format!("[{}]:{}", addr, port),
};
let protocol = "http";
format!("{}://{}", protocol, addr)
}
#[cfg(unix)]
BindAddr::SocketPath(path) => path.to_string(),
})
.collect::<Vec<_>>();
if urls.len() == 1 {
output.push_str(&format!("Listening on {}", urls[0]))
} else {
let info = urls
.iter()
.map(|v| format!(" {v}"))
.collect::<Vec<String>>()
.join("\n");
output.push_str(&format!("Listening on:\n{info}\n"))
}
Ok(output)
}
pub fn print_listening_with_protocol(print_addrs: &[BindAddr], port: u16,protocol:&str) -> Result<String> {
let mut output = String::new();
let urls = print_addrs
.iter()
.map(|bind_addr| match bind_addr {
BindAddr::IpAddr(addr) => {
let addr = match addr {
IpAddr::V4(_) => format!("{}:{}", addr, port),
IpAddr::V6(_) => format!("[{}]:{}", addr, port),
};
format!("{}://{}", protocol, addr)
}
#[cfg(unix)]
BindAddr::SocketPath(path) => path.to_string(),
})
.collect::<Vec<_>>();
if urls.len() == 1 {
output.push_str(&format!("Listening on {}", urls[0]))
} else {
let info = urls
.iter()
.map(|v| format!(" {v}"))
.collect::<Vec<String>>()
.join("\n");
output.push_str(&format!("Listening on:\n{info}\n"))
}
Ok(output)
}
pub fn create_listener(addr: SocketAddr) -> Result<TcpListener> {
use socket2::{Domain, Protocol, Socket, Type};
let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?;
if addr.is_ipv6() {
socket.set_only_v6(true)?;
}
socket.set_reuse_address(true)?;
socket.bind(&addr.into())?;
socket.listen(1024 )?;
let std_listener = StdTcpListener::from(socket);
std_listener.set_nonblocking(true)?;
let listener = TcpListener::from_std(std_listener)?;
Ok(listener)
}
pub fn get_addrs() -> Result<(Vec<BindAddr>, Vec<BindAddr>)> {
let (ipv4_addrs, ipv6_addrs) = interface_addrs()?;
Ok((ipv4_addrs, ipv6_addrs))
}
}
#[cfg(feature = "ip_addr")]
pub use ip_addr::*;