<commit_before>const fs = require('fs')
const find = require('./find')
const http = require('http')
const urlglob = require('urlglob')

module.exports = Hyperproxy

function Hyperproxy(opts, callback) {
  this.servers = opts.servers
  this.agent = opts.agent
    ? opts.agent
    : Hyperproxy.defaultAgent()

  if (callback)
    return this.createServer(callback)
}

Hyperproxy.prototype = {
  createServer: function createServer(callback) {
    callback = callback || Hyperproxy.connectionNoop
    const server = http.createServer()

    server.on('request', function (clientReq, clientRes) {
      function proxyMiss(server, req, res) {
        const handler = Hyperproxy.errorHandler({
          code: 'proxyMiss'
        })

        return handler({
          server: server,
          req: clientReq,
          res: clientRes
        })
      }

      function makeProxyRequest() {
        if (!clientReq.headers.host)
          return proxyMiss(server, clientReq, clientRes)

        const host = clientReq.headers.host.split(':')[0]

        const requestOpts = Hyperproxy.createRequestOpts({
          servers: this.servers,
          headers: clientReq.headers,
          host: host,
          path: clientReq.url,
          method: clientReq.method,
        })

        if (!requestOpts)
          return proxyMiss(server, clientReq, clientRes)

        const proxyReq = http.request(requestOpts, handleResponse)
        function handleResponse(proxyRes) {proxyRes.pipe(clientRes)}

        clientReq.pipe(proxyReq)

        proxyReq.on('error', function (err) {
          const handler = Hyperproxy.errorHandler(err)
          return handler({
            error: err,
            server: server,
            req: clientReq,
            res: clientRes
          })
        })
      }

      callback.apply(this, [
        clientReq,
        clientRes,
        makeProxyRequest.bind(this)
      ])

    }.bind(this))

    return server
  },
}

Hyperproxy.createRequestOpts = function createRequestOpts(opts) {
  const servers = opts.servers
  const path = opts.path

  const server = find(servers, function (server) {
    const hostPattern = server[0]
    return urlglob(hostPattern, opts.host)
  })

  if (!server)
    return false

  var endpoint = server[1]

  if (Array.isArray(endpoint)) {
    const urlPatterns = endpoint
    endpoint = find(urlPatterns, function (urlPairs) {
      const urlPattern = urlPairs[0]
      return urlglob(urlPattern, path)
    })[1]
  }

  if (!endpoint)
    return false

  return Hyperproxy.finishRequestOpts(endpoint, {
    path: opts.path,
    headers: opts.headers,
    method: opts.method,
  })
}

Hyperproxy.errorHandler = function errorHandler(error) {
  return ((Hyperproxy.errorHandlers[error.code]  ||
          Hyperproxy.errorHandlers.unknown)
          .bind(Hyperproxy.errorHandlers))
}

Hyperproxy.errorHandlers = {
  ENOENT: function (opts) {
    return Hyperproxy.errorEvent('missingSocketFile', opts)
  },
  ENOTFOUND: function (opts) {
    return Hyperproxy.errorEvent('hostNotFound', opts)
  },
  proxyMiss: function (opts) {
    return Hyperproxy.errorEvent('proxyMiss', opts)
  },
  unknown: function (opts) {
    return Hyperproxy.errorEvent('unknownError', opts)
  },
}

Hyperproxy.errorEvent = function (event, opts) {
  const server = opts.server
  const handleBadGateway =
    Hyperproxy.badGateway.bind(Hyperproxy, opts)

  if (!server.listeners(event).length)
    return handleBadGateway()

  server.emit(event, opts.error, opts.req, opts.res, handleBadGateway)
}

Hyperproxy.connectionNoop =
  function connectionNoop(_, _, done) {
    return done()
  }

