2019-10-21 16:53:58 +02:00
|
|
|
'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')
|
2019-10-21 16:53:58 +02:00
|
|
|
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')
|
2019-10-21 16:53:58 +02:00
|
|
|
|
|
|
|
const { codes } = require('./errors')
|
|
|
|
const {
|
2019-12-03 10:28:52 +01:00
|
|
|
DIAL_TIMEOUT,
|
2019-10-21 16:53:58 +02:00
|
|
|
MAX_PARALLEL_DIALS,
|
2019-12-04 13:58:23 +01:00
|
|
|
MAX_PER_PEER_DIALS
|
2019-10-21 16:53:58 +02:00
|
|
|
} = require('./constants')
|
|
|
|
|
|
|
|
class Dialer {
|
|
|
|
/**
|
|
|
|
* @constructor
|
|
|
|
* @param {object} options
|
|
|
|
* @param {TransportManager} options.transportManager
|
2019-11-26 16:40:04 +01:00
|
|
|
* @param {Peerstore} peerStore
|
2019-10-21 16:53:58 +02:00
|
|
|
* @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,
|
2019-11-26 16:40:04 +01:00
|
|
|
peerStore,
|
2019-10-21 16:53:58 +02:00
|
|
|
concurrency = MAX_PARALLEL_DIALS,
|
2019-12-03 10:28:52 +01:00
|
|
|
timeout = DIAL_TIMEOUT,
|
2019-12-04 13:58:23 +01:00
|
|
|
perPeerLimit = MAX_PER_PEER_DIALS
|
2019-10-21 16:53:58 +02:00
|
|
|
}) {
|
|
|
|
this.transportManager = transportManager
|
2019-11-26 16:40:04 +01:00
|
|
|
this.peerStore = peerStore
|
2019-10-21 16:53:58 +02:00
|
|
|
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-10-21 16:53:58 +02:00
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
*
|
2019-12-04 17:05:09 +01:00
|
|
|
* @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>}
|
|
|
|
*/
|
2019-12-04 17:05:09 +01:00
|
|
|
async connectToMultiaddr (addrs, options = {}) {
|
|
|
|
if (!Array.isArray(addrs)) addrs = [multiaddr(addrs)]
|
|
|
|
|
2019-12-04 16:27:33 +01:00
|
|
|
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)
|
2019-10-21 16:53:58 +02:00
|
|
|
|
|
|
|
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
|
2019-10-21 16:53:58 +02:00
|
|
|
} 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)
|
2019-10-21 16:53:58 +02:00
|
|
|
throw err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-11-26 16:40:04 +01:00
|
|
|
* Connects to a given `PeerInfo` or `PeerId` by dialing all of its known addresses.
|
2019-10-21 16:53:58 +02:00
|
|
|
* 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
|
2019-10-21 16:53:58 +02:00
|
|
|
* @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-11-26 16:40:04 +01:00
|
|
|
|
2019-12-03 10:28:52 +01:00
|
|
|
// TODO: ensure the peer id is on the multiaddr
|
|
|
|
|
2019-12-04 17:05:09 +01:00
|
|
|
return this.connectToMultiaddr(addrs, options)
|
2019-12-03 10:28:52 +01:00
|
|
|
}
|
2019-10-21 16:53:58 +02: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)
|
2019-10-21 16:53:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Dialer
|