// 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=--enable-isolate-groups --experimental-enable-isolate-groups-jit // VMOptions=--no-enable-isolate-groups // // VMOptions= // VMOptions=--short_socket_read // VMOptions=--short_socket_write // VMOptions=--short_socket_read --short_socket_write // // Test socket close events. import 'dart:async'; import 'dart:io'; import 'dart:isolate'; import "package:async_helper/async_helper.dart"; import "package:expect/expect.dart"; const SERVERSHUTDOWN = -1; const ITERATIONS = 10; Future sendReceive(SendPort port, message) { ReceivePort receivePort = new ReceivePort(); port.send([message, receivePort.sendPort]); return receivePort.first; } class SocketClose { SocketClose.start(this._mode, this._done) : _sendPort = null, _readBytes = 0, _dataEvents = 0, _closeEvents = 0, _errorEvents = 0, _iterations = 0 { initialize(); } void proceed() { if (_iterations < ITERATIONS) { Timer.run(sendData); } else { shutdown(); } } void sendData() { void dataHandler(bytes) { switch (_mode) { case 0: case 1: case 2: Expect.fail("No data expected"); break; case 3: case 4: case 5: case 6: _readBytes += bytes.length; if ((_readBytes % 5) == 0) { _dataEvents++; } break; default: Expect.fail("Unknown test mode"); } } void closeHandler(socket) { _closeEvents++; switch (_mode) { case 0: case 1: socket.close(); break; case 2: case 3: socket.close(); proceed(); break; case 4: proceed(); break; case 5: socket.close(); proceed(); break; case 6: proceed(); break; default: Expect.fail("Unknown test mode"); } } void errorHandler(Socket socket) { _errorEvents++; socket.close(); } void connectHandler(socket) { socket.listen(dataHandler, onDone: () => closeHandler(socket), onError: (error) => errorHandler(socket)); void writeHello() { socket.write("Hello"); } _iterations++; switch (_mode) { case 0: socket.destroy(); proceed(); break; case 1: writeHello(); socket.destroy(); proceed(); break; case 2: case 3: writeHello(); break; case 4: writeHello(); socket.close(); // Half close. break; case 5: writeHello(); break; case 6: writeHello(); socket.close(); // Half close. break; default: Expect.fail("Unknown test mode"); } } Socket.connect(SocketCloseServer.HOST, _port).then(connectHandler); } void initialize() { ReceivePort receivePort = new ReceivePort(); var remote = Isolate.spawn(startSocketCloseServer, receivePort.sendPort); receivePort.first.then((message) { this._sendPort = message; sendReceive(_sendPort, _mode).then((port) { this._port = port; proceed(); }); }); } void shutdown() { sendReceive(_sendPort, SERVERSHUTDOWN).then((_) { _done(); }); switch (_mode) { case 0: case 1: Expect.equals(0, _dataEvents); Expect.equals(ITERATIONS, _closeEvents); break; case 2: Expect.equals(0, _dataEvents); Expect.equals(ITERATIONS, _closeEvents); break; case 3: case 4: Expect.isTrue(_dataEvents <= ITERATIONS); Expect.isTrue(_dataEvents >= 0); Expect.equals(ITERATIONS, _closeEvents); break; case 5: case 6: Expect.equals(ITERATIONS, _dataEvents); Expect.equals(ITERATIONS, _closeEvents); break; default: Expect.fail("Unknown test mode"); } Expect.equals(0, _errorEvents); } int _port; SendPort _sendPort; List _buffer; int _readBytes; int _dataEvents; int _closeEvents; int _errorEvents; int _iterations; int _mode; Function _done; } class ConnectionData { ConnectionData(Socket this.connection) : readBytes = 0; Socket connection; int readBytes; } void startSocketCloseServer(Object replyToObj) { SendPort replyTo = replyToObj; var server = new SocketCloseServer(); replyTo.send(server.dispatchSendPort); } class SocketCloseServer { static const HOST = "127.0.0.1"; SocketCloseServer() : _dispatchPort = new ReceivePort() { _dispatchPort.listen(dispatch); } void connectionHandler(ConnectionData data) { var connection = data.connection; void readBytes(bytes, whenFiveBytes) { data.readBytes += bytes.length; Expect.isTrue(data.readBytes <= 5); if (data.readBytes == 5) { whenFiveBytes(); } } void writeHello() { connection.write("Hello"); } void dataHandler(bytes) { switch (_mode) { case 0: Expect.fail("No data expected"); break; case 1: readBytes(bytes, () { _dataEvents++; }); break; case 2: readBytes(bytes, () { _dataEvents++; connection.destroy(); }); break; case 3: readBytes(bytes, () { _dataEvents++; writeHello(); connection.destroy(); }); break; case 4: readBytes(bytes, () { _dataEvents++; writeHello(); }); break; case 5: case 6: readBytes(bytes, () { _dataEvents++; writeHello(); connection.close(); // Half close. }); break; default: Expect.fail("Unknown test mode"); } } void closeHandler() { _closeEvents++; connection.close(); } void errorHandler(e) { Expect.fail("Socket error $e"); } _iterations++; connection.listen(dataHandler, onDone: closeHandler, onError: errorHandler); } void errorHandlerServer(e) { Expect.fail("Server socket error"); } waitForResult() { // Make sure all iterations have been run. In multiple of these // scenarios it is possible to get the SERVERSHUTDOWN message // before we have received the last close event on the // server. In these cases we wait for the correct number of // close events. if (_iterations == ITERATIONS && (_closeEvents == ITERATIONS || (_mode == 2 || _mode == 3))) { switch (_mode) { case 0: Expect.equals(0, _dataEvents); Expect.equals(ITERATIONS, _closeEvents); break; case 1: Expect.isTrue(_dataEvents <= ITERATIONS); Expect.isTrue(_dataEvents >= 0); Expect.equals(ITERATIONS, _closeEvents); break; case 2: case 3: Expect.equals(ITERATIONS, _dataEvents); Expect.equals(ITERATIONS, _closeEvents); break; case 4: case 5: case 6: Expect.equals(ITERATIONS, _dataEvents); Expect.equals(ITERATIONS, _closeEvents); break; default: Expect.fail("Unknown test mode"); } Expect.equals(0, _errorEvents); _server.close(); _dispatchPort.close(); _donePort.send(null); } else { new Timer(new Duration(milliseconds: 100), waitForResult); } } SendPort get dispatchSendPort => _dispatchPort.sendPort; void dispatch(message) { var command = message[0]; SendPort replyTo = message[1]; _donePort = replyTo; if (command != SERVERSHUTDOWN) { _readBytes = 0; _errorEvents = 0; _dataEvents = 0; _closeEvents = 0; _iterations = 0; _mode = command; ServerSocket.bind("127.0.0.1", 0).then((server) { _server = server; _server.listen((socket) { var data = new ConnectionData(socket); connectionHandler(data); }, onError: errorHandlerServer); replyTo.send(_server.port); }); } else { Timer.run(waitForResult); } } ServerSocket _server; final ReceivePort _dispatchPort; SendPort _donePort; int _readBytes; int _errorEvents; int _dataEvents; int _closeEvents; int _iterations; int _mode; } main() { // Run the close test in these different "modes". // 0: Client closes without sending at all. // 1: Client sends and destroys. // 2: Client sends. Server destroys. // 3: Client sends. Server responds and destroys. // 4: Client sends and half-closes. Server responds and destroys. // 5: Client sends. Server responds and half closes. // 6: Client sends and half-closes. Server responds and half closes. var tests = 7; for (var i = 0; i < tests; i++) { asyncStart(); new SocketClose.start(i, asyncEnd); } }