a6a16b6801
R=ager@google.com, ajohnsen@google.com BUG=dart:7334 Review URL: https://codereview.chromium.org//11879042 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@17064 260f80e4-7a28-3924-810f-c04153c831b5
238 lines
6.2 KiB
Dart
238 lines
6.2 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.
|
|
|
|
import "dart:io";
|
|
import "dart:isolate";
|
|
import "dart:math";
|
|
|
|
class ExpectedDataOutputStream implements OutputStream {
|
|
ExpectedDataOutputStream(List<int> this._data,
|
|
int this._cutoff,
|
|
bool this._closeAsError,
|
|
SocketMock this._socket);
|
|
|
|
void set onNoPendingWrites(void callback()) {
|
|
_onNoPendingWrites = callback;
|
|
}
|
|
|
|
void set onClosed(void callback()) {
|
|
// Not used in test.
|
|
}
|
|
|
|
void set onError(void callback(e)) {
|
|
_onError = callback;
|
|
}
|
|
|
|
bool write(List data, [bool copyBuffer = true]) {
|
|
_onData(data);
|
|
return true;
|
|
}
|
|
|
|
bool writeFrom(List data, [int offset = 0, int len]) {
|
|
if (len == null) len = data.length - offset;
|
|
_onData(data.getRange(offset, len));
|
|
return true;
|
|
}
|
|
|
|
void close() {
|
|
_socket.close(true);
|
|
}
|
|
|
|
void _onData(List<int> data) {
|
|
// TODO(ajohnsen): To be removed, since the socket should not be written to
|
|
// after close.
|
|
if (_socket._closed) return;
|
|
Expect.isFalse(_written > _cutoff);
|
|
Expect.listEquals(data, _data.getRange(0, data.length));
|
|
_data = _data.getRange(data.length, _data.length - data.length);
|
|
_written += data.length;
|
|
if (_written >= _cutoff) {
|
|
// Tell HttpServer that the socket have closed.
|
|
_socket._closeInternal(_closeAsError);
|
|
}
|
|
}
|
|
|
|
Function _onNoPendingWrites;
|
|
Function _onError;
|
|
List<int> _data;
|
|
int _written = 0;
|
|
int _cutoff;
|
|
bool _closeAsError;
|
|
SocketMock _socket;
|
|
}
|
|
|
|
class SocketMock implements Socket {
|
|
SocketMock(List<int> this._data,
|
|
List<int> expected,
|
|
int cutoff,
|
|
bool closeAsError) :
|
|
_hashCode = new Random().nextInt(1<< 32),
|
|
_read = [] {
|
|
_outputStream =
|
|
new ExpectedDataOutputStream(expected, cutoff, closeAsError, this);
|
|
}
|
|
|
|
int available() {
|
|
return _data.length;
|
|
}
|
|
|
|
void _closeInternal([bool asError = false]) {
|
|
Expect.isFalse(_closed);
|
|
_closed = true;
|
|
_onClosedInternal();
|
|
if (asError) {
|
|
_onError(new Exception("Socket closed unexpected"));
|
|
} else {
|
|
_onClosed();
|
|
}
|
|
}
|
|
|
|
List<int> read([int len]) {
|
|
var result;
|
|
if (len == null) {
|
|
result = _data;
|
|
_data = [];
|
|
} else {
|
|
result = new Uint8List(len);
|
|
readList(result, 0, len);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int readList(List<int> buffer, int offset, int count) {
|
|
int max = min(count, _data.length);
|
|
buffer.setRange(offset, max, _data);
|
|
_data = _data.getRange(max, _data.length - max);
|
|
return max;
|
|
}
|
|
|
|
void close([bool halfClose = false]) {
|
|
if (!halfClose && !_closed) _closeInternal();
|
|
}
|
|
|
|
void set onData(void callback()) {
|
|
_onData = callback;
|
|
}
|
|
|
|
void set onClosed(void callback()) {
|
|
_onClosed = callback;
|
|
}
|
|
|
|
void set onError(void callback(e)) {
|
|
_onError = callback;
|
|
}
|
|
|
|
OutputStream get outputStream => _outputStream;
|
|
|
|
int get hashCode => _hashCode;
|
|
|
|
List<int> _read;
|
|
bool _closed = false;
|
|
int _hashCode;
|
|
Function _onData;
|
|
Function _onClosed;
|
|
Function _onError;
|
|
Function _onClosedInternal;
|
|
List<int> _data;
|
|
ExpectedDataOutputStream _outputStream;
|
|
}
|
|
|
|
class ServerSocketMock implements ServerSocket {
|
|
ServerSocketMock(String addr, int this._port, int backlog) :
|
|
_sockets = new Set<Socket>();
|
|
|
|
void spawnSocket(var data, String response, int cutOff, bool closeAsError) {
|
|
if (data is String) data = data.charCodes;
|
|
SocketMock socket = new SocketMock(data,
|
|
response.charCodes,
|
|
cutOff,
|
|
closeAsError);
|
|
_sockets.add(socket);
|
|
ReceivePort port = new ReceivePort();
|
|
socket._onClosedInternal = () {
|
|
// The server should always close the connection.
|
|
_sockets.remove(socket);
|
|
port.close();
|
|
};
|
|
// Tell HttpServer that a connection have come to life.
|
|
_onConnection(socket);
|
|
// Start 'sending' data.
|
|
socket._onData();
|
|
}
|
|
|
|
void close() {
|
|
Expect.fail("Don't close the connection, we attach to this socket");
|
|
}
|
|
|
|
void set onConnection(void callback(Socket connection)) {
|
|
_onConnection = callback;
|
|
}
|
|
|
|
void set onError(void callback(e)) {
|
|
_onError = callback;
|
|
}
|
|
|
|
int get port => _port;
|
|
|
|
int _port;
|
|
Function _onConnection;
|
|
Function _onError;
|
|
Set<Socket> _sockets;
|
|
}
|
|
|
|
void testSocketClose() {
|
|
ServerSocketMock serverSocket = new ServerSocketMock("0.0.0.0", 5432, 5);
|
|
|
|
HttpServer server = new HttpServer();
|
|
server.listenOn(serverSocket);
|
|
void testContent(String requestString,
|
|
String responseString,
|
|
[int okayFrom = 0,
|
|
bool expectError = true]) {
|
|
// Inner callback to actually run a given setting.
|
|
void runSettings(int cutoff,
|
|
bool closeAsError,
|
|
bool expectError) {
|
|
server.defaultRequestHandler =
|
|
(HttpRequest request, HttpResponse response) {
|
|
request.inputStream.onData = () {
|
|
};
|
|
request.inputStream.onClosed = () {
|
|
response.outputStream.close();
|
|
};
|
|
};
|
|
|
|
if (expectError) {
|
|
ReceivePort port = new ReceivePort();
|
|
server.onError = (var error) {
|
|
port.close();
|
|
};
|
|
} else {
|
|
server.onError = (var error) {
|
|
Expect.fail("An error was not expected: $error");
|
|
};
|
|
}
|
|
|
|
serverSocket.spawnSocket(requestString, responseString,
|
|
cutoff, closeAsError);
|
|
// TODO(ajohnsen): Validate HttpServers number of connections.
|
|
}
|
|
for (int i = 1; i < responseString.length; i++) {
|
|
bool _expectError = expectError && i < responseString.length - okayFrom;
|
|
runSettings(i, false, _expectError);
|
|
runSettings(i, true, _expectError);
|
|
}
|
|
}
|
|
testContent(
|
|
"GET / HTTP/1.1\r\nKeep-Alive: False\r\n\r\n",
|
|
"HTTP/1.1 200 OK\r\ntransfer-encoding: chunked\r\nconnection: close"
|
|
"\r\n\r\n0\r\n\r\n");
|
|
|
|
server.close();
|
|
}
|
|
|
|
void main() {
|
|
testSocketClose();
|
|
}
|