tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }