| |
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 } |