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:
Alan Shaw
2019-09-16 16:19:47 +01:00
committed by Jacob Heun
parent 49c7f33375
commit cf7d1b8501
19 changed files with 616 additions and 753 deletions

View File

@ -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))
})
}
}