Bug 1890689 accumulate input in LargerReceiverBlockSizeThanDesiredBuffering GTest...
[gecko.git] / remote / shared / WebSocketConnection.sys.mjs
blob57b533fffbf2c4e73381dfe55a5ab03dbe193798
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";
8 const lazy = {};
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 });
18 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
20 XPCOMUtils.defineLazyPreferenceGetter(
21   lazy,
22   "truncateLog",
23   "remote.log.truncate",
24   false
27 const MAX_LOG_LENGTH = 2500;
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();
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`);
46   }
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       }
57       let payload = JSON.stringify(
58         data,
59         replacer,
60         lazy.Log.verbose ? "\t" : null
61       );
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       }
71       lazy.logger.debug(
72         `${this.constructor.name} ${this.id} ${direction} ${payload}`
73       );
74     }
75   }
77   /**
78    * Close the WebSocket connection.
79    */
80   close() {
81     this.transport.close();
82   }
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   }
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   }
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   }
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   }
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   }
131   toString() {
132     return `[object ${this.constructor.name} ${this.id}]`;
133   }
135   // Transport hooks
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   }
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   }
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   }