|
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 } |
|
14 |
|
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 } |
|
21 |
|
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 } |
|
28 |
|
29 function push16LE(array, input) { |
|
30 array.push(String.fromCharCode((input) & 0xff)); |
|
31 array.push(String.fromCharCode((input >> 8) & 0xff)); |
|
32 } |
|
33 |
|
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; |
|
40 |
|
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 } |
|
60 |
|
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"); |
|
67 |
|
68 function poll(f) { |
|
69 if (f()) { |
|
70 return; |
|
71 } |
|
72 new Timer(function() { poll(f); }, 100, Ci.nsITimer.TYPE_ONE_SHOT); |
|
73 } |
|
74 |
|
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 } |
|
84 |
|
85 var samples = []; |
|
86 for (var i = 0; i < 1000000; ++i) { |
|
87 samples.push(0); |
|
88 } |
|
89 var bytes = buildWave(samples, 44100).join(""); |
|
90 |
|
91 var key = parseQuery(request, "key"); |
|
92 response.setHeader("Content-Type", "audio/x-wav"); |
|
93 response.setHeader("Content-Length", ""+bytes.length, false); |
|
94 |
|
95 var out = new BinaryOutputStream(response.bodyOutputStream); |
|
96 |
|
97 var start = 0, end = bytes.length - 1; |
|
98 if (request.hasHeader("Range")) |
|
99 { |
|
100 var rangeMatch = request.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/); |
|
101 |
|
102 if (rangeMatch[1] !== undefined) |
|
103 start = parseInt(rangeMatch[1], 10); |
|
104 |
|
105 if (rangeMatch[2] !== undefined) |
|
106 end = parseInt(rangeMatch[2], 10); |
|
107 |
|
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 } |
|
115 |
|
116 // start and end are inclusive |
|
117 if (end === undefined || end >= bytes.length) |
|
118 end = bytes.length - 1; |
|
119 |
|
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 } |
|
133 |
|
134 if (start > 0) { |
|
135 // Send all requested data |
|
136 out.write(bytes.slice(start, end + 1), end + 1 - start); |
|
137 return; |
|
138 } |
|
139 |
|
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); |
|
144 |
|
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 } |