code:
# HTTP Deceiver

[![Build Status](https://secure.travis-ci.org/indutny/http-deceiver.png)](http://travis-ci.org/indutny/http-deceiver)
[![NPM version](https://badge.fury.io/js/http-deceiver.svg)](http://badge.fury.io/js/http-deceiver)

Deceive!

## LICENSE

This software is licensed under the MIT License.

Copyright Fedor Indutny, 2015.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.


code:
var assert = require('assert');
var util = require('util');

var Buffer = require('buffer').Buffer;

// Node.js version
var mode = /^v0\.8\./.test(process.version) ? 'rusty' :
           /^v0\.(9|10)\./.test(process.version) ? 'old' :
           /^v0\.12\./.test(process.version) ? 'normal' :
           'modern';

var HTTPParser;

var methods;
var reverseMethods;

var kOnHeaders;
var kOnHeadersComplete;
var kOnMessageComplete;
var kOnBody;
if (mode === 'normal' || mode === 'modern') {
  HTTPParser = process.binding('http_parser').HTTPParser;
  methods = HTTPParser.methods;

  // v6
  if (!methods)
    methods = process.binding('http_parser').methods;

  reverseMethods = {};

  methods.forEach(function(method, index) {
    reverseMethods[method] = index;
  });

  kOnHeaders = HTTPParser.kOnHeaders | 0;
  kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
  kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
  kOnBody = HTTPParser.kOnBody | 0;
} else {
  kOnHeaders = 'onHeaders';
  kOnHeadersComplete = 'onHeadersComplete';
  kOnMessageComplete = 'onMessageComplete';
  kOnBody = 'onBody';
}

function Deceiver(socket, options) {
  this.socket = socket;
  this.options = options || {};
  this.isClient = this.options.isClient;
}
module.exports = Deceiver;

Deceiver.create = function create(stream, options) {
  return new Deceiver(stream, options);
};

Deceiver.prototype._toHeaderList = function _toHeaderList(object) {
  var out = [];
  var keys = Object.keys(object);

  for (var i = 0; i < keys.length; i++)
    out.push(keys[i], object[keys[i]]);

  return out;
};

Deceiver.prototype._isUpgrade = function _isUpgrade(request) {
  return request.method === 'CONNECT' ||
         request.headers.upgrade ||
         request.headers.connection &&
            /(^|\W)upgrade(\W|$)/i.test(request.headers.connection);
};

// TODO(indutny): support CONNECT
if (mode === 'modern') {
  /*
  function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
                                   url, statusCode, statusMessage, upgrade,
                                   shouldKeepAlive) {
   */
  Deceiver.prototype.emitRequest = function emitRequest(request) {
    var parser = this.socket.parser;
    assert(parser, 'No parser present');

    parser.execute = null;

    var self = this;
    var method = reverseMethods[request.method];
    parser.execute = function execute() {
      self._skipExecute(this);
      this[kOnHeadersComplete](1,
                               1,
                               self._toHeaderList(request.headers),
                               method,
                               request.path,
                               0,
                               '',
                               self._isUpgrade(request),
                               true);
      return 0;
    };

    this._emitEmpty();
  };

  Deceiver.prototype.emitResponse = function emitResponse(response) {
    var parser = this.socket.parser;
    assert(parser, 'No parser present');

    parser.execute = null;

    var self = this;
    parser.execute = function execute() {
      self._skipExecute(this);
      this[kOnHeadersComplete](1,
                               1,
                               self._toHeaderList(response.headers),
                               response.path,
                               response.code,
                               response.status,
                               response.reason || '',
                               self._isUpgrade(response),
                               true);
      return 0;
    };

    this._emitEmpty();
  };
} else {
  /*
    `function parserOnHeadersComplete(info) {`

    info = { .versionMajor, .versionMinor, .url, .headers, .method,
             .statusCode, .statusMessage, .upgrade, .shouldKeepAlive }
   */
  Deceiver.prototype.emitRequest = function emitRequest(request) {
    var parser = this.socket.parser;
    assert(parser, 'No parser present');

    var method = request.method;
    if (reverseMethods)
      method = reverseMethods[method];

    var info = {
      versionMajor: 1,
      versionMinor: 1,
      url: request.path,
      headers: this._toHeaderList(request.headers),
      method: method,
      statusCode: 0,
      statusMessage: '',
      upgrade: this._isUpgrade(request),
      shouldKeepAlive: true
    };

    var self = this;
    parser.execute = function execute() {
      self._skipExecute(this);
      this[kOnHeadersComplete](info);
      return 0;
    };

    this._emitEmpty();
  };

  Deceiver.prototype.emitResponse = function emitResponse(response) {
    var parser = this.socket.parser;
    assert(parser, 'No parser present');

    var info = {
      versionMajor: 1,
      versionMinor: 1,
      url: response.path,
      headers: this._toHeaderList(response.headers),
      method: false,
      statusCode: response.status,
      statusMessage: response.reason || '',
      upgrade: this._isUpgrade(response),
      shouldKeepAlive: true
    };

    var self = this;
    parser.execute = function execute() {
      self._skipExecute(this);
      this[kOnHeadersComplete](info);
      return 0;
    };

    this._emitEmpty();
  };
}

Deceiver.prototype._skipExecute = function _skipExecute(parser) {
  var self = this;
  var oldExecute = parser.constructor.prototype.execute;
  var oldFinish = parser.constructor.prototype.finish;

  parser.execute = null;
  parser.finish = null;

  parser.execute = function execute(buffer, start, len) {
    // Parser reuse
    if (this.socket !== self.socket) {
      this.execute = oldExecute;
      this.finish = oldFinish;
      return this.execute(buffer, start, len);
    }

    if (start !== undefined)
      buffer = buffer.slice(start, start + len);
    self.emitBody(buffer);
    return len;
  };

  parser.finish = function finish() {
    // Parser reuse
    if (this.socket !== self.socket) {
      this.execute = oldExecute;
      this.finish = oldFinish;
      return this.finish();
    }

    this.execute = oldExecute;
    this.finish = oldFinish;
    self.emitMessageComplete();
  };
};

Deceiver.prototype.emitBody = function emitBody(buffer) {
  var parser = this.socket.parser;
  assert(parser, 'No parser present');

  parser[kOnBody](buffer, 0, buffer.length);
};

Deceiver.prototype._emitEmpty = function _emitEmpty() {
  // Emit data to force out handling of UPGRADE
  var empty = new Buffer(0);
  if (this.socket.ondata)
    this.socket.ondata(empty, 0, 0);
  else
    this.socket.emit('data', empty);
};

Deceiver.prototype.emitMessageComplete = function emitMessageComplete() {
  var parser = this.socket.parser;
  assert(parser, 'No parser present');

  parser[kOnMessageComplete]();
};

license: mit


http-deceiver

description: Deceive HTTP parser

test: mocha --reporter=spec test/*-test.js

type: git

keywords: http net deceive

author: Fedor Indutny <fedor@indutny.com>

license: MIT


code:
var assert = require('assert');
var net = require('net');
var http = require('http');
var streamPair = require('stream-pair');
var thing = require('handle-thing');

var httpDeceiver = require('../');

describe('HTTP Deceiver', function() {
  var handle;
  var pair;
  var socket;
  var deceiver;

  beforeEach(function() {
    pair = streamPair.create();
    handle = thing.create(pair.other);
    socket = new net.Socket({ handle: handle });

    // For v0.8
    socket.readable = true;
    socket.writable = true;

    deceiver = httpDeceiver.create(socket);
  });

  it('should emit request', function(done) {
    var server = http.createServer();
    server.emit('connection', socket);

    server.on('request', function(req, res) {
      assert.equal(req.method, 'PUT');
      assert.equal(req.url, '/hello');
      assert.deepEqual(req.headers, { a: 'b' });

      done();
    });

    deceiver.emitRequest({
      method: 'PUT',
      path: '/hello',
      headers: {
        a: 'b'
      }
    });
  });

  it('should emit response', function(done) {
    var agent = new http.Agent();
    agent.createConnection = function createConnection() {
      return socket;
    };
    var client = http.request({
      method: 'POST',
      path: '/ok',
      agent: agent
    }, function(res) {
      assert.equal(res.statusCode, 421);
      assert.deepEqual(res.headers, { a: 'b' });

      done();
    });

    process.nextTick(function() {
      deceiver.emitResponse({
        status: 421,
        reason: 'F',
        headers: {
          a: 'b'
        }
      });
    });
  });

  it('should override .execute and .finish', function(done) {
    var server = http.createServer();
    server.emit('connection', socket);

    server.on('request', function(req, res) {
      assert.equal(req.method, 'PUT');
      assert.equal(req.url, '/hello');
      assert.deepEqual(req.headers, { a: 'b' });

      var actual = '';
      req.on('data', function(chunk) {
        actual += chunk;
      });
      req.once('end', function() {
        assert.equal(actual, 'hello world');
        done();
      });
    });

    deceiver.emitRequest({
      method: 'PUT',
      path: '/hello',
      headers: {
        a: 'b'
      }
    });

    pair.write('hello');
    pair.end(' world');
  });

  it('should work with reusing parser', function(done) {
    var server = http.createServer();
    server.emit('connection', socket);

    function secondRequest() {
      pair = streamPair.create();
      handle = thing.create(pair.other);
      socket = new net.Socket({ handle: handle });

      // For v0.8
      socket.readable = true;
      socket.writable = true;

      server.emit('connection', socket);

      pair.end('PUT /second HTTP/1.1\r\nContent-Length:11\r\n\r\nhello world');
    }

    server.on('request', function(req, res) {
      var actual = '';
      req.on('data', function(chunk) {
        actual += chunk;
      });
      req.once('end', function() {
        assert.equal(actual, 'hello world');

        if (req.url === '/first')
          secondRequest();
        else
          done();
      });
    });

    deceiver.emitRequest({
      method: 'PUT',
      path: '/first',
      headers: {
        a: 'b'
      }
    });

    pair.write('hello');
    pair.end(' world');
  });

  it('should emit CONNECT request', function(done) {
    var server = http.createServer();
    server.emit('connection', socket);

    server.on('connect', function(req, socket, bodyHead) {
      assert.equal(req.method, 'CONNECT');
      assert.equal(req.url, '/hello');

      done();
    });

    deceiver.emitRequest({
      method: 'CONNECT',
      path: '/hello',
      headers: {
      }
    });
  });

  it('should emit Upgrade request', function(done) {
    var server = http.createServer();
    server.emit('connection', socket);

    server.on('upgrade', function(req, socket, bodyHead) {
      assert.equal(req.method, 'POST');
      assert.equal(req.url, '/hello');

      socket.on('data', function(chunk) {
        assert.equal(chunk + '', 'hm');
        done();
      });
    });

    deceiver.emitRequest({
      method: 'POST',
      path: '/hello',
      headers: {
        'upgrade': 'websocket'
      }
    });

    pair.write('hm');
  });

  it('should emit Upgrade response', function(done) {
    var agent = new http.Agent();
    agent.createConnection = function createConnection() {
      return socket;
    };
    var client = http.request({
      method: 'POST',
      path: '/ok',
      headers: {
        connection: 'upgrade',
        upgrade: 'websocket'
      },
      agent: agent
    }, function(res) {
      assert(false);
    });
    client.on('upgrade', function(res, socket) {
      assert.equal(res.statusCode, 421);
      done();
    });

    process.nextTick(function() {
      deceiver.emitResponse({
        status: 421,
        reason: 'F',
        headers: {
          upgrade: 'websocket'
        }
      });
    });
  });
});


code:
2.0.0 / 2021-12-17
==================

  * Drop support for Node.js 0.6
  * Remove `I'mateapot` export; use `ImATeapot` instead
  * Remove support for status being non-first argument
  * Rename `UnorderedCollection` constructor to `TooEarly`
  * deps: depd@2.0.0
    - Replace internal `eval` usage with `Function` constructor
    - Use instance methods on `process` to check for listeners
  * deps: statuses@2.0.1
    - Fix messaging casing of `418 I'm a Teapot`
    - Remove code 306
    - Rename `425 Unordered Collection` to standard `425 Too Early`

2021-11-14 / 1.8.1
==================

  * deps: toidentifier@1.0.1

2020-06-29 / 1.8.0
==================

  * Add `isHttpError` export to determine if value is an HTTP error
  * deps: setprototypeof@1.2.0

2019-06-24 / 1.7.3
==================

  * deps: inherits@2.0.4

2019-02-18 / 1.7.2
==================

  * deps: setprototypeof@1.1.1

2018-09-08 / 1.7.1
==================

  * Fix error creating objects in some environments

2018-07-30 / 1.7.0
==================

  * Set constructor name when possible
  * Use `toidentifier` module to make class names
  * deps: statuses@'>= 1.5.0 < 2'

2018-03-29 / 1.6.3
==================

  * deps: depd@~1.1.2
    - perf: remove argument reassignment
  * deps: setprototypeof@1.1.0
  * deps: statuses@'>= 1.4.0 < 2'

2017-08-04 / 1.6.2
==================

  * deps: depd@1.1.1
    - Remove unnecessary `Buffer` loading

2017-02-20 / 1.6.1
==================

  * deps: setprototypeof@1.0.3
    - Fix shim for old browsers

2017-02-14 / 1.6.0
==================

  * Accept custom 4xx and 5xx status codes in factory
  * Add deprecation message to `"I'mateapot"` export
  * Deprecate passing status code as anything except first argument in factory
  * Deprecate using non-error status codes
  * Make `message` property enumerable for `HttpError`s

2016-11-16 / 1.5.1
==================

  * deps: inherits@2.0.3
    - Fix issue loading in browser
  * deps: setprototypeof@1.0.2
  * deps: statuses@'>= 1.3.1 < 2'

2016-05-18 / 1.5.0
==================

  * Support new code `421 Misdirected Request`
  * Use `setprototypeof` module to replace `__proto__` setting
  * deps: statuses@'>= 1.3.0 < 2'
    - Add `421 Misdirected Request`
    - perf: enable strict mode
  * perf: enable strict mode

2016-01-28 / 1.4.0
==================

  * Add `HttpError` export, for `err instanceof createError.HttpError`
  * deps: inherits@2.0.1
  * deps: statuses@'>= 1.2.1 < 2'
    - Fix message for status 451
    - Remove incorrect nginx status code

2015-02-02 / 1.3.1
==================

  * Fix regression where status can be overwritten in `createError` `props`

2015-02-01 / 1.3.0
==================

  * Construct errors using defined constructors from `createError`
  * Fix error names that are not identifiers
    - `createError["I'mateapot"]` is now `createError.ImATeapot`
  * Set a meaningful `name` property on constructed errors

2014-12-09 / 1.2.8
==================

  * Fix stack trace from exported function
  * Remove `arguments.callee` usage

2014-10-14 / 1.2.7
==================

  * Remove duplicate line

2014-10-02 / 1.2.6
==================

  * Fix `expose` to be `true` for `ClientError` constructor

2014-09-28 / 1.2.5
==================

  * deps: statuses@1

2014-09-21 / 1.2.4
==================

  * Fix dependency version to work with old `npm`s

2014-09-21 / 1.2.3
==================

  * deps: statuses@~1.1.0

2014-09-21 / 1.2.2
==================

  * Fix publish error

2014-09-21 / 1.2.1
==================

  * Support Node.js 0.6
  * Use `inherits` instead of `util`

2014-09-09 / 1.2.0
==================

  * Fix the way inheriting functions
  * Support `expose` being provided in properties argument

2014-09-08 / 1.1.0
==================

  * Default status to 500
  * Support provided `error` to extend

2014-09-08 / 1.0.1
==================

  * Fix accepting string message

2014-09-08 / 1.0.0
==================

  * Initial release


code:
# http-errors

[![NPM Version][npm-version-image]][npm-url]
[![NPM Downloads][npm-downloads-image]][node-url]
[![Node.js Version][node-image]][node-url]
[![Build Status][ci-image]][ci-url]
[![Test Coverage][coveralls-image]][coveralls-url]

Create HTTP errors for Express, Koa, Connect, etc. with ease.

## Install

This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/). Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):

```console
$ npm install http-errors
```

## Example

```js
var createError = require('http-errors')
var express = require('express')
var app = express()

app.use(function (req, res, next) {
  if (!req.user) return next(createError(401, 'Please login to view this page.'))
  next()
})
```

## API

This is the current API, currently extracted from Koa and subject to change.

### Error Properties

- `expose` - can be used to signal if `message` should be sent to the client,
  defaulting to `false` when `status` >= 500
- `headers` - can be an object of header names to values to be sent to the
  client, defaulting to `undefined`. When defined, the key names should all
  be lower-cased
- `message` - the traditional error message, which should be kept short and all
  single line
- `status` - the status code of the error, mirroring `statusCode` for general
  compatibility
- `statusCode` - the status code of the error, defaulting to `500`

### createError([status], [message], [properties])

Create a new error object with the given message `msg`.
The error object inherits from `createError.HttpError`.

```js
var err = createError(404, 'This video does not exist!')
```

- `status: 500` - the status code as a number
- `message` - the message of the error, defaulting to node's text for that status code.
- `properties` - custom properties to attach to the object

### createError([status], [error], [properties])

Extend the given `error` object with `createError.HttpError`
properties. This will not alter the inheritance of the given
`error` object, and the modified `error` object is the
return value.

<!-- eslint-disable no-redeclare -->

```js
fs.readFile('foo.txt', function (err, buf) {
  if (err) {
    if (err.code === 'ENOENT') {
      var httpError = createError(404, err, { expose: false })
    } else {
      var httpError = createError(500, err)
    }
  }
})
```

- `status` - the status code as a number
- `error` - the error object to extend
- `properties` - custom properties to attach to the object

### createError.isHttpError(val)

Determine if the provided `val` is an `HttpError`. This will return `true`
if the error inherits from the `HttpError` constructor of this module or
matches the "duck type" for an error this module creates. All outputs from
the `createError` factory will return `true` for this function, including
if an non-`HttpError` was passed into the factory.

### new createError\[code || name\](\[msg]\))

Create a new error object with the given message `msg`.
The error object inherits from `createError.HttpError`.

```js
var err = new createError.NotFound()
```

- `code` - the status code as a number
- `name` - the name of the error as a "bumpy case", i.e. `NotFound` or `InternalServerError`.

#### List of all constructors

|Status Code|Constructor Name             |
|-----------|-----------------------------|
|400        |BadRequest                   |
|401        |Unauthorized                 |
|402        |PaymentRequired              |
|403        |Forbidden                    |
|404        |NotFound                     |
|405        |MethodNotAllowed             |
|406        |NotAcceptable                |
|407        |ProxyAuthenticationRequired  |
|408        |RequestTimeout               |
|409        |Conflict                     |
|410        |Gone                         |
|411        |LengthRequired               |
|412        |PreconditionFailed           |
|413        |PayloadTooLarge              |
|414        |URITooLong                   |
|415        |UnsupportedMediaType         |
|416        |RangeNotSatisfiable          |
|417        |ExpectationFailed            |
|418        |ImATeapot                    |
|421        |MisdirectedRequest           |
|422        |UnprocessableEntity          |
|423        |Locked                       |
|424        |FailedDependency             |
|425        |TooEarly                     |
|426        |UpgradeRequired              |
|428        |PreconditionRequired         |
|429        |TooManyRequests              |
|431        |RequestHeaderFieldsTooLarge  |
|451        |UnavailableForLegalReasons   |
|500        |InternalServerError          |
|501        |NotImplemented               |
|502        |BadGateway                   |
|503        |ServiceUnavailable           |
|504        |GatewayTimeout               |
|505        |HTTPVersionNotSupported      |
|506        |VariantAlsoNegotiates        |
|507        |InsufficientStorage          |
|508        |LoopDetected                 |
|509        |BandwidthLimitExceeded       |
|510        |NotExtended                  |
|511        |NetworkAuthenticationRequired|

## License

[MIT](LICENSE)

[ci-image]: https://badgen.net/github/checks/jshttp/http-errors/master?label=ci
[ci-url]: https://github.com/jshttp/http-errors/actions?query=workflow%3Aci
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/http-errors/master
[coveralls-url]: https://coveralls.io/r/jshttp/http-errors?branch=master
[node-image]: https://badgen.net/npm/node/http-errors
[node-url]: https://nodejs.org/en/download
[npm-downloads-image]: https://badgen.net/npm/dm/http-errors
[npm-url]: https://npmjs.org/package/http-errors
[npm-version-image]: https://badgen.net/npm/v/http-errors
[travis-image]: https://badgen.net/travis/jshttp/http-errors/master
[travis-url]: https://travis-ci.org/jshttp/http-errors


code:
/*!
 * http-errors
 * Copyright(c) 2014 Jonathan Ong
 * Copyright(c) 2016 Douglas Christopher Wilson
 * MIT Licensed
 */

'use strict'

/**
 * Module dependencies.
 * @private
 */

var deprecate = require('depd')('http-errors')
var setPrototypeOf = require('setprototypeof')
var statuses = require('statuses')
var inherits = require('inherits')
var toIdentifier = require('toidentifier')

/**
 * Module exports.
 * @public
 */

module.exports = createError
module.exports.HttpError = createHttpErrorConstructor()
module.exports.isHttpError = createIsHttpErrorFunction(module.exports.HttpError)

// Populate exports for all constructors
populateConstructorExports(module.exports, statuses.codes, module.exports.HttpError)

/**
 * Get the code class of a status code.
 * @private
 */

function codeClass (status) {
  return Number(String(status).charAt(0) + '00')
}

/**
 * Create a new HTTP Error.
 *
 * @returns {Error}
 * @public
 */

function createError () {
  // so much arity going on ~_~
  var err
  var msg
  var status = 500
  var props = {}
  for (var i = 0; i < arguments.length; i++) {
    var arg = arguments[i]
    var type = typeof arg
    if (type === 'object' && arg instanceof Error) {
      err = arg
      status = err.status || err.statusCode || status
    } else if (type === 'number' && i === 0) {
      status = arg
    } else if (type === 'string') {
      msg = arg
    } else if (type === 'object') {
      props = arg
    } else {
      throw new TypeError('argument #' + (i + 1) + ' unsupported type ' + type)
    }
  }

  if (typeof status === 'number' && (status < 400 || status >= 600)) {
    deprecate('non-error status code; use only 4xx or 5xx status codes')
  }

  if (typeof status !== 'number' ||
    (!statuses.message[status] && (status < 400 || status >= 600))) {
    status = 500
  }

  // constructor
  var HttpError = createError[status] || createError[codeClass(status)]

  if (!err) {
    // create error
    err = HttpError
      ? new HttpError(msg)
      : new Error(msg || statuses.message[status])
    Error.captureStackTrace(err, createError)
  }

  if (!HttpError || !(err instanceof HttpError) || err.status !== status) {
    // add properties to generic error
    err.expose = status < 500
    err.status = err.statusCode = status
  }

  for (var key in props) {
    if (key !== 'status' && key !== 'statusCode') {
      err[key] = props[key]
    }
  }

  return err
}

/**
 * Create HTTP error abstract base class.
 * @private
 */

function createHttpErrorConstructor () {
  function HttpError () {
    throw new TypeError('cannot construct abstract class')
  }

  inherits(HttpError, Error)

  return HttpError
}

/**
 * Create a constructor for a client error.
 * @private
 */

function createClientErrorConstructor (HttpError, name, code) {
  var className = toClassName(name)

  function ClientError (message) {
    // create the error object
    var msg = message != null ? message : statuses.message[code]
    var err = new Error(msg)

    // capture a stack trace to the construction point
    Error.captureStackTrace(err, ClientError)

    // adjust the [[Prototype]]
    setPrototypeOf(err, ClientError.prototype)

    // redefine the error message
    Object.defineProperty(err, 'message', {
      enumerable: true,
      configurable: true,
      value: msg,
      writable: true
    })

    // redefine the error name
    Object.defineProperty(err, 'name', {
      enumerable: false,
      configurable: true,
      value: className,
      writable: true
    })

    return err
  }

  inherits(ClientError, HttpError)
  nameFunc(ClientError, className)

  ClientError.prototype.status = code
  ClientError.prototype.statusCode = code
  ClientError.prototype.expose = true

  return ClientError
}

/**
 * Create function to test is a value is a HttpError.
 * @private
 */

function createIsHttpErrorFunction (HttpError) {
  return function isHttpError (val) {
    if (!val || typeof val !== 'object') {
      return false
    }

    if (val instanceof HttpError) {
      return true
    }

    return val instanceof Error &&
      typeof val.expose === 'boolean' &&
      typeof val.statusCode === 'number' && val.status === val.statusCode
  }
}

/**
 * Create a constructor for a server error.
 * @private
 */

function createServerErrorConstructor (HttpError, name, code) {
  var className = toClassName(name)

  function ServerError (message) {
    // create the error object
    var msg = message != null ? message : statuses.message[code]
    var err = new Error(msg)

    // capture a stack trace to the construction point
    Error.captureStackTrace(err, ServerError)

    // adjust the [[Prototype]]
    setPrototypeOf(err, ServerError.prototype)

    // redefine the error message
    Object.defineProperty(err, 'message', {
      enumerable: true,
      configurable: true,
      value: msg,
      writable: true
    })

    // redefine the error name
    Object.defineProperty(err, 'name', {
      enumerable: false,
      configurable: true,
      value: className,
      writable: true
    })

    return err
  }

  inherits(ServerError, HttpError)
  nameFunc(ServerError, className)

  ServerError.prototype.status = code
  ServerError.prototype.statusCode = code
  ServerError.prototype.expose = false

  return ServerError
}

/**
 * Set the name of a function, if possible.
 * @private
 */

function nameFunc (func, name) {
  var desc = Object.getOwnPropertyDescriptor(func, 'name')

  if (desc && desc.configurable) {
    desc.value = name
    Object.defineProperty(func, 'name', desc)
  }
}

/**
 * Populate the exports object with constructors for every error class.
 * @private
 */

function populateConstructorExports (exports, codes, HttpError) {
  codes.forEach(function forEachCode (code) {
    var CodeError
    var name = toIdentifier(statuses.message[code])

    switch (codeClass(code)) {
      case 400:
        CodeError = createClientErrorConstructor(HttpError, name, code)
        break
      case 500:
        CodeError = createServerErrorConstructor(HttpError, name, code)
        break
    }

    if (CodeError) {
      // export the constructor
      exports[code] = CodeError
      exports[name] = CodeError
    }
  })
}

/**
 * Get a class name from a name identifier.
 * @private
 */

function toClassName (name) {
  return name.substr(-5) !== 'Error'
    ? name + 'Error'
    : name
}


http-errors

description: Create HTTP error objects

author: Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)

contributors: Douglas Christopher Wilson <doug@somethingdoug.com>

repository: jshttp/http-errors

lint: eslint . && node ./scripts/lint-readme-list.js

test: mocha --reporter spec --bail

test-ci: nyc --reporter=lcov --reporter=text npm test

test-cov: nyc --reporter=html --reporter=text npm test

version: node scripts/version-history.js && git add HISTORY.md

keywords: http error


code:
![Node](https://github.com/creationix/http-parser-js/workflows/Node/badge.svg)
![Node-v12](https://github.com/creationix/http-parser-js/workflows/Node-v12/badge.svg)

# HTTP Parser

This library parses HTTP protocol for requests and responses.
It was created to replace `http_parser.c` since calling C++ functions from JS is really slow in V8.
However, it is now primarily useful in having a more flexible/tolerant HTTP parser when dealing with legacy services that do not meet the strict HTTP parsing rules Node's parser follows.

This is packaged as a standalone npm module.
To use in node, monkeypatch HTTPParser.

```js
// Monkey patch before you require http for the first time.
process.binding('http_parser').HTTPParser = require('http-parser-js').HTTPParser;

var http = require('http');
// ...
```

## Testing

Simply run `npm test`.
The tests are copied from node and mscedex/io.js, with some modifcations.

## Status

This should now be usable in any node application, it now supports (nearly) everything `http_parser.c` does while still being tolerant with corrupted headers, and other kinds of malformed data.

### Node versions

`http-parser-js` should work via monkey-patching on Node v6-v11, and v13-14.

Node v12.x renamed the internal http parser, and did not expose it for monkey-patching, so to be able to monkey-patch on Node v12, you must run `node --http-parser=legacy file.js` to opt in to the old, monkey-patchable http_parser binding.

## Standalone usage

While this module is intended to be used as a replacement for the internal Node.js parser, it can be used as a standalone parser. The [`standalone-example.js`](standalone-example.js) demonstrates how to use the somewhat awkward API (coming from compatibility with the Node.js internals) to parse HTTP from raw Buffers.

## License

MIT.
See [LICENSE.md](LICENSE.md)


code:
type ParserType =
  | 'REQUEST'
  | 'RESPONSE'

type RequestMethod =
  | 'DELETE'
  | 'GET'
  | 'HEAD'
  | 'POST'
  | 'PUT'
  | 'CONNECT'
  | 'OPTIONS'
  | 'TRACE'
  | 'COPY'
  | 'LOCK'
  | 'MKCOL'
  | 'MOVE'
  | 'PROPFIND'
  | 'PROPPATCH'
  | 'SEARCH'
  | 'UNLOCK'
  | 'BIND'
  | 'REBIND'
  | 'UNBIND'
  | 'ACL'
  | 'REPORT'
  | 'MKACTIVITY'
  | 'CHECKOUT'
  | 'MERGE'
  | 'M-SEARCH'
  | 'NOTIFY'
  | 'SUBSCRIBE'
  | 'UNSUBSCRIBE'
  | 'PATCH'
  | 'PURGE'
  | 'MKCALENDAR'
  | 'LINK'
  | 'UNLINK'
  | string

type StateHeaderKey =
  | 'REQUEST_LINE'
  | 'RESPONSE_LINE'
  | 'HEADER'

type StateFinishAllowedKey =
  | 'REQUEST_LINE'
  | 'RESPONSE_LINE'
  | 'BODY_RAW'

type HeaderObject = Array<string>
type noop<T = void> = ()=> T

type HeaderInfo<HEADER = HeaderObject> = {
  versionMajor: number
  versionMinor: number
  headers: HEADER
  method: number
  url: string
  statusCode: number
  statusMessage: string
  upgrade: boolean
  shouldKeepAlive: boolean
}
export type OnHeadersCompleteParser<HEADER = HeaderObject, Mode_0_12 extends boolean = true> = Mode_0_12 extends true
  ? (info: HeaderInfo<HEADER>)=> number | void
  : (
    versionMajor: number,
    versionMinor: number,
    headers: HEADER,
    method: number,
    url: string,
    statusCode: number,
    statusMessage: string,
    upgrade: boolean,
    shouldKeepAlive: boolean,
  )=> number | void
export type OnBodyParser = (chunk: Buffer, offset: number, length: number)=> void
// Only called in the slow case where slow means
// that the request headers were either fragmented
// across multiple TCP packets or too large to be
// processed in a single run. This method is also
// called to process trailing HTTP headers.
export type OnHeadersParser = (headers: string[], url: string)=> void

declare class HTTPParserJS {
  initialize(type: ParserType, async_resource?: unknown): void

  // Some handler stubs, needed for compatibility
  [HTTPParser.kOnHeaders]: OnHeadersParser
  [HTTPParser.kOnHeadersComplete]: OnHeadersCompleteParser
  [HTTPParser.kOnBody]: OnBodyParser
  [HTTPParser.kOnMessageComplete]: noop

  reinitialize: HTTPParserConstructor
  close: noop
  pause: noop
  resume: noop
  free: noop
  private _compatMode0_11: false | boolean
  getAsyncId: noop<0>

  execute(chunk: Buffer, start?: number, length?: number): number | Error
  finish(): void | Error

  // These three methods are used for an internal speed optimization, and it also
  // works if theses are noops. Basically consume() asks us to read the bytes
  // ourselves, but if we don't do it we get them through execute().
  consume: noop
  unconsume: noop
  getCurrentBuffer: noop

  /**
   * For correct error handling - see HTTPParser#execute
   * @example this.userCall()(userFunction('arg'));
   */
  userCall<T = unknown>(): (ret?: T)=> T
  private nextRequest: noop
  private consumeLine: noop<string|void>
  parseHeader(line: string, headers: string[]): void
  private REQUEST_LINE: noop
  private RESPONSE_LINE: noop
  shouldKeepAlive(): boolean
  /**
   * For older versions of node (v6.x and older?), that return `skipBody=1` or `skipBody=true`, need this `return true;` if it's an upgrade request.
   */
  private HEADER(): void | boolean
  private BODY_CHUNKHEAD(): void
  private BODY_CHUNK(): void
  private BODY_CHUNKEMPTYLINE(): void
  private BODY_CHUNKTRAILERS(): void
  private BODY_RAW(): void
  private BODY_SIZED(): void

  get onHeaders(): OnHeadersParser
  set onHeaders(to: OnHeadersParser)

  get onHeadersComplete(): OnHeadersCompleteParser
  set onHeadersComplete(to: OnHeadersCompleteParser)

  get onBody(): OnBodyParser
  set onBody(to: OnBodyParser)

  get onMessageComplete(): noop
  set onMessageComplete(to: noop)
}

interface HTTPParserConstructor extends Function {
  new(type?: ParserType): HTTPParserJS
  (type?: ParserType): void

  readonly prototype: HTTPParserJS

  readonly REQUEST: 'REQUEST'
  readonly RESPONSE: 'RESPONSE'
  readonly methods: RequestMethod[]

  encoding: 'ascii'|string
  /**
   * maxHeaderSize (in bytes) is configurable, but 80kb by default;
   * @default 80 * 1024 = 80kb
   */
  maxHeaderSize: 81920|number

  // Note: *not* starting with kOnHeaders=0 line the Node parser, because any
  // newly added constants (kOnTimeout in Node v12.19.0) will overwrite 0!
  readonly kOnHeaders: 1
  readonly kOnHeadersComplete: 2
  readonly kOnBody: 3
  readonly kOnMessageComplete: 4

  kOnExecute(): void
}
export const HTTPParser: HTTPParserConstructor
export const methods: RequestMethod[]


code:
/*jshint node:true */

var assert = require('assert');

exports.HTTPParser = HTTPParser;
function HTTPParser(type) {
  assert.ok(type === HTTPParser.REQUEST || type === HTTPParser.RESPONSE || type === undefined);
  if (type === undefined) {
    // Node v12+
  } else {
    this.initialize(type);
  }
  this.maxHeaderSize=HTTPParser.maxHeaderSize
}
HTTPParser.prototype.initialize = function (type, async_resource) {
  assert.ok(type === HTTPParser.REQUEST || type === HTTPParser.RESPONSE);
  this.type = type;
  this.state = type + '_LINE';
  this.info = {
    headers: [],
    upgrade: false
  };
  this.trailers = [];
  this.line = '';
  this.isChunked = false;
  this.connection = '';
  this.headerSize = 0; // for preventing too big headers
  this.body_bytes = null;
  this.isUserCall = false;
  this.hadError = false;
};

HTTPParser.encoding = 'ascii';
HTTPParser.maxHeaderSize = 80 * 1024; // maxHeaderSize (in bytes) is configurable, but 80kb by default;
HTTPParser.REQUEST = 'REQUEST';
HTTPParser.RESPONSE = 'RESPONSE';

// Note: *not* starting with kOnHeaders=0 line the Node parser, because any
//   newly added constants (kOnTimeout in Node v12.19.0) will overwrite 0!
var kOnHeaders = HTTPParser.kOnHeaders = 1;
var kOnHeadersComplete = HTTPParser.kOnHeadersComplete = 2;
var kOnBody = HTTPParser.kOnBody = 3;
var kOnMessageComplete = HTTPParser.kOnMessageComplete = 4;

// Some handler stubs, needed for compatibility
HTTPParser.prototype[kOnHeaders] =
HTTPParser.prototype[kOnHeadersComplete] =
HTTPParser.prototype[kOnBody] =
HTTPParser.prototype[kOnMessageComplete] = function () {};

var compatMode0_12 = true;
Object.defineProperty(HTTPParser, 'kOnExecute', {
    get: function () {
      // hack for backward compatibility
      compatMode0_12 = false;
      return 99;
    }
  });

var methods = exports.methods = HTTPParser.methods = [
  'DELETE',
  'GET',
  'HEAD',
  'POST',
  'PUT',
  'CONNECT',
  'OPTIONS',
  'TRACE',
  'COPY',
  'LOCK',
  'MKCOL',
  'MOVE',
  'PROPFIND',
  'PROPPATCH',
  'SEARCH',
  'UNLOCK',
  'BIND',
  'REBIND',
  'UNBIND',
  'ACL',
  'REPORT',
  'MKACTIVITY',
  'CHECKOUT',
  'MERGE',
  'M-SEARCH',
  'NOTIFY',
  'SUBSCRIBE',
  'UNSUBSCRIBE',
  'PATCH',
  'PURGE',
  'MKCALENDAR',
  'LINK',
  'UNLINK',
  'SOURCE',
];
var method_connect = methods.indexOf('CONNECT');
HTTPParser.prototype.reinitialize = HTTPParser;
HTTPParser.prototype.close =
HTTPParser.prototype.pause =
HTTPParser.prototype.resume =
HTTPParser.prototype.free = function () {};
HTTPParser.prototype._compatMode0_11 = false;
HTTPParser.prototype.getAsyncId = function() { return 0; };

var headerState = {
  REQUEST_LINE: true,
  RESPONSE_LINE: true,
  HEADER: true
};
HTTPParser.prototype.execute = function (chunk, start, length) {
  if (!(this instanceof HTTPParser)) {
    throw new TypeError('not a HTTPParser');
  }

  // backward compat to node < 0.11.4
  // Note: the start and length params were removed in newer version
  start = start || 0;
  length = typeof length === 'number' ? length : chunk.length;

  this.chunk = chunk;
  this.offset = start;
  var end = this.end = start + length;
  try {
    while (this.offset < end) {
      if (this[this.state]()) {
        break;
      }
    }
  } catch (err) {
    if (this.isUserCall) {
      throw err;
    }
    this.hadError = true;
    return err;
  }
  this.chunk = null;
  length = this.offset - start;
  if (headerState[this.state]) {
    this.headerSize += length;
    if (this.headerSize > (this.maxHeaderSize||HTTPParser.maxHeaderSize)) {
      return new Error('max header size exceeded');
    }
  }
  return length;
};

var stateFinishAllowed = {
  REQUEST_LINE: true,
  RESPONSE_LINE: true,
  BODY_RAW: true
};
HTTPParser.prototype.finish = function () {
  if (this.hadError) {
    return;
  }
  if (!stateFinishAllowed[this.state]) {
    return new Error('invalid state for EOF');
  }
  if (this.state === 'BODY_RAW') {
    this.userCall()(this[kOnMessageComplete]());
  }
};

// These three methods are used for an internal speed optimization, and it also
// works if theses are noops. Basically consume() asks us to read the bytes
// ourselves, but if we don't do it we get them through execute().
HTTPParser.prototype.consume =
HTTPParser.prototype.unconsume =
HTTPParser.prototype.getCurrentBuffer = function () {};

//For correct error handling - see HTTPParser#execute
//Usage: this.userCall()(userFunction('arg'));
HTTPParser.prototype.userCall = function () {
  this.isUserCall = true;
  var self = this;
  return function (ret) {
    self.isUserCall = false;
    return ret;
  };
};

HTTPParser.prototype.nextRequest = function () {
  this.userCall()(this[kOnMessageComplete]());
  this.reinitialize(this.type);
};

HTTPParser.prototype.consumeLine = function () {
  var end = this.end,
      chunk = this.chunk;
  for (var i = this.offset; i < end; i++) {
    if (chunk[i] === 0x0a) { // \n
      var line = this.line + chunk.toString(HTTPParser.encoding, this.offset, i);
      if (line.charAt(line.length - 1) === '\r') {
        line = line.substr(0, line.length - 1);
      }
      this.line = '';
      this.offset = i + 1;
      return line;
    }
  }
  //line split over multiple chunks
  this.line += chunk.toString(HTTPParser.encoding, this.offset, this.end);
  this.offset = this.end;
};

var headerExp = /^([^: \t]+):[ \t]*((?:.*[^ \t])|)/;
var headerContinueExp = /^[ \t]+(.*[^ \t])/;
HTTPParser.prototype.parseHeader = function (line, headers) {
  if (line.indexOf('\r') !== -1) {
    throw parseErrorCode('HPE_LF_EXPECTED');
  }

  var match = headerExp.exec(line);
  var k = match && match[1];
  if (k) { // skip empty string (malformed header)
    headers.push(k);
    headers.push(match[2]);
  } else {
    var matchContinue = headerContinueExp.exec(line);
    if (matchContinue && headers.length) {
      if (headers[headers.length - 1]) {
        headers[headers.length - 1] += ' ';
      }
      headers[headers.length - 1] += matchContinue[1];
    }
  }
};

var requestExp = /^([A-Z-]+) ([^ ]+) HTTP\/(\d)\.(\d)$/;
HTTPParser.prototype.REQUEST_LINE = function () {
  var line = this.consumeLine();
  if (!line) {
    return;
  }
  var match = requestExp.exec(line);
  if (match === null) {
    throw parseErrorCode('HPE_INVALID_CONSTANT');
  }
  this.info.method = this._compatMode0_11 ? match[1] : methods.indexOf(match[1]);
  if (this.info.method === -1) {
    throw new Error('invalid request method');
  }
  this.info.url = match[2];
  this.info.versionMajor = +match[3];
  this.info.versionMinor = +match[4];
  this.body_bytes = 0;
  this.state = 'HEADER';
};

var responseExp = /^HTTP\/(\d)\.(\d) (\d{3}) ?(.*)$/;
HTTPParser.prototype.RESPONSE_LINE = function () {
  var line = this.consumeLine();
  if (!line) {
    return;
  }
  var match = responseExp.exec(line);
  if (match === null) {
    throw parseErrorCode('HPE_INVALID_CONSTANT');
  }
  this.info.versionMajor = +match[1];
  this.info.versionMinor = +match[2];
  var statusCode = this.info.statusCode = +match[3];
  this.info.statusMessage = match[4];
  // Implied zero length.
  if ((statusCode / 100 | 0) === 1 || statusCode === 204 || statusCode === 304) {
    this.body_bytes = 0;
  }
  this.state = 'HEADER';
};

HTTPParser.prototype.shouldKeepAlive = function () {
  if (this.info.versionMajor > 0 && this.info.versionMinor > 0) {
    if (this.connection.indexOf('close') !== -1) {
      return false;
    }
  } else if (this.connection.indexOf('keep-alive') === -1) {
    return false;
  }
  if (this.body_bytes !== null || this.isChunked) { // || skipBody
    return true;
  }
  return false;
};

HTTPParser.prototype.HEADER = function () {
  var line = this.consumeLine();
  if (line === undefined) {
    return;
  }
  var info = this.info;
  if (line) {
    this.parseHeader(line, info.headers);
  } else {
    var headers = info.headers;
    var hasContentLength = false;
    var currentContentLengthValue;
    var hasUpgradeHeader = false;
    for (var i = 0; i < headers.length; i += 2) {
      switch (headers[i].toLowerCase()) {
        case 'transfer-encoding':
          this.isChunked = headers[i + 1].toLowerCase() === 'chunked';
          break;
        case 'content-length':
          currentContentLengthValue = +headers[i + 1];
          if (hasContentLength) {
            // Fix duplicate Content-Length header with same values.
            // Throw error only if values are different.
            // Known issues:
            // https://github.com/request/request/issues/2091#issuecomment-328715113
            // https://github.com/nodejs/node/issues/6517#issuecomment-216263771
            if (currentContentLengthValue !== this.body_bytes) {
              throw parseErrorCode('HPE_UNEXPECTED_CONTENT_LENGTH');
            }
          } else {
            hasContentLength = true;
            this.body_bytes = currentContentLengthValue;
          }
          break;
        case 'connection':
          this.connection += headers[i + 1].toLowerCase();
          break;
        case 'upgrade':
          hasUpgradeHeader = true;
          break;
      }
    }

    // if both isChunked and hasContentLength, isChunked wins
    // This is required so the body is parsed using the chunked method, and matches
    // Chrome's behavior.  We could, maybe, ignore them both (would get chunked
    // encoding into the body), and/or disable shouldKeepAlive to be more
    // resilient.
    if (this.isChunked && hasContentLength) {
      hasContentLength = false;
      this.body_bytes = null;
    }

    // Logic from https://github.com/nodejs/http-parser/blob/921d5585515a153fa00e411cf144280c59b41f90/http_parser.c#L1727-L1737
    // "For responses, "Upgrade: foo" and "Connection: upgrade" are
    //   mandatory only when it is a 101 Switching Protocols response,
    //   otherwise it is purely informational, to announce support.
    if (hasUpgradeHeader && this.connection.indexOf('upgrade') != -1) {
      info.upgrade = this.type === HTTPParser.REQUEST || info.statusCode === 101;
    } else {
      info.upgrade = info.method === method_connect;
    }

    if (this.isChunked && info.upgrade) {
      this.isChunked = false;
    }

    info.shouldKeepAlive = this.shouldKeepAlive();
    //problem which also exists in original node: we should know skipBody before calling onHeadersComplete
    var skipBody;
    if (compatMode0_12) {
      skipBody = this.userCall()(this[kOnHeadersComplete](info));
    } else {
      skipBody = this.userCall()(this[kOnHeadersComplete](info.versionMajor,
          info.versionMinor, info.headers, info.method, info.url, info.statusCode,
          info.statusMessage, info.upgrade, info.shouldKeepAlive));
    }
    if (skipBody === 2) {
      this.nextRequest();
      return true;
    } else if (this.isChunked && !skipBody) {
      this.state = 'BODY_CHUNKHEAD';
    } else if (skipBody || this.body_bytes === 0) {
      this.nextRequest();
      // For older versions of node (v6.x and older?), that return skipBody=1 or skipBody=true,
      //   need this "return true;" if it's an upgrade request.
      return info.upgrade;
    } else if (this.body_bytes === null) {
      this.state = 'BODY_RAW';
    } else {
      this.state = 'BODY_SIZED';
    }
  }
};

HTTPParser.prototype.BODY_CHUNKHEAD = function () {
  var line = this.consumeLine();
  if (line === undefined) {
    return;
  }
  this.body_bytes = parseInt(line, 16);
  if (!this.body_bytes) {
    this.state = 'BODY_CHUNKTRAILERS';
  } else {
    this.state = 'BODY_CHUNK';
  }
};

HTTPParser.prototype.BODY_CHUNK = function () {
  var length = Math.min(this.end - this.offset, this.body_bytes);
  this.userCall()(this[kOnBody](this.chunk, this.offset, length));
  this.offset += length;
  this.body_bytes -= length;
  if (!this.body_bytes) {
    this.state = 'BODY_CHUNKEMPTYLINE';
  }
};

HTTPParser.prototype.BODY_CHUNKEMPTYLINE = function () {
  var line = this.consumeLine();
  if (line === undefined) {
    return;
  }
  assert.equal(line, '');
  this.state = 'BODY_CHUNKHEAD';
};

HTTPParser.prototype.BODY_CHUNKTRAILERS = function () {
  var line = this.consumeLine();
  if (line === undefined) {
    return;
  }
  if (line) {
    this.parseHeader(line, this.trailers);
  } else {
    if (this.trailers.length) {
      this.userCall()(this[kOnHeaders](this.trailers, ''));
    }
    this.nextRequest();
  }
};

HTTPParser.prototype.BODY_RAW = function () {
  var length = this.end - this.offset;
  this.userCall()(this[kOnBody](this.chunk, this.offset, length));
  this.offset = this.end;
};

HTTPParser.prototype.BODY_SIZED = function () {
  var length = Math.min(this.end - this.offset, this.body_bytes);
  this.userCall()(this[kOnBody](this.chunk, this.offset, length));
  this.offset += length;
  this.body_bytes -= length;
  if (!this.body_bytes) {
    this.nextRequest();
  }
};

// backward compat to node < 0.11.6
['Headers', 'HeadersComplete', 'Body', 'MessageComplete'].forEach(function (name) {
  var k = HTTPParser['kOn' + name];
  Object.defineProperty(HTTPParser.prototype, 'on' + name, {
    get: function () {
      return this[k];
    },
    set: function (to) {
      // hack for backward compatibility
      this._compatMode0_11 = true;
      method_connect = 'CONNECT';
      return (this[k] = to);
    }
  });
});

function parseErrorCode(code) {
  var err = new Error('Parse Error');
  err.code = code;
  return err;
}


http-parser-js

description: A pure JS HTTP parser for node.

test: python tests/test.py && node tests/iojs/test-http-parser-durability.js

testv12: python tests/test.py --node-args="--http-parser=legacy" && node --http-parser=legacy tests/iojs/test-http-parser-durability.js


code:
# http-proxy-middleware

[![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/chimurai/http-proxy-middleware/CI/master?style=flat-square)](https://github.com/chimurai/http-proxy-middleware/actions?query=branch%3Amaster)
[![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square)](https://coveralls.io/r/chimurai/http-proxy-middleware)
[![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg?style=flat-square)](https://snyk.io/test/npm/http-proxy-middleware)
[![npm](https://img.shields.io/npm/v/http-proxy-middleware?color=%23CC3534&style=flat-square)](https://www.npmjs.com/package/http-proxy-middleware)

Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/strongloop/express), [browser-sync](https://github.com/BrowserSync/browser-sync) and [many more](#compatible-servers).

Powered by the popular Nodejitsu [`http-proxy`](https://github.com/nodejitsu/node-http-proxy). [![GitHub stars](https://img.shields.io/github/stars/nodejitsu/node-http-proxy.svg?style=social&label=Star)](https://github.com/nodejitsu/node-http-proxy)

## ⚠ Note <!-- omit in toc -->

This page is showing documentation for version v2.x.x ([release notes](https://github.com/chimurai/http-proxy-middleware/releases))

If you're looking for v0.x documentation. Go to:
https://github.com/chimurai/http-proxy-middleware/tree/v0.21.0#readme

## TL;DR <!-- omit in toc -->

Proxy `/api` requests to `http://www.example.org`

```javascript
// javascript

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);

// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
```

```typescript
// typescript

import * as express from 'express';
import { createProxyMiddleware, Filter, Options, RequestHandler } from 'http-proxy-middleware';

const app = express();

app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);

// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
```

_All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#options) can be used, along with some extra `http-proxy-middleware` [options](#options).

:bulb: **Tip:** Set the option `changeOrigin` to `true` for [name-based virtual hosted sites](http://en.wikipedia.org/wiki/Virtual_hosting#Name-based).

## Table of Contents <!-- omit in toc -->

- [Install](#install)
- [Core concept](#core-concept)
- [Example](#example)
- [Context matching](#context-matching)
- [Options](#options)
  - [http-proxy-middleware options](#http-proxy-middleware-options)
  - [http-proxy events](#http-proxy-events)
  - [http-proxy options](#http-proxy-options)
- [Shorthand](#shorthand)
  - [app.use(path, proxy)](#appusepath-proxy)
- [WebSocket](#websocket)
  - [External WebSocket upgrade](#external-websocket-upgrade)
- [Intercept and manipulate requests](#intercept-and-manipulate-requests)
- [Intercept and manipulate responses](#intercept-and-manipulate-responses)
- [Working examples](#working-examples)
- [Recipes](#recipes)
- [Compatible servers](#compatible-servers)
- [Tests](#tests)
- [Changelog](#changelog)
- [License](#license)

## Install

```bash
$ npm install --save-dev http-proxy-middleware
```

## Core concept

Proxy middleware configuration.

#### createProxyMiddleware([context,] config)

```javascript
const { createProxyMiddleware } = require('http-proxy-middleware');

const apiProxy = createProxyMiddleware('/api', { target: 'http://www.example.org' });
//                                    \____/   \_____________________________/
//                                      |                    |
//                                    context             options

// 'apiProxy' is now ready to be used as middleware in a server.
```

- **context**: Determine which requests should be proxied to the target host.
  (more on [context matching](#context-matching))
- **options.target**: target host to proxy to. _(protocol + host)_

(full list of [`http-proxy-middleware` configuration options](#options))

#### createProxyMiddleware(uri [, config])

```javascript
// shorthand syntax for the example above:
const apiProxy = createProxyMiddleware('http://www.example.org/api');
```

More about the [shorthand configuration](#shorthand).

## Example

An example with `express` server.

```javascript
// include dependencies
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

// proxy middleware options
/** @type {import('http-proxy-middleware/dist/types').Options} */
const options = {
  target: 'http://www.example.org', // target host
  changeOrigin: true, // needed for virtual hosted sites
  ws: true, // proxy websockets
  pathRewrite: {
    '^/api/old-path': '/api/new-path', // rewrite path
    '^/api/remove/path': '/path', // remove base path
  },
  router: {
    // when request.headers.host == 'dev.localhost:3000',
    // override target 'http://www.example.org' to 'http://localhost:8000'
    'dev.localhost:3000': 'http://localhost:8000',
  },
};

// create the proxy (without context)
const exampleProxy = createProxyMiddleware(options);

// mount `exampleProxy` in web server
const app = express();
app.use('/api', exampleProxy);
app.listen(3000);
```

## Context matching

Providing an alternative way to decide which requests should be proxied; In case you are not able to use the server's [`path` parameter](http://expressjs.com/en/4x/api.html#app.use) to mount the proxy or when you need more flexibility.

[RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used for context matching.

```ascii
         foo://example.com:8042/over/there?name=ferret#nose
         \_/   \______________/\_________/ \_________/ \__/
          |           |            |            |        |
       scheme     authority       path        query   fragment
```

- **path matching**

  - `createProxyMiddleware({...})` - matches any path, all requests will be proxied.
  - `createProxyMiddleware('/', {...})` - matches any path, all requests will be proxied.
  - `createProxyMiddleware('/api', {...})` - matches paths starting with `/api`

- **multiple path matching**

  - `createProxyMiddleware(['/api', '/ajax', '/someotherpath'], {...})`

- **wildcard path matching**

  For fine-grained control you can use wildcard matching. Glob pattern matching is done by _micromatch_. Visit [micromatch](https://www.npmjs.com/package/micromatch) or [glob](https://www.npmjs.com/package/glob) for more globbing examples.

  - `createProxyMiddleware('**', {...})` matches any path, all requests will be proxied.
  - `createProxyMiddleware('**/*.html', {...})` matches any path which ends with `.html`
  - `createProxyMiddleware('/*.html', {...})` matches paths directly under path-absolute
  - `createProxyMiddleware('/api/**/*.html', {...})` matches requests ending with `.html` in the path of `/api`
  - `createProxyMiddleware(['/api/**', '/ajax/**'], {...})` combine multiple patterns
  - `createProxyMiddleware(['/api/**', '!**/bad.json'], {...})` exclusion

  **Note**: In multiple path matching, you cannot use string paths and wildcard paths together.

- **custom matching**

  For full control you can provide a custom function to determine which requests should be proxied or not.

  ```javascript
  /**
   * @return {Boolean}
   */
  const filter = function (pathname, req) {
    return pathname.match('^/api') && req.method === 'GET';
  };

  const apiProxy = createProxyMiddleware(filter, {
    target: 'http://www.example.org',
  });
  ```

## Options

### http-proxy-middleware options

- **option.pathRewrite**: object/function, rewrite target's url path. Object-keys will be used as _RegExp_ to match paths.

  ```javascript
  // rewrite path
  pathRewrite: {'^/old/api' : '/new/api'}

  // remove path
  pathRewrite: {'^/remove/api' : ''}

  // add base path
  pathRewrite: {'^/' : '/basepath/'}

  // custom rewriting
  pathRewrite: function (path, req) { return path.replace('/api', '/base/api') }

  // custom rewriting, returning Promise
  pathRewrite: async function (path, req) {
    const should_add_something = await httpRequestToDecideSomething(path);
    if (should_add_something) path += "something";
    return path;
  }
  ```

- **option.router**: object/function, re-target `option.target` for specific requests.

  ```javascript
  // Use `host` and/or `path` to match requests. First match will be used.
  // The order of the configuration matters.
  router: {
      'integration.localhost:3000' : 'http://localhost:8001',  // host only
      'staging.localhost:3000'     : 'http://localhost:8002',  // host only
      'localhost:3000/api'         : 'http://localhost:8003',  // host + path
      '/rest'                      : 'http://localhost:8004'   // path only
  }

  // Custom router function (string target)
  router: function(req) {
      return 'http://localhost:8004';
  }

  // Custom router function (target object)
  router: function(req) {
      return {
          protocol: 'https:', // The : is required
          host: 'localhost',
          port: 8004
      };
  }

  // Asynchronous router function which returns promise
  router: async function(req) {
      const url = await doSomeIO();
      return url;
  }
  ```

- **option.logLevel**: string, ['debug', 'info', 'warn', 'error', 'silent']. Default: `'info'`

- **option.logProvider**: function, modify or replace log provider. Default: `console`.

  ```javascript
  // simple replace
  function logProvider(provider) {
    // replace the default console log provider.
    return require('winston');
  }
  ```

  ```javascript
  // verbose replacement
  function logProvider(provider) {
    const logger = new (require('winston').Logger)();

    const myCustomProvider = {
      log: logger.log,
      debug: logger.debug,
      info: logger.info,
      warn: logger.warn,
      error: logger.error,
    };
    return myCustomProvider;
  }
  ```

### http-proxy events

Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events):

- **option.onError**: function, subscribe to http-proxy's `error` event for custom error handling.

  ```javascript
  function onError(err, req, res, target) {
    res.writeHead(500, {
      'Content-Type': 'text/plain',
    });
    res.end('Something went wrong. And we are reporting a custom error message.');
  }
  ```

- **option.onProxyRes**: function, subscribe to http-proxy's `proxyRes` event.

  ```javascript
  function onProxyRes(proxyRes, req, res) {
    proxyRes.headers['x-added'] = 'foobar'; // add new header to response
    delete proxyRes.headers['x-removed']; // remove header from response
  }
  ```

- **option.onProxyReq**: function, subscribe to http-proxy's `proxyReq` event.

  ```javascript
  function onProxyReq(proxyReq, req, res) {
    // add custom header to request
    proxyReq.setHeader('x-added', 'foobar');
    // or log the req
  }
  ```

- **option.onProxyReqWs**: function, subscribe to http-proxy's `proxyReqWs` event.

  ```javascript
  function onProxyReqWs(proxyReq, req, socket, options, head) {
    // add custom header
    proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
  }
  ```

- **option.onOpen**: function, subscribe to http-proxy's `open` event.

  ```javascript
  function onOpen(proxySocket) {
    // listen for messages coming FROM the target here
    proxySocket.on('data', hybridParseAndLogMessage);
  }
  ```

- **option.onClose**: function, subscribe to http-proxy's `close` event.

  ```javascript
  function onClose(res, socket, head) {
    // view disconnected websocket connections
    console.log('Client disconnected');
  }
  ```

### http-proxy options

The following options are provided by the underlying [http-proxy](https://github.com/nodejitsu/node-http-proxy#options) library.

- **option.target**: url string to be parsed with the url module
- **option.forward**: url string to be parsed with the url module
- **option.agent**: object to be passed to http(s).request (see Node's [https agent](http://nodejs.org/api/https.html#https_class_https_agent) and [http agent](http://nodejs.org/api/http.html#http_class_http_agent) objects)
- **option.ssl**: object to be passed to https.createServer()
- **option.ws**: true/false: if you want to proxy websockets
- **option.xfwd**: true/false, adds x-forward headers
- **option.secure**: true/false, if you want to verify the SSL Certs
- **option.toProxy**: true/false, passes the absolute URL as the `path` (useful for proxying to proxies)
- **option.prependPath**: true/false, Default: true - specify whether you want to prepend the target's path to the proxy path
- **option.ignorePath**: true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request (note: you will have to append / manually if required).
- **option.localAddress** : Local interface string to bind for outgoing connections
- **option.changeOrigin**: true/false, Default: false - changes the origin of the host header to the target URL
- **option.preserveHeaderKeyCase**: true/false, Default: false - specify whether you want to keep letter case of response header key
- **option.auth** : Basic authentication i.e. 'user:password' to compute an Authorization header.
- **option.hostRewrite**: rewrites the location hostname on (301/302/307/308) redirects.
- **option.autoRewrite**: rewrites the location host/port on (301/302/307/308) redirects based on requested host/port. Default: false.
- **option.protocolRewrite**: rewrites the location protocol on (301/302/307/308) redirects to 'http' or 'https'. Default: null.
- **option.cookieDomainRewrite**: rewrites domain of `set-cookie` headers. Possible values:
  - `false` (default): disable cookie rewriting
  - String: new domain, for example `cookieDomainRewrite: "new.domain"`. To remove the domain, use `cookieDomainRewrite: ""`.
  - Object: mapping of domains to new domains, use `"*"` to match all domains.
    For example keep one domain unchanged, rewrite one domain and remove other domains:
    ```json
    cookieDomainRewrite: {
      "unchanged.domain": "unchanged.domain",
      "old.domain": "new.domain",
      "*": ""
    }
    ```
- **option.cookiePathRewrite**: rewrites path of `set-cookie` headers. Possible values:
  - `false` (default): disable cookie rewriting
  - String: new path, for example `cookiePathRewrite: "/newPath/"`. To remove the path, use `cookiePathRewrite: ""`. To set path to root use `cookiePathRewrite: "/"`.
  - Object: mapping of paths to new paths, use `"*"` to match all paths.
    For example, to keep one path unchanged, rewrite one path and remove other paths:
    ```json
    cookiePathRewrite: {
      "/unchanged.path/": "/unchanged.path/",
      "/old.path/": "/new.path/",
      "*": ""
    }
    ```
- **option.headers**: object, adds [request headers](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields). (Example: `{host:'www.example.org'}`)
- **option.proxyTimeout**: timeout (in millis) when proxy receives no response from target
- **option.timeout**: timeout (in millis) for incoming requests
- **option.followRedirects**: true/false, Default: false - specify whether you want to follow redirects
- **option.selfHandleResponse** true/false, if set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the `proxyRes` event
- **option.buffer**: stream of data to send as the request body. Maybe you have some middleware that consumes the request stream before proxying it on e.g. If you read the body of a request into a field called 'req.rawbody' you could restream this field in the buffer option:

  ```javascript
  'use strict';

  const streamify = require('stream-array');
  const HttpProxy = require('http-proxy');
  const proxy = new HttpProxy();

  module.exports = (req, res, next) => {
    proxy.web(
      req,
      res,
      {
        target: 'http://localhost:4003/',
        buffer: streamify(req.rawBody),
      },
      next
    );
  };
  ```

## Shorthand

Use the shorthand syntax when verbose configuration is not needed. The `context` and `option.target` will be automatically configured when shorthand is used. Options can still be used if needed.

```javascript
createProxyMiddleware('http://www.example.org:8000/api');
// createProxyMiddleware('/api', {target: 'http://www.example.org:8000'});

createProxyMiddleware('http://www.example.org:8000/api/books/*/**.json');
// createProxyMiddleware('/api/books/*/**.json', {target: 'http://www.example.org:8000'});

createProxyMiddleware('http://www.example.org:8000/api', { changeOrigin: true });
// createProxyMiddleware('/api', {target: 'http://www.example.org:8000', changeOrigin: true});
```

### app.use(path, proxy)

If you want to use the server's `app.use` `path` parameter to match requests;
Create and mount the proxy without the http-proxy-middleware `context` parameter:

```javascript
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true }));
```

`app.use` documentation:

- express: http://expressjs.com/en/4x/api.html#app.use
- connect: https://github.com/senchalabs/connect#mount-middleware
- polka: https://github.com/lukeed/polka#usebase-fn

## WebSocket

```javascript
// verbose api
createProxyMiddleware('/', { target: 'http://echo.websocket.org', ws: true });

// shorthand
createProxyMiddleware('http://echo.websocket.org', { ws: true });

// shorter shorthand
createProxyMiddleware('ws://echo.websocket.org');
```

### External WebSocket upgrade

In the previous WebSocket examples, http-proxy-middleware relies on a initial http request in order to listen to the http `upgrade` event. If you need to proxy WebSockets without the initial http request, you can subscribe to the server's http `upgrade` event manually.

```javascript
const wsProxy = createProxyMiddleware('ws://echo.websocket.org', { changeOrigin: true });

const app = express();
app.use(wsProxy);

const server = app.listen(3000);
server.on('upgrade', wsProxy.upgrade); // <-- subscribe to http 'upgrade'
```

## Intercept and manipulate requests

Intercept requests from downstream by defining `onProxyReq` in `createProxyMiddleware`.

Currently the only pre-provided request interceptor is `fixRequestBody`, which is used to fix proxied POST requests when `bodyParser` is applied before this middleware.

Example:

```javascript
const { createProxyMiddleware, fixRequestBody } = require('http-proxy-middleware');

const proxy = createProxyMiddleware({
  /**
   * Fix bodyParser
   **/
  onProxyReq: fixRequestBody,
});
```

## Intercept and manipulate responses

Intercept responses from upstream with `responseInterceptor`. (Make sure to set `selfHandleResponse: true`)

Responses which are compressed with `brotli`, `gzip` and `deflate` will be decompressed automatically. The response will be returned as `buffer` ([docs](https://nodejs.org/api/buffer.html)) which you can manipulate.

With `buffer`, response manipulation is not limited to text responses (html/css/js, etc...); image manipulation will be possible too. ([example](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#manipulate-image-response))

NOTE: `responseInterceptor` disables streaming of target's response.

Example:

```javascript
const { createProxyMiddleware, responseInterceptor } = require('http-proxy-middleware');

const proxy = createProxyMiddleware({
  /**
   * IMPORTANT: avoid res.end being called automatically
   **/
  selfHandleResponse: true, // res.end() will be called internally by responseInterceptor()

  /**
   * Intercept response and replace 'Hello' with 'Goodbye'
   **/
  onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
    const response = responseBuffer.toString('utf8'); // convert buffer to string
    return response.replace('Hello', 'Goodbye'); // manipulate response and return the result
  }),
});
```

Check out [interception recipes](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#readme) for more examples.

## Working examples

View and play around with [working examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples).

- Browser-Sync ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/browser-sync/index.js))
- express ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/express/index.js))
- connect ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/connect/index.js))
- WebSocket ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/websocket/index.js))
- Response Manipulation ([example source](https://github.com/chimurai/http-proxy-middleware/blob/master/examples/response-interceptor/index.js))

## Recipes

View the [recipes](https://github.com/chimurai/http-proxy-middleware/tree/master/recipes) for common use cases.

## Compatible servers

`http-proxy-middleware` is compatible with the following servers:

- [connect](https://www.npmjs.com/package/connect)
- [express](https://www.npmjs.com/package/express)
- [fastify](https://www.npmjs.com/package/fastify)
- [browser-sync](https://www.npmjs.com/package/browser-sync)
- [lite-server](https://www.npmjs.com/package/lite-server)
- [polka](https://github.com/lukeed/polka)
- [grunt-contrib-connect](https://www.npmjs.com/package/grunt-contrib-connect)
- [grunt-browser-sync](https://www.npmjs.com/package/grunt-browser-sync)
- [gulp-connect](https://www.npmjs.com/package/gulp-connect)
- [gulp-webserver](https://www.npmjs.com/package/gulp-webserver)

Sample implementations can be found in the [server recipes](https://github.com/chimurai/http-proxy-middleware/tree/master/recipes/servers.md).

## Tests

Run the test suite:

```bash
# install dependencies
$ yarn

# linting
$ yarn lint
$ yarn lint:fix

# building (compile typescript to js)
$ yarn build

# unit tests
$ yarn test

# code coverage
$ yarn cover

# check spelling mistakes
$ yarn spellcheck
```

## Changelog

- [View changelog](https://github.com/chimurai/http-proxy-middleware/blob/master/CHANGELOG.md)

## License

The MIT License (MIT)

Copyright (c) 2015-2022 Steven Chim


code:
import type { Options } from './types';
import type * as httpProxy from 'http-proxy';
export declare function init(proxy: httpProxy, option: Options): void;
export declare function getHandlers(options: Options): any;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getHandlers = exports.init = void 0;
const logger_1 = require("./logger");
const logger = (0, logger_1.getInstance)();
function init(proxy, option) {
    const handlers = getHandlers(option);
    for (const eventName of Object.keys(handlers)) {
        proxy.on(eventName, handlers[eventName]);
    }
    // https://github.com/webpack/webpack-dev-server/issues/1642
    proxy.on('econnreset', (error, req, res, target) => {
        logger.error(`[HPM] ECONNRESET: %O`, error);
    });
    // https://github.com/webpack/webpack-dev-server/issues/1642#issuecomment-1104325120
    proxy.on('proxyReqWs', (proxyReq, req, socket, options, head) => {
        socket.on('error', (error) => {
            logger.error(`[HPM] WebSocket error: %O`, error);
        });
    });
    logger.debug('[HPM] Subscribed to http-proxy events:', Object.keys(handlers));
}
exports.init = init;
function getHandlers(options) {
    // https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events
    const proxyEventsMap = {
        error: 'onError',
        proxyReq: 'onProxyReq',
        proxyReqWs: 'onProxyReqWs',
        proxyRes: 'onProxyRes',
        open: 'onOpen',
        close: 'onClose',
    };
    const handlers = {};
    for (const [eventName, onEventName] of Object.entries(proxyEventsMap)) {
        // all handlers for the http-proxy events are prefixed with 'on'.
        // loop through options and try to find these handlers
        // and add them to the handlers object for subscription in init().
        const fnHandler = options ? options[onEventName] : null;
        if (typeof fnHandler === 'function') {
            handlers[eventName] = fnHandler;
        }
    }
    // add default error handler in absence of error handler
    if (typeof handlers.error !== 'function') {
        handlers.error = defaultErrorHandler;
    }
    // add default close handler in absence of close handler
    if (typeof handlers.close !== 'function') {
        handlers.close = logClose;
    }
    return handlers;
}
exports.getHandlers = getHandlers;
function defaultErrorHandler(err, req, res) {
    // Re-throw error. Not recoverable since req & res are empty.
    if (!req && !res) {
        throw err; // "Error: Must provide a proper URL as target"
    }
    const host = req.headers && req.headers.host;
    const code = err.code;
    if (res.writeHead && !res.headersSent) {
        if (/HPE_INVALID/.test(code)) {
            res.writeHead(502);
        }
        else {
            switch (code) {
                case 'ECONNRESET':
                case 'ENOTFOUND':
                case 'ECONNREFUSED':
                case 'ETIMEDOUT':
                    res.writeHead(504);
                    break;
                default:
                    res.writeHead(500);
            }
        }
    }
    res.end(`Error occurred while trying to proxy: ${host}${req.url}`);
}
function logClose(req, socket, head) {
    // view disconnected websocket connections
    logger.info('[HPM] Client disconnected');
}


code:
import { Filter, Options } from './types';
export declare type Config = {
    context: Filter;
    options: Options;
};
export declare function createConfig(context: any, opts?: Options): Config;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createConfig = void 0;
const isPlainObj = require("is-plain-obj");
const url = require("url");
const errors_1 = require("./errors");
const logger_1 = require("./logger");
const logger = (0, logger_1.getInstance)();
function createConfig(context, opts) {
    // structure of config object to be returned
    const config = {
        context: undefined,
        options: {},
    };
    // app.use('/api', proxy({target:'http://localhost:9000'}));
    if (isContextless(context, opts)) {
        config.context = '/';
        config.options = Object.assign(config.options, context);
        // app.use('/api', proxy('http://localhost:9000'));
        // app.use(proxy('http://localhost:9000/api'));
    }
    else if (isStringShortHand(context)) {
        const oUrl = url.parse(context);
        const target = [oUrl.protocol, '//', oUrl.host].join('');
        config.context = oUrl.pathname || '/';
        config.options = Object.assign(config.options, { target }, opts);
        if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') {
            config.options.ws = true;
        }
        // app.use('/api', proxy({target:'http://localhost:9000'}));
    }
    else {
        config.context = context;
        config.options = Object.assign(config.options, opts);
    }
    configureLogger(config.options);
    if (!config.options.target && !config.options.router) {
        throw new Error(errors_1.ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING);
    }
    return config;
}
exports.createConfig = createConfig;
/**
 * Checks if a String only target/config is provided.
 * This can be just the host or with the optional path.
 *
 * @example
 *      app.use('/api', proxy('http://localhost:9000'));
 *      app.use(proxy('http://localhost:9000/api'));
 *
 * @param  {String}  context [description]
 * @return {Boolean}         [description]
 */
function isStringShortHand(context) {
    if (typeof context === 'string') {
        return !!url.parse(context).host;
    }
}
/**
 * Checks if a Object only config is provided, without a context.
 * In this case the all paths will be proxied.
 *
 * @example
 *     app.use('/api', proxy({target:'http://localhost:9000'}));
 *
 * @param  {Object}  context [description]
 * @param  {*}       opts    [description]
 * @return {Boolean}         [description]
 */
function isContextless(context, opts) {
    return isPlainObj(context) && (opts == null || Object.keys(opts).length === 0);
}
function configureLogger(options) {
    if (options.logLevel) {
        logger.setLevel(options.logLevel);
    }
    if (options.logProvider) {
        logger.setProvider(options.logProvider);
    }
}


code:
import type { Filter, Request } from './types';
export declare function match(context: Filter, uri: string, req: Request): boolean;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.match = void 0;
const isGlob = require("is-glob");
const micromatch = require("micromatch");
const url = require("url");
const errors_1 = require("./errors");
function match(context, uri, req) {
    // single path
    if (isStringPath(context)) {
        return matchSingleStringPath(context, uri);
    }
    // single glob path
    if (isGlobPath(context)) {
        return matchSingleGlobPath(context, uri);
    }
    // multi path
    if (Array.isArray(context)) {
        if (context.every(isStringPath)) {
            return matchMultiPath(context, uri);
        }
        if (context.every(isGlobPath)) {
            return matchMultiGlobPath(context, uri);
        }
        throw new Error(errors_1.ERRORS.ERR_CONTEXT_MATCHER_INVALID_ARRAY);
    }
    // custom matching
    if (typeof context === 'function') {
        const pathname = getUrlPathName(uri);
        return context(pathname, req);
    }
    throw new Error(errors_1.ERRORS.ERR_CONTEXT_MATCHER_GENERIC);
}
exports.match = match;
/**
 * @param  {String} context '/api'
 * @param  {String} uri     'http://example.org/api/b/c/d.html'
 * @return {Boolean}
 */
function matchSingleStringPath(context, uri) {
    const pathname = getUrlPathName(uri);
    return pathname.indexOf(context) === 0;
}
function matchSingleGlobPath(pattern, uri) {
    const pathname = getUrlPathName(uri);
    const matches = micromatch([pathname], pattern);
    return matches && matches.length > 0;
}
function matchMultiGlobPath(patternList, uri) {
    return matchSingleGlobPath(patternList, uri);
}
/**
 * @param  {String} contextList ['/api', '/ajax']
 * @param  {String} uri     'http://example.org/api/b/c/d.html'
 * @return {Boolean}
 */
function matchMultiPath(contextList, uri) {
    let isMultiPath = false;
    for (const context of contextList) {
        if (matchSingleStringPath(context, uri)) {
            isMultiPath = true;
            break;
        }
    }
    return isMultiPath;
}
/**
 * Parses URI and returns RFC 3986 path
 *
 * @param  {String} uri from req.url
 * @return {String}     RFC 3986 path
 */
function getUrlPathName(uri) {
    return uri && url.parse(uri).pathname;
}
function isStringPath(context) {
    return typeof context === 'string' && !isGlob(context);
}
function isGlobPath(context) {
    return isGlob(context);
}


code:
export declare enum ERRORS {
    ERR_CONFIG_FACTORY_TARGET_MISSING = "[HPM] Missing \"target\" option. Example: {target: \"http://www.example.org\"}",
    ERR_CONTEXT_MATCHER_GENERIC = "[HPM] Invalid context. Expecting something like: \"/api\" or [\"/api\", \"/ajax\"]",
    ERR_CONTEXT_MATCHER_INVALID_ARRAY = "[HPM] Invalid context. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]",
    ERR_PATH_REWRITER_CONFIG = "[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function"
}


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ERRORS = void 0;
var ERRORS;
(function (ERRORS) {
    ERRORS["ERR_CONFIG_FACTORY_TARGET_MISSING"] = "[HPM] Missing \"target\" option. Example: {target: \"http://www.example.org\"}";
    ERRORS["ERR_CONTEXT_MATCHER_GENERIC"] = "[HPM] Invalid context. Expecting something like: \"/api\" or [\"/api\", \"/ajax\"]";
    ERRORS["ERR_CONTEXT_MATCHER_INVALID_ARRAY"] = "[HPM] Invalid context. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]";
    ERRORS["ERR_PATH_REWRITER_CONFIG"] = "[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function";
})(ERRORS = exports.ERRORS || (exports.ERRORS = {}));


code:
/// <reference types="node" />
import type * as http from 'http';
/**
 * Fix proxied body if bodyParser is involved.
 */
export declare function fixRequestBody(proxyReq: http.ClientRequest, req: http.IncomingMessage): void;


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fixRequestBody = void 0;
const querystring = require("querystring");
/**
 * Fix proxied body if bodyParser is involved.
 */
function fixRequestBody(proxyReq, req) {
    const requestBody = req.body;
    if (!requestBody) {
        return;
    }
    const contentType = proxyReq.getHeader('Content-Type');
    const writeBody = (bodyData) => {
        // deepcode ignore ContentLengthInCode: bodyParser fix
        proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
        proxyReq.write(bodyData);
    };
    if (contentType && contentType.includes('application/json')) {
        writeBody(JSON.stringify(requestBody));
    }
    if (contentType && contentType.includes('application/x-www-form-urlencoded')) {
        writeBody(querystring.stringify(requestBody));
    }
}
exports.fixRequestBody = fixRequestBody;


code:
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./public"), exports);


code:
export { responseInterceptor } from './response-interceptor';
export { fixRequestBody } from './fix-request-body';


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fixRequestBody = exports.responseInterceptor = void 0;
var response_interceptor_1 = require("./response-interceptor");
Object.defineProperty(exports, "responseInterceptor", { enumerable: true, get: function () { return response_interceptor_1.responseInterceptor; } });
var fix_request_body_1 = require("./fix-request-body");
Object.defineProperty(exports, "fixRequestBody", { enumerable: true, get: function () { return fix_request_body_1.fixRequestBody; } });


code:
/// <reference types="node" />
import type * as http from 'http';
declare type Interceptor = (buffer: Buffer, proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<Buffer | string>;
/**
 * Intercept responses from upstream.
 * Automatically decompress (deflate, gzip, brotli).
 * Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
 *
 * NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
 */
export declare function responseInterceptor(interceptor: Interceptor): (proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
export {};


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.responseInterceptor = void 0;
const zlib = require("zlib");
/**
 * Intercept responses from upstream.
 * Automatically decompress (deflate, gzip, brotli).
 * Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
 *
 * NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
 */
function responseInterceptor(interceptor) {
    return async function proxyRes(proxyRes, req, res) {
        const originalProxyRes = proxyRes;
        let buffer = Buffer.from('', 'utf8');
        // decompress proxy response
        const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding']);
        // concat data stream
        _proxyRes.on('data', (chunk) => (buffer = Buffer.concat([buffer, chunk])));
        _proxyRes.on('end', async () => {
            // copy original headers
            copyHeaders(proxyRes, res);
            // call interceptor with intercepted response (buffer)
            const interceptedBuffer = Buffer.from(await interceptor(buffer, originalProxyRes, req, res));
            // set correct content-length (with double byte character support)
            res.setHeader('content-length', Buffer.byteLength(interceptedBuffer, 'utf8'));
            res.write(interceptedBuffer);
            res.end();
        });
        _proxyRes.on('error', (error) => {
            res.end(`Error fetching proxied request: ${error.message}`);
        });
    };
}
exports.responseInterceptor = responseInterceptor;
/**
 * Streaming decompression of proxy response
 * source: https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L116
 */
function decompress(proxyRes, contentEncoding) {
    let _proxyRes = proxyRes;
    let decompress;
    switch (contentEncoding) {
        case 'gzip':
            decompress = zlib.createGunzip();
            break;
        case 'br':
            decompress = zlib.createBrotliDecompress();
            break;
        case 'deflate':
            decompress = zlib.createInflate();
            break;
        default:
            break;
    }
    if (decompress) {
        _proxyRes.pipe(decompress);
        _proxyRes = decompress;
    }
    return _proxyRes;
}
/**
 * Copy original headers
 * https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L78
 */
function copyHeaders(originalResponse, response) {
    response.statusCode = originalResponse.statusCode;
    response.statusMessage = originalResponse.statusMessage;
    if (response.setHeader) {
        let keys = Object.keys(originalResponse.headers);
        // ignore chunked, brotli, gzip, deflate headers
        keys = keys.filter((key) => !['content-encoding', 'transfer-encoding'].includes(key));
        keys.forEach((key) => {
            let value = originalResponse.headers[key];
            if (key === 'set-cookie') {
                // remove cookie domain
                value = Array.isArray(value) ? value : [value];
                value = value.map((x) => x.replace(/Domain=[^;]+?/i, ''));
            }
            response.setHeader(key, value);
        });
    }
    else {
        response.headers = originalResponse.headers;
    }
}


code:
import type { Filter, RequestHandler, Options } from './types';
export declare class HttpProxyMiddleware {
    private logger;
    private config;
    private wsInternalSubscribed;
    private serverOnCloseSubscribed;
    private proxyOptions;
    private proxy;
    private pathRewriter;
    constructor(context: Filter | Options, opts?: Options);
    middleware: RequestHandler;
    private catchUpgradeRequest;
    private handleUpgrade;
    /**
     * Determine whether request should be proxied.
     *
     * @private
     * @param  {String} context [description]
     * @param  {Object} req     [description]
     * @return {Boolean}
     */
    private shouldProxy;
    /**
     * Apply option.router and option.pathRewrite
     * Order matters:
     *    Router uses original path for routing;
     *    NOT the modified path, after it has been rewritten by pathRewrite
     * @param {Object} req
     * @return {Object} proxy options
     */
    private prepareProxyRequest;
    private applyRouter;
    private applyPathRewrite;
    private logError;
}


code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpProxyMiddleware = void 0;
const httpProxy = require("http-proxy");
const config_factory_1 = require("./config-factory");
const contextMatcher = require("./context-matcher");
const handlers = require("./_handlers");
const logger_1 = require("./logger");
const PathRewriter = require("./path-rewriter");
const Router = require("./router");
class HttpProxyMiddleware {
    constructor(context, opts) {
        this.logger = (0, logger_1.getInstance)();
        this.wsInternalSubscribed = false;
        this.serverOnCloseSubscribed = false;
        // https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
        this.middleware = async (req, res, next) => {
            var _a, _b;
            if (this.shouldProxy(this.config.context, req)) {
                try {
                    const activeProxyOptions = await this.prepareProxyRequest(req);
                    this.proxy.web(req, res, activeProxyOptions);
                }
                catch (err) {
                    next(err);
                }
            }
            else {
                next();
            }
            /**
             * Get the server object to subscribe to server events;
             * 'upgrade' for websocket and 'close' for graceful shutdown
             *
             * NOTE:
             * req.socket: node >= 13
             * req.connection: node < 13 (Remove this when node 12/13 support is dropped)
             */
            const server = (_b = ((_a = req.socket) !== null && _a !== void 0 ? _a : req.connection)) === null || _b === void 0 ? void 0 : _b.server;
            if (server && !this.serverOnCloseSubscribed) {
                server.on('close', () => {
                    this.logger.info('[HPM] server close signal received: closing proxy server');
                    this.proxy.close();
                });
                this.serverOnCloseSubscribed = true;
            }
            if (this.proxyOptions.ws === true) {
                // use initial request to access the server object to subscribe to http upgrade event
                this.catchUpgradeRequest(server);
            }
        };
        this.catchUpgradeRequest = (server) => {
            if (!this.wsInternalSubscribed) {
                server.on('upgrade', this.handleUpgrade);
                // prevent duplicate upgrade handling;
                // in case external upgrade is also configured
                this.wsInternalSubscribed = true;
            }
        };
        this.handleUpgrade = async (req, socket, head) => {
            if (this.shouldProxy(this.config.context, req)) {
                const activeProxyOptions = await this.prepareProxyRequest(req);
                this.proxy.ws(req, socket, head, activeProxyOptions);
                this.logger.info('[HPM] Upgrading to WebSocket');
            }
        };
        /**
         * Determine whether request should be proxied.
         *
         * @private
         * @param  {String} context [description]
         * @param  {Object} req     [description]
         * @return {Boolean}
         */
        this.shouldProxy = (context, req) => {
            const path = req.originalUrl || req.url;
            return contextMatcher.match(context, path, req);
        };
        /**
         * Apply option.router and option.pathRewrite
         * Order matters:
         *    Router uses original path for routing;
         *    NOT the modified path, after it has been rewritten by pathRewrite
         * @param {Object} req
         * @return {Object} proxy options
         */
        this.prepareProxyRequest = async (req) => {
            // https://github.com/chimurai/http-proxy-middleware/issues/17
            // https://github.com/chimurai/http-proxy-middleware/issues/94
            req.url = req.originalUrl || req.url;
            // store uri before it gets rewritten for logging
            const originalPath = req.url;
            const newProxyOptions = Object.assign({}, this.proxyOptions);
            // Apply in order:
            // 1. option.router
            // 2. option.pathRewrite
            await this.applyRouter(req, newProxyOptions);
            await this.applyPathRewrite(req, this.pathRewriter);
            // debug logging for both http(s) and websockets
            if (this.proxyOptions.logLevel === 'debug') {
                const arrow = (0, logger_1.getArrow)(originalPath, req.url, this.proxyOptions.target, newProxyOptions.target);
                this.logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target);
            }
            return newProxyOptions;
        };
        // Modify option.target when router present.
        this.applyRouter = async (req, options) => {
            let newTarget;
            if (options.router) {
                newTarget = await Router.getTarget(req, options);
                if (newTarget) {
                    this.logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget);
                    options.target = newTarget;
                }
            }
        };
        // rewrite path
        this.applyPathRewrite = async (req, pathRewriter) => {
            if (pathRewriter) {
                const path = await pathRewriter(req.url, req);
                if (typeof path === 'string') {
                    req.url = path;
                }
                else {
                    this.logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url);
                }
            }
        };
        this.logError = (err, req, res, target) => {
            var _a;
            const hostname = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
            const requestHref = `${hostname}${req.url}`;
            const targetHref = `${target === null || target === void 0 ? void 0 : target.href}`; // target is undefined when websocket errors
            const errorMessage = '[HPM] Error occurred while proxying request %s to %s [%s] (%s)';
            const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
            this.logger.error(errorMessage, requestHref, targetHref, err.code || err, errReference);
        };
        this.config = (0, config_factory_1.createConfig)(context, opts);
        this.proxyOptions = this.config.options;
        // create proxy
        this.proxy = httpProxy.createProxyServer({});
        this.logger.info(`[HPM] Proxy created: ${this.config.context}  -> ${this.proxyOptions.target}`);
        this.pathRewriter = PathRewriter.createPathRewriter(this.proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided
        // attach handler to http-proxy events
        handlers.init(this.proxy, this.proxyOptions);
        // log errors for debug purpose
        this.proxy.on('error', this.logError);
        // https://github.com/chimurai/http-proxy-middleware/issues/19
        // expose function to upgrade externally
        this.middleware.upgrade = (req, socket, head) => {
            if (!this.wsInternalSubscribed) {
                this.handleUpgrade(req, socket, head);
            }
        };
    }
}
exports.HttpProxyMiddleware = HttpProxyMiddleware;


code:
import { Filter, Options } from './types';
export declare function createProxyMiddleware(context: Filter | Options, options?: Options): import("./types").RequestHandler;
export * from './handlers';
export { Filter, Options, RequestHandler } from './types';


code:
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createProxyMiddleware = void 0;
const http_proxy_middleware_1 = require("./http-proxy-middleware");
function createProxyMiddleware(context, options) {
    const { middleware } = new http_proxy_middleware_1.HttpProxyMiddleware(context, options);
    return middleware;
}
exports.createProxyMiddleware = createProxyMiddleware;
__exportStar(require("./handlers"), exports);


code:
export declare function getInstance(): any;
/**
 * -> normal proxy
 * => router
 * ~> pathRewrite
 * ≈> router + pathRewrite
 *
 * @param  {String} originalPath
 * @param  {String} newPath
 * @param  {String} originalTarget
 * @param  {String} newTarget
 * @return {String}
 */
export declare function getArrow(originalPath: any, newPath: any, originalTarget: any, newTarget: any): string;