use std::net::{Ipv4Addr, TcpListener};
use std::time::Instant;
use crate::{HOT_LOOP_INTERVAL, LINGER_DURATION, PORT_RELEASE_TIMEOUT};
pub struct EphemeralPort {
port: u16,
listener: Option<TcpListener>,
}
impl EphemeralPort {
pub fn allocate() -> std::io::Result<Self> {
let socket = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?;
socket.set_reuse_address(true)?;
#[cfg(unix)]
socket.set_reuse_port(true)?;
socket.set_linger(Some(LINGER_DURATION))?;
socket.bind(&std::net::SocketAddr::from((Ipv4Addr::LOCALHOST, 0)).into())?;
socket.listen(1)?;
let listener = TcpListener::from(socket);
let port = listener.local_addr()?.port();
Ok(EphemeralPort {
port,
listener: Some(listener),
})
}
pub fn take(self) -> u16 {
drop(self.listener);
let start = Instant::now();
while start.elapsed() < PORT_RELEASE_TIMEOUT {
let res = std::net::TcpStream::connect((Ipv4Addr::LOCALHOST, self.port));
if res.is_err() {
break;
}
std::thread::sleep(HOT_LOOP_INTERVAL);
}
self.port
}
}