diff --git a/index.js b/index.js index d3b19ac..95c137e 100644 --- a/index.js +++ b/index.js @@ -13,10 +13,14 @@ var string2compact = require('string2compact') var dgram = require('dgram') var parseUrl = require('url').parse -var CONNECTION_ID = Buffer.concat([fromInt32(0x417), fromInt32(0x27101980)]); -var CONNECT = fromInt32(0); -var ANNOUNCE = fromInt32(1); -var EVENTS = {completed:1, started:2, stopped:3}; +var CONNECTION_ID = Buffer.concat([toUInt32(0x417), toUInt32(0x27101980)]) +var CONNECT = toUInt32(0) +var ANNOUNCE = toUInt32(1) +var EVENTS = { + completed: 1, + started: 2, + stopped: 3 +} inherits(Client, EventEmitter) @@ -84,77 +88,6 @@ Client.prototype.setInterval = function (intervalMs) { } } -Client.prototype._requestUdp = function(url, opts) { - var parsed = parseUrl(url) - var socket = dgram.createSocket('udp4') - var self = this - - var timeout = setTimeout(function() { - socket.close() - }, 5000) - - socket.on('error', function(err) { - self.emit('error', err) - }) - - socket.on('message', function(message, rinfo) { - var action = message.readUInt32BE(0) - - switch (action) { - case 0: - if (message.length < 16) return self.emit('error', new Error('invalid udp handshake')) - announce(message.slice(8, 16), opts) - return; - - case 1: - if (message.length < 20) return self.emit('error', new Error('invalid announce message')) - - self.emit('update', { - announce: url, - complete: message.readUInt32BE(16), - incomplete: message.readUInt32BE(12) - }) - - for (var i = 20; i < message.length; i += 6) { - self.emit('peer', compact2string(message.slice(i, i+6))) - } - - clearTimeout(timeout) - socket.close() - } - }); - - function announce(connectionId, opts) { - opts = opts || {} - - send(Buffer.concat([ - connectionId, - ANNOUNCE, - new Buffer(hat(32), 'hex'), - new Buffer(self._infoHash, 'hex'), - new Buffer(self._peerId, 'utf-8'), - fromInt32(0), fromInt32(opts.downloaded || 0), // fromUint32(0) to expand this to 64bit - fromInt32(0), fromInt32(opts.left || 0), - fromInt32(0), fromInt32(opts.uploaded || 0), - fromInt32(EVENTS[opts.event] || 0), - fromInt32(0), - fromInt32(0), - fromInt32(self._numWant), - fromInt16(self._port || 0) - ])); - }; - - function send(message) { - socket.send(message, 0, message.length, parsed.port, parsed.hostname) - }; - - send(Buffer.concat([ - CONNECTION_ID, - CONNECT, - new Buffer(hat(32), 'hex') - ])); -}; - /** * Send a request to the tracker */ @@ -175,34 +108,115 @@ Client.prototype._request = function (opts) { opts.trackerid = self._trackerId } - var q = querystring.stringify(opts) - - self._announce.forEach(function (announce) { - if (announce.indexOf('udp:') === 0) return self._requestUdp(announce, opts) - - var url = announce + '?' + q - var req = http.get(url, function (res) { - var data = '' - if (res.statusCode !== 200) { - res.resume() // consume the whole stream - self.emit('error', new Error('Invalid response code ' + res.statusCode + ' from tracker')) - return - } - res.on('data', function (chunk) { - data += chunk - }) - res.on('end', function () { - self._handleResponse(data, announce) - }) - }) - - req.on('error', function (err) { - self.emit('error', err) - }) + self._announce.forEach(function (announceUrl) { + if (announceUrl.indexOf('udp:') === 0) { + self._requestUdp(announceUrl, opts) + } else { + self._requestHttp(announceUrl, opts) + } }) } -Client.prototype._handleResponse = function (data, announce) { +Client.prototype._requestHttp = function (announceUrl, opts) { + var self = this + var url = announceUrl + '?' + querystring.stringify(opts) + + var req = http.get(url, function (res) { + var data = '' + if (res.statusCode !== 200) { + res.resume() // consume the whole stream + self.emit('error', new Error('Invalid response code ' + res.statusCode + ' from tracker')) + return + } + res.on('data', function (chunk) { + data += chunk + }) + res.on('end', function () { + self._handleResponse(data, announceUrl) + }) + }) + + req.on('error', function (err) { + self.emit('error', err) + }) +} + +Client.prototype._requestUdp = function (announceUrl, opts) { + var parsed = parseUrl(announceUrl) + var socket = dgram.createSocket('udp4') + var self = this + + var timeout = setTimeout(function() { + socket.close() + }, 5000) + + socket.on('error', function(err) { + self.emit('error', err) + }) + + socket.on('message', function(message, rinfo) { + var action = message.readUInt32BE(0) + + switch (action) { + case 0: + if (message.length < 16) { + return self.emit('error', new Error('invalid udp handshake')) + } + announce(message.slice(8, 16), opts) + return + + case 1: + if (message.length < 20) { + return self.emit('error', new Error('invalid announce message')) + } + + self.emit('update', { + announce: announceUrl, + complete: message.readUInt32BE(16), + incomplete: message.readUInt32BE(12) + }) + + for (var i = 20; i < message.length; i += 6) { + self.emit('peer', compact2string(message.slice(i, i+6))) + } + + clearTimeout(timeout) + socket.close() + } + }) + + function announce (connectionId, opts) { + opts = opts || {} + + send(Buffer.concat([ + connectionId, + ANNOUNCE, + new Buffer(hat(32), 'hex'), + new Buffer(self._infoHash, 'hex'), + new Buffer(self._peerId, 'utf-8'), + toUInt32(0), toUInt32(opts.downloaded || 0), // fromUint32(0) to expand this to 64bit + toUInt32(0), toUInt32(opts.left || 0), + toUInt32(0), toUInt32(opts.uploaded || 0), + toUInt32(EVENTS[opts.event] || 0), + toUInt32(0), + toUInt32(0), + toUInt32(self._numWant), + toUInt16(self._port || 0) + ])) + } + + function send (message) { + socket.send(message, 0, message.length, parsed.port, parsed.hostname) + } + + send(Buffer.concat([ + CONNECTION_ID, + CONNECT, + new Buffer(hat(32), 'hex') + ])) +} + +Client.prototype._handleResponse = function (data, announceUrl) { var self = this try { @@ -234,7 +248,7 @@ Client.prototype._handleResponse = function (data, announce) { } self.emit('update', { - announce: announce, + announce: announceUrl, complete: data.complete, incomplete: data.incomplete }) @@ -436,17 +450,17 @@ Server.prototype._getPeersCompact = function (swarm) { // HELPERS // -function fromInt16(n) { - var buf = new Buffer(2); - buf.writeUInt16BE(n, 0); - return buf; -}; +function toUInt16 (n) { + var buf = new Buffer(2) + buf.writeUInt16BE(n, 0) + return buf +} -function fromInt32(n) { - var buf = new Buffer(4); - buf.writeUInt32BE(n, 0); - return buf; -}; +function toUInt32 (n) { + var buf = new Buffer(4) + buf.writeUInt32BE(n, 0) + return buf +} function bytewiseEncodeURIComponent (buf) { if (!Buffer.isBuffer(buf)) {