tor-browser

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

WebSocketConnection.sys.mjs (4259B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
      7 
      8 const lazy = {};
      9 
     10 ChromeUtils.defineESModuleGetters(lazy, {
     11  generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
     12  Log: "chrome://remote/content/shared/Log.sys.mjs",
     13  truncate: "chrome://remote/content/shared/Format.sys.mjs",
     14  WebSocketTransport:
     15    "chrome://remote/content/server/WebSocketTransport.sys.mjs",
     16 });
     17 
     18 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
     19 
     20 XPCOMUtils.defineLazyPreferenceGetter(
     21  lazy,
     22  "truncateLog",
     23  "remote.log.truncate",
     24  false
     25 );
     26 
     27 const MAX_LOG_LENGTH = 2500;
     28 
     29 export class WebSocketConnection {
     30  /**
     31   * @param {WebSocket} webSocket
     32   *     The WebSocket server connection to wrap.
     33   * @param {Connection} httpdConnection
     34   *     Reference to the httpd.js's connection needed for clean-up.
     35   */
     36  constructor(webSocket, httpdConnection) {
     37    this.id = lazy.generateUUID();
     38 
     39    this.httpdConnection = httpdConnection;
     40 
     41    this.transport = new lazy.WebSocketTransport(webSocket);
     42    this.transport.hooks = this;
     43    this.transport.ready();
     44 
     45    lazy.logger.debug(`${this.constructor.name} ${this.id} accepted`);
     46  }
     47 
     48  #log(direction, data) {
     49    if (lazy.Log.isDebugLevelOrMore) {
     50      function replacer(key, value) {
     51        if (typeof value === "string") {
     52          return lazy.truncate`${value}`;
     53        }
     54        return value;
     55      }
     56 
     57      let payload = JSON.stringify(
     58        data,
     59        replacer,
     60        lazy.Log.verbose ? "\t" : null
     61      );
     62 
     63      if (lazy.truncateLog && payload.length > MAX_LOG_LENGTH) {
     64        // Even if we truncate individual values, the resulting message might be
     65        // huge if we are serializing big objects with many properties or items.
     66        // Truncate the overall message to avoid issues in logs.
     67        const truncated = payload.substring(0, MAX_LOG_LENGTH);
     68        payload = `${truncated} [... truncated after ${MAX_LOG_LENGTH} characters]`;
     69      }
     70 
     71      lazy.logger.debug(
     72        `${this.constructor.name} ${this.id} ${direction} ${payload}`
     73      );
     74    }
     75  }
     76 
     77  /**
     78   * Close the WebSocket connection.
     79   */
     80  close() {
     81    this.transport.close();
     82  }
     83 
     84  /**
     85   * Register a new Session to forward the messages to.
     86   *
     87   * Needs to be implemented in the sub class.
     88   */
     89  registerSession() {
     90    throw new Error("Not implemented");
     91  }
     92 
     93  /**
     94   * Send the JSON-serializable object to the client.
     95   *
     96   * @param {object} data
     97   *     The object to be sent.
     98   */
     99  send(data) {
    100    this.#log("<-", data);
    101    this.transport.send(data);
    102  }
    103 
    104  /**
    105   * Send an error back to the client.
    106   *
    107   * Needs to be implemented in the sub class.
    108   */
    109  sendError() {
    110    throw new Error("Not implemented");
    111  }
    112 
    113  /**
    114   * Send an event back to the client.
    115   *
    116   * Needs to be implemented in the sub class.
    117   */
    118  sendEvent() {
    119    throw new Error("Not implemented");
    120  }
    121 
    122  /**
    123   * Send the result of a call to a method back to the client.
    124   *
    125   * Needs to be implemented in the sub class.
    126   */
    127  sendResult() {
    128    throw new Error("Not implemented");
    129  }
    130 
    131  toString() {
    132    return `[object ${this.constructor.name} ${this.id}]`;
    133  }
    134 
    135  // Transport hooks
    136 
    137  /**
    138   * Called by the `transport` when the connection is closed.
    139   */
    140  onConnectionClose() {
    141    lazy.logger.debug(`${this.constructor.name} ${this.id} closed`);
    142  }
    143 
    144  /**
    145   * Called when the socket is closed.
    146   */
    147  onSocketClose() {
    148    // In addition to the WebSocket transport, we also have to close the
    149    // connection used internally within httpd.js. Otherwise the server doesn't
    150    // shut down correctly, and keeps these Connection instances alive.
    151    this.httpdConnection.close();
    152  }
    153 
    154  /**
    155   * Receive a packet from the WebSocket layer.
    156   *
    157   * This packet is sent by a WebSocket client and is meant to execute
    158   * a particular method.
    159   *
    160   * Needs to be implemented in the sub class.
    161   *
    162   * @param {object} packet
    163   *     JSON-serializable object sent by the client.
    164   */
    165  async onPacket(packet) {
    166    this.#log("->", packet);
    167  }
    168 }