Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / network / tests / test_udpsocket.html
blobf465c1727f810d2241555c4575bc248344707279
1 <!DOCTYPE HTML>
2 <html>
3 <head>
4 <title>Test UDPSocket API</title>
5 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
6 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
7 </head>
8 <body>
9 <p id="display"></p>
10 <div id="content" style="display: none">
11 </div>
12 <iframe id="iframe"></iframe>
13 <pre id="test">
14 <script type="application/javascript">
15 'use strict';
16 SimpleTest.waitForExplicitFinish();
17 SimpleTest.requestFlakyTimeout("untriaged");
19 const HELLO_WORLD = 'hlo wrld. ';
20 const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0];
21 const DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length);
22 const TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER);
23 const BIG_ARRAY = new Array(4096);
24 const BIG_ARRAY_BUFFER = new ArrayBuffer(BIG_ARRAY.length);
25 const BIG_TYPED_ARRAY = new Uint8Array(BIG_ARRAY_BUFFER);
27 for (let i = 0; i < BIG_ARRAY.length; i++) {
28 BIG_ARRAY[i] = Math.floor(Math.random() * 256);
31 TYPED_DATA_ARRAY.set(DATA_ARRAY);
32 BIG_TYPED_ARRAY.set(BIG_ARRAY);
34 function is_same_buffer(recv_data, expect_data) {
35 let recv_dataview = new Uint8Array(recv_data);
36 let expected_dataview = new Uint8Array(expect_data);
38 if (recv_dataview.length !== expected_dataview.length) {
39 return false;
42 for (let i = 0; i < recv_dataview.length; i++) {
43 if (recv_dataview[i] != expected_dataview[i]) {
44 info('discover byte differenct at ' + i);
45 return false;
48 return true;
51 function testOpen() {
52 info('test for creating an UDP Socket');
53 let socket = new UDPSocket();
54 is(socket.localPort, null, 'expect no local port before socket opened');
55 is(socket.localAddress, null, 'expect no local address before socket opened');
56 is(socket.remotePort, null, 'expected no default remote port');
57 is(socket.remoteAddress, null, 'expected no default remote address');
58 is(socket.readyState, 'opening', 'expected ready state = opening');
59 is(socket.loopback, false, 'expected no loopback');
60 is(socket.addressReuse, true, 'expect to reuse address');
62 return socket.opened.then(function() {
63 ok(true, 'expect openedPromise to be resolved after successful socket binding');
64 ok(!(socket.localPort === 0), 'expect allocated a local port');
65 is(socket.localAddress, '0.0.0.0', 'expect assigned to default address');
66 is(socket.readyState, 'open', 'expected ready state = open');
68 return socket;
69 });
72 function testSendString(socket) {
73 info('test for sending string data');
75 socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort);
77 return new Promise(function(resolve) {
78 socket.addEventListener('message', function(msg) {
79 let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
80 is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
81 is(recvData, HELLO_WORLD, 'expected same string data');
82 resolve(socket);
83 }, {once: true});
84 });
87 function testSendArrayBuffer(socket) {
88 info('test for sending ArrayBuffer');
90 socket.send(DATA_ARRAY_BUFFER, '127.0.0.1', socket.localPort);
92 return new Promise(function(resolve) {
93 socket.addEventListener('message', function(msg) {
94 is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
95 ok(is_same_buffer(msg.data, DATA_ARRAY_BUFFER), 'expected same buffer data');
96 resolve(socket);
97 }, {once: true});
98 });
101 function testSendArrayBufferView(socket) {
102 info('test for sending ArrayBufferView');
104 socket.send(TYPED_DATA_ARRAY, '127.0.0.1', socket.localPort);
106 return new Promise(function(resolve) {
107 socket.addEventListener('message', function(msg) {
108 is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
109 ok(is_same_buffer(msg.data, TYPED_DATA_ARRAY), 'expected same buffer data');
110 resolve(socket);
111 }, {once: true});
115 function testSendBlob(socket) {
116 info('test for sending Blob');
118 let blob = new Blob([HELLO_WORLD], {type : 'text/plain'});
119 socket.send(blob, '127.0.0.1', socket.localPort);
121 return new Promise(function(resolve) {
122 socket.addEventListener('message', function(msg) {
123 let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
124 is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
125 is(recvData, HELLO_WORLD, 'expected same string data');
126 resolve(socket);
127 }, {once: true});
131 function testSendBigArray(socket) {
132 info('test for sending Big ArrayBuffer');
134 socket.send(BIG_TYPED_ARRAY, '127.0.0.1', socket.localPort);
136 return new Promise(function(resolve) {
137 let byteReceived = 0;
138 socket.addEventListener('message', function recv_callback(msg) {
139 let byteBegin = byteReceived;
140 byteReceived += msg.data.byteLength;
141 is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
142 ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
143 if (byteReceived >= BIG_TYPED_ARRAY.length) {
144 socket.removeEventListener('message', recv_callback);
145 resolve(socket);
151 function testSendBigBlob(socket) {
152 info('test for sending Big Blob');
154 let blob = new Blob([BIG_TYPED_ARRAY]);
155 socket.send(blob, '127.0.0.1', socket.localPort);
157 return new Promise(function(resolve) {
158 let byteReceived = 0;
159 socket.addEventListener('message', function recv_callback(msg) {
160 let byteBegin = byteReceived;
161 byteReceived += msg.data.byteLength;
162 is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
163 ok(is_same_buffer(msg.data, BIG_TYPED_ARRAY.subarray(byteBegin, byteReceived)), 'expected same buffer data [' + byteBegin+ '-' + byteReceived + ']');
164 if (byteReceived >= BIG_TYPED_ARRAY.length) {
165 socket.removeEventListener('message', recv_callback);
166 resolve(socket);
172 function testUDPOptions(socket) {
173 info('test for UDP init options');
175 let remoteSocket = new UDPSocket({addressReuse: false,
176 loopback: true,
177 localAddress: '127.0.0.1',
178 remoteAddress: '127.0.0.1',
179 remotePort: socket.localPort});
180 is(remoteSocket.localAddress, '127.0.0.1', 'expected local address');
181 is(remoteSocket.remoteAddress, '127.0.0.1', 'expected remote address');
182 is(remoteSocket.remotePort, socket.localPort, 'expected remote port');
183 is(remoteSocket.addressReuse, false, 'expected address not reusable');
184 is(remoteSocket.loopback, true, 'expected loopback mode is on');
186 return remoteSocket.opened.then(function() {
187 remoteSocket.send(HELLO_WORLD);
188 return new Promise(function(resolve) {
189 socket.addEventListener('message', function(msg) {
190 let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
191 is(msg.remotePort, remoteSocket.localPort, 'expected packet from ' + remoteSocket.localPort);
192 is(recvData, HELLO_WORLD, 'expected same string data');
193 resolve(socket);
194 }, {once: true});
199 function testClose(socket) {
200 info('test for close');
202 socket.close();
203 is(socket.readyState, 'closed', 'expect ready state to be "closed"');
204 try {
205 socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort);
206 ok(false, 'unexpect to send successfully');
207 } catch (e) {
208 ok(true, 'expected send fail after socket closed');
211 return socket.closed.then(function() {
212 ok(true, 'expected closedPromise is resolved after socket.close()');
216 function testMulticast() {
217 info('test for multicast');
219 let socket = new UDPSocket({loopback: true});
221 const MCAST_ADDRESS = '224.0.0.255';
222 socket.joinMulticastGroup(MCAST_ADDRESS);
224 return socket.opened.then(function() {
225 socket.send(HELLO_WORLD, MCAST_ADDRESS, socket.localPort);
227 return new Promise(function(resolve) {
228 socket.addEventListener('message', function(msg) {
229 let recvData= String.fromCharCode.apply(null, new Uint8Array(msg.data));
230 is(msg.remotePort, socket.localPort, 'expected packet from ' + socket.localPort);
231 is(recvData, HELLO_WORLD, 'expected same string data');
232 socket.leaveMulticastGroup(MCAST_ADDRESS);
233 resolve();
234 }, {once: true});
239 function testInvalidUDPOptions() {
240 info('test for invalid UDPOptions');
241 try {
242 new UDPSocket({localAddress: 'not-a-valid-address'});
243 ok(false, 'should not create an UDPSocket with an invalid localAddress');
244 } catch (e) {
245 is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localAddress is not a valid IPv4/6 address');
248 try {
249 new UDPSocket({localPort: 0});
250 ok(false, 'should not create an UDPSocket with an invalid localPort');
251 } catch (e) {
252 is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localPort is not a valid port number');
255 try {
256 new UDPSocket({remotePort: 0});
257 ok(false, 'should not create an UDPSocket with an invalid remotePort');
258 } catch (e) {
259 is(e.name, 'InvalidAccessError', 'expected InvalidAccessError will be thrown if localPort is not a valid port number');
263 function testOpenFailed() {
264 info('test for falied on open');
266 //according to RFC5737, address block 192.0.2.0/24 should not be used in both local and public contexts
267 let socket = new UDPSocket({localAddress: '192.0.2.0'});
269 return socket.opened.then(function() {
270 ok(false, 'should not resolve openedPromise while fail to bind socket');
271 socket.close();
272 }).catch(function(reason) {
273 is(reason.name, 'NetworkError', 'expected openedPromise to be rejected while fail to bind socket');
277 function testSendBeforeOpen() {
278 info('test for send before open');
280 let socket = new UDPSocket();
282 try {
283 socket.send(HELLO_WORLD, '127.0.0.1', 9);
284 ok(false, 'unexpect to send successfully');
285 } catch (e) {
286 ok(true, 'expected send fail before openedPromise is resolved');
289 return socket.opened.then(function() {
290 socket.close();
294 function testCloseBeforeOpened() {
295 info('test for close socket before opened');
297 let socket = new UDPSocket();
298 socket.opened.then(function() {
299 ok(false, 'should not resolve openedPromise if it has already been closed');
300 }).catch(function(reason) {
301 is(reason.name, 'AbortError', 'expected openedPromise to be rejected while socket is closed during opening');
304 return socket.close().then(function() {
305 ok(true, 'expected closedPromise to be resolved');
306 }).then(socket.opened);
309 function testOpenWithoutClose() {
310 info('test for open without close');
312 let closed = [];
313 for (let i = 0; i < 50; i++) {
314 let socket = new UDPSocket();
315 closed.push(socket.closed);
318 SpecialPowers.gc();
319 info('all unrefereced socket should be closed right after GC');
321 return Promise.all(closed);
324 function awaitEvent(target, event) {
325 return new Promise(resolve => {
326 target.addEventListener(event, resolve, { once: true });
330 async function testBFCache() {
331 info('test for bfcache behavior');
333 let socket = new UDPSocket();
335 await socket.opened;
337 let win = window.open(`file_udpsocket_iframe.html?${socket.localPort}`);
339 let msg = await awaitEvent(socket, "message");
341 win.location = "file_postMessage_opener.html";
342 await awaitEvent(window, "message");
344 socket.send(HELLO_WORLD, '127.0.0.1', msg.remotePort);
346 await new Promise(resolve => {
347 function recv_again_callback() {
348 socket.removeEventListener('message', recv_again_callback);
349 ok(false, 'should not receive packet after page unload');
352 socket.addEventListener('message', recv_again_callback);
354 setTimeout(function() {
355 socket.removeEventListener('message', recv_again_callback);
356 socket.close();
357 resolve();
358 }, 5000);
361 win.close();
364 function runTest() {
365 testOpen()
366 .then(testSendString)
367 .then(testSendArrayBuffer)
368 .then(testSendArrayBufferView)
369 .then(testSendBlob)
370 .then(testSendBigArray)
371 .then(testSendBigBlob)
372 .then(testUDPOptions)
373 .then(testClose)
374 .then(testMulticast)
375 .then(testInvalidUDPOptions)
376 .then(testOpenFailed)
377 .then(testSendBeforeOpen)
378 .then(testCloseBeforeOpened)
379 .then(testOpenWithoutClose)
380 .then(testBFCache)
381 .then(function() {
382 info('test finished');
383 SimpleTest.finish();
385 .catch(function(err) {
386 ok(false, 'test failed due to: ' + err);
387 SimpleTest.finish();
391 window.addEventListener('load', function () {
392 SpecialPowers.pushPrefEnv({
393 'set': [
394 ['dom.udpsocket.enabled', true],
395 ['browser.sessionhistory.max_total_viewers', 10]
397 }, runTest);
400 </script>
401 </pre>
402 </body>
403 </html>