Bug 1869043 add a main thread record of track audio outputs r=padenot
[gecko.git] / remote / shared / WebSocketConnection.sys.mjs
bloba1d4138b0e123009eb9b62f5588fed592ec3e94d
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 const lazy = {};
8 ChromeUtils.defineESModuleGetters(lazy, {
9   generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
10   Log: "chrome://remote/content/shared/Log.sys.mjs",
11   truncate: "chrome://remote/content/shared/Format.sys.mjs",
12   WebSocketTransport:
13     "chrome://remote/content/server/WebSocketTransport.sys.mjs",
14 });
16 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
18 const MAX_LOG_LENGTH = 2500;
20 export class WebSocketConnection {
21   /**
22    * @param {WebSocket} webSocket
23    *     The WebSocket server connection to wrap.
24    * @param {Connection} httpdConnection
25    *     Reference to the httpd.js's connection needed for clean-up.
26    */
27   constructor(webSocket, httpdConnection) {
28     this.id = lazy.generateUUID();
30     this.httpdConnection = httpdConnection;
32     this.transport = new lazy.WebSocketTransport(webSocket);
33     this.transport.hooks = this;
34     this.transport.ready();
36     lazy.logger.debug(`${this.constructor.name} ${this.id} accepted`);
37   }
39   _log(direction, data) {
40     if (lazy.Log.isDebugLevelOrMore) {
41       function replacer(key, value) {
42         if (typeof value === "string") {
43           return lazy.truncate`${value}`;
44         }
45         return value;
46       }
48       let payload = JSON.stringify(
49         data,
50         replacer,
51         lazy.Log.verbose ? "\t" : null
52       );
54       if (payload.length > MAX_LOG_LENGTH) {
55         // Even if we truncate individual values, the resulting message might be
56         // huge if we are serializing big objects with many properties or items.
57         // Truncate the overall message to avoid issues in logs.
58         const truncated = payload.substring(0, MAX_LOG_LENGTH);
59         payload = `${truncated} [... truncated after ${MAX_LOG_LENGTH} characters]`;
60       }
62       lazy.logger.debug(
63         `${this.constructor.name} ${this.id} ${direction} ${payload}`
64       );
65     }
66   }
68   /**
69    * Close the WebSocket connection.
70    */
71   close() {
72     this.transport.close();
73   }
75   /**
76    * Register a new Session to forward the messages to.
77    *
78    * Needs to be implemented in the sub class.
79    *
80    * @param {Session} session
81    *     The session to register.
82    */
83   registerSession(session) {
84     throw new Error("Not implemented");
85   }
87   /**
88    * Send the JSON-serializable object to the client.
89    *
90    * @param {object} data
91    *     The object to be sent.
92    */
93   send(data) {
94     this._log("<-", data);
95     this.transport.send(data);
96   }
98   /**
99    * Send an error back to the client.
100    *
101    * Needs to be implemented in the sub class.
102    */
103   sendError() {
104     throw new Error("Not implemented");
105   }
107   /**
108    * Send an event back to the client.
109    *
110    * Needs to be implemented in the sub class.
111    */
112   sendEvent() {
113     throw new Error("Not implemented");
114   }
116   /**
117    * Send the result of a call to a method back to the client.
118    *
119    * Needs to be implemented in the sub class.
120    */
121   sendResult() {
122     throw new Error("Not implemented");
123   }
125   toString() {
126     return `[object ${this.constructor.name} ${this.id}]`;
127   }
129   // Transport hooks
131   /**
132    * Called by the `transport` when the connection is closed.
133    */
134   onConnectionClose(status) {
135     lazy.logger.debug(`${this.constructor.name} ${this.id} closed`);
136   }
138   /**
139    * Called when the socket is closed.
140    */
141   onSocketClose() {
142     // In addition to the WebSocket transport, we also have to close the
143     // connection used internally within httpd.js. Otherwise the server doesn't
144     // shut down correctly, and keeps these Connection instances alive.
145     this.httpdConnection.close();
146   }
148   /**
149    * Receive a packet from the WebSocket layer.
150    *
151    * This packet is sent by a WebSocket client and is meant to execute
152    * a particular method.
153    *
154    * Needs to be implemented in the sub class.
155    *
156    * @param {object} packet
157    *     JSON-serializable object sent by the client.
158    */
159   async onPacket(packet) {
160     this._log("->", packet);
161   }