|  | // 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= | 
|  | // VMOptions=--short_socket_read | 
|  | // VMOptions=--short_socket_write | 
|  | // VMOptions=--short_socket_read --short_socket_write | 
|  |  | 
|  | // @dart = 2.9 | 
|  |  | 
|  | import "dart:async"; | 
|  | import "dart:io"; | 
|  |  | 
|  | import "package:async_helper/async_helper.dart"; | 
|  | import "package:expect/expect.dart"; | 
|  |  | 
|  | void testArguments() { | 
|  | Expect.throws(() => RawServerSocket.bind("127.0.0.1", 65536)); | 
|  | Expect.throws(() => RawServerSocket.bind("127.0.0.1", -1)); | 
|  | Expect.throws(() => RawServerSocket.bind("127.0.0.1", 0, backlog: -1)); | 
|  | } | 
|  |  | 
|  | void testSimpleBind() { | 
|  | asyncStart(); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((s) { | 
|  | Expect.isTrue(s.port > 0); | 
|  | s.close(); | 
|  | asyncEnd(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testInvalidBind() { | 
|  | // Bind to a unknown DNS name. | 
|  | asyncStart(); | 
|  | RawServerSocket.bind("ko.faar.__hest__", 0).then((_) { | 
|  | Expect.fail("Failure expected"); | 
|  | }).catchError((error) { | 
|  | Expect.isTrue(error is SocketException); | 
|  | asyncEnd(); | 
|  | }); | 
|  |  | 
|  | // Bind to an unavaliable IP-address. | 
|  | asyncStart(); | 
|  | RawServerSocket.bind("8.8.8.8", 0).then((_) { | 
|  | Expect.fail("Failure expected"); | 
|  | }).catchError((error) { | 
|  | Expect.isTrue(error is SocketException); | 
|  | asyncEnd(); | 
|  | }); | 
|  |  | 
|  | // Bind to a port already in use. | 
|  | asyncStart(); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((s) { | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, s.port).then((t) { | 
|  | Expect.fail("Multiple listens on same port"); | 
|  | }).catchError((error) { | 
|  | Expect.isTrue(error is SocketException); | 
|  | s.close(); | 
|  | asyncEnd(); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testSimpleConnect() { | 
|  | asyncStart(); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | server.listen((socket) { | 
|  | socket.close(); | 
|  | }); | 
|  | RawSocket.connect("127.0.0.1", server.port).then((socket) { | 
|  | server.close(); | 
|  | socket.close(); | 
|  | asyncEnd(); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testCancelConnect() { | 
|  | asyncStart(); | 
|  | RawSocket.startConnect(InternetAddress.loopbackIPv4, 0) | 
|  | .then((ConnectionTask<RawSocket> task) { | 
|  | task.cancel(); | 
|  | task.socket.then((s) { | 
|  | Expect.fail("Unreachable"); | 
|  | }, onError: (e) { | 
|  | Expect.isTrue(e is SocketException); | 
|  | asyncEnd(); | 
|  | }); | 
|  | }, onError: (e) { | 
|  | Expect.fail("Unreachable"); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testCloseOneEnd(String toClose) { | 
|  | asyncStart(); | 
|  | Completer serverDone = new Completer(); | 
|  | Completer serverEndDone = new Completer(); | 
|  | Completer clientEndDone = new Completer(); | 
|  | Future.wait([ | 
|  | serverDone.future, | 
|  | serverEndDone.future, | 
|  | clientEndDone.future | 
|  | ]).then((_) { | 
|  | asyncEnd(); | 
|  | }); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | server.listen((serverConnection) { | 
|  | serverConnection.listen((event) { | 
|  | if (toClose == "server" || event == RawSocketEvent.readClosed) { | 
|  | serverConnection.shutdown(SocketDirection.send); | 
|  | } | 
|  | }, onDone: () { | 
|  | serverEndDone.complete(null); | 
|  | }); | 
|  | }, onDone: () { | 
|  | serverDone.complete(null); | 
|  | }); | 
|  | RawSocket.connect("127.0.0.1", server.port).then((clientConnection) { | 
|  | clientConnection.listen((event) { | 
|  | if (toClose == "client" || event == RawSocketEvent.readClosed) { | 
|  | clientConnection.shutdown(SocketDirection.send); | 
|  | } | 
|  | }, onDone: () { | 
|  | clientEndDone.complete(null); | 
|  | server.close(); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testServerListenAfterConnect() { | 
|  | asyncStart(); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | Expect.isTrue(server.port > 0); | 
|  | RawSocket.connect("127.0.0.1", server.port).then((client) { | 
|  | server.listen((socket) { | 
|  | client.close(); | 
|  | server.close(); | 
|  | socket.close(); | 
|  | asyncEnd(); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testSimpleReadWrite({bool dropReads}) { | 
|  | // This test creates a server and a client connects. The client then | 
|  | // writes and the server echos. When the server has finished its | 
|  | // echo it half-closes. When the client gets the close event is | 
|  | // closes fully. | 
|  | asyncStart(); | 
|  |  | 
|  | const messageSize = 1000; | 
|  | int serverReadCount = 0; | 
|  | int clientReadCount = 0; | 
|  |  | 
|  | List<int> createTestData() { | 
|  | return new List<int>.generate(messageSize, (index) => index & 0xff); | 
|  | } | 
|  |  | 
|  | void verifyTestData(List<int> data) { | 
|  | Expect.equals(messageSize, data.length); | 
|  | List<int> expected = createTestData(); | 
|  | for (int i = 0; i < messageSize; i++) { | 
|  | Expect.equals(expected[i], data[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | server.listen((client) { | 
|  | int bytesRead = 0; | 
|  | int bytesWritten = 0; | 
|  | bool closedEventReceived = false; | 
|  | List<int> data = new List<int>(messageSize); | 
|  |  | 
|  | client.writeEventsEnabled = false; | 
|  | client.listen((event) { | 
|  | switch (event) { | 
|  | case RawSocketEvent.read: | 
|  | if (dropReads) { | 
|  | if (serverReadCount != 10) { | 
|  | serverReadCount++; | 
|  | break; | 
|  | } else { | 
|  | serverReadCount = 0; | 
|  | } | 
|  | } | 
|  | Expect.isTrue(bytesWritten == 0); | 
|  | Expect.isTrue(client.available() > 0); | 
|  | var buffer = client.read(200); | 
|  | data.setRange(bytesRead, bytesRead + buffer.length, buffer); | 
|  | bytesRead += buffer.length; | 
|  | if (bytesRead == data.length) { | 
|  | verifyTestData(data); | 
|  | client.writeEventsEnabled = true; | 
|  | } | 
|  | break; | 
|  | case RawSocketEvent.write: | 
|  | Expect.isFalse(client.writeEventsEnabled); | 
|  | bytesWritten += | 
|  | client.write(data, bytesWritten, data.length - bytesWritten); | 
|  | if (bytesWritten < data.length) { | 
|  | client.writeEventsEnabled = true; | 
|  | } | 
|  | if (bytesWritten == data.length) { | 
|  | client.shutdown(SocketDirection.send); | 
|  | } | 
|  | break; | 
|  | case RawSocketEvent.readClosed: | 
|  | server.close(); | 
|  | break; | 
|  | case RawSocketEvent.closed: | 
|  | Expect.isFalse(closedEventReceived); | 
|  | closedEventReceived = true; | 
|  | break; | 
|  | default: | 
|  | throw "Unexpected event $event"; | 
|  | } | 
|  | }, onDone: () => Expect.isTrue(closedEventReceived)); | 
|  | }); | 
|  |  | 
|  | RawSocket.connect("127.0.0.1", server.port).then((socket) { | 
|  | int bytesRead = 0; | 
|  | int bytesWritten = 0; | 
|  | bool closedEventReceived = false; | 
|  | List<int> data = createTestData(); | 
|  |  | 
|  | socket.listen((event) { | 
|  | switch (event) { | 
|  | case RawSocketEvent.read: | 
|  | Expect.isTrue(socket.available() > 0); | 
|  | if (dropReads) { | 
|  | if (clientReadCount != 10) { | 
|  | clientReadCount++; | 
|  | break; | 
|  | } else { | 
|  | clientReadCount = 0; | 
|  | } | 
|  | } | 
|  | var buffer = socket.read(); | 
|  | data.setRange(bytesRead, bytesRead + buffer.length, buffer); | 
|  | bytesRead += buffer.length; | 
|  | break; | 
|  | case RawSocketEvent.write: | 
|  | Expect.isTrue(bytesRead == 0); | 
|  | Expect.isFalse(socket.writeEventsEnabled); | 
|  | bytesWritten += | 
|  | socket.write(data, bytesWritten, data.length - bytesWritten); | 
|  | if (bytesWritten < data.length) { | 
|  | socket.writeEventsEnabled = true; | 
|  | } else { | 
|  | data = new List<int>(messageSize); | 
|  | } | 
|  | break; | 
|  | case RawSocketEvent.readClosed: | 
|  | verifyTestData(data); | 
|  | socket.close(); | 
|  | break; | 
|  | case RawSocketEvent.closed: | 
|  | Expect.isFalse(closedEventReceived); | 
|  | closedEventReceived = true; | 
|  | break; | 
|  | default: | 
|  | throw "Unexpected event $event"; | 
|  | } | 
|  | }, onDone: () { | 
|  | Expect.isTrue(closedEventReceived); | 
|  | asyncEnd(); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | testPauseServerSocket() { | 
|  | const int socketCount = 10; | 
|  | var acceptCount = 0; | 
|  | var resumed = false; | 
|  |  | 
|  | asyncStart(); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | Expect.isTrue(server.port > 0); | 
|  | var subscription = server.listen((socket) { | 
|  | socket.close(); | 
|  | Expect.isTrue(resumed); | 
|  | if (++acceptCount == socketCount) { | 
|  | server.close(); | 
|  | asyncEnd(); | 
|  | } | 
|  | }); | 
|  |  | 
|  | // Pause the server socket subscription and resume it after having | 
|  | // connected a number client sockets. Then connect more client | 
|  | // sockets. | 
|  | subscription.pause(); | 
|  | var connectCount = 0; | 
|  | for (int i = 0; i < socketCount / 2; i++) { | 
|  | RawSocket.connect("127.0.0.1", server.port).then((socket) { | 
|  | if (++connectCount == socketCount / 2) { | 
|  | subscription.resume(); | 
|  | resumed = true; | 
|  | for (int i = connectCount; i < socketCount; i++) { | 
|  | RawSocket.connect("127.0.0.1", server.port).then((socket) { | 
|  | socket.close(); | 
|  | }); | 
|  | } | 
|  | } | 
|  | socket.close(); | 
|  | }); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testPauseSocket() { | 
|  | const messageSize = 1000; | 
|  | const loopCount = 10; | 
|  | Completer connected = new Completer(); | 
|  | int pauseResumeCount = 0; | 
|  | int bytesWritten = 0; | 
|  | int bytesRead = 0; | 
|  | var writeSubscription; | 
|  | var readSubscription; | 
|  |  | 
|  | asyncStart(); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | Expect.isTrue(server.port > 0); | 
|  | server.listen((client) { | 
|  | bool closedEventReceived = false; | 
|  | List<int> data = new List<int>.filled(messageSize, 0); | 
|  | writeSubscription = client.listen((event) { | 
|  | switch (event) { | 
|  | case RawSocketEvent.read: | 
|  | throw "Unexpected read event"; | 
|  | case RawSocketEvent.write: | 
|  | if (pauseResumeCount == loopCount) return; | 
|  | Expect.isFalse(client.writeEventsEnabled); | 
|  | Expect.equals(0, bytesRead); // Checks that reader is paused. | 
|  | bytesWritten += | 
|  | client.write(data, bytesWritten, data.length - bytesWritten); | 
|  | // Ensure all data is written. When done disable the write | 
|  | // event and resume the receiver. | 
|  | if (bytesWritten == data.length) { | 
|  | writeSubscription.pause(); | 
|  | bytesWritten = 0; | 
|  | connected.future.then((_) { | 
|  | readSubscription.resume(); | 
|  | }); | 
|  | } | 
|  | client.writeEventsEnabled = true; | 
|  | break; | 
|  | case RawSocketEvent.readClosed: | 
|  | client.close(); | 
|  | server.close(); | 
|  | break; | 
|  | case RawSocketEvent.closed: | 
|  | Expect.isFalse(closedEventReceived); | 
|  | closedEventReceived = true; | 
|  | break; | 
|  | default: | 
|  | throw "Unexpected event $event"; | 
|  | } | 
|  | }, onDone: () => Expect.isTrue(closedEventReceived)); | 
|  | }); | 
|  |  | 
|  | RawSocket.connect("127.0.0.1", server.port).then((socket) { | 
|  | bool closedEventReceived = false; | 
|  | socket.writeEventsEnabled = false; | 
|  | readSubscription = socket.listen((event) { | 
|  | switch (event) { | 
|  | case RawSocketEvent.read: | 
|  | Expect.equals(0, bytesWritten); // Checks that writer is paused. | 
|  | Expect.isTrue(socket.available() > 0); | 
|  | var buffer = socket.read(); | 
|  | bytesRead += buffer.length; | 
|  | // Ensure all data is read. When done pause and resume the sender | 
|  | if (bytesRead == messageSize) { | 
|  | if (++pauseResumeCount == loopCount) { | 
|  | socket.close(); | 
|  | asyncEnd(); | 
|  | } else { | 
|  | readSubscription.pause(); | 
|  | } | 
|  | // Always resume writer as it needs the read closed | 
|  | // event when done. | 
|  | bytesRead = 0; | 
|  | writeSubscription.resume(); | 
|  | } | 
|  | break; | 
|  | case RawSocketEvent.write: | 
|  | throw "Unexpected write event"; | 
|  | case RawSocketEvent.readClosed: | 
|  | throw "Unexpected read closed event"; | 
|  | case RawSocketEvent.closed: | 
|  | Expect.isFalse(closedEventReceived); | 
|  | closedEventReceived = true; | 
|  | break; | 
|  | default: | 
|  | throw "Unexpected event $event"; | 
|  | } | 
|  | }, onDone: () => Expect.isTrue(closedEventReceived)); | 
|  | readSubscription.pause(); | 
|  | connected.complete(true); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testSocketZone() { | 
|  | asyncStart(); | 
|  | Expect.equals(Zone.root, Zone.current); | 
|  | runZoned(() { | 
|  | Expect.notEquals(Zone.root, Zone.current); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | Expect.notEquals(Zone.root, Zone.current); | 
|  | server.listen((socket) { | 
|  | Expect.notEquals(Zone.root, Zone.current); | 
|  | socket.close(); | 
|  | server.close(); | 
|  | }); | 
|  | RawSocket.connect("127.0.0.1", server.port).then((socket) { | 
|  | socket.listen((event) { | 
|  | if (event == RawSocketEvent.readClosed) { | 
|  | socket.close(); | 
|  | asyncEnd(); | 
|  | } | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testSocketZoneError() { | 
|  | asyncStart(); | 
|  | Expect.equals(Zone.root, Zone.current); | 
|  | runZonedGuarded(() { | 
|  | Expect.notEquals(Zone.root, Zone.current); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | Expect.notEquals(Zone.root, Zone.current); | 
|  | server.listen((socket) { | 
|  | Expect.notEquals(Zone.root, Zone.current); | 
|  | var timer; | 
|  | void write() { | 
|  | socket.write(const [0]); | 
|  | timer = new Timer(const Duration(milliseconds: 5), write); | 
|  | } | 
|  |  | 
|  | write(); | 
|  | socket.listen((_) {}, onError: (error) { | 
|  | timer.cancel(); | 
|  | Expect.notEquals(Zone.root, Zone.current); | 
|  | socket.close(); | 
|  | server.close(); | 
|  | throw error; | 
|  | }); | 
|  | }); | 
|  | RawSocket.connect("127.0.0.1", server.port).then((socket) { | 
|  | socket.close(); | 
|  | }); | 
|  | }); | 
|  | }, (e, s) { | 
|  | asyncEnd(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testClosedError() { | 
|  | asyncStart(); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | server.listen((socket) { | 
|  | socket.close(); | 
|  | }); | 
|  | RawSocket.connect("127.0.0.1", server.port).then((socket) { | 
|  | server.close(); | 
|  | socket.close(); | 
|  | Expect.throws(() => socket.remotePort, (e) => e is SocketException); | 
|  | Expect.throws(() => socket.remoteAddress, (e) => e is SocketException); | 
|  | asyncEnd(); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void testClosedServer() { | 
|  | asyncStart(); | 
|  | RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) { | 
|  | int port = server.port; | 
|  | server.close().then((_) { | 
|  | RawSocket.connect(InternetAddress.loopbackIPv4, server.port).then((_) { | 
|  | Expect.fail('Connecting to the closed server socket should fail'); | 
|  | }, onError: (e) { | 
|  | Expect.isTrue(e is SocketException); | 
|  | asyncEnd(); | 
|  | }); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | main() { | 
|  | asyncStart(); | 
|  | testArguments(); | 
|  | testSimpleBind(); | 
|  | testCloseOneEnd("client"); | 
|  | testCloseOneEnd("server"); | 
|  | testInvalidBind(); | 
|  | testSimpleConnect(); | 
|  | testCancelConnect(); | 
|  | testServerListenAfterConnect(); | 
|  | testSimpleReadWrite(dropReads: false); | 
|  | testSimpleReadWrite(dropReads: true); | 
|  | testPauseServerSocket(); | 
|  | testPauseSocket(); | 
|  | testSocketZone(); | 
|  | testSocketZoneError(); | 
|  | testClosedError(); | 
|  | testClosedServer(); | 
|  | asyncEnd(); | 
|  | } |