bittorrent-dht
Advanced tools
Comparing version 3.0.8 to 3.1.0
@@ -78,8 +78,9 @@ module.exports = DHT | ||
self.ipv = opts.ipv || 4 | ||
self.ready = false | ||
self.listening = false | ||
self.destroyed = false | ||
self._binding = false | ||
self._destroyed = false | ||
self._port = null | ||
self._ipv = opts.ipv || 4 | ||
@@ -140,3 +141,3 @@ /** | ||
// Create socket and attach listeners | ||
self.socket = module.exports.dgram.createSocket('udp' + self.ipv) | ||
self.socket = module.exports.dgram.createSocket('udp' + self._ipv) | ||
self.socket.on('message', self._onData.bind(self)) | ||
@@ -179,2 +180,5 @@ self.socket.on('listening', self._onListening.bind(self)) | ||
var self = this | ||
if (self.destroyed) throw new Error('dht is destroyed') | ||
if (self._binding || self.listening) throw new Error('dht is already listening') | ||
if (typeof port === 'string') { | ||
@@ -197,3 +201,2 @@ onlistening = address | ||
if (self._destroyed || self._binding || self.listening) return | ||
self._binding = true | ||
@@ -233,3 +236,3 @@ | ||
if (!cb) cb = function () {} | ||
if (self._destroyed) return cb(new Error('dht is destroyed')) | ||
if (self.destroyed) return cb(new Error('dht is destroyed')) | ||
@@ -271,7 +274,7 @@ infoHash = idToBuffer(infoHash) | ||
cb = once(cb) | ||
if (self._destroyed) return cb(new Error('dht is destroyed')) | ||
if (self.destroyed) return cb(new Error('dht is destroyed')) | ||
if (self._binding) return self.once('listening', self.destroy.bind(self, cb)) | ||
self._debug('destroy') | ||
self._destroyed = true | ||
self.destroyed = true | ||
self.listening = false | ||
@@ -304,7 +307,27 @@ | ||
*/ | ||
DHT.prototype.addNode = function (addr, nodeId, from) { | ||
DHT.prototype.addNode = function (addr, nodeId) { | ||
var self = this | ||
if (self._destroyed) throw new Error('dht is destroyed') | ||
if (self.destroyed) throw new Error('dht is destroyed') | ||
// If `nodeId` is undefined, then the peer will be pinged to learn their node id. | ||
// If the peer does not respond, the will not be added to the routing table. | ||
if (nodeId == null) { | ||
self._sendPing(addr, function (err, res) { | ||
if (err) { | ||
self._debug('skipping addNode %s; peer did not respond: %s', addr, err.message) | ||
} | ||
// No need to call `self._addNode()` explicitly here. `_onData` automatically | ||
// attempts to add every node the client gets a message from to the routing table. | ||
}) | ||
return | ||
} | ||
nodeId = idToBuffer(nodeId) | ||
if (nodeId.length !== 20) throw new Error('invalid node id length') | ||
self._addNode(addr, nodeId, from) | ||
self._addNode(addr, nodeId) | ||
process.nextTick(function () { | ||
// TODO: only emit this event for new nodes | ||
self.emit('node', addr, nodeId, addr) | ||
}) | ||
} | ||
@@ -318,6 +341,7 @@ | ||
* @param {string=} from addr | ||
* @return {boolean} was the node valid and added to the table | ||
*/ | ||
DHT.prototype._addNode = function (addr, nodeId, from) { | ||
var self = this | ||
if (self._destroyed) return | ||
if (self.destroyed) return false | ||
nodeId = idToBuffer(nodeId) | ||
@@ -327,3 +351,3 @@ | ||
self._debug('skipping addNode %s %s; invalid id length', addr, idToHexString(nodeId)) | ||
return | ||
return false | ||
} | ||
@@ -333,3 +357,3 @@ | ||
self._debug('skip addNode %s %s; that is us!', addr, idToHexString(nodeId)) | ||
return | ||
return false | ||
} | ||
@@ -342,5 +366,5 @@ | ||
self.nodes.add(contact) | ||
// TODO: only emit this event for new nodes | ||
self.emit('node', addr, nodeId, from) | ||
self._debug('addNode %s %s discovered from %s', idToHexString(nodeId), addr, from) | ||
return true | ||
} | ||
@@ -354,3 +378,3 @@ | ||
var self = this | ||
if (self._destroyed) return | ||
if (self.destroyed) throw new Error('dht is destroyed') | ||
var contact = self.nodes.get(idToBuffer(nodeId)) | ||
@@ -370,3 +394,3 @@ if (contact) { | ||
var self = this | ||
if (self._destroyed) return | ||
if (self.destroyed) return | ||
@@ -396,5 +420,5 @@ infoHash = idToHexString(infoHash) | ||
*/ | ||
DHT.prototype.removePeer = function (addr, infoHash) { | ||
DHT.prototype._removePeer = function (addr, infoHash) { | ||
var self = this | ||
if (self._destroyed) return | ||
if (self.destroyed) return | ||
@@ -444,3 +468,4 @@ infoHash = idToHexString(infoHash) | ||
.forEach(function (contact) { | ||
self._addNode(contact.addr, contact.id, contact.from) | ||
var valid = self._addNode(contact.addr, contact.id, contact.from) | ||
if (valid) self.emit('node', contact.addr, contact.id, contact.from) | ||
}) | ||
@@ -477,3 +502,3 @@ | ||
self._bootstrapTimeout = setTimeout(function () { | ||
if (self._destroyed) return | ||
if (self.destroyed) return | ||
// If 0 nodes are in the table after a timeout, retry with bootstrap nodes | ||
@@ -503,3 +528,3 @@ if (self.nodes.count() === 0) { | ||
} else { | ||
dns.lookup(addrData[0], self.ipv, function (err, host) { | ||
dns.lookup(addrData[0], self._ipv, function (err, host) { | ||
if (err) return cb(null, null) | ||
@@ -542,3 +567,3 @@ contact.addr = host + ':' + addrData[1] | ||
if (self._destroyed) return cb(new Error('dht is destroyed')) | ||
if (self.destroyed) return cb(new Error('dht is destroyed')) | ||
if (!self.listening) return self.listen(self.lookup.bind(self, id, opts, cb)) | ||
@@ -614,3 +639,3 @@ if (id.length !== 20) throw new Error('invalid node id / info hash length') | ||
function onResponse (addr, err, res) { | ||
if (self._destroyed) return cb(new Error('dht is destroyed')) | ||
if (self.destroyed) return cb(new Error('dht is destroyed')) | ||
@@ -699,7 +724,9 @@ pending -= 1 | ||
// Attempt to add every (valid) node that we see to the routing table. | ||
// TODO: If they node is already in the table, just update the "last heard from" time | ||
// TODO: If the node is already in the table, just update the "last heard from" time | ||
var nodeId = (message.r && message.r.id) || (message.a && message.a.id) | ||
if (nodeId) { | ||
// self._debug('adding (potentially) new node %s %s', idToHexString(nodeId), addr) | ||
self._addNode(addr, nodeId, addr) | ||
var valid = self._addNode(addr, nodeId, addr) | ||
// TODO: only emit this event for new nodes | ||
if (valid) self.emit('node', addr, nodeId, addr) | ||
} | ||
@@ -740,2 +767,3 @@ | ||
var self = this | ||
if (self.destroyed) return | ||
@@ -755,8 +783,10 @@ var transactionId = Buffer.isBuffer(message.t) && message.t.length === 2 | ||
// unexpected message! | ||
var errMessage | ||
if (err) { | ||
var errMessage = 'got unexpected error from ' + addr + ' ' + err.message | ||
errMessage = 'got unexpected error from ' + addr + ' ' + err.message | ||
self._debug(errMessage) | ||
self.emit('warning', new Error(errMessage)) | ||
} else { | ||
self._debug('got unexpected message from ' + addr + ' ' + JSON.stringify(message)) | ||
errMessage = 'got unexpected message from ' + addr + ' ' + JSON.stringify(message) | ||
self._debug(errMessage) | ||
self.emit('warning', new Error(errMessage)) | ||
@@ -859,3 +889,5 @@ } | ||
res.nodes.forEach(function (node) { | ||
self._addNode(node.addr, node.id, addr) | ||
var valid = self._addNode(node.addr, node.id, addr) | ||
// TODO: only emit this event for new nodes | ||
if (valid) self.emit('node', node.addr, node.id, addr) | ||
}) | ||
@@ -926,3 +958,5 @@ } | ||
res.nodes.forEach(function (node) { | ||
self._addNode(node.addr, node.id, addr) | ||
var valid = self._addNode(node.addr, node.id, addr) | ||
// TODO: only emit this event for new nodes | ||
if (valid) self.emit('node', node.addr, node.id, addr) | ||
}) | ||
@@ -1200,3 +1234,3 @@ } | ||
return self._port && | ||
LOCAL_HOSTS[self.ipv].some(function (host) { return host + ':' + self._port === addr }) | ||
LOCAL_HOSTS[self._ipv].some(function (host) { return host + ':' + self._port === addr }) | ||
} | ||
@@ -1203,0 +1237,0 @@ |
{ | ||
"name": "bittorrent-dht", | ||
"description": "Simple, robust, BitTorrent DHT implementation", | ||
"version": "3.0.8", | ||
"version": "3.1.0", | ||
"author": { | ||
@@ -6,0 +6,0 @@ "name": "Feross Aboukhadijeh", |
@@ -16,82 +16,27 @@ var common = require('./common') | ||
t.equal(dht.nodeId, nodeId) | ||
dht.destroy() | ||
t.end() | ||
}) | ||
test('`ping` query send and response', function (t) { | ||
test('call `addNode` with nodeId argument', function (t) { | ||
t.plan(2) | ||
var dht1 = new DHT({ bootstrap: false }) | ||
var dht2 = new DHT({ bootstrap: false }) | ||
common.failOnWarningOrError(t, dht1) | ||
common.failOnWarningOrError(t, dht2) | ||
var dht = new DHT({ bootstrap: false }) | ||
common.failOnWarningOrError(t, dht) | ||
dht1.listen(function () { | ||
dht2._sendPing('127.0.0.1:' + dht1.address().port, function (err, res) { | ||
t.error(err) | ||
t.deepEqual(res.id, dht1.nodeId) | ||
var nodeId = common.randomId() | ||
dht1.destroy() | ||
dht2.destroy() | ||
}) | ||
dht.on('node', function (addr, _nodeId) { | ||
t.equal(addr, '127.0.0.1:9999') | ||
t.deepEqual(_nodeId, nodeId) | ||
dht.destroy() | ||
}) | ||
}) | ||
test('`find_node` query for exact match (with one in table)', function (t) { | ||
t.plan(3) | ||
var targetNodeId = common.randomId() | ||
dht.addNode('127.0.0.1:9999', nodeId) | ||
var dht1 = new DHT({ bootstrap: false }) | ||
var dht2 = new DHT({ bootstrap: false }) | ||
common.failOnWarningOrError(t, dht1) | ||
common.failOnWarningOrError(t, dht2) | ||
dht1.addNode('255.255.255.255:6969', targetNodeId) | ||
dht1.listen(function () { | ||
dht2._sendFindNode('127.0.0.1:' + dht1.address().port, targetNodeId, function (err, res) { | ||
t.error(err) | ||
t.deepEqual(res.id, dht1.nodeId) | ||
t.deepEqual( | ||
res.nodes.map(function (node) { return node.addr }), | ||
[ '255.255.255.255:6969', '127.0.0.1:' + dht2.address().port ] | ||
) | ||
dht1.destroy() | ||
dht2.destroy() | ||
}) | ||
}) | ||
}) | ||
test('`find_node` query (with many in table)', function (t) { | ||
t.plan(3) | ||
var dht1 = new DHT({ bootstrap: false }) | ||
var dht2 = new DHT({ bootstrap: false }) | ||
test('call `addNode` without nodeId argument', function (t) { | ||
t.plan(2) | ||
common.failOnWarningOrError(t, dht1) | ||
common.failOnWarningOrError(t, dht2) | ||
dht1.addNode('1.1.1.1:6969', common.randomId()) | ||
dht1.addNode('10.10.10.10:6969', common.randomId()) | ||
dht1.addNode('255.255.255.255:6969', common.randomId()) | ||
dht1.listen(function () { | ||
var targetNodeId = common.randomId() | ||
dht2._sendFindNode('127.0.0.1:' + dht1.address().port, targetNodeId, function (err, res) { | ||
t.error(err) | ||
t.deepEqual(res.id, dht1.nodeId) | ||
t.deepEqual( | ||
res.nodes.map(function (node) { return node.addr }).sort(), | ||
[ '1.1.1.1:6969', '10.10.10.10:6969', '127.0.0.1:' + dht2.address().port, | ||
'255.255.255.255:6969' ] | ||
) | ||
dht1.destroy() | ||
dht2.destroy() | ||
}) | ||
}) | ||
}) | ||
test('`get_peers` query to node with *no* peers in table', function (t) { | ||
t.plan(4) | ||
var dht1 = new DHT({ bootstrap: false }) | ||
@@ -103,47 +48,11 @@ var dht2 = new DHT({ bootstrap: false }) | ||
dht1.addNode('1.1.1.1:6969', common.randomId()) | ||
dht1.addNode('2.2.2.2:6969', common.randomId()) | ||
dht1.listen(function () { | ||
var targetInfoHash = common.randomId() | ||
dht2._sendGetPeers('127.0.0.1:' + dht1.address().port, targetInfoHash, function (err, res) { | ||
t.error(err) | ||
t.deepEqual(res.id, dht1.nodeId) | ||
t.ok(Buffer.isBuffer(res.token)) | ||
t.deepEqual( | ||
res.nodes.map(function (node) { return node.addr }).sort(), | ||
[ '1.1.1.1:6969', '127.0.0.1:' + dht2.address().port, '2.2.2.2:6969' ] | ||
) | ||
var port = dht1.address().port | ||
dht1.destroy() | ||
dht2.destroy() | ||
}) | ||
}) | ||
}) | ||
// If `nodeId` is undefined, then the peer will be pinged to learn their node id. | ||
dht2.addNode('127.0.0.1:' + port) | ||
test('`get_peers` query to node with peers in table', function (t) { | ||
t.plan(4) | ||
var dht1 = new DHT({ bootstrap: false }) | ||
var dht2 = new DHT({ bootstrap: false }) | ||
common.failOnWarningOrError(t, dht1) | ||
common.failOnWarningOrError(t, dht2) | ||
var targetInfoHash = common.randomId() | ||
dht1._addPeer('1.1.1.1:6969', targetInfoHash) | ||
dht1._addPeer('10.10.10.10:6969', targetInfoHash) | ||
dht1._addPeer('255.255.255.255:6969', targetInfoHash) | ||
dht1.listen(function () { | ||
dht2._sendGetPeers('127.0.0.1:' + dht1.address().port, targetInfoHash, function (err, res) { | ||
t.error(err) | ||
t.deepEqual(res.id, dht1.nodeId) | ||
t.ok(Buffer.isBuffer(res.token)) | ||
t.deepEqual( | ||
res.values.sort(), | ||
['1.1.1.1:6969', '10.10.10.10:6969', '255.255.255.255:6969'] | ||
) | ||
dht2.on('node', function (addr, _nodeId) { | ||
t.equal(addr, '127.0.0.1:' + port) | ||
t.deepEqual(_nodeId, dht1.nodeId) | ||
dht1.destroy() | ||
@@ -155,52 +64,21 @@ dht2.destroy() | ||
test('`announce_peer` query with bad token', function (t) { | ||
t.plan(2) | ||
var dht1 = new DHT({ bootstrap: false }) | ||
var dht2 = new DHT({ bootstrap: false }) | ||
test('call `addNode` without nodeId argument, and invalid addr', function (t) { | ||
t.plan(1) | ||
common.failOnWarningOrError(t, dht1) | ||
common.failOnWarningOrError(t, dht2) | ||
var dht = new DHT({ bootstrap: false }) | ||
common.failOnWarningOrError(t, dht) | ||
var infoHash = common.randomId() | ||
// If `nodeId` is undefined, then the peer will be pinged to learn their node id. | ||
// If the peer DOES NOT RESPOND, the will not be added to the routing table. | ||
dht.addNode('127.0.0.1:9999') | ||
dht1.listen(function () { | ||
var token = new Buffer('bad token') | ||
dht2._sendAnnouncePeer('127.0.0.1:' + dht1.address().port, infoHash, 9999, token, function (err, res) { | ||
t.ok(err, 'got error') | ||
t.ok(err.message.indexOf('bad token') !== -1) | ||
dht1.destroy() | ||
dht2.destroy() | ||
}) | ||
dht.on('node', function () { | ||
// No 'node' event should be emitted if the added node does not respond to ping | ||
t.fail('somehow found a node, even though no node actually responded') | ||
}) | ||
}) | ||
test('`announce_peer` query gets ack response', function (t) { | ||
t.plan(5) | ||
var dht1 = new DHT({ bootstrap: false }) | ||
var dht2 = new DHT({ bootstrap: false }) | ||
common.failOnWarningOrError(t, dht1) | ||
common.failOnWarningOrError(t, dht2) | ||
var infoHash = common.randomId() | ||
dht1.listen(function () { | ||
var port = dht1.address().port | ||
dht2._sendGetPeers('127.0.0.1:' + port, infoHash, function (err, res1) { | ||
t.error(err) | ||
t.deepEqual(res1.id, dht1.nodeId) | ||
t.ok(Buffer.isBuffer(res1.token)) | ||
dht2._sendAnnouncePeer('127.0.0.1:' + port, infoHash, 9999, res1.token, function (err, res2) { | ||
t.error(err) | ||
t.deepEqual(res2.id, dht1.nodeId) | ||
dht1.destroy() | ||
dht2.destroy() | ||
} | ||
) | ||
}) | ||
}) | ||
setTimeout(function () { | ||
t.pass('no "node" event emitted for 2 seconds') | ||
dht.destroy() | ||
}, 2000) | ||
}) |
@@ -6,5 +6,5 @@ var test = require('tape') | ||
test('announce+lookup with 2-10 DHTs', function (t) { | ||
test('announce+lookup with 2-20 DHTs', function (t) { | ||
var from = 2 | ||
var to = 10 | ||
var to = 20 | ||
@@ -11,0 +11,0 @@ var numRunning = to - from + 1 |
63121
17
1671