mirror of
https://github.com/fluencelabs/js-libp2p-tcp
synced 2025-07-07 05:41:49 +00:00
feat: change api to async / await (#112)
BREAKING CHANGE: All places in the API that used callbacks are now replaced with async/await. The API has also been updated according to the latest `interface-transport` version, https://github.com/libp2p/interface-transport/tree/v0.6.0#api.
This commit is contained in:
168
src/index.js
168
src/index.js
@ -1,83 +1,137 @@
|
||||
'use strict'
|
||||
|
||||
const net = require('net')
|
||||
const toPull = require('stream-to-pull-stream')
|
||||
const mafmt = require('mafmt')
|
||||
const withIs = require('class-is')
|
||||
const includes = require('lodash.includes')
|
||||
const isFunction = require('lodash.isfunction')
|
||||
const Connection = require('interface-connection').Connection
|
||||
const once = require('once')
|
||||
const debug = require('debug')
|
||||
const log = debug('libp2p:tcp:dial')
|
||||
|
||||
const errCode = require('err-code')
|
||||
const log = require('debug')('libp2p:tcp')
|
||||
const toConnection = require('./socket-to-conn')
|
||||
const createListener = require('./listener')
|
||||
const { AbortError } = require('abortable-iterator')
|
||||
const { CODE_CIRCUIT, CODE_P2P } = require('./constants')
|
||||
const assert = require('assert')
|
||||
|
||||
function noop () {}
|
||||
|
||||
/**
|
||||
* @class TCP
|
||||
*/
|
||||
class TCP {
|
||||
dial (ma, options, callback) {
|
||||
if (isFunction(options)) {
|
||||
callback = options
|
||||
options = {}
|
||||
}
|
||||
|
||||
callback = once(callback || noop)
|
||||
|
||||
const cOpts = ma.toOptions()
|
||||
log('Connecting to %s %s', cOpts.port, cOpts.host)
|
||||
|
||||
const rawSocket = net.connect(cOpts)
|
||||
|
||||
rawSocket.once('timeout', () => {
|
||||
log('timeout')
|
||||
rawSocket.emit('error', new Error('Timeout'))
|
||||
})
|
||||
|
||||
rawSocket.once('error', callback)
|
||||
|
||||
rawSocket.once('connect', () => {
|
||||
rawSocket.removeListener('error', callback)
|
||||
callback()
|
||||
})
|
||||
|
||||
const socket = toPull.duplex(rawSocket)
|
||||
|
||||
const conn = new Connection(socket)
|
||||
|
||||
conn.getObservedAddrs = (callback) => {
|
||||
return callback(null, [ma])
|
||||
}
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} options
|
||||
* @param {Upgrader} options.upgrader
|
||||
*/
|
||||
constructor ({ upgrader }) {
|
||||
assert(upgrader, 'An upgrader must be provided. See https://github.com/libp2p/interface-transport#upgrader.')
|
||||
this._upgrader = upgrader
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* @param {Multiaddr} ma
|
||||
* @param {object} options
|
||||
* @param {AbortSignal} options.signal Used to abort dial requests
|
||||
* @returns {Connection} An upgraded Connection
|
||||
*/
|
||||
async dial (ma, options) {
|
||||
options = options || {}
|
||||
const socket = await this._connect(ma, options)
|
||||
const maConn = toConnection(socket, { remoteAddr: ma, signal: options.signal })
|
||||
log('new outbound connection %s', maConn.remoteAddr)
|
||||
const conn = await this._upgrader.upgradeOutbound(maConn)
|
||||
log('outbound connection %s upgraded', maConn.remoteAddr)
|
||||
return conn
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Multiaddr} ma
|
||||
* @param {object} options
|
||||
* @param {AbortSignal} options.signal Used to abort dial requests
|
||||
* @returns {Promise<Socket>} Resolves a TCP Socket
|
||||
*/
|
||||
_connect (ma, options = {}) {
|
||||
if (options.signal && options.signal.aborted) {
|
||||
throw new AbortError()
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const start = Date.now()
|
||||
const cOpts = ma.toOptions()
|
||||
|
||||
log('dialing %s:%s', cOpts.host, cOpts.port)
|
||||
const rawSocket = net.connect(cOpts)
|
||||
|
||||
const onError = err => {
|
||||
err.message = `connection error ${cOpts.host}:${cOpts.port}: ${err.message}`
|
||||
done(err)
|
||||
}
|
||||
|
||||
const onTimeout = () => {
|
||||
log('connnection timeout %s:%s', cOpts.host, cOpts.port)
|
||||
const err = errCode(new Error(`connection timeout after ${Date.now() - start}ms`), 'ERR_CONNECT_TIMEOUT')
|
||||
// Note: this will result in onError() being called
|
||||
rawSocket.emit('error', err)
|
||||
}
|
||||
|
||||
const onConnect = () => {
|
||||
log('connection opened %s:%s', cOpts.host, cOpts.port)
|
||||
done()
|
||||
}
|
||||
|
||||
const onAbort = () => {
|
||||
log('connection aborted %s:%s', cOpts.host, cOpts.port)
|
||||
rawSocket.destroy()
|
||||
done(new AbortError())
|
||||
}
|
||||
|
||||
const done = err => {
|
||||
rawSocket.removeListener('error', onError)
|
||||
rawSocket.removeListener('timeout', onTimeout)
|
||||
rawSocket.removeListener('connect', onConnect)
|
||||
options.signal && options.signal.removeEventListener('abort', onAbort)
|
||||
|
||||
if (err) return reject(err)
|
||||
resolve(rawSocket)
|
||||
}
|
||||
|
||||
rawSocket.on('error', onError)
|
||||
rawSocket.on('timeout', onTimeout)
|
||||
rawSocket.on('connect', onConnect)
|
||||
options.signal && options.signal.addEventListener('abort', onAbort)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TCP listener. The provided `handler` function will be called
|
||||
* anytime a new incoming Connection has been successfully upgraded via
|
||||
* `upgrader.upgradeInbound`.
|
||||
* @param {*} [options]
|
||||
* @param {function(Connection)} handler
|
||||
* @returns {Listener} A TCP listener
|
||||
*/
|
||||
createListener (options, handler) {
|
||||
if (isFunction(options)) {
|
||||
if (typeof options === 'function') {
|
||||
handler = options
|
||||
options = {}
|
||||
}
|
||||
|
||||
handler = handler || noop
|
||||
|
||||
return createListener(handler)
|
||||
options = options || {}
|
||||
return createListener({ handler, upgrader: this._upgrader }, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a list of `Multiaddr`s and returns only valid TCP addresses
|
||||
* @param {Multiaddr[]} multiaddrs
|
||||
* @returns {Multiaddr[]} Valid TCP multiaddrs
|
||||
*/
|
||||
filter (multiaddrs) {
|
||||
if (!Array.isArray(multiaddrs)) {
|
||||
multiaddrs = [multiaddrs]
|
||||
}
|
||||
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
|
||||
|
||||
return multiaddrs.filter((ma) => {
|
||||
if (includes(ma.protoNames(), 'p2p-circuit')) {
|
||||
return multiaddrs.filter(ma => {
|
||||
if (ma.protoCodes().includes(CODE_CIRCUIT)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (includes(ma.protoNames(), 'ipfs')) {
|
||||
ma = ma.decapsulate('ipfs')
|
||||
}
|
||||
|
||||
return mafmt.TCP.matches(ma)
|
||||
return mafmt.TCP.matches(ma.decapsulateCode(CODE_P2P))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user