content/media/test/cancellable_request.sjs

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

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

mercurial