diff --git a/lib/server/swarm.js b/lib/server/swarm.js index 1dad058..f44f3cf 100644 --- a/lib/server/swarm.js +++ b/lib/server/swarm.js @@ -1,12 +1,16 @@ module.exports = Swarm var debug = require('debug')('bittorrent-tracker') +var LRU = require('lru') var randomIterate = require('random-iterate') // Regard this as the default implementation of an interface that you // need to support when overriding Server.createSwarm() and Server.getSwarm() function Swarm (infoHash, server) { - this.peers = {} + this.peers = new LRU({ + max: server.peersCacheLength || 10000, + maxAge: server.peersCacheTtl || 900 // 900s = 15 minutes + }) this.complete = 0 this.incomplete = 0 } @@ -14,7 +18,8 @@ function Swarm (infoHash, server) { Swarm.prototype.announce = function (params, cb) { var self = this var id = params.type === 'ws' ? params.peer_id : params.addr - var peer = self.peers[id] + // Mark the source peer as recently used in cache + var peer = self.peers.get(id) if (params.event === 'started') { self._onAnnounceStarted(params, peer) @@ -51,14 +56,14 @@ Swarm.prototype._onAnnounceStarted = function (params, peer) { if (params.left === 0) this.complete += 1 else this.incomplete += 1 var id = params.type === 'ws' ? params.peer_id : params.addr - peer = this.peers[id] = { + peer = this.peers.set(id, { type: params.type, complete: params.left === 0, peerId: params.peer_id, // as hex ip: params.ip, port: params.port, socket: params.socket // only websocket - } + }) } Swarm.prototype._onAnnounceStopped = function (params, peer) { @@ -70,7 +75,7 @@ Swarm.prototype._onAnnounceStopped = function (params, peer) { if (peer.complete) this.complete -= 1 else this.incomplete -= 1 var id = params.type === 'ws' ? params.peer_id : params.addr - delete this.peers[id] + this.peers.remove(id) } Swarm.prototype._onAnnounceCompleted = function (params, peer) { @@ -103,10 +108,11 @@ Swarm.prototype._onAnnounceUpdate = function (params, peer) { Swarm.prototype._getPeers = function (numwant, ownPeerId, isWebRTC) { var peers = [] - var ite = randomIterate(Object.keys(this.peers)) + var ite = randomIterate(Object.keys(this.peers.cache)) var peerId while ((peerId = ite()) && peers.length < numwant) { - var peer = this.peers[peerId] + // Don't mark the peer as most recently used on announce + var peer = this.peers.peek(peerId) if (isWebRTC && peer.peerId === ownPeerId) continue // don't send peer to itself if ((isWebRTC && peer.type !== 'ws') || (!isWebRTC && peer.type === 'ws')) continue // send proper peer type peers.push(peer) diff --git a/package.json b/package.json index 0e2da31..1334922 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "hat": "0.0.3", "inherits": "^2.0.1", "ip": "^1.0.1", + "lru": "^2.0.1", "minimist": "^1.1.1", "once": "^1.3.0", "random-iterate": "^1.0.1", diff --git a/server.js b/server.js index 62ca7be..4fc9e1e 100644 --- a/server.js +++ b/server.js @@ -50,6 +50,9 @@ function Server (opts) { self._trustProxy = !!opts.trustProxy if (typeof opts.filter === 'function') self._filter = opts.filter + self.peersCacheLength = opts.peersCacheLength + self.peersCacheTtl = opts.peersCacheTtl + self._listenCalled = false self.listening = false self.destroyed = false @@ -153,7 +156,7 @@ function Server (opts) { if (req.method === 'GET' && (req.url === '/stats' || req.url === '/stats.json')) { infoHashes.forEach(function (infoHash) { var peers = self.torrents[infoHash].peers - var keys = Object.keys(peers) + var keys = Object.keys(peers.cache) if (keys.length > 0) activeTorrents++ keys.forEach(function (peerId) { @@ -165,7 +168,8 @@ function Server (opts) { leecher: false } } - var peer = peers[peerId] + // Don't mark the peer as most recently used for stats + var peer = peers.peek(peerId) if (peer.ip.indexOf(':') >= 0) { allPeers[peerId].ipv6 = true } else { @@ -489,7 +493,8 @@ Server.prototype._onWebSocketRequest = function (socket, opts, params) { if (!swarm) { return self.emit('warning', new Error('no swarm with that `info_hash`')) } - var toPeer = swarm.peers[params.to_peer_id] + // Mark the destination peer as recently used in cache + var toPeer = swarm.peers.get(params.to_peer_id) if (!toPeer) { return self.emit('warning', new Error('no peer with that `to_peer_id`')) } diff --git a/test/server.js b/test/server.js index 526cab3..d8e2f4d 100644 --- a/test/server.js +++ b/test/server.js @@ -52,22 +52,23 @@ function serverTest (t, serverType, serverFamily) { t.equal(Object.keys(server.torrents).length, 1) t.equal(swarm.complete, 0) t.equal(swarm.incomplete, 1) - t.equal(Object.keys(swarm.peers).length, 1) + t.equal(Object.keys(swarm.peers.cache).length, 1) var id = serverType === 'ws' ? peerId.toString('hex') : hostname + ':6881' - t.equal(swarm.peers[id].type, serverType) - t.equal(swarm.peers[id].ip, clientIp) - t.equal(swarm.peers[id].peerId, peerId.toString('hex')) - t.equal(swarm.peers[id].complete, false) + var peer = swarm.peers.peek(id) + t.equal(peer.type, serverType) + t.equal(peer.ip, clientIp) + t.equal(peer.peerId, peerId.toString('hex')) + t.equal(peer.complete, false) if (serverType === 'ws') { - t.equal(typeof swarm.peers[id].port, 'number') - t.ok(swarm.peers[id].socket) + t.equal(typeof peer.port, 'number') + t.ok(peer.socket) } else { - t.equal(swarm.peers[id].port, 6881) - t.notOk(swarm.peers[id].socket) + t.equal(peer.port, 6881) + t.notOk(peer.socket) } client1.complete()