libp2p
Version:
JavaScript implementation of libp2p, a modular peer to peer network stack
97 lines • 4.19 kB
JavaScript
import { randomBytes } from '@libp2p/crypto';
import { serviceCapabilities, setMaxListeners } from '@libp2p/interface';
import { AdaptiveTimeout } from '@libp2p/utils/adaptive-timeout';
import { byteStream } from 'it-byte-stream';
const DEFAULT_PING_INTERVAL_MS = 10000;
const PROTOCOL_VERSION = '1.0.0';
const PROTOCOL_NAME = 'ping';
const PROTOCOL_PREFIX = 'ipfs';
const PING_LENGTH = 32;
const DEFAULT_ABORT_CONNECTION_ON_PING_FAILURE = true;
export class ConnectionMonitor {
protocol;
components;
log;
heartbeatInterval;
pingIntervalMs;
abortController;
timeout;
abortConnectionOnPingFailure;
constructor(components, init = {}) {
this.components = components;
this.protocol = `/${init.protocolPrefix ?? PROTOCOL_PREFIX}/${PROTOCOL_NAME}/${PROTOCOL_VERSION}`;
this.log = components.logger.forComponent('libp2p:connection-monitor');
this.pingIntervalMs = init.pingInterval ?? DEFAULT_PING_INTERVAL_MS;
this.abortConnectionOnPingFailure = init.abortConnectionOnPingFailure ?? DEFAULT_ABORT_CONNECTION_ON_PING_FAILURE;
this.timeout = new AdaptiveTimeout({
...(init.pingTimeout ?? {}),
metrics: components.metrics,
metricName: 'libp2p_connection_monitor_ping_time_milliseconds'
});
}
[Symbol.toStringTag] = '@libp2p/connection-monitor';
[serviceCapabilities] = [
'@libp2p/connection-monitor'
];
start() {
this.abortController = new AbortController();
setMaxListeners(Infinity, this.abortController.signal);
this.heartbeatInterval = setInterval(() => {
this.components.connectionManager.getConnections().forEach(conn => {
Promise.resolve().then(async () => {
let start = Date.now();
try {
const signal = this.timeout.getTimeoutSignal({
signal: this.abortController?.signal
});
const stream = await conn.newStream(this.protocol, {
signal,
runOnLimitedConnection: true
});
const bs = byteStream(stream);
start = Date.now();
await Promise.all([
bs.write(randomBytes(PING_LENGTH), {
signal
}),
bs.read(PING_LENGTH, {
signal
})
]);
conn.rtt = Date.now() - start;
await bs.unwrap().close({
signal
});
}
catch (err) {
if (err.name !== 'UnsupportedProtocolError') {
throw err;
}
// protocol was unsupported, but that's ok as it means the remote
// peer was still alive. We ran multistream-select which means two
// round trips (e.g. 1x for the mss header, then another for the
// protocol) so divide the time it took by two
conn.rtt = (Date.now() - start) / 2;
}
})
.catch(err => {
this.log.error('error during heartbeat', err);
if (this.abortConnectionOnPingFailure) {
this.log.error('aborting connection due to ping failure');
conn.abort(err);
}
else {
this.log('connection ping failed, but not aborting due to abortConnectionOnPingFailure flag');
}
});
});
}, this.pingIntervalMs);
}
stop() {
this.abortController?.abort();
if (this.heartbeatInterval != null) {
clearInterval(this.heartbeatInterval);
}
}
}
//# sourceMappingURL=connection-monitor.js.map