Fri, 16 Jan 2015 04:50:19 +0100
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 }