UNPKG

@libp2p/tcp

Version:
185 lines 6.94 kB
/** * @packageDocumentation * * A [libp2p transport](https://docs.libp2p.io/concepts/transports/overview/) based on the TCP networking stack. * * @example * * ```TypeScript * import { createLibp2p } from 'libp2p' * import { tcp } from '@libp2p/tcp' * import { multiaddr } from '@multiformats/multiaddr' * * const node = await createLibp2p({ * transports: [ * tcp() * ] * }) * * const ma = multiaddr('/ip4/123.123.123.123/tcp/1234') * * // dial a TCP connection, timing out after 10 seconds * const connection = await node.dial(ma, { * signal: AbortSignal.timeout(10_000) * }) * * // use connection... * ``` */ import net from 'net'; import { AbortError, TimeoutError, serviceCapabilities, transportSymbol } from '@libp2p/interface'; import { TCP as TCPMatcher } from '@multiformats/multiaddr-matcher'; import { CustomProgressEvent } from 'progress-events'; import { TCPListener } from './listener.js'; import { toMultiaddrConnection } from './socket-to-conn.js'; import { multiaddrToNetConfig } from './utils.js'; export class TCP { opts; metrics; components; log; constructor(components, options = {}) { this.log = components.logger.forComponent('libp2p:tcp'); this.opts = options; this.components = components; if (components.metrics != null) { this.metrics = { events: components.metrics.registerCounterGroup('libp2p_tcp_dialer_events_total', { label: 'event', help: 'Total count of TCP dialer events by type' }), errors: components.metrics.registerCounterGroup('libp2p_tcp_dialer_errors_total', { label: 'event', help: 'Total count of TCP dialer events by type' }) }; } } [transportSymbol] = true; [Symbol.toStringTag] = '@libp2p/tcp'; [serviceCapabilities] = [ '@libp2p/transport' ]; async dial(ma, options) { options.keepAlive = options.keepAlive ?? true; options.noDelay = options.noDelay ?? true; // options.signal destroys the socket before 'connect' event const socket = await this._connect(ma, options); let maConn; try { maConn = toMultiaddrConnection(socket, { remoteAddr: ma, socketInactivityTimeout: this.opts.outboundSocketInactivityTimeout, socketCloseTimeout: this.opts.socketCloseTimeout, metrics: this.metrics?.events, logger: this.components.logger, direction: 'outbound' }); } catch (err) { this.metrics?.errors.increment({ outbound_to_connection: true }); socket.destroy(err); throw err; } try { this.log('new outbound connection %s', maConn.remoteAddr); return await options.upgrader.upgradeOutbound(maConn, options); } catch (err) { this.metrics?.errors.increment({ outbound_upgrade: true }); this.log.error('error upgrading outbound connection', err); maConn.abort(err); throw err; } } async _connect(ma, options) { options.signal.throwIfAborted(); options.onProgress?.(new CustomProgressEvent('tcp:open-connection')); let rawSocket; return new Promise((resolve, reject) => { const start = Date.now(); const cOpts = multiaddrToNetConfig(ma, { ...(this.opts.dialOpts ?? {}), ...options }); this.log('dialing %a', ma); rawSocket = net.connect(cOpts); const onError = (err) => { this.log.error('dial to %a errored - %e', ma, err); const cOptsStr = cOpts.path ?? `${cOpts.host ?? ''}:${cOpts.port}`; err.message = `connection error ${cOptsStr}: ${err.message}`; this.metrics?.events.increment({ error: true }); done(err); }; const onTimeout = () => { this.log('connection timeout %a', ma); this.metrics?.events.increment({ timeout: true }); const err = new TimeoutError(`Connection timeout after ${Date.now() - start}ms`); // Note: this will result in onError() being called rawSocket.emit('error', err); }; const onConnect = () => { this.log('connection opened %a', ma); this.metrics?.events.increment({ connect: true }); done(); }; const onAbort = () => { this.log('connection aborted %a', ma); this.metrics?.events.increment({ abort: true }); done(new AbortError()); }; const done = (err) => { rawSocket.removeListener('error', onError); rawSocket.removeListener('timeout', onTimeout); rawSocket.removeListener('connect', onConnect); if (options.signal != null) { options.signal.removeEventListener('abort', onAbort); } if (err != null) { reject(err); return; } resolve(rawSocket); }; rawSocket.on('error', onError); rawSocket.on('timeout', onTimeout); rawSocket.on('connect', onConnect); options.signal.addEventListener('abort', onAbort); }) .catch(err => { rawSocket?.destroy(); throw err; }); } /** * Creates a TCP listener. The provided `handler` function will be called * anytime a new incoming Connection has been successfully upgraded via * `upgrader.upgradeInbound`. */ createListener(options) { return new TCPListener({ ...(this.opts.listenOpts ?? {}), ...options, maxConnections: this.opts.maxConnections, backlog: this.opts.backlog, closeServerOnMaxConnections: this.opts.closeServerOnMaxConnections, socketInactivityTimeout: this.opts.inboundSocketInactivityTimeout, socketCloseTimeout: this.opts.socketCloseTimeout, metrics: this.components.metrics, logger: this.components.logger }); } /** * Takes a list of `Multiaddr`s and returns only valid TCP addresses */ listenFilter(multiaddrs) { return multiaddrs.filter(ma => TCPMatcher.exactMatch(ma) || ma.toString().startsWith('/unix/')); } /** * Filter check for all Multiaddrs that this transport can dial */ dialFilter(multiaddrs) { return this.listenFilter(multiaddrs); } } //# sourceMappingURL=tcp.js.map