mirror of
https://github.com/webtorrent/bittorrent-tracker.git
synced 2024-12-13 19:06:27 +00:00
03bed33457
This PR merges webtorrent-tracker into this repo. Keeping the code in sync between the two repos was becoming burdensome. This change should not effect performance of the server since the webtorrent tracker is disabled by default. To enable the webtorrent tracker (disabled by default), do: ```js var server = new Server({ ws: true }) ```
224 lines
6.3 KiB
JavaScript
224 lines
6.3 KiB
JavaScript
module.exports = Client
|
|
|
|
var debug = require('debug')('bittorrent-tracker')
|
|
var EventEmitter = require('events').EventEmitter
|
|
var inherits = require('inherits')
|
|
var once = require('once')
|
|
var url = require('url')
|
|
|
|
var common = require('./lib/common')
|
|
var HTTPTracker = require('./lib/http-tracker') // empty object in browser
|
|
var UDPTracker = require('./lib/udp-tracker') // empty object in browser
|
|
var WebSocketTracker = require('./lib/websocket-tracker')
|
|
|
|
inherits(Client, EventEmitter)
|
|
|
|
/**
|
|
* BitTorrent tracker client.
|
|
*
|
|
* Find torrent peers, to help a torrent client participate in a torrent swarm.
|
|
*
|
|
* @param {string} peerId peer id
|
|
* @param {Number} port torrent client listening port
|
|
* @param {Object} torrent parsed torrent
|
|
* @param {Object} opts options object
|
|
* @param {Number} opts.numWant number of peers to request
|
|
* @param {Number} opts.interval announce interval (in ms)
|
|
* @param {Number} opts.rtcConfig RTCPeerConnection configuration object
|
|
*/
|
|
function Client (peerId, port, torrent, opts) {
|
|
var self = this
|
|
if (!(self instanceof Client)) return new Client(peerId, port, torrent, opts)
|
|
EventEmitter.call(self)
|
|
if (!opts) opts = {}
|
|
|
|
// required
|
|
self._peerId = Buffer.isBuffer(peerId)
|
|
? peerId
|
|
: new Buffer(peerId, 'hex')
|
|
self._port = port
|
|
self._infoHash = Buffer.isBuffer(torrent.infoHash)
|
|
? torrent.infoHash
|
|
: new Buffer(torrent.infoHash, 'hex')
|
|
self.torrentLength = torrent.length
|
|
|
|
// optional
|
|
self._numWant = opts.numWant || common.DEFAULT_ANNOUNCE_PEERS
|
|
self._intervalMs = opts.interval || common.DEFAULT_ANNOUNCE_INTERVAL
|
|
|
|
debug('new client %s', self._infoHash.toString('hex'))
|
|
|
|
if (typeof torrent.announce === 'string') torrent.announce = [ torrent.announce ]
|
|
if (torrent.announce == null) torrent.announce = []
|
|
|
|
self._trackers = torrent.announce
|
|
.map(function (announceUrl) {
|
|
var trackerOpts = { interval: self._intervalMs }
|
|
var protocol = url.parse(announceUrl).protocol
|
|
|
|
if ((protocol === 'http:' || protocol === 'https:') &&
|
|
typeof HTTPTracker === 'function') {
|
|
return new HTTPTracker(self, announceUrl, trackerOpts)
|
|
} else if (protocol === 'udp:' && typeof UDPTracker === 'function') {
|
|
return new UDPTracker(self, announceUrl, trackerOpts)
|
|
} else if (protocol === 'ws:' || protocol === 'wss:') {
|
|
return new WebSocketTracker(self, announceUrl, trackerOpts)
|
|
}
|
|
return null
|
|
})
|
|
.filter(Boolean)
|
|
}
|
|
|
|
/**
|
|
* Simple convenience function to scrape a tracker for an infoHash without
|
|
* needing to create a Client, pass it a parsed torrent, etc.
|
|
* @param {string} announceUrl
|
|
* @param {string} infoHash
|
|
* @param {function} cb
|
|
*/
|
|
Client.scrape = function (announceUrl, infoHash, cb) {
|
|
cb = once(cb)
|
|
|
|
var peerId = new Buffer('01234567890123456789') // dummy value
|
|
var port = 6881 // dummy value
|
|
var torrent = {
|
|
infoHash: infoHash,
|
|
announce: [ announceUrl ]
|
|
}
|
|
var client = new Client(peerId, port, torrent)
|
|
client.once('error', cb)
|
|
client.once('scrape', function (data) {
|
|
cb(null, data)
|
|
})
|
|
client.scrape()
|
|
}
|
|
|
|
/**
|
|
* Send a `start` announce to the trackers.
|
|
* @param {Object} opts
|
|
* @param {number=} opts.uploaded
|
|
* @param {number=} opts.downloaded
|
|
* @param {number=} opts.left (if not set, calculated automatically)
|
|
*/
|
|
Client.prototype.start = function (opts) {
|
|
var self = this
|
|
debug('send `start`')
|
|
opts = self._defaultAnnounceOpts(opts)
|
|
opts.event = 'started'
|
|
self._announce(opts)
|
|
|
|
// start announcing on intervals
|
|
self._trackers.forEach(function (tracker) {
|
|
tracker.setInterval(self._intervalMs)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Send a `stop` announce to the trackers.
|
|
* @param {Object} opts
|
|
* @param {number=} opts.uploaded
|
|
* @param {number=} opts.downloaded
|
|
* @param {number=} opts.left (if not set, calculated automatically)
|
|
*/
|
|
Client.prototype.stop = function (opts) {
|
|
var self = this
|
|
debug('send `stop`')
|
|
opts = self._defaultAnnounceOpts(opts)
|
|
opts.event = 'stopped'
|
|
self._announce(opts)
|
|
|
|
self.destroy()
|
|
}
|
|
|
|
/**
|
|
* Send a `complete` announce to the trackers.
|
|
* @param {Object} opts
|
|
* @param {number=} opts.uploaded
|
|
* @param {number=} opts.downloaded
|
|
* @param {number=} opts.left (if not set, calculated automatically)
|
|
*/
|
|
Client.prototype.complete = function (opts) {
|
|
var self = this
|
|
debug('send `complete`')
|
|
if (!opts) opts = {}
|
|
if (opts.downloaded == null && self.torrentLength != null) {
|
|
opts.downloaded = self.torrentLength
|
|
}
|
|
opts = self._defaultAnnounceOpts(opts)
|
|
opts.event = 'completed'
|
|
self._announce(opts)
|
|
}
|
|
|
|
/**
|
|
* Send a `update` announce to the trackers.
|
|
* @param {Object} opts
|
|
* @param {number=} opts.uploaded
|
|
* @param {number=} opts.downloaded
|
|
* @param {number=} opts.left (if not set, calculated automatically)
|
|
*/
|
|
Client.prototype.update = function (opts) {
|
|
var self = this
|
|
debug('send `update`')
|
|
opts = self._defaultAnnounceOpts(opts)
|
|
if (opts.event) delete opts.event
|
|
self._announce(opts)
|
|
}
|
|
|
|
Client.prototype._announce = function (opts) {
|
|
var self = this
|
|
self._trackers.forEach(function (tracker) {
|
|
tracker.announce(opts)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Send a scrape request to the trackers.
|
|
* @param {Object} opts
|
|
* @param {number=} opts.uploaded
|
|
* @param {number=} opts.downloaded
|
|
* @param {number=} opts.left (if not set, calculated automatically)
|
|
*/
|
|
Client.prototype.scrape = function (opts) {
|
|
var self = this
|
|
debug('send `scrape`')
|
|
if (!opts) opts = {}
|
|
self._trackers.forEach(function (tracker) {
|
|
tracker.scrape(opts)
|
|
})
|
|
}
|
|
|
|
Client.prototype.setInterval = function (intervalMs) {
|
|
var self = this
|
|
debug('setInterval')
|
|
self._intervalMs = intervalMs
|
|
|
|
self._trackers.forEach(function (tracker) {
|
|
tracker.setInterval(intervalMs)
|
|
})
|
|
}
|
|
|
|
Client.prototype.destroy = function () {
|
|
var self = this
|
|
debug('destroy')
|
|
|
|
self._trackers.forEach(function (tracker) {
|
|
if (tracker.destroy) tracker.destroy()
|
|
tracker.setInterval(0) // stop announcing on intervals
|
|
})
|
|
}
|
|
|
|
Client.prototype._defaultAnnounceOpts = function (opts) {
|
|
var self = this
|
|
if (!opts) opts = {}
|
|
|
|
if (opts.numWant == null) opts.numWant = self._numWant
|
|
|
|
if (opts.uploaded == null) opts.uploaded = 0
|
|
if (opts.downloaded == null) opts.downloaded = 0
|
|
|
|
if (opts.left == null && self.torrentLength != null) {
|
|
opts.left = self.torrentLength - opts.downloaded
|
|
}
|
|
return opts
|
|
}
|