1 /* This Source Code Form is subject to the terms of the Mozilla Public
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/. */
6 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
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",
15 "chrome://remote/content/server/WebSocketTransport.sys.mjs",
18 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
20 XPCOMUtils.defineLazyPreferenceGetter(
23 "remote.log.truncate",
27 const MAX_LOG_LENGTH = 2500;
29 export class WebSocketConnection {
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.
36 constructor(webSocket, httpdConnection) {
37 this.id = lazy.generateUUID();
39 this.httpdConnection = httpdConnection;
41 this.transport = new lazy.WebSocketTransport(webSocket);
42 this.transport.hooks = this;
43 this.transport.ready();
45 lazy.logger.debug(`${this.constructor.name} ${this.id} accepted`);
48 #log(direction, data) {
49 if (lazy.Log.isDebugLevelOrMore) {
50 function replacer(key, value) {
51 if (typeof value === "string") {
52 return lazy.truncate`${value}`;
57 let payload = JSON.stringify(
60 lazy.Log.verbose ? "\t" : null
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]`;
72 `${this.constructor.name} ${this.id} ${direction} ${payload}`
78 * Close the WebSocket connection.
81 this.transport.close();
85 * Register a new Session to forward the messages to.
87 * Needs to be implemented in the sub class.
90 throw new Error("Not implemented");
94 * Send the JSON-serializable object to the client.
96 * @param {object} data
97 * The object to be sent.
100 this.#log("<-", data);
101 this.transport.send(data);
105 * Send an error back to the client.
107 * Needs to be implemented in the sub class.
110 throw new Error("Not implemented");
114 * Send an event back to the client.
116 * Needs to be implemented in the sub class.
119 throw new Error("Not implemented");
123 * Send the result of a call to a method back to the client.
125 * Needs to be implemented in the sub class.
128 throw new Error("Not implemented");
132 return `[object ${this.constructor.name} ${this.id}]`;
138 * Called by the `transport` when the connection is closed.
140 onConnectionClose() {
141 lazy.logger.debug(`${this.constructor.name} ${this.id} closed`);
145 * Called when the socket is closed.
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();
155 * Receive a packet from the WebSocket layer.
157 * This packet is sent by a WebSocket client and is meant to execute
158 * a particular method.
160 * Needs to be implemented in the sub class.
162 * @param {object} packet
163 * JSON-serializable object sent by the client.
165 async onPacket(packet) {
166 this.#log("->", packet);