From d7a651f3605056e6e1d09f4bedf0b5f022d2249d Mon Sep 17 00:00:00 2001 From: Yoann Ciabaud Date: Tue, 7 Jun 2016 10:49:27 +0200 Subject: [PATCH] Fixes for PR #155 --- README.md | 2 +- server.js | 48 +++++++--------- test/stats.js | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 29 deletions(-) create mode 100644 test/stats.js diff --git a/README.md b/README.md index f9ae868..d6e1a91 100644 --- a/README.md +++ b/README.md @@ -32,7 +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/) -- Tracker statistics available via web interface at `/stats` +- Tracker statistics available via web interface at `/stats` or JSON data at `/stats.json` Also see [bittorrent-dht](https://github.com/feross/bittorrent-dht). diff --git a/server.js b/server.js index 4043314..62ca7be 100644 --- a/server.js +++ b/server.js @@ -150,7 +150,7 @@ function Server (opts) { return count } - if (req.method === 'GET' && req.url === '/stats' || 'stats.json') { + 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) @@ -185,36 +185,28 @@ function Server (opts) { var isIPv4 = function (peer) { return peer.ipv4 } var isIPv6 = function (peer) { return peer.ipv6 } - var torrents = infoHashes.length - var peersAll = Object.keys(allPeers).length - var peersSeederOnly = countPeers(isSeederOnly) - var peersLeecherOnly = countPeers(isLeecherOnly) - var peersSeederAndLeecher = countPeers(isSeederAndLeecher) - var peersIPv4 = countPeers(isIPv4) - var peersIPv6 = countPeers(isIPv6) - - if (req.url === '/stats') { - res.end('

' + torrents + ' torrents (' + activeTorrents + ' active)

\n' + - '

Connected Peers: ' + peersAll + '

\n' + - '

Peers Seeding Only: ' + peersSeederOnly + '

\n' + - '

Peers Leeching Only: ' + peersLeecherOnly + '

\n' + - '

Peers Seeding & Leeching: ' + peersSeederAndLeecher + '

\n' + - '

IPv4 Peers: ' + peersIPv4 + '

\n' + - '

IPv6 Peers: ' + peersIPv6 + '

\n') + var stats = { + torrents: infoHashes.length, + activeTorrents: activeTorrents, + peersAll: Object.keys(allPeers).length, + peersSeederOnly: countPeers(isSeederOnly), + peersLeecherOnly: countPeers(isLeecherOnly), + peersSeederAndLeecher: countPeers(isSeederAndLeecher), + peersIPv4: countPeers(isIPv4), + peersIPv6: countPeers(isIPv6) } - if (req.url === '/stats.json') { - res.write(JSON.stringify({ - torrents: torrents, - activeTorrents: activeTorrents, - peersAll: peersAll, - peersSeederOnly: peersSeederOnly, - peersLeecherOnly: peersLeecherOnly, - peersSeederAndLeecher: peersSeederAndLeecher, - peersIPv4: peersIPv4, - peersIPv6: peersIPv6 - })) + if (req.url === '/stats.json' || req.headers['content-type'] === 'application/json') { + res.write(JSON.stringify(stats)) res.end() + } else if (req.url === '/stats') { + res.end('

' + stats.torrents + ' torrents (' + stats.activeTorrents + ' active)

\n' + + '

Connected Peers: ' + stats.peersAll + '

\n' + + '

Peers Seeding Only: ' + stats.peersSeederOnly + '

\n' + + '

Peers Leeching Only: ' + stats.peersLeecherOnly + '

\n' + + '

Peers Seeding & Leeching: ' + stats.peersSeederAndLeecher + '

\n' + + '

IPv4 Peers: ' + stats.peersIPv4 + '

\n' + + '

IPv6 Peers: ' + stats.peersIPv6 + '

