<commit_before>// TODO: cleanup unused Peers when tracker doesn't respond with matches

module.exports = WebSocketTracker

var debug = require('debug')('bittorrent-tracker:websocket-tracker')
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_VARIANCE = 30 * 1000
var RECONNECT_MINIMUM = 5 * 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._openSocket()
}

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

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

  // TODO: Limit number of offers (temporarily)
  // TODO: remove this when we cleanup old RTCPeerConnections cleanly
  var numwant = Math.min(opts.numwant, 10)

  self._generateOffers(numwant, function (offers) {
    var params = {
      numwant: numwant,
      uploaded: opts.uploaded || 0,
      downloaded: opts.downloaded,
      event: opts.event,
      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) return
  self._onSocketError(new Error('scrape not supported ' + self.announceUrl))
}

WebSocketTracker.prototype.destroy = function (onclose) {
  var self = this
  if (self.destroyed) return
  self.destroyed = true
  clearInterval(self.interval)

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

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

  self.socket.on('error', noop) // ignore all future errors
  try {
    self.socket.destroy(onclose)
  } catch (err) {
    if (onclose) onclose()
  }

  self.socket = null
}

WebSocketTracker.prototype._openSocket = function () {
  var self = this
  self._onSocketErrorBound = self._onSocketError.bind(self)
  self._onSocketDataBound = self._onSocketData.bind(self)
  self._onSocketCloseBound = self._onSocketClose.bind(self)

  self.socket = socketPool[self.announceUrl]
  if (!self.socket) {
    self.socket = socketPool[self.announceUrl] = new Socket(self.announceUrl)
  }
<commit_msg>remove socket from pool on destroy<commit_after>// TODO: cleanup unused Peers when tracker doesn't respond with matches

module.exports = WebSocketTracker

var debug = require('debug')('bittorrent-tracker:websocket-tracker')
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_VARIANCE = 30 * 1000
var RECONNECT_MINIMUM = 5 * 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._openSocket()
}

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

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

  // TODO: Limit number of offers (temporarily)
  // TODO: remove this when we cleanup old RTCPeerConnections cleanly
  var numwant = Math.min(opts.numwant, 10)

  self._generateOffers(numwant, function (offers) {
    var params = {
      numwant: numwant,
      uploaded: opts.uploaded || 0,
      downloaded: opts.downloaded,
      event: opts.event,
      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) return
  self._onSocketError(new Error('scrape not supported ' + self.announceUrl))
}

WebSocketTracker.prototype.destroy = function (onclose) {
  var self = this
  if (self.destroyed) return
  self.destroyed = true
  clearInterval(self.interval)

  socketPool[self.announceUrl] = null

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

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

  self.socket.on('error', noop) // ignore all future errors
  try {
    self.socket.destroy(onclose)
  } catch (err) {
    if (onclose) onclose()
  }

  self.socket = null
}

WebSocketTracker.prototype._openSocket = function () {
  var self = this
  self._onSocketErrorBound = self._onSocketError.bind(self)
  self._onSocketDataBound = self._onSocketData.bind(self)
  self._onSocketCloseBound = self._onSocketClose.bind(self)

  self.socket = socketPool[self.announceUrl]
  if (!self.socket) {
    self.socket = socketPool[self.announceUrl] = new Socket(self.announceUrl)
  }

{'fraction_non_alphanumeric': 0.11057382333978079, 'fraction_numerical': 0.0070921985815602835, 'mean_word_length': 4.157938487115544, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4868757', 'n_tokens_mistral': 1996, 'n_tokens_neox': 1912, 'n_words': 485}