UNPKG

@electrum-cash/web-socket

Version:

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

1 lines 11.2 kB
{"mappings":"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","sources":["source/index.ts","source/web.ts","source/constants.ts"],"sourcesContent":["export * from './web.ts';\n","import { WebSocket } from '@monsterbitar/isomorphic-ws';\nimport { EventEmitter } from 'eventemitter3';\nimport debug from '@electrum-cash/debug-logs';\nimport { defaultTimeout } from './constants';\n\nimport type { MessageEvent, ErrorEvent } from '@monsterbitar/isomorphic-ws';\nimport type { ElectrumSocket, ElectrumSocketEvents } from '@electrum-cash/network';\n\n/**\n * Web Socket used when communicating with Electrum servers.\n */\nexport class ElectrumWebSocket extends EventEmitter<ElectrumSocketEvents> implements ElectrumSocket\n{\n\t// Declare an empty WebSocket.\n\tprivate webSocket: WebSocket;\n\n\t// Used to disconnect after some time if initial connection is too slow.\n\tprivate disconnectTimer?: number;\n\n\t// Initialize boolean that indicates whether the onConnect function has run (initialize to false).\n\tprivate onConnectHasRun = false;\n\n\t// Initialize event forwarding functions.\n\tprivate eventForwarders =\n\t{\n\t\tdisconnect: () => this.emit('disconnected'),\n\t\twsData: (event: MessageEvent) => this.emit('data', `${event.data}\\n`),\n\t\twsError: (event: ErrorEvent) => this.emit('error', new Error(event.error)),\n\t};\n\n\t/**\n\t * Creates a socket configured with connection information for a given Electrum server.\n\t *\n\t * @param host Fully qualified domain name or IP address of the host\n\t * @param port Network port for the host to connect to, defaults to the standard TLS port\n\t * @param encrypted If false, uses an unencrypted connection instead of the default on TLS\n\t * @param timeout If no connection is established after `timeout` ms, the connection is terminated\n\t */\n\tpublic constructor\n\t(\n\t\tpublic host: string,\n\t\tpublic port: number = 50004,\n\t\tpublic encrypted: boolean = true,\n\t\tpublic timeout: number = defaultTimeout,\n\t)\n\t{\n\t\t// Initialize the event emitter.\n\t\tsuper();\n\t}\n\n\t/**\n\t * Returns a string for the host identifier for usage in debug messages.\n\t */\n\tget hostIdentifier(): string\n\t{\n\t\treturn `${this.host}:${this.port}`;\n\t}\n\n\t/**\n\t * Connect to host:port using the specified transport\n\t */\n\tconnect(): void\n\t{\n\t\t// Check that no existing socket exists before initiating a new connection.\n\t\tif(this.webSocket)\n\t\t{\n\t\t\tthrow(new Error('Cannot initiate a new socket connection when an existing connection exists'));\n\t\t}\n\n\t\t// Set a timer to force disconnect after `timeout` seconds\n\t\tthis.disconnectTimer = setTimeout(() => this.disconnectOnTimeout(), this.timeout) as unknown as number;\n\n\t\t// Remove the timer if a connection is successfully established\n\t\tthis.once('connected', this.clearDisconnectTimerOnTimeout);\n\n\t\t// Set a named connection type for logging purposes.\n\t\tconst connectionType = (this.encrypted ? 'an encrypted WebSocket' : 'a WebSocket');\n\n\t\t// Log that we are trying to establish a connection.\n\t\tdebug.network(`Initiating ${connectionType} connection to '${this.host}:${this.port}'.`);\n\n\t\tif(this.encrypted)\n\t\t{\n\t\t\t// Initialize this.webSocket (rejecting self-signed certificates).\n\t\t\t// We reject self-signed certificates to match functionality of browsers.\n\t\t\tthis.webSocket = new WebSocket(`wss://${this.host}:${this.port}`);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Initialize this.webSocket.\n\t\t\tthis.webSocket = new WebSocket(`ws://${this.host}:${this.port}`);\n\t\t}\n\n\t\t// Trigger successful connection events.\n\t\tthis.webSocket.addEventListener('open', this.onConnect.bind(this));\n\n\t\t// Forward the encountered errors.\n\t\tthis.webSocket.addEventListener('error', this.eventForwarders.wsError);\n\t}\n\n\t/**\n\t * Sets up forwarding of events related to the connection.\n\t */\n\tprivate onConnect(): void\n\t{\n\t\t// If the onConnect function has already run, do not execute it again.\n\t\tif(this.onConnectHasRun) return;\n\n\t\t// Set a named connection type for logging purposes.\n\t\tconst connectionType = (this.encrypted ? 'an encrypted WebSocket' : 'a WebSocket');\n\n\t\t// Log that the connection has been established.\n\t\tdebug.network(`Established ${connectionType} connection with '${this.host}:${this.port}'.`);\n\n\t\t// Forward the socket events\n\t\tthis.webSocket.addEventListener('close', this.eventForwarders.disconnect);\n\t\tthis.webSocket.addEventListener('message', this.eventForwarders.wsData);\n\n\t\t// Indicate that the onConnect function has run.\n\t\tthis.onConnectHasRun = true;\n\n\t\t// Emit the connect event.\n\t\tthis.emit('connected');\n\t}\n\n\t/**\n\t * Clears the disconnect timer if it is still active.\n\t */\n\tprivate clearDisconnectTimerOnTimeout(): void\n\t{\n\t\t// Clear the retry timer if it is still active.\n\t\tif(this.disconnectTimer)\n\t\t{\n\t\t\tclearTimeout(this.disconnectTimer);\n\t\t}\n\t}\n\n\t/**\n\t * Forcibly terminate the connection.\n\t *\n\t * @throws {Error} if no connection was found\n\t */\n\tpublic disconnect(): void\n\t{\n\t\t// Clear the disconnect timer so that the socket does not try to disconnect again later.\n\t\tthis.clearDisconnectTimerOnTimeout();\n\n\t\ttry\n\t\t{\n\t\t\t// Remove all event forwarders.\n\t\t\tthis.webSocket.removeEventListener('close', this.eventForwarders.disconnect);\n\t\t\tthis.webSocket.removeEventListener('message', this.eventForwarders.wsData);\n\t\t\tthis.webSocket.removeEventListener('error', this.eventForwarders.wsError);\n\n\t\t\t// Gracefully terminate the connection.\n\t\t\tthis.webSocket.close();\n\t\t}\n\t\tcatch(ignored)\n\t\t{\n\t\t\t// close() will throw an error if the connection has not been established yet.\n\t\t\t// We ignore this error, since no similar error gets thrown in the TLS Socket.\n\t\t}\n\t\tfinally\n\t\t{\n\t\t\t// Remove the stored socket regardless of any thrown errors.\n\t\t\tthis.webSocket = undefined;\n\t\t}\n\n\t\t// Indicate that the onConnect function has not run and it has to be run again.\n\t\tthis.onConnectHasRun = false;\n\n\t\t// Emit a disconnect event\n\t\tthis.emit('disconnected');\n\t}\n\n\t/**\n\t * Write data to the socket\n\t *\n\t * @param data Data to be written to the socket\n\t * @param callback Callback function to be called when the write has completed\n\t *\n\t * @throws {Error} if no connection was found\n\t * @returns true if the message was fully flushed to the socket, false if part of the message\n\t * is queued in the user memory\n\t */\n\tpublic write(data: Uint8Array | string, callback?: (err?: Error) => void): boolean\n\t{\n\t\t// Throw an error if no active connection is found\n\t\tif(!this.webSocket)\n\t\t{\n\t\t\tthrow(new Error('Cannot write to socket when there is no active connection'));\n\t\t}\n\n\t\t// Write data to the WebSocket\n\t\tthis.webSocket.send(data, callback);\n\n\t\t// WebSockets always fit everything in a single request, so we return true\n\t\treturn true;\n\t}\n\n\t/**\n\t * Force a disconnection if no connection is established after `timeout` milliseconds.\n\t */\n\tprivate disconnectOnTimeout(): void\n\t{\n\t\t// Remove the connect listener.\n\t\tthis.removeListener('connected', this.clearDisconnectTimerOnTimeout);\n\n\t\t// Emit an error event so that connect is rejected upstream.\n\t\tthis.emit('error', new Error(`Connection to '${this.host}:${this.port}' timed out after ${this.timeout} milliseconds`));\n\n\t\t// Forcibly disconnect to clean up the connection on timeout\n\t\tthis.disconnect();\n\t}\n\n\t// Add magic glue that makes typedoc happy so that we can have the events listed on the class.\n\tpublic readonly connected: [];\n\tpublic readonly disconnected: [];\n\tpublic readonly data: [ string ];\n\tpublic readonly error: [ Error ];\n}\n","// Export a default timeout value of 30 seconds.\nexport const defaultTimeout = 30 * 1000;\n"],"names":[],"version":3,"file":"index.mjs.map"}