11exports . Client = Client
2- // TODO: exports.Server = Server
3- // TODO: support connecting to UDP trackers (http://www.bittorrent.org/beps/bep_0015.html)
2+ exports . Server = Server
43
54var bncode = require ( 'bncode' )
65var compact2string = require ( 'compact2string' )
76var EventEmitter = require ( 'events' ) . EventEmitter
87var extend = require ( 'extend.js' )
8+ var hat = require ( 'hat' )
99var http = require ( 'http' )
1010var inherits = require ( 'inherits' )
1111var querystring = require ( 'querystring' )
12+ var string2compact = require ( 'string2compact' )
1213
1314inherits ( Client , EventEmitter )
1415
1516function Client ( peerId , port , torrent , opts ) {
1617 var self = this
17- if ( ! ( self instanceof Client ) ) return new Client ( peerId , port , torrent )
18+ if ( ! ( self instanceof Client ) ) return new Client ( peerId , port , torrent , opts )
1819 EventEmitter . call ( self )
1920 self . _opts = opts || { }
2021
@@ -143,7 +144,7 @@ Client.prototype._handleResponse = function (data, announce) {
143144 if ( interval && ! self . _opts . interval && self . _intervalMs !== 0 ) {
144145 // use the interval the tracker recommends, UNLESS the user manually specifies an
145146 // interval they want to use
146- self . setInterval ( interval )
147+ self . setInterval ( interval * 1000 )
147148 }
148149
149150 var trackerId = data [ 'tracker id' ]
@@ -173,9 +174,180 @@ Client.prototype._handleResponse = function (data, announce) {
173174 }
174175}
175176
177+ inherits ( Server , EventEmitter )
178+
179+ function Server ( opts ) {
180+ var self = this
181+ if ( ! ( self instanceof Server ) ) return new Server ( opts )
182+ EventEmitter . call ( self )
183+ opts = opts || { }
184+
185+ self . _interval = opts . interval
186+ ? opts . interval / 1000
187+ : 10 * 60 // 10 min (in secs)
188+
189+ self . _trustProxy = ! ! opts . trustProxy
190+
191+ self . _swarms = { }
192+
193+ self . _server = http . createServer ( )
194+ self . _server . on ( 'request' , self . _onRequest . bind ( self ) )
195+ }
196+
197+ Server . prototype . listen = function ( port ) {
198+ var self = this
199+ self . _server . listen ( port , function ( ) {
200+ self . emit ( 'listening' )
201+ } )
202+ }
203+
204+ Server . prototype . close = function ( cb ) {
205+ var self = this
206+ self . _server . close ( cb )
207+ }
208+
209+ Server . prototype . _onRequest = function ( req , res ) {
210+ var self = this
211+ var s = req . url . split ( '?' )
212+ if ( s [ 0 ] === '/announce' ) {
213+ var params = querystring . parse ( s [ 1 ] )
214+
215+ var ip = self . _trustProxy
216+ ? req . headers [ 'x-forwarded-for' ] || req . connection . remoteAddress
217+ : req . connection . remoteAddress
218+ var port = Number ( params . port )
219+ var addr = ip + ':' + port
220+
221+ var infoHash = bytewiseDecodeURIComponent ( params . info_hash )
222+ var peerId = bytewiseDecodeURIComponent ( params . peer_id )
223+
224+ var swarm = self . _swarms [ infoHash ]
225+ if ( ! swarm ) {
226+ swarm = self . _swarms [ infoHash ] = {
227+ complete : 0 ,
228+ incomplete : 0 ,
229+ peers : { }
230+ }
231+ }
232+ var peer = swarm . peers [ addr ]
233+ switch ( params . event ) {
234+ case 'started' :
235+ if ( peer ) {
236+ return
237+ }
238+
239+ var left = Number ( params . left )
240+
241+ if ( left === 0 ) {
242+ swarm . complete += 1
243+ } else {
244+ swarm . incomplete += 1
245+ }
246+
247+ peer = swarm . peers [ addr ] = {
248+ ip : ip ,
249+ port : port ,
250+ peerId : peerId
251+ }
252+ self . emit ( 'start' , addr , params )
253+ break
254+
255+ case 'stopped' :
256+ if ( ! peer ) {
257+ return
258+ }
259+
260+ if ( peer . complete ) {
261+ swarm . complete -= 1
262+ } else {
263+ swarm . incomplete -= 1
264+ }
265+
266+ delete swarm . peers [ addr ]
267+
268+ self . emit ( 'stop' , addr , params )
269+ break
270+
271+ case 'completed' :
272+ if ( ! peer || peer . complete ) {
273+ return
274+ }
275+ peer . complete = true
276+
277+ swarm . complete += 1
278+ swarm . incomplete -= 1
279+
280+ self . emit ( 'complete' , addr , params )
281+ break
282+
283+ case '' : // update
284+ case undefined :
285+ if ( ! peer ) {
286+ return
287+ }
288+
289+ self . emit ( 'update' , addr , params )
290+ break
291+
292+ default :
293+ res . end ( bncode . encode ( {
294+ 'failure reason' : 'unexpected event: ' + params . event
295+ } ) )
296+ self . emit ( 'error' , new Error ( 'unexpected event: ' + params . event ) )
297+ return // early return
298+ }
299+
300+ // send peers
301+ var peers = Number ( params . compact ) === 1
302+ ? self . _getPeersCompact ( swarm )
303+ : self . _getPeers ( swarm )
304+
305+ res . end ( bncode . encode ( {
306+ complete : swarm . complete ,
307+ incomplete : swarm . incomplete ,
308+ peers : peers ,
309+ interval : self . _interval
310+ } ) )
311+ }
312+ }
313+
314+ Server . prototype . _getPeers = function ( swarm ) {
315+ var self = this
316+ var peers = [ ]
317+ for ( var peerId in swarm . peers ) {
318+ var peer = swarm . peers [ peerId ]
319+ peers . push ( {
320+ 'peer id' : peer . peerId ,
321+ ip : peer . ip ,
322+ port : peer . port
323+ } )
324+ }
325+ return peers
326+ }
327+
328+ Server . prototype . _getPeersCompact = function ( swarm ) {
329+ var self = this
330+ var addrs = Object . keys ( swarm . peers ) . map ( function ( peerId ) {
331+ var peer = swarm . peers [ peerId ]
332+ return peer . ip + ':' + peer . port
333+ } )
334+ return string2compact ( addrs )
335+ }
336+
337+ //
338+ // HELPERS
339+ //
340+
176341function bytewiseEncodeURIComponent ( buf ) {
177342 if ( ! Buffer . isBuffer ( buf ) ) {
178343 buf = new Buffer ( buf , 'hex' )
179344 }
180- return escape ( buf . toString ( 'binary' ) )
181- }
345+ return encodeURIComponent ( buf . toString ( 'binary' ) )
346+ }
347+
348+ function bytewiseDecodeURIComponent ( str ) {
349+ if ( Buffer . isBuffer ( str ) ) {
350+ str = str . toString ( 'utf8' )
351+ }
352+ return ( new Buffer ( decodeURIComponent ( str ) , 'binary' ) . toString ( 'hex' ) )
353+ }
0 commit comments