Hyperproxy.finishRequestOpts =
  function finishRequestOpts(endpoint, opts) {<commit_msg>Also emit proxyError event<commit_after>const fs = require('fs')
const find = require('./find')
const http = require('http')
const urlglob = require('urlglob')

module.exports = Hyperproxy

function Hyperproxy(opts, callback) {
  this.servers = opts.servers
  this.agent = opts.agent
    ? opts.agent
    : Hyperproxy.defaultAgent()

  if (callback)
    return this.createServer(callback)
}

Hyperproxy.prototype = {
  createServer: function createServer(callback) {
    callback = callback || Hyperproxy.connectionNoop
    const server = http.createServer()

    server.on('request', function (clientReq, clientRes) {
      function proxyMiss(server, req, res) {
        const handler = Hyperproxy.errorHandler({
          code: 'proxyMiss'
        })

        return handler({
          server: server,
          req: clientReq,
          res: clientRes
        })
      }

      function makeProxyRequest() {
        if (!clientReq.headers.host)
          return proxyMiss(server, clientReq, clientRes)

        const host = clientReq.headers.host.split(':')[0]

        const requestOpts = Hyperproxy.createRequestOpts({
          servers: this.servers,
          headers: clientReq.headers,
          host: host,
          path: clientReq.url,
          method: clientReq.method,
        })

        if (!requestOpts)
          return proxyMiss(server, clientReq, clientRes)

        const proxyReq = http.request(requestOpts, handleResponse)
        function handleResponse(proxyRes) {proxyRes.pipe(clientRes)}

        clientReq.pipe(proxyReq)

        proxyReq.on('error', function (err) {
          const handler = Hyperproxy.errorHandler(err)
          return handler({
            error: err,
            server: server,
            req: clientReq,
            res: clientRes
          })
        })
      }

      callback.apply(this, [
        clientReq,
        clientRes,
        makeProxyRequest.bind(this)
      ])

    }.bind(this))

    return server
  },
}

Hyperproxy.createRequestOpts = function createRequestOpts(opts) {
  const servers = opts.servers
  const path = opts.path

  const server = find(servers, function (server) {
    const hostPattern = server[0]
    return urlglob(hostPattern, opts.host)
  })

  if (!server)
    return false

  var endpoint = server[1]

  if (Array.isArray(endpoint)) {
    const urlPatterns = endpoint
    endpoint = find(urlPatterns, function (urlPairs) {
      const urlPattern = urlPairs[0]
      return urlglob(urlPattern, path)
    })[1]
  }

  if (!endpoint)
    return false

  return Hyperproxy.finishRequestOpts(endpoint, {
    path: opts.path,
    headers: opts.headers,
    method: opts.method,
  })
}

Hyperproxy.errorHandler = function errorHandler(error) {
  return ((Hyperproxy.errorHandlers[error.code]  ||
          Hyperproxy.errorHandlers.unknown)
          .bind(Hyperproxy.errorHandlers))
}

Hyperproxy.errorHandlers = {
  ENOENT: function (opts) {
    return Hyperproxy.errorEvent('missingSocketFile', opts)
  },
  ENOTFOUND: function (opts) {
    return Hyperproxy.errorEvent('hostNotFound', opts)
  },
  proxyMiss: function (opts) {
    return Hyperproxy.errorEvent('proxyMiss', opts)
  },
  unknown: function (opts) {
    return Hyperproxy.errorEvent('unknownError', opts)
  },
}

Hyperproxy.errorEvent = function (event, opts) {
  const server = opts.server
  const handleBadGateway =
    Hyperproxy.badGateway.bind(Hyperproxy, opts)

  if (!server.listeners(event).length)
    return handleBadGateway()

  server.emit('proxyError', opts.error)
  server.emit(event, opts.error, opts.req, opts.res, handleBadGateway)
}

Hyperproxy.connectionNoop =
  function connectionNoop(_, _, done) {
    return done()
  }

Hyperproxy.finishRequestOpts =
  function finishRequestOpts(endpoint, opts) {
{'fraction_non_alphanumeric': 0.10876818363806219, 'fraction_numerical': 0.0013345789403443214, 'mean_word_length': 2.847022587268994, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '269404', 'n_tokens_mistral': 2223, 'n_tokens_neox': 2167, 'n_words': 477}