\n') } } }) diff --git a/test/stats.js b/test/stats.js new file mode 100644 index 0000000..8971cd6 --- /dev/null +++ b/test/stats.js @@ -0,0 +1,152 @@ +var Buffer = require('safe-buffer').Buffer +var Client = require('../') +var commonTest = require('./common') +var fixtures = require('webtorrent-fixtures') +var get = require('simple-get') +var test = require('tape') + +var peerId = Buffer.from('01234567890123456789') + +function parseHtml (html) { + var extractValue = new RegExp('[^v^h](\\d+)') + var array = html.replace('torrents', '\n').split('\n').filter(function (line) { + return line && line.trim().length > 0 + }).map(function (line) { + var a = extractValue.exec(line) + return parseInt(a[1]) + }) + var i = 0 + return { + torrents: array[i++], + activeTorrents: array[i++], + peersAll: array[i++], + peersSeederOnly: array[i++], + peersLeecherOnly: array[i++], + peersSeederAndLeecher: array[i++], + peersIPv4: array[i++], + peersIPv6: array[i] + } +} + +test('server: get empty stats', function (t) { + t.plan(11) + + commonTest.createServer(t, 'http', function (server, announceUrl) { + var url = announceUrl.replace('/announce', '/stats') + + get.concat(url, function (err, res, data) { + t.error(err) + + var stats = parseHtml(data.toString()) + t.equal(res.statusCode, 200) + t.equal(stats.torrents, 0) + t.equal(stats.activeTorrents, 0) + t.equal(stats.peersAll, 0) + t.equal(stats.peersSeederOnly, 0) + t.equal(stats.peersLeecherOnly, 0) + t.equal(stats.peersSeederAndLeecher, 0) + t.equal(stats.peersIPv4, 0) + t.equal(stats.peersIPv6, 0) + + server.close(function () { t.pass('server closed') }) + }) + }) +}) + +test('server: get empty stats with json header', function (t) { + t.plan(11) + + commonTest.createServer(t, 'http', function (server, announceUrl) { + var opts = { + url: announceUrl.replace('/announce', '/stats'), + headers: { + 'content-type': 'json' + }, + json: true + } + + get.concat(opts, function (err, res, stats) { + t.error(err) + + t.equal(res.statusCode, 200) + t.equal(stats.torrents, 0) + t.equal(stats.activeTorrents, 0) + t.equal(stats.peersAll, 0) + t.equal(stats.peersSeederOnly, 0) + t.equal(stats.peersLeecherOnly, 0) + t.equal(stats.peersSeederAndLeecher, 0) + t.equal(stats.peersIPv4, 0) + t.equal(stats.peersIPv6, 0) + + server.close(function () { t.pass('server closed') }) + }) + }) +}) + +test('server: get empty stats on stats.json', function (t) { + t.plan(11) + + commonTest.createServer(t, 'http', function (server, announceUrl) { + var opts = { + url: announceUrl.replace('/announce', '/stats.json'), + json: true + } + + get.concat(opts, function (err, res, stats) { + t.error(err) + + t.equal(res.statusCode, 200) + t.equal(stats.torrents, 0) + t.equal(stats.activeTorrents, 0) + t.equal(stats.peersAll, 0) + t.equal(stats.peersSeederOnly, 0) + t.equal(stats.peersLeecherOnly, 0) + t.equal(stats.peersSeederAndLeecher, 0) + t.equal(stats.peersIPv4, 0) + t.equal(stats.peersIPv6, 0) + + server.close(function () { t.pass('server closed') }) + }) + }) +}) + +test('server: get leecher stats.json', function (t) { + t.plan(10) + + commonTest.createServer(t, 'http', function (server, announceUrl) { + // announce a torrent to the tracker + var client = new Client({ + infoHash: fixtures.leaves.parsedTorrent.infoHash, + announce: announceUrl, + peerId: peerId, + port: 6881 + }) + client.on('error', function (err) { t.error(err) }) + client.on('warning', function (err) { t.error(err) }) + + client.start() + + server.once('start', function () { + var opts = { + url: announceUrl.replace('/announce', '/stats.json'), + json: true + } + + get.concat(opts, function (err, res, stats) { + t.error(err) + console.log(stats) + + t.equal(res.statusCode, 200) + t.equal(stats.torrents, 1) + t.equal(stats.activeTorrents, 1) + t.equal(stats.peersAll, 1) + t.equal(stats.peersSeederOnly, 0) + t.equal(stats.peersLeecherOnly, 1) + t.equal(stats.peersSeederAndLeecher, 0) + + client.destroy(function () { t.pass('client destroyed') }) + server.close(function () { t.pass('server closed') }) + }) + }) + }) +})