js-libp2p/src/dialer.js

119 lines
3.7 KiB
JavaScript
Raw Normal View History

'use strict'
const multiaddr = require('multiaddr')
const errCode = require('err-code')
const AbortController = require('abort-controller')
2019-12-03 10:28:52 +01:00
const delay = require('delay')
2019-12-04 15:59:01 +01:00
const anySignal = require('any-signal')
const debug = require('debug')
const log = debug('libp2p:dialer')
log.error = debug('libp2p:dialer:error')
2019-12-03 10:28:52 +01:00
const { DialRequest } = require('./dialer/dial-request')
const { codes } = require('./errors')
const {
2019-12-03 10:28:52 +01:00
DIAL_TIMEOUT,
MAX_PARALLEL_DIALS,
MAX_PER_PEER_DIALS
} = require('./constants')
class Dialer {
/**
* @constructor
* @param {object} options
* @param {TransportManager} options.transportManager
* @param {Peerstore} peerStore
* @param {number} options.concurrency Number of max concurrent dials. Defaults to `MAX_PARALLEL_DIALS`
* @param {number} options.timeout How long a dial attempt is allowed to take. Defaults to `DIAL_TIMEOUT`
*/
constructor ({
transportManager,
peerStore,
concurrency = MAX_PARALLEL_DIALS,
2019-12-03 10:28:52 +01:00
timeout = DIAL_TIMEOUT,
perPeerLimit = MAX_PER_PEER_DIALS
}) {
this.transportManager = transportManager
this.peerStore = peerStore
this.concurrency = concurrency
this.timeout = timeout
2019-12-03 10:28:52 +01:00
this.perPeerLimit = perPeerLimit
this.tokens = [...new Array(concurrency)].map((_, index) => index)
}
2019-12-03 10:28:52 +01:00
/**
* Connects to the first success of a given list of `Multiaddr`. `addrs` should
* include the id of the peer being dialed, it will be used for encryption verification.
*
* @param {Array<Multiaddr>|Multiaddr} addrs
2019-12-03 10:28:52 +01:00
* @param {object} [options]
* @param {AbortSignal} [options.signal] An AbortController signal
* @returns {Promise<Connection>}
*/
async connectToMultiaddr (addrs, options = {}) {
if (!Array.isArray(addrs)) addrs = [multiaddr(addrs)]
const dialAction = (addr, options) => {
if (options.signal.aborted) throw errCode(new Error('already aborted'), 'ERR_ALREADY_ABORTED')
return this.transportManager.dial(addr, options)
}
2019-12-03 10:28:52 +01:00
const dialRequest = new DialRequest({
addrs,
dialAction,
dialer: this
})
// Combine the timeout signal and options.signal, if provided
const timeoutController = new AbortController()
const signals = [timeoutController.signal]
options.signal && signals.push(options.signal)
const signal = anySignal(signals)
2019-12-04 16:59:38 +01:00
const timeoutId = setTimeout(() => timeoutController.abort(), this.timeout)
try {
2019-12-04 16:59:38 +01:00
const dialResult = await dialRequest.run({ ...options, signal })
clearTimeout(timeoutId)
2019-12-03 10:28:52 +01:00
return dialResult
} catch (err) {
2019-12-04 16:59:38 +01:00
// Error is a timeout
if (timeoutController.signal.aborted) {
err = errCode(err, codes.ERR_TIMEOUT)
}
2019-12-03 10:28:52 +01:00
log.error(err)
throw err
}
}
/**
* Connects to a given `PeerInfo` or `PeerId` by dialing all of its known addresses.
* The dial to the first address that is successfully able to upgrade a connection
* will be used.
*
2019-12-03 10:28:52 +01:00
* @param {PeerId} peerId The remote peer id to dial
* @param {object} [options]
* @param {AbortSignal} [options.signal] An AbortController signal
* @returns {Promise<Connection>}
*/
2019-12-03 10:28:52 +01:00
connectToPeer (peerId, options = {}) {
const addrs = this.peerStore.multiaddrsForPeer(peerId)
2019-12-03 10:28:52 +01:00
// TODO: ensure the peer id is on the multiaddr
return this.connectToMultiaddr(addrs, options)
2019-12-03 10:28:52 +01:00
}
2019-12-03 10:28:52 +01:00
getTokens (num) {
const total = Math.min(num, this.perPeerLimit, this.tokens.length)
const tokens = this.tokens.splice(0, total)
log('%d tokens request, returning %d, %d remaining', num, total, this.tokens.length)
return tokens
}
releaseToken (token) {
log('token %d released', token)
this.tokens.push(token)
}
}
module.exports = Dialer