From 9d89de56bfdfbcb771b231d59a7b87d01dc75f5c Mon Sep 17 00:00:00 2001 From: Firaenix Date: Mon, 27 Apr 2020 16:07:30 +1000 Subject: [PATCH 1/5] Allow HTTP Request to /stats and /stats.json route (#1) --- server.js | 249 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 127 insertions(+), 122 deletions(-) diff --git a/server.js b/server.js index 1ec53da46..8d39ca993 100644 --- a/server.js +++ b/server.js @@ -136,147 +136,148 @@ class Server extends EventEmitter { } // Http handler for '/stats' route - this.http.on('request', (req, res) => { - if (res.headersSent) return + this.http.on('request', this.onStats); + } + + let num = !!this.http + !!this.udp4 + !!this.udp6 + const self = this + function onListening () { + num -= 1 + if (num === 0) { + self.listening = true + debug('listening') + self.emit('listening') + } + } + } - const infoHashes = Object.keys(this.torrents) - let activeTorrents = 0 - const allPeers = {} + onStats = (req, res) => { + if (res.headersSent) return - function countPeers (filterFunction) { - let count = 0 - let key + const infoHashes = Object.keys(this.torrents) + let activeTorrents = 0 + const allPeers = {} - for (key in allPeers) { - if (hasOwnProperty.call(allPeers, key) && filterFunction(allPeers[key])) { - count++ - } - } + function countPeers (filterFunction) { + let count = 0 + let key - return count + for (key in allPeers) { + if (hasOwnProperty.call(allPeers, key) && filterFunction(allPeers[key])) { + count++ } + } - function groupByClient () { - const clients = {} - for (const key in allPeers) { - if (hasOwnProperty.call(allPeers, key)) { - const peer = allPeers[key] - - if (!clients[peer.client.client]) { - clients[peer.client.client] = {} - } - const client = clients[peer.client.client] - // If the client is not known show 8 chars from peerId as version - const version = peer.client.version || Buffer.from(peer.peerId, 'hex').toString().substring(0, 8) - if (!client[version]) { - client[version] = 0 - } - client[version]++ - } + return count + } + + function groupByClient () { + const clients = {} + for (const key in allPeers) { + if (hasOwnProperty.call(allPeers, key)) { + const peer = allPeers[key] + + if (!clients[peer.client.client]) { + clients[peer.client.client] = {} + } + const client = clients[peer.client.client] + // If the client is not known show 8 chars from peerId as version + const version = peer.client.version || Buffer.from(peer.peerId, 'hex').toString().substring(0, 8) + if (!client[version]) { + client[version] = 0 } - return clients + client[version]++ } + } + return clients + } - function printClients (clients) { - let html = '' + return html + } - if (req.method === 'GET' && (req.url === '/stats' || req.url === '/stats.json')) { - infoHashes.forEach(infoHash => { - const peers = this.torrents[infoHash].peers - const keys = peers.keys - if (keys.length > 0) activeTorrents++ - - keys.forEach(peerId => { - // Don't mark the peer as most recently used for stats - const peer = peers.peek(peerId) - if (peer == null) return // peers.peek() can evict the peer - - if (!hasOwnProperty.call(allPeers, peerId)) { - allPeers[peerId] = { - ipv4: false, - ipv6: false, - seeder: false, - leecher: false - } - } - - if (peer.ip.includes(':')) { - allPeers[peerId].ipv6 = true - } else { - allPeers[peerId].ipv4 = true - } - - if (peer.complete) { - allPeers[peerId].seeder = true - } else { - allPeers[peerId].leecher = true - } - - allPeers[peerId].peerId = peer.peerId - allPeers[peerId].client = peerid(peer.peerId) - }) - }) + if (req.method === 'GET' && (req.url === '/stats' || req.url === '/stats.json')) { + infoHashes.forEach(infoHash => { + const peers = this.torrents[infoHash].peers + const keys = peers.keys + if (keys.length > 0) activeTorrents++ + + keys.forEach(peerId => { + // Don't mark the peer as most recently used for stats + const peer = peers.peek(peerId) + if (peer == null) return // peers.peek() can evict the peer + + if (!hasOwnProperty.call(allPeers, peerId)) { + allPeers[peerId] = { + ipv4: false, + ipv6: false, + seeder: false, + leecher: false + } + } - const isSeederOnly = peer => { return peer.seeder && peer.leecher === false } - const isLeecherOnly = peer => { return peer.leecher && peer.seeder === false } - const isSeederAndLeecher = peer => { return peer.seeder && peer.leecher } - const isIPv4 = peer => { return peer.ipv4 } - const isIPv6 = peer => { return peer.ipv6 } - - const stats = { - torrents: infoHashes.length, - activeTorrents, - peersAll: Object.keys(allPeers).length, - peersSeederOnly: countPeers(isSeederOnly), - peersLeecherOnly: countPeers(isLeecherOnly), - peersSeederAndLeecher: countPeers(isSeederAndLeecher), - peersIPv4: countPeers(isIPv4), - peersIPv6: countPeers(isIPv6), - clients: groupByClient() + if (peer.ip.includes(':')) { + allPeers[peerId].ipv6 = true + } else { + allPeers[peerId].ipv4 = true } - if (req.url === '/stats.json' || req.headers.accept === 'application/json') { - res.setHeader('Content-Type', 'application/json') - res.end(JSON.stringify(stats)) - } else if (req.url === '/stats') { - res.setHeader('Content-Type', 'text/html') - res.end(` -

${stats.torrents} torrents (${stats.activeTorrents} active)

-

Connected Peers: ${stats.peersAll}

-

Peers Seeding Only: ${stats.peersSeederOnly}

-

Peers Leeching Only: ${stats.peersLeecherOnly}

-

Peers Seeding & Leeching: ${stats.peersSeederAndLeecher}

-

IPv4 Peers: ${stats.peersIPv4}

-

IPv6 Peers: ${stats.peersIPv6}

-

Clients:

- ${printClients(stats.clients)} - `.replace(/^\s+/gm, '')) // trim left + if (peer.complete) { + allPeers[peerId].seeder = true + } else { + allPeers[peerId].leecher = true } - } + + allPeers[peerId].peerId = peer.peerId + allPeers[peerId].client = peerid(peer.peerId) + }) }) - } - let num = !!this.http + !!this.udp4 + !!this.udp6 - const self = this - function onListening () { - num -= 1 - if (num === 0) { - self.listening = true - debug('listening') - self.emit('listening') + const isSeederOnly = peer => { return peer.seeder && peer.leecher === false } + const isLeecherOnly = peer => { return peer.leecher && peer.seeder === false } + const isSeederAndLeecher = peer => { return peer.seeder && peer.leecher } + const isIPv4 = peer => { return peer.ipv4 } + const isIPv6 = peer => { return peer.ipv6 } + + const stats = { + torrents: infoHashes.length, + activeTorrents, + peersAll: Object.keys(allPeers).length, + peersSeederOnly: countPeers(isSeederOnly), + peersLeecherOnly: countPeers(isLeecherOnly), + peersSeederAndLeecher: countPeers(isSeederAndLeecher), + peersIPv4: countPeers(isIPv4), + peersIPv6: countPeers(isIPv6), + clients: groupByClient() + } + + if (req.url === '/stats.json' || req.headers.accept === 'application/json') { + res.write(JSON.stringify(stats)) + res.end() + } else if (req.url === '/stats') { + res.end(` +

${stats.torrents} torrents (${stats.activeTorrents} active)

+

Connected Peers: ${stats.peersAll}

+

Peers Seeding Only: ${stats.peersSeederOnly}

+

Peers Leeching Only: ${stats.peersLeecherOnly}

+

Peers Seeding & Leeching: ${stats.peersSeederAndLeecher}

+

IPv4 Peers: ${stats.peersIPv4}

+

IPv6 Peers: ${stats.peersIPv6}

+

Clients:

+ ${printClients(stats.clients)} + `.replace(/^\s+/gm, '')) // trim left } } } @@ -363,6 +364,10 @@ class Server extends EventEmitter { onHttpRequest (req, res, opts = {}) { opts.trustProxy = opts.trustProxy || this._trustProxy + if (req.originalUrl === "/stats" || req.originalUrl === "/stats.json") { + return this.onStats(req, res); + } + let params try { params = parseHttpRequest(req, opts) From abf0e205f71a4f5cab628a0935b360084974ad00 Mon Sep 17 00:00:00 2001 From: Firaenix Date: Mon, 27 Apr 2020 16:17:44 +1000 Subject: [PATCH 2/5] Convert onStats arrow function to class function --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 8d39ca993..26d82b061 100644 --- a/server.js +++ b/server.js @@ -151,7 +151,7 @@ class Server extends EventEmitter { } } - onStats = (req, res) => { + onStats (req, res) { if (res.headersSent) return const infoHashes = Object.keys(this.torrents) From bc1d6991ac5890e17374e66f26b7d10344d2ffdf Mon Sep 17 00:00:00 2001 From: Firaenix Date: Mon, 27 Apr 2020 16:31:57 +1000 Subject: [PATCH 3/5] Fix linter rules --- server.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.js b/server.js index 26d82b061..3e7e9d879 100644 --- a/server.js +++ b/server.js @@ -136,7 +136,7 @@ class Server extends EventEmitter { } // Http handler for '/stats' route - this.http.on('request', this.onStats); + this.http.on('request', this.onStats) } let num = !!this.http + !!this.udp4 + !!this.udp6 @@ -364,8 +364,8 @@ class Server extends EventEmitter { onHttpRequest (req, res, opts = {}) { opts.trustProxy = opts.trustProxy || this._trustProxy - if (req.originalUrl === "/stats" || req.originalUrl === "/stats.json") { - return this.onStats(req, res); + if (req.originalUrl === '/stats' || req.originalUrl === '/stats.json') { + return this.onStats(req, res) } let params From 984b2cacbc2526a805abf8f4898d6aa07441244b Mon Sep 17 00:00:00 2001 From: Firaenix Date: Sun, 24 May 2020 14:59:55 +1000 Subject: [PATCH 4/5] bind stats --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 3e7e9d879..3f1f147c7 100644 --- a/server.js +++ b/server.js @@ -365,7 +365,7 @@ class Server extends EventEmitter { opts.trustProxy = opts.trustProxy || this._trustProxy if (req.originalUrl === '/stats' || req.originalUrl === '/stats.json') { - return this.onStats(req, res) + return this.onStats.bind(req, res) } let params From 8508a68451f860114e2a0baa15da0f2c21ac0df7 Mon Sep 17 00:00:00 2001 From: Firaenix Date: Sun, 24 May 2020 15:00:56 +1000 Subject: [PATCH 5/5] bind stats this --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 3f1f147c7..c00989233 100644 --- a/server.js +++ b/server.js @@ -365,7 +365,7 @@ class Server extends EventEmitter { opts.trustProxy = opts.trustProxy || this._trustProxy if (req.originalUrl === '/stats' || req.originalUrl === '/stats.json') { - return this.onStats.bind(req, res) + return this.onStats.bind(this, req, res) } let params