TcpStream::bind
has an interface problem: it only binds to the first resolved address.
When TcpStream::bind
is called with param &("localhost", 12345)
it should bind to all addresses of localhost. Currently it binds only to IPv6 address on my Mac.
It is usually not what developers expect. If server has both IPv4 and IPv6 address, they want to bind on all addresses.
To address this problem TcpStream::bind
should return something like io::Result<Vec<TcpListener>>
.
(And probably, simpler functions
fn bind_one(addr: SocketAddr) -> io::Result<TcpListener>
fn bind_any(addr: ToSocketAddrs) -> io::Result<TcpListener>
should be introduced).
(BTW, empty string for hostname, &("", 12345).to_socket_addrs()
should be allowed, and should be resolved to the list of 0.0.0.0
and ::
, so developers could easily bind all protocols, this idea is from Python).
bind
signature change is easy. However, it implies harder thing.
zero port
When the port is zero, operating system has an algorithm to bind to some available port. However, there is no syscall to bind several sockets to the same port at once. So binding algorithm inside TcpStream
should be something like this (pseudocode):
fn bind_all(addrs: Iterator<SocketAddr>)
-> io::Result<Vec<TcpListener>>
{
let first_addr = addrs.next().unwrap();
// bind first socket to find available port from the operating system
// and hope that other protocols do not already use this address
let first_socket = try!(bind(first_addr));
let common_port = first_socket.get_sock_name().port;
let mut sockets = vec![first_socket];
for addr in addrs {
if (first_addr.port != 0 || addr.port != 0) && first_addr.port != addr.port {
return Err("cannot have both zero and non-zero port");
}
if addr.port == 0 {
// set the port from the first socket
// to make sure all sockets use the same port
addr.set_port(common_port);
}
// fail if we could not bind to any address
sockets.push(try!(bind(addr));
}
return Ok(sockets);
}
Binding to the same random port for all protocols is important, because it allows application to announce single port for hostname (e. g. server1.company.com:45654
), instead of a list of (socketaddr, port) pairs for hostname.
The summary of proposed changes
-
ToSocketAddrs
for("", port)
should return["0.0.0.0", "::"]
- Introduce simpler
bind_one(addr: SocketAddr)
and/orbind_any(addr: ToSocketAddr)
-
TcpStream::bind
should bind to all socket addresses -
TcpStream::bind
shoud bind all sockets to the same port if port is unspecified
Alternatively
If Vec<TcpListener>
is too much for the standard library, then at least bind
should fail, if ToSocketAddrs
resolves to more than one address. Because it is easier to crash at start than spend time discovering why clients cannot connect to the server (due to different IP versions on clients, firewall configurations, different client implementations etc).
And even if that is unacceptable, then current behavior should be at least documented.