tor-browser

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

test_websocket_server.js (12611B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 /* import-globals-from head_cache.js */
      8 /* import-globals-from head_cookies.js */
      9 /* import-globals-from head_channels.js */
     10 
     11 const {
     12  NodeWebSocketPlainServer,
     13  NodeWebSocketServer,
     14  NodeWebSocketHttp2Server,
     15  NodeHTTPProxyServer,
     16  NodeHTTPSProxyServer,
     17  NodeHTTP2ProxyServer,
     18  WebSocketConnection,
     19 } = ChromeUtils.importESModule("resource://testing-common/NodeServer.sys.mjs");
     20 
     21 const certOverrideService = Cc[
     22  "@mozilla.org/security/certoverride;1"
     23 ].getService(Ci.nsICertOverrideService);
     24 
     25 add_setup(async function setup() {
     26  certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
     27    true
     28  );
     29 
     30  registerCleanupFunction(async () => {
     31    certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
     32      false
     33    );
     34  });
     35 });
     36 
     37 function makeChan(uri) {
     38  let chan = NetUtil.newChannel({
     39    uri,
     40    loadUsingSystemPrincipal: true,
     41  }).QueryInterface(Ci.nsIHttpChannel);
     42  chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
     43  return chan;
     44 }
     45 
     46 function httpChannelOpenPromise(chan, flags) {
     47  return new Promise(resolve => {
     48    function finish(req, buffer) {
     49      resolve([req, buffer]);
     50    }
     51    chan.asyncOpen(new ChannelListener(finish, null, flags));
     52  });
     53 }
     54 
     55 async function channelOpenPromise(url, msg) {
     56  let conn = new WebSocketConnection();
     57  let statusObj = await Promise.race([conn.open(url), conn.finished()]);
     58  if (statusObj && statusObj.status != Cr.NS_OK) {
     59    return [statusObj.status, ""];
     60  }
     61  let finalStatusPromise = conn.finished();
     62  conn.send(msg);
     63  let res = await conn.receiveMessages();
     64  conn.close();
     65  let finalStatus = await finalStatusPromise;
     66  return [finalStatus.status, res];
     67 }
     68 
     69 // h1.1 direct
     70 async function test_h1_plain_websocket_direct() {
     71  let wss = new NodeWebSocketPlainServer();
     72  await wss.start();
     73  registerCleanupFunction(async () => wss.stop());
     74  Assert.notEqual(wss.port(), null);
     75  await wss.registerMessageHandler((data, ws) => {
     76    ws.send(data);
     77  });
     78  let url = `ws://localhost:${wss.port()}`;
     79  const msg = "test websocket";
     80 
     81  let conn = new WebSocketConnection();
     82  await conn.open(url);
     83  conn.send(msg);
     84  let mess1 = await conn.receiveMessages();
     85  Assert.deepEqual(mess1, [msg]);
     86 
     87  // Now send 3 more, and check that we received all of them
     88  conn.send(msg);
     89  conn.send(msg);
     90  conn.send(msg);
     91  let mess2 = [];
     92  while (mess2.length < 3) {
     93    // receive could return 1, 2 or all 3 replies.
     94    mess2 = mess2.concat(await conn.receiveMessages());
     95  }
     96  Assert.deepEqual(mess2, [msg, msg, msg]);
     97 
     98  conn.close();
     99  let { status } = await conn.finished();
    100 
    101  Assert.equal(status, Cr.NS_OK);
    102 }
    103 
    104 // h1.1 direct
    105 async function test_h1_websocket_direct() {
    106  let wss = new NodeWebSocketServer();
    107  await wss.start();
    108  registerCleanupFunction(async () => wss.stop());
    109  Assert.notEqual(wss.port(), null);
    110  await wss.registerMessageHandler((data, ws) => {
    111    ws.send(data);
    112  });
    113  let url = `wss://localhost:${wss.port()}`;
    114  const msg = "test websocket";
    115 
    116  let conn = new WebSocketConnection();
    117  await conn.open(url);
    118  conn.send(msg);
    119  let mess1 = await conn.receiveMessages();
    120  Assert.deepEqual(mess1, [msg]);
    121 
    122  // Now send 3 more, and check that we received all of them
    123  conn.send(msg);
    124  conn.send(msg);
    125  conn.send(msg);
    126  let mess2 = [];
    127  while (mess2.length < 3) {
    128    // receive could return 1, 2 or all 3 replies.
    129    mess2 = mess2.concat(await conn.receiveMessages());
    130  }
    131  Assert.deepEqual(mess2, [msg, msg, msg]);
    132 
    133  conn.close();
    134  let { status } = await conn.finished();
    135 
    136  Assert.equal(status, Cr.NS_OK);
    137 }
    138 
    139 // h1 server with secure h1.1 proxy
    140 async function test_h1_ws_with_secure_h1_proxy() {
    141  let proxy = new NodeHTTPSProxyServer();
    142  await proxy.start();
    143 
    144  let wss = new NodeWebSocketServer();
    145  await wss.start();
    146 
    147  registerCleanupFunction(async () => {
    148    await wss.stop();
    149    await proxy.stop();
    150  });
    151 
    152  Assert.notEqual(wss.port(), null);
    153  await wss.registerMessageHandler((data, ws) => {
    154    ws.send(data);
    155  });
    156 
    157  let url = `wss://localhost:${wss.port()}`;
    158  const msg = "test h1.1 websocket with h1.1 secure proxy";
    159  let [status, res] = await channelOpenPromise(url, msg);
    160  Assert.equal(status, Cr.NS_OK);
    161  Assert.deepEqual(res, [msg]);
    162 
    163  await proxy.stop();
    164 }
    165 
    166 async function test_h2_websocket_direct() {
    167  Services.prefs.setBoolPref("network.http.http2.websockets", true);
    168  let wss = new NodeWebSocketHttp2Server();
    169  await wss.start();
    170  registerCleanupFunction(async () => wss.stop());
    171 
    172  Assert.notEqual(wss.port(), null);
    173  await wss.registerMessageHandler((data, ws) => {
    174    ws.send(data);
    175  });
    176  let url = `wss://localhost:${wss.port()}`;
    177  const msg = "test h2 websocket h2 direct";
    178  let [status, res] = await channelOpenPromise(url, msg);
    179  Assert.equal(status, Cr.NS_OK);
    180  Assert.deepEqual(res, [msg]);
    181 }
    182 
    183 // ws h1.1 with insecure h1.1 proxy
    184 async function test_h1_ws_with_h1_insecure_proxy() {
    185  Services.prefs.setBoolPref("network.http.http2.websockets", false);
    186  let proxy = new NodeHTTPProxyServer();
    187  await proxy.start();
    188 
    189  let wss = new NodeWebSocketServer();
    190  await wss.start();
    191 
    192  registerCleanupFunction(async () => {
    193    await wss.stop();
    194    await proxy.stop();
    195  });
    196 
    197  Assert.notEqual(wss.port(), null);
    198 
    199  await wss.registerMessageHandler((data, ws) => {
    200    ws.send(data);
    201  });
    202  let url = `wss://localhost:${wss.port()}`;
    203  const msg = "test h1 websocket with h1 insecure proxy";
    204  let [status, res] = await channelOpenPromise(url, msg);
    205  Assert.equal(status, Cr.NS_OK);
    206  Assert.deepEqual(res, [msg]);
    207 
    208  await proxy.stop();
    209 }
    210 
    211 // ws h1.1 with h2 proxy
    212 async function test_h1_ws_with_h2_proxy() {
    213  Services.prefs.setBoolPref("network.http.http2.websockets", false);
    214 
    215  let proxy = new NodeHTTP2ProxyServer();
    216  await proxy.start();
    217 
    218  let wss = new NodeWebSocketServer();
    219  await wss.start();
    220 
    221  registerCleanupFunction(async () => {
    222    await wss.stop();
    223    await proxy.stop();
    224  });
    225 
    226  Assert.notEqual(wss.port(), null);
    227  await wss.registerMessageHandler((data, ws) => {
    228    ws.send(data);
    229  });
    230 
    231  let url = `wss://localhost:${wss.port()}`;
    232  const msg = "test h1 websocket with h2 proxy";
    233  let [status, res] = await channelOpenPromise(url, msg);
    234  Assert.equal(status, Cr.NS_OK);
    235  Assert.deepEqual(res, [msg]);
    236 
    237  await proxy.stop();
    238 }
    239 
    240 // ws h2 with insecure h1.1 proxy
    241 async function test_h2_ws_with_h1_insecure_proxy() {
    242  Services.prefs.setBoolPref("network.http.http2.websockets", true);
    243 
    244  let proxy = new NodeHTTPProxyServer();
    245  await proxy.start();
    246 
    247  registerCleanupFunction(async () => {
    248    await wss.stop();
    249    await proxy.stop();
    250  });
    251 
    252  let wss = new NodeWebSocketHttp2Server();
    253  await wss.start();
    254  Assert.notEqual(wss.port(), null);
    255  await wss.registerMessageHandler((data, ws) => {
    256    ws.send(data);
    257  });
    258 
    259  let url = `wss://localhost:${wss.port()}`;
    260  const msg = "test h2 websocket with h1 insecure proxy";
    261  let [status, res] = await channelOpenPromise(url, msg);
    262  Assert.equal(status, Cr.NS_OK);
    263  Assert.deepEqual(res, [msg]);
    264 
    265  await proxy.stop();
    266 }
    267 
    268 // ws h2 with secure h1 proxy
    269 async function test_h2_ws_with_h1_secure_proxy() {
    270  Services.prefs.setBoolPref("network.http.http2.websockets", true);
    271 
    272  let proxy = new NodeHTTPSProxyServer();
    273  await proxy.start();
    274 
    275  let wss = new NodeWebSocketHttp2Server();
    276  await wss.start();
    277 
    278  registerCleanupFunction(async () => {
    279    await wss.stop();
    280    await proxy.stop();
    281  });
    282 
    283  Assert.notEqual(wss.port(), null);
    284  await wss.registerMessageHandler((data, ws) => {
    285    ws.send(data);
    286  });
    287 
    288  let url = `wss://localhost:${wss.port()}`;
    289  const msg = "test h2 websocket with h1 secure proxy";
    290  let [status, res] = await channelOpenPromise(url, msg);
    291  Assert.equal(status, Cr.NS_OK);
    292  Assert.deepEqual(res, [msg]);
    293 
    294  await proxy.stop();
    295 }
    296 
    297 // ws h2 with secure h2 proxy
    298 async function test_h2_ws_with_h2_proxy() {
    299  Services.prefs.setBoolPref("network.http.http2.websockets", true);
    300 
    301  let proxy = new NodeHTTP2ProxyServer();
    302  await proxy.start(); // start and register proxy "filter"
    303 
    304  let wss = new NodeWebSocketHttp2Server();
    305  await wss.start(); // init port
    306 
    307  registerCleanupFunction(async () => {
    308    await wss.stop();
    309    await proxy.stop();
    310  });
    311 
    312  Assert.notEqual(wss.port(), null);
    313  await wss.registerMessageHandler((data, ws) => {
    314    ws.send(data);
    315  });
    316 
    317  let url = `wss://localhost:${wss.port()}`;
    318  const msg = "test h2 websocket with h2 proxy";
    319  let [status, res] = await channelOpenPromise(url, msg);
    320  Assert.equal(status, Cr.NS_OK);
    321  Assert.deepEqual(res, [msg]);
    322 
    323  await proxy.stop();
    324 }
    325 
    326 async function test_bug_1848013() {
    327  Services.prefs.setBoolPref("network.http.http2.websockets", true);
    328 
    329  let proxy = new NodeHTTPProxyServer();
    330  await proxy.start();
    331 
    332  registerCleanupFunction(async () => {
    333    await wss.stop();
    334    await proxy.stop();
    335  });
    336 
    337  let wss = new NodeWebSocketHttp2Server();
    338  await wss.start();
    339  Assert.notEqual(wss.port(), null);
    340  await wss.registerMessageHandler((data, ws) => {
    341    ws.send(data);
    342  });
    343 
    344  // To create a h2 connection before the websocket one.
    345  let chan = makeChan(`https://localhost:${wss.port()}/`);
    346  await httpChannelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL);
    347 
    348  let url = `wss://localhost:${wss.port()}`;
    349  const msg = "test h2 websocket with h1 insecure proxy";
    350  let [status, res] = await channelOpenPromise(url, msg);
    351  Assert.equal(status, Cr.NS_OK);
    352  Assert.deepEqual(res, [msg]);
    353 
    354  await proxy.stop();
    355 }
    356 
    357 function ActivityObserver() {}
    358 
    359 ActivityObserver.prototype = {
    360  activites: [],
    361  observeConnectionActivity(
    362    aHost,
    363    aPort,
    364    aSSL,
    365    aHasECH,
    366    aIsHttp3,
    367    aActivityType,
    368    aActivitySubtype,
    369    aTimestamp,
    370    aExtraStringData
    371  ) {
    372    info(
    373      "*** Connection Activity 0x" +
    374        aActivityType.toString(16) +
    375        " 0x" +
    376        aActivitySubtype.toString(16) +
    377        " " +
    378        aExtraStringData +
    379        "\n"
    380    );
    381    this.activites.push({
    382      host: aHost,
    383      subType: aActivitySubtype,
    384      connInfo: aExtraStringData,
    385    });
    386  },
    387 };
    388 
    389 function checkConnectionActivities(activites, host, port) {
    390  let connections = [];
    391  for (let activity of activites) {
    392    if (
    393      activity.host != host ||
    394      activity.subType !=
    395        Ci.nsIHttpActivityDistributor.ACTIVITY_SUBTYPE_CONNECTION_CREATED
    396    ) {
    397      continue;
    398    }
    399    connections.push(activity.connInfo);
    400  }
    401 
    402  function parseConnInfoHash(str) {
    403    if (str.length < 6) {
    404      throw new Error("Invalid input string");
    405    }
    406    // Extract the 6th character (index 5)
    407    const h2Flag = str[5];
    408    // Regular expression to extract hostname and port
    409    const regex = /\[.*?\](.*?):(\d+)/;
    410    const match = str.match(regex);
    411    if (!match) {
    412      throw new Error("Can not extract hostname and port");
    413    }
    414    return {
    415      h2Flag,
    416      host: match[1],
    417      port: match[2],
    418    };
    419  }
    420 
    421  Assert.equal(connections.length, 2);
    422 
    423  const firstConn = parseConnInfoHash(connections[0]);
    424  Assert.equal(firstConn.h2Flag, ".");
    425  Assert.equal(firstConn.host, host);
    426  Assert.equal(firstConn.port, port);
    427  const fallbackConn = parseConnInfoHash(connections[1]);
    428  Assert.equal(fallbackConn.h2Flag, "X");
    429  Assert.equal(fallbackConn.host, host);
    430  Assert.equal(fallbackConn.port, port);
    431 }
    432 
    433 async function test_websocket_fallback() {
    434  let observerService = Cc[
    435    "@mozilla.org/network/http-activity-distributor;1"
    436  ].getService(Ci.nsIHttpActivityDistributor);
    437  let observer = new ActivityObserver();
    438  observerService.addObserver(observer);
    439  observerService.observeConnection = true;
    440 
    441  Services.prefs.setBoolPref("network.http.http2.websockets", true);
    442  let wss = new NodeWebSocketHttp2Server();
    443  await wss.start(0, true);
    444  registerCleanupFunction(async () => wss.stop());
    445 
    446  Assert.notEqual(wss.port(), null);
    447  await wss.registerMessageHandler((data, ws) => {
    448    ws.send(data);
    449  });
    450  let url = `wss://localhost:${wss.port()}`;
    451  await channelOpenPromise(url, "");
    452  checkConnectionActivities(observer.activites, "localhost", wss.port());
    453 }
    454 
    455 add_task(test_h1_plain_websocket_direct);
    456 add_task(test_h1_websocket_direct);
    457 add_task(test_h2_websocket_direct);
    458 add_task(test_h1_ws_with_secure_h1_proxy);
    459 add_task(test_h1_ws_with_h1_insecure_proxy);
    460 add_task(test_h1_ws_with_h2_proxy);
    461 add_task(test_h2_ws_with_h1_insecure_proxy);
    462 add_task(test_h2_ws_with_h1_secure_proxy);
    463 add_task(test_h2_ws_with_h2_proxy);
    464 add_task(test_bug_1848013);
    465 add_task(test_websocket_fallback);