Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down Expand Up @@ -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
Expand Down
13 changes: 12 additions & 1 deletion bin/cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ var argv = minimist(process.argv.slice(2), {
'trust-proxy',
'udp',
'version',
'ws'
'ws',
'no-stats'
],
string: [
'http-hostname',
Expand Down Expand Up @@ -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
Expand All @@ -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']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary. We can solve this with the default option to minimist. I'll fix it in a follow up commit, and you can see how to use it.


var server = new Server({
http: argv.http,
interval: argv.interval,
stats: argv['no-stats'],
trustProxy: argv['trust-proxy'],
udp: argv.udp,
ws: argv.ws
Expand Down Expand Up @@ -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)
}
})
74 changes: 74 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! This is a great way to do this.


// Http handler for '/stats' route
self.http.on('request', function (req, res, opts) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be req, res. This event doesn't have a third parameter.

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('<h1>' + infoHashes.length + ' torrents (' + activeTorrents + ' active)</h1>\n' +
'<h2>Connected Peers: ' + Object.keys(allPeers).length + '</h2>\n' +
'<h3>Peers Seeding Only: ' + countPeers(isSeederOnly) + '</h3>\n' +
'<h3>Peers Leeching Only: ' + countPeers(isLeecherOnly) + '</h3>\n' +
'<h3>Peers Seeding & Leeching: ' + countPeers(isSeederAndLeecher) + '</h3>\n' +
'<h3>IPv4 Peers: ' + countPeers(isIPv4) + '</h3>\n' +
'<h3>IPv6 Peers: ' + countPeers(isIPv6) + '</h3>\n')
}
})
}

var num = !!self.http + !!self.udp4 + !!self.udp6
function onListening () {
num -= 1
Expand Down