libp2p-tcp
Advanced tools
Comparing version 0.15.4 to 0.16.0
@@ -0,1 +1,10 @@ | ||
# [0.16.0](https://github.com/libp2p/js-libp2p-tcp/compare/v0.15.4...v0.16.0) (2021-06-10) | ||
### Features | ||
* add types ([#145](https://github.com/libp2p/js-libp2p-tcp/issues/145)) ([3249e02](https://github.com/libp2p/js-libp2p-tcp/commit/3249e0292b2ef5d818fe428ce61f689b25060d85)) | ||
## [0.15.4](https://github.com/libp2p/js-libp2p-tcp/compare/v0.15.2...v0.15.4) (2021-04-12) | ||
@@ -2,0 +11,0 @@ |
{ | ||
"name": "libp2p-tcp", | ||
"version": "0.15.4", | ||
"version": "0.16.0", | ||
"description": "Node.js implementation of the TCP module that libp2p uses, which implements the interface-connection and interface-transport interfaces", | ||
@@ -9,7 +9,8 @@ "leadMaintainer": "Jacob Heun <jacobheun@gmail.com>", | ||
"lint": "aegir lint", | ||
"build": "aegir build", | ||
"test": "aegir test -t node", | ||
"test:node": "aegir test -t node", | ||
"release": "aegir release -t node --no-build", | ||
"release-minor": "aegir release -t node --type minor --no-build", | ||
"release-major": "aegir-release -t node --type major --no-build", | ||
"release": "aegir release -t node", | ||
"release-minor": "aegir release -t node --type minor", | ||
"release-major": "aegir-release -t node --type major", | ||
"coverage": "nyc --reporter=text --reporter=lcov npm run test:node" | ||
@@ -41,6 +42,8 @@ }, | ||
}, | ||
"types": "dist/src/index.d.ts", | ||
"devDependencies": { | ||
"aegir": "^33.0.0", | ||
"@types/debug": "^4.1.5", | ||
"aegir": "^33.2.0", | ||
"it-pipe": "^1.1.0", | ||
"libp2p-interfaces": "^0.9.0", | ||
"libp2p-interfaces": "^0.11.0", | ||
"sinon": "^10.0.1", | ||
@@ -69,6 +72,7 @@ "streaming-iterables": "^5.0.2" | ||
"Alan Shaw <alan@tableflip.io>", | ||
"Nazar Hussain <nazarhussain@gmail.com>", | ||
"Pedro Teixeira <i@pgte.me>", | ||
"Prashanth Chandra <coolshanth94@gmail.com>", | ||
"Ryan Mehta <ryan.mehta@gmail.com>", | ||
"João Antunes <j.goncalo.antunes@gmail.com>", | ||
"Linus Unnebäck <linus@folkdatorn.se>", | ||
"Cayman <caymannava@gmail.com>", | ||
@@ -79,5 +83,5 @@ "Diogo Silva <fsdiogo@gmail.com>", | ||
"Evan Schwartz <evan.mark.schwartz@gmail.com>", | ||
"Linus Unnebäck <linus@folkdatorn.se>", | ||
"João Antunes <j.goncalo.antunes@gmail.com>", | ||
"Mikeal Rogers <mikeal.rogers@gmail.com>" | ||
] | ||
} |
@@ -5,2 +5,4 @@ 'use strict' | ||
const mafmt = require('mafmt') | ||
// Missing Type | ||
// @ts-ignore | ||
const withIs = require('class-is') | ||
@@ -18,2 +20,5 @@ const errCode = require('err-code') | ||
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection | ||
* @typedef {import('libp2p-interfaces/src/transport/types').Upgrader} Upgrader | ||
* @typedef {import('libp2p-interfaces/src/transport/types').Listener} Listener | ||
* @typedef {import('net').Socket} Socket | ||
*/ | ||
@@ -38,4 +43,4 @@ | ||
* @param {object} options | ||
* @param {AbortSignal} options.signal - Used to abort dial requests | ||
* @returns {Connection} An upgraded Connection | ||
* @param {AbortSignal} [options.signal] - Used to abort dial requests | ||
* @returns {Promise<Connection>} An upgraded Connection | ||
*/ | ||
@@ -56,3 +61,3 @@ async dial (ma, options) { | ||
* @param {object} options | ||
* @param {AbortSignal} options.signal - Used to abort dial requests | ||
* @param {AbortSignal} [options.signal] - Used to abort dial requests | ||
* @returns {Promise<Socket>} Resolves a TCP Socket | ||
@@ -72,3 +77,3 @@ */ | ||
const onError = err => { | ||
const onError = /** @param {Error} err */ err => { | ||
err.message = `connection error ${cOpts.host}:${cOpts.port}: ${err.message}` | ||
@@ -79,3 +84,3 @@ done(err) | ||
const onTimeout = () => { | ||
log('connnection timeout %s:%s', cOpts.host, cOpts.port) | ||
log('connection timeout %s:%s', cOpts.host, cOpts.port) | ||
const err = errCode(new Error(`connection timeout after ${Date.now() - start}ms`), 'ERR_CONNECT_TIMEOUT') | ||
@@ -97,3 +102,3 @@ // Note: this will result in onError() being called | ||
const done = err => { | ||
const done = /** @param {Error} [err] */ err => { | ||
rawSocket.removeListener('error', onError) | ||
@@ -120,13 +125,17 @@ rawSocket.removeListener('timeout', onTimeout) | ||
* | ||
* @param {*} [options] | ||
* @param {function(Connection)} handler | ||
* @param {* | function(Connection):void} options | ||
* @param {function(Connection):void} [handler] | ||
* @returns {Listener} A TCP listener | ||
*/ | ||
createListener (options, handler) { | ||
let listenerHandler | ||
if (typeof options === 'function') { | ||
handler = options | ||
listenerHandler = options | ||
options = {} | ||
} else { | ||
listenerHandler = handler | ||
} | ||
options = options || {} | ||
return createListener({ handler, upgrader: this._upgrader }, options) | ||
return createListener({ handler: listenerHandler, upgrader: this._upgrader }, options) | ||
} | ||
@@ -133,0 +142,0 @@ |
@@ -6,5 +6,5 @@ 'use strict' | ||
const debug = require('debug') | ||
const log = debug('libp2p:tcp:listener') | ||
log.error = debug('libp2p:tcp:listener:error') | ||
const log = Object.assign( | ||
debug('libp2p:tcp:listener'), | ||
{ error: debug('libp2p:tcp:listener:error') }) | ||
const toConnection = require('./socket-to-conn') | ||
@@ -18,6 +18,15 @@ const { CODE_P2P } = require('./constants') | ||
/** | ||
* @typedef {import('multiaddr').Multiaddr} Multiaddr | ||
* @typedef {import('libp2p-interfaces/src/connection').Connection} Connection | ||
* @typedef {import('libp2p-interfaces/src/transport/types').Upgrader} Upgrader | ||
* @typedef {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} MultiaddrConnection | ||
* @typedef {import('libp2p-interfaces/src/transport/types').Listener} Listener | ||
* @typedef {import('net').Server & {__connections: MultiaddrConnection[]}} Server | ||
*/ | ||
/** | ||
* Attempts to close the given maConn. If a failure occurs, it will be logged. | ||
* | ||
* @private | ||
* @param {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} maConn | ||
* @param {MultiaddrConnection} maConn | ||
*/ | ||
@@ -32,9 +41,76 @@ async function attemptClose (maConn) { | ||
/** | ||
* Create listener | ||
* | ||
* @param {object} context | ||
* @param {function(Connection):void} context.handler | ||
* @param {Upgrader} context.upgrader | ||
* @param {*} options | ||
* @returns {Listener} | ||
*/ | ||
module.exports = ({ handler, upgrader }, options) => { | ||
const listener = new EventEmitter() | ||
/** @type {Server} */ | ||
// eslint-disable-next-line prefer-const | ||
let server | ||
const server = net.createServer(async socket => { | ||
/** @type {string | null} */ | ||
let peerId | ||
/** @type {Multiaddr} */ | ||
let listeningAddr | ||
const listener = Object.assign(new EventEmitter(), { | ||
getAddrs: () => { | ||
/** @type {Multiaddr[]} */ | ||
let addrs = [] | ||
/** @type {import('net').AddressInfo} */ | ||
// @ts-ignore | ||
const address = server.address() | ||
if (!address) { | ||
throw new Error('Listener is not ready yet') | ||
} | ||
// Because TCP will only return the IPv6 version | ||
// we need to capture from the passed multiaddr | ||
if (listeningAddr.toString().startsWith('/ip4')) { | ||
addrs = addrs.concat(getMultiaddrs('ip4', address.address, address.port)) | ||
} else if (address.family === 'IPv6') { | ||
addrs = addrs.concat(getMultiaddrs('ip6', address.address, address.port)) | ||
} | ||
return addrs.map(ma => peerId ? ma.encapsulate(`/p2p/${peerId}`) : ma) | ||
}, | ||
listen: async (/** @type {Multiaddr} */ ma) => { | ||
listeningAddr = ma | ||
peerId = ma.getPeerId() | ||
if (peerId) { | ||
listeningAddr = ma.decapsulateCode(CODE_P2P) | ||
} | ||
return new Promise((resolve, reject) => { | ||
const options = multiaddrToNetConfig(listeningAddr) | ||
server.listen(options, (/** @type {any} */ err) => { | ||
if (err) return reject(err) | ||
log('Listening on %s', server.address()) | ||
resolve(undefined) | ||
}) | ||
}) | ||
}, | ||
close: async () => { | ||
if (!server.listening) return | ||
return new Promise((resolve, reject) => { | ||
server.__connections.forEach(maConn => attemptClose(maConn)) | ||
server.close(err => err ? reject(err) : resolve(undefined)) | ||
}) | ||
} | ||
}) | ||
server = Object.assign(net.createServer(async socket => { | ||
// Avoid uncaught errors caused by unstable connections | ||
socket.on('error', err => log('socket error', err)) | ||
/** @type {MultiaddrConnection} */ | ||
let maConn | ||
@@ -48,2 +124,3 @@ let conn | ||
log.error('inbound connection failed', err) | ||
// @ts-ignore | ||
return attemptClose(maConn) | ||
@@ -58,3 +135,5 @@ } | ||
listener.emit('connection', conn) | ||
}) | ||
}), | ||
// Keep track of open connections to destroy in case of timeout | ||
{ __connections: [] }) | ||
@@ -66,56 +145,9 @@ server | ||
// Keep track of open connections to destroy in case of timeout | ||
server.__connections = [] | ||
listener.close = () => { | ||
if (!server.listening) return | ||
return new Promise((resolve, reject) => { | ||
server.__connections.forEach(maConn => attemptClose(maConn)) | ||
server.close(err => err ? reject(err) : resolve()) | ||
}) | ||
} | ||
let peerId, listeningAddr | ||
listener.listen = ma => { | ||
listeningAddr = ma | ||
peerId = ma.getPeerId() | ||
if (peerId) { | ||
listeningAddr = ma.decapsulateCode(CODE_P2P) | ||
} | ||
return new Promise((resolve, reject) => { | ||
const options = multiaddrToNetConfig(listeningAddr) | ||
server.listen(options, err => { | ||
if (err) return reject(err) | ||
log('Listening on %s', server.address()) | ||
resolve() | ||
}) | ||
}) | ||
} | ||
listener.getAddrs = () => { | ||
let addrs = [] | ||
const address = server.address() | ||
if (!address) { | ||
throw new Error('Listener is not ready yet') | ||
} | ||
// Because TCP will only return the IPv6 version | ||
// we need to capture from the passed multiaddr | ||
if (listeningAddr.toString().startsWith('/ip4')) { | ||
addrs = addrs.concat(getMultiaddrs('ip4', address.address, address.port)) | ||
} else if (address.family === 'IPv6') { | ||
addrs = addrs.concat(getMultiaddrs('ip6', address.address, address.port)) | ||
} | ||
return addrs.map(ma => peerId ? ma.encapsulate(`/p2p/${peerId}`) : ma) | ||
} | ||
return listener | ||
} | ||
/** | ||
* @param {Server} server | ||
* @param {MultiaddrConnection} maConn | ||
*/ | ||
function trackConn (server, maConn) { | ||
@@ -128,3 +160,4 @@ server.__connections.push(maConn) | ||
// @ts-ignore | ||
maConn.conn.once('close', untrackConn) | ||
} |
@@ -5,2 +5,4 @@ 'use strict' | ||
const log = require('debug')('libp2p:tcp:socket') | ||
// Missing Type | ||
// @ts-ignore | ||
const toIterable = require('stream-to-it') | ||
@@ -10,5 +12,22 @@ const toMultiaddr = require('libp2p-utils/src/ip-port-to-multiaddr') | ||
// Convert a socket into a MultiaddrConnection | ||
// https://github.com/libp2p/interface-transport#multiaddrconnection | ||
module.exports = (socket, options) => { | ||
/** | ||
* @typedef {import('multiaddr').Multiaddr} Multiaddr | ||
* @typedef {import('libp2p-interfaces/src/transport/types').MultiaddrConnection} MultiaddrConnection | ||
* @typedef {import('net').Socket} Socket | ||
*/ | ||
/** | ||
* Convert a socket into a MultiaddrConnection | ||
* https://github.com/libp2p/interface-transport#multiaddrconnection | ||
* | ||
* @private | ||
* @param {Socket} socket | ||
* @param {object} options | ||
* @param {Multiaddr} [options.listeningAddr] | ||
* @param {Multiaddr} [options.remoteAddr] | ||
* @param {Multiaddr} [options.localAddr] | ||
* @param {AbortSignal} [options.signal] | ||
* @returns {MultiaddrConnection} | ||
*/ | ||
const toConnection = (socket, options) => { | ||
options = options || {} | ||
@@ -26,5 +45,9 @@ | ||
const { sink, source } = toIterable.duplex(socket) | ||
/** @type {MultiaddrConnection} */ | ||
const maConn = { | ||
async sink (source) { | ||
if (options.signal) { | ||
// Missing Type for "abortable" | ||
// @ts-ignore | ||
source = abortable(source, options.signal) | ||
@@ -37,2 +60,4 @@ } | ||
// Convert BufferList to Buffer | ||
// Sink in StreamMuxer define argument as Uint8Array so chunk type infers as number which can't be sliced | ||
// @ts-ignore | ||
yield Buffer.isBuffer(chunk) ? chunk : chunk.slice() | ||
@@ -52,2 +77,4 @@ } | ||
// Missing Type for "abortable" | ||
// @ts-ignore | ||
source: options.signal ? abortable(source, options.signal) : source, | ||
@@ -60,7 +87,7 @@ | ||
// If the remote address was passed, use it - it may have the peer ID encapsulated | ||
remoteAddr: options.remoteAddr || toMultiaddr(socket.remoteAddress, socket.remotePort), | ||
remoteAddr: options.remoteAddr || toMultiaddr(socket.remoteAddress || '', socket.remotePort || ''), | ||
timeline: { open: Date.now() }, | ||
close () { | ||
async close () { | ||
if (socket.destroyed) return | ||
@@ -75,4 +102,8 @@ | ||
const { host, port } = maConn.remoteAddr.toOptions() | ||
log('timeout closing socket to %s:%s after %dms, destroying it manually', | ||
host, port, Date.now() - start) | ||
log( | ||
'timeout closing socket to %s:%s after %dms, destroying it manually', | ||
host, | ||
port, | ||
Date.now() - start | ||
) | ||
@@ -92,3 +123,3 @@ if (socket.destroyed) { | ||
}) | ||
socket.end(err => { | ||
socket.end(/** @param {Error} [err] */(err) => { | ||
maConn.timeline.close = Date.now() | ||
@@ -113,1 +144,3 @@ if (err) return reject(err) | ||
} | ||
module.exports = toConnection |
@@ -8,2 +8,10 @@ 'use strict' | ||
/** | ||
* @typedef {import('multiaddr').MultiaddrObject} MultiaddrObject | ||
*/ | ||
/** | ||
* @param {Multiaddr} addr | ||
* @returns {MultiaddrObject} | ||
*/ | ||
function multiaddrToNetConfig (addr) { | ||
@@ -13,2 +21,4 @@ const listenPath = addr.getPath() | ||
if (listenPath) { | ||
// TCP should not return unix socket else need to refactor listener which accepts connection options object | ||
// @ts-ignore | ||
return resolve(listenPath) | ||
@@ -20,7 +30,17 @@ } | ||
/** | ||
* @param {'ip4' | 'ip6'} proto | ||
* @param {string} ip | ||
* @param {number} port | ||
* @returns {Multiaddr[]} | ||
*/ | ||
function getMultiaddrs (proto, ip, port) { | ||
const toMa = ip => new Multiaddr(`/${proto}/${ip}/tcp/${port}`) | ||
const toMa = /** @param {string} ip */ ip => new Multiaddr(`/${proto}/${ip}/tcp/${port}`) | ||
return (isAnyAddr(ip) ? getNetworkAddrs(ProtoFamily[proto]) : [ip]).map(toMa) | ||
} | ||
/** | ||
* @param {string} ip | ||
* @returns {boolean} | ||
*/ | ||
function isAnyAddr (ip) { | ||
@@ -36,10 +56,21 @@ return ['0.0.0.0', '::'].includes(ip) | ||
const networks = os.networkInterfaces() | ||
/** | ||
* @param {string} family | ||
* @returns {string[]} | ||
*/ | ||
function getNetworkAddrs (family) { | ||
return Object.values(networks).reduce((addresses, netAddrs) => { | ||
netAddrs.forEach(netAddr => { | ||
// Add the ip of each matching network interface | ||
if (netAddr.family === family) addresses.push(netAddr.address) | ||
}) | ||
return addresses | ||
}, []) | ||
const addresses = [] | ||
for (const [, netAddrs] of Object.entries(networks)) { | ||
if (netAddrs) { | ||
for (const netAddr of netAddrs) { | ||
if (netAddr.family === family) { | ||
addresses.push(netAddr.address) | ||
} | ||
} | ||
} | ||
} | ||
return addresses | ||
} | ||
@@ -46,0 +77,0 @@ |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
450962
34
4168
6
2
6
5