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