beacon-handler.sjs (5023B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 const CC = Components.Constructor; 8 const BinaryInputStream = CC( 9 "@mozilla.org/binaryinputstream;1", 10 "nsIBinaryInputStream", 11 "setInputStream" 12 ); 13 14 function DEBUG(str) { 15 // dump("********** " + str + "\n"); 16 } 17 18 function setOurState(data) { 19 x = { 20 data, 21 QueryInterface(iid) { 22 return this; 23 }, 24 }; 25 x.wrappedJSObject = x; 26 setObjectState("beacon-handler", x); 27 DEBUG("our state is " + data); 28 } 29 30 function getOurState() { 31 var data; 32 getObjectState("beacon-handler", function (x) { 33 // x can be null if no one has set any state yet 34 if (x) { 35 data = x.wrappedJSObject.data; 36 } 37 }); 38 return data; 39 } 40 41 function handleRequest(request, response) { 42 DEBUG("Entered request handler"); 43 response.setHeader("Cache-Control", "no-cache", false); 44 45 function finishControlResponse(id, response) { 46 DEBUG("********* sending out the control GET response"); 47 var beacons = getOurState(); 48 let { data, mimetype } = beacons[id]; 49 DEBUG("GET was sending : " + data + "\n"); 50 DEBUG("GET was sending : " + mimetype + "\n"); 51 var result = { 52 data, 53 mimetype, 54 }; 55 response.write(JSON.stringify(result)); 56 beacons[id] = {}; 57 setOurState(beacons); 58 } 59 60 function getFinishOrWait(id, response) { 61 var beacons = getOurState() || {}; 62 const item = beacons[id]; 63 if (item && typeof item === "object" && item.data !== "") { 64 finishControlResponse(id, response); 65 } else { 66 DEBUG("GET has arrived, but POST has not, blocking response!"); 67 beacons[id] = {}; 68 beacons[id].response = response; 69 setOurState(beacons); 70 response.processAsync(); 71 } 72 } 73 74 if (request.method == "GET") { 75 DEBUG(" ------------ GET --------------- "); 76 response.setHeader("Content-Type", "application/json", false); 77 78 let searchParams = new URLSearchParams(request.queryString); 79 if (searchParams.has("getLastBeaconCors")) { 80 // Allow CORS responses of the last beacon 81 var originHeader = request.getHeader("origin"); 82 response.setHeader("Access-Control-Allow-Headers", "content-type", false); 83 response.setHeader("Access-Control-Allow-Methods", "POST, GET", false); 84 response.setHeader("Access-Control-Allow-Origin", originHeader, false); 85 response.setHeader("Access-Control-Allow-Credentials", "true", false); 86 getFinishOrWait("0", response); 87 } else if (searchParams.has("getBeacon")) { 88 let id = searchParams.get("getBeacon"); 89 getFinishOrWait(id, response); 90 } else { 91 response.setStatusLine(request.httpVersion, 400, "Bad Request"); 92 } 93 return; 94 } 95 96 if (request.method == "POST") { 97 DEBUG(" ------------ POST --------------- "); 98 var body = new BinaryInputStream(request.bodyInputStream); 99 var avail; 100 var bytes = []; 101 102 while ((avail = body.available()) > 0) { 103 Array.prototype.push.apply(bytes, body.readByteArray(avail)); 104 } 105 106 var data = ""; 107 for (var i = 0; i < bytes.length; i++) { 108 // We are only passing strings at this point. 109 if (bytes[i] < 32) { 110 continue; 111 } 112 var charcode = String.fromCharCode(bytes[i]); 113 data += charcode; 114 } 115 116 var mimetype = ""; 117 if (request.hasHeader("Content-Type")) { 118 mimetype = request.getHeader("Content-Type"); 119 } 120 121 // check to see if this is form data. 122 if (mimetype.indexOf("multipart/form-data") != -1) { 123 // trim the mime type to make testing easier. 124 mimetype = "multipart/form-data"; 125 // Extract only the form-data name. 126 127 var pattern = /; name=\"(.+)\";/; 128 data = data.split(pattern)[1]; 129 } 130 131 DEBUG("********** POST was sending : " + data + "\n"); 132 DEBUG("********** POST was sending : " + mimetype + "\n"); 133 134 response.setHeader("Content-Type", "text/plain", false); 135 response.write("ok"); 136 137 // associate the data from the beacon with the id 138 let searchParams = new URLSearchParams(request.queryString); 139 let id = searchParams.get("beaconid") || "0"; // "0" because test_beaconWithSafelistedContentType.html doesn't use id param 140 let beacons = getOurState() || {}; 141 142 const item = beacons[id]; 143 if (item && typeof item.response === "object" && item.response) { 144 DEBUG("GET is already pending, finishing!"); 145 beacons[id].data = data; 146 beacons[id].mimetype = mimetype; 147 finishControlResponse(id, item.response); 148 item.response.finish(); 149 } else { 150 DEBUG("GET has not arrived, marking it as unblocked"); 151 beacons[id] = {}; 152 beacons[id].data = data; 153 beacons[id].mimetype = mimetype; 154 setOurState(beacons); 155 } 156 157 return; 158 } 159 160 response.setStatusLine(request.httpVersion, 402, "Bad Request"); 161 }