@@ -10,6 +10,13 @@ var http = require('http')
1010var inherits = require ( 'inherits' )
1111var querystring = require ( 'querystring' )
1212var string2compact = require ( 'string2compact' )
13+ var dgram = require ( 'dgram' )
14+ var parseUrl = require ( 'url' ) . parse
15+
16+ var CONNECTION_ID = Buffer . concat ( [ fromInt32 ( 0x417 ) , fromInt32 ( 0x27101980 ) ] ) ;
17+ var CONNECT = fromInt32 ( 0 ) ;
18+ var ANNOUNCE = fromInt32 ( 1 ) ;
19+ var EVENTS = { completed :1 , started :2 , stopped :3 } ;
1320
1421inherits ( Client , EventEmitter )
1522
@@ -77,6 +84,77 @@ Client.prototype.setInterval = function (intervalMs) {
7784 }
7885}
7986
87+ Client . prototype . _requestUdp = function ( url , opts ) {
88+ var parsed = parseUrl ( url )
89+ var socket = dgram . createSocket ( 'udp4' )
90+ var self = this
91+
92+ var timeout = setTimeout ( function ( ) {
93+ socket . close ( )
94+ } , 5000 )
95+
96+ socket . on ( 'error' , function ( err ) {
97+ self . emit ( 'error' , err )
98+ } )
99+
100+ socket . on ( 'message' , function ( message , rinfo ) {
101+ var action = message . readUInt32BE ( 0 )
102+
103+ switch ( action ) {
104+ case 0 :
105+ if ( message . length < 16 ) return self . emit ( 'error' , new Error ( 'invalid udp handshake' ) )
106+ announce ( message . slice ( 8 , 16 ) , opts )
107+ return ;
108+
109+ case 1 :
110+ if ( message . length < 20 ) return self . emit ( 'error' , new Error ( 'invalid announce message' ) )
111+
112+ self . emit ( 'update' , {
113+ announce : url ,
114+ complete : message . readUInt32BE ( 16 ) ,
115+ incomplete : message . readUInt32BE ( 12 )
116+ } )
117+
118+ for ( var i = 20 ; i < message . length ; i += 6 ) {
119+ self . emit ( 'peer' , compact2string ( message . slice ( i , i + 6 ) ) )
120+ }
121+
122+ clearTimeout ( timeout )
123+ socket . close ( )
124+ }
125+ } ) ;
126+
127+ function announce ( connectionId , opts ) {
128+ opts = opts || { }
129+
130+ send ( Buffer . concat ( [
131+ connectionId ,
132+ ANNOUNCE ,
133+ new Buffer ( hat ( 32 ) , 'hex' ) ,
134+ new Buffer ( self . _infoHash , 'hex' ) ,
135+ new Buffer ( self . _peerId , 'utf-8' ) ,
136+ fromInt32 ( 0 ) , fromInt32 ( opts . downloaded || 0 ) , // fromUint32(0) to expand this to 64bit
137+ fromInt32 ( 0 ) , fromInt32 ( opts . left || 0 ) ,
138+ fromInt32 ( 0 ) , fromInt32 ( opts . uploaded || 0 ) ,
139+ fromInt32 ( EVENTS [ opts . event ] || 0 ) ,
140+ fromInt32 ( 0 ) ,
141+ fromInt32 ( 0 ) ,
142+ fromInt32 ( self . _numWant ) ,
143+ fromInt16 ( self . _port || 0 )
144+ ] ) ) ;
145+ } ;
146+
147+ function send ( message ) {
148+ socket . send ( message , 0 , message . length , parsed . port , parsed . hostname )
149+ } ;
150+
151+ send ( Buffer . concat ( [
152+ CONNECTION_ID ,
153+ CONNECT ,
154+ new Buffer ( hat ( 32 ) , 'hex' )
155+ ] ) ) ;
156+ } ;
157+
80158/**
81159 * Send a request to the tracker
82160 */
@@ -100,6 +178,8 @@ Client.prototype._request = function (opts) {
100178 var q = querystring . stringify ( opts )
101179
102180 self . _announce . forEach ( function ( announce ) {
181+ if ( announce . indexOf ( 'udp:' ) === 0 ) return self . _requestUdp ( announce , opts )
182+
103183 var url = announce + '?' + q
104184 var req = http . get ( url , function ( res ) {
105185 var data = ''
@@ -356,6 +436,18 @@ Server.prototype._getPeersCompact = function (swarm) {
356436// HELPERS
357437//
358438
439+ function fromInt16 ( n ) {
440+ var buf = new Buffer ( 2 ) ;
441+ buf . writeUInt16BE ( n , 0 ) ;
442+ return buf ;
443+ } ;
444+
445+ function fromInt32 ( n ) {
446+ var buf = new Buffer ( 4 ) ;
447+ buf . writeUInt32BE ( n , 0 ) ;
448+ return buf ;
449+ } ;
450+
359451function bytewiseEncodeURIComponent ( buf ) {
360452 if ( ! Buffer . isBuffer ( buf ) ) {
361453 buf = new Buffer ( buf , 'hex' )
0 commit comments