mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			168 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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/. */
 | 
						|
 | 
						|
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | 
						|
 | 
						|
const lazy = {};
 | 
						|
 | 
						|
ChromeUtils.defineESModuleGetters(lazy, {
 | 
						|
  generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
 | 
						|
  Log: "chrome://remote/content/shared/Log.sys.mjs",
 | 
						|
  truncate: "chrome://remote/content/shared/Format.sys.mjs",
 | 
						|
  WebSocketTransport:
 | 
						|
    "chrome://remote/content/server/WebSocketTransport.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
 | 
						|
 | 
						|
XPCOMUtils.defineLazyPreferenceGetter(
 | 
						|
  lazy,
 | 
						|
  "truncateLog",
 | 
						|
  "remote.log.truncate",
 | 
						|
  false
 | 
						|
);
 | 
						|
 | 
						|
const MAX_LOG_LENGTH = 2500;
 | 
						|
 | 
						|
export class WebSocketConnection {
 | 
						|
  /**
 | 
						|
   * @param {WebSocket} webSocket
 | 
						|
   *     The WebSocket server connection to wrap.
 | 
						|
   * @param {Connection} httpdConnection
 | 
						|
   *     Reference to the httpd.js's connection needed for clean-up.
 | 
						|
   */
 | 
						|
  constructor(webSocket, httpdConnection) {
 | 
						|
    this.id = lazy.generateUUID();
 | 
						|
 | 
						|
    this.httpdConnection = httpdConnection;
 | 
						|
 | 
						|
    this.transport = new lazy.WebSocketTransport(webSocket);
 | 
						|
    this.transport.hooks = this;
 | 
						|
    this.transport.ready();
 | 
						|
 | 
						|
    lazy.logger.debug(`${this.constructor.name} ${this.id} accepted`);
 | 
						|
  }
 | 
						|
 | 
						|
  #log(direction, data) {
 | 
						|
    if (lazy.Log.isDebugLevelOrMore) {
 | 
						|
      function replacer(key, value) {
 | 
						|
        if (typeof value === "string") {
 | 
						|
          return lazy.truncate`${value}`;
 | 
						|
        }
 | 
						|
        return value;
 | 
						|
      }
 | 
						|
 | 
						|
      let payload = JSON.stringify(
 | 
						|
        data,
 | 
						|
        replacer,
 | 
						|
        lazy.Log.verbose ? "\t" : null
 | 
						|
      );
 | 
						|
 | 
						|
      if (lazy.truncateLog && payload.length > MAX_LOG_LENGTH) {
 | 
						|
        // Even if we truncate individual values, the resulting message might be
 | 
						|
        // huge if we are serializing big objects with many properties or items.
 | 
						|
        // Truncate the overall message to avoid issues in logs.
 | 
						|
        const truncated = payload.substring(0, MAX_LOG_LENGTH);
 | 
						|
        payload = `${truncated} [... truncated after ${MAX_LOG_LENGTH} characters]`;
 | 
						|
      }
 | 
						|
 | 
						|
      lazy.logger.debug(
 | 
						|
        `${this.constructor.name} ${this.id} ${direction} ${payload}`
 | 
						|
      );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Close the WebSocket connection.
 | 
						|
   */
 | 
						|
  close() {
 | 
						|
    this.transport.close();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Register a new Session to forward the messages to.
 | 
						|
   *
 | 
						|
   * Needs to be implemented in the sub class.
 | 
						|
   */
 | 
						|
  registerSession() {
 | 
						|
    throw new Error("Not implemented");
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Send the JSON-serializable object to the client.
 | 
						|
   *
 | 
						|
   * @param {object} data
 | 
						|
   *     The object to be sent.
 | 
						|
   */
 | 
						|
  send(data) {
 | 
						|
    this.#log("<-", data);
 | 
						|
    this.transport.send(data);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Send an error back to the client.
 | 
						|
   *
 | 
						|
   * Needs to be implemented in the sub class.
 | 
						|
   */
 | 
						|
  sendError() {
 | 
						|
    throw new Error("Not implemented");
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Send an event back to the client.
 | 
						|
   *
 | 
						|
   * Needs to be implemented in the sub class.
 | 
						|
   */
 | 
						|
  sendEvent() {
 | 
						|
    throw new Error("Not implemented");
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Send the result of a call to a method back to the client.
 | 
						|
   *
 | 
						|
   * Needs to be implemented in the sub class.
 | 
						|
   */
 | 
						|
  sendResult() {
 | 
						|
    throw new Error("Not implemented");
 | 
						|
  }
 | 
						|
 | 
						|
  toString() {
 | 
						|
    return `[object ${this.constructor.name} ${this.id}]`;
 | 
						|
  }
 | 
						|
 | 
						|
  // Transport hooks
 | 
						|
 | 
						|
  /**
 | 
						|
   * Called by the `transport` when the connection is closed.
 | 
						|
   */
 | 
						|
  onConnectionClose() {
 | 
						|
    lazy.logger.debug(`${this.constructor.name} ${this.id} closed`);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Called when the socket is closed.
 | 
						|
   */
 | 
						|
  onSocketClose() {
 | 
						|
    // In addition to the WebSocket transport, we also have to close the
 | 
						|
    // connection used internally within httpd.js. Otherwise the server doesn't
 | 
						|
    // shut down correctly, and keeps these Connection instances alive.
 | 
						|
    this.httpdConnection.close();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Receive a packet from the WebSocket layer.
 | 
						|
   *
 | 
						|
   * This packet is sent by a WebSocket client and is meant to execute
 | 
						|
   * a particular method.
 | 
						|
   *
 | 
						|
   * Needs to be implemented in the sub class.
 | 
						|
   *
 | 
						|
   * @param {object} packet
 | 
						|
   *     JSON-serializable object sent by the client.
 | 
						|
   */
 | 
						|
  async onPacket(packet) {
 | 
						|
    this.#log("->", packet);
 | 
						|
  }
 | 
						|
}
 |