tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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>