michael@0: function parseQuery(request, key) { michael@0: var params = request.queryString.split('&'); michael@0: for (var j = 0; j < params.length; ++j) { michael@0: var p = params[j]; michael@0: if (p == key) michael@0: return true; michael@0: if (p.indexOf(key + "=") == 0) michael@0: return p.substring(key.length + 1); michael@0: if (p.indexOf("=") < 0 && key == "") michael@0: return p; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: function push32BE(array, input) { michael@0: array.push(String.fromCharCode((input >> 24) & 0xff)); michael@0: array.push(String.fromCharCode((input >> 16) & 0xff)); michael@0: array.push(String.fromCharCode((input >> 8) & 0xff)); michael@0: array.push(String.fromCharCode((input) & 0xff)); michael@0: } michael@0: michael@0: function push32LE(array, input) { michael@0: array.push(String.fromCharCode((input) & 0xff)); michael@0: array.push(String.fromCharCode((input >> 8) & 0xff)); michael@0: array.push(String.fromCharCode((input >> 16) & 0xff)); michael@0: array.push(String.fromCharCode((input >> 24) & 0xff)); michael@0: } michael@0: michael@0: function push16LE(array, input) { michael@0: array.push(String.fromCharCode((input) & 0xff)); michael@0: array.push(String.fromCharCode((input >> 8) & 0xff)); michael@0: } michael@0: michael@0: function buildWave(samples, sample_rate) { michael@0: const RIFF_MAGIC = 0x52494646; michael@0: const WAVE_MAGIC = 0x57415645; michael@0: const FRMT_MAGIC = 0x666d7420; michael@0: const DATA_MAGIC = 0x64617461; michael@0: const RIFF_SIZE = 44; michael@0: michael@0: var header = []; michael@0: push32BE(header, RIFF_MAGIC); michael@0: push32LE(header, RIFF_SIZE + samples.length * 2); michael@0: push32BE(header, WAVE_MAGIC); michael@0: push32BE(header, FRMT_MAGIC); michael@0: push32LE(header, 16); michael@0: push16LE(header, 1); michael@0: push16LE(header, 1); michael@0: push32LE(header, sample_rate); michael@0: push32LE(header, sample_rate); michael@0: push16LE(header, 2); michael@0: push16LE(header, 16); michael@0: push32BE(header, DATA_MAGIC); michael@0: push32LE(header, samples.length * 2); michael@0: for (var i = 0; i < samples.length; ++i) { michael@0: push16LE(header, samples[i], 2); michael@0: } michael@0: return header; michael@0: } michael@0: michael@0: const Ci = Components.interfaces; michael@0: const CC = Components.Constructor; michael@0: const Timer = CC("@mozilla.org/timer;1", "nsITimer", "initWithCallback"); michael@0: const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", michael@0: "nsIBinaryOutputStream", michael@0: "setOutputStream"); michael@0: michael@0: function poll(f) { michael@0: if (f()) { michael@0: return; michael@0: } michael@0: new Timer(function() { poll(f); }, 100, Ci.nsITimer.TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: function handleRequest(request, response) michael@0: { michael@0: var cancel = parseQuery(request, "cancelkey"); michael@0: if (cancel) { michael@0: setState(cancel[1], "cancelled"); michael@0: response.setStatusLine(request.httpVersion, 200, "OK"); michael@0: response.write("Cancel approved!"); michael@0: return; michael@0: } michael@0: michael@0: var samples = []; michael@0: for (var i = 0; i < 1000000; ++i) { michael@0: samples.push(0); michael@0: } michael@0: var bytes = buildWave(samples, 44100).join(""); michael@0: michael@0: var key = parseQuery(request, "key"); michael@0: response.setHeader("Content-Type", "audio/x-wav"); michael@0: response.setHeader("Content-Length", ""+bytes.length, false); michael@0: michael@0: var out = new BinaryOutputStream(response.bodyOutputStream); michael@0: michael@0: var start = 0, end = bytes.length - 1; michael@0: if (request.hasHeader("Range")) michael@0: { michael@0: var rangeMatch = request.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/); michael@0: michael@0: if (rangeMatch[1] !== undefined) michael@0: start = parseInt(rangeMatch[1], 10); michael@0: michael@0: if (rangeMatch[2] !== undefined) michael@0: end = parseInt(rangeMatch[2], 10); michael@0: michael@0: // No start given, so the end is really the count of bytes from the michael@0: // end of the file. michael@0: if (start === undefined) michael@0: { michael@0: start = Math.max(0, bytes.length - end); michael@0: end = bytes.length - 1; michael@0: } michael@0: michael@0: // start and end are inclusive michael@0: if (end === undefined || end >= bytes.length) michael@0: end = bytes.length - 1; michael@0: michael@0: if (end < start) michael@0: { michael@0: response.setStatusLine(request.httpVersion, 200, "OK"); michael@0: start = 0; michael@0: end = bytes.length - 1; michael@0: } michael@0: else michael@0: { michael@0: response.setStatusLine(request.httpVersion, 206, "Partial Content"); michael@0: var contentRange = "bytes " + start + "-" + end + "/" + bytes.length; michael@0: response.setHeader("Content-Range", contentRange); michael@0: } michael@0: } michael@0: michael@0: if (start > 0) { michael@0: // Send all requested data michael@0: out.write(bytes.slice(start, end + 1), end + 1 - start); michael@0: return; michael@0: } michael@0: michael@0: // Write the first 1.2M of the Wave file. We know the cache size is set to michael@0: // 100K so this will fill the cache and and cause a "suspend" event on michael@0: // the loading element. michael@0: out.write(bytes, 1200000); michael@0: michael@0: response.processAsync(); michael@0: // Now wait for the message to cancel this response michael@0: poll(function() { michael@0: if (getState(key[1]) != "cancelled") { michael@0: return false; michael@0: } michael@0: response.finish(); michael@0: return true; michael@0: }); michael@0: }