@libp2p/websockets
Version:
JavaScript implementation of the WebSockets module that libp2p uses and that implements the interface-transport spec
83 lines • 3.31 kB
JavaScript
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