use crate::pipeline::network::tcp::server::{DefaultIpResolver, IpResolver};
use local_ip_address::Error;
use std::net::{IpAddr, Ipv4Addr};
const FALLBACK: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
pub fn local_ip_for_advertise() -> String {
resolve(DefaultIpResolver)
}
pub fn tcp_rpc_host_from_env() -> String {
std::env::var("DYN_TCP_RPC_HOST").unwrap_or_else(|_| local_ip_for_advertise())
}
fn resolve<R: IpResolver>(resolver: R) -> String {
let ip = resolver
.local_ip()
.or_else(|err| match err {
Error::LocalIpAddressNotFound => resolver.local_ipv6(),
_ => Err(err),
})
.unwrap_or(FALLBACK);
match ip {
IpAddr::V6(_) => format!("[{ip}]"),
IpAddr::V4(_) => ip.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockIpResolver {
v4: Result<IpAddr, Error>,
v6: Result<IpAddr, Error>,
}
impl IpResolver for MockIpResolver {
fn local_ip(&self) -> Result<IpAddr, Error> {
self.v4
.as_ref()
.copied()
.map_err(|_| Error::LocalIpAddressNotFound)
}
fn local_ipv6(&self) -> Result<IpAddr, Error> {
self.v6
.as_ref()
.copied()
.map_err(|_| Error::LocalIpAddressNotFound)
}
}
#[test]
fn ipv4_returned_unbracketed() {
let r = MockIpResolver {
v4: Ok(IpAddr::from([192, 168, 1, 100])),
v6: Err(Error::LocalIpAddressNotFound),
};
assert_eq!(resolve(r), "192.168.1.100");
}
#[test]
fn ipv6_fallback_is_bracketed() {
let r = MockIpResolver {
v4: Err(Error::LocalIpAddressNotFound),
v6: Ok(IpAddr::from([0x2001, 0xdb8, 0, 0, 0, 0, 0, 1])),
};
assert_eq!(resolve(r), "[2001:db8::1]");
}
#[test]
fn both_fail_uses_localhost() {
let r = MockIpResolver {
v4: Err(Error::LocalIpAddressNotFound),
v6: Err(Error::LocalIpAddressNotFound),
};
assert_eq!(resolve(r), "127.0.0.1");
}
}