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 }