<commit_before>module.exports = WebSocketTracker

var debug = require('debug')('bittorrent-tracker:websocket-tracker')
var extend = require('xtend')
var hat = require('hat')
var inherits = require('inherits')
var Peer = require('simple-peer')
var Socket = require('simple-websocket')

var common = require('../common')
var Tracker = require('./tracker')

// Use a socket pool, so tracker clients share WebSocket objects for the same server.
// In practice, WebSockets are pretty slow to establish, so this gives a nice performance
// boost, and saves browser resources.
var socketPool = {}

var RECONNECT_MINIMUM = 15 * 1000
var RECONNECT_MAXIMUM = 30 * 60 * 1000
var RECONNECT_VARIANCE = 30 * 1000
var OFFER_TIMEOUT = 50 * 1000

inherits(WebSocketTracker, Tracker)

function WebSocketTracker (client, announceUrl, opts) {
  var self = this
  Tracker.call(self, client, announceUrl)
  debug('new websocket tracker %s', announceUrl)

  self.peers = {} // peers (offer id -> peer)
  self.socket = null
  self.reconnecting = false
  self.retries = 0

  self._openSocket()
}

WebSocketTracker.prototype.DEFAULT_ANNOUNCE_INTERVAL = 30 * 1000 // 30 seconds

WebSocketTracker.prototype.announce = function (opts) {
  var self = this
  if (self.destroyed || self.reconnecting) return
  if (!self.socket.connected) {
    return self.socket.once('connect', self.announce.bind(self, opts))
  }

  // Limit the number of offers that are generated, since it can be slow
  var numwant = Math.min(opts.numwant, 10)

  self._generateOffers(numwant, function (offers) {
    var params = extend(opts, {
      action: 'announce',
      numwant: numwant,
      info_hash: self.client._infoHashBinary,
      peer_id: self.client._peerIdBinary,
      offers: offers
    })
    if (self._trackerId) params.trackerid = self._trackerId

    self._send(params)
  })
}

WebSocketTracker.prototype.scrape = function (opts) {
  var self = this
  if (self.destroyed || self.reconnecting) return
  if (!self.socket.connected) {
    return self.socket.once('connect', self.scrape.bind(self, opts))
  }

  var infoHashes = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
    ? opts.infoHash.map(function (infoHash) {
      return infoHash.toString('binary')
    })
    : (opts.infoHash && opts.infoHash.toString('binary')) || self.client._infoHashBinary
  var params = {
    action: 'scrape',
    info_hash: infoHashes
  }

  self._send(params)
}

