Files
meanTorrent/modules/announce/server/controllers/common/tracker.js
2017-03-24 11:50:58 +08:00

215 lines
4.8 KiB
JavaScript

'use strict';
function now() {
return Math.floor(new Date().getTime() / 1000);
}
const EVENT_NONE = 0;
const EVENT_COMPLETED = 1;
const EVENT_STARTED = 2;
const EVENT_STOPPED = 3;
function event(e) {
switch (e) {
case 'completed':
return EVENT_COMPLETED;
case 'started':
return EVENT_STARTED;
case 'stopped':
return EVENT_STOPPED;
}
return EVENT_NONE;
}
const PEERSTATE_SEEDER = 0;
const PEERSTATE_LEECHER = 1;
const PEER_COMPACT_SIZE = 6;
const ANNOUNCE_INTERVAL = 60;
function Peer(ip, port, left) {
if (!(this instanceof Peer))
return new Peer(ip, port, left);
this.compact = this._compact(ip, port);
this.state = (left > 0) ? PEERSTATE_LEECHER : PEERSTATE_SEEDER;
this.touch();
}
Peer.prototype = {
touch: function () {
this.lastAction = now();
},
timedOut: function (n) {
return n - this.lastAction > ANNOUNCE_INTERVAL * 2;
},
_compact: function (ip, port) {
var b = new Buffer(PEER_COMPACT_SIZE);
var parts = ip.split('.');
if (parts.length !== 4)
throw new Error('ip length error');
for (var i = 0; i < 4; i++)
b[i] = parseInt(parts[i], 10);
b[4] = (port >> 8) & 0xff;
b[5] = port & 0xff;
return b;
}
};
function File() {
if (!(this instanceof File))
return new File();
this.peerList = [];
this.peerDict = {};
this.downloads = 0;
this.seeders = 0;
this.leechers = 0;
this.lastCompact = now();
}
File.prototype = {
addPeer: function (peerId, peer, event) {
// Check if it is time to compact the peer list
var n = now();
if (this.seeders + this.leechers < this.peerList.length / 2 && this.peerList.length > 10 || (n - this.lastCompact) > ANNOUNCE_INTERVAL * 2) {
var newPeerList = [];
var i = 0;
for (var p in this.peerDict) {
if (!this.peerDict.hasOwnProperty(p))
continue;
var tmpPeer = this.peerList[this.peerDict[p]];
// Check if the peer is still alive
if (tmpPeer.timedOut(n)) {
if (tmpPeer.state === PEERSTATE_LEECHER)
this.leechers--;
else
this.seeders--;
delete this.peerDict[p];
continue;
}
newPeerList.push(tmpPeer);
this.peerDict[p] = i++;
}
this.peerList = newPeerList;
this.lastCompact = n;
}
if (event === EVENT_COMPLETED && peer.state === PEERSTATE_SEEDER)
this.downloads++;
// Check if the peer already exists
if (this.peerDict.hasOwnProperty(peerId)) {
var index = this.peerDict[peerId];
var oldPeer = this.peerList[index];
if (event === EVENT_STOPPED) {
if (oldPeer.state === PEERSTATE_LEECHER)
this.leechers--;
else
this.seeders--;
delete this.peerList[index];
delete this.peerDict[peerId];
} else {
// TODO: Should probably update compact in the old peer. So we
// handle the case if the user switched IP or Port. But we
// probably only want to do it if they differ
// oldPeer.compact = peer.compact;
if (oldPeer.state !== peer.state) {
if (peer.state === PEERSTATE_LEECHER) {
this.leechers++;
this.seeders--;
} else {
this.leechers--;
this.seeders++;
}
oldPeer.state = peer.state;
}
}
peer = oldPeer;
peer.touch();
} else if (event !== EVENT_STOPPED) {
this.peerDict[peerId] = this.peerList.length;
this.peerList.push(peer);
if (peer.state === PEERSTATE_LEECHER)
this.leechers++;
else
this.seeders++;
}
return peer;
},
writePeers: function (b, count, selfPeer) {
var c = 0;
var i = 0;
var p;
if (count > this.seeders + this.leechers) {
for (i = this.peerList.length - 1; i >= 0; i--) {
p = this.peerList[i];
if (p !== undefined && p !== selfPeer)
p.compact.copy(b, c++ * PEER_COMPACT_SIZE);
}
} else {
var m = Math.min(this.peerList.length, count);
for (i = 0; i < m; i++) {
var index = Math.floor(Math.random() * this.peerList.length);
p = this.peerList[index];
if (p !== undefined && p !== selfPeer)
p.compact.copy(b, c++ * PEER_COMPACT_SIZE);
}
}
return c * PEER_COMPACT_SIZE;
}
};
function Tracker() {
if (!(this instanceof Tracker))
return new Tracker();
this.files = {};
}
Tracker.prototype = {
getFile: function (infoHash) {
if (this.files.hasOwnProperty(infoHash))
return this.files[infoHash];
return this.addFile(infoHash);
},
addFile: function (infoHash) {
return (this.files[infoHash] = new File());
}
};
exports.PEER_COMPACT_SIZE = PEER_COMPACT_SIZE;
exports.ANNOUNCE_INTERVAL = ANNOUNCE_INTERVAL;
exports.event = event;
exports.Peer = Peer;
exports.Tracker = Tracker;