use std::io;
use std::net::SocketAddr;
use socket2::{Domain, Protocol, Socket, Type};
const BACKLOG: i32 = 1024;
pub fn bind(addr: SocketAddr, reuse_port: bool) -> io::Result<std::net::TcpListener> {
let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?;
socket.set_reuse_address(true)?;
if reuse_port {
set_reuse_port(&socket)?;
}
socket.set_nonblocking(true)?;
socket.bind(&addr.into())?;
socket.listen(BACKLOG)?;
Ok(socket.into())
}
pub fn bind_tokio(addr: SocketAddr, reuse_port: bool) -> io::Result<tokio::net::TcpListener> {
tokio::net::TcpListener::from_std(bind(addr, reuse_port)?)
}
#[cfg(unix)]
fn set_reuse_port(socket: &Socket) -> io::Result<()> {
socket.set_reuse_port(true)
}
#[cfg(not(unix))]
fn set_reuse_port(_socket: &Socket) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
"--reuse-port needs SO_REUSEPORT, which this platform lacks",
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn listeners_share_a_port_only_when_both_ask() {
let first = bind("127.0.0.1:0".parse().unwrap(), true).unwrap();
let addr = first.local_addr().unwrap();
let second = bind(addr, true).unwrap();
assert_eq!(second.local_addr().unwrap(), addr);
assert!(bind(addr, false).is_err());
drop((first, second));
let alone = bind("127.0.0.1:0".parse().unwrap(), false).unwrap();
assert!(bind(alone.local_addr().unwrap(), true).is_err());
}
}