WebSocketTracker.prototype.destroy = function (cb) {
  var self = this
  if (!cb) cb = noop
  if (self.destroyed) return cb(null)
  self.destroyed = true
  clearInterval(self.interval)
  clearTimeout(self.reconnectTimer)

  if (socketPool[self.announceUrl]) socketPool[self.announceUrl].consumers--

  self.socket.removeListener('connect', self._onSocketConnectBound)
  self.socket.removeListener('data', self._onSocketDataBound)
  self.socket.removeListener('close', self._onSocketCloseBound)
  self.socket.removeListener('error', self._onSocketErrorBound)

  self._onSocketConnectBound = null
  self._onSocketErrorBound = null
  self._onSocketDataBound = null
  self._onSocketCloseBound = null

  if (socketPool[self.announceUrl].consumers === 0) {
    delete socketPool[self.announceUrl]

    self.socket.on('error', noop) // ignore all future errors

    try {
      self.socket.destroy(cb)
    } catch (err) {
      if (cb) cb()
    }
  }
<commit_msg>Destroy peers and tracker timeouts on destroy<commit_after>module.exports = WebSocketTracker

var debug = require('debug')('bittorrent-tracker:websocket-tracker')
var extend = require('xtend')
var hat = require('hat')
var inherits = require('inherits')
var Peer = require('simple-peer')
var Socket = require('simple-websocket')

var common = require('../common')
var Tracker = require('./tracker')

// Use a socket pool, so tracker clients share WebSocket objects for the same server.
// In practice, WebSockets are pretty slow to establish, so this gives a nice performance
// boost, and saves browser resources.
var socketPool = {}

var RECONNECT_MINIMUM = 15 * 1000
var RECONNECT_MAXIMUM = 30 * 60 * 1000
var RECONNECT_VARIANCE = 30 * 1000
var OFFER_TIMEOUT = 50 * 1000

inherits(WebSocketTracker, Tracker)

function WebSocketTracker (client, announceUrl, opts) {
  var self = this
  Tracker.call(self, client, announceUrl)
  debug('new websocket tracker %s', announceUrl)

  self.peers = {} // peers (offer id -> peer)
  self.socket = null
  self.reconnecting = false
  self.retries = 0

  self._openSocket()
}

WebSocketTracker.prototype.DEFAULT_ANNOUNCE_INTERVAL = 30 * 1000 // 30 seconds

WebSocketTracker.prototype.announce = function (opts) {
  var self = this
  if (self.destroyed || self.reconnecting) return
  if (!self.socket.connected) {
    return self.socket.once('connect', self.announce.bind(self, opts))
  }

  // Limit the number of offers that are generated, since it can be slow
  var numwant = Math.min(opts.numwant, 10)

  self._generateOffers(numwant, function (offers) {
    var params = extend(opts, {
      action: 'announce',
      numwant: numwant,
      info_hash: self.client._infoHashBinary,
      peer_id: self.client._peerIdBinary,
      offers: offers
    })
    if (self._trackerId) params.trackerid = self._trackerId

    self._send(params)
  })
}

WebSocketTracker.prototype.scrape = function (opts) {
  var self = this
  if (self.destroyed || self.reconnecting) return
  if (!self.socket.connected) {
    return self.socket.once('connect', self.scrape.bind(self, opts))
  }

  var infoHashes = (Array.isArray(opts.infoHash) && opts.infoHash.length > 0)
    ? opts.infoHash.map(function (infoHash) {
      return infoHash.toString('binary')
    })
    : (opts.infoHash && opts.infoHash.toString('binary')) || self.client._infoHashBinary
  var params = {
    action: 'scrape',
    info_hash: infoHashes
  }

  self._send(params)
}

WebSocketTracker.prototype.destroy = function (cb) {
  var self = this
  if (!cb) cb = noop
  if (self.destroyed) return cb(null)
  self.destroyed = true
  clearInterval(self.interval)
  clearTimeout(self.reconnectTimer)

  // Destroy peers
  for(var peerId in self.peers) {
    var peer = self.peers[peerId]
    clearTimeout(peer.trackerTimeout)
    peer.destroy()
  }
  delete self.peers

  // Close socked
  if (socketPool[self.announceUrl]) socketPool[self.announceUrl].consumers--

  self.socket.removeListener('connect', self._onSocketConnectBound)
  self.socket.removeListener('data', self._onSocketDataBound)
  self.socket.removeListener('close', self._onSocketCloseBound)
  self.socket.removeListener('error', self._onSocketErrorBound)

  self._onSocketConnectBound = null
  self._onSocketErrorBound = null
  self._onSocketDataBound = null
  self._onSocketCloseBound = null

  if (socketPool[self.announceUrl].consumers === 0) {
    delete socketPool[self.announceUrl]

    self.socket.on('error', noop) // ignore all future errors

    try {
      self.socket.destroy(cb)
    } catch (err) {
      if (cb) cb()
    }
  }

{'fraction_non_alphanumeric': 0.11943290849205213, 'fraction_numerical': 0.011169984247458112, 'mean_word_length': 3.9637526652452024, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2245830', 'n_tokens_mistral': 2310, 'n_tokens_neox': 2197, 'n_words': 535}