blob: a7722caa373e1918487e073b267d74b399db0950 [file] [log] [blame]
// 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");
#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 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();
}
}
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();
}