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

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

mercurial