Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | const CC = Components.Constructor; |
michael@0 | 8 | const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", |
michael@0 | 9 | "nsIBinaryInputStream", |
michael@0 | 10 | "setInputStream"); |
michael@0 | 11 | |
michael@0 | 12 | function DEBUG(str) |
michael@0 | 13 | { |
michael@0 | 14 | // dump("********** " + str + "\n"); |
michael@0 | 15 | } |
michael@0 | 16 | |
michael@0 | 17 | function setOurState(data) { |
michael@0 | 18 | x = { data: data, QueryInterface: function(iid) { return this } }; |
michael@0 | 19 | x.wrappedJSObject = x; |
michael@0 | 20 | setObjectState("beacon-handler", x); |
michael@0 | 21 | DEBUG("our state is " + data); |
michael@0 | 22 | } |
michael@0 | 23 | |
michael@0 | 24 | function getOurState() { |
michael@0 | 25 | var data; |
michael@0 | 26 | getObjectState("beacon-handler", function(x) { |
michael@0 | 27 | // x can be null if no one has set any state yet |
michael@0 | 28 | if (x) { |
michael@0 | 29 | data = x.wrappedJSObject.data; |
michael@0 | 30 | } |
michael@0 | 31 | }); |
michael@0 | 32 | return data; |
michael@0 | 33 | } |
michael@0 | 34 | |
michael@0 | 35 | function handleRequest(request, response) { |
michael@0 | 36 | DEBUG("Entered request handler"); |
michael@0 | 37 | response.setHeader("Cache-Control", "no-cache", false); |
michael@0 | 38 | |
michael@0 | 39 | function finishControlResponse(response) { |
michael@0 | 40 | DEBUG("********* sending out the control GET response"); |
michael@0 | 41 | var data = getState("beaconData"); |
michael@0 | 42 | var mimetype = getState("beaconMimetype"); |
michael@0 | 43 | DEBUG("GET was sending : " + data + "\n"); |
michael@0 | 44 | DEBUG("GET was sending : " + mimetype + "\n"); |
michael@0 | 45 | var result = { |
michael@0 | 46 | "data": data, |
michael@0 | 47 | "mimetype": mimetype, |
michael@0 | 48 | }; |
michael@0 | 49 | response.write(JSON.stringify(result)); |
michael@0 | 50 | setOurState(null); |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | if (request.method == "GET") { |
michael@0 | 54 | DEBUG(" ------------ GET --------------- "); |
michael@0 | 55 | response.setHeader("Content-Type", "application/json", false); |
michael@0 | 56 | switch (request.queryString) { |
michael@0 | 57 | case "getLastBeacon": |
michael@0 | 58 | var state = getOurState(); |
michael@0 | 59 | if (state === "unblocked") { |
michael@0 | 60 | finishControlResponse(response); |
michael@0 | 61 | } else { |
michael@0 | 62 | DEBUG("GET has arrived, but POST has not, blocking response!"); |
michael@0 | 63 | setOurState(response); |
michael@0 | 64 | response.processAsync(); |
michael@0 | 65 | } |
michael@0 | 66 | break; |
michael@0 | 67 | default: |
michael@0 | 68 | response.setStatusLine(request.httpVersion, 400, "Bad Request"); |
michael@0 | 69 | break; |
michael@0 | 70 | } |
michael@0 | 71 | return; |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | if (request.method == "POST") { |
michael@0 | 75 | DEBUG(" ------------ POST --------------- "); |
michael@0 | 76 | var body = new BinaryInputStream(request.bodyInputStream); |
michael@0 | 77 | var avail; |
michael@0 | 78 | var bytes = []; |
michael@0 | 79 | |
michael@0 | 80 | while ((avail = body.available()) > 0) { |
michael@0 | 81 | Array.prototype.push.apply(bytes, body.readByteArray(avail)); |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | var data = ""; |
michael@0 | 85 | for (var i=0; i < bytes.length; i++) { |
michael@0 | 86 | // We are only passing strings at this point. |
michael@0 | 87 | if (bytes[i] < 32) continue; |
michael@0 | 88 | var charcode = String.fromCharCode(bytes[i]); |
michael@0 | 89 | data += charcode; |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | var mimetype = request.getHeader("Content-Type"); |
michael@0 | 93 | |
michael@0 | 94 | // check to see if this is form data. |
michael@0 | 95 | if (mimetype.indexOf("multipart/form-data") != -1) { |
michael@0 | 96 | |
michael@0 | 97 | // trim the mime type to make testing easier. |
michael@0 | 98 | mimetype = "multipart/form-data"; |
michael@0 | 99 | // Extract only the form-data name. |
michael@0 | 100 | |
michael@0 | 101 | var pattern = /; name=\"(.+)\";/; |
michael@0 | 102 | data = data.split(pattern)[1]; |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | DEBUG("********** POST was sending : " + data + "\n"); |
michael@0 | 106 | DEBUG("********** POST was sending : " + mimetype + "\n"); |
michael@0 | 107 | setState("beaconData", data); |
michael@0 | 108 | setState("beaconMimetype", mimetype); |
michael@0 | 109 | |
michael@0 | 110 | response.setHeader("Content-Type", "text/plain", false); |
michael@0 | 111 | response.write('ok'); |
michael@0 | 112 | |
michael@0 | 113 | var blockedResponse = getOurState(); |
michael@0 | 114 | if (typeof(blockedResponse) == "object" && blockedResponse) { |
michael@0 | 115 | DEBUG("GET is already pending, finishing!"); |
michael@0 | 116 | finishControlResponse(blockedResponse); |
michael@0 | 117 | blockedResponse.finish(); |
michael@0 | 118 | } else { |
michael@0 | 119 | DEBUG("GET has not arrived, marking it as unblocked"); |
michael@0 | 120 | setOurState("unblocked"); |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | return; |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | response.setStatusLine(request.httpVersion, 402, "Bad Request"); |
michael@0 | 127 | } |