UNPKG

@libp2p/websockets

Version:

JavaScript implementation of the WebSockets module that libp2p uses and that implements the interface-transport spec

144 lines 5.22 kB
/** * @packageDocumentation * * A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). * * @example * * ```TypeScript * import { createLibp2p } from 'libp2p' * import { webSockets } from '@libp2p/websockets' * import { multiaddr } from '@multiformats/multiaddr' * * const node = await createLibp2p({ * transports: [ * webSockets() * ] * //... other config * }) * await node.start() * * const ma = multiaddr('/dns4/example.com/tcp/9090/tls/ws') * await node.dial(ma) * ``` */ import { transportSymbol, serviceCapabilities, ConnectionFailedError } from '@libp2p/interface'; import { multiaddrToUri as toUri } from '@multiformats/multiaddr-to-uri'; import { connect } from 'it-ws/client'; import pDefer from 'p-defer'; import { CustomProgressEvent } from 'progress-events'; import { raceSignal } from 'race-signal'; import * as filters from './filters.js'; import { createListener } from './listener.js'; import { socketToMaConn } from './socket-to-conn.js'; class WebSockets { log; init; logger; metrics; components; constructor(components, init = {}) { this.log = components.logger.forComponent('libp2p:websockets'); this.logger = components.logger; this.components = components; this.init = init; if (components.metrics != null) { this.metrics = { dialerEvents: components.metrics.registerCounterGroup('libp2p_websockets_dialer_events_total', { label: 'event', help: 'Total count of WebSockets dialer events by type' }) }; } } [transportSymbol] = true; [Symbol.toStringTag] = '@libp2p/websockets'; [serviceCapabilities] = [ '@libp2p/transport' ]; async dial(ma, options) { this.log('dialing %s', ma); options = options ?? {}; const socket = await this._connect(ma, options); const maConn = socketToMaConn(socket, ma, { logger: this.logger, metrics: this.metrics?.dialerEvents }); this.log('new outbound connection %s', maConn.remoteAddr); const conn = await options.upgrader.upgradeOutbound(maConn, options); this.log('outbound connection %s upgraded', maConn.remoteAddr); return conn; } async _connect(ma, options) { options?.signal?.throwIfAborted(); const cOpts = ma.toOptions(); this.log('dialing %s:%s', cOpts.host, cOpts.port); const errorPromise = pDefer(); const rawSocket = connect(toUri(ma), this.init); rawSocket.socket.addEventListener('error', () => { // the WebSocket.ErrorEvent type doesn't actually give us any useful // information about what happened // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/error_event const err = new ConnectionFailedError(`Could not connect to ${ma.toString()}`); this.log.error('connection error:', err); this.metrics?.dialerEvents.increment({ error: true }); errorPromise.reject(err); }); try { options.onProgress?.(new CustomProgressEvent('websockets:open-connection')); await raceSignal(Promise.race([rawSocket.connected(), errorPromise.promise]), options.signal); } catch (err) { if (options.signal?.aborted) { this.metrics?.dialerEvents.increment({ abort: true }); } rawSocket.close() .catch(err => { this.log.error('error closing raw socket', err); }); throw err; } this.log('connected %s', ma); this.metrics?.dialerEvents.increment({ connect: true }); return rawSocket; } /** * Creates a WebSockets listener. The provided `handler` function will be called * anytime a new incoming Connection has been successfully upgraded via * `upgrader.upgradeInbound` */ createListener(options) { return createListener({ logger: this.logger, events: this.components.events, metrics: this.components.metrics }, { ...this.init, ...options }); } /** * Takes a list of `Multiaddr`s and returns only valid WebSockets addresses. * By default, in a browser environment only DNS+WSS multiaddr is accepted, * while in a Node.js environment DNS+{WS, WSS} multiaddrs are accepted. */ listenFilter(multiaddrs) { multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]; if (this.init?.filter != null) { return this.init?.filter(multiaddrs); } return filters.all(multiaddrs); } /** * Filter check for all Multiaddrs that this transport can dial */ dialFilter(multiaddrs) { return this.listenFilter(multiaddrs); } } export function webSockets(init = {}) { return (components) => { return new WebSockets(components, init); }; } //# sourceMappingURL=index.js.map