Skip to content

Commit b72960d

Browse files
committed
server: split out Swarm
1 parent 1c754a2 commit b72960d

File tree

3 files changed

+164
-124
lines changed

3 files changed

+164
-124
lines changed

lib/swarm.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
var debug = require('debug')('bittorrent-tracker')
2+
var string2compact = require('string2compact')
3+
4+
module.exports = Swarm
5+
6+
// Regard this as the default implementation of an interface that you
7+
// need to support when overriding Server.getSwarm()
8+
function Swarm (infoHash, server) {
9+
this.peers = {}
10+
this.complete = 0
11+
this.incomplete = 0
12+
this.emit = server.emit.bind(server)
13+
}
14+
15+
Swarm.prototype.announce = function (params, cb) {
16+
var self = this
17+
var peer = self.peers[params.addr]
18+
19+
var start = function () {
20+
if (peer) {
21+
debug('unexpected `started` event from peer that is already in swarm')
22+
return update() // treat as an update
23+
}
24+
if (params.left === 0) self.complete += 1
25+
else self.incomplete += 1
26+
peer = self.peers[params.addr] = {
27+
ip: params.ip,
28+
port: params.port,
29+
peerId: params.peer_id
30+
}
31+
self.emit('start', params.addr)
32+
}
33+
34+
var stop = function () {
35+
if (!peer) {
36+
debug('unexpected `stopped` event from peer that is not in swarm')
37+
return // do nothing
38+
}
39+
if (peer.complete) self.complete -= 1
40+
else self.incomplete -= 1
41+
self.peers[params.addr] = null
42+
self.emit('stop', params.addr)
43+
}
44+
45+
var complete = function () {
46+
if (!peer) {
47+
debug('unexpected `completed` event from peer that is not in swarm')
48+
return start() // treat as a start
49+
}
50+
if (peer.complete) {
51+
debug('unexpected `completed` event from peer that is already marked as completed')
52+
return // do nothing
53+
}
54+
self.complete += 1
55+
self.incomplete -= 1
56+
peer.complete = true
57+
self.emit('complete', params.addr)
58+
}
59+
60+
var update = function () {
61+
if (!peer) {
62+
debug('unexpected `update` event from peer that is not in swarm')
63+
return start() // treat as a start
64+
}
65+
self.emit('update', params.addr)
66+
}
67+
68+
switch (params.event) {
69+
case 'started':
70+
start()
71+
break
72+
case 'stopped':
73+
stop()
74+
break
75+
case 'completed':
76+
complete()
77+
break
78+
case '': case undefined: case 'empty': case 'update': // update
79+
update()
80+
break
81+
default:
82+
return cb(new Error('invalid event')) // early return
83+
}
84+
85+
if (params.left === 0 && peer) peer.complete = true
86+
87+
// send peers
88+
var peers = params.compact === 1
89+
? self._getPeersCompact(params.numwant)
90+
: self._getPeers(params.numwant)
91+
92+
cb(null, {
93+
complete: this.complete,
94+
incomplete: this.incomplete,
95+
peers: peers
96+
})
97+
}
98+
99+
Swarm.prototype._getPeers = function (numwant) {
100+
var peers = []
101+
for (var peerId in this.peers) {
102+
if (peers.length >= numwant) break
103+
var peer = this.peers[peerId]
104+
if (!peer) continue // ignore null values
105+
peers.push({
106+
'peer id': peer.peerId,
107+
ip: peer.ip,
108+
port: peer.port
109+
})
110+
}
111+
return peers
112+
}
113+
114+
Swarm.prototype._getPeersCompact = function (numwant) {
115+
var peers = []
116+
117+
for (var peerId in this.peers) {
118+
if (peers.length >= numwant) break
119+
var peer = this.peers[peerId]
120+
if (!peer) continue // ignore null values
121+
peers.push(peer.ip + ':' + peer.port)
122+
}
123+
124+
return string2compact(peers)
125+
}
126+
127+
128+
Swarm.prototype.scrape = function (infoHash, params, cb) {
129+
cb(null, {
130+
complete: this.complete,
131+
incomplete: this.incomplete
132+
})
133+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"ip": "^0.3.0",
2727
"once": "^1.3.0",
2828
"portfinder": "^0.2.1",
29+
"run-series": "^1.0.2",
2930
"string2compact": "^1.1.1"
3031
},
3132
"devDependencies": {

server.js

Lines changed: 30 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
module.exports = Server
22

33
var bencode = require('bencode')
4-
var common = require('./lib/common')
54
var debug = require('debug')('bittorrent-tracker')
65
var dgram = require('dgram')
76
var EventEmitter = require('events').EventEmitter
87
var http = require('http')
98
var inherits = require('inherits')
109
var ipLib = require('ip')
1110
var portfinder = require('portfinder')
12-
var string2compact = require('string2compact')
11+
var series = require('run-series')
1312

13+
var common = require('./lib/common')
14+
var Swarm = require('./lib/swarm')
1415
var parseHttpRequest = require('./lib/parse_http')
1516
var parseUdpRequest = require('./lib/parse_udp')
1617

@@ -119,11 +120,7 @@ Server.prototype.getSwarm = function (binaryInfoHash) {
119120
if (Buffer.isBuffer(binaryInfoHash)) binaryInfoHash = binaryInfoHash.toString('binary')
120121
var swarm = self.torrents[binaryInfoHash]
121122
if (!swarm) {
122-
swarm = self.torrents[binaryInfoHash] = {
123-
complete: 0,
124-
incomplete: 0,
125-
peers: {}
126-
}
123+
swarm = self.torrents[binaryInfoHash] = new Swarm(binaryInfoHash, this)
127124
}
128125
return swarm
129126
}
@@ -214,89 +211,13 @@ Server.prototype._onRequest = function (params, cb) {
214211

215212
Server.prototype._onAnnounce = function (params, cb) {
216213
var self = this
217-
218214
var swarm = self.getSwarm(params.info_hash)
219-
var peer = swarm.peers[params.addr]
220-
221-
var start = function () {
222-
if (peer) {
223-
debug('unexpected `started` event from peer that is already in swarm')
224-
return update() // treat as an update
215+
swarm.announce(params, function (err, response) {
216+
if (response) {
217+
if (!response.action) response.action = common.ACTIONS.ANNOUNCE
218+
if (!response.intervalMs) response.intervalMs = self._intervalMs
225219
}
226-
if (params.left === 0) swarm.complete += 1
227-
else swarm.incomplete += 1
228-
peer = swarm.peers[params.addr] = {
229-
ip: params.ip,
230-
port: params.port,
231-
peerId: params.peer_id
232-
}
233-
self.emit('start', params.addr)
234-
}
235-
236-
var stop = function () {
237-
if (!peer) {
238-
debug('unexpected `stopped` event from peer that is not in swarm')
239-
return // do nothing
240-
}
241-
if (peer.complete) swarm.complete -= 1
242-
else swarm.incomplete -= 1
243-
swarm.peers[params.addr] = null
244-
self.emit('stop', params.addr)
245-
}
246-
247-
var complete = function () {
248-
if (!peer) {
249-
debug('unexpected `completed` event from peer that is not in swarm')
250-
return start() // treat as a start
251-
}
252-
if (peer.complete) {
253-
debug('unexpected `completed` event from peer that is already marked as completed')
254-
return // do nothing
255-
}
256-
swarm.complete += 1
257-
swarm.incomplete -= 1
258-
peer.complete = true
259-
self.emit('complete', params.addr)
260-
}
261-
262-
var update = function () {
263-
if (!peer) {
264-
debug('unexpected `update` event from peer that is not in swarm')
265-
return start() // treat as a start
266-
}
267-
self.emit('update', params.addr)
268-
}
269-
270-
switch (params.event) {
271-
case 'started':
272-
start()
273-
break
274-
case 'stopped':
275-
stop()
276-
break
277-
case 'completed':
278-
complete()
279-
break
280-
case '': case undefined: case 'empty': case 'update': // update
281-
update()
282-
break
283-
default:
284-
return cb(new Error('invalid event')) // early return
285-
}
286-
287-
if (params.left === 0 && peer) peer.complete = true
288-
289-
// send peers
290-
var peers = params.compact === 1
291-
? self._getPeersCompact(swarm, params.numwant)
292-
: self._getPeers(swarm, params.numwant)
293-
294-
cb(null, {
295-
action: common.ACTIONS.ANNOUNCE,
296-
complete: swarm.complete,
297-
incomplete: swarm.incomplete,
298-
peers: peers,
299-
intervalMs: self._intervalMs
220+
cb(err, response)
300221
})
301222
}
302223

@@ -324,46 +245,31 @@ Server.prototype._onScrape = function (params, cb) {
324245
min_request_interval: self._intervalMs
325246
}
326247
}
327-
328-
params.info_hash.some(function (infoHash) {
248+
249+
series(params.info_hash.map(function (infoHash) {
329250
var swarm = self.getSwarm(infoHash)
330-
331-
response.files[infoHash] = {
332-
complete: swarm.complete,
333-
incomplete: swarm.incomplete,
334-
downloaded: swarm.complete // TODO: this only provides a lower-bound
251+
return function (cb) {
252+
swarm.scrape(infoHash, params, function (err, scrapeInfo) {
253+
cb(err, scrapeInfo && {
254+
infoHash: infoHash,
255+
complete: scrapeInfo.complete || 0,
256+
incomplete: scrapeInfo.incomplete || 0
257+
})
258+
})
335259
}
336-
})
337-
338-
cb(null, response)
339-
}
340-
341-
Server.prototype._getPeers = function (swarm, numwant) {
342-
var peers = []
343-
for (var peerId in swarm.peers) {
344-
if (peers.length >= numwant) break
345-
var peer = swarm.peers[peerId]
346-
if (!peer) continue // ignore null values
347-
peers.push({
348-
'peer id': peer.peerId,
349-
ip: peer.ip,
350-
port: peer.port
260+
}), function (err, results) {
261+
if (err) return cb(err)
262+
263+
results.forEach(function (result) {
264+
response.files[result.infoHash] = {
265+
complete: result.complete,
266+
incomplete: result.incomplete,
267+
downloaded: result.complete // TODO: this only provides a lower-bound
268+
}
351269
})
352-
}
353-
return peers
354-
}
355270

356-
Server.prototype._getPeersCompact = function (swarm, numwant) {
357-
var peers = []
358-
359-
for (var peerId in swarm.peers) {
360-
if (peers.length >= numwant) break
361-
var peer = swarm.peers[peerId]
362-
if (!peer) continue // ignore null values
363-
peers.push(peer.ip + ':' + peer.port)
364-
}
365-
366-
return string2compact(peers)
271+
cb(null, response)
272+
})
367273
}
368274

369275

0 commit comments

Comments
 (0)