-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(net): added reuseAddress and reusePort in Sockets API #13794
Conversation
let socket = Socket::new(domain, Type::STREAM, None)?; | ||
if reuse_address.is_some() { | ||
socket.set_reuse_address(reuse_address.unwrap())?; | ||
} else { | ||
socket.set_reuse_address(true)?; | ||
} | ||
if reuse_port.is_some() { | ||
socket.set_reuse_port(reuse_port.unwrap())?; | ||
} | ||
let socket_addr = socket2::SockAddr::from(bind_addr); | ||
socket.bind(&socket_addr)?; | ||
socket.listen(128)?; | ||
socket.set_nonblocking(true)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe these are only available for unix systems which causes CI failure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is the case. I noticed that SO_REUSEADDR in windows seems to achieve the same results of SO_REUSEPORT in Linux. I will add a #[cfg(unix)]
to address this issue.
I also noticed that once swapped to Socket2
for the creation of TcpListener
, I had to set SO_REUSEADDR to true
by default in order to pass the net_test
unit tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Trolloldem that's a bit concerning. May I suggest to open a new PR that first switches to socket2
crate and then add new APIs in follow up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bartlomieju this is ok to me. But I think this is due to the different way the std::net
treats the presence of
a socket in the TIME_WAIT state compared to socket2
. During the test I used the netstat
utility to investigate why these test failures appened, and I noticed that the socket previously closed by other test were in the TIME_WAIT state, thus not allowing other sockets to bind to their hostname-port pair.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, that's why I'm suggesting to split the PR into two - so we can unsure that when changing the crates we keep the same behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I will open a new PR with only the changes needed for socket2
. When it is ready I will close this one, and propose the changes for SO_REUSEADDR in a new one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before starting to work on the new PR, I wanted to check how the current std::net::TcpListener
is configured. I used the following code:
fn listen_tcp(
state: &mut OpState,
addr: SocketAddr,
) -> Result<(u32, SocketAddr), AnyError> {
let std_listener1 = std::net::TcpListener::bind(&addr)?;
let so: socket2::Socket = socket2::Socket::from(std_listener1);
if so.reuse_address()? {
panic!("The reuseADDR is: {}", so.reuse_address()?);
}
let std_listener: std::net::TcpListener = so.into();
std_listener.set_nonblocking(true)?;
let listener = TcpListener::from_std(std_listener)?;
let local_addr = listener.local_addr()?;
let listener_resource = TcpListenerResource {
listener: AsyncRefCell::new(listener),
cancel: Default::default(),
};
let rid = state.resource_table.add(listener_resource);
Ok((rid, local_addr))
}
And the I run:
target/debug/deno test --allow-all --unstable --location=http://js-unit-tests/foo/bar cli/tests/unit/net_test.ts
The obtained output is the following:
running 33 tests from file:///home/oldem/git/deno/cli/tests/unit/net_test.ts
test netTcpListenClose ...
============================================================
Deno has panicked. This is a bug in Deno. Please report this
at https://github.com/denoland/deno/issues/new.
If you can reliably reproduce this panic, include the
reproduction steps and re-run with the RUST_BACKTRACE=1 env
var set and include the backtrace in your report.
Platform: linux x86_64
Version: 1.19.1
Args: ["target/debug/deno", "test", "--allow-all", "--unstable", "--location=http://js-unit-tests/foo/bar", "cli/tests/unit/net_test.ts"]
thread '<unnamed>' panicked at 'The reuseADDR is: true', ext/net/ops.rs:434:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The panic does not happen when the check over so.reuse_address()
is not present.
So, at least on Linux, it does seem that in this moment the socket already has SO_REUSEADDR option configured.
It is still preferable to separate the two PR?
Support for SO_REUSEADDR socket option is requested in #1195, to avoid using different ports during tests. In order to achieve the wanted feature, it now possible to use the SO_REUSEPORT socket option in addition.
The implemented changes are:
Deno.listen
andDeno.listenTls
, namelyreuseAddr
andreusePort
(one for each supported option)socket2
entry to support the SO_REUSEPORT optionreusePort
option works as intendedIn this moment, the option is checked and set only for
op_net_listen
andop_tls_listen
. In particular, forop_net_listen
, changes are applied only to thelisten_tcp
method.After testing
reuseAddr
, it has been noted that thetokio::net::lookup_host
function use to solve the provided hostname, sometimes resolves the address0.0.0.0
to127.0.0.1
, leading to unwanted effects when the following code is used:One possible solution could be using the dedicated dns-lookup package