7b5bd07e87
Pausing read on the SecureSocket should pause read on the underlying socket, because otherwise we will be stuck calling _readHandler which will not read anything but still schedule an SSL filter wasting CPU time. Fixes https://github.com/dart-lang/sdk/issues/62037 CoreLibraryReviewExempt: No API changes, VM specific implementation change Change-Id: I4144815d34c1e77a68533b6ebcb66c146a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/467804 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Slava Egorov <vegorov@google.com>
277 lines
7.4 KiB
Dart
277 lines
7.4 KiB
Dart
// Copyright (c) 2013, 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.
|
|
//
|
|
// VMOptions=
|
|
// VMOptions=--short_socket_read
|
|
// VMOptions=--short_socket_write
|
|
// VMOptions=--short_socket_read --short_socket_write
|
|
// OtherResources=certificates/server_chain.pem
|
|
// OtherResources=certificates/server_key.pem
|
|
// OtherResources=certificates/trusted_certs.pem
|
|
|
|
import "dart:async";
|
|
import "dart:io";
|
|
|
|
import "package:expect/async_helper.dart";
|
|
import "package:expect/expect.dart";
|
|
|
|
late InternetAddress HOST;
|
|
|
|
String localFile(path) => Platform.script.resolve(path).toFilePath();
|
|
|
|
SecurityContext serverContext = new SecurityContext()
|
|
..useCertificateChain(localFile('certificates/server_chain.pem'))
|
|
..usePrivateKey(
|
|
localFile('certificates/server_key.pem'),
|
|
password: 'dartdart',
|
|
);
|
|
|
|
SecurityContext clientContext = new SecurityContext()
|
|
..setTrustedCertificates(localFile('certificates/trusted_certs.pem'));
|
|
|
|
void testSimpleBind() {
|
|
asyncStart();
|
|
SecureServerSocket.bind(HOST, 0, serverContext).then((s) {
|
|
Expect.isTrue(s.port > 0);
|
|
s.close();
|
|
asyncEnd();
|
|
});
|
|
}
|
|
|
|
void testInvalidBind() {
|
|
int count = 0;
|
|
|
|
// Bind to a unknown DNS name.
|
|
asyncStart();
|
|
SecureServerSocket.bind("ko.faar.__hest__", 0, serverContext)
|
|
.then((_) {
|
|
Expect.fail("Failure expected");
|
|
})
|
|
.catchError((error) {
|
|
Expect.isTrue(error is SocketException);
|
|
asyncEnd();
|
|
});
|
|
|
|
// Bind to an unavailable IP-address.
|
|
asyncStart();
|
|
SecureServerSocket.bind("8.8.8.8", 0, serverContext)
|
|
.then((_) {
|
|
Expect.fail("Failure expected");
|
|
})
|
|
.catchError((error) {
|
|
Expect.isTrue(error is SocketException);
|
|
asyncEnd();
|
|
});
|
|
|
|
// Bind to a port already in use.
|
|
asyncStart();
|
|
SecureServerSocket.bind(HOST, 0, serverContext).then((s) {
|
|
SecureServerSocket.bind(HOST, s.port, serverContext)
|
|
.then((t) {
|
|
Expect.fail("Multiple listens on same port");
|
|
})
|
|
.catchError((error) {
|
|
Expect.isTrue(error is SocketException);
|
|
s.close();
|
|
asyncEnd();
|
|
});
|
|
});
|
|
}
|
|
|
|
void testSimpleConnect() {
|
|
asyncStart();
|
|
SecureServerSocket.bind(HOST, 0, serverContext).then((server) {
|
|
var clientEndFuture = SecureSocket.connect(
|
|
HOST,
|
|
server.port,
|
|
context: clientContext,
|
|
);
|
|
server.listen((serverEnd) {
|
|
clientEndFuture.then((clientEnd) async {
|
|
var x5 = clientEnd.peerCertificate!;
|
|
print(x5.subject);
|
|
print(x5.issuer);
|
|
print(x5.startValidity);
|
|
print(x5.endValidity);
|
|
await Future.wait([
|
|
clientEnd.drain(),
|
|
clientEnd.close(),
|
|
serverEnd.drain(),
|
|
serverEnd.close(),
|
|
server.close(),
|
|
]);
|
|
asyncEnd();
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
void testSimpleConnectFail(
|
|
SecurityContext? serverContext,
|
|
SecurityContext? clientContext,
|
|
bool cancelOnError,
|
|
) {
|
|
print('$serverContext $clientContext $cancelOnError');
|
|
asyncStart();
|
|
SecureServerSocket.bind(HOST, 0, serverContext).then((server) {
|
|
Future<void> clientEndFuture =
|
|
SecureSocket.connect(HOST, server.port, context: clientContext)
|
|
.then((clientEnd) {
|
|
Expect.fail("No client connection expected.");
|
|
})
|
|
.catchError((error) {
|
|
// TODO(whesse): When null context is supported, disallow
|
|
// the ArgumentError type here.
|
|
Expect.isTrue(
|
|
error is ArgumentError ||
|
|
error is HandshakeException ||
|
|
error is SocketException,
|
|
);
|
|
});
|
|
server.listen(
|
|
(serverEnd) {
|
|
Expect.fail("No server connection expected.");
|
|
},
|
|
onError: (error) {
|
|
// TODO(whesse): When null context is supported, disallow
|
|
// the ArgumentError type here.
|
|
Expect.isTrue(
|
|
error is ArgumentError ||
|
|
error is HandshakeException ||
|
|
error is SocketException,
|
|
);
|
|
clientEndFuture.then((_) async {
|
|
if (!cancelOnError) await server.close();
|
|
asyncEnd();
|
|
});
|
|
},
|
|
cancelOnError: cancelOnError,
|
|
);
|
|
});
|
|
}
|
|
|
|
void testServerListenAfterConnect() {
|
|
asyncStart();
|
|
SecureServerSocket.bind(HOST, 0, serverContext).then((server) {
|
|
Expect.isTrue(server.port > 0);
|
|
var clientEndFuture = SecureSocket.connect(
|
|
HOST,
|
|
server.port,
|
|
context: clientContext,
|
|
);
|
|
new Timer(const Duration(milliseconds: 500), () {
|
|
server.listen((serverEnd) {
|
|
clientEndFuture.then((clientEnd) async {
|
|
await Future.wait([
|
|
clientEnd.drain(),
|
|
clientEnd.close(),
|
|
serverEnd.drain(),
|
|
serverEnd.close(),
|
|
server.close(),
|
|
]);
|
|
asyncEnd();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
void testSimpleReadWrite() {
|
|
// This test creates a server and a client connects. The client then
|
|
// writes and the server echos. When the server has finished its
|
|
// echo it half-closes. When the client gets the close event is
|
|
// closes fully.
|
|
asyncStart();
|
|
|
|
const messageSize = 1000;
|
|
|
|
List<int> createTestData() {
|
|
List<int> data = new List<int>.filled(messageSize, 0);
|
|
for (int i = 0; i < messageSize; i++) {
|
|
data[i] = i & 0xff;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void verifyTestData(List<int> data) {
|
|
Expect.equals(messageSize, data.length);
|
|
List<int> expected = createTestData();
|
|
for (int i = 0; i < messageSize; i++) {
|
|
Expect.equals(expected[i], data[i]);
|
|
}
|
|
}
|
|
|
|
SecureServerSocket.bind(HOST, 0, serverContext).then((server) {
|
|
server.listen((client) {
|
|
int bytesRead = 0;
|
|
int bytesWritten = 0;
|
|
List<int> data = new List<int>.filled(messageSize, 0);
|
|
|
|
client.listen(
|
|
(buffer) {
|
|
Expect.isTrue(bytesWritten == 0);
|
|
data.setRange(bytesRead, bytesRead + buffer.length, buffer);
|
|
bytesRead += buffer.length;
|
|
if (bytesRead == data.length) {
|
|
verifyTestData(data);
|
|
client.add(data);
|
|
client.close();
|
|
}
|
|
},
|
|
onDone: () {
|
|
server.close();
|
|
},
|
|
);
|
|
});
|
|
|
|
SecureSocket.connect(HOST, server.port, context: clientContext).then((
|
|
socket,
|
|
) {
|
|
int bytesRead = 0;
|
|
int bytesWritten = 0;
|
|
List<int> dataSent = createTestData();
|
|
List<int> dataReceived = new List<int>.filled(dataSent.length, 0);
|
|
socket.add(dataSent);
|
|
socket.close(); // Can also be delayed.
|
|
socket.listen(
|
|
(List<int> buffer) {
|
|
dataReceived.setRange(bytesRead, bytesRead + buffer.length, buffer);
|
|
bytesRead += buffer.length;
|
|
},
|
|
onDone: () async {
|
|
verifyTestData(dataReceived);
|
|
await socket.close();
|
|
asyncEnd();
|
|
},
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
main() {
|
|
asyncStart();
|
|
InternetAddress.lookup("localhost").then((hosts) {
|
|
HOST = hosts.first;
|
|
runTests();
|
|
asyncEnd();
|
|
});
|
|
}
|
|
|
|
runTests() {
|
|
testSimpleBind();
|
|
testInvalidBind();
|
|
testSimpleConnect();
|
|
for (var server in [serverContext, null]) {
|
|
for (var client in [clientContext, null]) {
|
|
for (bool cancelOnError in [true, false]) {
|
|
if (server == null || client == null) {
|
|
testSimpleConnectFail(server, client, cancelOnError);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
testServerListenAfterConnect();
|
|
testSimpleReadWrite();
|
|
}
|