<commit_before>module.exports = Server

var Buffer = require('safe-buffer').Buffer
var bencode = require('bencode')
var debug = require('debug')('bittorrent-tracker:server')
var dgram = require('dgram')
var EventEmitter = require('events').EventEmitter
var http = require('http')
var inherits = require('inherits')
var peerid = require('bittorrent-peerid')
var series = require('run-series')
var string2compact = require('string2compact')
var WebSocketServer = require('ws').Server

var common = require('./lib/common')
var Swarm = require('./lib/server/swarm')
var parseHttpRequest = require('./lib/server/parse-http')
var parseUdpRequest = require('./lib/server/parse-udp')
var parseWebSocketRequest = require('./lib/server/parse-websocket')

inherits(Server, EventEmitter)

/**
 * BitTorrent tracker server.
 *
 * HTTP service which responds to GET requests from torrent clients. Requests include
 * metrics from clients that help the tracker keep overall statistics about the torrent.
 * Responses include a peer list that helps the client participate in the torrent.
 *
 * @param {Object}  opts            options object
 * @param {Number}  opts.interval   tell clients to announce on this interval (ms)
 * @param {Number}  opts.trustProxy trust 'x-forwarded-for' header from reverse proxy
 * @param {boolean} opts.http       start an http server? (default: true)
 * @param {boolean} opts.udp        start a udp server? (default: true)
 * @param {boolean} opts.ws         start a websocket server? (default: true)
 * @param {boolean} opts.stats      enable web-based statistics? (default: true)
 * @param {function} opts.filter    black/whitelist fn for disallowing/allowing torrents
 */
