11module . exports = Server
2+ module . exports . parseHttpRequest = parseHttpRequest
23
34var bencode = require ( 'bencode' )
45var bufferEqual = require ( 'buffer-equal' )
@@ -138,48 +139,43 @@ Server.prototype._getSwarm = function (binaryInfoHash) {
138139
139140Server . prototype . _onHttpRequest = function ( req , res ) {
140141 var self = this
141- var s = req . url . split ( '?' )
142- var params = common . querystringParse ( s [ 1 ] )
143- var response
144- if ( s [ 0 ] === '/announce' ) {
145- var infoHash = typeof params . info_hash === 'string' && params . info_hash
146- var peerId = typeof params . peer_id === 'string' && common . binaryToUtf8 ( params . peer_id )
147- var port = Number ( params . port )
148142
149- if ( ! infoHash ) return error ( 'invalid info_hash' )
150- if ( infoHash . length !== 20 ) return error ( 'invalid info_hash' )
151- if ( ! peerId ) return error ( 'invalid peer_id' )
152- if ( peerId . length !== 20 ) return error ( 'invalid peer_id' )
153- if ( ! port ) return error ( 'invalid port' )
154-
155- var left = Number ( params . left )
156- var compact = Number ( params . compact )
157-
158- var ip = self . _trustProxy
159- ? req . headers [ 'x-forwarded-for' ] || req . connection . remoteAddress
160- : req . connection . remoteAddress . replace ( REMOVE_IPV6_RE , '' ) // force ipv4
161- var addr = ip + ':' + port
162- var swarm = self . _getSwarm ( infoHash )
163- var peer = swarm . peers [ addr ]
143+ var params
144+ try {
145+ params = parseHttpRequest ( req , {
146+ trustProxy : self . _trustProxy
147+ } )
148+ } catch ( err ) {
149+ debug ( 'sent error %s' , err . message )
150+ res . end ( bencode . encode ( {
151+ 'failure reason' : err . message
152+ } ) )
164153
165- var numWant = Math . min (
166- Number ( params . numwant ) || NUM_ANNOUNCE_PEERS ,
167- MAX_ANNOUNCE_PEERS
168- )
154+ // even though it's an error for the client, it's just a warning for the server.
155+ // don't crash the server because a client sent bad data :)
156+ self . emit ( 'warning' , err )
157+
158+ return
159+ }
160+
161+ var response
162+ if ( params && params . request === 'announce' ) {
163+ var swarm = self . _getSwarm ( params . info_hash )
164+ var peer = swarm . peers [ params . addr ]
169165
170166 var start = function ( ) {
171167 if ( peer ) {
172168 debug ( 'unexpected `started` event from peer that is already in swarm' )
173169 return update ( ) // treat as an update
174170 }
175- if ( left === 0 ) swarm . complete += 1
171+ if ( params . left === 0 ) swarm . complete += 1
176172 else swarm . incomplete += 1
177- peer = swarm . peers [ addr ] = {
178- ip : ip ,
179- port : port ,
180- peerId : peerId
173+ peer = swarm . peers [ params . addr ] = {
174+ ip : params . ip ,
175+ port : params . port ,
176+ peerId : params . peer_id
181177 }
182- self . emit ( 'start' , addr )
178+ self . emit ( 'start' , params . addr )
183179 }
184180
185181 var stop = function ( ) {
@@ -189,8 +185,8 @@ Server.prototype._onHttpRequest = function (req, res) {
189185 }
190186 if ( peer . complete ) swarm . complete -= 1
191187 else swarm . incomplete -= 1
192- swarm . peers [ addr ] = null
193- self . emit ( 'stop' , addr )
188+ swarm . peers [ params . addr ] = null
189+ self . emit ( 'stop' , params . addr )
194190 }
195191
196192 var complete = function ( ) {
@@ -205,15 +201,15 @@ Server.prototype._onHttpRequest = function (req, res) {
205201 swarm . complete += 1
206202 swarm . incomplete -= 1
207203 peer . complete = true
208- self . emit ( 'complete' , addr )
204+ self . emit ( 'complete' , params . addr )
209205 }
210206
211207 var update = function ( ) {
212208 if ( ! peer ) {
213209 debug ( 'unexpected `update` event from peer that is not in swarm' )
214210 return start ( ) // treat as a start
215211 }
216- self . emit ( 'update' , addr )
212+ self . emit ( 'update' , params . addr )
217213 }
218214
219215 switch ( params . event ) {
@@ -233,12 +229,12 @@ Server.prototype._onHttpRequest = function (req, res) {
233229 return error ( 'invalid event' ) // early return
234230 }
235231
236- if ( left === 0 && peer ) peer . complete = true
232+ if ( params . left === 0 && peer ) peer . complete = true
237233
238234 // send peers
239- var peers = compact === 1
240- ? self . _getPeersCompact ( swarm , numWant )
241- : self . _getPeers ( swarm , numWant )
235+ var peers = params . compact === 1
236+ ? self . _getPeersCompact ( swarm , params . numwant )
237+ : self . _getPeers ( swarm , params . numwant )
242238
243239 response = {
244240 complete : swarm . complete ,
@@ -250,11 +246,12 @@ Server.prototype._onHttpRequest = function (req, res) {
250246 res . end ( bencode . encode ( response ) )
251247 debug ( 'sent response %s' , response )
252248
253- } else if ( s [ 0 ] === '/ scrape' ) { // unofficial scrape message
249+ } else if ( params . request === 'scrape' ) { // unofficial scrape message
254250 if ( typeof params . info_hash === 'string' ) {
255251 params . info_hash = [ params . info_hash ]
256252 } else if ( params . info_hash == null ) {
257253 // if info_hash param is omitted, stats for all torrents are returned
254+ // TODO: make this configurable!
258255 params . info_hash = Object . keys ( self . torrents )
259256 }
260257
@@ -268,11 +265,6 @@ Server.prototype._onHttpRequest = function (req, res) {
268265 }
269266
270267 params . info_hash . some ( function ( infoHash ) {
271- if ( infoHash . length !== 20 ) {
272- error ( 'invalid info_hash' )
273- return true // early return
274- }
275-
276268 var swarm = self . _getSwarm ( infoHash )
277269
278270 response . files [ infoHash ] = {
@@ -338,7 +330,7 @@ Server.prototype._onUdpRequest = function (msg, rinfo) {
338330 var event = msg . readUInt32BE ( 80 )
339331 var ip = msg . readUInt32BE ( 84 ) // optional
340332 var key = msg . readUInt32BE ( 88 ) // TODO: what is this for?
341- var numWant = msg . readUInt32BE ( 92 ) // optional
333+ var numwant = msg . readUInt32BE ( 92 ) // optional
342334 var port = msg . readUInt16BE ( 96 ) // optional
343335
344336 if ( ip ) {
@@ -358,7 +350,7 @@ Server.prototype._onUdpRequest = function (msg, rinfo) {
358350
359351 // never send more than MAX_ANNOUNCE_PEERS or else the UDP packet will get bigger than
360352 // 512 bytes which is not safe
361- numWant = Math . min ( numWant || NUM_ANNOUNCE_PEERS , MAX_ANNOUNCE_PEERS )
353+ numwant = Math . min ( numwant || NUM_ANNOUNCE_PEERS , MAX_ANNOUNCE_PEERS )
362354
363355 var start = function ( ) {
364356 if ( peer ) {
@@ -429,7 +421,7 @@ Server.prototype._onUdpRequest = function (msg, rinfo) {
429421 if ( left === 0 && peer ) peer . complete = true
430422
431423 // send peers
432- var peers = self . _getPeersCompact ( swarm , numWant )
424+ var peers = self . _getPeersCompact ( swarm , numwant )
433425
434426 send ( Buffer . concat ( [
435427 common . toUInt32 ( common . ACTIONS . ANNOUNCE ) ,
@@ -479,10 +471,10 @@ Server.prototype._onUdpRequest = function (msg, rinfo) {
479471 }
480472}
481473
482- Server . prototype . _getPeers = function ( swarm , numWant ) {
474+ Server . prototype . _getPeers = function ( swarm , numwant ) {
483475 var peers = [ ]
484476 for ( var peerId in swarm . peers ) {
485- if ( peers . length >= numWant ) break
477+ if ( peers . length >= numwant ) break
486478 var peer = swarm . peers [ peerId ]
487479 if ( ! peer ) continue // ignore null values
488480 peers . push ( {
@@ -494,11 +486,11 @@ Server.prototype._getPeers = function (swarm, numWant) {
494486 return peers
495487}
496488
497- Server . prototype . _getPeersCompact = function ( swarm , numWant ) {
489+ Server . prototype . _getPeersCompact = function ( swarm , numwant ) {
498490 var peers = [ ]
499491
500492 for ( var peerId in swarm . peers ) {
501- if ( peers . length >= numWant ) break
493+ if ( peers . length >= numwant ) break
502494 var peer = swarm . peers [ peerId ]
503495 if ( ! peer ) continue // ignore null values
504496 peers . push ( peer . ip + ':' + peer . port )
@@ -507,6 +499,61 @@ Server.prototype._getPeersCompact = function (swarm, numWant) {
507499 return string2compact ( peers )
508500}
509501
502+
503+ function parseHttpRequest ( req , options ) {
504+ var s = req . url . split ( '?' )
505+ var params = common . querystringParse ( s [ 1 ] )
506+
507+ if ( s [ 0 ] === '/announce' ) {
508+ params . request = 'announce'
509+
510+ params . peer_id = typeof params . peer_id === 'string' && common . binaryToUtf8 ( params . peer_id )
511+ params . port = Number ( params . port )
512+
513+ if ( typeof params . info_hash !== 'string' ) throw new Error ( 'invalid info_hash' )
514+ if ( params . info_hash . length !== 20 ) throw new Error ( 'invalid info_hash length' )
515+ if ( typeof params . peer_id !== 'string' ) throw new Error ( 'invalid peer_id' )
516+ if ( params . peer_id . length !== 20 ) throw new Error ( 'invalid peer_id length' )
517+ if ( ! params . port ) throw new Error ( 'invalid port' )
518+
519+ params . left = Number ( params . left )
520+ params . compact = Number ( params . compact )
521+
522+ params . ip = options . trustProxy
523+ ? req . headers [ 'x-forwarded-for' ] || req . connection . remoteAddress
524+ : req . connection . remoteAddress . replace ( REMOVE_IPV6_RE , '' ) // force ipv4
525+ params . addr = params . ip + ':' + params . port // TODO: ipv6 brackets?
526+
527+ params . numwant = Math . min (
528+ Number ( params . numwant ) || NUM_ANNOUNCE_PEERS ,
529+ MAX_ANNOUNCE_PEERS
530+ )
531+
532+ return params
533+ } else if ( s [ 0 ] === '/scrape' ) { // unofficial scrape message
534+ params . request = 'scrape'
535+
536+ if ( typeof params . info_hash === 'string' ) {
537+ params . info_hash = [ params . info_hash ]
538+ }
539+
540+ if ( params . info_hash ) {
541+ if ( ! Array . isArray ( params . info_hash ) ) throw new Error ( 'invalid info_hash' )
542+
543+ params . info_hash . some ( function ( infoHash ) {
544+ if ( infoHash . length !== 20 ) {
545+ throw new Error ( 'invalid info_hash' )
546+ }
547+ } )
548+ }
549+
550+ return params
551+ } else {
552+ return null
553+ }
554+ }
555+
556+
510557// HELPER FUNCTIONS
511558
512559var TWO_PWR_32 = ( 1 << 16 ) * 2
0 commit comments