diff --git a/README.md b/README.md index f06ebbf..6cf2de5 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ This module is used by [WebTorrent](http://webtorrent.io). - Robust and well-tested - Comprehensive test suite (runs entirely offline, so it's reliable) - Used by popular clients: [WebTorrent](http://webtorrent.io), [peerflix](https://github.com/mafintosh/peerflix), and [playback](https://mafintosh.github.io/playback/) +- Web-Based Statistics Also see [bittorrent-dht](https://github.com/feross/bittorrent-dht). @@ -143,6 +144,7 @@ var server = new Server({ udp: true, // enable udp server? [default=true] http: true, // enable http server? [default=true] ws: true, // enable websocket server? [default=true] + stats: true, // enable web-based statistics [default=true] filter: function (infoHash, params, cb) { // Blacklist/whitelist function for allowing/disallowing torrents. If this option is // omitted, all torrents are allowed. It is possible to interface with a database or diff --git a/bin/cmd.js b/bin/cmd.js index 079c725..5da1139 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -19,7 +19,8 @@ var argv = minimist(process.argv.slice(2), { 'trust-proxy', 'udp', 'version', - 'ws' + 'ws', + 'no-stats' ], string: [ 'http-hostname', @@ -56,6 +57,7 @@ if (argv.help) { --http enable http server --udp enable udp server --ws enable websocket server + --no-stats disable web-based statistics -q, --quiet only show error output -s, --silent show no output -v, --version print the current version @@ -75,9 +77,12 @@ argv.http = allFalsy || argv.http argv.udp = allFalsy || argv.udp argv.ws = allFalsy || argv.ws +argv['no-stats'] = !!argv['no-stats'] + var server = new Server({ http: argv.http, interval: argv.interval, + stats: argv['no-stats'], trustProxy: argv['trust-proxy'], udp: argv.udp, ws: argv.ws @@ -133,4 +138,10 @@ server.listen(argv.port, hostname, function () { var wsPort = wsAddr.port console.log('WebSocket tracker: ws://' + wsHost + ':' + wsPort) } + if (server.http && argv['no-stats'] && !argv.quiet) { + var statsAddr = server.http.address() + var statsHost = statsAddr.address !== '::' ? statsAddr.address : 'localhost' + var statsPort = statsAddr.port + console.log('Tracker statistics: http://' + statsHost + ':' + statsPort) + } }) diff --git a/server.js b/server.js index f430daa..135d490 100644 --- a/server.js +++ b/server.js @@ -120,6 +120,80 @@ function Server (opts) { self.ws.on('connection', function (socket) { self.onWebSocketConnection(socket) }) } + if (opts.stats !== false) { + if (!self.http) { + self.http = http.createServer() + self.http.on('error', function (err) { self._onError(err) }) + self.http.on('listening', onListening) + } + + // Http handler for '/stats' route + self.http.on('request', function (req, res, opts) { + if (res.headersSent) return + + var infoHashes = Object.keys(self.torrents) + var activeTorrents = 0 + var allPeers = {} + + function countPeers (filterFunction) { + var count = 0 + var key + + for (key in allPeers) { + if (allPeers.hasOwnProperty(key) && filterFunction(allPeers[key])) { + count++ + } + } + + return count + } + + if (req.method === 'GET' && req.url === '/stats') { + infoHashes.forEach(function (infoHash) { + var peers = self.torrents[infoHash].peers + var keys = Object.keys(peers) + if (keys.length > 0) activeTorrents++ + + keys.forEach(function (peerId) { + if (!allPeers.hasOwnProperty(peerId)) { + allPeers[peerId] = { + ipv4: false, + ipv6: false, + seeder: false, + leecher: false + } + } + var peer = peers[peerId] + if (peer.ip.indexOf(':') >= 0) { + allPeers[peerId].ipv6 = true + } else { + allPeers[peerId].ipv4 = true + } + if (peer.complete) { + allPeers[peerId].seeder = true + } else { + allPeers[peerId].leecher = true + } + }) + }) + + var isSeederOnly = function (peer) { return peer.seeder && peer.leecher === false } + var isLeecherOnly = function (peer) { return peer.leecher && peer.seeder === false } + var isSeederAndLeecher = function (peer) { return peer.seeder && peer.leecher } + var isIPv4 = function (peer) { return peer.ipv4 } + var isIPv6 = function (peer) { return peer.ipv6 } + + res.end('

' + infoHashes.length + ' torrents (' + activeTorrents + ' active)

\n' + + '

Connected Peers: ' + Object.keys(allPeers).length + '

\n' + + '

Peers Seeding Only: ' + countPeers(isSeederOnly) + '

\n' + + '

Peers Leeching Only: ' + countPeers(isLeecherOnly) + '

\n' + + '

Peers Seeding & Leeching: ' + countPeers(isSeederAndLeecher) + '

\n' + + '

IPv4 Peers: ' + countPeers(isIPv4) + '

\n' + + '

IPv6 Peers: ' + countPeers(isIPv6) + '

\n') + } + }) + } + var num = !!self.http + !!self.udp4 + !!self.udp6 function onListening () { num -= 1