[io/gardening] Fix client address binding & fix standalone/io/socket_local_port_test

Various changes to the stanalone/io/socket_local_port_test:

  * Make server ports ephemeral: The test is testing client-side
    binding of address & port, the server part can use ephemeral ports.
    => This eliminates the issue of another process using the
       hard-coded port
  * Close sockets normally instead of using `Socket.destroy()`
  * Make dead code alive: There were test in the tests that were
    not invoked by `main()`.
  * Align the individual test helper functions.
  * ...

Then we change the dart:io implementation of `Socket::CreateBindConnect`
to set the `SO_REUSEADDR` socket option. We do this already for the
server side in `Socket::CreateBindListen`, now we do it also for the
client side.
=> This will ensure that one can bind the client side socket to
   specific address/source despite there being an old closed
   socket that's now in `TIME_WAIT` state.
=> This is the same reason we also do it in `Socket::CreateBindListen`.

Fuchsia doesn't implement `Socket::CreateBindConnect` and on Windows
the socket option seemingly has different semantics (we also don't use
`SO_REUSEADDR` on windows for the server socket, but another option).

Issue https://github.com/dart-lang/sdk/issues/51477

TEST=standalone/io/socket_local_port_test

Change-Id: I7d07becad0cd98c3a9b973ef2f9037730d3f8b19
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/436902
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Martin Kustermann
2025-06-25 02:56:21 -07:00
committed by Commit Queue
parent fecf1b0750
commit 931ec382d4
3 changed files with 80 additions and 94 deletions
+4
View File
@@ -81,6 +81,10 @@ intptr_t Socket::CreateBindConnect(const RawAddr& addr,
return fd;
}
int optval = 1;
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
intptr_t result = TEMP_FAILURE_RETRY(
bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr)));
if (result != 0) {
+4
View File
@@ -86,6 +86,10 @@ intptr_t Socket::CreateBindConnect(const RawAddr& addr,
return fd;
}
int optval = 1;
VOID_NO_RETRY_EXPECTED(
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)));
intptr_t result = TEMP_FAILURE_RETRY(
bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(source_addr)));
if (result != 0) {
+72 -94
View File
@@ -8,46 +8,40 @@ import "dart:io";
import "package:expect/expect.dart";
Future testCustomPortIPv4() {
String clientAddress = "127.0.0.1";
String host = "127.0.0.1";
int customLocalPort = 50988;
String serverAddress = clientAddress;
int port = 50989;
String customAddress = host;
return testCustomPort(serverAddress, port, clientAddress, customLocalPort);
return testCustomPort(host, customAddress, customLocalPort);
}
Future testCustomPortIPv6() {
String clientAddress = "::1";
int customLocalPort = 50988;
String serverAddress = clientAddress;
int port = 50989;
String host = "::1";
int customLocalPort = 50989;
String customAddress = host;
return testCustomPort(serverAddress, port, clientAddress, customLocalPort);
return testCustomPort(host, customAddress, customLocalPort);
}
Future testCustomPortIPv4NoSourceAddress() {
String expectedClientAddress = "127.0.0.1";
int customLocalPort = 50988;
String serverAddress = expectedClientAddress;
int port = 50989;
String host = "127.0.0.1";
int customLocalPort = 50990;
String expectedClientAddress = host;
return testCustomPort(
serverAddress,
port,
return testCustomPortNoSourceAddress(
host,
expectedClientAddress,
customLocalPort,
);
}
Future testCustomPortIPv6NoSourceAddress() {
String expectedClientAddress = "::1";
int customLocalPort = 50988;
String serverAddress = expectedClientAddress;
int port = 50989;
String host = "::1";
int customLocalPort = 50991;
String expectedClientAddress = host;
return testCustomPort(
serverAddress,
port,
return testCustomPortNoSourceAddress(
host,
expectedClientAddress,
customLocalPort,
);
@@ -56,134 +50,118 @@ Future testCustomPortIPv6NoSourceAddress() {
Future testNoCustomPortIPv4() {
String host = "127.0.0.1";
String clientAddress = host;
int serverPort = 39998;
return testNoCustomPortNoSourceAddress(host, serverPort, clientAddress);
return testNoCustomPort(host, clientAddress);
}
Future testNoCustomPortIPv6() {
String host = "::1";
String clientAddress = host;
int serverPort = 39998;
return testNoCustomPortNoSourceAddress(host, serverPort, clientAddress);
return testNoCustomPort(host, clientAddress);
}
Future testNoCustomPortNoSourceAddressIPv4() {
String host = "127.0.0.1";
String expectedAddress = host;
int serverPort = 39998;
return testNoCustomPortNoSourceAddress(host, serverPort, expectedAddress);
return testNoCustomPortNoSourceAddress(host, expectedAddress);
}
Future testNoCustomPortNoSourceAddressIPv6() {
String host = "::1";
String expectedAddress = host;
int serverPort = 39998;
return testNoCustomPortNoSourceAddress(host, serverPort, expectedAddress);
return testNoCustomPortNoSourceAddress(host, expectedAddress);
}
// Core functionality
Future testCustomPort(
String host,
int port,
String sourceAddress,
int sourcePort,
) async {
var server = await ServerSocket.bind(host, port);
server.listen((client) {
Expect.equals(server.port, port);
Future testCustomPort(String host, String sourceAddress, int sourcePort) async {
final serverTestDone = Completer();
final server = await ServerSocket.bind(host, 0);
server.listen((Socket client) async {
Expect.equals(client.remotePort, sourcePort);
Expect.equals(client.address.address, sourceAddress);
client.destroy();
await (client.close(), client.drain()).wait;
serverTestDone.complete();
});
Socket s = await Socket.connect(
final client = await Socket.connect(
host,
port,
server.port,
sourceAddress: sourceAddress,
sourcePort: sourcePort,
);
s.destroy();
await (client.close(), client.drain()).wait;
await serverTestDone.future;
await server.close();
}
Future testCustomPortNoSourceAddress(
String host,
int port,
String expectedAddress,
int sourcePort,
) async {
Completer completer = new Completer();
var server = await ServerSocket.bind(host, port);
server.listen((client) {
Expect.equals(server.port, port);
final serverTestDone = Completer();
final server = await ServerSocket.bind(host, 0);
server.listen((Socket client) async {
Expect.equals(client.remotePort, sourcePort);
Expect.equals(client.address.address, expectedAddress);
client.destroy();
completer.complete();
await (client.close(), client.drain()).wait;
serverTestDone.complete();
});
Socket s = await Socket.connect(host, port, sourcePort: sourcePort);
s.destroy();
final client = await Socket.connect(
host,
server.port,
sourcePort: sourcePort,
);
await (client.close(), client.drain()).wait;
await serverTestDone.future;
await server.close();
return completer.future;
}
Future testNoCustomPort(String host, int port, String sourceAddress) async {
Completer serverCompleter = new Completer();
Completer clientCompleter = new Completer();
var server = await ServerSocket.bind(host, port);
Socket.connect(host, port, sourceAddress: sourceAddress).then((
clientSocket,
) async {
server.listen((client) async {
Expect.equals(server.port, port);
Expect.equals(client.remotePort, clientSocket.port);
Expect.equals(client.address.address, sourceAddress);
client.destroy();
clientCompleter.complete();
});
clientSocket.destroy();
await server.close();
serverCompleter.complete();
Future testNoCustomPort(String host, String sourceAddress) async {
final serverTestDone = Completer();
final server = await ServerSocket.bind(host, 0);
server.listen((Socket client) async {
Expect.equals(client.address.address, sourceAddress);
await (client.close(), client.drain()).wait;
serverTestDone.complete();
});
await serverCompleter.future;
await clientCompleter.future;
final client = await Socket.connect(
host,
server.port,
sourceAddress: sourceAddress,
);
await (client.close(), client.drain()).wait;
await serverTestDone.future;
await server.close();
}
Future testNoCustomPortNoSourceAddress(
String host,
int port,
String expectedAddress,
) async {
Completer completer = new Completer();
var server = await ServerSocket.bind(host, port);
Socket.connect(host, port).then((clientSocket) {
server.listen((client) async {
Expect.equals(server.port, port);
Expect.equals(client.remotePort, clientSocket.port);
Expect.equals(client.address.address, expectedAddress);
clientSocket.destroy();
client.destroy();
await server.close();
completer.complete();
});
final serverTestDone = Completer();
final server = await ServerSocket.bind(host, 0);
server.listen((Socket client) async {
Expect.equals(client.address.address, expectedAddress);
await (client.close(), client.drain()).wait;
serverTestDone.complete();
});
return completer.future;
final client = await Socket.connect(host, server.port);
await (client.close(), client.drain()).wait;
await serverTestDone.future;
await server.close();
}
Future main() async {
await testCustomPortIPv4();
await testCustomPortIPv6();
await testCustomPortIPv4NoSourceAddress();
await testCustomPortIPv6NoSourceAddress();
await testNoCustomPortIPv4();
await testNoCustomPortIPv6();