Skip to content

Commit cbe4a3f

Browse files
committed
UPDATE: all route services extracted, tests pass
1 parent 27a0c5f commit cbe4a3f

File tree

2 files changed

+80
-206
lines changed

2 files changed

+80
-206
lines changed

server.js

Lines changed: 1 addition & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ const setupStatsRoute = require('./services/statsRoute')
1010
const common = require('./lib/common')
1111
const Swarm = require('./lib/server/swarm')
1212

13-
const SDP_TRICKLE_REGEX = /a=ice-options:trickle\s\n/
14-
1513
/**
1614
* BitTorrent tracker server.
1715
*
@@ -64,150 +62,7 @@ class Server extends EventEmitter {
6462
if (opts.http !== false) attachHttpServer(this, onListening);
6563
if (opts.ws !== false) attachWSServer(this, onListening);
6664
if (opts.udp !== false) attachUdpServer(this, onListening);
67-
// if (opts.stats !== false) setupStatsRoute(this, onListening);
68-
69-
70-
71-
if (opts.stats !== false) {
72-
if (!this.http) {
73-
attachHttpServer(this, onListening);
74-
this.http.on('error', err => { this.onError(err) })
75-
this.http.on('listening', onListening)
76-
}
77-
78-
// Http handler for '/stats' route
79-
this.http.on('request', (req, res) => {
80-
if (res.headersSent) return
81-
82-
const infoHashes = Object.keys(this.torrents)
83-
let activeTorrents = 0
84-
const allPeers = {}
85-
86-
function countPeers (filterFunction) {
87-
let count = 0
88-
let key
89-
90-
for (key in allPeers) {
91-
if (hasOwnProperty.call(allPeers, key) && filterFunction(allPeers[key])) {
92-
count++
93-
}
94-
}
95-
96-
return count
97-
}
98-
99-
function groupByClient () {
100-
const clients = {}
101-
for (const key in allPeers) {
102-
if (hasOwnProperty.call(allPeers, key)) {
103-
const peer = allPeers[key]
104-
105-
if (!clients[peer.client.client]) {
106-
clients[peer.client.client] = {}
107-
}
108-
const client = clients[peer.client.client]
109-
// If the client is not known show 8 chars from peerId as version
110-
const version = peer.client.version || Buffer.from(peer.peerId, 'hex').toString().substring(0, 8)
111-
if (!client[version]) {
112-
client[version] = 0
113-
}
114-
client[version]++
115-
}
116-
}
117-
return clients
118-
}
119-
120-
function printClients (clients) {
121-
let html = '<ul>\n'
122-
for (const name in clients) {
123-
if (hasOwnProperty.call(clients, name)) {
124-
const client = clients[name]
125-
for (const version in client) {
126-
if (hasOwnProperty.call(client, version)) {
127-
html += `<li><strong>${name}</strong> ${version} : ${client[version]}</li>\n`
128-
}
129-
}
130-
}
131-
}
132-
html += '</ul>'
133-
return html
134-
}
135-
136-
if (req.method === 'GET' && (req.url === '/stats' || req.url === '/stats.json')) {
137-
infoHashes.forEach(infoHash => {
138-
const peers = this.torrents[infoHash].peers
139-
const keys = peers.keys
140-
if (keys.length > 0) activeTorrents++
141-
142-
keys.forEach(peerId => {
143-
// Don't mark the peer as most recently used for stats
144-
const peer = peers.peek(peerId)
145-
if (peer == null) return // peers.peek() can evict the peer
146-
147-
if (!hasOwnProperty.call(allPeers, peerId)) {
148-
allPeers[peerId] = {
149-
ipv4: false,
150-
ipv6: false,
151-
seeder: false,
152-
leecher: false
153-
}
154-
}
155-
156-
if (peer.ip.includes(':')) {
157-
allPeers[peerId].ipv6 = true
158-
} else {
159-
allPeers[peerId].ipv4 = true
160-
}
161-
162-
if (peer.complete) {
163-
allPeers[peerId].seeder = true
164-
} else {
165-
allPeers[peerId].leecher = true
166-
}
167-
168-
allPeers[peerId].peerId = peer.peerId
169-
allPeers[peerId].client = peerid(peer.peerId)
170-
})
171-
})
172-
173-
const isSeederOnly = peer => { return peer.seeder && peer.leecher === false }
174-
const isLeecherOnly = peer => { return peer.leecher && peer.seeder === false }
175-
const isSeederAndLeecher = peer => { return peer.seeder && peer.leecher }
176-
const isIPv4 = peer => { return peer.ipv4 }
177-
const isIPv6 = peer => { return peer.ipv6 }
178-
179-
const stats = {
180-
torrents: infoHashes.length,
181-
activeTorrents,
182-
peersAll: Object.keys(allPeers).length,
183-
peersSeederOnly: countPeers(isSeederOnly),
184-
peersLeecherOnly: countPeers(isLeecherOnly),
185-
peersSeederAndLeecher: countPeers(isSeederAndLeecher),
186-
peersIPv4: countPeers(isIPv4),
187-
peersIPv6: countPeers(isIPv6),
188-
clients: groupByClient()
189-
}
190-
191-
if (req.url === '/stats.json' || req.headers.accept === 'application/json') {
192-
res.setHeader('Content-Type', 'application/json')
193-
res.end(JSON.stringify(stats))
194-
} else if (req.url === '/stats') {
195-
res.setHeader('Content-Type', 'text/html')
196-
res.end(`
197-
<h1>${stats.torrents} torrents (${stats.activeTorrents} active)</h1>
198-
<h2>Connected Peers: ${stats.peersAll}</h2>
199-
<h3>Peers Seeding Only: ${stats.peersSeederOnly}</h3>
200-
<h3>Peers Leeching Only: ${stats.peersLeecherOnly}</h3>
201-
<h3>Peers Seeding & Leeching: ${stats.peersSeederAndLeecher}</h3>
202-
<h3>IPv4 Peers: ${stats.peersIPv4}</h3>
203-
<h3>IPv6 Peers: ${stats.peersIPv6}</h3>
204-
<h3>Clients:</h3>
205-
${printClients(stats.clients)}
206-
`.replace(/^\s+/gm, '')) // trim left
207-
}
208-
}
209-
})
210-
}
65+
if (opts.stats !== false) setupStatsRoute(this, onListening);
21166

21267
let num = !!this.http + !!this.udp4 + !!this.udp6
21368
this.num = num

services/statsRoute.js

Lines changed: 79 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
const peerid = require('bittorrent-peerid')
2+
13
const attachHttpServer = require('./attachHttp')
24

35
const get8Chars =
46
peerId => Buffer.from(peerId, "hex")
57
.toString()
68
.substring(0, 8)
79

8-
function addPeerClient( _client, peer) {
10+
function addPeerClient( _clients, peer) {
911
const clientId = peer.client.client;
1012
const client = _clients[clientId] || {};
1113
// If the client is not known show 8 chars from peerId as version
@@ -48,52 +50,75 @@ function printClients(clients) {
4850
return html + "</ul>";
4951
}
5052

51-
function addPeerId(allPeers, peerId) {
52-
// Don't mark the peer as most recently used for stats
53-
const peer = peers.peek(peerId);
53+
const countPeers =
54+
allPeers =>
55+
filterFunction => {
56+
let count = 0
57+
let key
5458

55-
if (peer == null) return; // peers.peek() can evict the peer
59+
for (key in allPeers) {
60+
if (hasOwnProperty.call(allPeers, key) && filterFunction(allPeers[key])) {
61+
count++
62+
} 0
63+
}
5664

57-
allPeers[peerId] = {
58-
ipv6: peer.ip.includes(":"),
59-
ipv4: !peer.ip.includes(":"),
60-
seeder: peer.completee,
61-
leecher: !peer.complete,
62-
peerId: peer.peerId,
63-
client: peerid(peer.peerId),
64-
};
65+
return count
66+
}
6567

66-
return allPeers
67-
}
68+
const addPeer =
69+
peers =>
70+
(allPeers, peerId) => {
71+
// Don't mark the peer as most recently used for stats
72+
const peer = peers.peek(peerId)
6873

69-
function addTorrentStats(allPeers, torrent) {
70-
const keys = torrent.peers.keys;
74+
if (peer == null) return // peers.peek() can evict the peer
7175

72-
const result = keys.reduce(addPeerId, allPeers)
76+
allPeers[peerId] = {
77+
ipv4: false,
78+
ipv6: false,
79+
seeder: false,
80+
leecher: false
81+
}
7382

74-
return result
83+
if (peer.ip.includes(':')) allPeers[peerId].ipv6 = true
84+
if (!peer.ip.includes(':')) allPeers[peerId].ipv4 = true
85+
86+
if (peer.complete) allPeers[peerId].seeder = true
87+
if (!peer.complete) allPeers[peerId].leecher = true
88+
89+
allPeers[peerId].peerId = peer.peerId
90+
allPeers[peerId].client = peerid(peer.peerId)
91+
92+
return allPeers
93+
}
94+
95+
const countActiveTorrents = torrents => {
96+
const hasKeys = (torrent) => (torrent.peers.keys.length > 0)
97+
98+
const activeTorrents = torrents.filter(hasKeys, 0)
99+
return activeTorrents.length
75100
}
76101

102+
const addTorrentKeys = (allPeers, torrent) => {
103+
const peers = torrent.peers
104+
const keys = peers.keys
77105

78-
const countPeersF =
79-
peers =>
80-
filterFunction =>
81-
Object.values(peers).filter(filterFunction).length
106+
const result = keys.reduce(addPeer(peers), allPeers)
82107

83-
const isActive = torrent => (torrent.peers.keys.length > 0)
84-
const countActive = torrents => torrents.filter(isActive).length
85-
const isSeederOnly = peer => (peer.seeder && peer.leecher === false);
86-
const isLeecherOnly = peer => (peer.leecher && peer.seeder === false);
87-
const isSeederAndLeecher = peer => (peer.seeder && peer.leecher);
88-
const isIPv4 = peer => peer.ipv4;
89-
const isIPv6 = peer => peer.ipv6;
108+
return result
109+
}
90110

91-
function getStats(server) {
92-
const torrents = Object.values(server.torrents);
93-
const allPeers = torrents.reduce(addTorrentStats, {})
111+
const getTheStats = server => {
112+
const torrents = Object.values(server.torrents)
113+
const allPeers = torrents.reduce(addTorrentKeys, {})
94114

95-
const countAllPeers = countPeersF(allPeers);
96-
const activeTorrents = countActive(torrents);
115+
const activeTorrents = countActiveTorrents(Object.values(server.torrents))
116+
const isSeederOnly = peer => (peer.seeder && peer.leecher === false)
117+
const isLeecherOnly = peer => (peer.leecher && peer.seeder === false)
118+
const isSeederAndLeecher = peer => (peer.seeder && peer.leecher)
119+
const isIPv4 = peer => peer.ipv4
120+
const isIPv6 = peer => peer.ipv6
121+
const countAllPeers = countPeers(allPeers)
97122

98123
const stats = {
99124
torrents: torrents.length,
@@ -104,34 +129,28 @@ function getStats(server) {
104129
peersSeederAndLeecher: countAllPeers(isSeederAndLeecher),
105130
peersIPv4: countAllPeers(isIPv4),
106131
peersIPv6: countAllPeers(isIPv6),
107-
clients: groupByClient(allPeers),
108-
};
132+
clients: groupByClient(allPeers)
133+
}
109134

110-
return stats;
135+
return stats
111136
}
112137

113-
function setupStatsRoute(server) {
114-
if (!server.http) attachHttpServer(server);
115-
116-
// Http handler for '/stats' route
117-
server.http.on("request", (req, res) => {
118-
if (res.headersSent) return;
138+
function setupStatsRoute(server, onListening) {
139+
if (!server.http) attachHttpServer(server, onListening);
140+
// Http handler for '/stats' route
141+
server.http.on('request', (req, res) => {
142+
if (res.headersSent) return
119143

120-
const stats = getStats(server);
144+
if (req.method === 'GET' && (req.url === '/stats' || req.url === '/stats.json')) {
145+
const stats = getTheStats(server)
121146

122-
if (
123-
req.url === "/stats.json" ||
124-
req.headers.accept === "application/json"
125-
) {
126-
res.setHeader("Content-Type", "application/json");
127-
res.end(JSON.stringify(stats));
128-
}
129-
else if (req.url === "/stats") {
130-
const stats = getStats(server);
147+
if (req.url === '/stats.json' || req.headers.accept === 'application/json') {
131148

132-
res.setHeader("Content-Type", "text/html");
133-
res.end(
134-
`
149+
res.setHeader('Content-Type', 'application/json')
150+
res.end(JSON.stringify(stats))
151+
} else if (req.url === '/stats') {
152+
res.setHeader('Content-Type', 'text/html')
153+
res.end(`
135154
<h1>${stats.torrents} torrents (${stats.activeTorrents} active)</h1>
136155
<h2>Connected Peers: ${stats.peersAll}</h2>
137156
<h3>Peers Seeding Only: ${stats.peersSeederOnly}</h3>
@@ -141,10 +160,10 @@ function setupStatsRoute(server) {
141160
<h3>IPv6 Peers: ${stats.peersIPv6}</h3>
142161
<h3>Clients:</h3>
143162
${printClients(stats.clients)}
144-
`.replace(/^\s+/gm, "")
145-
); // trim left
163+
`.replace(/^\s+/gm, '')) // trim left
164+
}
146165
}
147-
});
166+
})
148167
}
149168

150169
module.exports = setupStatsRoute

0 commit comments

Comments
 (0)