| // 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(); |
| } |