OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 import "dart:async"; | |
6 import "dart:io"; | |
7 import "dart:isolate"; | |
8 import "dart:math"; | |
9 | |
10 import "package:async_helper/async_helper.dart"; | |
11 import "package:expect/expect.dart"; | |
12 | |
13 const String LOOPBACK_IP_V4_STRING = "127.0.0.1"; | |
14 | |
15 void testArguments() { | |
16 Expect.throws(() => RawSynchronousSocket.connectSync(null, 0)); | |
17 Expect.throws( | |
18 () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, null)); | |
19 Expect.throws( | |
20 () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, 65536)); | |
21 Expect.throws( | |
22 () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, -1)); | |
23 Expect.throws(() => | |
24 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, 0, backlog: -1)); | |
25 } | |
26 | |
27 void testInvalidConnect() { | |
28 // Connect to an unknown DNS name. | |
29 try { | |
30 var socket = RawSynchronousSocket.connectSync("ko.faar.__hest__", 0); | |
31 Expect.fail("Failure expected"); | |
32 } catch (e) { | |
33 Expect.isTrue(e is SocketException); | |
34 } | |
35 | |
36 // Connect to an unavaliable IP-address. | |
37 try { | |
38 var socket = RawSynchronousSocket.connectSync("1.2.3.4", 0); | |
39 Expect.fail("Failure expected"); | |
40 } catch (e) { | |
41 Expect.isTrue(e is SocketException); | |
42 } | |
43 ; | |
44 } | |
45 | |
46 void testSimpleConnect() { | |
47 asyncStart(); | |
48 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) { | |
49 var socket = | |
50 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port); | |
51 server.listen((serverSocket) { | |
52 Expect.equals(socket.address, serverSocket.remoteAddress); | |
53 Expect.equals(socket.port, serverSocket.remotePort); | |
54 Expect.equals(socket.remoteAddress, server.address); | |
55 Expect.equals(socket.remotePort, server.port); | |
56 socket.closeSync(); | |
57 server.close(); | |
58 asyncEnd(); | |
59 }); | |
60 }); | |
61 } | |
62 | |
63 void testServerListenAfterConnect() { | |
64 asyncStart(); | |
65 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) { | |
66 Expect.isTrue(server.port > 0); | |
67 var client = | |
68 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port); | |
69 server.listen((socket) { | |
70 client.closeSync(); | |
71 server.close(); | |
72 socket.close(); | |
73 asyncEnd(); | |
74 }); | |
75 }); | |
76 } | |
77 | |
78 const messageSize = 1000; | |
79 // Configuration fields for the EchoServer. | |
80 enum EchoServerTypes { | |
81 // Max accumulated connections to server before close. Defaults to 1. | |
82 CONNECTION_COUNT, | |
83 // Sets the range of the fields to check in the list generated by | |
84 // createTestData(). | |
85 OFFSET_END, | |
86 OFFSET_START, | |
87 // The port used to communicate with an isolate. | |
88 ISOLATE_SEND_PORT, | |
89 // The port of the newly created echo server. | |
90 SERVER_PORT | |
91 } | |
92 | |
93 List<int> createTestData() { | |
94 return new List<int>.generate(messageSize, (index) => index & 0xff); | |
95 } | |
96 | |
97 // Consumes data generated by a test and compares it against the original test | |
98 // data. The optional fields, start and end, are used to compare against | |
99 // segments of the original test data list. In other words, data.length == (end | |
100 // - start). | |
101 void verifyTestData(List<int> data, [int start = 0, int end]) { | |
102 assert(data != null); | |
103 List<int> expected = createTestData(); | |
104 if (end == null) { | |
105 end = data.length; | |
106 } | |
107 end = min(messageSize, end); | |
108 Expect.equals(end - start, data.length); | |
109 for (int i = 0; i < (end - start); i++) { | |
110 Expect.equals(expected[start + i], data[i]); | |
111 } | |
112 } | |
113 | |
114 // The echo server is spawned in a new isolate and is used to test various | |
115 // synchronous read/write operations by echoing any data received back to the | |
116 // sender. The server should shutdown automatically after a specified number of | |
117 // socket disconnections (default: 1). | |
118 Future echoServer(var sendPort) async { | |
119 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) async { | |
120 ReceivePort receivePort = new ReceivePort(); | |
121 Map response = { | |
122 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort, | |
123 EchoServerTypes.SERVER_PORT: server.port | |
124 }; | |
125 sendPort.send(response); | |
126 Map limits = await receivePort.first; | |
127 int start = limits[EchoServerTypes.OFFSET_START]; | |
128 int end = limits[EchoServerTypes.OFFSET_END]; | |
129 int length = end - start; | |
130 int connection_count = limits[EchoServerTypes.CONNECTION_COUNT] ?? 1; | |
131 int connections = 0; | |
132 sendPort = limits[EchoServerTypes.ISOLATE_SEND_PORT]; | |
133 server.listen((client) { | |
134 int bytesRead = 0; | |
135 int bytesWritten = 0; | |
136 bool closedEventReceived = false; | |
137 List<int> data = new List<int>(length); | |
138 client.writeEventsEnabled = false; | |
139 client.listen((event) { | |
140 switch (event) { | |
141 case RawSocketEvent.READ: | |
142 Expect.isTrue(bytesWritten == 0); | |
143 Expect.isTrue(client.available() > 0); | |
144 var buffer = client.read(client.available()); | |
145 data.setRange(bytesRead, bytesRead + buffer.length, buffer); | |
146 bytesRead += buffer.length; | |
147 // Once we've read all the data, we can echo it back. Otherwise, | |
148 // keep waiting for more bytes. | |
149 if (bytesRead >= length) { | |
150 verifyTestData(data, start, end); | |
151 client.writeEventsEnabled = true; | |
152 } | |
153 break; | |
154 case RawSocketEvent.WRITE: | |
155 Expect.isFalse(client.writeEventsEnabled); | |
156 bytesWritten += | |
157 client.write(data, bytesWritten, data.length - bytesWritten); | |
158 if (bytesWritten < length) { | |
159 client.writeEventsEnabled = true; | |
160 } else if (bytesWritten == length) { | |
161 // Close the socket for writing from the server since we're done | |
162 // writing to this socket. The connection is closed completely | |
163 // after the client closes the socket for reading from the server. | |
164 client.shutdown(SocketDirection.SEND); | |
165 } | |
166 break; | |
167 case RawSocketEvent.READ_CLOSED: | |
168 client.close(); | |
169 break; | |
170 case RawSocketEvent.CLOSED: | |
171 Expect.isFalse(closedEventReceived); | |
172 closedEventReceived = true; | |
173 break; | |
174 default: | |
175 throw "Unexpected event $event"; | |
176 } | |
177 }, onDone: () { | |
178 Expect.isTrue(closedEventReceived); | |
179 connections++; | |
180 if (connections >= connection_count) { | |
181 server.close(); | |
182 } | |
183 }); | |
184 }, onDone: () { | |
185 // Let the client know we're shutting down then kill the isolate. | |
186 sendPort.send(null); | |
187 kill(); | |
188 }); | |
189 }); | |
190 } | |
191 | |
192 Future testSimpleReadWrite({bool dropReads}) async { | |
193 asyncStart(); | |
194 // This test creates a server and a client connects. The client writes data | |
195 // to the socket and the server echos it back. The client confirms the data it | |
196 // reads is the same as the data sent, then closes the socket, resulting in | |
197 // the closing of the server, which responds on receivePort with null to | |
198 // specify the echo server isolate is about to be killed. If an error occurs | |
199 // in the echo server, the exception and stack trace are sent to receivePort, | |
200 // which prints the exception and stack trace before eventually throwing an | |
201 // error. | |
202 ReceivePort receivePort = new ReceivePort(); | |
203 Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort); | |
204 | |
205 Map response = await receivePort.first; | |
206 SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT]; | |
207 int serverInternetPort = response[EchoServerTypes.SERVER_PORT]; | |
208 | |
209 receivePort = new ReceivePort(); | |
210 echo.addErrorListener(receivePort.sendPort); | |
211 | |
212 Map limits = { | |
213 EchoServerTypes.OFFSET_START: 0, | |
214 EchoServerTypes.OFFSET_END: messageSize, | |
215 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort | |
216 }; | |
217 sendPort.send(limits); | |
218 | |
219 try { | |
220 var socket = RawSynchronousSocket.connectSync( | |
221 LOOPBACK_IP_V4_STRING, serverInternetPort); | |
222 List<int> data = createTestData(); | |
223 socket.writeFromSync(data); | |
224 List<int> result = socket.readSync(data.length); | |
225 verifyTestData(result); | |
226 socket.shutdown(SocketDirection.SEND); | |
227 socket.closeSync(); | |
228 } catch (e, stack) { | |
229 print("Echo test failed in the client"); | |
230 rethrow; | |
231 } | |
232 // Wait for the server to shutdown before finishing the test. | |
233 var result = await receivePort.first; | |
234 if (result != null) { | |
235 throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" + | |
236 " ${result[1]}"; | |
237 } | |
238 asyncEnd(); | |
239 } | |
240 | |
241 Future testPartialRead() async { | |
242 asyncStart(); | |
243 // This test is based on testSimpleReadWrite, but instead of reading the | |
244 // entire echoed message at once, it reads it in two calls to readIntoSync. | |
245 ReceivePort receivePort = new ReceivePort(); | |
246 Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort); | |
247 | |
248 Map response = await receivePort.first; | |
249 SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT]; | |
250 int serverInternetPort = response[EchoServerTypes.SERVER_PORT]; | |
251 List<int> data = createTestData(); | |
252 | |
253 receivePort = new ReceivePort(); | |
254 echo.addErrorListener(receivePort.sendPort); | |
255 | |
256 Map limits = { | |
257 EchoServerTypes.OFFSET_START: 0, | |
258 EchoServerTypes.OFFSET_END: 1000, | |
259 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort | |
260 }; | |
261 sendPort.send(limits); | |
262 | |
263 try { | |
264 var socket = RawSynchronousSocket.connectSync( | |
265 LOOPBACK_IP_V4_STRING, serverInternetPort); | |
266 int half_length = (data.length / 2).toInt(); | |
267 | |
268 // Send the full data list to the server. | |
269 socket.writeFromSync(data); | |
270 List<int> result = new List<int>(data.length); | |
271 | |
272 // Read half at a time and check that there's still more bytes available. | |
273 socket.readIntoSync(result, 0, half_length); | |
274 verifyTestData(result.sublist(0, half_length), 0, half_length); | |
275 Expect.isTrue(socket.available() == (data.length - half_length)); | |
276 | |
277 // Read the second half and verify again. | |
278 socket.readIntoSync(result, half_length); | |
279 verifyTestData(result); | |
280 Expect.isTrue(socket.available() == 0); | |
281 | |
282 socket.closeSync(); | |
283 } catch (e, stack) { | |
284 print("Echo test failed in the client."); | |
285 rethrow; | |
286 } | |
287 // Wait for the server to shutdown before finishing the test. | |
288 var result = await receivePort.first; | |
289 if (result != null) { | |
290 throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" + | |
291 " ${result[1]}"; | |
292 } | |
293 asyncEnd(); | |
294 } | |
295 | |
296 Future testPartialWrite() async { | |
297 asyncStart(); | |
298 // This test is based on testSimpleReadWrite, but instead of writing the | |
299 // entire data buffer at once, it writes different parts of the buffer over | |
300 // multiple calls to writeFromSync. | |
301 ReceivePort receivePort = new ReceivePort(); | |
302 Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort); | |
303 | |
304 Map response = await receivePort.first; | |
305 List<int> data = createTestData(); | |
306 SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT]; | |
307 int startOffset = 32; | |
308 int endOffset = (data.length / 2).toInt(); | |
309 int serverInternetPort = response[EchoServerTypes.SERVER_PORT]; | |
310 | |
311 receivePort = new ReceivePort(); | |
312 echo.addErrorListener(receivePort.sendPort); | |
313 | |
314 Map limits = { | |
315 EchoServerTypes.OFFSET_START: startOffset, | |
316 EchoServerTypes.OFFSET_END: endOffset, | |
317 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort | |
318 }; | |
319 sendPort.send(limits); | |
320 try { | |
321 var socket = RawSynchronousSocket.connectSync( | |
322 LOOPBACK_IP_V4_STRING, serverInternetPort); | |
323 List<int> data = createTestData(); | |
324 | |
325 // Write a subset of data to the server. | |
326 socket.writeFromSync(data, startOffset, endOffset); | |
327 | |
328 // Grab the response and verify it's correct. | |
329 List<int> result = new List<int>(endOffset - startOffset); | |
330 socket.readIntoSync(result); | |
331 | |
332 Expect.equals(result.length, endOffset - startOffset); | |
333 verifyTestData(result, startOffset, endOffset); | |
334 socket.closeSync(); | |
335 } catch (e, stack) { | |
336 print("Echo test failed in the client."); | |
337 rethrow; | |
338 } | |
339 | |
340 // Wait for the server to shutdown before finishing the test. | |
341 var result = await receivePort.first; | |
342 if (result != null) { | |
343 throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" + | |
344 " ${result[1]}"; | |
345 } | |
346 asyncEnd(); | |
347 } | |
348 | |
349 Future testShutdown() async { | |
350 asyncStart(); | |
351 // This test creates a server and a client connects. The client then tries to | |
352 // perform various operations after being shutdown in a specific direction, to | |
353 // ensure reads or writes cannot be performed if the socket has been shutdown for | |
zra
2017/04/10 21:39:44
long line
bkonyi
2017/04/11 01:30:52
Done.
| |
354 // reading or writing. | |
355 ReceivePort receivePort = new ReceivePort(); | |
356 Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort); | |
357 | |
358 Map response = await receivePort.first; | |
359 SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT]; | |
360 int serverInternetPort = response[EchoServerTypes.SERVER_PORT]; | |
361 List<int> data = createTestData(); | |
362 | |
363 receivePort = new ReceivePort(); | |
364 echo.addErrorListener(receivePort.sendPort); | |
365 | |
366 Map limits = { | |
367 EchoServerTypes.OFFSET_START: 0, | |
368 EchoServerTypes.OFFSET_END: data.length, | |
369 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort, | |
370 // Tell the server to shutdown after 3 sockets disconnect. | |
371 EchoServerTypes.CONNECTION_COUNT: 3 | |
372 }; | |
373 sendPort.send(limits); | |
374 | |
375 try { | |
376 var socket = RawSynchronousSocket.connectSync( | |
377 LOOPBACK_IP_V4_STRING, serverInternetPort); | |
378 | |
379 // Close from both directions. Shouldn't be able to read/write to the | |
380 // socket. | |
381 socket.shutdown(SocketDirection.BOTH); | |
382 Expect.throws( | |
383 () => socket.writeFromSync(data), (e) => e is SocketException); | |
384 Expect.throws( | |
385 () => socket.readSync(data.length), (e) => e is SocketException); | |
386 socket.closeSync(); | |
387 | |
388 // Close the socket for reading, do a write, and see if we can get any | |
389 // response from the server (we shouldn't be able to). | |
390 socket = RawSynchronousSocket.connectSync( | |
391 LOOPBACK_IP_V4_STRING, serverInternetPort); | |
392 socket.shutdown(SocketDirection.RECEIVE); | |
393 socket.writeFromSync(data); | |
394 // Throws exception when the socket is closed for RECEIVE. | |
395 Expect.throws( | |
396 () => socket.readSync(data.length), (e) => e is SocketException); | |
397 Expect.isTrue(socket.available() == 0); | |
398 socket.closeSync(); | |
399 | |
400 // Close the socket for writing and try to do a write. This should cause an | |
401 // OSError to be throw as the pipe is closed for writing. | |
402 socket = RawSynchronousSocket.connectSync( | |
403 LOOPBACK_IP_V4_STRING, serverInternetPort); | |
404 socket.shutdown(SocketDirection.SEND); | |
405 Expect.throws( | |
406 () => socket.writeFromSync(data), (e) => e is SocketException); | |
407 socket.closeSync(); | |
408 } catch (e, stack) { | |
409 print("Echo test failed in client."); | |
410 rethrow; | |
411 } | |
412 // Wait for the server to shutdown before finishing the test. | |
413 var result = await receivePort.first; | |
414 if (result != null) { | |
415 throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" + | |
416 " ${result[1]}"; | |
417 } | |
418 asyncEnd(); | |
419 } | |
420 | |
421 Future testInvalidReadWriteOperations() { | |
422 asyncStart(); | |
423 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) { | |
424 server.listen((socket) {}); | |
425 List<int> data = createTestData(); | |
426 var socket = | |
427 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port); | |
428 | |
429 // Invalid writeFromSync invocations | |
430 Expect.throws(() => socket.writeFromSync(data, data.length + 1), | |
431 (e) => e is RangeError); | |
432 Expect.throws(() => socket.writeFromSync(data, 0, data.length + 1), | |
433 (e) => e is RangeError); | |
434 Expect.throws( | |
435 () => socket.writeFromSync(data, 1, 0), (e) => e is RangeError); | |
436 Expect.throws( | |
437 () => socket.writeFromSync(data, null), (e) => e is ArgumentError); | |
438 | |
439 // Invalid readIntoSync invocations | |
440 List<int> buffer = new List<int>(10); | |
441 Expect.throws(() => socket.readIntoSync(buffer, buffer.length + 1), | |
442 (e) => e is RangeError); | |
443 Expect.throws(() => socket.readIntoSync(buffer, 0, buffer.length + 1), | |
444 (e) => e is RangeError); | |
445 Expect.throws( | |
446 () => socket.readIntoSync(buffer, 1, 0), (e) => e is RangeError); | |
447 Expect.throws( | |
448 () => socket.readIntoSync(buffer, null), (e) => e is ArgumentError); | |
449 | |
450 // Invalid readSync invocation | |
451 Expect.throws(() => socket.readSync(-1), (e) => e is ArgumentError); | |
452 | |
453 server.close(); | |
454 socket.closeSync(); | |
455 asyncEnd(); | |
456 }); | |
457 } | |
458 | |
459 void testClosedError() { | |
460 asyncStart(); | |
461 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) { | |
462 server.listen((socket) { | |
463 socket.close(); | |
464 }); | |
465 var socket = | |
466 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port); | |
467 server.close(); | |
468 socket.closeSync(); | |
469 Expect.throws(() => socket.remotePort, (e) => e is SocketException); | |
470 Expect.throws(() => socket.remoteAddress, (e) => e is SocketException); | |
471 asyncEnd(); | |
472 }); | |
473 } | |
474 | |
475 main() async { | |
476 asyncStart(); | |
477 testArguments(); | |
478 testInvalidConnect(); | |
479 await testShutdown(); | |
480 testSimpleConnect(); | |
481 testServerListenAfterConnect(); | |
482 await testSimpleReadWrite(); | |
483 await testPartialRead(); | |
484 await testPartialWrite(); | |
485 testInvalidReadWriteOperations(); | |
486 testClosedError(); | |
487 asyncEnd(); | |
488 } | |
OLD | NEW |