function Server (opts) {
  var self = this
  if (!(self instanceof Server)) return new Server(opts)
  EventEmitter.call(self)
  if (!opts) opts = {}

  debug('new server %s', JSON.stringify(opts))

  self.intervalMs = opts.interval
    ? opts.interval
    : 10 * 60 * 1000 // 10 min

  self._trustProxy = !!opts.trustProxy
  if (typeof opts.filter === 'function') self._filter = opts.filter

  self.peersCacheLength = opts.peersCacheLength
  self.peersCacheTtl = opts.peersCacheTtl

  self._listenCalled = false
  self.listening = false
  self.destroyed = false
  self.torrents = {}

  self.http = null
  self.udp4 = null
  self.udp6 = null
  self.ws = null

  // start an http tracker unless the user explictly says no
  if (opts.http !== false) {
    self.http = http.createServer()
    self.http.on('error', function (err) { self._onError(err) })
    self.http.on('listening', onListening)

    // Add default http request handler on next tick to give user the chance to add
    // their own handler first. Handle requests untouched by user's handler.
    process.nextTick(function () {
      self.http.on('request', function (req, res) {
        if (res.headersSent) return
        self.onHttpRequest(req, res)
      })
    })
  }

  // start a udp tracker unless the user explicitly says no
  if (opts.udp !== false) {
    var isNode10 = /^v0.10./.test(process.version)

    self.udp4 = self.udp = dgram.createSocket(
      isNode10 ? 'udp4' : { type: 'udp4', reuseAddr: true }
    )
    self.udp4.on('message', function (msg, rinfo) { self.onUdpRequest(msg, rinfo) })
    self.udp4.on('error', function (err) { self._onError(err) })
    self.udp4.on('listening', onListening)

    self.udp6 = dgram.createSocket(
      isNode10 ? 'udp6' : { type: 'udp6', reuseAddr: true }
    )
    self.udp6.on('message', function (msg, rinfo) { self.onUdpRequest(msg, rinfo) })
    self.udp6.on('error', function (err) { self._onError(err) })
    self.udp6.on('listening', onListening)
  }

  // start a websocket tracker (for WebTorrent) unless the user explicitly says no
  if (opts.ws !== false) {
    if (!self.http) {
      self.http = http.createServer()
      self.http.on('error', function (err) { self._onError(err) })
      self.http.on('listening', onListening)

      // Add default http request handler on next tick to give user the chance to add
      // their own handler first. Handle requests untouched by user's handler.
      process.nextTick(function () {
        self.http.on('request', function (req, res) {
          if (res.headersSent) return
          // For websocket trackers, we only need to handle the UPGRADE http method.
          // Return 404 for all other request types.
          res.statusCode = 404
          res.end('404 Not Found')
        })
      })
    }
    self.ws = new WebSocketServer({
      server: self.http,
      perMessageDeflate: false,
      clientTracking: false
    })
    self.ws.address = function () {
      return self.http.address()
    }
    self.ws.on('error', function (err) { self._onError(err) })
    self.ws.on('connection', function (socket, req) {
      // Note: socket.upgradeReq was removed in ws@3.0.0, so re-add it.
      // https://github.com/websockets/ws/pull/1099
      socket.upgradeReq = req
      self.onWebSocketConnection(socket)
    })
  }

  if (opts.stats !== false) {
    if (!self.http) {
      self.http = http.createServer()
      self.http.on('error', function (err) { self._onError(err) })
      self.http.on('listening', onListening)
    }

    // Http handler for '/stats' route
    self.http.on('request', function (req, res) {
      if (res.headersSent) return

      var infoHashes = Object.keys(self.torrents)
      var activeTorrents = 0
      var allPeers = {}

      function countPeers (filterFunction) {
        var count = 0
        var key

        for (key in allPeers) {
          if (allPeers.hasOwnProperty(key) && filterFunction(allPeers[key])) {
            count++
          }
        }

        return count
      }

      function groupByClient () {
        var clients = {}
        for (var key in allPeers) {
          if (allPeers.hasOwnProperty(key)) {
            var peer = allPeers[key]

            if (!clients[peer.client.client]) {
              clients[peer.client.client] = {}
            }
            var client = clients[peer.client.client]
            // If the client is not known show 8 chars from peerId as version
            var version = peer.client.version || new Buffer(peer.peerId, 'hex').toString().substring(0, 8)
            if (!client[version]) {
              client[version] = 0
            }
            client[version]++
          }
        }
        return clients
      }

      function printClients (clients) {
        var html = '<ul>\n'
        for (var name in clients) {
          if (clients.hasOwnProperty(name)) {
            var client = clients[name]
            for (var version in client) {
              if (client.hasOwnProperty(version)) {
                html += '<li><strong>' + name + '</strong> ' + version + ' : ' + client[version] + '</li>\n'
              }
            }
          }
        }
        html += '</ul>'<commit_msg>Replace new Buffer with Buffer.from<commit_after>module.exports = Server

var Buffer = require('safe-buffer').Buffer
var bencode = require('bencode')
var debug = require('debug')('bittorrent-tracker:server')
var dgram = require('dgram')
var EventEmitter = require('events').EventEmitter
var http = require('http')
var inherits = require('inherits')
var peerid = require('bittorrent-peerid')
var series = require('run-series')
var string2compact = require('string2compact')
var WebSocketServer = require('ws').Server

var common = require('./lib/common')
var Swarm = require('./lib/server/swarm')
var parseHttpRequest = require('./lib/server/parse-http')
var parseUdpRequest = require('./lib/server/parse-udp')
var parseWebSocketRequest = require('./lib/server/parse-websocket')

inherits(Server, EventEmitter)

/**
 * BitTorrent tracker server.
 *
 * HTTP service which responds to GET requests from torrent clients. Requests include
 * metrics from clients that help the tracker keep overall statistics about the torrent.
 * Responses include a peer list that helps the client participate in the torrent.
 *
 * @param {Object}  opts            options object
 * @param {Number}  opts.interval   tell clients to announce on this interval (ms)
 * @param {Number}  opts.trustProxy trust 'x-forwarded-for' header from reverse proxy
 * @param {boolean} opts.http       start an http server? (default: true)
 * @param {boolean} opts.udp        start a udp server? (default: true)
 * @param {boolean} opts.ws         start a websocket server? (default: true)
 * @param {boolean} opts.stats      enable web-based statistics? (default: true)
 * @param {function} opts.filter    black/whitelist fn for disallowing/allowing torrents
 */
function Server (opts) {
  var self = this
  if (!(self instanceof Server)) return new Server(opts)
  EventEmitter.call(self)
  if (!opts) opts = {}

  debug('new server %s', JSON.stringify(opts))

  self.intervalMs = opts.interval
    ? opts.interval
    : 10 * 60 * 1000 // 10 min

  self._trustProxy = !!opts.trustProxy
  if (typeof opts.filter === 'function') self._filter = opts.filter

  self.peersCacheLength = opts.peersCacheLength
  self.peersCacheTtl = opts.peersCacheTtl

  self._listenCalled = false
  self.listening = false
  self.destroyed = false
  self.torrents = {}

  self.http = null
  self.udp4 = null
  self.udp6 = null
  self.ws = null

  // start an http tracker unless the user explictly says no
  if (opts.http !== false) {
    self.http = http.createServer()
    self.http.on('error', function (err) { self._onError(err) })
    self.http.on('listening', onListening)

    // Add default http request handler on next tick to give user the chance to add
    // their own handler first. Handle requests untouched by user's handler.
    process.nextTick(function () {
      self.http.on('request', function (req, res) {
        if (res.headersSent) return
        self.onHttpRequest(req, res)
      })
    })
  }

  // start a udp tracker unless the user explicitly says no
  if (opts.udp !== false) {
    var isNode10 = /^v0.10./.test(process.version)

    self.udp4 = self.udp = dgram.createSocket(
      isNode10 ? 'udp4' : { type: 'udp4', reuseAddr: true }
    )
    self.udp4.on('message', function (msg, rinfo) { self.onUdpRequest(msg, rinfo) })
    self.udp4.on('error', function (err) { self._onError(err) })
    self.udp4.on('listening', onListening)

    self.udp6 = dgram.createSocket(
      isNode10 ? 'udp6' : { type: 'udp6', reuseAddr: true }
    )
    self.udp6.on('message', function (msg, rinfo) { self.onUdpRequest(msg, rinfo) })
    self.udp6.on('error', function (err) { self._onError(err) })
    self.udp6.on('listening', onListening)
  }

  // start a websocket tracker (for WebTorrent) unless the user explicitly says no
  if (opts.ws !== false) {
    if (!self.http) {
      self.http = http.createServer()
      self.http.on('error', function (err) { self._onError(err) })
      self.http.on('listening', onListening)

      // Add default http request handler on next tick to give user the chance to add
      // their own handler first. Handle requests untouched by user's handler.
      process.nextTick(function () {
        self.http.on('request', function (req, res) {
          if (res.headersSent) return
          // For websocket trackers, we only need to handle the UPGRADE http method.
          // Return 404 for all other request types.
          res.statusCode = 404
          res.end('404 Not Found')
        })
      })
    }
    self.ws = new WebSocketServer({
      server: self.http,
      perMessageDeflate: false,
      clientTracking: false
    })
    self.ws.address = function () {
      return self.http.address()
    }
    self.ws.on('error', function (err) { self._onError(err) })
    self.ws.on('connection', function (socket, req) {
      // Note: socket.upgradeReq was removed in ws@3.0.0, so re-add it.
      // https://github.com/websockets/ws/pull/1099
      socket.upgradeReq = req
      self.onWebSocketConnection(socket)
    })
  }

  if (opts.stats !== false) {
    if (!self.http) {
      self.http = http.createServer()
      self.http.on('error', function (err) { self._onError(err) })
      self.http.on('listening', onListening)
    }

    // Http handler for '/stats' route
    self.http.on('request', function (req, res) {
      if (res.headersSent) return

      var infoHashes = Object.keys(self.torrents)
      var activeTorrents = 0
      var allPeers = {}

      function countPeers (filterFunction) {
        var count = 0
        var key

        for (key in allPeers) {
          if (allPeers.hasOwnProperty(key) && filterFunction(allPeers[key])) {
            count++
          }
        }

        return count
      }

      function groupByClient () {
        var clients = {}
        for (var key in allPeers) {
          if (allPeers.hasOwnProperty(key)) {
            var peer = allPeers[key]

            if (!clients[peer.client.client]) {
              clients[peer.client.client] = {}
            }
            var client = clients[peer.client.client]
            // If the client is not known show 8 chars from peerId as version
            var version = peer.client.version || Buffer.from(peer.peerId, 'hex').toString().substring(0, 8)
            if (!client[version]) {
              client[version] = 0
            }
            client[version]++
          }
        }
        return clients
      }

      function printClients (clients) {
        var html = '<ul>\n'
        for (var name in clients) {
          if (clients.hasOwnProperty(name)) {
            var client = clients[name]
            for (var version in client) {
              if (client.hasOwnProperty(version)) {
                html += '<li><strong>' + name + '</strong> ' + version + ' : ' + client[version] + '</li>\n'
              }
            }
          }
        }
        html += '</ul>'
{'fraction_non_alphanumeric': 0.12696572215377927, 'fraction_numerical': 0.008261468222334952, 'mean_word_length': 2.5825545171339566, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 2, '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': '2292969', 'n_tokens_mistral': 4274, 'n_tokens_neox': 4127, 'n_words': 1154}