UNPKG

@electrum-cash/web-socket

Version:

@electrum-cash/web-socket implements the ElectrumSocket interface using web sockets.

169 lines (156 loc) 7.77 kB
import {WebSocket as $dvphU$WebSocket} from "@monsterbitar/isomorphic-ws"; import {EventEmitter as $dvphU$EventEmitter} from "eventemitter3"; import $dvphU$electrumcashdebuglogs from "@electrum-cash/debug-logs"; function $parcel$export(e, n, v, s) { Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); } var $05743633fea447d4$exports = {}; $parcel$export($05743633fea447d4$exports, "ElectrumWebSocket", () => $05743633fea447d4$export$25b4633f61498e1); // Export a default timeout value of 30 seconds. const $d801b1f9b7fc3074$export$1bddf2b96e25d075 = 30000; class $05743633fea447d4$export$25b4633f61498e1 extends (0, $dvphU$EventEmitter) { host; port; encrypted; timeout; // Declare an empty WebSocket. webSocket; // Used to disconnect after some time if initial connection is too slow. disconnectTimer; // Initialize boolean that indicates whether the onConnect function has run (initialize to false). onConnectHasRun; // Initialize event forwarding functions. eventForwarders; /** * Creates a socket configured with connection information for a given Electrum server. * * @param host Fully qualified domain name or IP address of the host * @param port Network port for the host to connect to, defaults to the standard TLS port * @param encrypted If false, uses an unencrypted connection instead of the default on TLS * @param timeout If no connection is established after `timeout` ms, the connection is terminated */ constructor(host, port = 50004, encrypted = true, timeout = (0, $d801b1f9b7fc3074$export$1bddf2b96e25d075)){ // Initialize the event emitter. super(); this.host = host; this.port = port; this.encrypted = encrypted; this.timeout = timeout; this.onConnectHasRun = false; this.eventForwarders = { disconnect: ()=>this.emit("disconnected"), wsData: (event)=>this.emit("data", `${event.data}\n`), wsError: (event)=>this.emit("error", new Error(event.error)) }; } /** * Returns a string for the host identifier for usage in debug messages. */ get hostIdentifier() { return `${this.host}:${this.port}`; } /** * Connect to host:port using the specified transport */ connect() { // Check that no existing socket exists before initiating a new connection. if (this.webSocket) throw new Error("Cannot initiate a new socket connection when an existing connection exists"); // Set a timer to force disconnect after `timeout` seconds this.disconnectTimer = setTimeout(()=>this.disconnectOnTimeout(), this.timeout); // Remove the timer if a connection is successfully established this.once("connected", this.clearDisconnectTimerOnTimeout); // Set a named connection type for logging purposes. const connectionType = this.encrypted ? "an encrypted WebSocket" : "a WebSocket"; // Log that we are trying to establish a connection. (0, $dvphU$electrumcashdebuglogs).network(`Initiating ${connectionType} connection to '${this.host}:${this.port}'.`); if (this.encrypted) // Initialize this.webSocket (rejecting self-signed certificates). // We reject self-signed certificates to match functionality of browsers. this.webSocket = new (0, $dvphU$WebSocket)(`wss://${this.host}:${this.port}`); else // Initialize this.webSocket. this.webSocket = new (0, $dvphU$WebSocket)(`ws://${this.host}:${this.port}`); // Trigger successful connection events. this.webSocket.addEventListener("open", this.onConnect.bind(this)); // Forward the encountered errors. this.webSocket.addEventListener("error", this.eventForwarders.wsError); } /** * Sets up forwarding of events related to the connection. */ onConnect() { // If the onConnect function has already run, do not execute it again. if (this.onConnectHasRun) return; // Set a named connection type for logging purposes. const connectionType = this.encrypted ? "an encrypted WebSocket" : "a WebSocket"; // Log that the connection has been established. (0, $dvphU$electrumcashdebuglogs).network(`Established ${connectionType} connection with '${this.host}:${this.port}'.`); // Forward the socket events this.webSocket.addEventListener("close", this.eventForwarders.disconnect); this.webSocket.addEventListener("message", this.eventForwarders.wsData); // Indicate that the onConnect function has run. this.onConnectHasRun = true; // Emit the connect event. this.emit("connected"); } /** * Clears the disconnect timer if it is still active. */ clearDisconnectTimerOnTimeout() { // Clear the retry timer if it is still active. if (this.disconnectTimer) clearTimeout(this.disconnectTimer); } /** * Forcibly terminate the connection. * * @throws {Error} if no connection was found */ disconnect() { // Clear the disconnect timer so that the socket does not try to disconnect again later. this.clearDisconnectTimerOnTimeout(); try { // Remove all event forwarders. this.webSocket.removeEventListener("close", this.eventForwarders.disconnect); this.webSocket.removeEventListener("message", this.eventForwarders.wsData); this.webSocket.removeEventListener("error", this.eventForwarders.wsError); // Gracefully terminate the connection. this.webSocket.close(); } catch (ignored) { // close() will throw an error if the connection has not been established yet. // We ignore this error, since no similar error gets thrown in the TLS Socket. } finally{ // Remove the stored socket regardless of any thrown errors. this.webSocket = undefined; } // Indicate that the onConnect function has not run and it has to be run again. this.onConnectHasRun = false; // Emit a disconnect event this.emit("disconnected"); } /** * Write data to the socket * * @param data Data to be written to the socket * @param callback Callback function to be called when the write has completed * * @throws {Error} if no connection was found * @returns true if the message was fully flushed to the socket, false if part of the message * is queued in the user memory */ write(data, callback) { // Throw an error if no active connection is found if (!this.webSocket) throw new Error("Cannot write to socket when there is no active connection"); // Write data to the WebSocket this.webSocket.send(data, callback); // WebSockets always fit everything in a single request, so we return true return true; } /** * Force a disconnection if no connection is established after `timeout` milliseconds. */ disconnectOnTimeout() { // Remove the connect listener. this.removeListener("connected", this.clearDisconnectTimerOnTimeout); // Emit an error event so that connect is rejected upstream. this.emit("error", new Error(`Connection to '${this.host}:${this.port}' timed out after ${this.timeout} milliseconds`)); // Forcibly disconnect to clean up the connection on timeout this.disconnect(); } // Add magic glue that makes typedoc happy so that we can have the events listed on the class. connected; disconnected; data; error; } export {$05743633fea447d4$export$25b4633f61498e1 as ElectrumWebSocket}; //# sourceMappingURL=index.mjs.map