test_udpsocket.html (12878B)
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"); 18 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); 26 27 for (let i = 0; i < BIG_ARRAY.length; i++) { 28 BIG_ARRAY[i] = Math.floor(Math.random() * 256); 29 } 30 31 TYPED_DATA_ARRAY.set(DATA_ARRAY); 32 BIG_TYPED_ARRAY.set(BIG_ARRAY); 33 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); 37 38 if (recv_dataview.length !== expected_dataview.length) { 39 return false; 40 } 41 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; 46 } 47 } 48 return true; 49 } 50 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'); 61 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'); 67 68 return socket; 69 }); 70 } 71 72 function testSendString(socket) { 73 info('test for sending string data'); 74 75 socket.send(HELLO_WORLD, '127.0.0.1', socket.localPort); 76 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 }); 85 } 86 87 function testSendArrayBuffer(socket) { 88 info('test for sending ArrayBuffer'); 89 90 socket.send(DATA_ARRAY_BUFFER, '127.0.0.1', socket.localPort); 91 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 }); 99 } 100 101 function testSendArrayBufferView(socket) { 102 info('test for sending ArrayBufferView'); 103 104 socket.send(TYPED_DATA_ARRAY, '127.0.0.1', socket.localPort); 105 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}); 112 }); 113 } 114 115 function testSendBlob(socket) { 116 info('test for sending Blob'); 117 118 let blob = new Blob([HELLO_WORLD], {type : 'text/plain'}); 119 socket.send(blob, '127.0.0.1', socket.localPort); 120 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}); 128 }); 129 } 130 131 function testSendBigArray(socket) { 132 info('test for sending Big ArrayBuffer'); 133 134 socket.send(BIG_TYPED_ARRAY, '127.0.0.1', socket.localPort); 135 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); 146 } 147 }); 148 }); 149 } 150 151 function testSendBigBlob(socket) { 152 info('test for sending Big Blob'); 153 154 let blob = new Blob([BIG_TYPED_ARRAY]); 155 socket.send(blob, '127.0.0.1', socket.localPort); 156 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); 167 } 168 }); 169 }); 170 } 171 172 function testUDPOptions(socket) { 173 info('test for UDP init options'); 174 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'); 185 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}); 195 }); 196 }); 197 } 198 199 function testClose(socket) { 200 info('test for close'); 201 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'); 209 } 210 211 return socket.closed.then(function() { 212 ok(true, 'expected closedPromise is resolved after socket.close()'); 213 }); 214 } 215 216 function testMulticast() { 217 info('test for multicast'); 218 219 let socket = new UDPSocket({loopback: true}); 220 221 const MCAST_ADDRESS = '224.0.0.255'; 222 socket.joinMulticastGroup(MCAST_ADDRESS); 223 224 return socket.opened.then(function() { 225 socket.send(HELLO_WORLD, MCAST_ADDRESS, socket.localPort); 226 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}); 235 }); 236 }); 237 } 238 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'); 246 } 247 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'); 253 } 254 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'); 260 } 261 } 262 263 function testOpenFailed() { 264 info('test for falied on open'); 265 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'}); 268 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'); 274 }); 275 } 276 277 function testSendBeforeOpen() { 278 info('test for send before open'); 279 280 let socket = new UDPSocket(); 281 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'); 287 } 288 289 return socket.opened.then(function() { 290 socket.close(); 291 }); 292 } 293 294 function testCloseBeforeOpened() { 295 info('test for close socket before opened'); 296 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'); 302 }); 303 304 return socket.close().then(function() { 305 ok(true, 'expected closedPromise to be resolved'); 306 }).then(socket.opened); 307 } 308 309 function testOpenWithoutClose() { 310 info('test for open without close'); 311 312 let closed = []; 313 for (let i = 0; i < 50; i++) { 314 let socket = new UDPSocket(); 315 closed.push(socket.closed); 316 } 317 318 SpecialPowers.gc(); 319 info('all unrefereced socket should be closed right after GC'); 320 321 return Promise.all(closed); 322 } 323 324 function awaitEvent(target, event) { 325 return new Promise(resolve => { 326 target.addEventListener(event, resolve, { once: true }); 327 }); 328 } 329 330 async function testBFCache() { 331 info('test for bfcache behavior'); 332 333 let socket = new UDPSocket(); 334 335 await socket.opened; 336 337 let win = window.open(`file_udpsocket_iframe.html?${socket.localPort}`); 338 339 let msg = await awaitEvent(socket, "message"); 340 341 win.location = "file_postMessage_opener.html"; 342 await awaitEvent(window, "message"); 343 344 socket.send(HELLO_WORLD, '127.0.0.1', msg.remotePort); 345 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'); 350 } 351 352 socket.addEventListener('message', recv_again_callback); 353 354 setTimeout(function() { 355 socket.removeEventListener('message', recv_again_callback); 356 socket.close(); 357 resolve(); 358 }, 5000); 359 }); 360 361 win.close(); 362 } 363 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(); 384 }) 385 .catch(function(err) { 386 ok(false, 'test failed due to: ' + err); 387 SimpleTest.finish(); 388 }); 389 } 390 391 window.addEventListener('load', function () { 392 SpecialPowers.pushPrefEnv({ 393 'set': [ 394 ['dom.udpsocket.enabled', true], 395 ['browser.sessionhistory.max_total_viewers', 10] 396 ] 397 }, runTest); 398 }); 399 400 </script> 401 </pre> 402 </body> 403 </html>