tor-browser

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

test_alt-data_simple.js (5787B)


      1 /**
      2 * Test for the "alternative data stream" stored withing a cache entry.
      3 *
      4 * - we load a URL with preference for an alt data (check what we get is the raw data,
      5 *   since there was nothing previously cached)
      6 * - we store the alt data along the channel (to the cache entry)
      7 * - we flush the HTTP cache
      8 * - we reload the same URL using a new channel, again prefering the alt data be loaded
      9 * - this time the alt data must arive
     10 */
     11 
     12 "use strict";
     13 
     14 const { HttpServer } = ChromeUtils.importESModule(
     15  "resource://testing-common/httpd.sys.mjs"
     16 );
     17 
     18 ChromeUtils.defineLazyGetter(this, "URL", function () {
     19  return "http://localhost:" + httpServer.identity.primaryPort + "/content";
     20 });
     21 
     22 var httpServer = null;
     23 
     24 function make_channel(url) {
     25  return NetUtil.newChannel({ uri: url, loadUsingSystemPrincipal: true });
     26 }
     27 
     28 function inChildProcess() {
     29  return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
     30 }
     31 
     32 const responseContent = "response body";
     33 const responseContent2 = "response body 2";
     34 const altContent = "!@#$%^&*()";
     35 const altContentType = "text/binary";
     36 
     37 var servedNotModified = false;
     38 var shouldPassRevalidation = true;
     39 
     40 var cache_storage = null;
     41 
     42 function contentHandler(metadata, response) {
     43  response.setHeader("Content-Type", "text/plain");
     44  response.setHeader("Cache-Control", "no-cache");
     45  response.setHeader("ETag", "test-etag1");
     46 
     47  let etag;
     48  try {
     49    etag = metadata.getHeader("If-None-Match");
     50  } catch (ex) {
     51    etag = "";
     52  }
     53 
     54  if (etag == "test-etag1" && shouldPassRevalidation) {
     55    response.setStatusLine(metadata.httpVersion, 304, "Not Modified");
     56    servedNotModified = true;
     57  } else {
     58    var content = shouldPassRevalidation ? responseContent : responseContent2;
     59    response.bodyOutputStream.write(content, content.length);
     60  }
     61 }
     62 
     63 function check_has_alt_data_in_index(aHasAltData, callback) {
     64  if (inChildProcess()) {
     65    callback();
     66    return;
     67  }
     68 
     69  syncWithCacheIOThread(() => {
     70    var hasAltData = {};
     71    cache_storage.getCacheIndexEntryAttrs(createURI(URL), "", hasAltData, {});
     72    Assert.equal(hasAltData.value, aHasAltData);
     73    callback();
     74  }, true);
     75 }
     76 
     77 function run_test() {
     78  do_get_profile();
     79  httpServer = new HttpServer();
     80  httpServer.registerPathHandler("/content", contentHandler);
     81  httpServer.start(-1);
     82  do_test_pending();
     83 
     84  if (!inChildProcess()) {
     85    cache_storage = getCacheStorage("disk");
     86    wait_for_cache_index(asyncOpen);
     87  } else {
     88    asyncOpen();
     89  }
     90 }
     91 
     92 function asyncOpen() {
     93  var chan = make_channel(URL);
     94 
     95  var cc = chan.QueryInterface(Ci.nsICacheInfoChannel);
     96  cc.preferAlternativeDataType(
     97    altContentType,
     98    "",
     99    Ci.nsICacheInfoChannel.ASYNC
    100  );
    101 
    102  chan.asyncOpen(new ChannelListener(readServerContent, null));
    103 }
    104 
    105 function readServerContent(request, buffer) {
    106  var cc = request.QueryInterface(Ci.nsICacheInfoChannel);
    107 
    108  Assert.equal(buffer, responseContent);
    109  Assert.equal(cc.alternativeDataType, "");
    110  check_has_alt_data_in_index(false, () => {
    111    executeSoon(() => {
    112      var os = cc.openAlternativeOutputStream(
    113        altContentType,
    114        altContent.length
    115      );
    116      os.write(altContent, altContent.length);
    117      os.close();
    118 
    119      executeSoon(flushAndOpenAltChannel);
    120    });
    121  });
    122 }
    123 
    124 // needs to be rooted
    125 var cacheFlushObserver = (cacheFlushObserver = {
    126  observe() {
    127    cacheFlushObserver = null;
    128    openAltChannel();
    129  },
    130 });
    131 
    132 function flushAndOpenAltChannel() {
    133  // We need to do a GC pass to ensure the cache entry has been freed.
    134  gc();
    135  if (!inChildProcess()) {
    136    Services.cache2
    137      .QueryInterface(Ci.nsICacheTesting)
    138      .flush(cacheFlushObserver);
    139  } else {
    140    do_send_remote_message("flush");
    141    do_await_remote_message("flushed").then(() => {
    142      openAltChannel();
    143    });
    144  }
    145 }
    146 
    147 function openAltChannel() {
    148  var chan = make_channel(URL);
    149  var cc = chan.QueryInterface(Ci.nsICacheInfoChannel);
    150  cc.preferAlternativeDataType(
    151    "dummy1",
    152    "text/javascript",
    153    Ci.nsICacheInfoChannel.ASYNC
    154  );
    155  cc.preferAlternativeDataType(
    156    altContentType,
    157    "text/plain",
    158    Ci.nsICacheInfoChannel.ASYNC
    159  );
    160  cc.preferAlternativeDataType("dummy2", "", Ci.nsICacheInfoChannel.ASYNC);
    161 
    162  chan.asyncOpen(new ChannelListener(readAltContent, null));
    163 }
    164 
    165 function readAltContent(request, buffer) {
    166  var cc = request.QueryInterface(Ci.nsICacheInfoChannel);
    167 
    168  Assert.equal(servedNotModified, true);
    169  Assert.equal(cc.alternativeDataType, altContentType);
    170  Assert.equal(buffer, altContent);
    171  check_has_alt_data_in_index(true, () => {
    172    cc.getOriginalInputStream({
    173      onInputStreamReady(aInputStream) {
    174        executeSoon(() => readOriginalInputStream(aInputStream));
    175      },
    176    });
    177  });
    178 }
    179 
    180 function readOriginalInputStream(aInputStream) {
    181  // We expect the async stream length to match the expected content.
    182  // If the test times out, it's probably because of this.
    183  try {
    184    let originalData = read_stream(aInputStream, responseContent.length);
    185    Assert.equal(originalData, responseContent);
    186    requestAgain();
    187  } catch (e) {
    188    equal(e.result, Cr.NS_BASE_STREAM_WOULD_BLOCK);
    189    executeSoon(() => readOriginalInputStream(aInputStream));
    190  }
    191 }
    192 
    193 function requestAgain() {
    194  shouldPassRevalidation = false;
    195  var chan = make_channel(URL);
    196  var cc = chan.QueryInterface(Ci.nsICacheInfoChannel);
    197  cc.preferAlternativeDataType(
    198    altContentType,
    199    "",
    200    Ci.nsICacheInfoChannel.ASYNC
    201  );
    202  chan.asyncOpen(new ChannelListener(readEmptyAltContent, null));
    203 }
    204 
    205 function readEmptyAltContent(request, buffer) {
    206  var cc = request.QueryInterface(Ci.nsICacheInfoChannel);
    207 
    208  // the cache is overwrite and the alt-data is reset
    209  Assert.equal(cc.alternativeDataType, "");
    210  Assert.equal(buffer, responseContent2);
    211  check_has_alt_data_in_index(false, () => httpServer.stop(do_test_finished));
    212 }