mirror of
https://github.com/dart-lang/sdk
synced 2024-09-22 18:23:41 +00:00
Implement input and output streams for secure network sockets.
BUG=dart:6701 Review URL: https://codereview.chromium.org//11416108 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15267 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
218c3ed3ff
commit
0807c2662b
|
@ -465,7 +465,7 @@ class _Socket extends _SocketBase implements Socket {
|
|||
}
|
||||
|
||||
void set onConnect(void callback()) {
|
||||
if (_seenFirstOutEvent || _outputStream != null) {
|
||||
if (_seenFirstOutEvent) {
|
||||
throw new StreamException(
|
||||
"Cannot set connect handler when already connected");
|
||||
}
|
||||
|
@ -509,7 +509,7 @@ class _Socket extends _SocketBase implements Socket {
|
|||
if (_outputStream == null) {
|
||||
if (_handlerMap[_SocketBase._OUT_EVENT] != null) {
|
||||
throw new StreamException(
|
||||
"Cannot get input stream when socket handlers are used");
|
||||
"Cannot get output stream when socket handlers are used");
|
||||
}
|
||||
_outputStream = new _SocketOutputStream(this);
|
||||
}
|
||||
|
|
|
@ -82,11 +82,6 @@ class _TlsSocket implements TlsSocket {
|
|||
_tlsFilter.registerHandshakeCompleteCallback(_tlsHandshakeCompleteHandler);
|
||||
}
|
||||
|
||||
InputStream get inputStream {
|
||||
// TODO(6701): Implement stream interfaces on TlsSocket.
|
||||
throw new UnimplementedError("TlsSocket.inputStream not implemented yet");
|
||||
}
|
||||
|
||||
int get port => _socket.port;
|
||||
|
||||
String get remoteHost => _socket.remoteHost;
|
||||
|
@ -94,26 +89,79 @@ class _TlsSocket implements TlsSocket {
|
|||
int get remotePort => _socket.remotePort;
|
||||
|
||||
void set onClosed(void callback()) {
|
||||
if (_inputStream != null) {
|
||||
throw new StreamException(
|
||||
"Cannot set close handler when input stream is used");
|
||||
}
|
||||
_onClosed = callback;
|
||||
}
|
||||
|
||||
void set _onClosed(void callback()) {
|
||||
_socketCloseHandler = callback;
|
||||
}
|
||||
|
||||
void set onConnect(void callback()) {
|
||||
if (_outputStream != null) {
|
||||
throw new StreamException(
|
||||
"Cannot set connect handler when output stream is used");
|
||||
}
|
||||
if (_status == CONNECTED || _status == CLOSED) {
|
||||
throw new StreamException(
|
||||
"Cannot set connect handler when already connected");
|
||||
}
|
||||
_onConnect = callback;
|
||||
}
|
||||
|
||||
void set _onConnect(void callback()) {
|
||||
_socketConnectHandler = callback;
|
||||
}
|
||||
|
||||
void set onData(void callback()) {
|
||||
if (_outputStream != null) {
|
||||
throw new StreamException(
|
||||
"Cannot set data handler when input stream is used");
|
||||
}
|
||||
_onData = callback;
|
||||
}
|
||||
|
||||
void set _onData(void callback()) {
|
||||
_socketDataHandler = callback;
|
||||
}
|
||||
|
||||
void set onWrite(void callback()) {
|
||||
if (_outputStream != null) {
|
||||
throw new StreamException(
|
||||
"Cannot set write handler when output stream is used");
|
||||
}
|
||||
_onWrite = callback;
|
||||
}
|
||||
|
||||
void set _onWrite(void callback()) {
|
||||
_socketWriteHandler = callback;
|
||||
// Reset the one-shot onWrite handler.
|
||||
_socket.onWrite = _tlsWriteHandler;
|
||||
}
|
||||
|
||||
InputStream get inputStream {
|
||||
if (_inputStream == null) {
|
||||
if (_socketDataHandler != null || _socketCloseHandler != null) {
|
||||
throw new StreamException(
|
||||
"Cannot get input stream when socket handlers are used");
|
||||
}
|
||||
_inputStream = new _SocketInputStream(this);
|
||||
}
|
||||
return _inputStream;
|
||||
}
|
||||
|
||||
OutputStream get outputStream {
|
||||
// TODO(6701): Implement stream interfaces on TlsSocket.
|
||||
throw new UnimplementedError("TlsSocket.inputStream not implemented yet");
|
||||
if (_outputStream == null) {
|
||||
if (_socketConnectHandler != null || _socketWriteHandler != null) {
|
||||
throw new StreamException(
|
||||
"Cannot get output stream when socket handlers are used");
|
||||
}
|
||||
_outputStream = new _SocketOutputStream(this);
|
||||
}
|
||||
return _outputStream;
|
||||
}
|
||||
|
||||
int available() {
|
||||
|
@ -143,10 +191,15 @@ class _TlsSocket implements TlsSocket {
|
|||
}
|
||||
}
|
||||
|
||||
void _closeWrite() => close(true);
|
||||
|
||||
List<int> read([int len]) {
|
||||
if (_closedRead) {
|
||||
throw new SocketException("Reading from a closed socket");
|
||||
}
|
||||
if (_status != CONNECTED) {
|
||||
return new List<int>(0);
|
||||
}
|
||||
var buffer = _tlsFilter.buffers[READ_PLAINTEXT];
|
||||
_readEncryptedData();
|
||||
int toRead = buffer.length;
|
||||
|
@ -173,6 +226,9 @@ class _TlsSocket implements TlsSocket {
|
|||
throw new ArgumentError(
|
||||
"Invalid offset or bytes in TlsSocket.readList");
|
||||
}
|
||||
if (_status != CONNECTED && _status != CLOSED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bytesRead = 0;
|
||||
var buffer = _tlsFilter.buffers[READ_PLAINTEXT];
|
||||
|
@ -200,6 +256,7 @@ class _TlsSocket implements TlsSocket {
|
|||
if (_closedWrite) {
|
||||
throw new SocketException("Writing to a closed socket");
|
||||
}
|
||||
if (_status != CONNECTED) return 0;
|
||||
var buffer = _tlsFilter.buffers[WRITE_PLAINTEXT];
|
||||
if (bytes > buffer.free) {
|
||||
bytes = buffer.free;
|
||||
|
@ -285,8 +342,15 @@ class _TlsSocket implements TlsSocket {
|
|||
_connectPending = false;
|
||||
_socketConnectHandler();
|
||||
}
|
||||
if (_socketWriteHandler != null) {
|
||||
_socket.onWrite = _tlsWriteHandler;
|
||||
}
|
||||
}
|
||||
|
||||
// True if the underlying socket is closed, the filter has been emptied of
|
||||
// all data, and the close event has been fired.
|
||||
get _closed => _socketClosed && !_fireCloseEventPending;
|
||||
|
||||
void _fireCloseEvent() {
|
||||
if (scheduledDataEvent != null) {
|
||||
scheduledDataEvent.cancel();
|
||||
|
@ -408,6 +472,8 @@ class _TlsSocket implements TlsSocket {
|
|||
}
|
||||
}
|
||||
|
||||
bool get _socketClosed => _closedRead;
|
||||
|
||||
// _TlsSocket cannot extend _Socket and use _Socket's factory constructor.
|
||||
Socket _socket;
|
||||
String _host;
|
||||
|
@ -422,6 +488,8 @@ class _TlsSocket implements TlsSocket {
|
|||
bool _closedWrite = false; // The secure socket has been closed for writing.
|
||||
bool _filterReadEmpty = true; // There is no buffered data to read.
|
||||
bool _filterWriteEmpty = true; // There is no buffered data to be written.
|
||||
_SocketInputStream _inputStream;
|
||||
_SocketOutputStream _outputStream;
|
||||
bool _connectPending = false;
|
||||
Function _socketConnectHandler;
|
||||
Function _socketWriteHandler;
|
||||
|
|
98
tests/standalone/io/tls_server_stream_test.dart
Normal file
98
tests/standalone/io/tls_server_stream_test.dart
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2012, 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:io";
|
||||
|
||||
const SERVER_ADDRESS = "127.0.0.1";
|
||||
const HOST_NAME = "localhost";
|
||||
|
||||
class TlsTestServer {
|
||||
void onConnection(Socket connection) {
|
||||
numConnections++;
|
||||
var input = connection.inputStream;
|
||||
String received = "";
|
||||
input.onData = () {
|
||||
received = received.concat(new String.fromCharCodes(input.read()));
|
||||
};
|
||||
input.onClosed = () {
|
||||
Expect.isTrue(received.contains("Hello from client "));
|
||||
String name = received.substring(received.indexOf("client ") + 7);
|
||||
connection.outputStream.write("Welcome, client $name".charCodes);
|
||||
connection.outputStream.close();
|
||||
};
|
||||
}
|
||||
|
||||
void errorHandlerServer(Exception e) {
|
||||
Expect.fail("Server socket error $e");
|
||||
}
|
||||
|
||||
int start() {
|
||||
server = new TlsServerSocket(SERVER_ADDRESS, 0, 10, "CN=$HOST_NAME");
|
||||
Expect.isNotNull(server);
|
||||
server.onConnection = onConnection;
|
||||
server.onError = errorHandlerServer;
|
||||
return server.port;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
server.close();
|
||||
}
|
||||
|
||||
int numConnections = 0;
|
||||
TlsServerSocket server;
|
||||
}
|
||||
|
||||
class TlsTestClient {
|
||||
TlsTestClient(int this.port, String this.name) {
|
||||
socket = new TlsSocket(HOST_NAME, port);
|
||||
numRequests++;
|
||||
socket.outputStream.write("Hello from client $name".charCodes);
|
||||
socket.outputStream.close();
|
||||
socket.inputStream.onData = () {
|
||||
reply = reply.concat(new String.fromCharCodes(socket.inputStream.read()));
|
||||
};
|
||||
socket.inputStream.onClosed = this.done;
|
||||
reply = "";
|
||||
}
|
||||
|
||||
void done() {
|
||||
Expect.equals("Welcome, client $name", reply);
|
||||
numReplies++;
|
||||
if (numReplies == CLIENT_NAMES.length) {
|
||||
Expect.equals(numRequests, numReplies);
|
||||
EndTest();
|
||||
}
|
||||
}
|
||||
|
||||
static int numRequests = 0;
|
||||
static int numReplies = 0;
|
||||
|
||||
int port;
|
||||
String name;
|
||||
TlsSocket socket;
|
||||
String reply;
|
||||
}
|
||||
|
||||
Function EndTest;
|
||||
|
||||
const CLIENT_NAMES = const ['able', 'baker', 'camera', 'donut', 'echo'];
|
||||
|
||||
void main() {
|
||||
Path scriptDir = new Path.fromNative(new Options().script).directoryPath;
|
||||
Path certificateDatabase = scriptDir.append('pkcert');
|
||||
TlsSocket.setCertificateDatabase(certificateDatabase.toNativePath(),
|
||||
'dartdart');
|
||||
|
||||
var server = new TlsTestServer();
|
||||
int port = server.start();
|
||||
|
||||
EndTest = () {
|
||||
Expect.equals(CLIENT_NAMES.length, server.numConnections);
|
||||
server.stop();
|
||||
};
|
||||
|
||||
for (var x in CLIENT_NAMES) {
|
||||
new TlsTestClient(port, x);
|
||||
}
|
||||
}
|
36
tests/standalone/io/tls_stream_test.dart
Normal file
36
tests/standalone/io/tls_stream_test.dart
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) 2012, 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
|
||||
// The --short_socket_write option does not work with external server
|
||||
// www.google.dk. Add this to the test when we have secure server sockets.
|
||||
// See TODO below.
|
||||
|
||||
#import("dart:isolate");
|
||||
#import("dart:io");
|
||||
|
||||
void main() {
|
||||
var testPkcertDatabase =
|
||||
new Path.fromNative(new Options().script).directoryPath.append('pkcert/');
|
||||
TlsSocket.setCertificateDatabase(testPkcertDatabase.toNativePath());
|
||||
// TODO(3593): Use a Dart HTTPS server for this test using TLS server sockets.
|
||||
// When we use a Dart HTTPS server, allow --short_socket_write. The flag
|
||||
// causes fragmentation of the client hello message, which doesn't seem to
|
||||
// work with www.google.dk.
|
||||
var tls = new TlsSocket("www.google.dk", 443);
|
||||
List<String> chunks = <String>[];
|
||||
var input = tls.inputStream;
|
||||
var output = tls.outputStream;
|
||||
|
||||
output.write("GET / HTTP/1.0\r\nHost: www.google.dk\r\n\r\n".charCodes);
|
||||
output.close();
|
||||
input.onData = () {
|
||||
chunks.add(new String.fromCharCodes(input.read()));
|
||||
};
|
||||
input.onClosed = () {
|
||||
String fullPage = Strings.concatAll(chunks);
|
||||
Expect.isTrue(fullPage.contains('</body></html>'));
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue