cancellable_request.sjs (4338B)
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 } 8 if (p.indexOf(key + "=") == 0) { 9 return p.substring(key.length + 1); 10 } 11 if (!p.includes("=") && key == "") { 12 return p; 13 } 14 } 15 return false; 16 } 17 18 function push32BE(array, input) { 19 array.push(String.fromCharCode((input >> 24) & 0xff)); 20 array.push(String.fromCharCode((input >> 16) & 0xff)); 21 array.push(String.fromCharCode((input >> 8) & 0xff)); 22 array.push(String.fromCharCode(input & 0xff)); 23 } 24 25 function push32LE(array, input) { 26 array.push(String.fromCharCode(input & 0xff)); 27 array.push(String.fromCharCode((input >> 8) & 0xff)); 28 array.push(String.fromCharCode((input >> 16) & 0xff)); 29 array.push(String.fromCharCode((input >> 24) & 0xff)); 30 } 31 32 function push16LE(array, input) { 33 array.push(String.fromCharCode(input & 0xff)); 34 array.push(String.fromCharCode((input >> 8) & 0xff)); 35 } 36 37 function buildWave(samples, sample_rate) { 38 const RIFF_MAGIC = 0x52494646; 39 const WAVE_MAGIC = 0x57415645; 40 const FRMT_MAGIC = 0x666d7420; 41 const DATA_MAGIC = 0x64617461; 42 const RIFF_SIZE = 44; 43 44 var header = []; 45 push32BE(header, RIFF_MAGIC); 46 push32LE(header, RIFF_SIZE + samples.length * 2); 47 push32BE(header, WAVE_MAGIC); 48 push32BE(header, FRMT_MAGIC); 49 push32LE(header, 16); 50 push16LE(header, 1); 51 push16LE(header, 1); 52 push32LE(header, sample_rate); 53 push32LE(header, sample_rate); 54 push16LE(header, 2); 55 push16LE(header, 16); 56 push32BE(header, DATA_MAGIC); 57 push32LE(header, samples.length * 2); 58 for (var i = 0; i < samples.length; ++i) { 59 push16LE(header, samples[i], 2); 60 } 61 return header; 62 } 63 64 const CC = Components.Constructor; 65 const Timer = CC("@mozilla.org/timer;1", "nsITimer", "initWithCallback"); 66 const BinaryOutputStream = CC( 67 "@mozilla.org/binaryoutputstream;1", 68 "nsIBinaryOutputStream", 69 "setOutputStream" 70 ); 71 72 function poll(f) { 73 if (f()) { 74 return; 75 } 76 new Timer( 77 function () { 78 poll(f); 79 }, 80 100, 81 Ci.nsITimer.TYPE_ONE_SHOT 82 ); 83 } 84 85 function handleRequest(request, response) { 86 var cancel = parseQuery(request, "cancelkey"); 87 if (cancel) { 88 setState(cancel[1], "cancelled"); 89 response.setStatusLine(request.httpVersion, 200, "OK"); 90 response.write("Cancel approved!"); 91 return; 92 } 93 94 var samples = []; 95 for (var i = 0; i < 1000000; ++i) { 96 samples.push(0); 97 } 98 var bytes = buildWave(samples, 44100).join(""); 99 100 var key = parseQuery(request, "key"); 101 response.setHeader("Content-Type", "audio/x-wav"); 102 response.setHeader("Content-Length", "" + bytes.length, false); 103 104 var out = new BinaryOutputStream(response.bodyOutputStream); 105 106 var start = 0, 107 end = bytes.length - 1; 108 if (request.hasHeader("Range")) { 109 var rangeMatch = request.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/); 110 111 if (rangeMatch[1] !== undefined) { 112 start = parseInt(rangeMatch[1], 10); 113 } 114 115 if (rangeMatch[2] !== undefined) { 116 end = parseInt(rangeMatch[2], 10); 117 } 118 119 // No start given, so the end is really the count of bytes from the 120 // end of the file. 121 if (start === undefined) { 122 start = Math.max(0, bytes.length - end); 123 end = bytes.length - 1; 124 } 125 126 // start and end are inclusive 127 if (end === undefined || end >= bytes.length) { 128 end = bytes.length - 1; 129 } 130 131 if (end < start) { 132 response.setStatusLine(request.httpVersion, 200, "OK"); 133 start = 0; 134 end = bytes.length - 1; 135 } else { 136 response.setStatusLine(request.httpVersion, 206, "Partial Content"); 137 var contentRange = "bytes " + start + "-" + end + "/" + bytes.length; 138 response.setHeader("Content-Range", contentRange); 139 } 140 } 141 142 if (start > 0) { 143 // Send all requested data 144 out.write(bytes.slice(start, end + 1), end + 1 - start); 145 return; 146 } 147 148 // Write the first 1.2M of the Wave file. We know the cache size is set to 149 // 100K so this will fill the cache and and cause a "suspend" event on 150 // the loading element. 151 out.write(bytes, 1200000); 152 153 response.processAsync(); 154 // Now wait for the message to cancel this response 155 poll(function () { 156 if (getState(key[1]) != "cancelled") { 157 return false; 158 } 159 response.finish(); 160 return true; 161 }); 162 }