/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const CC = Components.Constructor; const BinaryInputStream = CC( "@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", "setInputStream" ); function DEBUG(str) { // dump("********** " + str + "\n"); } function setOurState(data) { x = { data, QueryInterface(iid) { return this; }, }; x.wrappedJSObject = x; setObjectState("beacon-handler", x); DEBUG("our state is " + data); } function getOurState() { var data; getObjectState("beacon-handler", function (x) { // x can be null if no one has set any state yet if (x) { data = x.wrappedJSObject.data; } }); return data; } function handleRequest(request, response) { DEBUG("Entered request handler"); response.setHeader("Cache-Control", "no-cache", false); function finishControlResponse(response) { DEBUG("********* sending out the control GET response"); var data = getState("beaconData"); var mimetype = getState("beaconMimetype"); DEBUG("GET was sending : " + data + "\n"); DEBUG("GET was sending : " + mimetype + "\n"); var result = { data, mimetype, }; response.write(JSON.stringify(result)); setOurState(null); } if (request.method == "GET") { DEBUG(" ------------ GET --------------- "); response.setHeader("Content-Type", "application/json", false); switch (request.queryString) { case "getLastBeaconCors": // Allow CORS responses of the last beacon var originHeader = request.getHeader("origin"); response.setHeader( "Access-Control-Allow-Headers", "content-type", false ); response.setHeader("Access-Control-Allow-Methods", "POST, GET", false); response.setHeader("Access-Control-Allow-Origin", originHeader, false); response.setHeader("Access-Control-Allow-Credentials", "true", false); // fallthrough case "getLastBeacon": var state = getOurState(); if (state === "unblocked") { finishControlResponse(response); } else { DEBUG("GET has arrived, but POST has not, blocking response!"); setOurState(response); response.processAsync(); } break; default: response.setStatusLine(request.httpVersion, 400, "Bad Request"); break; } return; } if (request.method == "POST") { DEBUG(" ------------ POST --------------- "); var body = new BinaryInputStream(request.bodyInputStream); var avail; var bytes = []; while ((avail = body.available()) > 0) { Array.prototype.push.apply(bytes, body.readByteArray(avail)); } var data = ""; for (var i = 0; i < bytes.length; i++) { // We are only passing strings at this point. if (bytes[i] < 32) { continue; } var charcode = String.fromCharCode(bytes[i]); data += charcode; } var mimetype = ""; if (request.hasHeader("Content-Type")) { mimetype = request.getHeader("Content-Type"); } // check to see if this is form data. if (mimetype.indexOf("multipart/form-data") != -1) { // trim the mime type to make testing easier. mimetype = "multipart/form-data"; // Extract only the form-data name. var pattern = /; name=\"(.+)\";/; data = data.split(pattern)[1]; } DEBUG("********** POST was sending : " + data + "\n"); DEBUG("********** POST was sending : " + mimetype + "\n"); setState("beaconData", data); setState("beaconMimetype", mimetype); response.setHeader("Content-Type", "text/plain", false); response.write("ok"); var blockedResponse = getOurState(); if (typeof blockedResponse == "object" && blockedResponse) { DEBUG("GET is already pending, finishing!"); finishControlResponse(blockedResponse); blockedResponse.finish(); } else { DEBUG("GET has not arrived, marking it as unblocked"); setOurState("unblocked"); } return; } response.setStatusLine(request.httpVersion, 402, "Bad Request"); }