tor-browser

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

cancellable_request.sjs (4338B)


      1 function parseQuery(request, key) {
      2   var params = request.queryString.split("&");
      3   for (var j = 0; j < params.length; ++j) {
      4     var p = params[j];
      5     if (p == key) {
      6       return true;
      7     }
      8     if (p.indexOf(key + "=") == 0) {
      9       return p.substring(key.length + 1);
     10     }
     11     if (!p.includes("=") && key == "") {
     12       return p;
     13     }
     14   }
     15   return false;
     16 }
     17 
     18 function push32BE(array, input) {
     19   array.push(String.fromCharCode((input >> 24) & 0xff));
     20   array.push(String.fromCharCode((input >> 16) & 0xff));
     21   array.push(String.fromCharCode((input >> 8) & 0xff));
     22   array.push(String.fromCharCode(input & 0xff));
     23 }
     24 
     25 function push32LE(array, input) {
     26   array.push(String.fromCharCode(input & 0xff));
     27   array.push(String.fromCharCode((input >> 8) & 0xff));
     28   array.push(String.fromCharCode((input >> 16) & 0xff));
     29   array.push(String.fromCharCode((input >> 24) & 0xff));
     30 }
     31 
     32 function push16LE(array, input) {
     33   array.push(String.fromCharCode(input & 0xff));
     34   array.push(String.fromCharCode((input >> 8) & 0xff));
     35 }
     36 
     37 function buildWave(samples, sample_rate) {
     38   const RIFF_MAGIC = 0x52494646;
     39   const WAVE_MAGIC = 0x57415645;
     40   const FRMT_MAGIC = 0x666d7420;
     41   const DATA_MAGIC = 0x64617461;
     42   const RIFF_SIZE = 44;
     43 
     44   var header = [];
     45   push32BE(header, RIFF_MAGIC);
     46   push32LE(header, RIFF_SIZE + samples.length * 2);
     47   push32BE(header, WAVE_MAGIC);
     48   push32BE(header, FRMT_MAGIC);
     49   push32LE(header, 16);
     50   push16LE(header, 1);
     51   push16LE(header, 1);
     52   push32LE(header, sample_rate);
     53   push32LE(header, sample_rate);
     54   push16LE(header, 2);
     55   push16LE(header, 16);
     56   push32BE(header, DATA_MAGIC);
     57   push32LE(header, samples.length * 2);
     58   for (var i = 0; i < samples.length; ++i) {
     59     push16LE(header, samples[i], 2);
     60   }
     61   return header;
     62 }
     63 
     64 const CC = Components.Constructor;
     65 const Timer = CC("@mozilla.org/timer;1", "nsITimer", "initWithCallback");
     66 const BinaryOutputStream = CC(
     67   "@mozilla.org/binaryoutputstream;1",
     68   "nsIBinaryOutputStream",
     69   "setOutputStream"
     70 );
     71 
     72 function poll(f) {
     73   if (f()) {
     74     return;
     75   }
     76   new Timer(
     77     function () {
     78       poll(f);
     79     },
     80     100,
     81     Ci.nsITimer.TYPE_ONE_SHOT
     82   );
     83 }
     84 
     85 function handleRequest(request, response) {
     86   var cancel = parseQuery(request, "cancelkey");
     87   if (cancel) {
     88     setState(cancel[1], "cancelled");
     89     response.setStatusLine(request.httpVersion, 200, "OK");
     90     response.write("Cancel approved!");
     91     return;
     92   }
     93 
     94   var samples = [];
     95   for (var i = 0; i < 1000000; ++i) {
     96     samples.push(0);
     97   }
     98   var bytes = buildWave(samples, 44100).join("");
     99 
    100   var key = parseQuery(request, "key");
    101   response.setHeader("Content-Type", "audio/x-wav");
    102   response.setHeader("Content-Length", "" + bytes.length, false);
    103 
    104   var out = new BinaryOutputStream(response.bodyOutputStream);
    105 
    106   var start = 0,
    107     end = bytes.length - 1;
    108   if (request.hasHeader("Range")) {
    109     var rangeMatch = request.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/);
    110 
    111     if (rangeMatch[1] !== undefined) {
    112       start = parseInt(rangeMatch[1], 10);
    113     }
    114 
    115     if (rangeMatch[2] !== undefined) {
    116       end = parseInt(rangeMatch[2], 10);
    117     }
    118 
    119     // No start given, so the end is really the count of bytes from the
    120     // end of the file.
    121     if (start === undefined) {
    122       start = Math.max(0, bytes.length - end);
    123       end = bytes.length - 1;
    124     }
    125 
    126     // start and end are inclusive
    127     if (end === undefined || end >= bytes.length) {
    128       end = bytes.length - 1;
    129     }
    130 
    131     if (end < start) {
    132       response.setStatusLine(request.httpVersion, 200, "OK");
    133       start = 0;
    134       end = bytes.length - 1;
    135     } else {
    136       response.setStatusLine(request.httpVersion, 206, "Partial Content");
    137       var contentRange = "bytes " + start + "-" + end + "/" + bytes.length;
    138       response.setHeader("Content-Range", contentRange);
    139     }
    140   }
    141 
    142   if (start > 0) {
    143     // Send all requested data
    144     out.write(bytes.slice(start, end + 1), end + 1 - start);
    145     return;
    146   }
    147 
    148   // Write the first 1.2M of the Wave file. We know the cache size is set to
    149   // 100K so this will fill the cache and and cause a "suspend" event on
    150   // the loading element.
    151   out.write(bytes, 1200000);
    152 
    153   response.processAsync();
    154   // Now wait for the message to cancel this response
    155   poll(function () {
    156     if (getState(key[1]) != "cancelled") {
    157       return false;
    158     }
    159     response.finish();
    160     return true;
    161   });
    162 }