tor-browser

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

IPProtectionUsage.sys.mjs (3141B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /**
      6 * Service Class to observe and record IP protection usage.
      7 *
      8 * When started it will observe all HTTP requests and record the
      9 * transfer sizes of requests and responses that are proxied through
     10 * the IP protection proxy.
     11 *
     12 * It should be started when the IP protection proxy is active.
     13 * It should be stopped when we know all proxy requests have been completed.
     14 *
     15 * It will record all Proxied Requests that match the isolation keys.
     16 * So after a connection is established, the isolation key should be added.
     17 */
     18 export class IPProtectionUsage {
     19  constructor() {
     20    ChromeUtils.generateQI([Ci.nsIObserver]);
     21  }
     22 
     23  start() {
     24    if (this.#active) {
     25      return;
     26    }
     27    Services.obs.addObserver(this, "http-on-stop-request");
     28    this.#active = true;
     29  }
     30  stop() {
     31    if (!this.#active) {
     32      return;
     33    }
     34    this.#active = false;
     35    this.#isolationKeys.clear();
     36    Services.obs.removeObserver(this, "http-on-stop-request");
     37  }
     38 
     39  addIsolationKey(key) {
     40    if (typeof key !== "string" || !key) {
     41      throw new Error("Isolation key must be a non-empty string");
     42    }
     43    this.#isolationKeys.add(key);
     44  }
     45 
     46  observe(subject, topic) {
     47    if (topic != "http-on-stop-request") {
     48      return;
     49    }
     50    try {
     51      const chan = subject.QueryInterface(Ci.nsIHttpChannel);
     52      if (this.shouldCountChannel(chan)) {
     53        IPProtectionUsage.countChannel(chan);
     54      }
     55    } catch (err) {
     56      // If the channel is not an nsIHttpChannel
     57    }
     58  }
     59  /**
     60   * Checks if a channel should be counted.
     61   *
     62   * @param {nsIHttpChannel} channel
     63   * @returns {boolean} true if the channel should be counted.
     64   */
     65  shouldCountChannel(channel) {
     66    try {
     67      const proxiedChannel = channel.QueryInterface(Ci.nsIProxiedChannel);
     68      const proxyInfo = proxiedChannel.proxyInfo;
     69      if (!proxyInfo) {
     70        // No proxy info, nothing to do.
     71        return false;
     72      }
     73      const isolationKey = proxyInfo.connectionIsolationKey;
     74      return isolationKey && this.#isolationKeys.has(isolationKey);
     75    } catch (err) {
     76      // If the channel is not an nsIHttpChannel or nsIProxiedChannel, as it's irrelevant
     77      // for this class.
     78    }
     79    return false;
     80  }
     81 
     82  /**
     83   * Checks a completed channel and records the transfer sizes to glean.
     84   *
     85   * @param {nsIHttpChannel} chan - A completed Channel to check.
     86   */
     87  static countChannel(chan) {
     88    try {
     89      const cacheInfo = chan.QueryInterface(Ci.nsICacheInfoChannel);
     90      if (cacheInfo.isFromCache()) {
     91        return;
     92      }
     93    } catch (_) {
     94      /* not all channels support it */
     95    }
     96 
     97    if (chan.transferSize > 0) {
     98      Glean.ipprotection.usageRx.accumulate(chan.transferSize);
     99    }
    100    if (chan.requestSize > 0) {
    101      Glean.ipprotection.usageTx.accumulate(chan.requestSize);
    102    }
    103  }
    104 
    105  #active = false;
    106  #isolationKeys = new Set();
    107 }
    108 
    109 IPProtectionUsage.prototype.QueryInterface = ChromeUtils.generateQI([
    110  Ci.nsIObserver,
    111 ]);