mirror of
https://github.com/webtorrent/bittorrent-tracker.git
synced 2025-01-18 12:11:36 +00:00
replace self with this
This commit is contained in:
parent
386e0a5fbe
commit
c8ceaee306
@ -20,47 +20,44 @@ class HTTPTracker extends Tracker {
|
|||||||
constructor (client, announceUrl, opts) {
|
constructor (client, announceUrl, opts) {
|
||||||
super(client, announceUrl)
|
super(client, announceUrl)
|
||||||
|
|
||||||
const self = this
|
|
||||||
debug('new http tracker %s', announceUrl)
|
debug('new http tracker %s', announceUrl)
|
||||||
|
|
||||||
// Determine scrape url (if http tracker supports it)
|
// Determine scrape url (if http tracker supports it)
|
||||||
self.scrapeUrl = null
|
this.scrapeUrl = null
|
||||||
|
|
||||||
const match = self.announceUrl.match(HTTP_SCRAPE_SUPPORT)
|
const match = this.announceUrl.match(HTTP_SCRAPE_SUPPORT)
|
||||||
if (match) {
|
if (match) {
|
||||||
const pre = self.announceUrl.slice(0, match.index)
|
const pre = this.announceUrl.slice(0, match.index)
|
||||||
const post = self.announceUrl.slice(match.index + 9)
|
const post = this.announceUrl.slice(match.index + 9)
|
||||||
self.scrapeUrl = `${pre}/scrape${post}`
|
this.scrapeUrl = `${pre}/scrape${post}`
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cleanupFns = []
|
this.cleanupFns = []
|
||||||
self.maybeDestroyCleanup = null
|
this.maybeDestroyCleanup = null
|
||||||
}
|
}
|
||||||
|
|
||||||
announce (opts) {
|
announce (opts) {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
|
||||||
|
|
||||||
const params = Object.assign({}, opts, {
|
const params = Object.assign({}, opts, {
|
||||||
compact: (opts.compact == null) ? 1 : opts.compact,
|
compact: (opts.compact == null) ? 1 : opts.compact,
|
||||||
info_hash: self.client._infoHashBinary,
|
info_hash: this.client._infoHashBinary,
|
||||||
peer_id: self.client._peerIdBinary,
|
peer_id: this.client._peerIdBinary,
|
||||||
port: self.client._port
|
port: this.client._port
|
||||||
})
|
})
|
||||||
if (self._trackerId) params.trackerid = self._trackerId
|
if (this._trackerId) params.trackerid = this._trackerId
|
||||||
|
|
||||||
self._request(self.announceUrl, params, (err, data) => {
|
this._request(this.announceUrl, params, (err, data) => {
|
||||||
if (err) return self.client.emit('warning', err)
|
if (err) return this.client.emit('warning', err)
|
||||||
self._onAnnounceResponse(data)
|
this._onAnnounceResponse(data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
scrape (opts) {
|
scrape (opts) {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
|
||||||
|
|
||||||
if (!self.scrapeUrl) {
|
if (!this.scrapeUrl) {
|
||||||
self.client.emit('error', new Error(`scrape not supported ${self.announceUrl}`))
|
this.client.emit('error', new Error(`scrape not supported ${this.announceUrl}`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,24 +65,24 @@ class HTTPTracker extends Tracker {
|
|||||||
? opts.infoHash.map(infoHash => {
|
? opts.infoHash.map(infoHash => {
|
||||||
return infoHash.toString('binary')
|
return infoHash.toString('binary')
|
||||||
})
|
})
|
||||||
: (opts.infoHash && opts.infoHash.toString('binary')) || self.client._infoHashBinary
|
: (opts.infoHash && opts.infoHash.toString('binary')) || this.client._infoHashBinary
|
||||||
const params = {
|
const params = {
|
||||||
info_hash: infoHashes
|
info_hash: infoHashes
|
||||||
}
|
}
|
||||||
self._request(self.scrapeUrl, params, (err, data) => {
|
this._request(this.scrapeUrl, params, (err, data) => {
|
||||||
if (err) return self.client.emit('warning', err)
|
if (err) return this.client.emit('warning', err)
|
||||||
self._onScrapeResponse(data)
|
this._onScrapeResponse(data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy (cb) {
|
destroy (cb) {
|
||||||
const self = this
|
const self = this
|
||||||
if (self.destroyed) return cb(null)
|
if (this.destroyed) return cb(null)
|
||||||
self.destroyed = true
|
this.destroyed = true
|
||||||
clearInterval(self.interval)
|
clearInterval(this.interval)
|
||||||
|
|
||||||
// If there are no pending requests, destroy immediately.
|
// If there are no pending requests, destroy immediately.
|
||||||
if (self.cleanupFns.length === 0) return destroyCleanup()
|
if (this.cleanupFns.length === 0) return destroyCleanup()
|
||||||
|
|
||||||
// Otherwise, wait a short time for pending requests to complete, then force
|
// Otherwise, wait a short time for pending requests to complete, then force
|
||||||
// destroy them.
|
// destroy them.
|
||||||
@ -93,8 +90,8 @@ class HTTPTracker extends Tracker {
|
|||||||
|
|
||||||
// But, if all pending requests complete before the timeout fires, do cleanup
|
// But, if all pending requests complete before the timeout fires, do cleanup
|
||||||
// right away.
|
// right away.
|
||||||
self.maybeDestroyCleanup = () => {
|
this.maybeDestroyCleanup = () => {
|
||||||
if (self.cleanupFns.length === 0) destroyCleanup()
|
if (this.cleanupFns.length === 0) destroyCleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroyCleanup () {
|
function destroyCleanup () {
|
||||||
@ -116,13 +113,13 @@ class HTTPTracker extends Tracker {
|
|||||||
const u = requestUrl + (!requestUrl.includes('?') ? '?' : '&') +
|
const u = requestUrl + (!requestUrl.includes('?') ? '?' : '&') +
|
||||||
common.querystringStringify(params)
|
common.querystringStringify(params)
|
||||||
|
|
||||||
self.cleanupFns.push(cleanup)
|
this.cleanupFns.push(cleanup)
|
||||||
|
|
||||||
let request = get.concat({
|
let request = get.concat({
|
||||||
url: u,
|
url: u,
|
||||||
timeout: common.REQUEST_TIMEOUT,
|
timeout: common.REQUEST_TIMEOUT,
|
||||||
headers: {
|
headers: {
|
||||||
'user-agent': self.client._userAgent || ''
|
'user-agent': this.client._userAgent || ''
|
||||||
}
|
}
|
||||||
}, onResponse)
|
}, onResponse)
|
||||||
|
|
||||||
@ -171,22 +168,20 @@ class HTTPTracker extends Tracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onAnnounceResponse (data) {
|
_onAnnounceResponse (data) {
|
||||||
const self = this
|
|
||||||
|
|
||||||
const interval = data.interval || data['min interval']
|
const interval = data.interval || data['min interval']
|
||||||
if (interval) self.setInterval(interval * 1000)
|
if (interval) this.setInterval(interval * 1000)
|
||||||
|
|
||||||
const trackerId = data['tracker id']
|
const trackerId = data['tracker id']
|
||||||
if (trackerId) {
|
if (trackerId) {
|
||||||
// If absent, do not discard previous trackerId value
|
// If absent, do not discard previous trackerId value
|
||||||
self._trackerId = trackerId
|
this._trackerId = trackerId
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = Object.assign({}, data, {
|
const response = Object.assign({}, data, {
|
||||||
announce: self.announceUrl,
|
announce: this.announceUrl,
|
||||||
infoHash: common.binaryToHex(data.info_hash)
|
infoHash: common.binaryToHex(data.info_hash)
|
||||||
})
|
})
|
||||||
self.client.emit('update', response)
|
this.client.emit('update', response)
|
||||||
|
|
||||||
let addrs
|
let addrs
|
||||||
if (Buffer.isBuffer(data.peers)) {
|
if (Buffer.isBuffer(data.peers)) {
|
||||||
@ -194,15 +189,15 @@ class HTTPTracker extends Tracker {
|
|||||||
try {
|
try {
|
||||||
addrs = compact2string.multi(data.peers)
|
addrs = compact2string.multi(data.peers)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return self.client.emit('warning', err)
|
return this.client.emit('warning', err)
|
||||||
}
|
}
|
||||||
addrs.forEach(addr => {
|
addrs.forEach(addr => {
|
||||||
self.client.emit('peer', addr)
|
this.client.emit('peer', addr)
|
||||||
})
|
})
|
||||||
} else if (Array.isArray(data.peers)) {
|
} else if (Array.isArray(data.peers)) {
|
||||||
// tracker returned normal response
|
// tracker returned normal response
|
||||||
data.peers.forEach(peer => {
|
data.peers.forEach(peer => {
|
||||||
self.client.emit('peer', `${peer.ip}:${peer.port}`)
|
this.client.emit('peer', `${peer.ip}:${peer.port}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,10 +206,10 @@ class HTTPTracker extends Tracker {
|
|||||||
try {
|
try {
|
||||||
addrs = compact2string.multi6(data.peers6)
|
addrs = compact2string.multi6(data.peers6)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return self.client.emit('warning', err)
|
return this.client.emit('warning', err)
|
||||||
}
|
}
|
||||||
addrs.forEach(addr => {
|
addrs.forEach(addr => {
|
||||||
self.client.emit('peer', addr)
|
this.client.emit('peer', addr)
|
||||||
})
|
})
|
||||||
} else if (Array.isArray(data.peers6)) {
|
} else if (Array.isArray(data.peers6)) {
|
||||||
// tracker returned normal response
|
// tracker returned normal response
|
||||||
@ -222,20 +217,19 @@ class HTTPTracker extends Tracker {
|
|||||||
const ip = /^\[/.test(peer.ip) || !/:/.test(peer.ip)
|
const ip = /^\[/.test(peer.ip) || !/:/.test(peer.ip)
|
||||||
? peer.ip /* ipv6 w/ brackets or domain name */
|
? peer.ip /* ipv6 w/ brackets or domain name */
|
||||||
: `[${peer.ip}]` /* ipv6 without brackets */
|
: `[${peer.ip}]` /* ipv6 without brackets */
|
||||||
self.client.emit('peer', `${ip}:${peer.port}`)
|
this.client.emit('peer', `${ip}:${peer.port}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onScrapeResponse (data) {
|
_onScrapeResponse (data) {
|
||||||
const self = this
|
|
||||||
// NOTE: the unofficial spec says to use the 'files' key, 'host' has been
|
// NOTE: the unofficial spec says to use the 'files' key, 'host' has been
|
||||||
// seen in practice
|
// seen in practice
|
||||||
data = data.files || data.host || {}
|
data = data.files || data.host || {}
|
||||||
|
|
||||||
const keys = Object.keys(data)
|
const keys = Object.keys(data)
|
||||||
if (keys.length === 0) {
|
if (keys.length === 0) {
|
||||||
self.client.emit('warning', new Error('invalid scrape response'))
|
this.client.emit('warning', new Error('invalid scrape response'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,10 +237,10 @@ class HTTPTracker extends Tracker {
|
|||||||
// TODO: optionally handle data.flags.min_request_interval
|
// TODO: optionally handle data.flags.min_request_interval
|
||||||
// (separate from announce interval)
|
// (separate from announce interval)
|
||||||
const response = Object.assign(data[infoHash], {
|
const response = Object.assign(data[infoHash], {
|
||||||
announce: self.announceUrl,
|
announce: this.announceUrl,
|
||||||
infoHash: common.binaryToHex(infoHash)
|
infoHash: common.binaryToHex(infoHash)
|
||||||
})
|
})
|
||||||
self.client.emit('scrape', response)
|
this.client.emit('scrape', response)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,25 +4,23 @@ class Tracker extends EventEmitter {
|
|||||||
constructor (client, announceUrl) {
|
constructor (client, announceUrl) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
const self = this
|
this.client = client
|
||||||
self.client = client
|
this.announceUrl = announceUrl
|
||||||
self.announceUrl = announceUrl
|
|
||||||
|
|
||||||
self.interval = null
|
this.interval = null
|
||||||
self.destroyed = false
|
this.destroyed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval (intervalMs) {
|
setInterval (intervalMs) {
|
||||||
const self = this
|
if (intervalMs == null) intervalMs = this.DEFAULT_ANNOUNCE_INTERVAL
|
||||||
if (intervalMs == null) intervalMs = self.DEFAULT_ANNOUNCE_INTERVAL
|
|
||||||
|
|
||||||
clearInterval(self.interval)
|
clearInterval(this.interval)
|
||||||
|
|
||||||
if (intervalMs) {
|
if (intervalMs) {
|
||||||
self.interval = setInterval(() => {
|
this.interval = setInterval(() => {
|
||||||
self.announce(self.client._defaultAnnounceOpts())
|
this.announce(this.client._defaultAnnounceOpts())
|
||||||
}, intervalMs)
|
}, intervalMs)
|
||||||
if (self.interval.unref) self.interval.unref()
|
if (this.interval.unref) this.interval.unref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,34 +20,31 @@ const Tracker = require('./tracker')
|
|||||||
class UDPTracker extends Tracker {
|
class UDPTracker extends Tracker {
|
||||||
constructor (client, announceUrl, opts) {
|
constructor (client, announceUrl, opts) {
|
||||||
super(client, announceUrl)
|
super(client, announceUrl)
|
||||||
const self = this
|
|
||||||
debug('new udp tracker %s', announceUrl)
|
debug('new udp tracker %s', announceUrl)
|
||||||
|
|
||||||
self.cleanupFns = []
|
this.cleanupFns = []
|
||||||
self.maybeDestroyCleanup = null
|
this.maybeDestroyCleanup = null
|
||||||
}
|
}
|
||||||
|
|
||||||
announce (opts) {
|
announce (opts) {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
this._request(opts)
|
||||||
self._request(opts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scrape (opts) {
|
scrape (opts) {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
|
||||||
opts._scrape = true
|
opts._scrape = true
|
||||||
self._request(opts) // udp scrape uses same announce url
|
this._request(opts) // udp scrape uses same announce url
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy (cb) {
|
destroy (cb) {
|
||||||
const self = this
|
const self = this
|
||||||
if (self.destroyed) return cb(null)
|
if (this.destroyed) return cb(null)
|
||||||
self.destroyed = true
|
this.destroyed = true
|
||||||
clearInterval(self.interval)
|
clearInterval(this.interval)
|
||||||
|
|
||||||
// If there are no pending requests, destroy immediately.
|
// If there are no pending requests, destroy immediately.
|
||||||
if (self.cleanupFns.length === 0) return destroyCleanup()
|
if (this.cleanupFns.length === 0) return destroyCleanup()
|
||||||
|
|
||||||
// Otherwise, wait a short time for pending requests to complete, then force
|
// Otherwise, wait a short time for pending requests to complete, then force
|
||||||
// destroy them.
|
// destroy them.
|
||||||
@ -55,8 +52,8 @@ class UDPTracker extends Tracker {
|
|||||||
|
|
||||||
// But, if all pending requests complete before the timeout fires, do cleanup
|
// But, if all pending requests complete before the timeout fires, do cleanup
|
||||||
// right away.
|
// right away.
|
||||||
self.maybeDestroyCleanup = () => {
|
this.maybeDestroyCleanup = () => {
|
||||||
if (self.cleanupFns.length === 0) destroyCleanup()
|
if (this.cleanupFns.length === 0) destroyCleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroyCleanup () {
|
function destroyCleanup () {
|
||||||
@ -76,7 +73,7 @@ class UDPTracker extends Tracker {
|
|||||||
_request (opts) {
|
_request (opts) {
|
||||||
const self = this
|
const self = this
|
||||||
if (!opts) opts = {}
|
if (!opts) opts = {}
|
||||||
const parsedUrl = url.parse(self.announceUrl)
|
const parsedUrl = url.parse(this.announceUrl)
|
||||||
let transactionId = genTransactionId()
|
let transactionId = genTransactionId()
|
||||||
let socket = dgram.createSocket('udp4')
|
let socket = dgram.createSocket('udp4')
|
||||||
|
|
||||||
@ -88,7 +85,7 @@ class UDPTracker extends Tracker {
|
|||||||
}, common.REQUEST_TIMEOUT)
|
}, common.REQUEST_TIMEOUT)
|
||||||
if (timeout.unref) timeout.unref()
|
if (timeout.unref) timeout.unref()
|
||||||
|
|
||||||
self.cleanupFns.push(cleanup)
|
this.cleanupFns.push(cleanup)
|
||||||
|
|
||||||
send(Buffer.concat([
|
send(Buffer.concat([
|
||||||
common.CONNECTION_ID,
|
common.CONNECTION_ID,
|
||||||
|
@ -19,61 +19,58 @@ const OFFER_TIMEOUT = 50 * 1000
|
|||||||
class WebSocketTracker extends Tracker {
|
class WebSocketTracker extends Tracker {
|
||||||
constructor (client, announceUrl, opts) {
|
constructor (client, announceUrl, opts) {
|
||||||
super(client, announceUrl)
|
super(client, announceUrl)
|
||||||
const self = this
|
|
||||||
debug('new websocket tracker %s', announceUrl)
|
debug('new websocket tracker %s', announceUrl)
|
||||||
|
|
||||||
self.peers = {} // peers (offer id -> peer)
|
this.peers = {} // peers (offer id -> peer)
|
||||||
self.socket = null
|
this.socket = null
|
||||||
|
|
||||||
self.reconnecting = false
|
this.reconnecting = false
|
||||||
self.retries = 0
|
this.retries = 0
|
||||||
self.reconnectTimer = null
|
this.reconnectTimer = null
|
||||||
|
|
||||||
// Simple boolean flag to track whether the socket has received data from
|
// Simple boolean flag to track whether the socket has received data from
|
||||||
// the websocket server since the last time socket.send() was called.
|
// the websocket server since the last time socket.send() was called.
|
||||||
self.expectingResponse = false
|
this.expectingResponse = false
|
||||||
|
|
||||||
self._openSocket()
|
this._openSocket()
|
||||||
}
|
}
|
||||||
|
|
||||||
announce (opts) {
|
announce (opts) {
|
||||||
const self = this
|
if (this.destroyed || this.reconnecting) return
|
||||||
if (self.destroyed || self.reconnecting) return
|
if (!this.socket.connected) {
|
||||||
if (!self.socket.connected) {
|
this.socket.once('connect', () => {
|
||||||
self.socket.once('connect', () => {
|
this.announce(opts)
|
||||||
self.announce(opts)
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = Object.assign({}, opts, {
|
const params = Object.assign({}, opts, {
|
||||||
action: 'announce',
|
action: 'announce',
|
||||||
info_hash: self.client._infoHashBinary,
|
info_hash: this.client._infoHashBinary,
|
||||||
peer_id: self.client._peerIdBinary
|
peer_id: this.client._peerIdBinary
|
||||||
})
|
})
|
||||||
if (self._trackerId) params.trackerid = self._trackerId
|
if (this._trackerId) params.trackerid = this._trackerId
|
||||||
|
|
||||||
if (opts.event === 'stopped' || opts.event === 'completed') {
|
if (opts.event === 'stopped' || opts.event === 'completed') {
|
||||||
// Don't include offers with 'stopped' or 'completed' event
|
// Don't include offers with 'stopped' or 'completed' event
|
||||||
self._send(params)
|
this._send(params)
|
||||||
} else {
|
} else {
|
||||||
// Limit the number of offers that are generated, since it can be slow
|
// Limit the number of offers that are generated, since it can be slow
|
||||||
const numwant = Math.min(opts.numwant, 10)
|
const numwant = Math.min(opts.numwant, 10)
|
||||||
|
|
||||||
self._generateOffers(numwant, offers => {
|
this._generateOffers(numwant, offers => {
|
||||||
params.numwant = numwant
|
params.numwant = numwant
|
||||||
params.offers = offers
|
params.offers = offers
|
||||||
self._send(params)
|
this._send(params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scrape (opts) {
|
scrape (opts) {
|
||||||
const self = this
|
if (this.destroyed || this.reconnecting) return
|
||||||
if (self.destroyed || self.reconnecting) return
|
if (!this.socket.connected) {
|
||||||
if (!self.socket.connected) {
|
this.socket.once('connect', () => {
|
||||||
self.socket.once('connect', () => {
|
this.scrape(opts)
|
||||||
self.scrape(opts)
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -82,60 +79,58 @@ class WebSocketTracker extends Tracker {
|
|||||||
? opts.infoHash.map(infoHash => {
|
? opts.infoHash.map(infoHash => {
|
||||||
return infoHash.toString('binary')
|
return infoHash.toString('binary')
|
||||||
})
|
})
|
||||||
: (opts.infoHash && opts.infoHash.toString('binary')) || self.client._infoHashBinary
|
: (opts.infoHash && opts.infoHash.toString('binary')) || this.client._infoHashBinary
|
||||||
const params = {
|
const params = {
|
||||||
action: 'scrape',
|
action: 'scrape',
|
||||||
info_hash: infoHashes
|
info_hash: infoHashes
|
||||||
}
|
}
|
||||||
|
|
||||||
self._send(params)
|
this._send(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy (cb) {
|
destroy (cb = noop) {
|
||||||
const self = this
|
if (this.destroyed) return cb(null)
|
||||||
if (!cb) cb = noop
|
|
||||||
if (self.destroyed) return cb(null)
|
|
||||||
|
|
||||||
self.destroyed = true
|
this.destroyed = true
|
||||||
|
|
||||||
clearInterval(self.interval)
|
clearInterval(this.interval)
|
||||||
clearTimeout(self.reconnectTimer)
|
clearTimeout(this.reconnectTimer)
|
||||||
|
|
||||||
// Destroy peers
|
// Destroy peers
|
||||||
for (const peerId in self.peers) {
|
for (const peerId in this.peers) {
|
||||||
const peer = self.peers[peerId]
|
const peer = this.peers[peerId]
|
||||||
clearTimeout(peer.trackerTimeout)
|
clearTimeout(peer.trackerTimeout)
|
||||||
peer.destroy()
|
peer.destroy()
|
||||||
}
|
}
|
||||||
self.peers = null
|
this.peers = null
|
||||||
|
|
||||||
if (self.socket) {
|
if (this.socket) {
|
||||||
self.socket.removeListener('connect', self._onSocketConnectBound)
|
this.socket.removeListener('connect', this._onSocketConnectBound)
|
||||||
self.socket.removeListener('data', self._onSocketDataBound)
|
this.socket.removeListener('data', this._onSocketDataBound)
|
||||||
self.socket.removeListener('close', self._onSocketCloseBound)
|
this.socket.removeListener('close', this._onSocketCloseBound)
|
||||||
self.socket.removeListener('error', self._onSocketErrorBound)
|
this.socket.removeListener('error', this._onSocketErrorBound)
|
||||||
self.socket = null
|
this.socket = null
|
||||||
}
|
}
|
||||||
|
|
||||||
self._onSocketConnectBound = null
|
this._onSocketConnectBound = null
|
||||||
self._onSocketErrorBound = null
|
this._onSocketErrorBound = null
|
||||||
self._onSocketDataBound = null
|
this._onSocketDataBound = null
|
||||||
self._onSocketCloseBound = null
|
this._onSocketCloseBound = null
|
||||||
|
|
||||||
if (socketPool[self.announceUrl]) {
|
if (socketPool[this.announceUrl]) {
|
||||||
socketPool[self.announceUrl].consumers -= 1
|
socketPool[this.announceUrl].consumers -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other instances are using the socket, so there's nothing left to do here
|
// Other instances are using the socket, so there's nothing left to do here
|
||||||
if (socketPool[self.announceUrl].consumers > 0) return cb()
|
if (socketPool[this.announceUrl].consumers > 0) return cb()
|
||||||
|
|
||||||
let socket = socketPool[self.announceUrl]
|
let socket = socketPool[this.announceUrl]
|
||||||
delete socketPool[self.announceUrl]
|
delete socketPool[this.announceUrl]
|
||||||
socket.on('error', noop) // ignore all future errors
|
socket.on('error', noop) // ignore all future errors
|
||||||
socket.once('close', cb)
|
socket.once('close', cb)
|
||||||
|
|
||||||
// If there is no data response expected, destroy immediately.
|
// If there is no data response expected, destroy immediately.
|
||||||
if (!self.expectingResponse) return destroyCleanup()
|
if (!this.expectingResponse) return destroyCleanup()
|
||||||
|
|
||||||
// Otherwise, wait a short time for potential responses to come in from the
|
// Otherwise, wait a short time for potential responses to come in from the
|
||||||
// server, then force close the socket.
|
// server, then force close the socket.
|
||||||
@ -157,147 +152,142 @@ class WebSocketTracker extends Tracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_openSocket () {
|
_openSocket () {
|
||||||
const self = this
|
this.destroyed = false
|
||||||
self.destroyed = false
|
|
||||||
|
|
||||||
if (!self.peers) self.peers = {}
|
if (!this.peers) this.peers = {}
|
||||||
|
|
||||||
self._onSocketConnectBound = () => {
|
this._onSocketConnectBound = () => {
|
||||||
self._onSocketConnect()
|
this._onSocketConnect()
|
||||||
}
|
}
|
||||||
self._onSocketErrorBound = err => {
|
this._onSocketErrorBound = err => {
|
||||||
self._onSocketError(err)
|
this._onSocketError(err)
|
||||||
}
|
}
|
||||||
self._onSocketDataBound = data => {
|
this._onSocketDataBound = data => {
|
||||||
self._onSocketData(data)
|
this._onSocketData(data)
|
||||||
}
|
}
|
||||||
self._onSocketCloseBound = () => {
|
this._onSocketCloseBound = () => {
|
||||||
self._onSocketClose()
|
this._onSocketClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.socket = socketPool[self.announceUrl]
|
this.socket = socketPool[this.announceUrl]
|
||||||
if (self.socket) {
|
if (this.socket) {
|
||||||
socketPool[self.announceUrl].consumers += 1
|
socketPool[this.announceUrl].consumers += 1
|
||||||
} else {
|
} else {
|
||||||
self.socket = socketPool[self.announceUrl] = new Socket(self.announceUrl)
|
this.socket = socketPool[this.announceUrl] = new Socket(this.announceUrl)
|
||||||
self.socket.consumers = 1
|
this.socket.consumers = 1
|
||||||
self.socket.once('connect', self._onSocketConnectBound)
|
this.socket.once('connect', this._onSocketConnectBound)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.socket.on('data', self._onSocketDataBound)
|
this.socket.on('data', this._onSocketDataBound)
|
||||||
self.socket.once('close', self._onSocketCloseBound)
|
this.socket.once('close', this._onSocketCloseBound)
|
||||||
self.socket.once('error', self._onSocketErrorBound)
|
this.socket.once('error', this._onSocketErrorBound)
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSocketConnect () {
|
_onSocketConnect () {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
|
||||||
|
|
||||||
if (self.reconnecting) {
|
if (this.reconnecting) {
|
||||||
self.reconnecting = false
|
this.reconnecting = false
|
||||||
self.retries = 0
|
this.retries = 0
|
||||||
self.announce(self.client._defaultAnnounceOpts())
|
this.announce(this.client._defaultAnnounceOpts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSocketData (data) {
|
_onSocketData (data) {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
|
||||||
|
|
||||||
self.expectingResponse = false
|
this.expectingResponse = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(data)
|
data = JSON.parse(data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
self.client.emit('warning', new Error('Invalid tracker response'))
|
this.client.emit('warning', new Error('Invalid tracker response'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.action === 'announce') {
|
if (data.action === 'announce') {
|
||||||
self._onAnnounceResponse(data)
|
this._onAnnounceResponse(data)
|
||||||
} else if (data.action === 'scrape') {
|
} else if (data.action === 'scrape') {
|
||||||
self._onScrapeResponse(data)
|
this._onScrapeResponse(data)
|
||||||
} else {
|
} else {
|
||||||
self._onSocketError(new Error(`invalid action in WS response: ${data.action}`))
|
this._onSocketError(new Error(`invalid action in WS response: ${data.action}`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onAnnounceResponse (data) {
|
_onAnnounceResponse (data) {
|
||||||
const self = this
|
if (data.info_hash !== this.client._infoHashBinary) {
|
||||||
|
|
||||||
if (data.info_hash !== self.client._infoHashBinary) {
|
|
||||||
debug(
|
debug(
|
||||||
'ignoring websocket data from %s for %s (looking for %s: reused socket)',
|
'ignoring websocket data from %s for %s (looking for %s: reused socket)',
|
||||||
self.announceUrl, common.binaryToHex(data.info_hash), self.client.infoHash
|
this.announceUrl, common.binaryToHex(data.info_hash), this.client.infoHash
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.peer_id && data.peer_id === self.client._peerIdBinary) {
|
if (data.peer_id && data.peer_id === this.client._peerIdBinary) {
|
||||||
// ignore offers/answers from this client
|
// ignore offers/answers from this client
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(
|
debug(
|
||||||
'received %s from %s for %s',
|
'received %s from %s for %s',
|
||||||
JSON.stringify(data), self.announceUrl, self.client.infoHash
|
JSON.stringify(data), this.announceUrl, this.client.infoHash
|
||||||
)
|
)
|
||||||
|
|
||||||
const failure = data['failure reason']
|
const failure = data['failure reason']
|
||||||
if (failure) return self.client.emit('warning', new Error(failure))
|
if (failure) return this.client.emit('warning', new Error(failure))
|
||||||
|
|
||||||
const warning = data['warning message']
|
const warning = data['warning message']
|
||||||
if (warning) self.client.emit('warning', new Error(warning))
|
if (warning) this.client.emit('warning', new Error(warning))
|
||||||
|
|
||||||
const interval = data.interval || data['min interval']
|
const interval = data.interval || data['min interval']
|
||||||
if (interval) self.setInterval(interval * 1000)
|
if (interval) this.setInterval(interval * 1000)
|
||||||
|
|
||||||
const trackerId = data['tracker id']
|
const trackerId = data['tracker id']
|
||||||
if (trackerId) {
|
if (trackerId) {
|
||||||
// If absent, do not discard previous trackerId value
|
// If absent, do not discard previous trackerId value
|
||||||
self._trackerId = trackerId
|
this._trackerId = trackerId
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.complete != null) {
|
if (data.complete != null) {
|
||||||
const response = Object.assign({}, data, {
|
const response = Object.assign({}, data, {
|
||||||
announce: self.announceUrl,
|
announce: this.announceUrl,
|
||||||
infoHash: common.binaryToHex(data.info_hash)
|
infoHash: common.binaryToHex(data.info_hash)
|
||||||
})
|
})
|
||||||
self.client.emit('update', response)
|
this.client.emit('update', response)
|
||||||
}
|
}
|
||||||
|
|
||||||
let peer
|
let peer
|
||||||
if (data.offer && data.peer_id) {
|
if (data.offer && data.peer_id) {
|
||||||
debug('creating peer (from remote offer)')
|
debug('creating peer (from remote offer)')
|
||||||
peer = self._createPeer()
|
peer = this._createPeer()
|
||||||
peer.id = common.binaryToHex(data.peer_id)
|
peer.id = common.binaryToHex(data.peer_id)
|
||||||
peer.once('signal', answer => {
|
peer.once('signal', answer => {
|
||||||
const params = {
|
const params = {
|
||||||
action: 'announce',
|
action: 'announce',
|
||||||
info_hash: self.client._infoHashBinary,
|
info_hash: this.client._infoHashBinary,
|
||||||
peer_id: self.client._peerIdBinary,
|
peer_id: this.client._peerIdBinary,
|
||||||
to_peer_id: data.peer_id,
|
to_peer_id: data.peer_id,
|
||||||
answer,
|
answer,
|
||||||
offer_id: data.offer_id
|
offer_id: data.offer_id
|
||||||
}
|
}
|
||||||
if (self._trackerId) params.trackerid = self._trackerId
|
if (this._trackerId) params.trackerid = this._trackerId
|
||||||
self._send(params)
|
this._send(params)
|
||||||
})
|
})
|
||||||
peer.signal(data.offer)
|
peer.signal(data.offer)
|
||||||
self.client.emit('peer', peer)
|
this.client.emit('peer', peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.answer && data.peer_id) {
|
if (data.answer && data.peer_id) {
|
||||||
const offerId = common.binaryToHex(data.offer_id)
|
const offerId = common.binaryToHex(data.offer_id)
|
||||||
peer = self.peers[offerId]
|
peer = this.peers[offerId]
|
||||||
if (peer) {
|
if (peer) {
|
||||||
peer.id = common.binaryToHex(data.peer_id)
|
peer.id = common.binaryToHex(data.peer_id)
|
||||||
peer.signal(data.answer)
|
peer.signal(data.answer)
|
||||||
self.client.emit('peer', peer)
|
this.client.emit('peer', peer)
|
||||||
|
|
||||||
clearTimeout(peer.trackerTimeout)
|
clearTimeout(peer.trackerTimeout)
|
||||||
peer.trackerTimeout = null
|
peer.trackerTimeout = null
|
||||||
delete self.peers[offerId]
|
delete this.peers[offerId]
|
||||||
} else {
|
} else {
|
||||||
debug(`got unexpected answer: ${JSON.stringify(data.answer)}`)
|
debug(`got unexpected answer: ${JSON.stringify(data.answer)}`)
|
||||||
}
|
}
|
||||||
@ -305,12 +295,11 @@ class WebSocketTracker extends Tracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onScrapeResponse (data) {
|
_onScrapeResponse (data) {
|
||||||
const self = this
|
|
||||||
data = data.files || {}
|
data = data.files || {}
|
||||||
|
|
||||||
const keys = Object.keys(data)
|
const keys = Object.keys(data)
|
||||||
if (keys.length === 0) {
|
if (keys.length === 0) {
|
||||||
self.client.emit('warning', new Error('invalid scrape response'))
|
this.client.emit('warning', new Error('invalid scrape response'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,51 +307,47 @@ class WebSocketTracker extends Tracker {
|
|||||||
// TODO: optionally handle data.flags.min_request_interval
|
// TODO: optionally handle data.flags.min_request_interval
|
||||||
// (separate from announce interval)
|
// (separate from announce interval)
|
||||||
const response = Object.assign(data[infoHash], {
|
const response = Object.assign(data[infoHash], {
|
||||||
announce: self.announceUrl,
|
announce: this.announceUrl,
|
||||||
infoHash: common.binaryToHex(infoHash)
|
infoHash: common.binaryToHex(infoHash)
|
||||||
})
|
})
|
||||||
self.client.emit('scrape', response)
|
this.client.emit('scrape', response)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSocketClose () {
|
_onSocketClose () {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
this.destroy()
|
||||||
self.destroy()
|
this._startReconnectTimer()
|
||||||
self._startReconnectTimer()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onSocketError (err) {
|
_onSocketError (err) {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
this.destroy()
|
||||||
self.destroy()
|
|
||||||
// errors will often happen if a tracker is offline, so don't treat it as fatal
|
// errors will often happen if a tracker is offline, so don't treat it as fatal
|
||||||
self.client.emit('warning', err)
|
this.client.emit('warning', err)
|
||||||
self._startReconnectTimer()
|
this._startReconnectTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
_startReconnectTimer () {
|
_startReconnectTimer () {
|
||||||
const self = this
|
const ms = Math.floor(Math.random() * RECONNECT_VARIANCE) + Math.min(Math.pow(2, this.retries) * RECONNECT_MINIMUM, RECONNECT_MAXIMUM)
|
||||||
const ms = Math.floor(Math.random() * RECONNECT_VARIANCE) + Math.min(Math.pow(2, self.retries) * RECONNECT_MINIMUM, RECONNECT_MAXIMUM)
|
|
||||||
|
|
||||||
self.reconnecting = true
|
this.reconnecting = true
|
||||||
clearTimeout(self.reconnectTimer)
|
clearTimeout(this.reconnectTimer)
|
||||||
self.reconnectTimer = setTimeout(() => {
|
this.reconnectTimer = setTimeout(() => {
|
||||||
self.retries++
|
this.retries++
|
||||||
self._openSocket()
|
this._openSocket()
|
||||||
}, ms)
|
}, ms)
|
||||||
if (self.reconnectTimer.unref) self.reconnectTimer.unref()
|
if (this.reconnectTimer.unref) this.reconnectTimer.unref()
|
||||||
|
|
||||||
debug('reconnecting socket in %s ms', ms)
|
debug('reconnecting socket in %s ms', ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
_send (params) {
|
_send (params) {
|
||||||
const self = this
|
if (this.destroyed) return
|
||||||
if (self.destroyed) return
|
this.expectingResponse = true
|
||||||
self.expectingResponse = true
|
|
||||||
const message = JSON.stringify(params)
|
const message = JSON.stringify(params)
|
||||||
debug('send %s', message)
|
debug('send %s', message)
|
||||||
self.socket.send(message)
|
this.socket.send(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
_generateOffers (numwant, cb) {
|
_generateOffers (numwant, cb) {
|
||||||
|
Loading…
Reference in New Issue
Block a user