Added sourcePort to Socket.connect()/startConnect() as optional parameter.

This allows to specify the local port for TCP client sockets.

All prototypes in Socket,RawSocket,_Rawsocket,_NativeSocket have been adopted,
aswell as the native counterpart to nativeCreateBindConnect.

TEST=new tests added

Change-Id: I3408b687cbfd7eaaaeafdda29f7093d92c92aea0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/217604
Reviewed-by: Siva Annamalai <asiva@google.com>
Auto-Submit: Moritz Feldmann <moritz.feldmann@kabelmail.de>
Reviewed-by: Kevin Moore <kevmoo@google.com>
Commit-Queue: Kevin Moore <kevmoo@google.com>
This commit is contained in:
Mo 2021-12-08 18:33:00 +00:00 committed by Commit Bot
parent 6153e0536b
commit 8caaa1b9cf
12 changed files with 421 additions and 64 deletions

View file

@ -17,6 +17,7 @@
- **Breaking Change** [#47769](https://github.com/dart-lang/sdk/issues/47769):
The `Platform.packageRoot` API has been removed. It had been marked deprecated
in 2018, as it doesn't work with any Dart 2.x release.
- Add optional `sourcePort` parameter to `Socket.connect`, `Socket.startConnect`, `RawSocket.connect` and `RawSocket.startConnect`
#### `dart:isolate`

View file

@ -144,7 +144,7 @@ namespace bin {
V(SocketBase_IsBindError, 2) \
V(Socket_Available, 1) \
V(Socket_AvailableDatagram, 1) \
V(Socket_CreateBindConnect, 5) \
V(Socket_CreateBindConnect, 6) \
V(Socket_CreateUnixDomainBindConnect, 4) \
V(Socket_CreateBindDatagram, 6) \
V(Socket_CreateConnect, 4) \

View file

@ -396,8 +396,13 @@ void FUNCTION_NAME(Socket_CreateBindConnect)(Dart_NativeArguments args) {
SocketAddress::SetAddrPort(&addr, static_cast<intptr_t>(port));
RawAddr sourceAddr;
SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 3), &sourceAddr);
Dart_Handle source_port_arg = Dart_GetNativeArgument(args, 4);
int64_t source_port =
DartUtils::GetInt64ValueCheckRange(source_port_arg, 0, 65535);
SocketAddress::SetAddrPort(&sourceAddr, static_cast<intptr_t>(source_port));
if (addr.addr.sa_family == AF_INET6) {
Dart_Handle scope_id_arg = Dart_GetNativeArgument(args, 4);
Dart_Handle scope_id_arg = Dart_GetNativeArgument(args, 5);
int64_t scope_id =
DartUtils::GetInt64ValueCheckRange(scope_id_arg, 0, 65535);
SocketAddress::SetAddrScope(&addr, scope_id);

View file

@ -475,13 +475,13 @@ class ServerSocket {
class RawSocket {
@patch
static Future<RawSocket> connect(dynamic host, int port,
{dynamic sourceAddress, Duration? timeout}) {
{dynamic sourceAddress, int sourcePort = 0, Duration? timeout}) {
throw UnsupportedError("RawSocket constructor");
}
@patch
static Future<ConnectionTask<RawSocket>> startConnect(dynamic host, int port,
{dynamic sourceAddress}) {
{dynamic sourceAddress, int sourcePort = 0}) {
throw UnsupportedError("RawSocket constructor");
}
}
@ -490,13 +490,13 @@ class RawSocket {
class Socket {
@patch
static Future<Socket> _connect(dynamic host, int port,
{dynamic sourceAddress, Duration? timeout}) {
{dynamic sourceAddress, int sourcePort = 0, Duration? timeout}) {
throw UnsupportedError("Socket constructor");
}
@patch
static Future<ConnectionTask<Socket>> _startConnect(dynamic host, int port,
{dynamic sourceAddress}) {
{dynamic sourceAddress, int sourcePort = 0}) {
throw UnsupportedError("Socket constructor");
}
}

View file

@ -475,13 +475,13 @@ class ServerSocket {
class RawSocket {
@patch
static Future<RawSocket> connect(dynamic host, int port,
{dynamic sourceAddress, Duration? timeout}) {
{dynamic sourceAddress, int sourcePort = 0, Duration? timeout}) {
throw new UnsupportedError("RawSocket constructor");
}
@patch
static Future<ConnectionTask<RawSocket>> startConnect(dynamic host, int port,
{dynamic sourceAddress}) {
{dynamic sourceAddress, int sourcePort = 0}) {
throw new UnsupportedError("RawSocket constructor");
}
}
@ -490,13 +490,13 @@ class RawSocket {
class Socket {
@patch
static Future<Socket> _connect(dynamic host, int port,
{dynamic sourceAddress, Duration? timeout}) {
{dynamic sourceAddress, int sourcePort = 0, Duration? timeout}) {
throw new UnsupportedError("Socket constructor");
}
@patch
static Future<ConnectionTask<Socket>> _startConnect(dynamic host, int port,
{dynamic sourceAddress}) {
{dynamic sourceAddress, int sourcePort = 0}) {
throw new UnsupportedError("Socket constructor");
}
}

View file

@ -17,14 +17,14 @@ class RawServerSocket {
class RawSocket {
@patch
static Future<RawSocket> connect(dynamic host, int port,
{dynamic sourceAddress, Duration? timeout}) {
return _RawSocket.connect(host, port, sourceAddress, timeout);
{dynamic sourceAddress, int sourcePort = 0, Duration? timeout}) {
return _RawSocket.connect(host, port, sourceAddress, sourcePort, timeout);
}
@patch
static Future<ConnectionTask<RawSocket>> startConnect(dynamic host, int port,
{dynamic sourceAddress}) {
return _RawSocket.startConnect(host, port, sourceAddress);
{dynamic sourceAddress, int sourcePort = 0}) {
return _RawSocket.startConnect(host, port, sourceAddress, sourcePort);
}
}
@ -654,7 +654,7 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
}
static Future<ConnectionTask<_NativeSocket>> startConnect(
dynamic host, int port, dynamic sourceAddress) {
dynamic host, int port, dynamic sourceAddress, int sourcePort) {
// Looks up [sourceAddress] to one or more IP addresses,
// then tries connecting to each one until a connection succeeds.
// Attempts are staggered by a minimum delay, so a new
@ -666,6 +666,7 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
host = escapeLinkLocalAddress(host);
}
_throwOnBadPort(port);
_throwOnBadPort(sourcePort);
_InternetAddress? source;
if (sourceAddress != null) {
if (sourceAddress is _InternetAddress) {
@ -682,7 +683,7 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
return new Future.value(host).then<ConnectionTask<_NativeSocket>>((host) {
if (host is _InternetAddress) {
return tryConnectToResolvedAddresses(host, port, source,
return tryConnectToResolvedAddresses(host, port, source, sourcePort,
Stream.value(<_InternetAddress>[host]), stackTrace);
}
final hostname = host as String;
@ -700,7 +701,7 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
: lookupAsStream(hostname);
return tryConnectToResolvedAddresses(
host, port, source, stream, stackTrace);
host, port, source, sourcePort, stream, stackTrace);
});
}
@ -708,6 +709,7 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
dynamic host,
int port,
_InternetAddress? source,
int sourcePort,
Stream<List<InternetAddress>> addresses,
StackTrace callerStackTrace) {
// Completer for result.
@ -758,10 +760,16 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
connectionResult is OSError);
} else {
final address_ = address as _InternetAddress;
if (source == null) {
if (source == null && sourcePort == 0) {
connectionResult = socket.nativeCreateConnect(
address_._in_addr, port, address_._scope_id);
} else {
// allow specified port without address
if (source == null) {
source = address_.type == InternetAddressType.IPv4
? _InternetAddress.anyIPv4
: _InternetAddress.anyIPv6;
}
if (source.type != InternetAddressType.IPv4 &&
source.type != InternetAddressType.IPv6) {
return SocketException(
@ -773,8 +781,8 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
"${InternetAddressType.IPv6} but was ${source.type}",
address: address);
}
connectionResult = socket.nativeCreateBindConnect(
address_._in_addr, port, source._in_addr, address_._scope_id);
connectionResult = socket.nativeCreateBindConnect(address_._in_addr,
port, source._in_addr, sourcePort, address_._scope_id);
}
assert(connectionResult == true || connectionResult is OSError);
}
@ -935,9 +943,9 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
return new ConnectionTask<_NativeSocket>._(result.future, onCancel);
}
static Future<_NativeSocket> connect(
dynamic host, int port, dynamic sourceAddress, Duration? timeout) {
return startConnect(host, port, sourceAddress)
static Future<_NativeSocket> connect(dynamic host, int port,
dynamic sourceAddress, int sourcePort, Duration? timeout) {
return startConnect(host, port, sourceAddress, sourcePort)
.then((ConnectionTask<_NativeSocket> task) {
Future<_NativeSocket> socketFuture = task.socket;
if (timeout != null) {
@ -1653,8 +1661,8 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject {
@pragma("vm:external-name", "Socket_CreateUnixDomainConnect")
external nativeCreateUnixDomainConnect(String addr, _Namespace namespace);
@pragma("vm:external-name", "Socket_CreateBindConnect")
external nativeCreateBindConnect(
Uint8List addr, int port, Uint8List sourceAddr, int scope_id);
external nativeCreateBindConnect(Uint8List addr, int port,
Uint8List sourceAddr, int sourcePort, int scope_id);
@pragma("vm:external-name", "Socket_CreateUnixDomainBindConnect")
external nativeCreateUnixDomainBindConnect(
String addr, String sourceAddr, _Namespace namespace);
@ -1792,9 +1800,9 @@ class _RawSocket extends Stream<RawSocketEvent> implements RawSocket {
// Flag to handle Ctrl-D closing of stdio on Mac OS.
bool _isMacOSTerminalInput = false;
static Future<RawSocket> connect(
dynamic host, int port, dynamic sourceAddress, Duration? timeout) {
return _NativeSocket.connect(host, port, sourceAddress, timeout)
static Future<RawSocket> connect(dynamic host, int port,
dynamic sourceAddress, int sourcePort, Duration? timeout) {
return _NativeSocket.connect(host, port, sourceAddress, sourcePort, timeout)
.then((socket) {
if (!const bool.fromEnvironment("dart.vm.product")) {
_SocketProfile.collectNewSocket(
@ -1805,8 +1813,8 @@ class _RawSocket extends Stream<RawSocketEvent> implements RawSocket {
}
static Future<ConnectionTask<_RawSocket>> startConnect(
dynamic host, int port, dynamic sourceAddress) {
return _NativeSocket.startConnect(host, port, sourceAddress)
dynamic host, int port, dynamic sourceAddress, int sourcePort) {
return _NativeSocket.startConnect(host, port, sourceAddress, sourcePort)
.then((ConnectionTask<_NativeSocket> nativeTask) {
final Future<_RawSocket> raw =
nativeTask.socket.then((_NativeSocket nativeSocket) {
@ -2016,16 +2024,19 @@ class _ServerSocket extends Stream<Socket> implements ServerSocket {
class Socket {
@patch
static Future<Socket> _connect(dynamic host, int port,
{dynamic sourceAddress, Duration? timeout}) {
{dynamic sourceAddress, int sourcePort = 0, Duration? timeout}) {
return RawSocket.connect(host, port,
sourceAddress: sourceAddress, timeout: timeout)
sourceAddress: sourceAddress,
sourcePort: sourcePort,
timeout: timeout)
.then((socket) => new _Socket(socket));
}
@patch
static Future<ConnectionTask<Socket>> _startConnect(dynamic host, int port,
{dynamic sourceAddress}) {
return RawSocket.startConnect(host, port, sourceAddress: sourceAddress)
{dynamic sourceAddress, int sourcePort = 0}) {
return RawSocket.startConnect(host, port,
sourceAddress: sourceAddress, sourcePort: sourcePort)
.then((rawTask) {
Future<Socket> socket =
rawTask.socket.then((rawSocket) => new _Socket(rawSocket));

View file

@ -81,10 +81,10 @@ abstract class IOOverrides {
// Socket
Future<Socket> Function(dynamic, int,
{dynamic sourceAddress, Duration? timeout})?
{dynamic sourceAddress, int sourcePort, Duration? timeout})?
socketConnect,
Future<ConnectionTask<Socket>> Function(dynamic, int,
{dynamic sourceAddress})?
{dynamic sourceAddress, int sourcePort})?
socketStartConnect,
// ServerSocket
@ -269,9 +269,9 @@ abstract class IOOverrides {
/// When this override is installed, this functions overrides the behavior of
/// `Socket.connect(...)`.
Future<Socket> socketConnect(host, int port,
{sourceAddress, Duration? timeout}) {
{sourceAddress, int sourcePort = 0, Duration? timeout}) {
return Socket._connect(host, port,
sourceAddress: sourceAddress, timeout: timeout);
sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout);
}
/// Asynchronously returns a [ConnectionTask] that connects to the given host
@ -280,8 +280,9 @@ abstract class IOOverrides {
/// When this override is installed, this functions overrides the behavior of
/// `Socket.startConnect(...)`.
Future<ConnectionTask<Socket>> socketStartConnect(host, int port,
{sourceAddress}) {
return Socket._startConnect(host, port, sourceAddress: sourceAddress);
{sourceAddress, int sourcePort = 0}) {
return Socket._startConnect(host, port,
sourceAddress: sourceAddress, sourcePort: sourcePort);
}
// ServerSocket
@ -355,9 +356,11 @@ class _IOOverridesScope extends IOOverrides {
// Socket
Future<Socket> Function(dynamic, int,
{dynamic sourceAddress, Duration? timeout})? _socketConnect;
{dynamic sourceAddress,
int sourcePort,
Duration? timeout})? _socketConnect;
Future<ConnectionTask<Socket>> Function(dynamic, int,
{dynamic sourceAddress})? _socketStartConnect;
{dynamic sourceAddress, int sourcePort})? _socketStartConnect;
// ServerSocket
Future<ServerSocket> Function(dynamic, int,
@ -518,30 +521,34 @@ class _IOOverridesScope extends IOOverrides {
// Socket
@override
Future<Socket> socketConnect(host, int port,
{sourceAddress, Duration? timeout}) {
{sourceAddress, int sourcePort = 0, Duration? timeout}) {
if (_socketConnect != null) {
return _socketConnect!(host, port,
sourceAddress: sourceAddress, timeout: timeout);
}
if (_previous != null) {
return _previous!.socketConnect(host, port,
sourceAddress: sourceAddress, timeout: timeout);
sourceAddress: sourceAddress,
sourcePort: sourcePort,
timeout: timeout);
}
return super.socketConnect(host, port,
sourceAddress: sourceAddress, timeout: timeout);
sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout);
}
@override
Future<ConnectionTask<Socket>> socketStartConnect(host, int port,
{sourceAddress}) {
{sourceAddress, int sourcePort = 0}) {
if (_socketStartConnect != null) {
return _socketStartConnect!(host, port, sourceAddress: sourceAddress);
return _socketStartConnect!(host, port,
sourceAddress: sourceAddress, sourcePort: sourcePort);
}
if (_previous != null) {
return _previous!
.socketStartConnect(host, port, sourceAddress: sourceAddress);
return _previous!.socketStartConnect(host, port,
sourceAddress: sourceAddress, sourcePort: sourcePort);
}
return super.socketStartConnect(host, port, sourceAddress: sourceAddress);
return super.socketStartConnect(host, port,
sourceAddress: sourceAddress, sourcePort: sourcePort);
}
// ServerSocket

View file

@ -589,19 +589,22 @@ abstract class RawSocket implements Stream<RawSocketEvent> {
/// be a [String] or an [InternetAddress]. If a [String] is passed it must
/// hold a numeric IP address.
///
/// The [sourcePort] defines the local port to bind to. If [sourcePort] is
/// not specified or zero, a port will be chosen.
///
/// The argument [timeout] is used to specify the maximum allowed time to wait
/// for a connection to be established. If [timeout] is longer than the system
/// level timeout duration, a timeout may occur sooner than specified in
/// [timeout]. On timeout, a [SocketException] is thrown and all ongoing
/// connection attempts to [host] are cancelled.
external static Future<RawSocket> connect(host, int port,
{sourceAddress, Duration? timeout});
{sourceAddress, int sourcePort = 0, Duration? timeout});
/// Like [connect], but returns a [Future] that completes with a
/// [ConnectionTask] that can be cancelled if the [RawSocket] is no
/// longer needed.
external static Future<ConnectionTask<RawSocket>> startConnect(host, int port,
{sourceAddress});
{sourceAddress, int sourcePort = 0});
/// The number of received and non-read bytes in the socket that can be read.
int available();
@ -758,40 +761,46 @@ abstract class Socket implements Stream<Uint8List>, IOSink {
/// be a [String] or an [InternetAddress]. If a [String] is passed it must
/// hold a numeric IP address.
///
/// The [sourcePort] defines the local port to bind to. If [sourcePort] is
/// not specified or zero, a port will be chosen.
///
/// The argument [timeout] is used to specify the maximum allowed time to wait
/// for a connection to be established. If [timeout] is longer than the system
/// level timeout duration, a timeout may occur sooner than specified in
/// [timeout]. On timeout, a [SocketException] is thrown and all ongoing
/// connection attempts to [host] are cancelled.
static Future<Socket> connect(host, int port,
{sourceAddress, Duration? timeout}) {
{sourceAddress, int sourcePort = 0, Duration? timeout}) {
final IOOverrides? overrides = IOOverrides.current;
if (overrides == null) {
return Socket._connect(host, port,
sourceAddress: sourceAddress, timeout: timeout);
sourceAddress: sourceAddress,
sourcePort: sourcePort,
timeout: timeout);
}
return overrides.socketConnect(host, port,
sourceAddress: sourceAddress, timeout: timeout);
sourceAddress: sourceAddress, sourcePort: sourcePort, timeout: timeout);
}
/// Like [connect], but returns a [Future] that completes with a
/// [ConnectionTask] that can be cancelled if the [Socket] is no
/// longer needed.
static Future<ConnectionTask<Socket>> startConnect(host, int port,
{sourceAddress}) {
{sourceAddress, int sourcePort = 0}) {
final IOOverrides? overrides = IOOverrides.current;
if (overrides == null) {
return Socket._startConnect(host, port, sourceAddress: sourceAddress);
return Socket._startConnect(host, port,
sourceAddress: sourceAddress, sourcePort: sourcePort);
}
return overrides.socketStartConnect(host, port,
sourceAddress: sourceAddress);
sourceAddress: sourceAddress, sourcePort: sourcePort);
}
external static Future<Socket> _connect(host, int port,
{sourceAddress, Duration? timeout});
{sourceAddress, int sourcePort = 0, Duration? timeout});
external static Future<ConnectionTask<Socket>> _startConnect(host, int port,
{sourceAddress});
{sourceAddress, int sourcePort = 0});
/// Destroys the socket in both directions.
///

View file

@ -161,12 +161,12 @@ class LinkMock extends FileSystemEntity implements Link {
}
Future<Socket> socketConnect(dynamic host, int port,
{dynamic sourceAddress, Duration? timeout}) {
{dynamic sourceAddress, int sourcePort = 0, Duration? timeout}) {
throw "";
}
Future<ConnectionTask<Socket>> socketStartConnect(dynamic host, int port,
{dynamic sourceAddress}) {
{dynamic sourceAddress, int sourcePort = 0}) {
throw "";
}

View file

@ -0,0 +1,162 @@
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import "dart:io";
import "package:expect/expect.dart";
Future testCustomPortIPv4() async {
String clientAddress = "127.0.0.1";
int customLocalPort = 50988;
String serverAddress = clientAddress;
int port = 50989;
testCustomPort(serverAddress, port, clientAddress, customLocalPort);
}
Future testCustomPortIPv6() async {
String clientAddress = "::1";
int customLocalPort = 50988;
String serverAddress = clientAddress;
int port = 50989;
testCustomPort(serverAddress, port, clientAddress, customLocalPort);
}
Future testCustomPortIPv4NoSourceAddress() async {
String expectedClientAddress = "127.0.0.1";
int customLocalPort = 50988;
String serverAddress = expectedClientAddress;
int port = 50989;
testCustomPort(serverAddress, port, expectedClientAddress, customLocalPort);
}
Future testCustomPortIPv6NoSourceAddress() async {
String expectedClientAddress = "::1";
int customLocalPort = 50988;
String serverAddress = expectedClientAddress;
int port = 50989;
testCustomPort(serverAddress, port, expectedClientAddress, customLocalPort);
}
Future testNoCustomPortIPv4() async {
String host = "127.0.0.1";
String clientAddress = host;
int serverPort = 39998;
await testNoCustomPortNoSourceAddress(host, serverPort, clientAddress);
}
Future testNoCustomPortIPv6() async {
String host = "::1";
String clientAddress = host;
int serverPort = 39998;
await testNoCustomPortNoSourceAddress(host, serverPort, clientAddress);
}
Future testNoCustomPortNoSourceAddressIPv4() async {
String host = "127.0.0.1";
String expectedAddress = host;
int serverPort = 39998;
await testNoCustomPortNoSourceAddress(host, serverPort, expectedAddress);
}
Future testNoCustomPortNoSourceAddressIPv6() async {
String host = "::1";
String expectedAddress = host;
int serverPort = 39998;
await testNoCustomPortNoSourceAddress(host, serverPort, expectedAddress);
}
// Core functionality
void 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);
Expect.equals(client.remotePort, sourcePort);
Expect.equals(client.address.address, sourceAddress);
client.destroy();
});
Socket s = await Socket.connect(host, port,
sourceAddress: sourceAddress, sourcePort: sourcePort);
s.destroy();
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);
Expect.equals(client.remotePort, sourcePort);
Expect.equals(client.address.address, expectedAddress);
client.destroy();
completer.complete();
});
Socket s = await Socket.connect(host, port, sourcePort: sourcePort);
s.destroy();
server.close();
return completer.future;
}
Future testNoCustomPort(String host, int port, String sourceAddress) async {
Completer completer = new Completer();
var server = await ServerSocket.bind(host, port);
Socket.connect(host, port, sourceAddress: sourceAddress).then((clientSocket) {
server.listen((client) {
Expect.equals(server.port, port);
Expect.equals(client.remotePort, clientSocket.port);
Expect.equals(client.address.address, sourceAddress);
client.destroy();
completer.complete();
});
clientSocket.destroy();
server.close();
});
return completer.future;
}
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) {
Expect.equals(server.port, port);
Expect.equals(client.remotePort, clientSocket.port);
Expect.equals(client.address.address, expectedAddress);
clientSocket.destroy();
client.destroy();
server.close();
completer.complete();
});
});
return completer.future;
}
Future main() async {
await testCustomPortIPv4();
await testCustomPortIPv6();
await testNoCustomPortIPv4();
await testNoCustomPortIPv6();
await testNoCustomPortNoSourceAddressIPv4();
await testNoCustomPortNoSourceAddressIPv6();
}

View file

@ -161,12 +161,12 @@ class LinkMock extends FileSystemEntity implements Link {
}
Future<Socket> socketConnect(host, int port,
{sourceAddress, Duration timeout}) {
{sourceAddress, int sourcePort, Duration timeout}) {
return null;
}
Future<ConnectionTask<Socket>> socketStartConnect(host, int port,
{sourceAddress}) {
{sourceAddress, int sourcePort}) {
return null;
}

View file

@ -0,0 +1,162 @@
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import "dart:io";
import "package:expect/expect.dart";
Future testCustomPortIPv4() async {
String clientAddress = "127.0.0.1";
int customLocalPort = 50988;
String serverAddress = clientAddress;
int port = 50989;
testCustomPort(serverAddress, port, clientAddress, customLocalPort);
}
Future testCustomPortIPv6() async {
String clientAddress = "::1";
int customLocalPort = 50988;
String serverAddress = clientAddress;
int port = 50989;
testCustomPort(serverAddress, port, clientAddress, customLocalPort);
}
Future testCustomPortIPv4NoSourceAddress() async {
String expectedClientAddress = "127.0.0.1";
int customLocalPort = 50988;
String serverAddress = expectedClientAddress;
int port = 50989;
testCustomPort(serverAddress, port, expectedClientAddress, customLocalPort);
}
Future testCustomPortIPv6NoSourceAddress() async {
String expectedClientAddress = "::1";
int customLocalPort = 50988;
String serverAddress = expectedClientAddress;
int port = 50989;
testCustomPort(serverAddress, port, expectedClientAddress, customLocalPort);
}
Future testNoCustomPortIPv4() async {
String host = "127.0.0.1";
String clientAddress = host;
int serverPort = 39998;
await testNoCustomPortNoSourceAddress(host, serverPort, clientAddress);
}
Future testNoCustomPortIPv6() async {
String host = "::1";
String clientAddress = host;
int serverPort = 39998;
await testNoCustomPortNoSourceAddress(host, serverPort, clientAddress);
}
Future testNoCustomPortNoSourceAddressIPv4() async {
String host = "127.0.0.1";
String expectedAddress = host;
int serverPort = 39998;
await testNoCustomPortNoSourceAddress(host, serverPort, expectedAddress);
}
Future testNoCustomPortNoSourceAddressIPv6() async {
String host = "::1";
String expectedAddress = host;
int serverPort = 39998;
await testNoCustomPortNoSourceAddress(host, serverPort, expectedAddress);
}
// Core functionality
void 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);
Expect.equals(client.remotePort, sourcePort);
Expect.equals(client.address.address, sourceAddress);
client.destroy();
});
Socket s = await Socket.connect(host, port,
sourceAddress: sourceAddress, sourcePort: sourcePort);
s.destroy();
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);
Expect.equals(client.remotePort, sourcePort);
Expect.equals(client.address.address, expectedAddress);
client.destroy();
completer.complete();
});
Socket s = await Socket.connect(host, port, sourcePort: sourcePort);
s.destroy();
server.close();
return completer.future;
}
Future testNoCustomPort(String host, int port, String sourceAddress) async {
Completer completer = new Completer();
var server = await ServerSocket.bind(host, port);
Socket.connect(host, port, sourceAddress: sourceAddress).then((clientSocket) {
server.listen((client) {
Expect.equals(server.port, port);
Expect.equals(client.remotePort, clientSocket.port);
Expect.equals(client.address.address, sourceAddress);
client.destroy();
completer.complete();
});
clientSocket.destroy();
server.close();
});
return completer.future;
}
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) {
Expect.equals(server.port, port);
Expect.equals(client.remotePort, clientSocket.port);
Expect.equals(client.address.address, expectedAddress);
clientSocket.destroy();
client.destroy();
server.close();
completer.complete();
});
});
return completer.future;
}
Future main() async {
await testCustomPortIPv4();
await testCustomPortIPv6();
await testNoCustomPortIPv4();
await testNoCustomPortIPv6();
await testNoCustomPortNoSourceAddressIPv4();
await testNoCustomPortNoSourceAddressIPv6();
}