UNPKG

@libp2p/websockets

Version:

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

83 lines 3.31 kB
import { AbortError } from '@libp2p/interface'; import { CLOSE_TIMEOUT } from './constants.js'; // Convert a stream into a MultiaddrConnection // https://github.com/libp2p/interface-transport#multiaddrconnection export function socketToMaConn(stream, remoteAddr, options) { const log = options.logger.forComponent('libp2p:websockets:maconn'); const metrics = options.metrics; const metricPrefix = options.metricPrefix ?? ''; const maConn = { log, async sink(source) { try { await stream.sink((async function* () { for await (const buf of source) { if (buf instanceof Uint8Array) { yield buf; } else { yield buf.subarray(); } } })()); } catch (err) { if (err.type !== 'aborted') { log.error(err); } } }, source: stream.source, remoteAddr, timeline: { open: Date.now() }, async close(options = {}) { const start = Date.now(); if (options.signal == null) { const signal = AbortSignal.timeout(CLOSE_TIMEOUT); options = { ...options, signal }; } const listener = () => { const { host, port } = maConn.remoteAddr.toOptions(); log('timeout closing stream to %s:%s after %dms, destroying it manually', host, port, Date.now() - start); this.abort(new AbortError('Socket close timeout')); }; options.signal?.addEventListener('abort', listener); try { await stream.close(); } catch (err) { log.error('error closing WebSocket gracefully', err); this.abort(err); } finally { options.signal?.removeEventListener('abort', listener); maConn.timeline.close = Date.now(); } }, abort(err) { const { host, port } = maConn.remoteAddr.toOptions(); log('timeout closing stream to %s:%s due to error', host, port, err); stream.destroy(); maConn.timeline.close = Date.now(); // ws WebSocket.terminate does not accept an Error arg to emit an 'error' // event on destroy like other node streams so we can't update a metric // with an event listener // https://github.com/websockets/ws/issues/1752#issuecomment-622380981 metrics?.increment({ [`${metricPrefix}error`]: true }); } }; stream.socket.addEventListener('close', () => { metrics?.increment({ [`${metricPrefix}close`]: true }); // In instances where `close` was not explicitly called, // such as an iterable stream ending, ensure we have set the close // timeline if (maConn.timeline.close == null) { maConn.timeline.close = Date.now(); } }, { once: true }); return maConn; } //# sourceMappingURL=socket-to-conn.js.map