tor-browser

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

test_cache-entry-write-handle.js (4831B)


      1 "use strict";
      2 
      3 const { HttpServer } = ChromeUtils.importESModule(
      4  "resource://testing-common/httpd.sys.mjs"
      5 );
      6 
      7 const { XPCShellContentUtils } = ChromeUtils.importESModule(
      8  "resource://testing-common/XPCShellContentUtils.sys.mjs"
      9 );
     10 
     11 XPCShellContentUtils.ensureInitialized(this);
     12 
     13 const responseContent = "response body";
     14 
     15 // NOTE: This is executed both on the parent process and the content process.
     16 //       Some variables are re-defined.
     17 async function testTask(port, path, responseContent, getHandleOnStopRequest) {
     18  const { NetUtil } = ChromeUtils.importESModule(
     19    "resource://gre/modules/NetUtil.sys.mjs"
     20  );
     21 
     22  function makeChannel(url) {
     23    return NetUtil.newChannel({ uri: url, loadUsingSystemPrincipal: true });
     24  }
     25 
     26  const FILE_URL = "http://localhost:" + port + path;
     27 
     28  const altContent = "altData";
     29  const altContentType = "text/binary";
     30 
     31  const nonAltChan = makeChannel(FILE_URL);
     32  nonAltChan
     33    .QueryInterface(Ci.nsICacheInfoChannel)
     34    .preferAlternativeDataType(
     35      altContentType,
     36      "",
     37      Ci.nsICacheInfoChannel.ASYNC
     38    );
     39 
     40  function ChannelListener(callback) {
     41    this._callback = callback;
     42    this._buffer = "";
     43  }
     44  ChannelListener.prototype = {
     45    QueryInterface: ChromeUtils.generateQI([
     46      "nsIStreamListener",
     47      "nsIRequestObserver",
     48    ]),
     49 
     50    onStartRequest(_request) {},
     51 
     52    onDataAvailable(request, stream, offset, count) {
     53      const bi = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
     54        Ci.nsIBinaryInputStream
     55      );
     56      bi.setInputStream(stream);
     57      while (count > 0) {
     58        const bytes = bi.readByteArray(Math.min(65535, count));
     59        this._buffer += String.fromCharCode.apply(null, bytes);
     60        count -= bytes.length;
     61      }
     62    },
     63 
     64    onStopRequest(request, _status) {
     65      this._callback(request, this._buffer);
     66    },
     67  };
     68 
     69  const { promise: handleOrCcPromise, resolve: handleOrCcResolve } =
     70    Promise.withResolvers();
     71 
     72  nonAltChan.asyncOpen(
     73    new ChannelListener((request, buffer) => {
     74      const cc = request.QueryInterface(Ci.nsICacheInfoChannel);
     75 
     76      Assert.equal(buffer, responseContent);
     77      Assert.equal(cc.alternativeDataType, "");
     78 
     79      if (getHandleOnStopRequest) {
     80        handleOrCcResolve(cc.getCacheEntryWriteHandle());
     81      } else {
     82        handleOrCcResolve(cc);
     83      }
     84    })
     85  );
     86 
     87  let handle;
     88  if (getHandleOnStopRequest) {
     89    handle = await handleOrCcPromise;
     90  } else {
     91    const cc = await handleOrCcPromise;
     92    // In nsHttpChannel's case, this is after clearing the mCacheEntry field,
     93    // and this should fallback to mAltDataCacheEntry field.
     94    handle = cc.getCacheEntryWriteHandle();
     95  }
     96 
     97  const os = handle.openAlternativeOutputStream(
     98    altContentType,
     99    altContent.length
    100  );
    101  os.write(altContent, altContent.length);
    102  os.close();
    103 
    104  const altChan = makeChannel(FILE_URL);
    105  altChan
    106    .QueryInterface(Ci.nsICacheInfoChannel)
    107    .preferAlternativeDataType(
    108      altContentType,
    109      "",
    110      Ci.nsICacheInfoChannel.ASYNC
    111    );
    112 
    113  const { promise: altDataPromise, resolve: altDataResolve } =
    114    Promise.withResolvers();
    115 
    116  altChan.asyncOpen(
    117    new ChannelListener((request, buffer) => {
    118      const cc = request.QueryInterface(Ci.nsICacheInfoChannel);
    119 
    120      Assert.equal(buffer, altContent);
    121      Assert.equal(cc.alternativeDataType, altContentType);
    122 
    123      altDataResolve();
    124    })
    125  );
    126 
    127  await altDataPromise;
    128 }
    129 
    130 let httpServer;
    131 
    132 add_setup(async function setup() {
    133  httpServer = new HttpServer();
    134  httpServer.registerPathHandler("/page", (metadata, response) => {
    135    response.setHeader("Content-Type", "text/plain");
    136    response.setHeader("Cache-Control", "max-age=86400");
    137 
    138    response.bodyOutputStream.write("", 0);
    139  });
    140  for (let i = 1; i <= 4; i++) {
    141    httpServer.registerPathHandler(`/content${i}`, (metadata, response) => {
    142      response.setHeader("Content-Type", "text/plain");
    143      response.setHeader("Cache-Control", "max-age=86400");
    144 
    145      response.bodyOutputStream.write(responseContent, responseContent.length);
    146    });
    147  }
    148  httpServer.start(-1);
    149 
    150  registerCleanupFunction(async () => {
    151    await new Promise(resolve => httpServer.stop(resolve));
    152  });
    153 });
    154 
    155 add_task(async function test_CacheEntryWriteHandle_ParentProcess() {
    156  const port = httpServer.identity.primaryPort;
    157 
    158  testTask(port, "/content1", responseContent, true);
    159  testTask(port, "/content2", responseContent, false);
    160 });
    161 
    162 add_task(async function test_CacheEntryWriteHandle_ContentProcess() {
    163  const port = httpServer.identity.primaryPort;
    164  const PAGE_URL = "http://localhost:" + port + "/page";
    165 
    166  const page = await XPCShellContentUtils.loadContentPage(PAGE_URL, {
    167    remote: true,
    168  });
    169 
    170  await page.spawn([port, "/content3", responseContent, true], testTask);
    171  await page.spawn([port, "/content4", responseContent, false], testTask);
    172 });