[
  {
    "path": ".dir-locals.el",
    "content": "((nil . ((indent-tabs-mode . nil)\n         (tab-width . 4)\n         (fill-column . 80)))\n (js-mode . ((js-indent-level . 4)\n             (indent-tabs-mode . nil)\n             )))\n"
  },
  {
    "path": ".eslintignore",
    "content": "# node_modules ignored by default\nnode_modules/\n\n# other ignored directories\nbin/\ndeps/\ndocs/\nexamples/\ncover_html/\n\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "'use strict';\n\nvar OFF = 0;\nvar ERROR = 2;\n\nvar config = {\n    extends: [],\n    plugins: ['jsdoc'],\n    env: {\n        browser: false,\n        node: true,\n        es6: true\n    },\n    parserOptions: {\n        ecmaVersion: 2018\n    },\n    rules: {}\n};\n\nif (!process.env.NO_LINT) {\n    // possible errors\n    config.rules['no-cond-assign'] = ERROR;\n    config.rules['no-console'] = OFF;\n    config.rules['no-constant-condition'] = ERROR;\n    config.rules['no-control-regex'] = ERROR;\n    config.rules['no-debugger'] = ERROR;\n    config.rules['no-dupe-args'] = ERROR;\n    config.rules['no-dupe-keys'] = ERROR;\n    config.rules['no-duplicate-case'] = ERROR;\n    config.rules['no-empty'] = ERROR;\n    config.rules['no-empty-character-class'] = ERROR;\n    config.rules['no-ex-assign'] = ERROR;\n    config.rules['no-extra-boolean-cast'] = ERROR;\n    config.rules['no-extra-semi'] = ERROR;\n    config.rules['no-func-assign'] = ERROR;\n    // config.rules['one-var'] = [ERROR, 'always']; // TODO: var overlapping\n    // this is for variable hoisting, not necessary if we use block scoped declarations\n    // config.rules['no-inner-declarations'] = [ ERROR, 'both' ];\n    config.rules['no-invalid-regexp'] = ERROR;\n    config.rules['no-irregular-whitespace'] = ERROR;\n    config.rules['no-negated-in-lhs'] = ERROR;\n    config.rules['no-reserved-keys'] = OFF;\n    config.rules['no-regex-spaces'] = ERROR;\n    config.rules['no-sparse-arrays'] = ERROR;\n    config.rules['no-unreachable'] = ERROR;\n    config.rules['use-isnan'] = ERROR;\n    config.rules['valid-jsdoc'] = [\n        ERROR,\n        {\n            requireReturnDescription: false,\n            prefer: {\n                return: 'returns'\n            }\n        }\n    ];\n    config.rules['valid-typeof'] = ERROR;\n\n    // best practices\n    config.rules['func-names'] = ERROR;\n    config.rules['block-scoped-var'] = ERROR;\n    config.rules['consistent-return'] = ERROR;\n    config.rules['curly'] = OFF;\n    config.rules['default-case'] = ERROR;\n    config.rules['dot-notation'] = [ERROR, { allowKeywords: true }];\n    config.rules['eqeqeq'] = ERROR;\n    config.rules['guard-for-in'] = ERROR;\n    config.rules['no-alert'] = ERROR;\n    config.rules['no-caller'] = ERROR;\n    config.rules['no-div-regex'] = ERROR;\n    config.rules['no-eq-null'] = ERROR;\n    config.rules['no-eval'] = ERROR;\n    config.rules['no-extend-native'] = ERROR;\n    config.rules['no-extra-bind'] = ERROR;\n    config.rules['no-fallthrough'] = ERROR;\n    config.rules['no-floating-decimal'] = ERROR;\n    config.rules['no-implied-eval'] = ERROR;\n    config.rules['no-iterator'] = ERROR;\n    config.rules['no-labels'] = ERROR;\n    config.rules['no-lone-blocks'] = ERROR;\n    config.rules['no-loop-func'] = OFF;\n    config.rules['no-multi-spaces'] = OFF;\n    config.rules['no-multi-str'] = OFF;\n    config.rules['no-native-reassign'] = ERROR;\n    config.rules['no-new'] = OFF;\n    config.rules['no-new-func'] = ERROR;\n    config.rules['no-new-wrappers'] = ERROR;\n    config.rules['no-octal'] = ERROR;\n    config.rules['no-octal-escape'] = ERROR;\n    config.rules['no-param-reassign'] = OFF;\n    config.rules['no-proto'] = ERROR;\n    config.rules['no-process-env'] = OFF;\n    config.rules['no-redeclare'] = ERROR;\n    config.rules['no-return-assign'] = ERROR;\n    config.rules['no-script-url'] = ERROR;\n    config.rules['no-self-compare'] = ERROR;\n    config.rules['no-sequences'] = ERROR;\n    config.rules['no-throw-literal'] = ERROR;\n    config.rules['no-unused-expressions'] = ERROR;\n\n    config.rules['no-warning-comments'] = [1];\n    config.rules['no-with'] = ERROR;\n    config.rules['radix'] = ERROR;\n    config.rules['wrap-iife'] = ERROR;\n\n    // strict mode\n    config.rules['strict'] = [ERROR, 'global'];\n\n    // variables\n    config.rules['no-catch-shadow'] = ERROR;\n    config.rules['no-delete-var'] = ERROR;\n    config.rules['no-shadow'] = ERROR;\n    config.rules['no-shadow-restricted-names'] = ERROR;\n    config.rules['no-undef'] = ERROR;\n    config.rules['no-undef-init'] = ERROR;\n    config.rules['no-undefined'] = OFF;\n    config.rules['no-unused-vars'] = [ERROR, { vars: 'all', args: 'none' }];\n    config.rules['no-use-before-define'] = [ERROR, 'nofunc'];\n\n    // node.js\n    config.rules['handle-callback-err'] = [ERROR, '^.*(e|E)rr'];\n    config.rules['no-mixed-requires'] = ERROR;\n    config.rules['no-new-require'] = ERROR;\n    config.rules['no-path-concat'] = OFF;\n    config.rules['no-process-exit'] = OFF;\n}\n\n// stylistic.\nif (!process.env.NO_STYLE) {\n    // Global\n    config.rules['max-len'] = [ERROR, { code: 80, ignoreComments: true }];\n\n    // Prettier\n    config.extends.push('prettier');\n    config.plugins.push('prettier');\n    config.rules['prettier/prettier'] = ERROR;\n\n    // JSDoc\n    config.rules['jsdoc/check-param-names'] = ERROR;\n    config.rules['jsdoc/check-tag-names'] = ERROR;\n    config.rules['jsdoc/newline-after-description'] = ERROR;\n    config.rules['jsdoc/require-hyphen-before-param-description'] = ERROR;\n    config.rules['jsdoc/require-param'] = ERROR;\n    config.rules['jsdoc/require-param-description'] = ERROR;\n    config.rules['jsdoc/require-param-type'] = ERROR;\n    config.rules['jsdoc/require-returns-description'] = ERROR;\n    config.rules['jsdoc/require-returns-type'] = ERROR;\n}\n\nmodule.exports = config;\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1-bug-report.md",
    "content": "---\nname: \"\\U0001F41B Bug report\"\nabout: Create a report to help us improve\n\n---\n\n<!-- Thank you for taking the time to open an issue for restify! If this is\nyour first time here, welcome to our community! We are a group of developers\nwho work on restify in our free-time. Some of us do it as a hobby, others are\nusing restify at work. When asking for help here, keep in mind most of us are\nvolunteers contributing our daily work back to the community at no cost (and\noften for no reward). Please be respectful!\n\nBelow you will find two templates, one for filing a bug report, and the other\nfor requesting a feature. Remove the comments from around the template that is\napplicable to your case and fill it out accordingly. This standardization helps\nthe maintainers gather the information they need up front to verify and respond\nto problems accordingly, ensuring you get the fastest response possible! -->\n\n<!-- REQUIRED: Pre-Submission Checklist -->\n\n- [ ] Used appropriate template for the issue type\n- [ ] Searched both open and closed issues for duplicates of this issue\n- [ ] Title adequately and _concisely_ reflects the feature or the bug\n\n**Restify Version**: \n**Node.js Version**: \n\n## Expected behaviour\n<!-- This section details what you expected restify to do based on the code\nthat you wrote -->\n\n## Actual behaviour\n<!-- This section details what restify actually did when you ran your code -->\n\n## Repro case\n<!-- Please include a simple and concise example reproducing this bug. Please\n_do not_ just dump your application here. By either not providing a repro case\nor by providing an overly complicated repro case, you are offloading the work\nof isolating your bug to other developers, many of which are here voluntarily.\nGood repro cases are single file Node.js applications, where the only logic\npresent is logic necessary to expose the undesired behaviour. You will often\nfind that when creating your repro case, you will solve the problem yourself!\n-->\n\n## Cause\n<!--\nIf you have been able to trace the bug back to it source(s) in the code base,\nplease link to them here. -->\n\n## Are you willing and able to fix this?\n<!-- \"Yes\" or, if \"no\", what can current contributors do to help you create a\nPR?  If this issue is unique, as the checklist you completed above suggests,\nthen you are one of the few people who have encountered this bug in the wild.\nWhile contributors will often help work on issues out of the kindness of their\nhearts, its important to remember that you are the largest stakeholder in\nseeing this bug resolved as you are the one experiencing it. Kindness and\ncontributions are what make Free Software go round, help pay it forward! -->\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2-feature-request.md",
    "content": "---\nname: \"\\U0001F680 Feature request\"\nabout: Suggest an idea for this project\n\n---\n\n<!-- Thank you for taking the time to open an issue for restify! If this is\nyour first time here, welcome to our community! We are a group of developers\nwho work on restify in our free-time. Some of us do it as a hobby, others are\nusing restify at work. When asking for help here, keep in mind most of us are\nvolunteers contributing our daily work back to the community at no cost (and\noften for no reward). Please be respectful!\n\nBelow you will find two templates, one for filing a bug report, and the other\nfor requesting a feature. Remove the comments from around the template that is\napplicable to your case and fill it out accordingly. This standardization helps\nthe maintainers gather the information they need up front to verify and respond\nto problems accordingly, ensuring you get the fastest response possible! -->\n\n<!-- REQUIRED: Pre-Submission Checklist -->\n\n- [ ] Used appropriate template for the issue type\n- [ ] Searched both open and closed issues for duplicates of this issue\n- [ ] Title adequately and _concisely_ reflects the feature or the bug\n\n# Feature Request\n\n## Use Case\n<!-- Why do you want this? -->\n\n## Example API\n<!-- This should include code snippets and documentation for the proposed\nfeature -->\n\n## Are you willing and able to implement this?\n<!-- \"Yes\" or, if \"no\", what can current contributors do to help you create a\nPR? -->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nThank you for taking the time to open an PR for restify! If this is your first\ntime here, welcome to our community! We are a group of developers who work on\nrestify in our free-time. Some of us do it as a hobby, others are using restify\nat work. When asking for help here, keep in mind most of us are volunteers\ncontributing our daily work back to the community at no cost (and often for no\nreward). Please be respectful!\n\nBelow you will find a checklist to help you create the best PR possible. While\nthe checklist items aren't all _strictly_ required, they dramatically increase\nthe probability of your PR getting a response and getting merged. Often times,\nthe least time consuming part of maintaining and open source project is writing\ncode, its the process and discussions that happen around the code base that\nconsume a majority of the maintainers' time. By spending a few moments to\nadhere to this template, you are not only improve the quality of your PR, you\nare also helping save the maintainers a considerable amount of time when trying\nto understand and review your changes.\n\nAnd remember, positive vibes are met with positive vibes. Kindness helps Free\nSoftware go round, pay it forward!\n-->\n\n## Pre-Submission Checklist\n\n- [ ] Opened an issue discussing these changes before opening the PR\n- [ ] Ran the linter and tests via `make prepush`\n- [ ] Included comprehensive and convincing tests for changes\n\n## Issues\n\nCloses:\n\n* Issue #\n* Issue #\n* Issue #\n\n> Summarize the issues that discussed these changes\n\n# Changes\n\n> What does this PR do?\n"
  },
  {
    "path": ".github/stale.yml",
    "content": "daysUntilStale: 60\ndaysUntilClose: 14\nexemptLabels:\n  - Critical\n  - Serve\nstaleLabel: Stale\nmarkComment: >\n  This issue has been automatically marked as stale because it has not had\n  recent activity. It will be closed if no further activity occurs. Thank you\n  for your contributions.\ncloseComment: >\n  This issue has been automatically closed as stale because it has not had\n  recent activity.\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "on:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\nname: ci\njobs:\n  lint:\n    name: lint\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: install node v16\n        uses: actions/setup-node@v1\n        with:\n          node-version: v16.x\n      - name: install dependencies\n        run: npm install\n      - name: check lint\n        run: make check-lint\n  test:\n    name: test node ${{ matrix.node-version }} on ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os:\n          - ubuntu-latest\n        node-version:\n          - 14.x\n          - 16.x\n          - 18.x\n          - 20.x\n    runs-on: ${{matrix.os}}\n    steps:\n      - uses: actions/checkout@v2\n      - name: use node ${{ matrix.node-version }}\n        uses: actions/setup-node@v1\n        with:\n          node-version: ${{ matrix.node-version }}\n      - name: install dependencies\n        run: npm install\n      - name: test\n        run: make test\n        env:\n          TEST_SKIP_IP_V6: true\n"
  },
  {
    "path": ".github/workflows/release-please.yml",
    "content": "on:\n  push:\n    branches:\n      - master\n      - 9.x\nname: release-please\njobs:\n  release-please:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: GoogleCloudPlatform/release-please-action@v3.6.1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          release-type: node\n          package-name: restify\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ndocs/*.html\ndocs/pkg\nexamples/todoapp/node_modules\n*.log\n*.tar.gz\n*.tgz\nbuild\ndocs/*.json\nnbproject\ndeps/javascriptlint\ndeps/jsstyle\npackage-lock.json\nbenchmark/results\n.nyc_output/\ncoverage/\ncover_html/\n"
  },
  {
    "path": ".npmignore",
    "content": ".coverage_data\n.dir-locals.el\n.gitmodules\n.github\n.travis.yml\nMakefile\ncover_html\ndeps\ndocs\nexamples\ntest\ntools\n.vscode\n.idea\nbenchmark\n.dir-locals.el\n.eslintignore\n.eslintrc.js\n.gitignore\n.prettierignore\n.prettierrc\n.tern-project\n.travis.yml\nCONTRIBUTING.md\nFEATURE_REQUESTS.md\n*.log\n*.tar.gz\n*.tgz\nnode_modules\n"
  },
  {
    "path": ".prettierignore",
    "content": "cover_html\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"tabWidth\": 4,\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": ".tern-project",
    "content": "{\n  \"libs\": [\n     \"ecma5\",\n     \"chai\"\n  ],\n  \"plugins\": {\n    \"node\": {},\n    \"complete_strings\": {},\n    \"doc_comment\": {},\n    \"node_resolve\": {}\n  }\n}\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## [9.0.0](https://www.github.com/restify/node-restify/compare/v8.6.1...v9.0.0) (2022-11-15)\n\n\n### ⚠ BREAKING CHANGES\n\n* remove deprecated usage of pino.child (#1902)\n* deprecates and removes re-routing when passing a string parameter to `next()`\n* removes `RequestCaptureStream` and replaces `Bunyan` with `Pino`\n* adds async/await support to pre, use and handler chains\n* drops suppoprt to node 8 and updates linting rules\n* **server:** - Server returns `RequestCloseError` instead of `RequestAbortedError`\n* **travisci:** dropping support below Node.js 4\n\n### Features\n\n* async/await support ([12be9e2](https://www.github.com/restify/node-restify/commit/12be9e243a407eaf7a30cbb16e399ee2a46dec93))\n* deprecate req.closed ([d052b7c](https://www.github.com/restify/node-restify/commit/d052b7cec561133c002211a20dccf7cc2a8a0897))\n* provide callback to uncaughtException handler ([#1766](https://www.github.com/restify/node-restify/issues/1766)) ([5e8b5e2](https://www.github.com/restify/node-restify/commit/5e8b5e2b28e32c79c413d9dec2466fe8f1135332))\n* remove re-routing from handler ([#1847](https://www.github.com/restify/node-restify/issues/1847)) ([9153587](https://www.github.com/restify/node-restify/commit/9153587c023a876237c1d8bc7491fee4984d9074))\n* send 500s for unhandled requests ([#1777](https://www.github.com/restify/node-restify/issues/1777)) ([885cecd](https://www.github.com/restify/node-restify/commit/885cecd7f9753b62faaa930f3cd39329057587f3))\n* **audit:** Add the ability to specify a custom audit log serializer (for err, req and res) ([#1746](https://www.github.com/restify/node-restify/issues/1746)) ([6231acd](https://www.github.com/restify/node-restify/commit/6231acda7e16ce64253b08039bd0ad341126c11a))\n* **chain:** schedule handlers to the next tick ([#1798](https://www.github.com/restify/node-restify/issues/1798)) ([806ed71](https://www.github.com/restify/node-restify/commit/806ed7119db9ed4cce77aef3d898aae561224dd8))\n* **chain:** use nextTick instead of setImmediate ([#1808](https://www.github.com/restify/node-restify/issues/1808)) ([703470a](https://www.github.com/restify/node-restify/commit/703470ad82fd01e7f3b2197ebb7eb1b5b37975f8))\n* **deps:** replace cover/istanbul with nyc ([#1823](https://www.github.com/restify/node-restify/issues/1823)) ([361f83e](https://www.github.com/restify/node-restify/commit/361f83e5acd814881c82add3e1bd06ce9ded777c))\n* **first:** Handlers that execute ASAP in the req/res lifecycle ([#1756](https://www.github.com/restify/node-restify/issues/1756)) ([8178098](https://www.github.com/restify/node-restify/commit/8178098d3e85ad9bd13c536b504adf940ef08563))\n* **http2:** add native HTTP/2 support ([#1489](https://www.github.com/restify/node-restify/issues/1489)) ([6b20285](https://www.github.com/restify/node-restify/commit/6b202853d62394f0448486c9b5bbc18589fd44e2))\n* **plugin:** plugin to serve static files ([#1753](https://www.github.com/restify/node-restify/issues/1753)) ([a67b25f](https://www.github.com/restify/node-restify/commit/a67b25f472c7ec99e63f358b3c1e8801d6261148))\n* Ability to find a route by a path ([711a489](https://www.github.com/restify/node-restify/commit/711a4897800e2ef8bc4a1a9c6cc833af71cd925d))\n* add router.render() back to support hypermedia usecase ([#1752](https://www.github.com/restify/node-restify/issues/1752)) ([0700cfd](https://www.github.com/restify/node-restify/commit/0700cfd445e45401c36c4229e37e12b8220339d9)), closes [#1684](https://www.github.com/restify/node-restify/issues/1684)\n* **helpers:** add compose feature ([#1660](https://www.github.com/restify/node-restify/issues/1660)) ([eb60ef4](https://www.github.com/restify/node-restify/commit/eb60ef403ad77b1dd187e199d72e7c80caca248c))\n* **plugins:** context, req.get() returns the whole context ([#1739](https://www.github.com/restify/node-restify/issues/1739)) ([6e35e01](https://www.github.com/restify/node-restify/commit/6e35e01eb6d64f80c0e3db2daf4dbf3f66c35e86))\n* **plugins:** do not include user-input in UnsupportedMediaTypeError message (VError fails), move it to info ([#1733](https://www.github.com/restify/node-restify/issues/1733)) ([06c220d](https://www.github.com/restify/node-restify/commit/06c220d2d9629e3510aed493a8877629bbc0c4ae))\n* **req:** add restifyDone event ([#1740](https://www.github.com/restify/node-restify/issues/1740)) ([4900d6b](https://www.github.com/restify/node-restify/commit/4900d6bdd51fa4e1769678562de69929c38a0c4b))\n* add support for non-strict formatters ([#1721](https://www.github.com/restify/node-restify/issues/1721)) ([de1833a](https://www.github.com/restify/node-restify/commit/de1833a44084e5f231de289421518ec646b86f60))\n* jsonBodyParser handles extended content types *+json ([#1663](https://www.github.com/restify/node-restify/issues/1663)) ([4537514](https://www.github.com/restify/node-restify/commit/45375144feb6a215ebfdb967ff0944e3aa21f48d))\n* **router:** add ignoreTrailingSlash router option ([#1632](https://www.github.com/restify/node-restify/issues/1632)) ([92ffbf5](https://www.github.com/restify/node-restify/commit/92ffbf5cbe49df09d9c59a6081285c12fe5943b4))\n* **server:** new router and middleware system ([#1561](https://www.github.com/restify/node-restify/issues/1561)) ([8283277](https://www.github.com/restify/node-restify/commit/82832771826321480e5e524db258668f62b689c2))\n* cpuUsageThrottle ([#1460](https://www.github.com/restify/node-restify/issues/1460)) ([84be679](https://www.github.com/restify/node-restify/commit/84be6799c4a80ae67f3aa03165c8031a55bddc97))\n* **throttle plugin:** expose rate limit metrics as headers ([#1453](https://www.github.com/restify/node-restify/issues/1453)) ([1627a55](https://www.github.com/restify/node-restify/commit/1627a557bd4ed94ba1c6adbe916c51f83bc46059))\n* create inflightRequestThrottle plugin ([#1431](https://www.github.com/restify/node-restify/issues/1431)) ([285faf4](https://www.github.com/restify/node-restify/commit/285faf4b6a2e56f0e4d9fc6dfaa3dd5e311530c1))\n* revert async formatters ([#1377](https://www.github.com/restify/node-restify/issues/1377)) ([a2e300f](https://www.github.com/restify/node-restify/commit/a2e300f785edb087da9a52f562bd1f900e9ab47a))\n\n\n### Bug Fixes\n\n* add support for secureOptions in createServer ([#1575](https://www.github.com/restify/node-restify/issues/1575)) ([656e60e](https://www.github.com/restify/node-restify/commit/656e60e03d5fe2b011f8b2198178bc22d749b21f))\n* Allow multiple unmerged set-cookie headers. ([#1570](https://www.github.com/restify/node-restify/issues/1570)) ([df04015](https://www.github.com/restify/node-restify/commit/df04015439becae8e8c48a02cb6e1992d6040037))\n* Correct typo in assertion message ([#1904](https://www.github.com/restify/node-restify/issues/1904)) ([195cf13](https://www.github.com/restify/node-restify/commit/195cf136e3a7de2b2720261dfd459c051b5be037))\n* documentation typo fix ([#1688](https://www.github.com/restify/node-restify/issues/1688)) ([0fa7132](https://www.github.com/restify/node-restify/commit/0fa71328b8f01f301b0e729f5ef0f00d1b203231))\n* don't create empty clientError listener for http.Server ([#1895](https://www.github.com/restify/node-restify/issues/1895)) ([ddc1042](https://www.github.com/restify/node-restify/commit/ddc1042af427fe6383ebea37201c06b7b424e72f))\n* emit after event with proper error param for node versions >= 11.4.0 ([#1732](https://www.github.com/restify/node-restify/issues/1732)) ([7a1378b](https://www.github.com/restify/node-restify/commit/7a1378b0353e9b3f1b630e4cab489c8c578000f5))\n* examples/todoapp/package.json to reduce vulnerabilities ([#1832](https://www.github.com/restify/node-restify/issues/1832)) ([d9b27c6](https://www.github.com/restify/node-restify/commit/d9b27c602e260fc6c4f0e18e8b6835e89fa2adca))\n* format falsy constants properly in json formatter ([#1792](https://www.github.com/restify/node-restify/issues/1792)) ([3002182](https://www.github.com/restify/node-restify/commit/3002182cacc7a9334237a9284a339ba93d3f213c))\n* make arity error message actionable ([#1901](https://www.github.com/restify/node-restify/issues/1901)) ([97b6f93](https://www.github.com/restify/node-restify/commit/97b6f936e43860873f847bdd752b8090b3119da0))\n* more flaky metrics.test.js fixes ([#1730](https://www.github.com/restify/node-restify/issues/1730)) ([71aac42](https://www.github.com/restify/node-restify/commit/71aac4283a1ae4ebd3c290afb83487b67010666f))\n* properly handle non-errors thrown in domains ([#1757](https://www.github.com/restify/node-restify/issues/1757)) ([cb2e717](https://www.github.com/restify/node-restify/commit/cb2e7177c8b735987aed1c0839747f9658c19bb0))\n* proxy events into instance var and add test script ([#1661](https://www.github.com/restify/node-restify/issues/1661)) ([de72f49](https://www.github.com/restify/node-restify/commit/de72f49eade48cc14dd916916ea86f88d46d3c8a))\n* Re-add support for clientError listeners ([#1897](https://www.github.com/restify/node-restify/issues/1897)) ([05f12a6](https://www.github.com/restify/node-restify/commit/05f12a6864f4fa9aea617a42ae2d5c890478d2df))\n* remove invalid triggering of uncaughtException handler ([#1710](https://www.github.com/restify/node-restify/issues/1710)) ([ee69806](https://www.github.com/restify/node-restify/commit/ee69806a338add1ebfef7eaad92a13273826c98e))\n* Return 444 status code for closed and aborted requests ([#1579](https://www.github.com/restify/node-restify/issues/1579)) ([644c198](https://www.github.com/restify/node-restify/commit/644c1980aa1a21b0c7fa9aa41e22df9af6eab31e))\n* send numbers or bools as payloads ([#1609](https://www.github.com/restify/node-restify/issues/1609)) ([0919f26](https://www.github.com/restify/node-restify/commit/0919f26db5d5614c0b2fa2567ac2ed43ee70b6d5))\n* server should fire not acceptable event ([#1627](https://www.github.com/restify/node-restify/issues/1627)) ([8b11b71](https://www.github.com/restify/node-restify/commit/8b11b71b487d0001c96312519298f7f85b196471))\n* use close event on response instead of socket ([#1892](https://www.github.com/restify/node-restify/issues/1892)) ([5c7eb95](https://www.github.com/restify/node-restify/commit/5c7eb95319aa54ef3b4b60d000d434824a666e18))\n* use more reliable close event ([36318ae](https://www.github.com/restify/node-restify/commit/36318ae4c1fee02d3bc3737e34e1ea33e604f674))\n* **benchmark:** force latest restify version ([#1810](https://www.github.com/restify/node-restify/issues/1810)) ([b8ec60e](https://www.github.com/restify/node-restify/commit/b8ec60e335b3ce95be4f2507623d357f4a600331))\n* **bodyReader:** Fix memory leak ([#1566](https://www.github.com/restify/node-restify/issues/1566)) ([756b3f0](https://www.github.com/restify/node-restify/commit/756b3f02ba1dec114cf76c4e723ed054170a081c))\n* **cpuUsageThrottle:** Always queue a new timeout ([#1484](https://www.github.com/restify/node-restify/issues/1484)) ([e4ffe43](https://www.github.com/restify/node-restify/commit/e4ffe430b47a2b51fe5fbef00dfa8bd3a1fb66c1))\n* **cpuUsageThrottle:** Correctly named handler for debugInfo ([#1499](https://www.github.com/restify/node-restify/issues/1499)) ([78b0900](https://www.github.com/restify/node-restify/commit/78b0900b0ffcefa86e541c850d27779c5f656f00))\n* **cpuUsageThrottle:** dont include interval in lag ([#1504](https://www.github.com/restify/node-restify/issues/1504)) ([eecb2d2](https://www.github.com/restify/node-restify/commit/eecb2d259deda34c2f297f2ef8b6d4fedc504e9e))\n* **cpuUsageThrottle:** support breaking change in pidusage module ([7460064](https://www.github.com/restify/node-restify/commit/7460064fc13e5b977a295a2c939e050129c47797))\n* **dev:** pin to exact versions of linting tools and fix lint errors ([3740a6b](https://www.github.com/restify/node-restify/commit/3740a6b7bf6e3bd589d9c1bc0c3d690978270564))\n* **dev:** remove nsp since the project merged with npm ([1dc34b4](https://www.github.com/restify/node-restify/commit/1dc34b48de361960d7fa37d8bbc82b9d4a612981))\n* **dev:** upgrading modules including restify-errors ([#1755](https://www.github.com/restify/node-restify/issues/1755)) ([3b71229](https://www.github.com/restify/node-restify/commit/3b712298c16577394d16b149be6c9a99044332b2))\n* **dtrace:** route probes ([#1659](https://www.github.com/restify/node-restify/issues/1659)) ([84bcded](https://www.github.com/restify/node-restify/commit/84bcded77e9a42d3762146802418a1ae1ece8c30))\n* **inflightRequestThrottle:** properly handle next ([#1471](https://www.github.com/restify/node-restify/issues/1471)) ([4db404f](https://www.github.com/restify/node-restify/commit/4db404f979d0da9651c00b076ceefb7b98a4e71f))\n* **jsonBodyParser:** fix percent sign causing server fail ([#1411](https://www.github.com/restify/node-restify/issues/1411)) ([bde8fda](https://www.github.com/restify/node-restify/commit/bde8fda646a6f69b57fd72af1f00d6153fe056ec))\n* **npm:** exclude extraneous files ([#1818](https://www.github.com/restify/node-restify/issues/1818)) ([e8516c3](https://www.github.com/restify/node-restify/commit/e8516c3735487ad5ebd332bc781404654c8c3cec))\n* **npm:** remove unleash dependency ([#1522](https://www.github.com/restify/node-restify/issues/1522)) ([a43aa60](https://www.github.com/restify/node-restify/commit/a43aa60f090d29b8e66a58a9656126cb37bf2ef9))\n* **package-lock.json:** remove artifacts.netflix.com repo ([#1526](https://www.github.com/restify/node-restify/issues/1526)) ([3d2f0f7](https://www.github.com/restify/node-restify/commit/3d2f0f7d0ddc14238691944cb9a1a60b02ae5947))\n* **plugins:** save req._matchedVersion ([#1642](https://www.github.com/restify/node-restify/issues/1642)) ([69f917a](https://www.github.com/restify/node-restify/commit/69f917a3db66fac58f01c9e16535c2e2fcf2172b))\n* **plugins:** use process.hrtime() for duration calculation ([#1507](https://www.github.com/restify/node-restify/issues/1507)) ([e8efd6c](https://www.github.com/restify/node-restify/commit/e8efd6cdcb73e674583e2a7081d2a9b923c72809))\n* **request:** date() and time() methods return value ([#1576](https://www.github.com/restify/node-restify/issues/1576)) ([4c2cb1a](https://www.github.com/restify/node-restify/commit/4c2cb1a7edfe6252e68e409d850aef73961338ca))\n* **server:** address domain performance regression with Node v12.x ([#1809](https://www.github.com/restify/node-restify/issues/1809)) ([e648d49](https://www.github.com/restify/node-restify/commit/e648d491151484f17263c6774678f1e7ac2fa188))\n* **server:** address req and res close event changes in Node v10.x ([#1672](https://www.github.com/restify/node-restify/issues/1672)) ([6be3fb7](https://www.github.com/restify/node-restify/commit/6be3fb7c07483ee1991eba9aaa9ad4897c5a4965))\n* **server:** avoid http2 experimental warning without http2 option ([#1555](https://www.github.com/restify/node-restify/issues/1555)) ([12da7fd](https://www.github.com/restify/node-restify/commit/12da7fdfc68dd9467da97ae0b2f45b89cb540b9b))\n* **server:** avoiding uncaughtException in _routeErrorResponse by only sending response when not sent ([#1568](https://www.github.com/restify/node-restify/issues/1568)) ([cf65c65](https://www.github.com/restify/node-restify/commit/cf65c65cabd06bd5d17d84cd28999248dada94f7))\n* **server:** fix uncaught exceptions triggering route lookups ([#1717](https://www.github.com/restify/node-restify/issues/1717)) ([e49cb3b](https://www.github.com/restify/node-restify/commit/e49cb3b24c3f4d77fa0b3204f3c1a618fb054789))\n* **test:** make upgrade test pass ([#1772](https://www.github.com/restify/node-restify/issues/1772)) ([d30b748](https://www.github.com/restify/node-restify/commit/d30b7483c4d035e9a3fa94114557ae9d5f058f79))\n* 652 - Incorrect error on route with no versions ([#1465](https://www.github.com/restify/node-restify/issues/1465)) ([ee15490](https://www.github.com/restify/node-restify/commit/ee154908d3ec4fd4a4108019140820c172df66b5))\n* Add migration guid to website ([#1402](https://www.github.com/restify/node-restify/issues/1402)) ([5f053c7](https://www.github.com/restify/node-restify/commit/5f053c7efebc414b5a26daac3cc5e89dc0054fe3))\n* add node 7-8 travis support ([#1405](https://www.github.com/restify/node-restify/issues/1405)) ([536a473](https://www.github.com/restify/node-restify/commit/536a4735266a7f56c205be4c6cafaa6adf81f480))\n* create unit tests for sanitizePath plugin ([#1352](https://www.github.com/restify/node-restify/issues/1352)) ([12714cf](https://www.github.com/restify/node-restify/commit/12714cfce5048c65b4256df660766e863578b90a))\n* doc site ([#1393](https://www.github.com/restify/node-restify/issues/1393)) ([76ee548](https://www.github.com/restify/node-restify/commit/76ee5480cfcb7f36e39e3e0955102c04abdac867))\n* documentation update for restifyError event example ([#1398](https://www.github.com/restify/node-restify/issues/1398)) ([94fe715](https://www.github.com/restify/node-restify/commit/94fe715173ffcebd8814bed7e17a22a24fac4ae8))\n* emit restifyError event even for router errors ([#1420](https://www.github.com/restify/node-restify/issues/1420)) ([f9d02d5](https://www.github.com/restify/node-restify/commit/f9d02d5b358863b9e067da5d6c89b4e283f420ba))\n* redirect should work even when hostname or protocol is not specified in req.url ([#1497](https://www.github.com/restify/node-restify/issues/1497)) ([e696a1f](https://www.github.com/restify/node-restify/commit/e696a1f80cd84e7d3db9fb85a18212f970f9a0d3))\n* **server:** error in pre handler triggers after event ([#1500](https://www.github.com/restify/node-restify/issues/1500)) ([c2e6dea](https://www.github.com/restify/node-restify/commit/c2e6deae5dab78187a8b09ce5256fb09db390bc9))\n* exclude package-lock.json ([#1477](https://www.github.com/restify/node-restify/issues/1477)) ([011fdf0](https://www.github.com/restify/node-restify/commit/011fdf0e2e5b456fe18c9d2ef838819f52586c14))\n* **static:** avoid user-provided data in Error messages being interpreted as sprintf codes ([#1384](https://www.github.com/restify/node-restify/issues/1384)) ([#1472](https://www.github.com/restify/node-restify/issues/1472)) ([9906344](https://www.github.com/restify/node-restify/commit/99063447419e7dcd0bf4ff6c38c5ad1867a2e1f3))\n* audit timers of same name should accumulate ([#1435](https://www.github.com/restify/node-restify/issues/1435)) ([#1443](https://www.github.com/restify/node-restify/issues/1443)) ([a2d34aa](https://www.github.com/restify/node-restify/commit/a2d34aaa461cabf47147990a1c2910ea9a53b2d8))\n* GH-1438, error reponse customization documentation incorrect ([#1439](https://www.github.com/restify/node-restify/issues/1439)) ([dd66088](https://www.github.com/restify/node-restify/commit/dd66088f3067d4b0858a2dd0274c705faf374e0e))\n* Honor port for redirect ([#1363](https://www.github.com/restify/node-restify/issues/1363)) ([61c0cb5](https://www.github.com/restify/node-restify/commit/61c0cb5c697bcd84c2f7255bfe158619694fb73d))\n* monkey patch getHeaders for pre-v7 Node.js (GH-1409) ([82088a7](https://www.github.com/restify/node-restify/commit/82088a7185331c7de092450ffec52d815c079739))\n* package.json version now matches npm ([9944dbd](https://www.github.com/restify/node-restify/commit/9944dbd57795fa312c8f35c4734977698d70c895))\n* respect when status code is set with res.status (GH-1429) ([#1440](https://www.github.com/restify/node-restify/issues/1440)) ([5abc067](https://www.github.com/restify/node-restify/commit/5abc06779df3b3ed4faf4d19f0815051a7c3106b))\n* test static plugin's handling of sprintf escape sequences ([#1391](https://www.github.com/restify/node-restify/issues/1391)) ([5d7039a](https://www.github.com/restify/node-restify/commit/5d7039a5b97e158347fbb918b866b7aeebd4a14f))\n* update chai (^3.4.1 to ^4.0.0) ([f982d0c](https://www.github.com/restify/node-restify/commit/f982d0c71f1b72f79e07f33f6cdf43741242f5d8))\n* Update dependency mime to 1.4.0 ([#1467](https://www.github.com/restify/node-restify/issues/1467)) ([6d38b38](https://www.github.com/restify/node-restify/commit/6d38b38c7a67e9b7cb8500fd1a92751e5ea4ee38))\n* update http-signature to v1.0.0 ([#1401](https://www.github.com/restify/node-restify/issues/1401)) ([ec88737](https://www.github.com/restify/node-restify/commit/ec887376a8314edbb623db48e6288d5a352a4efd))\n* use `Buffer.isBuffer` instead of `util.isBuffer`. ([#1593](https://www.github.com/restify/node-restify/issues/1593)) ([35bd1c2](https://www.github.com/restify/node-restify/commit/35bd1c2b375ea70dc2b4a4549461ff59ff5e4ec4))\n* versioned route matching should not throw TypeError ([#1381](https://www.github.com/restify/node-restify/issues/1381)) ([25d10f0](https://www.github.com/restify/node-restify/commit/25d10f00a4c9128b87cda0261aa3a041ac652f63))\n* **audit:** use public APIs for accessing response headers ([5169db7](https://www.github.com/restify/node-restify/commit/5169db7b1d2c9979e534b2c27912f5be398bcbca)), closes [/nodejs.org/api/deprecations.html#deprecations_dep0066](https://www.github.com/restify//nodejs.org/api/deprecations.html/issues/deprecations_dep0066)\n\n\n* Prefer Pino logger over Bunyan (#1841) ([2f5bf87](https://www.github.com/restify/node-restify/commit/2f5bf8722c9e0ba0d45f32af5c2c16ddbaa538b4)), closes [#1841](https://www.github.com/restify/node-restify/issues/1841)\n\n\n### Miscellaneous Chores\n\n* drop support for node 8 ([bd34988](https://www.github.com/restify/node-restify/commit/bd349884321d3e8af549f4d9da4456774e82ac8b))\n* remove deprecated usage of pino.child ([#1902](https://www.github.com/restify/node-restify/issues/1902)) ([0a8cf83](https://www.github.com/restify/node-restify/commit/0a8cf8345de26f8ee98e87c0085f0f9439302d98))\n* **travisci:** revisit nodejs version. Change to: LTS active, LTS maintenance (4.x) and stable releases ([#1553](https://www.github.com/restify/node-restify/issues/1553)) ([49eb008](https://www.github.com/restify/node-restify/commit/49eb008d987f1c425989b78e2336e3583e05a88a))\n\n## [11.2.0](https://github.com/restify/node-restify/compare/v11.1.0...v11.2.0) (2023-08-11)\n\n\n### Features\n\n* allow alternate name for request id in logs ([cbd16ef](https://github.com/restify/node-restify/commit/cbd16efa3be36e7888ecccc15ee28eaa8fa6c5ef))\n* support Node.js 20 ([9f1d249](https://github.com/restify/node-restify/commit/9f1d249c3fd023b05ac15c02352ec937ff7d1299))\n\n\n### Bug Fixes\n\n* tests broke due to find-my-way update ([f8beaae](https://github.com/restify/node-restify/commit/f8beaaef64c0541185bc4c2d864948d3c1299cc9))\n\n## [11.1.0](https://github.com/restify/node-restify/compare/v11.0.0...v11.1.0) (2023-02-24)\n\n\n### Features\n\n* allow custom alternatives to domains ([54adfcb](https://github.com/restify/node-restify/commit/54adfcbdea1a6be3675dbc05573f8063fc16a05b))\n\n## [11.0.0](https://github.com/restify/node-restify/compare/v10.0.0...v11.0.0) (2023-01-17)\n\n\n### ⚠ BREAKING CHANGES\n\n* don't override req.log if set during .first\n* use req.log on audit plugin\n\n### Features\n\n* don't override req.log if set during .first ([852d2c1](https://github.com/restify/node-restify/commit/852d2c153d1815274db8cdd7799625e9740090b3))\n* use req.log on audit plugin ([528ecbc](https://github.com/restify/node-restify/commit/528ecbcec5d70c458749bdd4c4cc3f9e06ab69a2))\n\n## [10.0.0](https://github.com/restify/node-restify/compare/v9.0.0...v10.0.0) (2022-11-29)\n\n\n### ⚠ BREAKING CHANGES\n\n* support v18.x\n\n### Features\n\n* bump dtrace-provider version to avoid MacOS errors ([fa52f60](https://github.com/restify/node-restify/commit/fa52f60d85c3df8a1babde98be184bb918958ef3))\n* support v18.x ([5795223](https://github.com/restify/node-restify/commit/57952239fa1808a6cf6e70deb2754c4c85c1be39))\n\n### 8.5.1 (2019-12-13)\n\n\n#### Bug Fixes\n\n* **benchmark:** force latest restify version (#1810) ([b8ec60e3](git://github.com/restify/node-restify.git/commit/b8ec60e3))\n* **server:** address domain performance regression with Node v12.x (#1809) ([e648d491](git://github.com/restify/node-restify.git/commit/e648d491))\n\n\n<a name=\"8.5.0\"></a>\n## 8.5.0 (2019-12-02)\n\n\n#### Features\n\n* **chain:** use nextTick instead of setImmediate (#1808) ([703470ad](git://github.com/restify/node-restify.git/commit/703470ad))\n\n\n<a name=\"8.4.1\"></a>\n### 8.4.1 (2019-11-27)\n\n\n<a name=\"8.4.0\"></a>\n## 8.4.0 (2019-07-31)\n\n\n#### Features\n\n* **chain:** schedule handlers to the next tick (#1798) ([806ed711](git://github.com/restify/node-restify.git/commit/806ed711))\n\n\n<a name=\"8.3.3\"></a>\n### 8.3.3 (2019-06-04)\n\n\n<a name=\"8.3.2\"></a>\n### 8.3.2 (2019-05-06)\n\n\n<a name=\"8.3.1\"></a>\n### 8.3.1 (2019-04-25)\n\n\n#### Bug Fixes\n\n* **test:** make upgrade test pass (#1772) ([d30b7483](git://github.com/restify/node-restify.git/commit/d30b7483))\n\n\n<a name=\"8.3.0\"></a>\n## 8.3.0 (2019-04-11)\n\n\n#### Features\n\n* provide callback to uncaughtException handler (#1766) ([5e8b5e2b](git://github.com/restify/node-restify.git/commit/5e8b5e2b))\n\n\n<a name=\"8.2.0\"></a>\n## 8.2.0 (2019-03-18)\n\n\n#### Bug Fixes\n\n* properly handle non-errors thrown in domains (#1757) ([cb2e7177](git://github.com/restify/node-restify.git/commit/cb2e7177))\n* **cpuUsageThrottle:** support breaking change in pidusage module ([7460064f](git://github.com/restify/node-restify.git/commit/7460064f))\n\n\n#### Features\n\n* **first:** Handlers that execute ASAP in the req/res lifecycle (#1756) ([8178098d](git://github.com/restify/node-restify.git/commit/8178098d))\n\n\n<a name=\"8.1.1\"></a>\n### 8.1.1 (2019-03-14)\n\n#### Bug Fixes\n\n* Published NPM package had a bad dependency on `npm` causing new irrelevant packages to get installed\n\n<a name=\"8.1.0\"></a>\n## 8.1.0 (2019-03-06)\n\n\n#### Bug Fixes\n\n* **dev:** upgrading modules including restify-errors (#1755) ([3b712298](git://github.com/restify/node-restify.git/commit/3b712298))\n\n\n#### Features\n\n* add router.render() back to support hypermedia usecase (#1752) ([0700cfd4](git://github.com/restify/node-restify.git/commit/0700cfd4), closes [#1684](git://github.com/restify/node-restify.git/issues/1684))\n* **plugin:** plugin to serve static files (#1753) ([a67b25f4](git://github.com/restify/node-restify.git/commit/a67b25f4))\n\n\n<a name=\"8.0.0\"></a>\n## 8.0.0 (2019-02-20)\n#### Breaking Changes\n\n* Dropped Support for Node v4.x and Node v6.x\n\n\n<a name=\"7.7.0\"></a>\n## 7.7.0 (2019-02-01)\n\n\n#### Bug Fixes\n\n* **dev:**\n  * remove nsp since the project merged with npm ([1dc34b48](git://github.com/restify/node-restify.git/commit/1dc34b48))\n  * pin to exact versions of linting tools and fix lint errors ([3740a6b7](git://github.com/restify/node-restify.git/commit/3740a6b7))\n\n\n#### Features\n\n* **audit:** Add the ability to specify a custom audit log serializer (for err, req and res)  ([6231acda](git://github.com/restify/node-restify.git/commit/6231acda))\n\n\n<a name=\"7.6.0\"></a>\n## 7.6.0 (2019-01-18)\n\n\n#### Features\n\n* **req:** add restifyDone event (#1740) ([4900d6bd](git://github.com/restify/node-restify.git/commit/4900d6bd))\n\n\n<a name=\"7.5.0\"></a>\n## 7.5.0 (2019-01-09)\n\n\n#### Bug Fixes\n\n* emit after event with proper error param for node versions >= 11.4.0 (#1732) ([7a1378b0](git://github.com/restify/node-restify.git/commit/7a1378b0))\n\n\n#### Features\n\n* **plugins:** context, req.get() returns the whole context (#1739) ([6e35e01e](git://github.com/restify/node-restify.git/commit/6e35e01e))\n\n\n<a name=\"7.4.0\"></a>\n## 7.4.0 (2019-01-02)\n\n\n#### Bug Fixes\n\n* more flaky metrics.test.js fixes (#1730) ([71aac428](git://github.com/restify/node-restify.git/commit/71aac428))\n\n\n#### Features\n\n* **plugins:** do not include user-input in UnsupportedMediaTypeError message (VError fails), m ([06c220d2](git://github.com/restify/node-restify.git/commit/06c220d2))\n\n\n<a name=\"7.3.0\"></a>\n## 7.3.0 (2018-12-07)\n\n\n#### Features\n\n* add support for non-strict formatters (#1721) ([de1833a4](git://github.com/restify/node-restify.git/commit/de1833a4))\n\n\n<a name=\"7.2.3\"></a>\n### 7.2.3 (2018-11-16)\n\n\n#### Bug Fixes\n\n* **server:** fix uncaught exceptions triggering route lookups (#1717) ([e49cb3b2](git://github.com/restify/node-restify.git/commit/e49cb3b2))\n\n\n<a name=\"7.2.2\"></a>\n### 7.2.2 (2018-10-29)\n\n\n#### Bug Fixes\n\n* documentation typo fix (#1688) ([0fa71328](git://github.com/restify/node-restify.git/commit/0fa71328))\n\n\n<a name=\"7.2.1\"></a>\n### 7.2.1 (2018-06-07)\n\n\n#### Bug Fixes\n\n* proxy events into instance var and add test script (#1661) ([de72f49e](git://github.com/restify/node-restify.git/commit/de72f49e))\n* **server:** address req and res close event changes in Node v10.x (#1672) ([6be3fb7c](git://github.com/restify/node-restify.git/commit/6be3fb7c))\n\n\n#### Features\n\n* jsonBodyParser handles extended content types *+json (#1663) ([45375144](git://github.com/restify/node-restify.git/commit/45375144))\n\n\n<a name=\"7.2.0\"></a>\n## 7.2.0 (2018-05-16)\n\n\n#### Features\n\n* **helpers:** add compose feature (#1660) ([eb60ef40](git://github.com/restify/node-restify.git/commit/eb60ef40))\n\n\n<a name=\"7.1.2\"></a>\n### 7.1.2 (2018-05-15)\n\n\n#### Bug Fixes\n\n* **dtrace:** route probes (#1659) ([84bcded7](git://github.com/restify/node-restify.git/commit/84bcded7))\n\n\n<a name=\"7.1.1\"></a>\n### 7.1.1 (2018-04-10)\n\n\n#### Bug Fixes\n\n* **plugins:** save req._matchedVersion (#1642) ([69f917a3](git://github.com/restify/node-restify.git/commit/69f917a3))\n\n\n<a name=\"7.1.0\"></a>\n## 7.1.0 (2018-03-26)\n\n\n#### Features\n\n* **router:** add ignoreTrailingSlash router option (#1632) ([92ffbf5c](git://github.com/restify/node-restify.git/commit/92ffbf5c))\n\n\n<a name=\"7.0.0\"></a>\n## 7.0.0 (2018-03-20)\n\n\n#### Features\n\n* **server:** new router and middleware system (#1561) ([1161370b](git://github.com/restify/node-restify.git/commit/1161370b))\n\n\n#### Breaking Changes\n\n*\n- Server returns `RequestCloseError` instead of `RequestAbortedError`\n- Non-strict routing is gone\n- Different `RegExp` usage in router path and wildcards\n- Remove already deprecated `next.ifError`\n- Disable DTrace probes by default\n- Change in calling `next` multiple times\n- Router versioning and content type as a separate plugin: `conditionalHandler`\n- After event fires when both request is flushed and the last handler is finished\n- Metrics plugin latency logic changes and new latencies were added\n\nFor more info see the `/guides/6to7guide.md`.\n ([1161370b](git://github.com/restify/node-restify.git/commit/1161370b))\n* dropping support below Node.js 4\n ([0698f45c](git://github.com/restify/node-restify.git/commit/0698f45c))\n\n\n<a name=\"6.4.0\"></a>\n## 6.4.0 (2018-03-20)\n\n\n#### Bug Fixes\n\n* server should fire not acceptable event (#1627) ([8b11b71b](git://github.com/restify/node-restify.git/commit/8b11b71b))\n* send numbers or bools as payloads (#1609) ([0919f26d](git://github.com/restify/node-restify.git/commit/0919f26d))\n* Allow multiple unmerged set-cookie headers. (#1570) ([df040154](git://github.com/restify/node-restify.git/commit/df040154))\n* add support for secureOptions in createServer (#1575) ([656e60e0](git://github.com/restify/node-restify.git/commit/656e60e0))\n* use `Buffer.isBuffer` instead of `util.isBuffer`. (#1593) ([35bd1c2b](git://github.com/restify/node-restify.git/commit/35bd1c2b))\n* **jsonBodyParser:** fix percent sign causing server fail (#1411) ([bde8fda6](git://github.com/restify/node-restify.git/commit/bde8fda6))\n* **request:** date() and time() methods return value (#1576) ([4c2cb1a7](git://github.com/restify/node-restify.git/commit/4c2cb1a7))\n\n\n<a name=\"6.3.4\"></a>\n### 6.3.4 (2017-11-21)\n\n\n#### Bug Fixes\n\n* **bodyReader:** Fix memory leak (#1566) ([756b3f02](git://github.com/restify/node-restify.git/commit/756b3f02))\n* **server:** avoiding uncaughtException in _routeErrorResponse by only sending response when  ([cf65c65c](git://github.com/restify/node-restify.git/commit/cf65c65c))\n\n\n<a name=\"6.3.2\"></a>\n### 6.3.2 (2017-11-08)\n\n\n<a name=\"6.3.1\"></a>\n### 6.3.1 (2017-11-03)\n\n\n#### Bug Fixes\n\n* **server:** avoid http2 experimental warning without http2 option (#1555) ([12da7fdf](git://github.com/restify/node-restify.git/commit/12da7fdf))\n\n\n<a name=\"6.3.0\"></a>\n## 6.3.0 (2017-11-02)\n\n\n#### Features\n\n* **http2:** add native HTTP/2 support (#1489) ([6b202853](git://github.com/restify/node-restify.git/commit/6b202853))\n\n\n<a name=\"6.2.3\"></a>\n### 6.2.3 (2017-10-18)\n\n\n<a name=\"6.2.2\"></a>\n### 6.2.2 (2017-10-18)\n\n\n#### Bug Fixes\n\n* **package-lock.json:** remove artifacts.netflix.com repo (#1526) ([3d2f0f7d](git://github.com/restify/node-restify.git/commit/3d2f0f7d))\n\n\n<a name=\"6.2.1\"></a>\n### 6.2.1 (2017-10-18)\n\n\n#### Bug Fixes\n\n* **cpuUsageThrottle:** dont include interval in lag (#1504) ([eecb2d25](git://github.com/restify/node-restify.git/commit/eecb2d25))\n* **npm:** remove unleash dependency (#1522) ([a43aa60f](git://github.com/restify/node-restify.git/commit/a43aa60f))\n* **plugins:** use process.hrtime() for duration calculation (#1507) ([e8efd6cd](git://github.com/restify/node-restify.git/commit/e8efd6cd))\n\n\n<a name=\"6.2.0\"></a>\n## 6.2.0 (2017-10-16)\n\n\n#### Bug Fixes\n\n* **cpuUsageThrottle:** dont include interval in lag (#1504) ([eecb2d25](git://github.com/restify/node-restify.git/commit/eecb2d25))\n* **plugins:** use process.hrtime() for duration calculation (#1507) ([e8efd6cd](git://github.com/restify/node-restify.git/commit/e8efd6cd))\n\n\n<a name=\"6.1.0\"></a>\n## 6.1.0 (2017-10-16)\n\n\n#### Bug Fixes\n\n* **cpuUsageThrottle:** dont include interval in lag (#1504) ([eecb2d25](git://github.com/restify/node-restify.git/commit/eecb2d25))\n* **plugins:** use process.hrtime() for duration calculation (#1507) ([e8efd6cd](git://github.com/restify/node-restify.git/commit/e8efd6cd))\n\n\n<a name=\"6.0.1\"></a>\n### 6.0.1 (2017-09-19)\n\n\n#### Bug Fixes\n\n* **cpuUsageThrottle:** Correctly named handler for debugInfo (#1499) ([78b0900b](git://github.com/restify/node-restify.git/commit/78b0900b))\n* **server:** error in pre handler triggers after event (#1500) ([c2e6deae](git://github.com/restify/node-restify.git/commit/c2e6deae))\n\n\n<a name=\"6.0.0\"></a>\n## 6.0.0 (2017-09-15)\n\n\n#### Bug Fixes\n\n* exclude package-lock.json (#1477) ([011fdf0e](git://github.com/restify/node-restify.git/commit/011fdf0e))\n* Update dependency mime to 1.4.0 (#1467) ([6d38b38c](git://github.com/restify/node-restify.git/commit/6d38b38c))\n* **cpuUsageThrottle:** Always queue a new timeout (#1484) ([e4ffe430](git://github.com/restify/node-restify.git/commit/e4ffe430))\n* **inflightRequestThrottle:** properly handle next (#1471) ([4db404f9](git://github.com/restify/node-restify.git/commit/4db404f9))\n* **static:** avoid user-provided data in Error messages being interpreted as sprintf codes (# ([99063447](git://github.com/restify/node-restify.git/commit/99063447))\n\n\n#### Features\n\n* cpuUsageThrottle (#1460) ([84be6799](git://github.com/restify/node-restify.git/commit/84be6799))\n* **throttle plugin:** expose rate limit metrics as headers (#1453) ([1627a557](git://github.com/restify/node-restify.git/commit/1627a557))\n\n\n<a name=\"5.2.0\"></a>\n## 5.2.0 (2017-08-16)\n\n\n#### Bug Fixes\n\n* package.json version now matches npm ([9944dbd5](git://github.com/restify/node-restify.git/commit/9944dbd5))\n* create unit tests for sanitizePath plugin (#1352) ([12714cfc](git://github.com/restify/node-restify.git/commit/12714cfc))\n* audit timers of same name should accumulate (#1435) (#1443) ([a2d34aaa](git://github.com/restify/node-restify.git/commit/a2d34aaa))\n* respect when status code is set with res.status (GH-1429) (#1440) ([5abc0677](git://github.com/restify/node-restify.git/commit/5abc0677))\n* versioned route matching should not throw TypeError (#1381) ([25d10f00](git://github.com/restify/node-restify.git/commit/25d10f00))\n\n\n<a name=\"5.0.1\"></a>\n### 5.0.1 (2017-07-17)\n\n\n#### Bug Fixes\n\n* monkey patch getHeaders for pre-v7 Node.js (GH-1409) ([82088a71](git://github.com/restify/node-restify.git/commit/82088a71))\n* add node 7-8 travis support (#1405) ([536a4735](git://github.com/restify/node-restify.git/commit/536a4735))\n* Add migration guid to website (#1402) ([5f053c7e](git://github.com/restify/node-restify.git/commit/5f053c7e))\n* update http-signature to v1.0.0 (#1401) ([ec887376](git://github.com/restify/node-restify.git/commit/ec887376))\n* documentation update for restifyError event example (#1398) ([94fe7151](git://github.com/restify/node-restify.git/commit/94fe7151))\n* doc site (#1393) ([76ee5480](git://github.com/restify/node-restify.git/commit/76ee5480))\n* test static plugin's handling of sprintf escape sequences (#1391) ([5d7039a5](git://github.com/restify/node-restify.git/commit/5d7039a5))\n"
  },
  {
    "path": "CHANGES.md",
    "content": "# restify Changelog\n\n## 5.0.0\n - #1377 Remove async formatters\n - #1363 Honor port for `Response.prototype.redirect`\n - #1369 Use public APIs for accessing response headers\n - #1353 Deprecate `next.ifError`\n - #1346 Return plugins to repo\n - #1322 Remove duplicate `close` event from `Server`\n - #1309 Add `getRoute()` to Request to get the route object. Rajat Kumar\n - #1288 add `pre` and `routed` events. Yunong Xiao\n - #1281 Add `server.getDebugInfo()` method, Yunong Xiao, Alex Liu\n - #1281 `server.unfinishedRequests()` to `server.inflightRequests()`, Yunong Xiao\n - #1256 add `req.id()` method, Alex Liu\n - #1251 add `req.connectionState()` method, Alex Liu\n - #1250 add `server.unfinishedRequests()` method, Alex Liu\n - #1247 Update jyoent cloud API link in README, Devinsuit\n - #1246 Fix syntax error in plugins.md example, Aria Stewart\n - #1241 Rev formidable to remove Node6+ warnings, Alex Liu\n - #1234 Update uuid to version 3.0.0, Marc Bachmann\n - #1212 Fix typos in plugins.md, Greg Walden\n - #1199 Update examples to use ES6, Amila Welihinda\n - #1190 Fix minor typo, The-Alchemist\n - #1179 Fix typo in comment, Niklas Ingholt\n - Fix dtrace demo to not use async formatter, Yunong Xiao\n - #1171 Router unmount now works for versioned routes, gcssabbagh\n - #1143 add docs about serveStatic plugin, Michael Burguet\n - #1135 ability to find a route by a path, Jacob Quatier\n - #1129 always invoke res.send callback if one is provided, even when the\n   selected formatter is sync, Alex Liu\n - #1128 don't send domain errors to the client, Alex Liu\n - #1123 add deprecation warnings for domain dependent features, Alex Liu\n - #1119 set response status code to 0 when the request is terminated by the\n   client, Alex Liu\n - #1118 remove undocumented exports and other unused methods, Alex Liu\n - #1113 Fix JSDOC comments, Marc Riegel\n - #1111 new documentation guides, Nicolas Artman\n - #1092 support for strict routing, lukealbao\n - #1089 remove route from LRU cache on when calling `server.rm`, Luis Gómez\n - #1086 support re-using request id headers on incoming requests, Alex Liu\n - #1081 update documentation, default content-type is now application/json,\n   Dmitry Kirilyuk\n - #1078 send the server name down in the header per documentation, Alex Liu\n - #1072 update documentation for accept-version header, Ingo Renner\n - #1071 rev node-uuid to address advisory CVE-2015-8851, Alex Liu\n - #1056 fix `req.absoluteUri()` to use correct protocol, David Marek\n - #1032 fix potential xss vector, Alex Liu\n - #1024 **BREAKING** Disabled the uncaughtException handler by default, added\n   server option 'handleUncaughtExceptions' to allow enabling of it again\n   (restify 4.x and before used to handle exceptions by default), Todd Whiteman\n - #1047 update documentation for spdy example, Tyler Akins\n - #1041 add `rejectUnknown` option to restify plugin documentation, Dmitry\n   Kirilyuk\n - #1038 capitalize header field for Location per RFC, Tommi Kyntola\n - #1011 update documentation to remove outdated references. fix more links,\n   lukealbao\n - #1010 update documentation to include charSet property for static plugin,\n   Greg Walker\n - #1000 update spdy to 3.2.0, Andy Tzeng\n - #1007 remove `defaultResponseHeaders` from documentation, lukealbao\n - #999 server `NotFound` handler is now normalized, works like other error\n   events and no longer flushes responses automatically, Alex Liu\n - #991 update documentation links for new plugins and errors repo, Ken Warner\n - #987 disallow multiple values for content-type header, James O'Cull\n - #985 CORS removed from restify core. support `next(false)` in pre handler\n   chains, Alex Liu\n - #982 allow sending of null body, Felix Milea-Ciobanu\n - #973 rev latest restify-errors, fix todoapp examples, Micah Ransdell\n - #972 added shrinkwrap+nsp for security vuln checks, Alex Liu\n - #971 Fix error creation when error message contain URI encoded characters,\n   Benjamin Urban\n - #969 Fix incorrect usage of assert.AssertionError, Alex Liu\n - #965 added unit test for sending null body, Michael Nisi\n - #964 Fix cached routes not setting maxVersion, Alex Liu\n - #963 enhancements to res.redirect. server now emits `redirect` event, James\n   Womack\n - #960 update documentation for websocket example, Richard Kiene\n - #958 RequestCaptureStream now writes triggering record, Gerrard Lindsay,\n   Yunong Xiao\n - #955 update documentation for socket.io example, Thorsten Hans\n - #952 Formatters no longer set status codes or inspect payload, Christian\n   Bongiorno, Alex Liu\n - #951 `res.sendRaw()` allows sending of responses without use of formatters,\n   Matthew Amato, Alex Liu\n - #947 update documentation links for readme.md & badges, ReadmeCritic\n - #944 Support generic event listener, Alex Liu\n - #943 Fix typos in documentation, azollyx\n - #939 Fix issue where missing content-type header would hang response, Alex\n   Liu\n - #935 Clearer docs for using certs, Vikram Tiwari\n - #932 Update to spdy 2.x, Fedor Indutny\n - #924 Update docs for async formatter breaking change, Magnus Wolffelt\n - #891 stop processing requests when 'close' event has been fired (early\n   client termination), Alex Liu\n - #883 hypens no longer stripped from route names, Sean Wragg\n - #903 Update docs to reflect new error handling, Jacob Quatier\n - #889 Bump dependencies to latest, Micah Ransdell\n - #845 Support sync and async formatters, Alex Liu\n - #844 Move Errors to their own module, Alex Liu\n - #855 Clients now live in its own repo and npm module, Alex Liu\n - Various documentation improvements from leitsubomi\n\n## 4.3.0\n\n- #1024 Add `handleUncaughtExceptions` server option to supporting disabling\n  the uncaughtException handler.\n\n## 4.2.0\n\n- #925 Support passing (most) [qs](https://github.com/ljharb/qs#readme) options\n  to the `restify.queryParser` plugin. Update the \"qs\" dependency to latest (v6)\n  while maintaining backward compatibility (see notes in the API doc and\n  \"test/query.test.js\" on `allowDots` and `plainObjects`).\n\n## 4.1.1\n\n- update negotiator (NSP advisory #106) and lru-cache (bug fix).\n\n## 4.1.0\n\n- Bump SPDY to latest.\n- #959: fix issue where cached routes were not setting maxVersion on the req\n\n## 4.0.4\n\n- #937 Fix missing content-type header causing response to hang\n\n## 4.0.3\n- #917 Fix: HTTP 413 status name, Micah Ransdell\n\n## 4.0.2\n- #887 Bump dtrace-provider to 0.6.0 for Node 4 support, Corbin Uselton\n\n## 4.0.0\n- #877 content-type can be case-insensitive. Yunong Xiao\n- #856 update various dependencies. Alex Liu\n- #851 **BREAKING** fix formatters such that they always return cb. Yunong Xiao\n- #847 fix body parser race condition. Yunong Xiao\n- #842 add `req.matchedVersion()` Nathan Peck, Micah Ransdell\n- #840 Fix issue with server toString Method. OiNutter, Micah Ransdell\n- #836 Add JSDoc comments. Alex Liu\n- #835 Update static.js to allow for serving static files that do not use the route as a path. Wavewash, Micah Ransdell\n- #831 Support hash option to Formidable for multipart file uploads. blakevanian, ManRueda\n- #832 Updated dtrace-provider. yads\n- #812 add query parameters to auditlogger. Alex Liu\n- #800 Allow 0, false, and null as json body. Alex Dobeck\n- #771 q-value choice on wildcards ignores default q-value of 1. Kevin Peno\n- #822 Allow optional headers to be added as properties to bunyan logs. Michael Paulson.\n- #824 Don't include large coverage files in published packages. Trent Mick\n- #819 Add a feature to allow the expiration of old unprocessed requests. Michael Paulson\n- #803 Add redirect support to Response. Alex Liu\n- #686 `res.send` can't send 0, false and null. Alex Dobeck\n\n## 3.0.3\n- #669 Fix socket.io 1.x integration. Mark Doeswijk\n- #662 Improve request logger doc. Jordan Klassen\n- #793 Update Server API error event listener doc. github.com/durkes\n- #795 Remove unused vars in source. James Womack\n- #796 Hypermedia API fails when paths have multiple patterns with sub-regexs. Morten Fangel\n- #775 Fix UTF-8 corruption in body parser. Michał Moskal\n\n## 3.0.2\n- #785 update semver dependency.\n\n## 3.0.1\n- #779 set-cookie headers should not include comma separated values. See:\n  http://tools.ietf.org/html/rfc6265#section-3\n\n## 3.0.0\n\n- Bumping major because of #753\n\n## 2.9.0\n\n- #688 Fix various throttle bugs\n- #691 Fix an issue where posting with text/csv content type crashes Restify\n- #693 Support multiple response header values\n- #704 Allow partial regex for named parameters\n- #726 Allow per-request agent overrides\n- #726 Ebanle `{agent: false}` option override per request\n- #727 Fix JSON body parser behavior when request body is null\n- #727 Fix a bug when `req.body === null`\n- #731 SVG badges in README\n- #734 Add API to track timers for nested handlers\n- #744 Fix `request.isUpload` for PATCH requests\n- #751 Fix `server.url` property when using IPv6\n- #758 Switch to UUID v4\n- #758 Use v4 UUIDs for `[x-]request-id`\n- #759 Documentation fix\n- #762 `res.noCache()` API to prevent all caching\n- #767 Prefer the existing `err` serializer for audit logging\n- Update dtrace-provider dependency\n- #753 **BREAKING** Include `err` parameter for all \\*Error events:\n  Error events will all have the signature `function (req, res, err, cb)` to\n  become consistent with the handling functionality introduced in 2.8.5.\n  Error handlers using the `function (req, res, cb)` signature must be updated.\n\n## 2.8.5\n\n- Add ability to listen for error events\n- Documentation fixes\n\n## 2.8.4\n\n- Update dtrace-provider, bunyan and backoff\n- Fix request URL cache interaction with dtrace probes\n\n## 2.8.3\n\n- Support html5 multiple file uploads\n\n## 2.8.2\n\n- #619 Default to url, if string provided to createClient\n- #614 do not compute the MD5 Hash of a partial content\n- #516 Allow an `options` object to be passed into the authorization plugin\n- Updating dependencies\n- #626 Add more built-in errors to doc\n- #460 Provide direct access to https server options if needed\n- #656 update qs\n\n## 2.8.1\n\n- revert #604, work around by not removing client listener\n\n## 2.8.0\n\n- #604 trap http client errors\n- #598 Simplify and correct path segment regexp\n- #570 Route matching should only prefer later routes if version is greater\n- #564 Using req.accepts() according to implementation\n- #504 Helper to render a route with given parameters (for hypermedia APIs)\n\n## 2.7.0\n\n- #547 Added mapFiles option to bodyParser\n- #552 PUT JsonClient test should use PUT not POST\n- #550 Make router preflight code more readable\n- #548 Allow custom handling of multipart data.\n\n## 2.6.3\n\n- Hotfix for CORS plugin if no origin was set in the options\n\n## 2.6.2\n\n- #508 add server option: `ciphers` to pass down to https(tls)\n- #502 `server.on('request')` not emitting\n- #496 static plugin incorrectly handling `directories`; revert back to 2.6.0\n       version\n- #495 don't override client response code with custom error object\n- #494 socket connecting detection logic incorrect\n- #492 client `false` needs to actually disable retries\n- changed indent from four to eight\n- #505 fix audit logger plugin bug\n- #510 request timeout support\n- #523 added Access-Control-Allow-Credentials to the preflight handler\n\n## 2.6.1\n\n- #478 Add `req.timers` to audit logging plugin.\n- #487 RequestCaptureStream: dumpDefault, haveNonRawStreams, zero ring after dump\n- #407 - bunyan 0.21.3\n- Add CSV/TSV parser (Dominik Lessel)\n- Add `req.timers`: a list of hrtime's for each handler\n- Set TCP SO_KEEPALIVE when default KeepAliveAgent is on (client)\n\n## 2.6.0\n\n- EXPERIMENTAL: Native websocket support via watershed (Josh Clulow)\n- Pass entire route, not just route.name to `after` (Dingman)\n- Type coercion bug in Cache Control API (Chris Cannell)\n\n## 2.5.1\n\n- GH-401 RegEx routes stomp one another, resulting in 404\n- GH-389 StringClient should handle garbage servers that send neither\n         `Content-Length` nor `Transfer-Encoding: chunked` headers.\n\n## 2.5.0\n\n- Pick up http-signature@0.10.0 (breaking change, to those using it); see\n  https://github.com/joyent/node-http-signature/issues/10\n- GH-388 JSON client blows up on bad content\n- GH-379 Static plugin: NotAuthorizedError for file path with\n         parentheses (Ricardo Stuven)\n- GH-370 Add charSet option for static file plugin (Jonathan Dahan)\n\n## 2.4.1\n\n- Support node 0.10.X TLS options in client(s)\n\n## 2.4.0\n\n- GH-368 Route /\\/.*/ does not match request /? (Ben Hutchison)\n- GH-366 `req.accepts()` not working with short-hand mime types\n- GH-362 Empty body throws TypeError in StringClient (Bryan Donovan)\n- GH-355 Serve gzip encoded files from static if they are available\n         (Nathanael Anderson)\n- GH-338 turn `req.body` into an `Object` when content-type is\n         JSON (Daan Kuijsten)\n- GH-336 `res.charSet()` back in\n- dependency version bumps\n- 0.10.X support in tests (add necessary `resume()` calls)\n- client should log request/response pairs\n\n## 2.3.5\n\n- bunyan@0.20.0\n- GH-346 `server.toString()` crashes (Alex Whitman)\n- GH-193 support `next('name_of_route')`\n\n## 2.3.4\n\n- GH-343 default to 'identity' for accept-encoding\n- GH-342 client support for PATCH\n- Pick up spdy@1.4.6 (doesn't ship all the example garbage)\n\n## 2.3.3\n\n- Stop logging client_req in bunyan output\n- GH-319 make DTrace optional\n- GH-335 Content-Type'd routes not accepting array (Pedro Palazón)\n\n## 2.3.2\n\n- pick up bunyan 0.18.3\n- server.address() returning null causes server.url to deref null\n\n## 2.3.1\n\n- GH-335 Content-Type'd routes not correct when only Accept-Extension varies,\n         part deux (switch to `negotiator`, drop `mimeparse`).\n\n## 2.3.0\n\n- GH-335 Content-Type'd routes not correct when only Accept-Extension varies\n- GH-332 Cache-Control max-age should show minutes (Ben Hutchison)\n- GH-329 Wrong values in res.methods on some cases\n- GH-327 server.versionedUse('1.2.3', function (req, res, next) {}) (Tim Kuijsten)\n- GH-326 non-default origins not working, Chrome requires allow/origin and\n         allow users to append to CORS array (John Fieber/Damon Oehlman)\n- GH-323 <path>/?<querystring> broken\n- GH-322 add `req.route`, which contains the original params for the\n         route (Tim Kuijsten)\n- GH-312 bodyParser() should return buffers when data is binary (Tim Kuijsten)\n- GH-318 Allow use of 'requestBodyOnGet' option in bodyParser (@geebee)\n\n## 2.2.1\n\n- GH-283 broke versioned, typed routes. Fix.\n- node-http-signature@0.9.11\n\n## 2.2.0\n\n- GH-316 drop `clone`, and just shallow copy (Trent Mick)\n- GH-284 preflight requests not working without access-control-request-headers\n- GH-283 versioned routes should use maximum match, not first (Colin O'Brien)\n- dtrace probes for restify clients\n- node-dtrace-provider@0.2.8\n- backoff@2.0.0 and necessary changes\n\n## 2.1.1\n\n- revert to backoff@1.2.0\n\n## 2.1.0\n\n- GH-284 built-in CORS\n- GH-290 next.ifError\n- GH-291 fix overwriting `options.type` in createJSONClient (Trent Mick)\n- GH-297 default document serving in static plugin (Adam Argo)\n- GH-299 gzip plugin doesn't play nice with content-length (Ben Hale)\n- GH-301 support private keys w/passphrase (Erik Kristensen)\n- GH-302 responseTime cleanup\n- Move to `node-backoff` and rework retry logic in HttpClient\n- Support keep-alive by default in HttpClient\n\n## 2.0.4\n\n- GH-280 req.params cached by router\n- RequestCaptureStream should support outputting to multiple streams\n- Default uncaughtException handler should check if headers have been sent\n\n## 2.0.2/2.0.3\n\n- GH-278 missing events on route errors\n- Undo `RestError` `constructorOpt` from 2.0.1\n\n## 2.0.1\n\n- GH-269 plugin to make curl happy\n- RestError not honoring `constructorOpt` from `cause`\n- GH-271 bump to dtrace 0.2.6 (fix build on Mountain Lion)\n\n# Legacy Releases\n\n## 1.4.2\n\n- logging typo (Pedro Candel)\n- response `beforeSend` event (Paul Bouzakis)\n\n## 1.4.1\n\n- GH-130 Allow restify and express to coexist.\n- GH-129 format HttpErrors as well as RestErrors (Domenic Denicola)\n- GH-127 add absolute uri to request (Paul Bouzakis)\n- GH-124 `req.query` is `undefined` if no query string was sent\n- GH-123 Generated DTrace probe names should be valid\n- GH-122 Response._writeHead can cause infinite loop (Trent Mick)\n- GH-120 Allow server.patch (Paul Bouzakis)\n- GH-119 defaultResponseHeaders not settable\n- GH-113 document `return next(false)`\n\n\n## 1.4.0\n\n- GH-116 More friendly error objects (Domenic Denicola)\n- GH-115 Client hangs on server \"hard kills\" (i.e., RST)\n- GH-111 JSON parser only works on objects (not arrays)\n- GH-110 emit expectContinue (Paul Bouzakis)\n- Fix \"undefined\" log message in string_client.js\n- GH-107\n  - Go back to hacking up http.prototype for performance reasons\n  - Default to keep-alive on for HTTP/1.1 requests\n  - Misc fixes after refactoring.\n- GH-109 routes not honoring regex flags.\n- GH-108 server missing `listening` event.\n- Audit logger optionally logs request/response bodies\n- Require http-signature@0.9.9/ctype@0.5.0 (node 0.7 compatible)\n\n## 1.3.0\n\n- GH-100 Make DTrace an optional dependency, and stub it out if not found.\n- res.link API not allowing sprintf style sets.\n- Support for `socketPath` in client API (alternative to url).\n- OPTIONS api not returning access-control-allow-methods header (Steve Mason).\n- Allow null passwords in HTTP basic auth (Andrew Robinson).\n- set `req.files` on multipart file uploads (Andrew Robinson).\n\n## 1.2.0\n\n- Don't rely on instanceof checks for Errors in response.\n- Change route.run log level from trace to debug on next(err).\n- Add `res.link` API (wrap up sending a Link: response header).\n- GH-98 req.secure needs to return a boolean, not an object\n- GH-97 Malformed URI results in server crash\n- GH-94 leverage `qs` module for object notation in query string.\n\n## 1.1.1\n\n- dependency version bumps\n- res.header accepts sprintf-style arguments\n- GH-95 Make restify compatible with node-logging (Andrew Robinson)\n- GH-93 Minimal port of express pre-conditions (Dominic Barnes)\n- GH-92 Make X-Response-Time configurable (Shaun Berryman)\n- GH-87 server.listen on port as string (Andrew Sliwinski)\n\n## 1.1.0\n\n- GH-86 Bunyan version bump.\n- Conditional Request plugin tests and fixes for some errors (Mike Williams).\n- GH-83 pluggable storage engine for throttling, and LRU for default engine.\n- GH-77 `server.on('uncaughtException', function (req, res, route, err) {});`\n- GH-79 Docs typo(s).\n\n## 1.0.1\n\n- Version bump bunyan to 0.6.4.\n\n\n## 1.0.0\n\n- Makefile restructure (use Joyent templates)\n- GH-20 HttpClient connectTimeout.\n- Allow parser plugins to allow \"override\" params\n- Proper handling of Expect: 100\n- multipart/form-data plugin\n- Added a 'header' event on res.writeHead\n- GH-72 Wrong server name in response header on 404/405/...\n- RegExp mounts throw a TypeError\n- Allow pre handlers to update request url\n- try/catch around route running\n- Bundled audit logger (via bunyan)\n- strict adherence to RFC3986 for URL encoding\n- range versioning changed to be an array of explicit versions\n- Switch from log4js to [bunyan](https://github.com/trentm/node-bunyan)\n- Official version of `ConditionalRequest` plugin (Falco Nogatz)\n- order formatters on response such that JSON/text are before custom ones\n- RestErrors can use format strings\n- date plugin has bad log check\n\n\n## 1.0.0-rc2\n\n- GH-66 Support for charSets in responses\n- GH-65 Initial version of etag plugin (Falco Nogatz)\n- GH-68 res.header() can serialize Date objects to RFC1123\n- GH-67 Set some default response headers earlier (access-control-*)\n- http-client should auto insert the date header\n- GH-64 Support for a pre-routing chain\n- JsonClient should \"upcast\" errors to RestErrors if it can\n- GH-63 res.send(204) returning a body of 204\n- GH-61 Make bodyParser merging into req.params optional\n- Make Error objects backwards compatible with older restify (httpCode/restCode)\n- GH-57, GH-62 range versioning on server (Diego Torres)\n- GH-59 routes with just '/' are returning 404\n- DTrace *-done actually firing content-length (was always 0)\n- [Issue 56] Support streaming downloads\n- Modify server.on('after') to emit the `Route` object, rather than the name.\n\n## 1.0.0-rc1\n\n(Started maintaining this log 21 January 2012. For earlier change information\nyou'll have to dig into the commit history.)\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Restify\n\nWelcome to the restify community! This document is written both for maintainers and community members!\n\n## Issues and PRs\n\n### Commit Messages\n\nWhen merging a PR, we squash and merge to keep our commit history clean. Our commit messages use the conventional changelog format (http://conventionalcommits.org/) to automagically manage semver for us.\n\n### Labels and Templates\n\nWe try to keep things organized around here. Maintainers have a finite amount of time and are often juggling multiple things in their lives. Keeping things consistent and well labeled helps reduce the amount of concentration and effort required for us to both find and carry out work on the project. Simple things like using our templates and adding the appropriate labels may only take you a few minutes, but it can save cummulative hours worth of work for maintainers trying to digest dozens of issues.\n\n## Website\n\n### Design\n\nThe website templates are maintained at https://github.com/restify/restify.github.io and are populated from the docs directory in this repo.\n\n### Releasing a change\n\nTo update the documentaiton on the website to reflect the latest version of 5.x simply:\n\n```\ngit clone --recursive git@github.com:restify/restify.github.io\ncd restify.github.io\ngit submodule update --remote && git add _docs && git commit -m 'bump' && git push origin master\n```\n\nThe website will automatically deploy itself with the new changes.\n\n### Updating a documentation page\n\nTo update docs, simply run:  \n\n```\nmake docs-build\n```\n\n### Adding a documentation page\n\nTo add a new page, simply give it a [permalink](https://github.com/restify/node-restify/blob/94fe715173ffcebd8814bed7e17a22a24fac4ae8/docs/index.md) and then update [docs.yml](https://github.com/restify/restify.github.io/blob/master/_data/docs.yml) with the new permalink.\n\n## Running a benchmark\n\n```\nmake benchmark\n```\n\n## Cutting a release\n\nCutting a release is currently a manual process. We use a [Conventional Changelog](http://conventionalcommits.org/) to simplify the process of managing semver on this project. Generally, the following series of commands will cut a release from the `master` branch:\n\n```\n$ git fetch\n$ git pull origin master # ensure you have the latest changes\n$ npx unleash [-p for patch, -m for minor, -M for major] --no-publish -d # do a dry run to verify\n$ npx unleash [-p for patch, -m for minor, -M for major] --no-publish \n# Unleash doesnt support 2FA, hence we use --no-publish flag here.\n# This ensures we have the package.json updated, changelog generated, tag created\n# and all the changes into origin\n# Next, publish to npm manually and do not forget to provide the 2FA code.\n$ npm publish\n```\n"
  },
  {
    "path": "FEATURE_REQUESTS.md",
    "content": "Feature Requests\n================\n\nWhile the maintainers of restify work hard to provide the best possible\nframwork for building REST services, there is more work to go around than there\nare hours in the day. This document contains a set of features that have been\nrequested by the community. If you are looking to contribute, the items on\nthis list -- along with the open bugs on the issues tab -- are a great place to\nstart!\n\n> The features here are not sorted in any particular order. Each feature links\n> to the original GitHub issue requesting it. While feature requests are\n> generally closed and moved to this document, discussion around the feature\n> still takes place on the original issue. Even if there has been a discussion\n> on the issue already, it is still worth declaring your intent to open a PR\n> before investing time in writing code.\n\n## Code\n\n* [Support `server.use(route, handler)` and `server.all`][289]\n* [Support asynchronous callbacks for throttle][381]\n* [Streaming multipart parser without needing temporary files][474]\n* [Default client to http protocol][790]\n* [Exponential backoff and retry][633]\n* [Arbitrary HTTP methods][576]\n* [Detect route conflicts][909]\n* [Improve upon `next.ifError`][875]\n* [Benchmark suite][860]\n* [HTTP/2 support][853]\n* [Improve performance of route lookup][850]\n* [Support HTTP_PROXY][813]\n* [IE9 support for `bodyParser`][801]\n* [Multipart Client Support][921]\n* [Remove `next(err)`][1019]\n* [Support `RegExp` for `route.render`][632]\n* [Run internal handlers on `NotFound`][708]\n* [Support multiple apps on the same port][1035]\n* [Multiple versions for routes][1134]\n* [sysdig support][1323]\n* [Migrate routing DSL to `path-to-regexp`][1292]\n* [JQuery Style Query Expansion][895]\n* [Support Proxy Protocol][1046]\n\n## Documentation\n\n* [Socket.io support][717]\n* [`uncaughtException` handler when opting into domains][829]\n* [Forward `req_id`][1101]\n* [Document `bodyParser` headers][989]\n* [Improve signRequest documentation][737]\n* [Better documentation for `client.close`][859]\n* [Document all client options][1326]\n* [Client tunneling vs. proxying][1327]\n* [Explain why Restify is great!][927]\n* [`BasicAuth` examples][1099]\n* [Document `next` behaviour][1068]\n* [Remove defaultResponseHeaders][1040]\n* [Properly document req.accepts][957]\n* [Plugin custom errors][948]\n* [`findByPath` on `Router`][1136]\n* [Multiple route handlers][1183]\n* [Update new `HttpError` codes][1206]\n* [v4 res.headers][1286]\n* [Document RegExp DSL for routing][1065]\n\n[289]: https://github.com/restify/node-restify/issues/289\n[381]: https://github.com/restify/node-restify/issues/381\n[474]: https://github.com/restify/node-restify/issues/474\n[575]: https://github.com/restify/node-restify/issues/575\n[790]: https://github.com/restify/node-restify/issues/790\n[633]: https://github.com/restify/node-restify/issues/663\n[717]: https://github.com/restify/node-restify/issues/717#issuecomment-296531086\n[576]: https://github.com/restify/node-restify/issues/576\n[576]: https://github.com/restify/node-restify/issues/576\n[909]: https://github.com/restify/node-restify/issues/909\n[875]: https://github.com/restify/node-restify/issues/875\n[860]: https://github.com/restify/node-restify/issues/860\n[853]: https://github.com/restify/node-restify/issues/853\n[850]: https://github.com/restify/node-restify/issues/850\n[829]: https://github.com/restify/node-restify/issues/829\n[813]: https://github.com/restify/node-restify/issues/813\n[801]: https://github.com/restify/node-restify/issues/801\n[921]: https://github.com/restify/node-restify/issues/921\n[1101]: https://github.com/restify/node-restify/issues/1101\n[1019]: https://github.com/restify/node-restify/issues/1019\n[989]: https://github.com/restify/node-restify/issues/989\n[632]: https://github.com/restify/node-restify/issues/632\n[708]: https://github.com/restify/node-restify/issues/708\n[737]: https://github.com/restify/node-restify/issues/737\n[859]: https://github.com/restify/node-restify/issues/859\n[1326]: https://github.com/restify/node-restify/issues/1326\n[1327]: https://github.com/restify/node-restify/issues/1327\n[927]: https://github.com/restify/node-restify/issues/927\n[1099]: https://github.com/restify/node-restify/issues/1099\n[1068]: https://github.com/restify/node-restify/issues/1068\n[1040]: https://github.com/restify/node-restify/issues/1040\n[1035]: https://github.com/restify/node-restify/issues/1035\n[957]: https://github.com/restify/node-restify/issues/957\n[948]: https://github.com/restify/node-restify/issues/948\n[1134]: https://github.com/restify/node-restify/issues/1134\n[1136]: https://github.com/restify/node-restify/issues/1136\n[1183]: https://github.com/restify/node-restify/issues/1183\n[1206]: https://github.com/restify/node-restify/issues/1206\n[1286]: https://github.com/restify/node-restify/issues/1286\n[1323]: https://github.com/restify/node-restify/issues/1323\n[1292]: https://github.com/restify/node-restify/issues/1292\n[1065]: https://github.com/restify/node-restify/pull/1065\n[895]: https://github.com/restify/node-restify/issues/895\n[1046]: https://github.com/restify/node-restify/pull/1046\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2011 Mark Cavage, All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE\n"
  },
  {
    "path": "Makefile",
    "content": "#\n# Copyright (c) 2012, Joyent, Inc. All rights reserved.\n#\n# Makefile: basic Makefile for template API service\n#\n# This Makefile is a template for new repos. It contains only repo-specific\n# logic and uses included makefiles to supply common targets (javascriptlint,\n# jsstyle, restdown, etc.), which are used by other repos as well. You may well\n# need to rewrite most of this file, but you shouldn't need to touch the\n# included makefiles.\n#\n# If you find yourself adding support for new targets that could be useful for\n# other projects too, you should add these to the original versions of the\n# included Makefiles (in eng.git) so that other teams can use them too.\n#\n\n#\n# Tools\n#\nESLINT\t\t:= ./node_modules/.bin/eslint\nDOCUMENTATION\t\t:= ./node_modules/.bin/documentation\nNODEUNIT\t:= ./node_modules/.bin/nodeunit\nMOCHA\t\t:= ./node_modules/.bin/mocha\nNODECOVER\t:= ./node_modules/.bin/nyc\nDOCS_BUILD\t:= ./tools/docsBuild.js\nNPM\t\t:= npm\nNODE\t\t:= node\nPRETTIER\t\t:= ./node_modules/.bin/prettier\n\n#\n# Files\n#\nJS_FILES\t = '.'\n\nCLEAN_FILES\t+= node_modules cscope.files\n\ninclude ./tools/mk/Makefile.defs\n\n#\n# Repo-specific targets\n#\n.PHONY: all\nall: $(NODEUNIT) $(REPO_DEPS)\n\t$(NPM) rebuild\n\n$(NODEUNIT): | $(NPM_EXEC)\n\t$(NPM) install\n\n$(NODECOVER): | $(NPM_EXEC)\n\t$(NPM) install\n\n.PHONY: cover\ncover: $(NODECOVER)\n\t@rm -fr ./.coverage_data\n\t$(NODECOVER) --reporter=html --reporter=text-summary --reporter=lcov $(NODEUNIT) ./test/*.js\n\nCLEAN_FILES += $(TAP) ./node_modules/nodeunit\n\n.PHONY: test\ntest: $(NODEUNIT)\n\t$(NODEUNIT) test/*.test.js\n\t$(MOCHA) --full-trace --no-exit test/plugins/*.test.js\n\n.PHONY: docs-build\ndocs-build:\n\t@($(NODE) $(DOCS_BUILD))\n\n.PHONY: benchmark\nbenchmark:\n\t@(cd ./benchmark && $(NPM) i && $(NODE) index.js)\n\ninclude ./tools/mk/Makefile.deps\ninclude ./tools/mk/Makefile.targ\n"
  },
  {
    "path": "README.md",
    "content": "<!-- Please don't remove this: Grab your social icons from https://github.com/carlsednaoui/gitsocial -->\n\n<!-- display the social media buttons in your README -->\n\n[![alt text][1.1]][1]\n\n\n<!-- links to social media icons -->\n<!-- no need to change these -->\n\n<!-- icons with padding -->\n\n[1.1]: http://i.imgur.com/tXSoThF.png (twitter icon with padding)\n\n<!-- icons without padding -->\n\n[1.2]: http://i.imgur.com/wWzX9uB.png (twitter icon without padding)\n\n\n<!-- links to your social media accounts -->\n<!-- update these accordingly -->\n\n[1]: http://www.twitter.com/restifyjs\n\n<!-- Please don't remove this: Grab your social icons from https://github.com/carlsednaoui/gitsocial -->\n\n![restify](/../gh-images/logo/png/restify_logo_black_transp_288x288.png?raw=true \"restify\")\n\n[![Build Status](https://travis-ci.org/restify/node-restify.svg?branch=master)](https://travis-ci.org/restify/node-restify)\n[![Dependency Status](https://david-dm.org/restify/node-restify.svg)](https://david-dm.org/restify/node-restify)\n[![devDependency Status](https://david-dm.org/restify/node-restify/dev-status.svg)](https://david-dm.org/restify/node-restify#info=devDependencies)\n[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)\n\n[restify](http://restify.com) is a framework, utilizing\n[connect](https://github.com/senchalabs/connect) style middleware for building\nREST APIs.  For full details, see http://restify.com\n\nFollow restify on [![alt text][1.2]][1]\n\n# Usage\n\n## Server\n```javascript\nvar restify = require('restify');\n\nconst server = restify.createServer({\n  name: 'myapp',\n  version: '1.0.0'\n});\n\nserver.use(restify.plugins.acceptParser(server.acceptable));\nserver.use(restify.plugins.queryParser());\nserver.use(restify.plugins.bodyParser());\n\nserver.get('/echo/:name', function (req, res, next) {\n  res.send(req.params);\n  return next();\n});\n\nserver.listen(8080, function () {\n  console.log('%s listening at %s', server.name, server.url);\n});\n```\n\n## Client\n```javascript\nvar assert = require('assert');\nvar clients = require('restify-clients');\n\nvar client = clients.createJsonClient({\n  url: 'http://localhost:8080',\n  version: '~1.0'\n});\n\nclient.get('/echo/mark', function (err, req, res, obj) {\n  assert.ifError(err);\n  console.log('Server returned: %j', obj);\n});\n```\n\n# Installation\n```bash\n$ npm install restify\n```\n\n## Supported Node Versions\n\nRestify currently works on Node.js v14.x and v16.x.\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2018 restify\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n## Bugs\n\nSee <https://github.com/restify/node-restify/issues>.\n\n## Other repositories\n\n- For the errors module, please go [here](https://github.com/restify/errors).\n\n\n## Mailing list\n\nSee the\n[Google group](https://groups.google.com/forum/?hl=en&fromgroups#!forum/restify)\n.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Reporting a Vulnerability\n\nDo not disclose vulnerabilities in public issues. Please report vulnerabilities to\nsecurity@restify.com with steps to reproduce the vulnerability, and a patch to fix\nit if possible.\n"
  },
  {
    "path": "benchmark/benchmarks/middleware.js",
    "content": "'use strict';\n\nvar restify = process.argv.includes('version=head')\n    ? require('../../lib')\n    : require('restify');\n\nvar server = restify.createServer();\nvar path = '/';\nvar port = 3000;\n\nmodule.exports = {\n    url: 'http://localhost:' + port + path\n};\n\nfunction handler(req, res, next) {\n    next();\n}\n\nfor (var i = 0; i < 10; i++) {\n    server.pre(handler);\n}\n\nfor (var j = 0; j < 10; j++) {\n    server.use(handler);\n}\n\nserver.get(path, function get(req, res) {\n    res.send('hello world');\n});\n\nif (!module.parent) {\n    server.listen(port);\n}\n"
  },
  {
    "path": "benchmark/benchmarks/response-json.js",
    "content": "'use strict';\n\nvar restify = process.argv.includes('version=head')\n    ? require('../../lib')\n    : require('restify');\n\nvar server = restify.createServer();\nvar path = '/';\nvar port = 3000;\n\nmodule.exports = {\n    url: 'http://localhost:' + port + path\n};\n\nserver.get(path, function onRequest(req, res) {\n    res.send({ hello: 'world' });\n});\n\nif (!module.parent) {\n    server.listen(port);\n}\n"
  },
  {
    "path": "benchmark/benchmarks/response-text.js",
    "content": "'use strict';\n\nvar restify = process.argv.includes('version=head')\n    ? require('../../lib')\n    : require('restify');\n\nvar server = restify.createServer();\nvar path = '/';\nvar port = 3000;\n\nmodule.exports = {\n    url: 'http://localhost:' + port + path\n};\n\nserver.get(path, function onRequest(req, res) {\n    res.send('hello world');\n});\n\nif (!module.parent) {\n    server.listen(port);\n}\n"
  },
  {
    "path": "benchmark/benchmarks/router-heavy.js",
    "content": "'use strict';\n\nvar restify = process.argv.includes('version=head')\n    ? require('../../lib')\n    : require('restify');\n\nvar server = restify.createServer();\nvar path = '/whiskeys/scotch/islay/lagavulin/16-years/50';\nvar methods = ['post', 'put', 'get', 'del', 'patch'];\nvar _ = require('lodash');\nvar port = 3000;\n\n// Disabling cache: it's not fair as it aims to the worst case, when\n// cache hit ratio is 0%. However, it's still better than the worst\n// as it doesn't require extra time to maintain the LRU cache.\n// There is no other way to simulate 100+ different endpoint\n// calls with the current benchmark suite.\nif (!process.argv.includes('version=head')) {\n    server.router.cache = {\n        get: function get() {\n            return null;\n        },\n        set: function get() {\n            return null;\n        },\n        dump: function get() {\n            return [];\n        }\n    };\n}\n\nmodule.exports = {\n    url: 'http://localhost:' + port + path\n};\n\nvar routes = {\n    beers: {\n        ale: {\n            'pale-ale': {\n                'american-pale-ale': [],\n                'indian-pale-ale': []\n            },\n            lambic: [],\n            stout: {\n                'american-porter': [],\n                'imperial-stout': [],\n                'irish-stout': []\n            }\n        },\n        lager: {\n            'german-lager': {\n                marzen: []\n            },\n            pilsner: {\n                'german-pilsner': []\n            }\n        }\n    },\n\n    whiskeys: {\n        american: {\n            bourbon: {\n                kentchuky: {\n                    'jim-beam': ['jim-beam', 'bookers', 'old-crow'],\n                    'makers-mark': ['makers-mark'],\n                    'woodford-reserve': ['woodford-reserve']\n                },\n                tennessee: {\n                    'jack-daniels': ['jack-daniels']\n                }\n            },\n            rye: {\n                'beam-suntory': ['jim-beam-rye', 'knob-creek']\n            }\n        },\n        irish: {\n            'single-malt': {\n                bushmills: ['bushmills'],\n                connemare: ['connemare']\n            },\n            'single-pot': {\n                redbreast: ['redbreast'],\n                jameson: ['jameson-15-year']\n            }\n        },\n        japanese: {\n            nikka: ['coffeey-malt', 'blended', 'from-the-barrel'],\n            hibiki: ['japanese-harmony'],\n            yamazakura: ['blended']\n        },\n        scotch: {\n            islay: {\n                bruichladdich: ['25-years', 'islay-barley-2009'],\n                octomore: ['7.2', 'islay-barley-8.3'],\n                laphroaig: ['lore', '15-years', 'four-oak'],\n                lagavulin: ['distillers-edition', '8-years', '16-years']\n            }\n        }\n    }\n};\n\nfunction handler(req, res) {\n    res.send('hello');\n}\n\nfunction attachRoute(parent, routeConfig) {\n    _.map(routeConfig, function map(route, routeKey) {\n        var pathChunk = _.isString(routeKey) ? routeKey : route;\n        var routePath = parent + '/' + pathChunk;\n\n        methods.forEach(function forEach(method) {\n            server[method](routePath, handler);\n        });\n\n        if (_.isObject(route) || _.isArray(route)) {\n            attachRoute(routePath, route);\n        }\n        if (_.isString(route)) {\n            for (var i = 0; i <= 100; i++) {\n                methods.forEach(function forEach(method) {\n                    server[method](routePath + '/' + i, handler);\n                });\n            }\n        }\n    });\n}\n\nattachRoute('', routes);\n\nif (!module.parent) {\n    server.listen(port);\n}\n"
  },
  {
    "path": "benchmark/index.js",
    "content": "#!/usr/bin/env node\n'use strict';\n\nvar inquirer = require('inquirer');\nvar bench = require('./lib/bench');\nvar stableVersion = require('restify/package.json').version;\n\nvar BENCHMARKS = [\n    'response-json',\n    'response-text',\n    'router-heavy',\n    'middleware'\n];\n\nfunction select(callback) {\n    var choices = BENCHMARKS.map(function map(name) {\n        return {\n            name: name,\n            checked: true\n        };\n    });\n\n    choices.unshift(new inquirer.Separator(' = The usual ='));\n\n    inquirer\n        .prompt([\n            {\n                type: 'checkbox',\n                message: 'Select packages',\n                name: 'list',\n                choices: choices,\n                validate: function validate(answer) {\n                    if (answer.length < 1) {\n                        return 'You must choose at least one package.';\n                    }\n                    return true;\n                }\n            }\n        ])\n        .then(function onPrompted(answers) {\n            callback(answers.list);\n        });\n}\n\ninquirer\n    .prompt([\n        {\n            type: 'confirm',\n            name: 'track',\n            message: 'Do you want to track progress?',\n            default: false\n        },\n        {\n            type: 'confirm',\n            name: 'compare',\n            message:\n                'Do you want to compare HEAD with the stable release (' +\n                stableVersion +\n                ')?',\n            default: true\n        },\n        {\n            type: 'confirm',\n            name: 'all',\n            message: 'Do you want to run all benchmark tests?',\n            default: true\n        },\n        {\n            type: 'input',\n            name: 'connection',\n            message: 'How many connections do you need?',\n            default: 100,\n            validate: function validate(value) {\n                return (\n                    !Number.isNaN(parseFloat(value)) || 'Please enter a number'\n                );\n            },\n            filter: Number\n        },\n        {\n            type: 'input',\n            name: 'pipelining',\n            message: 'How many pipelining do you need?',\n            default: 10,\n            validate: function validate(value) {\n                return (\n                    !Number.isNaN(parseFloat(value)) || 'Please enter a number'\n                );\n            },\n            filter: Number\n        },\n        {\n            type: 'input',\n            name: 'duration',\n            message: 'How long does it take?',\n            default: 30,\n            validate: function validate(value) {\n                return (\n                    !Number.isNaN(parseFloat(value)) || 'Please enter a number'\n                );\n            },\n            filter: Number\n        }\n    ])\n    .then(function validate(opts) {\n        if (!opts.all) {\n            select(function onSelected(list) {\n                bench(opts, list);\n            });\n        } else {\n            bench(opts, BENCHMARKS);\n        }\n    });\n"
  },
  {
    "path": "benchmark/lib/autocannon.js",
    "content": "'use strict';\n\nvar autocannon = require('autocannon');\nvar fs = require('fs');\nvar autocannonCompare = require('autocannon-compare');\nvar path = require('path');\n\nvar resultsDirectory = path.join(__dirname, '../results');\n\nfunction writeResult(handler, version, result) {\n    try {\n        fs.accessSync(resultsDirectory);\n    } catch (e) {\n        fs.mkdirSync(resultsDirectory);\n    }\n\n    result.server = handler;\n\n    var dest = path.join(resultsDirectory, handler + '-' + version + '.json');\n    return fs.writeFileSync(dest, JSON.stringify(result, null, 4));\n}\n\nfunction fire(opts, handler, version, save, cb) {\n    opts = opts || {};\n    opts.url = opts.url || 'http://localhost:3000';\n\n    var instance = autocannon(opts, function onResult(err, result) {\n        if (err) {\n            cb(err);\n            return;\n        }\n\n        if (save) {\n            writeResult(handler, version, result);\n        }\n\n        cb();\n    });\n\n    if (opts.track && save) {\n        autocannon.track(instance);\n    }\n}\n\nfunction compare(handler) {\n    var resStable = require(resultsDirectory + '/' + handler + '-stable.json');\n    var resHead = require(resultsDirectory + '/' + handler + '-head.json');\n    var comp = autocannonCompare(resStable, resHead);\n    var result = {\n        throughput: {\n            significant: comp.throughput.significant\n        }\n    };\n\n    if (comp.equal) {\n        result.throughput.equal = true;\n    } else if (comp.aWins) {\n        result.throughput.equal = false;\n        result.throughput.wins = 'stable';\n        result.throughput.diff = comp.throughput.difference;\n    } else {\n        result.throughput.equal = false;\n        result.throughput.wins = 'head';\n        result.throughput.diff = autocannonCompare(\n            resHead,\n            resStable\n        ).throughput.difference;\n    }\n\n    return result;\n}\n\nmodule.exports = {\n    fire: fire,\n    compare: compare\n};\n"
  },
  {
    "path": "benchmark/lib/bench.js",
    "content": "#!/usr/bin/env node\n'use strict';\n\nvar fork = require('child_process').fork;\nvar ora = require('ora');\nvar path = require('path');\nvar autocannon = require('./autocannon');\nvar pipeline = require('vasync').pipeline;\n\nfunction runBenchmark(opts, handler, version, cb) {\n    if (opts.track) {\n        console.log(version.toUpperCase() + ':');\n    }\n\n    var spinner = ora('Started ' + version + '/' + handler).start();\n    var modulePath = path.join(__dirname, '../benchmarks', handler);\n    var url = require(modulePath).url;\n    var forked = fork(modulePath, ['version=' + version]);\n\n    pipeline(\n        {\n            funcs: [\n                function warm(_, callback) {\n                    spinner.color = 'magenta';\n                    spinner.text =\n                        'Warming ' + version + '/' + handler + ' for 5s';\n\n                    var fireOpts = Object.assign({}, opts, {\n                        duration: 5,\n                        url: url\n                    });\n                    autocannon.fire(\n                        fireOpts,\n                        handler,\n                        version,\n                        false,\n                        callback\n                    );\n                },\n\n                function benchmark(_, callback) {\n                    if (opts.track) {\n                        spinner.stop();\n                    } else {\n                        spinner.color = 'yellow';\n                        spinner.text =\n                            'Benchmarking ' +\n                            version +\n                            '/' +\n                            handler +\n                            ' for ' +\n                            opts.duration +\n                            's';\n                    }\n\n                    var fireOpts = Object.assign({}, opts, { url: url });\n                    autocannon.fire(fireOpts, handler, version, true, callback);\n                }\n            ]\n        },\n        function onPipelineFinished(err) {\n            forked.kill('SIGINT');\n\n            if (err) {\n                spinner.fail();\n                cb(err);\n                return;\n            }\n\n            spinner.text = 'Results saved for ' + version + '/' + handler;\n            spinner.succeed();\n\n            cb();\n        }\n    );\n}\n\nfunction start(opts, list, index) {\n    index = index || 0;\n\n    // No more item\n    if (list.length === index) {\n        return;\n    }\n\n    var handler = list[index];\n    console.log('---- ' + handler + ' ----');\n\n    pipeline(\n        {\n            funcs: [\n                function head(_, callback) {\n                    runBenchmark(opts, handler, 'head', callback);\n                },\n                function stable(_, callback) {\n                    if (!opts.compare) {\n                        callback();\n                        return;\n                    }\n                    runBenchmark(opts, handler, 'stable', callback);\n                }\n            ]\n        },\n        function onPipelineFinished(err) {\n            if (err) {\n                console.log(err);\n                return;\n            }\n\n            // Compare versions\n            if (opts.compare) {\n                var result = autocannon.compare(handler);\n\n                console.log(handler + ' throughput:');\n                console.log(JSON.stringify(result.throughput, null, 4) + '\\n');\n            }\n\n            // Benchmark next handler\n            start(opts, list, ++index);\n        }\n    );\n}\n\nmodule.exports = start;\n"
  },
  {
    "path": "benchmark/package.json",
    "content": "{\n  \"name\": \"restify-benchmark\",\n  \"homepage\": \"http://restifyjs.com\",\n  \"description\": \"Restify benchmark\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"main\": \"index.js\",\n  \"engines\": {\n    \"node\": \">=0.10\"\n  },\n  \"dependencies\": {\n    \"restify\": \"latest\"\n  },\n  \"devDependencies\": {},\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"start\": \"node indec\"\n  }\n}\n"
  },
  {
    "path": "bin/report-latency",
    "content": "#!/usr/bin/env node\n// -*- mode: js -*-\n\nvar fs = require('fs');\nvar path = require('path');\nvar spawn = require('child_process').spawn;\nvar readline = require('readline');\nvar sprintf = require('util').format;\n\nvar nopt = require('nopt');\n\n\n///--- Globals\n\nvar BUCKETS = {};\nvar REQUEST_IDS = {};\n\nvar OPTS = {\n    'average': Boolean,\n    'help': Boolean,\n    'end': Date,\n    'max-latency': Number,\n    'max-requests': Number,\n    'output': String,\n    'percentile': [Number, Array],\n    'period': Number,\n    'requests': Boolean,\n    'start': Date\n};\n\nvar SHORT_OPTS = {\n    'a': ['--average'],\n    'h': ['--help'],\n    'i': ['--period'],\n    'e': ['--end'],\n    'l': ['--max-latency'],\n    'n': ['--max-requests'],\n    'o': ['--output'],\n    'p': ['--percentile'],\n    'r': ['--requests'],\n    's': ['--start']\n};\n\n\n///--- Functions\n\nfunction percentile(p, vals) {\n    p = parseInt(p, 10);\n    return vals[(Math.round(((p / 100) * vals.length) + 1 / 2) - 1)].latency;\n}\n\n\nfunction report(buckets, output) {\n    Object.keys(buckets).sort(function (a, b) {\n        return parseInt(a, 10) - parseInt(b, 10);\n    }).forEach(function (k) {\n            var avg = 0;\n            var perc = [];\n            var req = buckets[k].length;\n            var sum = 0;\n            var t = Math.round(buckets[k]._time);\n\n            buckets[k] = buckets[k].sort(function (a, b) {\n                return a.latency - b.latency;\n            });\n\n            buckets[k].forEach(function (v) {\n                sum += v.latency;\n            });\n\n            if (sum > 0 && req > 0) {\n                if (output.average)\n                    output.average.push([t, Math.round(sum / req)]);\n                if (output.requests)\n                    output.requests.push([t, buckets[k].length]);\n                Object.keys(output.percentile).forEach(function (p) {\n                    var _p = percentile(p, buckets[k]);\n                    output.percentile[p].push([t, _p]);\n                });\n            }\n        });\n\n    return output;\n}\n\n\nfunction usage(code, message) {\n    var str = '';\n    Object.keys(SHORT_OPTS).forEach(function (k) {\n        if (!Array.isArray(SHORT_OPTS[k]))\n            return;\n\n        var opt = SHORT_OPTS[k][0].replace('--', '');\n        var type = OPTS[opt].name || 'string';\n        if (type && type === 'boolean')\n            type = '';\n        type = type.toLowerCase();\n\n        str += ' [--' + opt + ' ' + type + ']';\n    });\n    str += ' [file ...]';\n\n    if (message)\n        console.error(message);\n\n    console.error('usage: ' + path.basename(process.argv[1]) + str);\n    process.exit(code);\n}\n\n\n///--- Mainline\n\nvar parsed;\n\ntry {\n    parsed = nopt(OPTS, SHORT_OPTS, process.argv, 2);\n} catch (e) {\n    usage(1, e.toString());\n}\n\nif (parsed.help)\n    usage(0);\nif (!parsed.average && !parsed.percentile)\n    usage(1, '--average or --percentile required');\nif (parsed.argv.remain.length < 1)\n    usage(1, 'log file required');\n\nvar config = {\n    average: parsed.average || false,\n    maxLatency: parsed['max-latency'] || 1000,\n    maxRequests: parsed['max-requests'] || 10000,\n    percentile: parsed.percentile || [],\n    period: parsed.period || 60,\n    requests: parsed.requests || false,\n    start: parsed.start ? (parsed.start.getTime() / 1000) : 0,\n    end: parsed.end ? (parsed.end.getTime() / 1000) : Number.MAX_VALUE\n};\n\nvar buckets = {};\n\nvar done = 0;\nparsed.argv.remain.forEach(function (f) {\n    var stream = readline.createInterface({\n        input: fs.createReadStream(f),\n        output: null\n    })\n    stream.on('line', function (l) {\n        var record;\n        var t = -1;\n\n        try {\n            record = JSON.parse(l);\n        } catch (e) {\n        }\n\n        if (!record)\n            return;\n\n        var t = -1;\n        if (record.time)\n            t = (new Date(record.time).getTime() / 1000);\n\n        if (record._audit !== true ||\n            REQUEST_IDS[record.req_id] ||\n            t < config.start ||\n            t > config.end) {\n\n            console.error('Skipping %s', l);\n        }\n\n        REQUEST_IDS[record.req_id] = true;\n        record.time = t;\n\n        var b = Math.round(record.time / config.period) + '';\n        if (!buckets[b])\n            buckets[b] = [];\n\n        buckets[b].push(record);\n        buckets[b]._time = record.time // good enough\n    });\n\n    stream.on('end', function () {\n        if (++done === parsed.argv.remain.length) {\n            console.error('Generating report...');\n\n            var output = {\n                average: config.average ? [] : false,\n                requests: config.requests ? [] : false,\n                percentile: {}\n            };\n            config.percentile.forEach(function (p) {\n                output.percentile[p] = [];\n            });\n\n            output = report(buckets, output);\n            var finalOutput = [];\n            if (output.average) {\n                finalOutput.push({\n                    name: 'avg',\n                    values: output.average\n                });\n            }\n            if (output.requests) {\n                finalOutput.push({\n                    name: 'n',\n                    values: output.requests\n                });\n            }\n            Object.keys(output.percentile).forEach(function (k) {\n                finalOutput.push({\n                    name: 'p' + k,\n                    values: output.percentile[k]\n                });\n            });\n\n            console.log(JSON.stringify(finalOutput));\n        }\n    });\n});\n"
  },
  {
    "path": "docs/_api/formatters.md",
    "content": "---\ntitle: Formatters API\npermalink: /docs/formatters-api/\n---\n\n<!-- Generated by documentation.js. Update this documentation by updating the source code. -->\n\n### Table of Contents\n\n-   [Usage][1]\n-   [Types][2]\n    -   [formatter][3]\n-   [Included formatters][4]\n    -   [formatText][5]\n    -   [formatJSON][6]\n    -   [formatJSONP][7]\n    -   [formatBinary][8]\n\n## Usage\n\nRestify comes bundled with a selection of useful formatters that prepare your\nresponses for being sent over the wire, but you are free to include your own!\n\n```js\nfunction formatGraphQL(req, res, body) {\n    var data = body;\n    /* Do a thing to data */\n    res.setHeader('Content-Length', Buffer.byteLength(data));\n    return data;\n}\n\nvar server = restify.createServer({\n    formatters: {\n        'application/graphql': formatGraphQL\n    }\n});\n\n// Your application now supports content-type 'application/graphql'\n```\n\n\n## Types\n\n\n\n\n### formatter\n\nFormat a response for being sent over the wire\n\nType: [Function][9]\n\n**Parameters**\n\n-   `req` **[Object][10]** the request object (not used)\n-   `res` **[Object][10]** the response object\n-   `body` **[Object][10]** response body to format\n\nReturns **[String][11]** formatted response data\n\n## Included formatters\n\nrestify comes pre-loaded with a standard set of formatters for common\nuse cases.\n\n\n### formatText\n\nFormats the body to 'text' by invoking a toString() on the body if it\nexists. If it doesn't, then the response is a zero-length string.\n\n**Parameters**\n\n-   `req` **[Object][10]** the request object (not used)\n-   `res` **[Object][10]** the response object\n-   `body` **[Object][10]** response body. If it has a toString() method this\n                              will be used to make the string representation\n\nReturns **[String][11]** data\n\n### formatJSON\n\nJSON formatter. Will look for a toJson() method on the body. If one does not\nexist then a JSON.stringify will be attempted.\n\n**Parameters**\n\n-   `req` **[Object][10]** the request object (not used)\n-   `res` **[Object][10]** the response object\n-   `body` **[Object][10]** response body\n\nReturns **[String][11]** data\n\n### formatJSONP\n\nJSONP formatter. like JSON, but with a callback invocation.\nUnicode escapes line and paragraph separators.\n\n**Parameters**\n\n-   `req` **[Object][10]** the request object\n-   `res` **[Object][10]** the response object\n-   `body` **[Object][10]** response body\n\nReturns **[String][11]** data\n\n### formatBinary\n\nBinary formatter.\n\n**Parameters**\n\n-   `req` **[Object][10]** the request object\n-   `res` **[Object][10]** the response object\n-   `body` **[Object][10]** response body\n\nReturns **[Buffer][12]** body\n\n[1]: #usage\n\n[2]: #types\n\n[3]: #formatter\n\n[4]: #included-formatters\n\n[5]: #formattext\n\n[6]: #formatjson\n\n[7]: #formatjsonp\n\n[8]: #formatbinary\n\n[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function\n\n[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object\n\n[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String\n\n[12]: https://nodejs.org/api/buffer.html\n"
  },
  {
    "path": "docs/_api/plugins.md",
    "content": "---\ntitle: Plugins API\npermalink: /docs/plugins-api/\n---\n\n<!-- Generated by documentation.js. Update this documentation by updating the source code. -->\n\n### Table of Contents\n\n-   [Usage][1]\n-   [server.pre() plugins][2]\n    -   [context][3]\n    -   [dedupeSlashes][4]\n    -   [pause][5]\n    -   [sanitizePath][6]\n    -   [reqIdHeaders][7]\n    -   [strictQueryParams][8]\n    -   [userAgentConnection][9]\n-   [server.use() plugins][10]\n    -   [acceptParser][11]\n    -   [authorizationParser][12]\n    -   [dateParser][13]\n    -   [queryParser][14]\n    -   [jsonp][15]\n    -   [bodyParser][16]\n    -   [requestLogger][17]\n    -   [gzipResponse][18]\n    -   [serveStatic][19]\n    -   [serveStaticFiles][20]\n    -   [throttle][21]\n    -   [requestExpiry][22]\n        -   [Using an external storage mechanism for key/bucket mappings.][23]\n    -   [inflightRequestThrottle][24]\n    -   [cpuUsageThrottle][25]\n    -   [conditionalHandler][26]\n    -   [conditionalRequest][27]\n    -   [auditLogger][28]\n    -   [metrics][29]\n-   [Types][30]\n    -   [metrics~callback][31]\n-   [req.set][32]\n-   [req.get][33]\n-   [req.getAll][34]\n\n## Usage\n\nRestify comes bundled with a selection of useful plugins. These are accessible\noff of `restify.plugins` and `restify.pre`.\n\n```js\nvar server = restify.createServer();\nserver.use(restify.plugins.acceptParser(server.acceptable));\nserver.use(restify.plugins.authorizationParser());\nserver.use(restify.plugins.dateParser());\nserver.use(restify.plugins.queryParser());\nserver.use(restify.plugins.jsonp());\nserver.use(restify.plugins.gzipResponse());\nserver.use(restify.plugins.bodyParser());\nserver.use(restify.plugins.requestExpiry());\nserver.use(restify.plugins.throttle({\n  burst: 100,\n  rate: 50,\n  ip: true,\n  overrides: {\n    '192.168.1.1': {\n      rate: 0,        // unlimited\n      burst: 0\n    }\n  }\n}));\nserver.use(restify.plugins.conditionalRequest());\n```\n\n\n## server.pre() plugins\n\nThis module includes various pre plugins, which are intended to be used prior\nto routing of the URL. To use a plugin before routing, use the `server.pre()`\nmethod.\n\n\n### context\n\nThis plugin creates `req.set(key, val)` and `req.get(key)` methods for\nsetting and retrieving request specific data.\n\n**Examples**\n\n```javascript\nserver.pre(restify.plugins.pre.context());\nserver.get('/', [\n    function(req, res, next) {\n        req.set(myMessage, 'hello world');\n        return next();\n    },\n    function two(req, res, next) {\n        res.send(req.get(myMessage)); // => sends 'hello world'\n        return next();\n    }\n]);\n```\n\nReturns **[Function][35]** Handler\n\n### dedupeSlashes\n\nThis plugin deduplicates extra slashes found in the URL. This can help with\nmalformed URLs that might otherwise get misrouted.\n\n**Examples**\n\n```javascript\nserver.pre(restify.plugins.pre.dedupeSlashes());\nserver.get('/hello/:one', function(req, res, next) {\n    res.send(200);\n    return next();\n});\n\n// the server will now convert requests to /hello//jake => /hello/jake\n```\n\nReturns **[Function][35]** Handler\n\n### pause\n\nThis pre handler fixes issues with node hanging when an `asyncHandler` is\nused prior to `bodyParser`.\n[https://github.com/restify/node-restify/issues/287][36]\n[https://github.com/restify/node-restify/issues/409][37]\n[https://github.com/restify/node-restify/wiki/1.4-to-2.0-Migration-Tips][38]\n\nReturns **[Function][35]** Handler\n\n### sanitizePath\n\nCleans up sloppy URLs on the request object,\nlike `/foo////bar///` to `/foo/bar`.\n\nReturns **[Function][35]** Handler\n\n### reqIdHeaders\n\nThis plugin pulls the value from an incoming request header and uses it\nas the value of the request id. Subsequent calls to `req.id()`\nwill return the header values.\n\n**Parameters**\n\n-   `opts` **[Object][39]** an options object\n    -   `opts.headers` **[Array][40]&lt;[String][41]>** array of headers from where to pull existing\n                                       request id headers. Lookup precedence\n                                       is left to right (lowest index first)\n\nReturns **[Function][35]** Handler\n\n### strictQueryParams\n\nPrevents `req.urls` non-strict key-value query params\n\nThe Request-URI is transmitted in the format specified in section 3.2.1.\nIf the Request-URI is encoded using the \"% HEX HEX\" encoding [42],\nthe origin server MUST decode the Request-URI\nin order to properly interpret the request.\nServers SHOULD respond to invalid Request-URIs\nwith an appropriate status code.\n\npart of Hypertext Transfer Protocol -- HTTP/1.1 | 5.1.2 Request-URI\nRFC 2616 Fielding, et al.\n\n**Parameters**\n\n-   `options` **[Object][39]?** an options object\n    -   `options.message` **[String][41]?** a custom error message\n                                     default value:\n                                     \"Url query params does not meet strict format\"\n\nReturns **[Function][35]** Handler\n\n### userAgentConnection\n\nThis basically exists for `curl`. `curl` on `HEAD` requests usually\njust sits there and hangs, unless you explicitly set\nConnection:close. And in general, you probably want to set\nConnection: close to curl anyway.\n\nAlso, because curl spits out an annoying message to stderr about\nremaining bytes if content-length is set, this plugin also drops\nthe `content-length` header (some user agents handle it and want it,\ncurl does not).\n\nTo be slightly more generic, the options block takes a user\nagent regexp, however.\n\n**Parameters**\n\n-   `options` **[Object][39]?** an options object\n    -   `options.userAgentRegExp` **[RegExp][42]** matching any\n                                                               user-agents applicable (optional, default `/^curl.+/`)\n\nReturns **[Function][35]** Handler\n\n## server.use() plugins\n\n\n\n\n### acceptParser\n\nParses the `Accept` header, and ensures that the server can respond to what\nthe client asked for. In almost all cases passing in `server.acceptable` is\nall that's required, as that's an array of content types the server knows\nhow to respond to (with the formatters you've registered). If the request is\nfor a non-handled type, this plugin will return a `NotAcceptableError` (406).\n\nNote you can get the set of types allowed from a restify server by doing\n`server.acceptable`.\n\n**Parameters**\n\n-   `accepts` **[Array][40]&lt;[String][41]>** array of accept types.\n\n**Examples**\n\n```javascript\nserver.use(restify.plugins.acceptParser(server.acceptable));\n```\n\n-   Throws **NotAcceptableError** \n\nReturns **[Function][35]** restify handler.\n\n### authorizationParser\n\nParses out the `Authorization` header as best restify can.\nCurrently only HTTP Basic Auth and\n[HTTP Signature][43]\nschemes are supported.\n\n**Parameters**\n\n-   `options` **[Object][39]?** an optional options object that is\n                                   passed to http-signature\n\n**Examples**\n\nSubsequent handlers will see `req.authorization`, which looks like above.\n\n`req.username` will also be set, and defaults to 'anonymous'.  If the scheme\nis unrecognized, the only thing available in `req.authorization` will be\n`scheme` and `credentials` - it will be up to you to parse out the rest.\n\n\n```javascript\n{\n  scheme: \"<Basic|Signature|...>\",\n  credentials: \"<Undecoded value of header>\",\n  basic: {\n    username: $user\n    password: $password\n  }\n}\n```\n\n-   Throws **InvalidArgumentError** \n\nReturns **[Function][35]** Handler\n\n### dateParser\n\nParses out the HTTP Date header (if present) and checks for clock skew.\nIf the header is invalid, a `InvalidHeaderError` (`400`) is returned.\nIf the clock skew exceeds the specified value,\na `RequestExpiredError` (`400`) is returned.\nWhere expired means the request originated at a time\nbefore (`$now - $clockSkew`).\nThe default clockSkew allowance is 5m (thanks\nKerberos!)\n\n**Parameters**\n\n-   `clockSkew` **[Number][44]** allowed clock skew in seconds. (optional, default `300`)\n\n**Examples**\n\n```javascript\n// Allows clock skew of 1m\nserver.use(restify.plugins.dateParser(60));\n```\n\n-   Throws **RequestExpiredError** \n-   Throws **InvalidHeaderError** \n\nReturns **[Function][35]** restify handler.\n\n### queryParser\n\nParses the HTTP query string (i.e., `/foo?id=bar&name=mark`).\nIf you use this, the parsed content will always be available in `req.query`,\nadditionally params are merged into `req.params`.\nYou can disable by passing in `mapParams: false` in the options object.\n\nMany options correspond directly to option defined for the underlying\n[`qs.parse`][45].\n\n**Parameters**\n\n-   `options` **[Object][39]?** an options object\n    -   `options.mapParams` **[Object][39]** disable passing (optional, default `true`)\n    -   `options.mapParams` **[Boolean][46]** Copies parsed query parameters\n        into`req.params`. (optional, default `false`)\n    -   `options.overrideParams` **[Boolean][46]** Only applies when if\n        mapParams true.\n        When true, will stomp on req.params field when existing value is found. (optional, default `false`)\n    -   `options.allowDots` **[Boolean][46]** Transform `?foo.bar=baz` to a\n        nested object: `{foo: {bar: 'baz'}}`. (optional, default `false`)\n    -   `options.arrayLimit` **[Number][44]** Only transform `?a[$index]=b`\n        to an array if `$index` is less than `arrayLimit`. (optional, default `20`)\n    -   `options.depth` **[Number][44]** The depth limit for parsing\n        nested objects, e.g. `?a[b][c][d][e][f][g][h][i]=j`. (optional, default `5`)\n    -   `options.parameterLimit` **[Number][44]** Maximum number of query\n        params parsed. Additional params are silently dropped. (optional, default `1000`)\n    -   `options.parseArrays` **[Boolean][46]** Whether to parse\n        `?a[]=b&a[1]=c` to an array, e.g. `{a: ['b', 'c']}`. (optional, default `true`)\n    -   `options.plainObjects` **[Boolean][46]** Whether `req.query` is a\n        \"plain\" object -- does not inherit from `Object`.\n        This can be used to allow query params whose names collide with Object\n        methods, e.g. `?hasOwnProperty=blah`. (optional, default `false`)\n    -   `options.strictNullHandling` **[Boolean][46]** If true, `?a&b=`\n        results in `{a: null, b: ''}`. Otherwise, `{a: '', b: ''}`. (optional, default `false`)\n\n**Examples**\n\n```javascript\nserver.use(restify.plugins.queryParser({ mapParams: false }));\n```\n\nReturns **[Function][35]** Handler\n\n### jsonp\n\nParses the jsonp callback out of the request.\nSupports checking the query string for `callback` or `jsonp` and ensuring\nthat the content-type is appropriately set if JSONP params are in place.\nThere is also a default `application/javascript` formatter to handle this.\n\nYou _should_ set the `queryParser` plugin to run before this, but if you\ndon't this plugin will still parse the query string properly.\n\n**Examples**\n\n```javascript\nvar server = restify.createServer();\nserver.use(restify.plugins.jsonp());\n```\n\nReturns **[Function][35]** Handler\n\n### bodyParser\n\nBlocks your chain on reading and parsing the HTTP request body.  Switches on\n`Content-Type` and does the appropriate logic.  `application/json`,\n`application/x-www-form-urlencoded` and `multipart/form-data` are currently\nsupported.\n\nParses `POST` bodies to `req.body`. automatically uses one of the following\nparsers based on content type:\n\n-   `urlEncodedBodyParser(options)` - parses url encoded form bodies\n-   `jsonBodyParser(options)` - parses JSON POST bodies\n-   `multipartBodyParser(options)` - parses multipart form bodies\n\nAll bodyParsers support the following options:\n\n-   `options.mapParams` - default false. copies parsed post body values onto\n    req.params\n-   `options.overrideParams` - default false. only applies when if\n    mapParams true. when true, will stomp on req.params value when\n    existing value is found.\n\n**Parameters**\n\n-   `options` **[Object][39]?** an option object\n    -   `options.maxBodySize` **[Number][44]?** The maximum size in bytes allowed in\n        the HTTP body. Useful for limiting clients from hogging server memory.\n    -   `options.mapParams` **[Boolean][46]?** if `req.params` should be filled with\n        parsed parameters from HTTP body.\n    -   `options.mapFiles` **[Boolean][46]?** if `req.params` should be filled with\n        the contents of files sent through a multipart request.\n        [formidable][47] is used internally\n        for parsing, and a file is denoted as a multipart part with the `filename`\n        option set in its `Content-Disposition`. This will only be performed if\n        `mapParams` is true.\n    -   `options.overrideParams` **[Boolean][46]?** if an entry in `req.params`\n        should be overwritten by the value in the body if the names are the same.\n        For instance, if you have the route `/:someval`,\n        and someone posts an `x-www-form-urlencoded`\n        Content-Type with the body `someval=happy` to `/sad`, the value will be\n        `happy` if `overrideParams` is `true`, `sad` otherwise.\n    -   `options.multipartHandler` **[Function][35]?** a callback to handle any\n        multipart part which is not a file.\n        If this is omitted, the default handler is invoked which may\n        or may not map the parts into `req.params`, depending on\n        the `mapParams`-option.\n    -   `options.multipartFileHandler` **[Function][35]?** a callback to handle any\n        multipart file.\n        It will be a file if the part has a `Content-Disposition` with the\n        `filename` parameter set. This typically happens when a browser sends a\n        form and there is a parameter similar to `<input type=\"file\" />`.\n        If this is not provided, the default behaviour is to map the contents\n        into `req.params`.\n    -   `options.keepExtensions` **[Boolean][46]?** if you want the uploaded\n        files to include the extensions of the original files\n        (multipart uploads only).\n        Does nothing if `multipartFileHandler` is defined.\n    -   `options.uploadDir` **[String][41]?** Where uploaded files are\n        intermediately stored during transfer before the contents is mapped\n        into `req.params`.\n        Does nothing if `multipartFileHandler` is defined.\n    -   `options.multiples` **[Boolean][46]?** if you want to support html5 multiple\n        attribute in upload fields.\n    -   `options.hash` **[String][41]?** If you want checksums calculated for\n        incoming files, set this to either `sha1` or `md5`.\n    -   `options.rejectUnknown` **[Boolean][46]?** Set to `true` if you want to end\n        the request with a `UnsupportedMediaTypeError` when none of\n        the supported content types was given.\n    -   `options.requestBodyOnGet` **[Boolean][46]**  Parse body of a GET\n        request. (optional, default `false`)\n    -   `options.reviver` **[Function][35]?** `jsonParser` only. If a function,\n        this prescribes how the value originally produced by parsing is transformed,\n        before being returned. For more information check out\n        `JSON.parse(text[, reviver])`.\n    -   `options.maxFieldsSize` **[Number][44]** `multipartParser`\n        only.\n        Limits the amount of memory all fields together (except files)\n        can allocate in bytes.\n        The default size is `2 * 1024 * 1024` bytes _(2MB)_. (optional, default `2*1024*1024`)\n\n**Examples**\n\n```javascript\nserver.use(restify.plugins.bodyParser({\n    maxBodySize: 0,\n    mapParams: true,\n    mapFiles: false,\n    overrideParams: false,\n    multipartHandler: function(part) {\n        part.on('data', function(data) {\n          // do something with the multipart data\n        });\n    },\n   multipartFileHandler: function(part) {\n        part.on('data', function(data) {\n          // do something with the multipart file data\n        });\n    },\n    keepExtensions: false,\n    uploadDir: os.tmpdir(),\n    multiples: true,\n    hash: 'sha1',\n    rejectUnknown: true,\n    requestBodyOnGet: false,\n    reviver: undefined,\n    maxFieldsSize: 2 * 1024 * 1024\n }));\n```\n\n-   Throws **UnsupportedMediaTypeError** \n\nReturns **[Function][35]** Handler\n\n### requestLogger\n\nSets up a child [bunyan][48] logger with\nthe current request id filled in, along with any other parameters you define.\n\nYou can pass in no options to this, in which case only the request id will be\nappended, and no serializers appended (this is also the most performant); the\nlogger created at server creation time will be used as the parent logger.\nThis logger can be used normally, with [req.log][49].\n\nThis plugin does _not_ log each individual request. Use the Audit Logging\nplugin or a custom middleware for that use.\n\n**Parameters**\n\n-   `options` **[Object][39]?** an options object\n    -   `options.headers` **[Array][40]?** A list of headers to transfer from\n                                         the request to top level props on the log.\n\n**Examples**\n\n```javascript\nserver.use(restify.plugins.requestLogger({\n    properties: {\n        foo: 'bar'\n    },\n    serializers: {...}\n}));\n```\n\nReturns **[Function][35]** Handler\n\n### gzipResponse\n\nIf the client sends an `accept-encoding: gzip` header (or one with an\nappropriate q-val), then the server will automatically gzip all\nresponse data.\nNote that only `gzip` is supported, as this is most widely supported by\nclients in the wild.\nThis plugin will overwrite some of the internal streams, so any\ncalls to `res.send`, `res.write`, etc., will be compressed.  A side effect is\nthat the `content-length` header cannot be known, and so\n`transfer-encoding: chunked` will _always_ be set when this is in effect.\nThis plugin has no impact if the client does not send\n`accept-encoding: gzip`.\n\n[https://github.com/restify/node-restify/issues/284][50]\n\n**Parameters**\n\n-   `opts` **[Object][39]?** an options object, see: zlib.createGzip\n\n**Examples**\n\n```javascript\nserver.use(restify.plugins.gzipResponse());\n```\n\nReturns **[Function][35]** Handler\n\n### serveStatic\n\nServes static files.\n\n**Parameters**\n\n-   `options` **[Object][39]** an options object\n\n**Examples**\n\nThe serveStatic module is different than most of the other plugins, in that\nit is expected that you are going to map it to a route, as below:\n\n\n```javascript\nserver.get('/docs/current/*', restify.plugins.serveStatic({\n  directory: './documentation/v1',\n  default: 'index.html'\n}));\n```\n\nThe above `route` and `directory` combination will serve a file located in\n`./documentation/v1/docs/current/index.html` when you attempt to hit\n`http://localhost:8080/docs/current/`. If you want the serveStatic module to\nserve files directly from the `/documentation/v1` directory\n(and not append the request path `/docs/current/`),\nyou can set the `appendRequestPath` option to `false`, and the served file\nwould be `./documentation/v1/index.html`, in the previous example.\n\nThe plugin will enforce that all files under `directory` are served.\nThe `directory` served is relative to the process working directory.\nYou can also provide a `default` parameter such as index.html for any\ndirectory that lacks a direct file match.\nYou can specify additional restrictions by passing in a `match` parameter,\nwhich is just a `RegExp` to check against the requested file name.\nAdditionally, you may set the `charSet` parameter, which will append a\ncharacter set to the content-type detected by the plugin.\nFor example, `charSet: 'utf-8'` will result in HTML being served with a\n`Content-Type` of `text/html; charset=utf-8`.\nLastly, you can pass in a `maxAge` numeric, which will set the\n`Cache-Control` header. Default is `3600` (1 hour).\n\nAn additional option for serving a static file is to pass `file` in to the\nserveStatic method as an option. The following will serve index.html from\nthe documentation/v1/ directory anytime a client requests `/home/`.\n\n\n```javascript\nserver.get('/home/*', restify.plugins.serveStatic({\n  directory: './documentation/v1',\n  file: 'index.html'\n}));\n// or\nserver.get('/home/([a-z]+[.]html)', restify.plugins.serveStatic({\n  directory: './documentation/v1',\n  file: 'index.html'\n}));\n```\n\n-   Throws **MethodNotAllowedError** \\|\n-   Throws **NotAuthorizedError** \n-   Throws **ResourceNotFoundError** \n\nReturns **[Function][35]** Handler\n\n### serveStaticFiles\n\nServes static files, with API similar to expressjs\n\n**Parameters**\n\n-   `directory` **[String][41]** the directory to serve files from\n-   `opts` **[Object][39]** an options object, which is optional\n    -   `opts.maxAge` **[Number][44]** specify max age in millisecs (optional, default `0`)\n    -   `opts.etag` **[Boolean][46]** enable/disable etag, default = true (optional, default `true`)\n    -   `opts.setHeaders` **[Function][35]?** set custom headers for the Files\n        (synchronously), The function is called as `fn(res, path, stat)`,\n        where the arguments are:\n             `res` the response object\n             `path` the file path that is being sent\n             `stat` the stat object of the file that is being sent\n\n**Examples**\n\nThe serveStaticFiles plugin allows you to map a GET route to a\ndirectory on the disk\n\n\n```javascript\nserver.get('/public/*', // don't forget the `/*`\n     restify.plugins.serveStaticFiles('./documentation/v1')\n);\n```\n\nThe GET `route` and `directory` combination will serve a file\nlocated in `./documentation/v1/index.html` when you attempt to hit\n`http://localhost:8080/public/index.html`\n\nThe plugin uses [send][51] under the hood\nwhich is also used by `expressjs` to serve static files. Most of the options\nthat work with `send` will work with this plugin.\n\nThe default file the plugin looks for is `index.html`\n\n\n```javascript\nserver.get('/public/*',\n     restify.plugins.serveStaticFiles('./documentation/v1', {\n     maxAge: 3600000, // this is in millisecs\n     etag: false,\n     setHeaders: function setCustomHeaders(response, requestedPath, stat) {\n             response.setHeader('restify-plugin-x', 'awesome');\n         }\n     })\n);\n```\n\n-   Throws **MethodNotAllowedError** \n-   Throws **NotAuthorizedError** \n-   Throws **ResourceNotFoundError** \n\nReturns **[Function][35]** Handler\n\n### throttle\n\nCreates an API rate limiter that can be plugged into the standard\nrestify request handling pipeline.\n\n`restify` ships with a fairly comprehensive implementation of\n[Token bucket][52], with the ability\nto throttle on IP (or x-forwarded-for) and username (from `req.username`).\nYou define \"global\" request rate and burst rate, and you can define\noverrides for specific keys.\nNote that you can always place this on per-URL routes to enable\ndifferent request rates to different resources (if for example, one route,\nlike `/my/slow/database` is much easier to overwhlem\nthan `/my/fast/memcache`).\n\nIf a client has consumed all of their available rate/burst, an HTTP response\ncode of `429`\n[Too Many Requests][53]\nis returned.\n\nThis throttle gives you three options on which to throttle:\nusername, IP address and 'X-Forwarded-For'. IP/XFF is a /32 match,\nso keep that in mind if using it.  Username takes the user specified\non req.username (which gets automagically set for supported Authorization\ntypes; otherwise set it yourself with a filter that runs before this).\n\nIn both cases, you can set a `burst` and a `rate` (in requests/seconds),\nas an integer/float.  Those really translate to the `TokenBucket`\nalgorithm, so read up on that (or see the comments above...).\n\nIn either case, the top level options burst/rate set a blanket throttling\nrate, and then you can pass in an `overrides` object with rates for\nspecific users/IPs.  You should use overrides sparingly, as we make a new\nTokenBucket to track each.\n\nOn the `options` object ip and username are treated as an XOR.\n\n**Parameters**\n\n-   `options` **[Object][39]** required options with:\n    -   `options.burst` **[Number][44]** burst\n    -   `options.rate` **[Number][44]** rate\n    -   `options.ip` **[Boolean][46]?** ip\n    -   `options.username` **[Boolean][46]?** username\n    -   `options.xff` **[Boolean][46]?** xff\n    -   `options.setHeaders` **[Boolean][46]** Set response headers for rate,\n                                      limit (burst) and remaining. (optional, default `false`)\n    -   `options.overrides` **[Object][39]?** overrides\n    -   `options.tokensTable` **[Object][39]** a storage engine this plugin will\n                                     use to store throttling keys -> bucket mappings.\n                                     If you don't specify this, the default is to\n                                     use an in-memory O(1) LRU, with 10k distinct\n                                     keys.  Any implementation just needs to support\n                                     put/get.\n    -   `options.maxKeys` **[Number][44]** If using the default\n                                     implementation, you can specify how large you\n                                     want the table to be. (optional, default `10000`)\n\n**Examples**\n\nAn example options object with overrides:\n\n\n```javascript\n{\n  burst: 10,  // Max 10 concurrent requests (if tokens)\n  rate: 0.5,  // Steady state: 1 request / 2 seconds\n  ip: true,   // throttle per IP\n  overrides: {\n    '192.168.1.1': {\n      burst: 0,\n      rate: 0    // unlimited\n  }\n}\n```\n\n-   Throws **TooManyRequestsError** \n\nReturns **[Function][35]** Handler\n\n### requestExpiry\n\nRequest Expiry can be used to throttle requests that have already exceeded\ntheir client timeouts. Requests can be sent with a configurable client\ntimeout header, e.g. 'x-request-expiry-time', which gives in absolute ms\nsince epoch, when this request will be timed out by the client.\n\nThis plugin will throttle all incoming requests via a 504 where\n'x-request-expiry-time' less than Date.now() -- since these incoming requests\nhave already been timed out by the client. This prevents the server from\nprocessing unnecessary requests.\n\nRequest expiry will use headers to tell if the incoming request has expired.\nThere are two options for this plugin:\n 1\\. Absolute Time\n    _ Time in Milliseconds since Epoch when this request should be\n    considered expired\n 2\\. Timeout\n    _ The request start time is supplied\n    _ A timeout, in milliseconds, is given\n    _ The timeout is added to the request start time to arrive at the\n      absolute time in which the request is considered expired\n\n#### Using an external storage mechanism for key/bucket mappings.\n\nBy default, the restify throttling plugin uses an in-memory LRU to store\nmappings between throttling keys (i.e., IP address) to the actual bucket that\nkey is consuming.  If this suits you, you can tune the maximum number of keys\nto store in memory with `options.maxKeys`; the default is 10000.\n\nIn some circumstances, you want to offload this into a shared system, such as\nRedis, if you have a fleet of API servers and you're not getting steady\nand/or uniform request distribution.  To enable this, you can pass in\n`options.tokensTable`, which is simply any Object that supports `put` and\n`get` with a `String` key, and an `Object` value.\n\n**Parameters**\n\n-   `opts` **[Object][39]** an options object\n    -   `opts.absoluteHeader` **[String][41]?** The header key to be used for\n                                          the expiry time of each request.\n    -   `opts.startHeader` **[String][41]** The header key for the start time\n                                          of the request.\n    -   `opts.timeoutHeader` **[String][41]** The header key for the time in\n                                          milliseconds that should ellapse before\n                                          the request is considered expired.\n\n**Examples**\n\nThe only option provided is `header` which is the request header used\nto specify the client timeout.\n\n\n```javascript\nserver.use(restify.plugins.requestExpiry({\n    header: 'x-request-expiry-time'\n});\n```\n\nReturns **[Function][35]** Handler\n\n### inflightRequestThrottle\n\nThe `inflightRequestThrottle` module allows you to specify an upper limit to\nthe maximum number of inflight requests your server is able to handle. This\nis a simple heuristic for protecting against event loop contention between\nrequests causing unacceptable latencies.\n\nThe custom error is optional, and allows you to specify your own response\nand status code when rejecting incoming requests due to too many inflight\nrequests. It defaults to `503 ServiceUnavailableError`.\n\nThis plugin should be registered as early as possibly in the middleware stack\nusing `pre` to avoid performing unnecessary work.\n\n**Parameters**\n\n-   `opts` **[Object][39]** configure this plugin\n    -   `opts.limit` **[Number][44]** maximum number of inflight requests the server\n           will handle before returning an error\n    -   `opts.err` **[Error][54]** A restify error used as a response when the\n           inflight request limit is exceeded\n    -   `opts.server` **[Function][35]** the instance of the restify server this\n           plugin will throttle.\n\n**Examples**\n\n```javascript\nvar errors = require('restify-errors');\nvar restify = require('restify');\n\nvar server = restify.createServer();\nconst options = { limit: 600, server: server };\noptions.res = new errors.InternalServerError();\nserver.pre(restify.plugins.inflightRequestThrottle(options));\n```\n\nReturns **[Function][35]** middleware to be registered on server.pre\n\n### cpuUsageThrottle\n\ncpuUsageThrottle is a middleware that rejects a variable number of requests\n(between 0% and 100%) based on a historical view of CPU utilization of a\nNode.js process. Essentially, this plugin allows you to define what\nconstitutes a saturated Node.js process via CPU utilization and it will\nhandle dropping a % of requests based on that definiton. This is useful when\nyou would like to keep CPU bound tasks from piling up causing an increased\nper-request latency.\n\nThe algorithm asks you for a maximum CPU utilization rate, which it uses to\ndetermine at what point it should be rejecting 100% of traffic. For a normal\nNode.js service, this is 1 since Node is single threaded. It uses this,\npaired with a limit that you provide to determine the total % of traffic it\nshould be rejecting. For example, if you specify a limit of .5 and a max of\n1, and the current EWMA (next paragraph) value reads .75, this plugin will\nreject approximately 50% of all requests.\n\nWhen looking at the process' CPU usage, this algorithm will take a load\naverage over a user specified interval. example, if given an interval of\n250ms, this plugin will attempt to record the average CPU utilization over\n250ms intervals. Due to contention for resources, the duration of each\naverage may be wider or narrower than 250ms. To compensate for this, we use\nan exponentially weighted moving average. The EWMA algorithm is provided by\nthe ewma module. The parameter for configuring the EWMA is halfLife. This\nvalue controls how quickly each load average measurment decays to half it's\nvalue when being represented in the current average. For example, if you\nhave an interval of 250, and a halfLife of 250, you will take the previous\newma value multiplied by 0.5 and add it to the new CPU utilization average\nmeasurement multiplied by 0.5. The previous value and the new measurement\nwould each represent 50% of the new value. A good way of thinking about the\nhalfLife is in terms of how responsive this plugin will be to spikes in CPU\nutilization. The higher the halfLife, the longer CPU utilization will have\nto remain above your defined limit before this plugin begins rejecting\nrequests and, converserly, the longer it will have to drop below your limit\nbefore the plugin begins accepting requests again. This is a knob you will\nwant to with play when trying to determine the ideal value for your use\ncase.\n\nFor a better understanding of the EWMA algorithn, refer to the documentation\nfor the ewma module.\n\n**Parameters**\n\n-   `opts` **[Object][39]** Configure this plugin.\n    -   `opts.limit` **[Number][44]?** The point at which restify will begin\n           rejecting a % of all requests at the front door.\n           This value is a percentage.\n           For example 0.8 === 80% average CPU utilization. Defaults to 0.75.\n    -   `opts.max` **[Number][44]?** The point at which restify will reject 100% of\n           all requests at the front door. This is used in conjunction with limit to\n           determine what % of traffic restify needs to reject when attempting to\n           bring the average load back to the user requested values. Since Node.js is\n           single threaded, the default for this is 1. In some rare cases, a Node.js\n           process can exceed 100% CPU usage and you will want to update this value.\n    -   `opts.interval` **[Number][44]?** How frequently we calculate the average CPU\n           utilization. When we calculate an average CPU utilization, we calculate it\n           over this interval, and this drives whether or not we should be shedding\n           load. This can be thought of as a \"resolution\" where the lower this value,\n           the higher the resolution our load average will be and the more frequently\n           we will recalculate the % of traffic we should be shedding. This check\n           is rather lightweight, while the default is 250ms, you should be able to\n           decrease this value without seeing a significant impact to performance.\n    -   `opts.halfLife` **[Number][44]?** When we sample the CPU usage on an\n           interval, we create a series of data points.\n           We take these points and calculate a\n           moving average. The halfLife indicates how quickly a point \"decays\" to\n           half it's value in the moving average. The lower the halfLife, the more\n           impact newer data points have on the average. If you want to be extremely\n           responsive to spikes in CPU usage, set this to a lower value. If you want\n           your process to put more emphasis on recent historical CPU usage when\n           determininng whether it should shed load, set this to a higher value. The\n           unit is in ms. Defaults to 250.\n\n**Examples**\n\n```javascript\nvar restify = require('restify');\n\nvar server = restify.createServer();\nconst options = {\n  limit: .75,\n  max: 1,\n  interval: 250,\n  halfLife: 500,\n}\n\nserver.pre(restify.plugins.cpuUsageThrottle(options));\n```\n\nYou can also update the plugin during runtime using the `.update()` function.\nThis function accepts the same `opts` object as a constructor.\n\n\n```javascript\nvar plugin = restify.plugins.cpuUsageThrottle(options);\nserver.pre(plugin);\n\nplugin.update({ limit: .4, halfLife: 5000 });\n```\n\nReturns **[Function][35]** middleware to be registered on server.pre\n\n### conditionalHandler\n\nRuns first handler that matches to the condition\n\n**Parameters**\n\n-   `candidates` **([Object][39] \\| [Array][40]&lt;[Object][39]>)** candidates\n    -   `candidates.handler` **([Function][35] \\| [Array][40]&lt;[Function][35]>)** handler(s)\n    -   `candidates.version` **([String][41] \\| [Array][40]&lt;[String][41]>)?** '1.1.0', ['1.1.0', '1.2.0']\n    -   `candidates.contentType` **[String][41]?** accepted content type, '\\*\\\\/json'\n\n**Examples**\n\n```javascript\nserver.use(restify.plugins.conditionalHandler({\n   contentType: 'application/json',\n   version: '1.0.0',\n   handler: function (req, res, next) {\n       next();\n   })\n});\n\nserver.get('/hello/:name', restify.plugins.conditionalHandler([\n  {\n     version: '1.0.0',\n     handler: function(req, res, next) { res.send('1.x'); }\n  },\n  {\n     version: ['1.5.0', '2.0.0'],\n     handler: function(req, res, next) { res.send('1.5.x, 2.x'); }\n  },\n  {\n     version: '3.0.0',\n     contentType: ['text/html', 'text/html']\n     handler: function(req, res, next) { res.send('3.x, text'); }\n  },\n  {\n     version: '3.0.0',\n     contentType: 'application/json'\n     handler: function(req, res, next) { res.send('3.x, json'); }\n  },\n  // Array of handlers\n  {\n     version: '4.0.0',\n     handler: [\n         function(req, res, next) { next(); },\n         function(req, res, next) { next(); },\n         function(req, res, next) { res.send('4.x') }\n     ]\n  },\n]);\n// 'accept-version': '^1.1.0' => 1.5.x, 2.x'\n// 'accept-version': '3.x', accept: 'application/json' => '3.x, json'\n```\n\n-   Throws **InvalidVersionError** \n-   Throws **UnsupportedMediaTypeError** \n\nReturns **[Function][35]** Handler\n\n### conditionalRequest\n\nReturns a set of plugins that will compare an already set `ETag` header with\nthe client's `If-Match` and `If-None-Match` header, and an already set\nLast-Modified header with the client's `If-Modified-Since` and\n`If-Unmodified-Since` header.\n\nYou can use this handler to let clients do nice HTTP semantics with the\n\"match\" headers.  Specifically, with this plugin in place, you would set\n`res.etag=$yourhashhere`, and then this plugin will do one of:\n\n-   return `304` (Not Modified) [and stop the handler chain]\n-   return `412` (Precondition Failed) [and stop the handler chain]\n-   Allow the request to go through the handler chain.\n\nThe specific headers this plugin looks at are:\n\n-   `Last-Modified`\n-   `If-Match`\n-   `If-None-Match`\n-   `If-Modified-Since`\n-   `If-Unmodified-Since`\n\n**Examples**\n\n```javascript\nserver.use(restify.plugins.conditionalRequest());\n```\n\n```javascript\nserver.use(function setETag(req, res, next) {\n  res.header('ETag', 'myETag');\n  res.header('Last-Modified', new Date());\n});\n\nserver.use(restify.plugins.conditionalRequest());\n\nserver.get('/hello/:name', function(req, res, next) {\n  res.send('hello ' + req.params.name);\n});\n```\n\n-   Throws **BadRequestError** \n-   Throws **PreconditionFailedError** \n\nReturns **[Array][40]&lt;[Function][35]>** Handlers\n\n### auditLogger\n\n**Parameters**\n\n-   `opts` **[Object][39]** The options object.\n    -   `opts.log` **[Object][39]** The logger.\n    -   `opts.event` **[String][41]** The event from the server which initiates the\n        log, one of 'pre', 'routed', or 'after'\n    -   `opts.context` **[Function][35]?** The optional context function of signature\n        f(req, res, route, err).  Invoked each time an audit log is generated. This\n        function can return an object that customizes the format of anything off the\n        req, res, route, and err objects. The output of this function will be\n        available on the `context` key in the audit object.\n    -   `opts.server` **[Object][39]?** The restify server, used to emit\n        the audit log object programmatically\n    -   `opts.printLog` **[boolean][46]** Whether to print the log\n        via the logger. (optional, default `true`)\n    -   `opts.serializers` **[Object][39]?** Override the default logger serializers\n        for err, req and res\n\n**Examples**\n\nAudit logging is a special plugin, as you don't use it with `.use()`\nbut with the `after` event:\n\n\n```javascript\nserver.on('after', restify.plugins.auditLogger({\n  log: bunyan.createLogger({\n    name: 'audit',\n    stream: process.stdout\n  }),\n  event: 'after',\n  server: SERVER,\n  logMetrics : logBuffer,\n  printLog : true\n}));\n```\n\nYou pass in the auditor a bunyan logger, optionally server object,\nRingbuffer and a flag printLog indicate if log needs to be print out at info\nlevel or not.  By default, without specify printLog flag, it will write out\nrecord lookling like this:\n\n\n```javascript\n{\n  \"name\": \"audit\",\n  \"hostname\": \"your.host.name\",\n  \"audit\": true,\n  \"remoteAddress\": \"127.0.0.1\",\n  \"remotePort\": 57692,\n  \"req_id\": \"ed634c3e-1af0-40e4-ad1e-68c2fb67c8e1\",\n  \"req\": {\n    \"method\": \"GET\",\n    \"url\": \"/foo\",\n    \"headers\": {\n      \"authorization\": \"Basic YWRtaW46am95cGFzczEyMw==\",\n      \"user-agent\": \"curl/7.19.7 (universal-apple-darwin10.0)\n         libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3\",\n      \"host\": \"localhost:8080\",\n      \"accept\": \"application/json\"\n    },\n    \"httpVersion\": \"1.1\",\n    \"query\": {\n        \"foo\": \"bar\"\n    },\n    \"trailers\": {},\n    \"version\": \"*\",\n    \"timers\": {\n      \"bunyan\": 52,\n      \"saveAction\": 8,\n      \"reqResTracker\": 213,\n      \"addContext\": 8,\n      \"addModels\": 4,\n      \"resNamespaces\": 5,\n      \"parseQueryString\": 11,\n      \"instanceHeaders\": 20,\n      \"xForwardedProto\": 7,\n      \"httpsRedirector\": 14,\n      \"readBody\": 21,\n      \"parseBody\": 6,\n      \"xframe\": 7,\n      \"restifyCookieParser\": 15,\n      \"fooHandler\": 23,\n      \"barHandler\": 14,\n      \"carHandler\": 14\n    }\n  },\n  \"res\": {\n    \"statusCode\": 200,\n    \"headers\": {\n      \"access-control-allow-origin\": \"*\",\n      \"access-control-allow-headers\": \"Accept, Accept-Version,\n         Content-Length, Content-MD5, Content-Type, Date, Api-Version\",\n      \"access-control-expose-headers\": \"Api-Version, Request-Id,\n         Response-Time\",\n      \"server\": \"Joyent SmartDataCenter 7.0.0\",\n      \"x-request-id\": \"ed634c3e-1af0-40e4-ad1e-68c2fb67c8e1\",\n      \"access-control-allow-methods\": \"GET\",\n      \"x-api-version\": \"1.0.0\",\n      \"connection\": \"close\",\n      \"content-length\": 158,\n      \"content-md5\": \"zkiRn2/k3saflPhxXI7aXA==\",\n      \"content-type\": \"application/json\",\n      \"date\": \"Tue, 07 Feb 2012 20:30:31 GMT\",\n      \"x-response-time\": 1639\n    },\n    \"trailer\": false\n  },\n  \"route\": {\n  \"name\": \"GetFoo\",\n  \"version\": [\"1.0.0\"]\n  },\n  \"secure\": false,\n  \"level\": 30,\n  \"msg\": \"GetFoo handled: 200\",\n  \"time\": \"2012-02-07T20:30:31.896Z\",\n  \"v\": 0\n}\n```\n\nThe `timers` field shows the time each handler took to run in microseconds.\nRestify by default will record this information for every handler for each\nroute. However, if you decide to include nested handlers, you can track the\ntiming yourself by utilizing the Request\n[startHandlerTimer][55] and\n[endHandlerTimer][56] API.\nYou can also listen to auditlog event and get same above log object when\nlog event emits. For example\n\n\n```javascript\nSERVER.on('auditlog', function (data) {\n    //do some process with log\n});\n```\n\nReturns **[Function][35]** Handler\n\n### metrics\n\nThe module includes the following plugins to be used with restify's `after`\nevent, e.g., `server.on('after', restify.plugins.metrics());`:\n\nA plugin that listens to the server's after event and emits information\nabout that request.\n\n**Parameters**\n\n-   `opts` **[Object][39]** an options obj\n    -   `opts.server` **Server** restify server\n-   `callback` **createMetrics~callback** a callback fn\n\n**Examples**\n\n```javascript\nserver.on('after', restify.plugins.metrics({ server: server },\n    function (err, metrics, req, res, route) {\n        // metrics is an object containing information about the request\n}));\n```\n\nReturns **[Function][35]** returns a function suitable to be used\n  with restify server's `after` event\n\n## Types\n\n\n\n\n### metrics~callback\n\nCallback used by metrics plugin\n\nType: [Function][35]\n\n**Parameters**\n\n-   `err` **[Error][54]** \n-   `metrics` **[Object][39]** metrics about the request\n    -   `metrics.statusCode` **[Number][44]** status code of the response. can be\n          undefined in the case of an uncaughtException\n    -   `metrics.method` **[String][41]** http request verb\n    -   `metrics.totalLatency` **[Number][44]** latency includes both request is flushed\n                                             and all handlers finished\n    -   `metrics.latency` **[Number][44]** latency when request is flushed\n    -   `metrics.preLatency` **([Number][44] | null)** pre handlers latency\n    -   `metrics.useLatency` **([Number][44] | null)** use handlers latency\n    -   `metrics.routeLatency` **([Number][44] | null)** route handlers latency\n    -   `metrics.path` **[String][41]** `req.path()` value\n    -   `metrics.inflightRequests` **[Number][44]** Number of inflight requests pending\n          in restify.\n    -   `metrics.unifinishedRequests` **[Number][44]** Same as `inflightRequests`\n    -   `metrics.connectionState` **[String][41]** can be either `'close'` or\n         `undefined`. If this value is set, err will be a\n          corresponding `RequestCloseError`.\n          If connectionState is either\n          `'close'`, then the `statusCode` is not applicable since the\n          connection was severed before a response was written.\n-   `req` **[Request][57]** the request obj\n-   `res` **[Response][58]** the response obj\n-   `route` **Route** the route obj that serviced the request\n\n## req.set\n\nSet context value by key\nRequires the context plugin.\n\n**Parameters**\n\n-   `key` **[String][41]** key\n-   `value` **any** value\n\nReturns **[undefined][59]** no return value\n\n## req.get\n\nGet context value by key.\nRequires the context plugin.\n\n**Parameters**\n\n-   `key` **[String][41]** key\n\nReturns **any** value stored in context\n\n## req.getAll\n\nGet all context\nRequires the context plugin.\n\nReturns **any** value stored in context\n\n[1]: #usage\n\n[2]: #serverpre-plugins\n\n[3]: #context\n\n[4]: #dedupeslashes\n\n[5]: #pause\n\n[6]: #sanitizepath\n\n[7]: #reqidheaders\n\n[8]: #strictqueryparams\n\n[9]: #useragentconnection\n\n[10]: #serveruse-plugins\n\n[11]: #acceptparser\n\n[12]: #authorizationparser\n\n[13]: #dateparser\n\n[14]: #queryparser\n\n[15]: #jsonp\n\n[16]: #bodyparser\n\n[17]: #requestlogger\n\n[18]: #gzipresponse\n\n[19]: #servestatic\n\n[20]: #servestaticfiles\n\n[21]: #throttle\n\n[22]: #requestexpiry\n\n[23]: #using-an-external-storage-mechanism-for-keybucket-mappings\n\n[24]: #inflightrequestthrottle\n\n[25]: #cpuusagethrottle\n\n[26]: #conditionalhandler\n\n[27]: #conditionalrequest\n\n[28]: #auditlogger\n\n[29]: #metrics\n\n[30]: #types\n\n[31]: #metricscallback\n\n[32]: #reqset\n\n[33]: #reqget\n\n[34]: #reqgetall\n\n[35]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function\n\n[36]: https://github.com/restify/node-restify/issues/287\n\n[37]: https://github.com/restify/node-restify/issues/409\n\n[38]: https://github.com/restify/node-restify/wiki/1.4-to-2.0-Migration-Tips\n\n[39]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object\n\n[40]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array\n\n[41]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String\n\n[42]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp\n\n[43]: https://github.com/joyent/node-http-signature\n\n[44]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number\n\n[45]: https://github.com/ljharb/qs\n\n[46]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean\n\n[47]: https://github.com/felixge/node-formidable\n\n[48]: https://github.com/trentm/node-bunyan\n\n[49]: #request-api\n\n[50]: https://github.com/restify/node-restify/issues/284\n\n[51]: https://github.com/pillarjs/send\n\n[52]: http://en.wikipedia.org/wiki/Token_bucket\n\n[53]: http://tools.ietf.org/html/draft-nottingham-http-new-status-03#section-4\n\n[54]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error\n\n[55]: #starthandlertimerhandlername\n\n[56]: #endhandlertimerhandlername\n\n[57]: https://developer.mozilla.org/Add-ons/SDK/High-Level_APIs/request\n\n[58]: https://developer.mozilla.org/docs/Web/Guide/HTML/HTML5\n\n[59]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined\n"
  },
  {
    "path": "docs/_api/request.md",
    "content": "---\ntitle: Request API\npermalink: /docs/request-api/\n---\n\n<!-- Generated by documentation.js. Update this documentation by updating the source code. -->\n\n### Table of Contents\n\n-   [Request][1]\n    -   [accepts][2]\n    -   [acceptsEncoding][3]\n    -   [contentLength][4]\n    -   [getContentType][5]\n    -   [date][6]\n    -   [href][7]\n    -   [id][8]\n    -   [getPath][9]\n    -   [getQuery][10]\n    -   [time][11]\n    -   [version][12]\n    -   [header][13]\n    -   [trailer][14]\n    -   [is][15]\n    -   [isChunked][16]\n    -   [isKeepAlive][17]\n    -   [isSecure][18]\n    -   [isUpgradeRequest][19]\n    -   [isUpload][20]\n    -   [toString][21]\n    -   [userAgent][22]\n    -   [startHandlerTimer][23]\n    -   [endHandlerTimer][24]\n    -   [connectionState][25]\n    -   [getRoute][26]\n-   [Events][27]\n-   [Log][28]\n\n## Request\n\n**Extends http.IncomingMessage**\n\nWraps all of the node\n[http.IncomingMessage][29]\nAPIs, events and properties, plus the following.\n\n### accepts\n\nCheck if the Accept header is present, and includes the given type.\nWhen the Accept header is not present true is returned.\nOtherwise the given type is matched by an exact match, and then subtypes.\n\n**Parameters**\n\n-   `types` **([String][30] \\| [Array][31]&lt;[String][30]>)** an array of accept type headers\n\n**Examples**\n\nYou may pass the subtype such as html which is then converted internally\nto text/html using the mime lookup table:\n\n\n```javascript\n// Accept: text/html\nreq.accepts('html');\n// => true\n\n// Accept: text/*; application/json\nreq.accepts('html');\nreq.accepts('text/html');\nreq.accepts('text/plain');\nreq.accepts('application/json');\n// => true\n\nreq.accepts('image/png');\nreq.accepts('png');\n// => false\n```\n\nReturns **[Boolean][32]** is accepteed\n\n### acceptsEncoding\n\nChecks if the request accepts the encoding type(s) specified.\n\n**Parameters**\n\n-   `types` **([String][30] \\| [Array][31]&lt;[String][30]>)** an array of accept type headers\n\nReturns **[Boolean][32]** is accepted encoding\n\n### contentLength\n\nReturns the value of the content-length header.\n\nReturns **[Number][33]** \n\n### getContentType\n\nReturns the value of the content-type header. If a content-type is not\nset, this will return a default value of `application/octet-stream`\n\nReturns **[String][30]** content type\n\n### date\n\nReturns a Date object representing when the request was setup.\nLike `time()`, but returns a Date object.\n\nReturns **[Date][34]** date when request began being processed\n\n### href\n\nReturns the full requested URL.\n\n**Examples**\n\n```javascript\n// incoming request is http://localhost:3000/foo/bar?a=1\nserver.get('/:x/bar', function(req, res, next) {\n    console.warn(req.href());\n    // => /foo/bar/?a=1\n});\n```\n\nReturns **[String][30]** \n\n### id\n\nReturns the request id. If a `reqId` value is passed in,\nthis will become the request’s new id. The request id is immutable,\nand can only be set once. Attempting to set the request id more than\nonce will cause restify to throw.\n\n**Parameters**\n\n-   `reqId` **[String][30]** request id\n\nReturns **[String][30]** id\n\n### getPath\n\nReturns the cleaned up requested URL.\n\n**Examples**\n\n```javascript\n// incoming request is http://localhost:3000/foo/bar?a=1\nserver.get('/:x/bar', function(req, res, next) {\n    console.warn(req.path());\n    // => /foo/bar\n});\n```\n\nReturns **[String][30]** \n\n### getQuery\n\nReturns the raw query string. Returns empty string\nif no query string is found.\n\n**Examples**\n\n```javascript\n// incoming request is /foo?a=1\nreq.getQuery();\n// => 'a=1'\n```\n\nIf the queryParser plugin is used, the parsed query string is\navailable under the req.query:\n\n\n```javascript\n// incoming request is /foo?a=1\nserver.use(restify.plugins.queryParser());\nreq.query;\n// => { a: 1 }\n```\n\nReturns **[String][30]** query\n\n### time\n\nThe number of ms since epoch of when this request began being processed.\nLike date(), but returns a number.\n\nReturns **[Number][33]** time when request began being processed in epoch:\n                   ellapsed milliseconds since\n                   January 1, 1970, 00:00:00 UTC\n\n### version\n\nReturns the accept-version header.\n\nReturns **[String][30]** \n\n### header\n\nGet the case-insensitive request header key,\nand optionally provide a default value (express-compliant).\nReturns any header off the request. also, 'correct' any\ncorrectly spelled 'referrer' header to the actual spelling used.\n\n**Parameters**\n\n-   `key` **[String][30]** the key of the header\n-   `defaultValue` **[String][30]?** default value if header isn't\n                                      found on the req\n\n**Examples**\n\n```javascript\nreq.header('Host');\nreq.header('HOST');\nreq.header('Accept', '*\\/*');\n```\n\nReturns **[String][30]** header value\n\n### trailer\n\nReturns any trailer header off the request. Also, 'correct' any\ncorrectly spelled 'referrer' header to the actual spelling used.\n\n**Parameters**\n\n-   `name` **[String][30]** the name of the header\n-   `value` **[String][30]** default value if header isn't found on the req\n\nReturns **[String][30]** trailer value\n\n### is\n\nCheck if the incoming request contains the `Content-Type` header field,\nand if it contains the given mime type.\n\n**Parameters**\n\n-   `type` **[String][30]** a content-type header value\n\n**Examples**\n\n```javascript\n// With Content-Type: text/html; charset=utf-8\nreq.is('html');\nreq.is('text/html');\n// => true\n\n// When Content-Type is application/json\nreq.is('json');\nreq.is('application/json');\n// => true\n\nreq.is('html');\n// => false\n```\n\nReturns **[Boolean][32]** is content-type header\n\n### isChunked\n\nCheck if the incoming request is chunked.\n\nReturns **[Boolean][32]** is chunked\n\n### isKeepAlive\n\nCheck if the incoming request is kept alive.\n\nReturns **[Boolean][32]** is keep alive\n\n### isSecure\n\nCheck if the incoming request is encrypted.\n\nReturns **[Boolean][32]** is secure\n\n### isUpgradeRequest\n\nCheck if the incoming request has been upgraded.\n\nReturns **[Boolean][32]** is upgraded\n\n### isUpload\n\nCheck if the incoming request is an upload verb.\n\nReturns **[Boolean][32]** is upload\n\n### toString\n\ntoString serialization\n\nReturns **[String][30]** serialized request\n\n### userAgent\n\nReturns the user-agent header.\n\nReturns **[String][30]** user agent\n\n### startHandlerTimer\n\nStart the timer for a request handler.\nBy default, restify uses calls this automatically for all handlers\nregistered in your handler chain.\nHowever, this can be called manually for nested functions inside the\nhandler chain to record timing information.\n\n**Parameters**\n\n-   `handlerName` **[String][30]** The name of the handler.\n\n**Examples**\n\nYou must explicitly invoke\nendHandlerTimer() after invoking this function. Otherwise timing\ninformation will be inaccurate.\n\n\n```javascript\nserver.get('/', function fooHandler(req, res, next) {\n    vasync.pipeline({\n        funcs: [\n            function nestedHandler1(req, res, next) {\n                req.startHandlerTimer('nestedHandler1');\n                // do something\n                req.endHandlerTimer('nestedHandler1');\n                return next();\n            },\n            function nestedHandler1(req, res, next) {\n                req.startHandlerTimer('nestedHandler2');\n                // do something\n                req.endHandlerTimer('nestedHandler2');\n                return next();\n\n            }...\n       ]...\n    }, next);\n});\n```\n\nReturns **[undefined][35]** no return value\n\n### endHandlerTimer\n\nEnd the timer for a request handler.\nYou must invoke this function if you called `startRequestHandler` on a\nhandler. Otherwise the time recorded will be incorrect.\n\n**Parameters**\n\n-   `handlerName` **[String][30]** The name of the handler.\n\nReturns **[undefined][35]** no return value\n\n### connectionState\n\nReturns the connection state of the request. Current possible values are:\n\n-   `close` - when the request has been closed by the clien\n\nReturns **[String][30]** connection state (`\"close\"`)\n\n### getRoute\n\nReturns the route object to which the current request was matched to.\n\n**Examples**\n\nRoute info object structure:\n\n\n```javascript\n{\n path: '/ping/:name',\n method: 'GET',\n versions: [],\n name: 'getpingname'\n}\n```\n\nReturns **[Object][36]** route\n\n## Events\n\nIn additional to emitting all the events from node's\n[http.Server][37],\nrestify servers also emit a number of additional events that make building REST\nand web applications much easier.\n\n### restifyError\n\nThis event is emitted following all error events as a generic catch all. It is\nrecommended to use specific error events to handle specific errors, but this\nevent can be useful for metrics or logging. If you use this in conjunction with\nother error events, the most specific event will be fired first, followed by\nthis one:\n\n```js\nserver.get('/', function(req, res, next) {\n  return next(new InternalServerError('boom'));\n});\n\nserver.on('InternalServer', function(req, res, err, callback) {\n  // this will get fired first, as it's the most relevant listener\n  return callback();\n});\n\nserver.on('restifyError', function(req, res, err, callback) {\n  // this is fired second.\n  return callback();\n});\n```\n\n### after\n\nAfter each request has been fully serviced, an `after` event is fired. This\nevent can be hooked into to handle audit logs and other metrics. Note that\nflushing a response does not necessarily correspond with an `after` event.\nrestify considers a request to be fully serviced when either:\n\n1) The handler chain for a route has been fully completed\n2) An error was returned to `next()`, and the corresponding error events have\n   been fired for that error type\n\nThe signature is for the after event is as follows:\n\n```js\nfunction(req, res, route, error) { }\n```\n\n-   `req` - the request object\n-   `res` - the response object\n-   `route` - the route object that serviced the request\n-   `error` - the error passed to `next()`, if applicable\n\nNote that when the server automatically responds with a\nNotFound/MethodNotAllowed/VersionNotAllowed, this event will still be fired.\n\n### pre\n\nBefore each request has been routed, a `pre` event is fired. This event can be\nhooked into handle audit logs and other metrics. Since this event fires\n_before_ routing has occured, it will fire regardless of whether the route is\nsupported or not, e.g. requests that result in a `404`.\n\nThe signature for the `pre` event is as follows:\n\n```js\nfunction(req, res) {}\n```\n\n-   `req` - the request object\n-   `res` - the response object\n\nNote that when the server automatically responds with a\nNotFound/MethodNotAllowed/VersionNotAllowed, this event will still be fired.\n\n### routed\n\nA `routed` event is fired after a request has been routed by the router, but\nbefore handlers specific to that route has run.\n\nThe signature for the `routed` event is as follows:\n\n```js\nfunction(req, res, route) {}\n```\n\n-   `req` - the request object\n-   `res` - the response object\n-   `route` - the route object that serviced the request\n\nNote that this event will _not_ fire if a requests comes in that are not\nroutable, i.e. one that would result in a `404`.\n\n### uncaughtException\n\nIf the restify server was created with `handleUncaughtExceptions: true`,\nrestify will leverage [domains][38] to handle\nthrown errors in the handler chain. Thrown errors are a result of an explicit\n`throw` statement, or as a result of programmer errors like a typo or a null\nref. These thrown errors are caught by the domain, and will be emitted via this\nevent. For example:\n\n```js\nserver.get('/', function(req, res, next) {\n    res.send(x);  // this will cause a ReferenceError\n    return next();\n});\n\nserver.on('uncaughtException', function(req, res, route, err) {\n    // this event will be fired, with the error object from above:\n    // ReferenceError: x is not defined\n});\n```\n\nIf you listen to this event, you **must** send a response to the client. This\nbehavior is different from the standard error events. If you do not listen to\nthis event, restify's default behavior is to call `res.send()` with the error\nthat was thrown.\n\nThe signature is for the after event is as follows:\n\n```js\nfunction(req, res, route, error) { }\n```\n\n-   `req` - the request object\n-   `res` - the response object\n-   `route` - the route object that serviced the request\n-   `error` - the error passed to `next()`, if applicable\n\n### close\n\nEmitted when the server closes.\n\n\n## Log\n\nIf you are using the [RequestLogger][39] plugin, the child logger\nwill be available on `req.log`:\n\n```js\nfunction myHandler(req, res, next) {\n  var log = req.log;\n\n  log.debug({params: req.params}, 'Hello there %s', 'foo');\n}\n```\n\nThe child logger will inject the request's UUID in the `req._id` attribute of\neach log statement. Since the logger lasts for the life of the request, you can\nuse this to correlate statements for an individual request across any number of\nseparate handlers.\n\n\n[1]: #request\n\n[2]: #accepts\n\n[3]: #acceptsencoding\n\n[4]: #contentlength\n\n[5]: #getcontenttype\n\n[6]: #date\n\n[7]: #href\n\n[8]: #id\n\n[9]: #getpath\n\n[10]: #getquery\n\n[11]: #time\n\n[12]: #version\n\n[13]: #header\n\n[14]: #trailer\n\n[15]: #is\n\n[16]: #ischunked\n\n[17]: #iskeepalive\n\n[18]: #issecure\n\n[19]: #isupgraderequest\n\n[20]: #isupload\n\n[21]: #tostring\n\n[22]: #useragent\n\n[23]: #starthandlertimer\n\n[24]: #endhandlertimer\n\n[25]: #connectionstate\n\n[26]: #getroute\n\n[27]: #events\n\n[28]: #log\n\n[29]: https://nodejs.org/api/http.html\n\n[30]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String\n\n[31]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array\n\n[32]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean\n\n[33]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number\n\n[34]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date\n\n[35]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined\n\n[36]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object\n\n[37]: http://nodejs.org/docs/latest/api/http.html#http_class_http_server\n\n[38]: https://nodejs.org/api/domain.html\n\n[39]: #bundled-plugins\n"
  },
  {
    "path": "docs/_api/response.md",
    "content": "---\ntitle: Response API\npermalink: /docs/response-api/\n---\n\n<!-- Generated by documentation.js. Update this documentation by updating the source code. -->\n\n### Table of Contents\n\n-   [Response][1]\n    -   [cache][2]\n    -   [noCache][3]\n    -   [charSet][4]\n    -   [header][5]\n    -   [json][6]\n    -   [link][7]\n    -   [send][8]\n    -   [sendRaw][9]\n    -   [set][10]\n    -   [status][11]\n    -   [redirect][12]\n    -   [redirect][13]\n    -   [redirect][14]\n\n## Response\n\n**Extends http.ServerResponse**\n\nWraps all of the node\n[http.ServerResponse][15]\nAPIs, events and properties, plus the following.\n\n### cache\n\nSets the `cache-control` header.\n\n**Parameters**\n\n-   `type` **[String][16]** value of the header\n                                       (`\"public\"` or `\"private\"`) (optional, default `\"public\"`)\n-   `options` **[Object][17]?** an options object\n    -   `options.maxAge` **[Number][18]** max-age in seconds\n\nReturns **[String][16]** the value set to the header\n\n### noCache\n\nTurns off all cache related headers.\n\nReturns **[Response][19]** self, the response object\n\n### charSet\n\nAppends the provided character set to the response's `Content-Type`.\n\n**Parameters**\n\n-   `type` **[String][16]** char-set value\n\n**Examples**\n\n```javascript\nres.charSet('utf-8');\n```\n\nReturns **[Response][19]** self, the response object\n\n### header\n\nSets headers on the response.\n\n**Parameters**\n\n-   `key` **[String][16]** the name of the header\n-   `value` **[String][16]** the value of the header\n\n**Examples**\n\nIf only key is specified, return the value of the header.\nIf both key and value are specified, set the response header.\n\n\n```javascript\nres.header('Content-Length');\n// => undefined\n\nres.header('Content-Length', 123);\n// => 123\n\nres.header('Content-Length');\n// => 123\n\nres.header('foo', new Date());\n// => Fri, 03 Feb 2012 20:09:58 GMT\n```\n\n`header()` can also be used to automatically chain header values\nwhen applicable:\n\n\n```javascript\nres.header('x-foo', 'a');\nres.header('x-foo', 'b');\n// => { 'x-foo': ['a', 'b'] }\n```\n\nReturns **[Object][17]** the retrieved value or the value that was set\n\n### json\n\nSyntatic sugar for:\n\n```js\nres.contentType = 'json';\nres.send({hello: 'world'});\n```\n\n**Parameters**\n\n-   `code` **[Number][18]?**    http status code\n-   `body` **[Object][17]?**    value to json.stringify\n-   `headers` **[Object][17]?** headers to set on the response\n\n**Examples**\n\n```javascript\nres.header('content-type', 'json');\nres.send({hello: 'world'});\n```\n\nReturns **[Object][17]** the response object\n\n### link\n\nSets the link header.\n\n**Parameters**\n\n-   `key` **[String][16]**  the link key\n-   `value` **[String][16]** the link value\n\nReturns **[String][16]** the header value set to res\n\n### send\n\nSends the response object. pass through to internal `__send` that uses a\nformatter based on the `content-type` header.\n\n**Parameters**\n\n-   `code` **[Number][18]?** http status code\n-   `body` **([Object][17] \\| [Buffer][20] \\| [Error][21])?** the content to send\n-   `headers` **[Object][17]?** any add'l headers to set\n\n**Examples**\n\nYou can use send() to wrap up all the usual writeHead(), write(), end()\ncalls on the HTTP API of node.\nYou can pass send either a `code` and `body`, or just a body. body can be\nan `Object`, a `Buffer`, or an `Error`.\nWhen you call `send()`, restify figures out how to format the response\nbased on the `content-type`.\n\n\n```javascript\nres.send({hello: 'world'});\nres.send(201, {hello: 'world'});\nres.send(new BadRequestError('meh'));\n```\n\nReturns **[Object][17]** the response object\n\n### sendRaw\n\nLike `res.send()`, but skips formatting. This can be useful when the\npayload has already been preformatted.\nSends the response object. pass through to internal `__send` that skips\nformatters entirely and sends the content as is.\n\n**Parameters**\n\n-   `code` **[Number][18]?** http status code\n-   `body` **([Object][17] \\| [Buffer][20] \\| [Error][21])?** the content to send\n-   `headers` **[Object][17]?** any add'l headers to set\n\nReturns **[Object][17]** the response object\n\n### set\n\nSets multiple header(s) on the response.\nUses `header()` underneath the hood, enabling multi-value headers.\n\n**Parameters**\n\n-   `name` **([String][16] \\| [Object][17])** name of the header or\n                                   `Object` of headers\n-   `val` **[String][16]** value of the header\n\n**Examples**\n\n```javascript\nres.header('x-foo', 'a');\nres.set({\n    'x-foo', 'b',\n    'content-type': 'application/json'\n});\n// =>\n// {\n//    'x-foo': [ 'a', 'b' ],\n//    'content-type': 'application/json'\n// }\n```\n\nReturns **[Object][17]** self, the response object\n\n### status\n\nSets the http status code on the response.\n\n**Parameters**\n\n-   `code` **[Number][18]** http status code\n\n**Examples**\n\n```javascript\nres.status(201);\n```\n\nReturns **[Number][18]** the status code passed in\n\n### redirect\n\nRedirect is sugar method for redirecting.\n\n**Parameters**\n\n-   `options` **[Object][17]** url or an options object to configure a redirect\n    -   `options.secure` **[Boolean][22]?** whether to redirect to http or https\n    -   `options.hostname` **[String][16]?** redirect location's hostname\n    -   `options.pathname` **[String][16]?** redirect location's pathname\n    -   `options.port` **[String][16]?** redirect location's port number\n    -   `options.query` **[String][16]?** redirect location's query string\n                                        parameters\n    -   `options.overrideQuery` **[Boolean][22]?** if true, `options.query`\n                                                 stomps over any existing query\n                                                 parameters on current URL.\n                                                 by default, will merge the two.\n    -   `options.permanent` **[Boolean][22]?** if true, sets 301. defaults to 302.\n-   `next` **[Function][23]** mandatory, to complete the response and trigger\n                           audit logger.\n\n**Examples**\n\n```javascript\nres.redirect({...}, next);\n```\n\nA convenience method for 301/302 redirects. Using this method will tell\nrestify to stop execution of your handler chain.\nYou can also use an options object. `next` is required.\n\n\n```javascript\nres.redirect({\n  hostname: 'www.foo.com',\n  pathname: '/bar',\n  port: 80,                 // defaults to 80\n  secure: true,             // sets https\n  permanent: true,\n  query: {\n    a: 1\n  }\n}, next);  // => redirects to 301 https://www.foo.com/bar?a=1\n```\n\nReturns **[undefined][24]** \n\n### redirect\n\nRedirect with code and url.\n\n**Parameters**\n\n-   `code` **[Number][18]** http redirect status code\n-   `url` **[String][16]** redirect url\n-   `next` **[Function][23]** mandatory, to complete the response and trigger\n                           audit logger.\n\n**Examples**\n\n```javascript\nres.redirect(301, 'www.foo.com', next);\n```\n\nReturns **[undefined][24]** \n\n### redirect\n\nRedirect with url.\n\n**Parameters**\n\n-   `url` **[String][16]** redirect url\n-   `next` **[Function][23]** mandatory, to complete the response and trigger\n                           audit logger.\n\n**Examples**\n\n```javascript\nres.redirect('www.foo.com', next);\nres.redirect('/foo', next);\n```\n\nReturns **[undefined][24]** \n\n[1]: #response\n\n[2]: #cache\n\n[3]: #nocache\n\n[4]: #charset\n\n[5]: #header\n\n[6]: #json\n\n[7]: #link\n\n[8]: #send\n\n[9]: #sendraw\n\n[10]: #set\n\n[11]: #status\n\n[12]: #redirect\n\n[13]: #redirect-1\n\n[14]: #redirect-2\n\n[15]: https://nodejs.org/docs/latest/api/http.html\n\n[16]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String\n\n[17]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object\n\n[18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number\n\n[19]: #response\n\n[20]: https://nodejs.org/api/buffer.html\n\n[21]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error\n\n[22]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean\n\n[23]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function\n\n[24]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined\n"
  },
  {
    "path": "docs/_api/server.md",
    "content": "---\ntitle: Server API\npermalink: /docs/server-api/\n---\n\n<!-- Generated by documentation.js. Update this documentation by updating the source code. -->\n\n### Table of Contents\n\n-   [createServer][1]\n-   [Server][2]\n    -   [listen][3]\n    -   [close][4]\n    -   [get][5]\n    -   [head][6]\n    -   [post][7]\n    -   [put][8]\n    -   [patch][9]\n    -   [del][10]\n    -   [opts][11]\n    -   [pre][12]\n    -   [use][13]\n    -   [param][14]\n    -   [rm][15]\n    -   [address][16]\n    -   [inflightRequests][17]\n    -   [debugInfo][18]\n    -   [toString][19]\n-   [Events][20]\n-   [Errors][21]\n-   [Types][22]\n    -   [Server~methodOpts][23]\n\n## createServer\n\nA restify server object is the main interface through which you will register\nroutes and handlers for incoming requests.\n\n**Parameters**\n\n-   `options` **[Object][24]?** an options object\n    -   `options.name` **[String][25]** Name of the server. (optional, default `\"restify\"`)\n    -   `options.dtrace` **[Boolean][26]** enable DTrace support (optional, default `false`)\n    -   `options.router` **Router** Router (optional, default `newRouter(opts)`)\n    -   `options.log` **[Object][24]** [bunyan][27] instance. (optional, default `bunyan.createLogger(options.name||\"restify\")`)\n    -   `options.url` **[String][25]?** Once listen() is called, this will be filled\n        in with where the server is running.\n    -   `options.certificate` **([String][25] \\| [Buffer][28])?** If you want to create an HTTPS\n        server, pass in a PEM-encoded certificate and key.\n    -   `options.key` **([String][25] \\| [Buffer][28])?** If you want to create an HTTPS server,\n        pass in a PEM-encoded certificate and key.\n    -   `options.formatters` **[Object][24]?** Custom response formatters for\n        `res.send()`.\n    -   `options.handleUncaughtExceptions` **[Boolean][26]** When true restify\n        will use a domain to catch and respond to any uncaught\n        exceptions that occur in its handler stack.\n        Comes with significant negative performance impact. (optional, default `false`)\n    -   `options.spdy` **[Object][24]?** Any options accepted by\n        [node-spdy][29].\n    -   `options.http2` **[Object][24]?** Any options accepted by\n        [http2.createSecureServer][30].\n    -   `options.handleUpgrades` **[Boolean][26]** Hook the `upgrade` event\n        from the node HTTP server, pushing `Connection: Upgrade` requests through the\n         regular request handling chain. (optional, default `false`)\n    -   `options.onceNext` **[Boolean][26]** Prevents calling next multiple\n         times (optional, default `false`)\n    -   `options.strictNext` **[Boolean][26]** Throws error when next() is\n         called more than once, enabled onceNext option (optional, default `false`)\n    -   `options.httpsServerOptions` **[Object][24]?** Any options accepted by\n        [node-https Server][31].\n        If provided the following restify server options will be ignored:\n        spdy, ca, certificate, key, passphrase, rejectUnauthorized, requestCert and\n        ciphers; however these can all be specified on httpsServerOptions.\n    -   `options.noWriteContinue` **[Boolean][26]** prevents\n         `res.writeContinue()` in `server.on('checkContinue')` when proxing (optional, default `false`)\n    -   `options.ignoreTrailingSlash` **[Boolean][26]** ignore trailing slash\n        on paths (optional, default `false`)\n    -   `options.strictFormatters` **[Boolean][26]** enables strict formatters\n        behavior: a formatter matching the response's content-type is required. If\n        not found, the response's content-type is automatically set to\n        'application/octet-stream'. If a formatter for that content-type is not\n        found, sending the response errors. (optional, default `true`)\n\n**Examples**\n\n```javascript\nvar restify = require('restify');\nvar server = restify.createServer();\n\nserver.listen(8080, function () {\n  console.log('ready on %s', server.url);\n});\n```\n\nReturns **[Server][32]** server\n\n## Server\n\nCreates a new Server.\n\n**Parameters**\n\n-   `options` **[Object][24]** an options object\n    -   `options.name` **[String][25]** Name of the server.\n    -   `options.dtrace` **[Boolean][26]** enable DTrace support (optional, default `false`)\n    -   `options.router` **Router** Router\n    -   `options.log` **[Object][24]** [bunyan][27]\n        instance.\n    -   `options.url` **[String][25]?** Once listen() is called, this will be filled\n        in with where the server is running.\n    -   `options.certificate` **([String][25] \\| [Buffer][28])?** If you want to create an HTTPS\n        server, pass in a PEM-encoded certificate and key.\n    -   `options.key` **([String][25] \\| [Buffer][28])?** If you want to create an HTTPS server,\n        pass in a PEM-encoded certificate and key.\n    -   `options.formatters` **[Object][24]?** Custom response formatters for\n        `res.send()`.\n    -   `options.handleUncaughtExceptions` **[Boolean][26]** When true restify\n        will use a domain to catch and respond to any uncaught\n        exceptions that occur in its handler stack.\n        Comes with significant negative performance impact.\n    -   `options.spdy` **[Object][24]?** Any options accepted by\n        [node-spdy][29].\n    -   `options.http2` **[Object][24]?** Any options accepted by\n        [http2.createSecureServer][30].\n    -   `options.handleUpgrades` **[Boolean][26]** Hook the `upgrade` event\n        from the node HTTP server, pushing `Connection: Upgrade` requests through the\n         regular request handling chain. (optional, default `false`)\n    -   `options.onceNext` **[Boolean][26]** Prevents calling next multiple\n         times (optional, default `false`)\n    -   `options.strictNext` **[Boolean][26]** Throws error when next() is\n         called more than once, enabled onceNext option (optional, default `false`)\n    -   `options.httpsServerOptions` **[Object][24]?** Any options accepted by\n        [node-https Server][31].\n        If provided the following restify server options will be ignored:\n        spdy, ca, certificate, key, passphrase, rejectUnauthorized, requestCert and\n        ciphers; however these can all be specified on httpsServerOptions.\n    -   `options.noWriteContinue` **[Boolean][26]** prevents\n         `res.writeContinue()` in `server.on('checkContinue')` when proxing (optional, default `false`)\n    -   `options.ignoreTrailingSlash` **[Boolean][26]** ignore trailing slash\n        on paths (optional, default `false`)\n    -   `options.strictFormatters` **[Boolean][26]** enables strict formatters\n        behavior: a formatter matching the response's content-type is required. If\n        not found, the response's content-type is automatically set to\n        'application/octet-stream'. If a formatter for that content-type is not\n        found, sending the response errors. (optional, default `true`)\n\n**Examples**\n\n```javascript\nvar restify = require('restify');\nvar server = restify.createServer();\n\nserver.listen(8080, function () {\n  console.log('ready on %s', server.url);\n});\n```\n\n### listen\n\nGets the server up and listening.\nWraps node's\n[listen()][33].\n\n**Parameters**\n\n-   `port` **[Number][34]** Port\n-   `host` **[Number][34]?** Host\n-   `callback` **[Function][35]?** optionally get notified when listening.\n\n**Examples**\n\nYou can call like:\n\n\n```javascript\nserver.listen(80)\nserver.listen(80, '127.0.0.1')\nserver.listen('/tmp/server.sock')\n```\n\n-   Throws **[TypeError][36]** \n\nReturns **[undefined][37]** no return value\n\n### close\n\nShuts down this server, and invokes callback (optionally) when done.\nWraps node's\n[close()][38].\n\n**Parameters**\n\n-   `callback` **[Function][35]?** callback to invoke when done\n\nReturns **[undefined][37]** no return value\n\n### get\n\nMounts a chain on the given path against this HTTP verb\n\n**Parameters**\n\n-   `opts` **[Server~methodOpts][39]** if string, the URL to handle.\n                                    if options, the URL to handle, at minimum.\n\n**Examples**\n\n```javascript\nserver.get('/', function (req, res, next) {\n   res.send({ hello: 'world' });\n   next();\n});\n```\n\nReturns **Route** the newly created route.\n\n### head\n\nMounts a chain on the given path against this HTTP verb\n\n**Parameters**\n\n-   `opts` **[Server~methodOpts][39]** if string, the URL to handle.\n                                    if options, the URL to handle, at minimum.\n\nReturns **Route** the newly created route.\n\n### post\n\nMounts a chain on the given path against this HTTP verb\n\n**Parameters**\n\n-   `post` **[Server~methodOpts][39]** if string, the URL to handle.\n                                    if options, the URL to handle, at minimum.\n\nReturns **Route** the newly created route.\n\n### put\n\nMounts a chain on the given path against this HTTP verb\n\n**Parameters**\n\n-   `put` **[Server~methodOpts][39]** if string, the URL to handle.\n                                    if options, the URL to handle, at minimum.\n\nReturns **Route** the newly created route.\n\n### patch\n\nMounts a chain on the given path against this HTTP verb\n\n**Parameters**\n\n-   `patch` **[Server~methodOpts][39]** if string, the URL to handle.\n                                    if options, the URL to handle, at minimum.\n\nReturns **Route** the newly created route.\n\n### del\n\nMounts a chain on the given path against this HTTP verb\n\n**Parameters**\n\n-   `opts` **[Server~methodOpts][39]** if string, the URL to handle.\n                                    if options, the URL to handle, at minimum.\n\nReturns **Route** the newly created route.\n\n### opts\n\nMounts a chain on the given path against this HTTP verb\n\n**Parameters**\n\n-   `opts` **[Server~methodOpts][39]** if string, the URL to handle.\n                                    if options, the URL to handle, at minimum.\n\nReturns **Route** the newly created route.\n\n### pre\n\nGives you hooks to run _before_ any routes are located.  This gives you\na chance to intercept the request and change headers, etc., that routing\ndepends on.  Note that req.params will _not_ be set yet.\n\n**Parameters**\n\n-   `handler` **...([Function][35] \\| [Array][40])** Allows you to add handlers that\n    run for all routes. _before_ routing occurs.\n    This gives you a hook to change request headers and the like if you need to.\n    Note that `req.params` will be undefined, as that's filled in _after_\n    routing.\n    Takes a function, or an array of functions.\n    variable number of nested arrays of handler functions\n\n**Examples**\n\n```javascript\nserver.pre(function(req, res, next) {\n  req.headers.accept = 'application/json';\n  return next();\n});\n```\n\nFor example, `pre()` can be used to deduplicate slashes in\nURLs\n\n\n```javascript\nserver.pre(restify.pre.dedupeSlashes());\n```\n\nReturns **[Object][24]** returns self\n\n### use\n\nAllows you to add in handlers that run for all routes. Note that handlers\nadded\nvia `use()` will run only after the router has found a matching route. If no\nmatch is found, these handlers will never run. Takes a function, or an array\nof functions.\n\nYou can pass in any combination of functions or array of functions.\n\n**Parameters**\n\n-   `handler` **...([Function][35] \\| [Array][40])** A variable number of handler functions-   and/or a\n        variable number of nested arrays of handler functions\n\nReturns **[Object][24]** returns self\n\n### param\n\n-   **See: [http://expressjs.com/guide.html#route-param%20pre-conditions][41]**\n\nMinimal port of the functionality offered by Express.js Route Param\nPre-conditions\n\nThis basically piggy-backs on the `server.use` method. It attaches a\nnew middleware function that only fires if the specified parameter exists\nin req.params\n\nExposes an API:\n  server.param(\"user\", function (req, res, next) {\n    // load the user's information here, always making sure to call next()\n  });\n\n**Parameters**\n\n-   `name` **[String][25]** The name of the URL param to respond to\n-   `fn` **[Function][35]**   The middleware function to execute\n\nReturns **[Object][24]** returns self\n\n### rm\n\nRemoves a route from the server.\nYou pass in the route 'blob' you got from a mount call.\n\n**Parameters**\n\n-   `routeName` **[String][25]** the route name.\n\n\n-   Throws **[TypeError][36]** on bad input.\n\nReturns **[Boolean][26]** true if route was removed, false if not.\n\n### address\n\nReturns the server address.\nWraps node's\n[address()][42].\n\n**Examples**\n\n```javascript\nserver.address()\n```\n\nOutput:\n\n\n```javascript\n{ address: '::', family: 'IPv6', port: 8080 }\n```\n\nReturns **[Object][24]** Address of server\n\n### inflightRequests\n\nReturns the number of inflight requests currently being handled by the server\n\nReturns **[number][34]** number of inflight requests\n\n### debugInfo\n\nReturn debug information about the server.\n\n**Examples**\n\n```javascript\nserver.getDebugInfo()\n```\n\nOutput:\n\n\n```javascript\n{\n  routes: [\n    {\n      name: 'get',\n      method: 'get',\n      input: '/',\n      compiledRegex: /^[\\/]*$/,\n      compiledUrlParams: null,\n      handlers: [Array]\n     }\n  ],\n  server: {\n    formatters: {\n      'application/javascript': [Function: formatJSONP],\n      'application/json': [Function: formatJSON],\n      'text/plain': [Function: formatText],\n      'application/octet-stream': [Function: formatBinary]\n    },\n    address: '::',\n    port: 8080,\n    inflightRequests: 0,\n    pre: [],\n    use: [ 'parseQueryString', '_jsonp' ],\n    after: []\n  }\n}\n```\n\nReturns **[Object][24]** debug info\n\n### toString\n\ntoString() the server for easy reading/output.\n\n**Examples**\n\n```javascript\nserver.toString()\n```\n\nOutput:\n\n\n```javascript\nAccepts: application/json, text/plain, application/octet-stream,\napplication/javascript\nName: restify\nPre: []\nRouter: RestifyRouter:\n\tDELETE: []\n\tGET: [get]\n\tHEAD: []\n\tOPTIONS: []\n\tPATCH: []\n\tPOST: []\n\tPUT: []\n\nRoutes:\n\tget: [parseQueryString, _jsonp, function]\nSecure: false\nUrl: http://[::]:8080\nVersion:\n```\n\nReturns **[String][25]** stringified server\n\n## Events\n\nIn additional to emitting all the events from node's\n[http.Server][43],\nrestify servers also emit a number of additional events that make building REST\nand web applications much easier.\n\n### restifyError\n\nThis event is emitted following all error events as a generic catch all. It is\nrecommended to use specific error events to handle specific errors, but this\nevent can be useful for metrics or logging. If you use this in conjunction with\nother error events, the most specific event will be fired first, followed by\nthis one:\n\n```js\nserver.get('/', function(req, res, next) {\n  return next(new InternalServerError('boom'));\n});\n\nserver.on('InternalServer', function(req, res, err, callback) {\n  // this will get fired first, as it's the most relevant listener\n  return callback();\n});\n\nserver.on('restifyError', function(req, res, err, callback) {\n  // this is fired second.\n  return callback();\n});\n```\n\n### after\n\nAfter each request has been fully serviced, an `after` event is fired. This\nevent can be hooked into to handle audit logs and other metrics. Note that\nflushing a response does not necessarily correspond with an `after` event.\nrestify considers a request to be fully serviced when either:\n\n1) The handler chain for a route has been fully completed\n2) An error was returned to `next()`, and the corresponding error events have\n   been fired for that error type\n\nThe signature is for the after event is as follows:\n\n```js\nfunction(req, res, route, error) { }\n```\n\n-   `req` - the request object\n-   `res` - the response object\n-   `route` - the route object that serviced the request\n-   `error` - the error passed to `next()`, if applicable\n\nNote that when the server automatically responds with a\nNotFound/MethodNotAllowed/VersionNotAllowed, this event will still be fired.\n\n### pre\n\nBefore each request has been routed, a `pre` event is fired. This event can be\nhooked into handle audit logs and other metrics. Since this event fires\n_before_ routing has occured, it will fire regardless of whether the route is\nsupported or not, e.g. requests that result in a `404`.\n\nThe signature for the `pre` event is as follows:\n\n```js\nfunction(req, res) {}\n```\n\n-   `req` - the request object\n-   `res` - the response object\n\nNote that when the server automatically responds with a\nNotFound/MethodNotAllowed/VersionNotAllowed, this event will still be fired.\n\n### routed\n\nA `routed` event is fired after a request has been routed by the router, but\nbefore handlers specific to that route has run.\n\nThe signature for the `routed` event is as follows:\n\n```js\nfunction(req, res, route) {}\n```\n\n-   `req` - the request object\n-   `res` - the response object\n-   `route` - the route object that serviced the request\n\nNote that this event will _not_ fire if a requests comes in that are not\nroutable, i.e. one that would result in a `404`.\n\n### uncaughtException\n\nIf the restify server was created with `handleUncaughtExceptions: true`,\nrestify will leverage [domains][44] to handle\nthrown errors in the handler chain. Thrown errors are a result of an explicit\n`throw` statement, or as a result of programmer errors like a typo or a null\nref. These thrown errors are caught by the domain, and will be emitted via this\nevent. For example:\n\n```js\nserver.get('/', function(req, res, next) {\n    res.send(x);  // this will cause a ReferenceError\n    return next();\n});\n\nserver.on('uncaughtException', function(req, res, route, err) {\n    // this event will be fired, with the error object from above:\n    // ReferenceError: x is not defined\n});\n```\n\nIf you listen to this event, you **must** send a response to the client. This\nbehavior is different from the standard error events. If you do not listen to\nthis event, restify's default behavior is to call `res.send()` with the error\nthat was thrown.\n\nThe signature is for the after event is as follows:\n\n```js\nfunction(req, res, route, error) { }\n```\n\n-   `req` - the request object\n-   `res` - the response object\n-   `route` - the route object that serviced the request\n-   `error` - the error passed to `next()`, if applicable\n\n### close\n\nEmitted when the server closes.\n\n\n## Errors\n\nRestify handles errors as first class citizens. When an error object is passed\nto the `next()` function, an event is emitted on the server object, and the\nerror object will be serialized and sent to the client. An error object is any\nobject that passes an `instanceof Error` check.\n\nBefore the error object is sent to the client, the server will fire an event\nusing the name of the error, without the `Error` part of the name. For example,\ngiven an `InternalServerError`, the server will emit an `InternalServer` event.\nThis creates opportunities to do logging, metrics, or payload mutation based on\nthe type of error. For example:\n\n```js\nvar errs = require('restify-errors');\n\nserver.get('/', function(req, res, next) {\n    return next(new errs.InternalServerError('boom!'));\n});\n\nserver.on('InternalServer', function(req, res, err, callback) {\n    // before the response is sent, this listener will be invoked, allowing\n    // opportunities to do metrics capturing or logging.\n    myMetrics.capture(err);\n    // invoke the callback to complete your work, and the server will send out\n    // a response.\n    return callback();\n});\n```\n\nInside the error event listener, it is also possible to change the serialization\nmethod of the error if desired. To do so, simply implement a custom\n`toString()` or `toJSON()`. Depending on the content-type and formatter being\nused for the response, one of the two serializers will be used. For example,\ngiven the folllwing example:\n\n```js\nserver.on('restifyError', function(req, res, err, callback) {\n    err.toJSON = function customToJSON() {\n        return {\n            name: err.name,\n            message: err.message\n        };\n    };\n    err.toString = function customToString() {\n        return 'i just want a string';\n    };\n    return callback();\n});\n```\n\nA request with an `accept: application/json` will trigger the `toJSON()`\nserializer, while a request with `accept: text/plain` will trigger the\n`toString()` serializer.\n\nNote that the function signature for the error listener is identical for all\nemitted error events. The signature is as follows:\n\n```js\nfunction(req, res, err, callback) { }\n```\n\n-   `req` - the request object\n-   `res` - the response object\n-   `err` - the error object\n-   `callback` - a callback function to invoke\n\nWhen using this feature in conjunction with\n[restify-errors][45], restify will emit events\nfor all of the basic http errors:\n\n-   `400` - `BadRequestError`\n-   `401` - `UnauthorizedError`\n-   `402` - `PaymentRequiredError`\n-   `403` - `ForbiddenError`\n-   `404` - `NotFoundError`\n-   `405` - `MethodNotAllowedError`\n-   `406` - `NotAcceptableError`\n-   `407` - `ProxyAuthenticationRequiredError`\n-   `408` - `RequestTimeoutError`\n-   `409` - `ConflictError`\n-   `410` - `GoneError`\n-   `411` - `LengthRequiredError`\n-   `412` - `PreconditionFailedError`\n-   `413` - `RequestEntityTooLargeError`\n-   `414` - `RequesturiTooLargeError`\n-   `415` - `UnsupportedMediaTypeError`\n-   `416` - `RangeNotSatisfiableError` (node >= 4)\n-   `416` - `RequestedRangeNotSatisfiableError` (node 0.x)\n-   `417` - `ExpectationFailedError`\n-   `418` - `ImATeapotError`\n-   `422` - `UnprocessableEntityError`\n-   `423` - `LockedError`\n-   `424` - `FailedDependencyError`\n-   `425` - `UnorderedCollectionError`\n-   `426` - `UpgradeRequiredError`\n-   `428` - `PreconditionRequiredError`\n-   `429` - `TooManyRequestsError`\n-   `431` - `RequestHeaderFieldsTooLargeError`\n-   `500` - `InternalServerError`\n-   `501` - `NotImplementedError`\n-   `502` - `BadGatewayError`\n-   `503` - `ServiceUnavailableError`\n-   `504` - `GatewayTimeoutError`\n-   `505` - `HttpVersionNotSupportedError`\n-   `506` - `VariantAlsoNegotiatesError`\n-   `507` - `InsufficientStorageError`\n-   `509` - `BandwidthLimitExceededError`\n-   `510` - `NotExtendedError`\n-   `511` - `NetworkAuthenticationRequiredError`\n\nRestify will also emit the following events:\n\n### NotFound\n\nWhen a client request is sent for a URL that does not exist, restify\nwill emit this event. Note that restify checks for listeners on this\nevent, and if there are none, responds with a default 404 handler.\n\n### MethodNotAllowed\n\nWhen a client request is sent for a URL that exists, but not for the requested\nHTTP verb, restify will emit this event. Note that restify checks for listeners\non this event, and if there are none, responds with a default 405 handler.\n\n### VersionNotAllowed\n\nWhen a client request is sent for a route that exists, but does not\nmatch the version(s) on those routes, restify will emit this\nevent. Note that restify checks for listeners on this event, and if\nthere are none, responds with a default 400 handler.\n\n### UnsupportedMediaType\n\nWhen a client request is sent for a route that exist, but has a `content-type`\nmismatch, restify will emit this event. Note that restify checks for listeners\non this event, and if there are none, responds with a default 415 handler.\n\n\n## Types\n\n\n\n\n### Server~methodOpts\n\nServer method opts\n\nType: ([String][25] \\| [Regexp][46] \\| [Object][24])\n\n**Properties**\n\n-   `name` **[String][25]** a name for the route\n-   `path` **[String][25]** can be any String accepted by\n    [find-my-way][47]\n\n**Examples**\n\n```javascript\n// a static route\nserver.get('/foo', function(req, res, next) {});\n// a parameterized route\nserver.get('/foo/:bar', function(req, res, next) {});\n// a regular expression\nserver.get('/example/:file(^\\\\d+).png', function(req, res, next) {});\n// an options object\nserver.get({\n    path: '/foo',\n}, function(req, res, next) {});\n```\n\n[1]: #createserver\n\n[2]: #server\n\n[3]: #listen\n\n[4]: #close\n\n[5]: #get\n\n[6]: #head\n\n[7]: #post\n\n[8]: #put\n\n[9]: #patch\n\n[10]: #del\n\n[11]: #opts\n\n[12]: #pre\n\n[13]: #use\n\n[14]: #param\n\n[15]: #rm\n\n[16]: #address\n\n[17]: #inflightrequests\n\n[18]: #debuginfo\n\n[19]: #tostring\n\n[20]: #events\n\n[21]: #errors\n\n[22]: #types\n\n[23]: #servermethodopts\n\n[24]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object\n\n[25]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String\n\n[26]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean\n\n[27]: https://github.com/trentm/node-bunyan\n\n[28]: https://nodejs.org/api/buffer.html\n\n[29]: https://github.com/indutny/node-spdy\n\n[30]: https://nodejs.org/api/http2.html\n\n[31]: http://nodejs.org/api/https.html#https_https\n\n[32]: #server\n\n[33]: http://nodejs.org/docs/latest/api/net.html#net_server_listen_path_callback\n\n[34]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number\n\n[35]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function\n\n[36]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypeError\n\n[37]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined\n\n[38]: http://nodejs.org/docs/latest/api/net.html#net_event_close\n\n[39]: #servermethodopts\n\n[40]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array\n\n[41]: http://expressjs.com/guide.html#route-param%20pre-conditions\n\n[42]: http://nodejs.org/docs/latest/api/net.html#net_server_address\n\n[43]: http://nodejs.org/docs/latest/api/http.html#http_class_http_server\n\n[44]: https://nodejs.org/api/domain.html\n\n[45]: https://github.com/restify/errors\n\n[46]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp\n\n[47]: https://github.com/delvedor/find-my-way\n"
  },
  {
    "path": "docs/api/formatters-usage.md",
    "content": "Restify comes bundled with a selection of useful formatters that prepare your\nresponses for being sent over the wire, but you are free to include your own!\n\n```js\nfunction formatGraphQL(req, res, body) {\n    var data = body;\n    /* Do a thing to data */\n    res.setHeader('Content-Length', Buffer.byteLength(data));\n    return data;\n}\n\nvar server = restify.createServer({\n    formatters: {\n        'application/graphql': formatGraphQL\n    }\n});\n\n// Your application now supports content-type 'application/graphql'\n```\n"
  },
  {
    "path": "docs/api/plugins-usage.md",
    "content": "Restify comes bundled with a selection of useful plugins. These are accessible\noff of `restify.plugins` and `restify.pre`.\n\n```js\nvar server = restify.createServer();\nserver.use(restify.plugins.acceptParser(server.acceptable));\nserver.use(restify.plugins.authorizationParser());\nserver.use(restify.plugins.dateParser());\nserver.use(restify.plugins.queryParser());\nserver.use(restify.plugins.jsonp());\nserver.use(restify.plugins.gzipResponse());\nserver.use(restify.plugins.bodyParser());\nserver.use(restify.plugins.requestExpiry());\nserver.use(restify.plugins.throttle({\n  burst: 100,\n  rate: 50,\n  ip: true,\n  overrides: {\n    '192.168.1.1': {\n      rate: 0,        // unlimited\n      burst: 0\n    }\n  }\n}));\nserver.use(restify.plugins.conditionalRequest());\n"
  },
  {
    "path": "docs/api/request-events.md",
    "content": "### restifyDone\n\nAfter request has been fully serviced, an `restifyDone` event is fired.\nrestify considers a request to be fully serviced when either:\n\n1) The handler chain for a route has been fully completed\n2) An error was returned to `next()`, and the corresponding error events have\n   been fired for that error type\n\nThe signature for the `restifyDone` event is as follows:\n\n```js\nfunction(route, error) { }\n```\n\n* `route` - the route object that serviced the request\n* `error` - the error passed to `next()`, if applicable\n\nNote that when the server automatically responds with a\n`NotFound`/`MethodNotAllowed`/`VersionNotAllowed`, this event will still be\nfired.\n"
  },
  {
    "path": "docs/api/request-log.md",
    "content": "If you are using the [RequestLogger](#bundled-plugins) plugin, the child logger\nwill be available on `req.log`:\n\n```js\nfunction myHandler(req, res, next) {\n  var log = req.log;\n\n  log.debug({params: req.params}, 'Hello there %s', 'foo');\n}\n```\n\nThe child logger will inject the request's UUID in the `req._id` attribute of\neach log statement. Since the logger lasts for the life of the request, you can\nuse this to correlate statements for an individual request across any number of\nseparate handlers."
  },
  {
    "path": "docs/api/server-errors.md",
    "content": "Restify handles errors as first class citizens. When an error object is passed\nto the `next()` function, an event is emitted on the server object, and the\nerror object will be serialized and sent to the client. An error object is any\nobject that passes an `instanceof Error` check.\n\nBefore the error object is sent to the client, the server will fire an event\nusing the name of the error, without the `Error` part of the name. For example,\ngiven an `InternalServerError`, the server will emit an `InternalServer` event.\nThis creates opportunities to do logging, metrics, or payload mutation based on\nthe type of error. For example:\n\n```js\nvar errs = require('restify-errors');\n\nserver.get('/', function(req, res, next) {\n    return next(new errs.InternalServerError('boom!'));\n});\n\nserver.on('InternalServer', function(req, res, err, callback) {\n    // before the response is sent, this listener will be invoked, allowing\n    // opportunities to do metrics capturing or logging.\n    myMetrics.capture(err);\n    // invoke the callback to complete your work, and the server will send out\n    // a response.\n    return callback();\n});\n```\n\nInside the error event listener, it is also possible to change the serialization\nmethod of the error if desired. To do so, simply implement a custom\n`toString()` or `toJSON()`. Depending on the content-type and formatter being\nused for the response, one of the two serializers will be used. For example,\ngiven the folllwing example:\n\n```js\nserver.on('restifyError', function(req, res, err, callback) {\n    err.toJSON = function customToJSON() {\n        return {\n            name: err.name,\n            message: err.message\n        };\n    };\n    err.toString = function customToString() {\n        return 'i just want a string';\n    };\n    return callback();\n});\n```\n\nA request with an `accept: application/json` will trigger the `toJSON()`\nserializer, while a request with `accept: text/plain` will trigger the\n`toString()` serializer.\n\nNote that the function signature for the error listener is identical for all\nemitted error events. The signature is as follows:\n\n```js\nfunction(req, res, err, callback) { }\n```\n\n* `req` - the request object\n* `res` - the response object\n* `err` - the error object\n* `callback` - a callback function to invoke\n\n\nWhen using this feature in conjunction with\n[restify-errors](https://github.com/restify/errors), restify will emit events\nfor all of the basic http errors:\n\n* `400` - `BadRequestError`\n* `401` - `UnauthorizedError`\n* `402` - `PaymentRequiredError`\n* `403` - `ForbiddenError`\n* `404` - `NotFoundError`\n* `405` - `MethodNotAllowedError`\n* `406` - `NotAcceptableError`\n* `407` - `ProxyAuthenticationRequiredError`\n* `408` - `RequestTimeoutError`\n* `409` - `ConflictError`\n* `410` - `GoneError`\n* `411` - `LengthRequiredError`\n* `412` - `PreconditionFailedError`\n* `413` - `RequestEntityTooLargeError`\n* `414` - `RequesturiTooLargeError`\n* `415` - `UnsupportedMediaTypeError`\n* `416` - `RangeNotSatisfiableError` (node >= 4)\n* `416` - `RequestedRangeNotSatisfiableError` (node 0.x)\n* `417` - `ExpectationFailedError`\n* `418` - `ImATeapotError`\n* `422` - `UnprocessableEntityError`\n* `423` - `LockedError`\n* `424` - `FailedDependencyError`\n* `425` - `UnorderedCollectionError`\n* `426` - `UpgradeRequiredError`\n* `428` - `PreconditionRequiredError`\n* `429` - `TooManyRequestsError`\n* `431` - `RequestHeaderFieldsTooLargeError`\n* `500` - `InternalServerError`\n* `501` - `NotImplementedError`\n* `502` - `BadGatewayError`\n* `503` - `ServiceUnavailableError`\n* `504` - `GatewayTimeoutError`\n* `505` - `HttpVersionNotSupportedError`\n* `506` - `VariantAlsoNegotiatesError`\n* `507` - `InsufficientStorageError`\n* `509` - `BandwidthLimitExceededError`\n* `510` - `NotExtendedError`\n* `511` - `NetworkAuthenticationRequiredError`\n\n\nRestify will also emit the following events:\n\n### NotFound\n\nWhen a client request is sent for a URL that does not exist, restify\nwill emit this event. Note that restify checks for listeners on this\nevent, and if there are none, responds with a default 404 handler.\n\n### MethodNotAllowed\n\nWhen a client request is sent for a URL that exists, but not for the requested\nHTTP verb, restify will emit this event. Note that restify checks for listeners\non this event, and if there are none, responds with a default 405 handler.\n\n### VersionNotAllowed\n\nWhen a client request is sent for a route that exists, but does not\nmatch the version(s) on those routes, restify will emit this\nevent. Note that restify checks for listeners on this event, and if\nthere are none, responds with a default 400 handler.\n\n### UnsupportedMediaType\n\nWhen a client request is sent for a route that exist, but has a `content-type`\nmismatch, restify will emit this event. Note that restify checks for listeners\non this event, and if there are none, responds with a default 415 handler."
  },
  {
    "path": "docs/api/server-events.md",
    "content": "In additional to emitting all the events from node's\n[http.Server](http://nodejs.org/docs/latest/api/http.html#http_class_http_server),\nrestify servers also emit a number of additional events that make building REST\nand web applications much easier.\n\n### restifyError\n\nThis event is emitted following all error events as a generic catch all. It is\nrecommended to use specific error events to handle specific errors, but this\nevent can be useful for metrics or logging. If you use this in conjunction with\nother error events, the most specific event will be fired first, followed by\nthis one:\n\n```js\nserver.get('/', function(req, res, next) {\n  return next(new InternalServerError('boom'));\n});\n\nserver.on('InternalServer', function(req, res, err, callback) {\n  // this will get fired first, as it's the most relevant listener\n  return callback();\n});\n\nserver.on('restifyError', function(req, res, err, callback) {\n  // this is fired second.\n  return callback();\n});\n```\n\n\n### after\n\nAfter each request has been fully serviced, an `after` event is fired. This\nevent can be hooked into to handle audit logs and other metrics. Note that\nflushing a response does not necessarily correspond with an `after` event.\nrestify considers a request to be fully serviced when either:\n\n1) The handler chain for a route has been fully completed\n2) An error was returned to `next()`, and the corresponding error events have\n   been fired for that error type\n\nThe signature is for the after event is as follows:\n\n```js\nfunction(req, res, route, error) { }\n```\n\n* `req` - the request object\n* `res` - the response object\n* `route` - the route object that serviced the request\n* `error` - the error passed to `next()`, if applicable\n\nNote that when the server automatically responds with a\nNotFound/MethodNotAllowed/VersionNotAllowed, this event will still be fired.\n\n\n### pre\n\nBefore each request has been routed, a `pre` event is fired. This event can be\nhooked into handle audit logs and other metrics. Since this event fires\n*before* routing has occured, it will fire regardless of whether the route is\nsupported or not, e.g. requests that result in a `404`.\n\nThe signature for the `pre` event is as follows:\n\n```js\nfunction(req, res) {}\n```\n* `req` - the request object\n* `res` - the response object\n\nNote that when the server automatically responds with a\nNotFound/MethodNotAllowed/VersionNotAllowed, this event will still be fired.\n\n\n### routed\n\nA `routed` event is fired after a request has been routed by the router, but\nbefore handlers specific to that route has run.\n\nThe signature for the `routed` event is as follows:\n\n```js\nfunction(req, res, route) {}\n```\n\n* `req` - the request object\n* `res` - the response object\n* `route` - the route object that serviced the request\n\nNote that this event will *not* fire if a requests comes in that are not\nroutable, i.e. one that would result in a `404`.\n\n\n### uncaughtException\n\nIf the restify server was created with `handleUncaughtExceptions: true`,\nrestify will leverage [domains](https://nodejs.org/api/domain.html) to handle\nthrown errors in the handler chain. Thrown errors are a result of an explicit\n`throw` statement, or as a result of programmer errors like a typo or a null\nref. These thrown errors are caught by the domain, and will be emitted via this\nevent. For example:\n\n```js\nserver.get('/', function(req, res, next) {\n    res.send(x);  // this will cause a ReferenceError\n    return next();\n});\n\nserver.on('uncaughtException', function(req, res, route, err, callback) {\n    // this event will be fired, with the error object from above:\n    // ReferenceError: x is not defined\n    res.send(504, 'boom');\n    callback();\n});\n```\n\nIf you listen to this event, you __must__:\n\n1. send a response to the client _and_\n2. call the callback function passed as the fourth argument of the event listener\n\nThis behavior is different from the standard error events. If you do not listen\nto this event, restify's default behavior is to call `res.send()` with the error\nthat was thrown.\n\nThe signature is for the after event is as follows:\n\n```js\nfunction(req, res, route, error) { }\n```\n\n* `req` - the request object\n* `res` - the response object\n* `route` - the route object that serviced the request\n* `error` - the error passed to `next()`, if applicable\n\n### close\n\nEmitted when the server closes."
  },
  {
    "path": "docs/config/formatters.yaml",
    "content": "toc:\n  - name: Usage\n    file: ../api/formatters-usage.md\n  - name: Types\n    children:\n      - formatter\n  - name: Included formatters\n    description: |\n      restify comes pre-loaded with a standard set of formatters for common\n      use cases.\n    children:\n      - formatText\n      - formatJSON\n      - formatJSONP\n      - formatBinary\n"
  },
  {
    "path": "docs/config/plugins.yaml",
    "content": "toc:\n  - name: Usage\n    file: ../api/plugins-usage.md\n  - name: server.pre() plugins\n    description: |\n      This module includes various pre plugins, which are intended to be used prior\n      to routing of the URL. To use a plugin before routing, use the `server.pre()`\n      method.\n    children:\n      - context\n      - dedupeSlashes\n      - pause\n      - sanitizePath\n      - reqIdHeaders\n      - strictQueryParams\n      - userAgentConnection\n  - name: server.use() plugins\n    children:\n      - acceptParser\n      - authorizationParser\n      - dateParser\n      - queryParser\n      - jsonp\n      - bodyParser\n      - requestLogger\n      - gzipResponse\n      - serveStatic\n      - serveStaticFiles\n      - throttle\n      - requestExpiry\n      - inflightRequestThrottle\n      - cpuUsageThrottle\n      - conditionalHandler\n      - conditionalRequest\n      - auditLogger\n      - metrics\n  - name: Types\n    children:\n      - metrics~callback\n"
  },
  {
    "path": "docs/config/request.yaml",
    "content": "toc:\n  - Request\n  - name: Events\n    file: ../api/server-events.md\n  - name: Log\n    file: ../api/request-log.md\n"
  },
  {
    "path": "docs/config/server.yaml",
    "content": "toc:\n  - createServer\n  - Server\n  - name: Events\n    file: ../api/server-events.md\n  - name: Errors\n    file: ../api/server-errors.md\n  - name: Types\n    children:\n      - Server~methodOpts\n"
  },
  {
    "path": "docs/guides/4TO5GUIDE.md",
    "content": "---\ntitle: restify 4.x to 5.x migration guide\npermalink: /docs/4to5/\n---\n\n\n## Introduction\n\nrestify 5.0 is finally here! And a great big thank you to all of our\ncontributors. 5.x fixes a ton of bugs, adds some new features, and introduces\nsome breaking changes. This guide helps make sense of all the major changes\nthat have happened since the last 4.x release. A more detailed change log can\nbe found in CHANGES.md.\n\n\n#### queryParser() and bodyParser()\n\nBy default, queryParser and bodyParser no longer map req.query and req.body to\nreq.params. To get the old behavior, please enable the `mapParams` behavior\nwith these plugins.\n\n\n### restify-errors\n\nErrors, which used to be available on the `restify.errors` namespace, now live\nin their own [repository](https://github.com/restify/errors) and are published\n[independently on npm](https://www.npmjs.com/package/restify-errors).\n`restify-errors` can be used independently of restify in any of your other\nprojects for customizable error classes and chained errors.\n\n\n### restify-clients\n\nAll restify clients have been broken out into their own\n[repository](https://github.com/restify/clients), and are published\n[independently on npm](https://www.npmjs.com/package/restify-clients).\n\n\n### server.on('restifyError', ...)\n\nrestify now emits a generic error event. This error event will be fired for all\nerrors passed to `next()`. If you have specific listeners attached for a class\nof error, the most specific one will be fired first, with the generic one being\nfired last.\n\n```js\n// in some route, create a 500\nserver.get('/', function(req, res, next) {\n  return next(new InternalServerError('oh noes!'));\n  // this will hit the InternalServerError FIRST, allowing you to handle it some fashion,\n  // before firing restifyError event. the semantics of the generic handler means it should\n  // always be fired, but doesn't mean we shouldn't allow you to handle it first within\n  // the error handler they care about. this is only possible if we fire events in serial.\n});\n\n// handle 500s\nserver.on('InternalServer', function(req, res, err, cb) {\n  // this event is fired first. you can annotate errors here by saying\n  // err.handled = true, because we must ALWAYS fire the generic handler after.\n  err.handled = true;\n  return cb();\n});\n\n// generic error handler\nserver.on('restifyError', function(req, res, err, cb) {\n  // this event is fired last. do some generic metrics/logging\n  if (!err.handled) {\n    // do something\n  }\n  return cb();\n});\n```\n\n### server.on('redirect', ...)\n\nrestify now emits a redirect event when `res.redirect()` is used. The event is\nfired with the new location of the redirect.\n\n```js\nSERVER.on('redirect', function (newLocation) {\n  // newLocation is the new url we redirected to.\n});\n```\n\n### server.on('NotFound', ...)\n### server.on('MethodNotAllowed', ...)\n### server.on('VersionNotAllowed', ...)\n### server.on('UnsupportedMediaType', ...)\n\nrestify's error events for these four types of errors have now been normalized\nto act like other error events. Previously, listening to these events would\nrequire you to send a response. It has now been normalized to work like the\nother error events:\n\n```js\nserver.on('NotFound', function(req, res, err, cb) {\n  // do some logging or metrics collection here. if you want to send a custom\n  // response, you can do so here by setting the response on the body of the\n  // error object.\n  err.body = \"whoops! can't find your stuff!\"; // the body of the error becomes the response\n  return cb();\n});\n```\n\n\n### CORS\n\nCORS has been removed from restify core. For CORS support, please use\n[TabDigital's](https://github.com/TabDigital/restify-cors-middleware) plugin.\n\n### strict routing\n\nStrict routing is now supported via the `strictRouting` option. This allows\ndifferentiation of routes with trailing slashes. The default value is `false`,\nwhich mimics the behavior in 4.x which is to strip trailing slashes.\n\n```js\nvar server = restify.createServer({\n    strictRouting: true\n});\n// these two routes are distinct with strictRouting option\nserver.get('/foo/', function(req, res, next) { });\nserver.get('/foo', function(req, res, next) { });\n```\n\n\n### res.sendRaw()\n\nrestify has a concept of formatters, where each formatter is executed to format\na the content of a response before sending it out. A new method,\n`res.sendRaw()`, has been added which allows bypassing of the formatters in\nscenarios where you have preformatted content (pre-gzipped, pre-JSON\nstringified, etc.). `sendRaw` has the same signature as `send`.\n\n\n### Removal of undocumented APIs\n\nPrevious versions of restify had some undocumented exports on the main object.\nThese have been removed as of 5.x. These include:\n\n* `restify.CORS` - due to removal of CORS from core\n* `restify.httpDate` - undocumented\n* `restify.realizeUrl` - undocumented\n\n\n### next(err) & res.send(err)\n\nTo help reduce unintentional exposure of errors to the client, restify no\nlonger does special JSON serialization for Error objects. For example:\n\n```js\nserver.get('/sendErr', function(req, res, next) {\n  res.send(new Error('where is my msg?'));\n  return next();\n});\n\nserver.get('/nextErr', function(req, res, next) {\n  return next(new Error('where is my msg?'));\n});\n```\n\n```sh\n$ curl -is localhost:8080/sendErr\nHTTP/1.1 410 Gone\nContent-Type: application/json\nContent-Length: 37\nDate: Fri, 03 Jun 2016 20:17:48 GMT\nConnection: keep-alive\n\n{}\n\n$ curl -is localhost:8080/nextErr\nHTTP/1.1 410 Gone\nContent-Type: application/json\nContent-Length: 37\nDate: Fri, 03 Jun 2016 20:17:48 GMT\nConnection: keep-alive\n\n{}\n```\n\nThe response is an empty object because `JSON.stringify(err)` returns an empty\nobject. In order to get properly serialized Errors, the preferred method is to\nuse restify-errors, which will have defined `toJSON` methods. Alternatively,\nif you have custom Error classes, you can define a `toJSON` method which is\ninvoked when your Error is being stringified. If you have many custom error\ntypes, consider using restify-errors to help you create and manage them easily.\nLastly, you can use restify-errors to opt-in to automatic `toJSON`\nserialization:\n\n```js\nvar errs = require('restify-errors');\n\nserver.get('/', function(req, res, next) {\n  res.send(new errs.GoneError('gone girl'));\n  return next();\n});\n```\n\n```sh\n$ curl -is localhost:8080/\nHTTP/1.1 410 Gone\nContent-Type: application/json\nContent-Length: 37\nDate: Fri, 03 Jun 2016 20:17:48 GMT\nConnection: keep-alive\n\n{\"code\":\"Gone\",\"message\":\"gone girl\"}\n```\n\n## Deprecations\n\nThe following are still currently supported, but are on life support and may be\nremoved in future versions. Usage of these features will cause restify to spit\nout deprecation warnings in the logs.\n\n\n### domains\n\nIn 4.x, restify utilized domains by default. Any errors captured by the domain\ncould be handled to via the `server.on('uncaughtException', ...)` event.\nHowever, it was not immediately obvious that this behavior was happening by\ndefault, and many errors often went unhandled or unnoticed by end users.\n\nWith domains being deprecated, we've opted to turn domains off by default. If\nyou want to use domains, you can turn them back on via the\n`handleUncaughtExceptions` option when you create the server:\n\n```js\nvar server = restify.createServer({\n    handleUncaughtExceptions: true\n});\n```\n\n### next.ifError()\n\nThe `next.ifError()` feature leveraged domains under the hood. This feature is\nalso deprecated, and will only be available to you if the\n`handleUncaughtExceptions` flag is set to true.\n"
  },
  {
    "path": "docs/guides/6to7guide.md",
    "content": "---\ntitle: restify 6.x to 7.x migration guide\npermalink: /docs/6to7/\n---\n\n## Introduction\n\nrestify `7.x` comes with a completely new router and middleware logic that\nbrings significant performance improvement to your application.\nFrom `v7.0.0` restify uses the Radix Tree based\n[find-my-way](https://github.com/delvedor/find-my-way) package as a router\nbackend.\n\n## Breaking Changes\n\n### Server returns `RequestCloseError` instead of `RequestAbortedError`\n\nServer returns `RequestCloseError` instead of `RequestAbortedError` in the case\nof the request was terminated by the client for some reason.\n\nThe new version of restify never returns `RequestAbortedError`.\n\n### Non-strict routing is gone\n\nOption `strictRouting` is removed `createServer({ strictRouting: false })`.\nStrict routing is the new default.\n\n### Path trailing slash at the end\n\n`/path` and `/path/` are not the same thing in restify `v7.x`.\nUse `ignoreTrailingSlash: true` server option if you don't want to differentiate\nthem from each other.\n\n### Path must start with `/`\n\nIn restify 7.x path must start with a `/`.\nFor example `server.get('foo')` is invalid, change it to `server.get('/foo')`.\n\nIf you use [enroute](https://github.com/restify/enroute) be sure\nthat you updated it to the latest version.\n\n### Different `RegExp` usage in router path and wildcards\n\nrestify's new router backend\n[find-my-way](https://github.com/delvedor/find-my-way) has limited RegExp\nsupport.\n\n#### Guide to define paths\n\nTo register a **parametric** path, use the *colon* before the parameter name.\nFor **wildcard** use the *star*.\n*Remember that static routes are always inserted before parametric and wildcard.*\n\n```js\n// parametric\nserver.get('GET', '/example/:userId', (req, res, next) => {}))\nserver.get('GET', '/example/:userId/:secretToken', (req, res, next) => {}))\n\n// wildcard\nserver.get('GET', '/example/*', (req, res, next) => {}))\n```\n\nRegular expression routes are supported as well, but pay attention, RegExp are\nvery expensive in term of performance!\n\n```js\n// parametric with RegExp\nserver.get('GET', '/example/:file(^\\\\d+).png', () => {}))\n```\n\nRegExp path chunks needs to be between parentheses.\n\nIt's possible to define more than one parameter within the same couple of slash\n(\"/\"). Such as:\n\n```js\nserver.get('/example/near/:lat-:lng/radius/:r', (req, res, next) => {}))\n```\n\n*Remember in this case to use the dash (\"-\") as parameters separator.*\n\nFinally it's possible to have multiple parameters with RegExp.\n\n```js\nserver.get('/example/at/:hour(^\\\\d{2})h:minute(^\\\\d{2})m', (req, res, next) => {\n  // req.params => { hour: 12, minute: 15 }\n}))\n```\nIn this case as parameter separator it's possible to use whatever character is\nnot matched by the regular expression.\n\nHaving a route with multiple parameters may affect negatively the performance,\nso prefer single parameter approach whenever possible, especially on routes\nwhich are on the hot path of your application.\n\nFore more info see: https://github.com/delvedor/find-my-way\n\n### Remove already deprecated `next.ifError`\n\n`next.ifError(err)` is not available anymore.\n\n### Disable DTrace probes by default\n\nDTrace probes come with some performance impact that's fine for the sake of\nobservability but you don't have to use them: they are disabled by default.\n\n### Change in calling `next` multiple times\n\nEarlier `restify` automatically prevented calling the `next()` more than once.\nIn the new version this behaviour is disabled by default, but you can activate\nit with the `onceNext` property.\n\nThe behaviour of the `strictNext` option is unchanged.\nWhich means `strictNext` enforces `onceNext` option.\n\n```js\nvar server = restify.createServer({ onceNext: true })\nserver.use(function (req, req, next) {\n    next();\n    next();\n});\n// -> fine\n\nvar server = restify.createServer({ strictNext: true })\nserver.use(function (req, req, next) {\n    next();\n    next();\n});\n// -> throws an Error\n```\n\n### Router versioning and content type\n\n`accept-version` and `accept` based conditional routing moved to the\n`conditionalHandler` plugin, see docs or example:\n\n```js\nvar server = restify.createServer()\n\nserver.use(restify.plugins.conditionalHandler({\n   contentType: 'application/json',\n   version: '1.0.0'\n   handler: function (req, res, next) {\n       next();\n   })\n});\n\nserver.get('/hello/:name', restify.plugins.conditionalHandler([\n  {\n     version: '1.0.0',\n     handler: function(req, res, next) { res.send('1.x') }\n  },\n  {\n     version: ['1.5.0', '2.0.0'],\n     handler: function(req, res, next) { res.send('1.5.x, 2.x') }\n  },\n  {\n     version: '3.0.0',\n     contentType: ['text/html', 'text/html']\n     handler: function(req, res, next) { res.send('3.x, text') }\n  },\n  {\n     version: '3.0.0',\n     contentType: 'application/json'\n     handler: function(req, res, next) { res.send('3.x, json') }\n  }\n]);\n\n// 'accept-version': '^1.1.0' => 1.5.x, 2.x'\n// 'accept-version': '3.x', accept: 'application/json' => '3.x, json'\n```\n\n### After event fires when both request is flushed and last handler is finished\n\nIn 7.x `after` event fires after both request is flushed\nand last handler is finished.\n\n### Metrics plugin latency\n\nIn 7.x Metrics plugin's `latency` is calculated when the request is\nfully flushed. Earlier it was calculated when the last handler finished.\n\nTo address the previous use-cases, new timings were added to the metrics plugin:\n\n - `metrics.totalLatency` both request is flushed and all handlers finished\n - `metrics.preLatency` pre handlers latency\n - `metrics.useLatency` use handlers latency\n - `metrics.routeLatency` route handlers latency\n"
  },
  {
    "path": "docs/guides/8to9guide.md",
    "content": "---\ntitle: restify 8.x to 9.x migration guide\npermalink: /docs/8to9/\n---\n\n## Introduction\n\nRestify `9.x` comes with `async/await` support, `pino` and more!\n\n## Breaking Changes\n\n### Drops support for Node.js `8.x`\n\nRestify requires Node.js version `>=10.0.0`. \n\n### Async/await support\n\n`async/await` basic support for `.pre()`, `.use()` and route handlers.\n\n#### Example\n\n```js\nconst restify = require('restify');\n\nconst server = restify.createServer({});\n\nserver.use(async (req, res) => {\n  req.something = await doSomethingAsync();\n});\n\nserver.get('/params', async (req, res) => {\n  const value = await asyncOperation(req.something);\n   res.send(value);\n});\n```\n\n#### Middleware API (`.pre()` and `.use()`)\n\n```js\nserver.use(async (req, res) => {\n  req.something = await doSomethingAsync();\n});\n```\n- `fn.length === 2` (arity 2);\n- `fn instanceof AsyncFunction`;\n- if the async function resolves, it calls `next()`;\n- any value returned by the async function will be discarded;\n- if it rejects with an `Error` instance it calls `next(err)`;\n- if it rejects with anything else it wraps in a `AsyncError` and calls `next(err)`;\n\n#### Route handler API\n\n```js\nserver.get('/something', async (req, res) => {\n  const someData = await fetchSomeDataAsync();\n  res.send({ data: someData });\n});\n```\n- `fn.length === 2` (arity 2);\n- `fn instanceof AsyncFunction`;\n- if the async function resolves without a value, it calls `next()`;\n- if the async function resolves with a string value, it calls `next(string)` (re-routes*);\n- if the async function resolves with a value other than string, it calls `next(any)`;\n- if it rejects with an `Error` instance it calls `next(err)`;\n- if it rejects with anything else it wraps in a `AsyncError` and calls `next(err)` (error-handing**);\n\n##### (*) Note about re-routing:\nThe `8.x` API allows re-routing when calling `next()` with a string value. If the string matches a valid route,\nit will re-route to the given handler. The same is valid for resolving a async function. If the value returned by\nthe async function is a string, it will try to re-route to the given handler.\n\n##### (**) Note about error handling:\nAlthough it is recommended to always reject with an instance of Error, in a async function it is possible to\nthrow or reject without returning an `Error` instance or even anything at all. In such cases, the value rejected\nwill be wrapped on a `AsyncError`.\n\n### Handler arity check\nHandlers expecting 2 or fewer parameters added to a `.pre()`, `.use()` or route chain must be async functions, as:\n\n```js\nserver.use(async (req, res) => {\n  req.something = await doSomethingAsync();\n});\n```\n\nHandlers expecting more than 2 parameters shouldn't be async functions, as:\n\n````js\n// This middleware will be rejected and restify will throw\nserver.use(async (req, res, next) => {\n  doSomethingAsync(function callback(val) {\n    req.something = val;\n    next();\n  });\n});\n````\n\n### Remove `RequestCaptureStream`\n\nRemoves `RequestCaptureStream` from Restify core.\n\n### Use `Pino` as default logger (removes dependency on `Bunyan`)\n\n[Pino](https://github.com/pinojs/pino) is well maintained, performance-focused, \ncompatible API. It does have a few key differences from `Bunyan`:\n\n- As a performance optimization, it stores bindings a single serialized `string`, \nwhile `Bunyan` stores them as object keys;\n- It uses a `setter` to set the log level, `Bunyan` uses a method;\n- It only accepts one stream. If you need the multi-stream functionality, you\nmust use [pino-multistream](https://github.com/pinojs/pino-multi-stream).  \n"
  },
  {
    "path": "docs/guides/client.md",
    "content": "---\ntitle: Client Guide\npermalink: /docs/client-guide/\n---\n\nThere are actually three separate clients shipped in restify:\n\n* **JsonClient:** sends and expects application/json\n* **StringClient:** sends url-encoded request and expects text/plain\n* **HttpClient:** thin wrapper over node's http/https libraries\n\nThe idea being that if you want to support \"typical\" control-plane\nREST APIs, you probably want the `JsonClient`, or if you're using some\nother serialization (like XML) you'd write your own client that\nextends the `StringClient`. If you need streaming support, you'll need\nto do some work on top of the `HttpClient`, as `StringClient` and\nfriends buffer requests/responses.\n\nAll clients support retry with exponential backoff for getting a TCP\nconnection; they do not perform retries on 5xx error codes like\nprevious versions of the restify client.  You can set `retry` to `false` to\ndisable this logic altogether.  Also, all clients support a `connectTimeout`\nfield, which is use *on each retry*.  The default is not to set a\n`connectTimeout`, so you end up with the node.js socket defaults.\n\nHere's an example of hitting the\n[Joyent CloudAPI](https://apidocs.joyent.com/cloudapi/):\n\n```js\nvar restify = require('restify-clients');\n\n// Creates a JSON client\nvar client = restify.createJsonClient({\n  url: 'https://us-east-1.api.joyent.com'\n});\n\n\nclient.basicAuth('$login', '$password');\nclient.get('/my/machines', function(err, req, res, obj) {\n  assert.ifError(err);\n\n  console.log(JSON.stringify(obj, null, 2));\n});\n```\n\nAs a short-hand, a client can be initialized with a string-URL rather than\nan options object:\n\n```js\nvar restify = require('restify-clients');\n\nvar client = restify.createJsonClient('https://us-east-1.api.joyent.com');\n```\n\nNote that all further documentation refers to the \"short-hand\" form of\nmethods like `get/put/del` which take a string path.  You can also\npass in an object to any of those methods with extra params (notably\nheaders):\n\n```js\nvar options = {\n  path: '/foo/bar',\n  headers: {\n    'x-foo': 'bar'\n  },\n  retry: {\n    'retries': 0\n  },\n  agent: false\n};\n\nclient.get(options, function(err, req, res) { .. });\n```\n\nIf you need to interpose additional headers in the request before it is sent on\nto the server, you can provide a synchronous callback function as the\n`signRequest` option when creating a client.  This is particularly useful with\n[node-http-signature](https://github.com/joyent/node-http-signature), which\nneeds to attach a cryptographic signature of selected outgoing headers.  If\nprovided, this callback will be invoked with a single parameter: the outgoing\n`http.ClientRequest` object.\n\n## JsonClient\n\nThe JSON Client is the highest-level client bundled with restify-clients; it\nexports a set of methods that map directly to HTTP verbs.  All\ncallbacks look like `function(err, req, res, [obj])`, where `obj` is\noptional, depending on if content was returned. HTTP status codes are\nnot interpreted, so if the server returned 4xx or something with a\nJSON payload, `obj` will be the JSON payload.  `err` however will be\nset if the server returned a status code >= 400 (it will be one of the\nrestify HTTP errors).  If `obj` looks like a `RestError`:\n\n```js\n{\n  \"code\": \"FooError\",\n  \"message\": \"some foo happened\"\n}\n```\n\nthen `err` gets \"upconverted\" into a `RestError` for you.  Otherwise\nit will be an `HttpError`.\n\n### createJsonClient(options)\n\n```js\nvar client = restify.createJsonClient({\n  url: 'https://api.us-east-1.joyent.com',\n  version: '*'\n});\n```\n\nOptions:\n\n|Name|Type|Description|\n|----|----|-----------|\n|accept|String|Accept header to send|\n|connectTimeout|Number|Amount of time to wait for a socket|\n|requestTimeout|Number|Amount of time to wait for the request to finish|\n|dtrace|Object|node-dtrace-provider handle|\n|gzip|Object|Will compress data when sent using `content-encoding: gzip`|\n|headers|Object|HTTP headers to set in all requests|\n|log|Object|[pino](https://github.com/pinojs/pino) instance|\n|retry|Object|options to provide to node-retry;\"false\" disables retry; defaults to 4 retries|\n|signRequest|Function|synchronous callback for interposing headers before request is sent|\n|url|String|Fully-qualified URL to connect to|\n|userAgent|String|user-agent string to use; restify inserts one, but you can override it|\n|version|String|semver string to set the accept-version|\n\n### get(path, callback)\n\nPerforms an HTTP get; if no payload was returned, `obj` defaults to\n`{}` for you (so you don't get a bunch of null pointer errors).\n\n```js\nclient.get('/foo/bar', function(err, req, res, obj) {\n  assert.ifError(err);\n  console.log('%j', obj);\n});\n```\n\n### head(path, callback)\n\nJust like `get`, but without `obj`:\n\n```js\nclient.head('/foo/bar', function(err, req, res) {\n  assert.ifError(err);\n  console.log('%d -> %j', res.statusCode, res.headers);\n});\n```\n\n### post(path, object, callback)\n\nTakes a complete object to serialize and send to the server.\n\n```js\nclient.post('/foo', { hello: 'world' }, function(err, req, res, obj) {\n  assert.ifError(err);\n  console.log('%d -> %j', res.statusCode, res.headers);\n  console.log('%j', obj);\n});\n```\n\n### put(path, object, callback)\n\nJust like `post`:\n\n```js\nclient.put('/foo', { hello: 'world' }, function(err, req, res, obj) {\n  assert.ifError(err);\n  console.log('%d -> %j', res.statusCode, res.headers);\n  console.log('%j', obj);\n});\n```\n\n### del(path, callback)\n\n`del` doesn't take content, since you know, it should't:\n\n```js\nclient.del('/foo/bar', function(err, req, res) {\n  assert.ifError(err);\n  console.log('%d -> %j', res.statusCode, res.headers);\n});\n```\n\n## StringClient\n\n`StringClient` is what `JsonClient` is built on, and provides a base\nfor you to write other buffering/parsing clients (like say an XML\nclient). If you need to talk to some \"raw\" HTTP server, then\n`StringClient` is what you want, as it by default will provide you\nwith content uploads in `application/x-www-form-url-encoded` and\ndownloads as `text/plain`.  To extend a `StringClient`, take a look at\nthe source for `JsonClient`. Effectively, you extend it, and set the\nappropriate options in the constructor and implement a `write` (for\nput/post) and `parse` method (for all HTTP bodies), and that's it.\n\n### createStringClient(options)\n\n```js\nvar client = restify.createStringClient({\n  url: 'https://example.com'\n})\n```\n\n### get(path, callback)\n\nPerforms an HTTP get; if no payload was returned, `data` defaults to\n`''` for you (so you don't get a bunch of null pointer errors).\n\n```js\nclient.get('/foo/bar', function(err, req, res, data) {\n  assert.ifError(err);\n  console.log('%s', data);\n});\n```\n\n### head(path, callback)\n\nJust like `get`, but without `data`:\n\n```js\nclient.head('/foo/bar', function(err, req, res) {\n  assert.ifError(err);\n  console.log('%d -> %j', res.statusCode, res.headers);\n});\n```\n\n### post(path, object, callback)\n\nTakes a complete object to serialize and send to the server.\n\n```js\nclient.post('/foo', { hello: 'world' }, function(err, req, res, data) {\n  assert.ifError(err);\n  console.log('%d -> %j', res.statusCode, res.headers);\n  console.log('%s', data);\n});\n```\n\n### put(path, object, callback)\n\nJust like `post`:\n\n```js\nclient.put('/foo', { hello: 'world' }, function(err, req, res, data) {\n  assert.ifError(err);\n  console.log('%d -> %j', res.statusCode, res.headers);\n  console.log('%s', data);\n});\n```\n\n### del(path, callback)\n\n`del` doesn't take content, since you know, it should't:\n\n```js\nclient.del('/foo/bar', function(err, req, res) {\n  assert.ifError(err);\n  console.log('%d -> %j', res.statusCode, res.headers);\n});\n```\n\n## HttpClient\n\n`HttpClient` is the lowest-level client shipped in restify, and is\nbasically just some sugar over the top of node's http/https modules\n(with HTTP methods like the other clients).  It is useful if you want\nto stream with restify.  Note that the event below is unfortunately\nnamed `result` and not `response` (because\n[Event 'response'](http://nodejs.org/docs/latest/api/all.html#event_response_)\nis already used).\n\n```js\nclient = restify.createClient({\n  url: 'http://127.0.0.1'\n});\n\nclient.get('/str/mcavage', function(err, req) {\n  assert.ifError(err); // connection error\n\n  req.on('result', function(err, res) {\n    assert.ifError(err); // HTTP status code >= 400\n\n    res.body = '';\n    res.setEncoding('utf8');\n    res.on('data', function(chunk) {\n      res.body += chunk;\n    });\n\n    res.on('end', function() {\n      console.log(res.body);\n    });\n  });\n});\n```\n\nOr a write:\n\n```js\nclient.post(opts, function(err, req) {\n  assert.ifError(connectErr);\n\n  req.on('result', function(err, res) {\n    assert.ifError(err);\n    res.body = '';\n    res.setEncoding('utf8');\n    res.on('data', function(chunk) {\n      res.body += chunk;\n    });\n\n    res.on('end', function() {\n      console.log(res.body);\n    });\n  });\n\n  req.write('hello world');\n  req.end();\n});\n```\n\nNote that get/head/del all call `req.end()` for you, so you can't\nwrite data over those. Otherwise, all the same methods exist as\n`JsonClient/StringClient`.\n\nOne wishing to extend the `HttpClient` should look at the internals\nand note that `read` and `write` probably need to be overridden.\n\n### Proxy\n\nThere are several options for enabling a proxy for the\nhttp client. The following options are available to set a proxy url:\n\n```js\n// Set the proxy option in the client configuration\nrestify.createClient({\n    proxy: 'http://127.0.0.1'\n});\n```\n\nFrom environment variables:\n\n```sh\n$ export HTTPS_PROXY = 'https://127.0.0.1'\n$ export HTTP_PROXY = 'http://127.0.0.1'\n```\n\nThere is an option to disable the use of a proxy on a url basis or for\nall urls. This can be enabled by setting an environment variable.\n\nDon't proxy requests to any urls\n\n```sh\n$ export NO_PROXY='*'\n```\n\nDon't proxy requests to localhost\n\n```sh\n$ export NO_PROXY='127.0.0.1'\n```\n\nDon't proxy requests to localhost on port 8000\n\n```sh\n$ export NO_PROXY='localhost:8000'\n```\n\nDon't proxy requests to multiple IPs\n\n```sh\n$ export NO_PROXY='127.0.0.1, 8.8.8.8'\n```\n\n**Note**: The url being requested must match the full hostname in\nthe proxy configuration or NO_PROXY environment variable. DNS\nlookups are not performed to determine the IP address of a hostname.\n\n### basicAuth(username, password)\n\nSince it hasn't been mentioned yet, this convenience method (available\non all clients), just sets the `Authorization` header for all HTTP requests:\n\n```js\nclient.basicAuth('mark', 'mysupersecretpassword');\n```\n\n### Upgrades\n\nIf you successfully negotiate an Upgrade with the HTTP server, an\n`upgradeResult` event will be emitted with the arguments `err`, `res`, `socket`\nand `head`.  You can use this functionality to establish a WebSockets\nconnection with a server.  For example, using the\n[watershed](https://github.com/jclulow/node-watershed) library:\n\n```js\nvar ws = new Watershed();\nvar wskey = ws.generateKey();\nvar options = {\n  path: '/websockets/attach',\n  headers: {\n    connection: 'upgrade',\n    upgrade: 'websocket',\n    'sec-websocket-key': wskey,\n  }\n};\nclient.get(options, function(err, res, socket, head) {\n  res.once('upgradeResult', function(err2, res2, socket2, head2) {\n    var shed = ws.connect(res2, socket2, head2, wskey);\n    shed.on('text', function(msg) {\n      console.log('message from server: ' + msg);\n      shed.end();\n    });\n    shed.send('greetings program');\n  });\n});\n```\n"
  },
  {
    "path": "docs/guides/dtrace.md",
    "content": "---\ntitle: Dtrace\npermalink: /docs/dtrace/\n---\n\nOne of the coolest features of restify is that it automatically\ncreates DTrace probes for you whenever you add a new route/handler.\nTo use DTrace you need to pass `dtrace` option to the server\n`restify.createServer({ dtrace: true })`.\nThe easiest way to explain this is with an example:\n\n```js\nvar restify = require('restify');\n\nvar server = restify.createServer({\n  name: 'helloworld',\n  dtrace: true\n});\n\nserver.use(restify.plugins.acceptParser(server.acceptable));\nserver.use(restify.plugins.authorizationParser());\nserver.use(restify.plugins.dateParser());\nserver.use(restify.plugins.queryParser());\nserver.use(restify.plugins.urlEncodedBodyParser());\n\nserver.use(function slowHandler(req, res, next) {\n  setTimeout(function() {\n    return next();\n  }, 250);\n});\n\nserver.get({path: '/hello/:name', name: 'GetFoo'}, function respond(req, res, next) {\n  res.send({\n    hello: req.params.name\n  });\n  return next();\n});\n\nserver.listen(8080, function() {\n  console.log('listening: %s', server.url);\n});\n```\n\nSo we've got our typical \"hello world\" server now, with a slight twist; we\nintroduced an artificial 250ms lag.  Also, note that we named our server, our\nroutes, and all of our handlers (functions); while that's optional, it\ndoes make DTrace much more usable.  So, if you started that server,\nthen looked for DTrace probes, you'd see something like this:\n\n```sh\n$ dtrace -l -P restify*\nID   PROVIDER            MODULE                          FUNCTION NAME\n24   restify38789        mod-88f3f88                     route-start route-start\n25   restify38789        mod-88f3f88                     handler-start handler-start\n26   restify38789        mod-88f3f88                     handler-done handler-done\n27   restify38789        mod-88f3f88                     route-done route-done\n```\n\n## route-start\n\n|Field|Type|Description|\n|-----|----|-----------|\n|server name|char *|name of the restify server that fired|\n|route name|char *|name of the route that fired|\n|id|int|unique id for this request|\n|method|char *|HTTP request method|\n|url|char *|(full) HTTP URL|\n|headers|char *|JSON encoded map of all request headers|\n\n## handler-start\n\n|Field|Type|Description|\n|-----|----|-----------|\n|server name|char *|name of the restify server that fired|\n|route name|char *|name of the route that fired|\n|handler name|char *|name of the function that just entered|\n|id|int|unique id for this request|\n\n## route-done\n\n|Field|Type|Description|\n|-----|----|-----------|\n|server name|char *|name of the restify server that fired|\n|route name|char *|name of the route that fired|\n|id|int|unique id for this request|\n|statusCode|int|HTTP response code|\n|headers|char *|JSON encoded map of response headers|\n\n## handler-done\n\n|Field|Type|Description|\n|-----|----|-----------|\n|server name|char *|name of the restify server that fired|\n|route name|char *|name of the route that fired|\n|handler name|char *|name of the function that just entered|\n|id|int|unique id for this request|\n\n## Example D Script\n\nNow, if you wanted to say get a breakdown of latency by handler, you\ncould do something like this:\n\n```\n#!/usr/sbin/dtrace -s\n#pragma D option quiet\n\nrestify*:::route-start\n{\n   track[arg2] = timestamp;\n}\n\nrestify*:::handler-start\n/track[arg3]/\n{\n   h[arg3, copyinstr(arg2)] = timestamp;\n}\n\nrestify*:::handler-done\n/track[arg3] && h[arg3, copyinstr(arg2)]/\n{\n   @[copyinstr(arg2)] = quantize((timestamp - h[arg3, copyinstr(arg2)]) / 1000000);\n   h[arg3, copyinstr(arg2)] = 0;\n}\n\nrestify*:::route-done\n/track[arg2]/\n{\n   @[copyinstr(arg1)] = quantize((timestamp - track[arg2]) / 1000000);\n   track[arg2] = 0;\n}\n```\n\nSo running the server in one terminal:\n\n```sh\n$ node helloworld.js\n```\n\nThe D script in another:\n\n```sh\n$ ./helloworld.d\n```\n\nHit the server a few times with curl:\n\n```sh\n$ for i in {1..10} ; do curl -is http://127.0.0.1:8080/hello/mark ; done\n```\n\nThen Ctrl-C the D script, and you'll see the \"slowHandler\" at the\nbottom of the stack, bucketized that it's the vast majority of latency\nin this pipeline\n\n```sh\nhandler-6\nvalue  ------------- Distribution ------------- count\n-1 |                                         0\n0  |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n1  |                                         0\n\nparseAccept\nvalue  ------------- Distribution ------------- count\n-1 |                                         0\n0  |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n1  |                                         0\n\nparseAuthorization\nvalue  ------------- Distribution ------------- count\n-1 |                                         0\n0  |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n1  |                                         0\n\nparseDate\nvalue  ------------- Distribution ------------- count\n-1 |                                         0\n0  |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n1  |                                         0\n\nparseQueryString\nvalue  ------------- Distribution ------------- count\n-1 |                                         0\n0  |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n1  |                                         0\n\nparseUrlEncodedBody\nvalue  ------------- Distribution ------------- count\n-1 |                                         0\n0  |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n1  |                                         0\n\nrespond\nvalue  ------------- Distribution ------------- count\n1  |                                         0\n2  |@@@@                                     1\n4  |                                         0\n8  |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@     9\n16 |                                         0\n\nslowHandler\nvalue  ------------- Distribution ------------- count\n64  |                                         0\n128 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@     9\n256 |@@@@                                     1\n512 |                                         0\n\ngetfoo\nvalue  ------------- Distribution ------------- count\n64  |                                         0\n128 |@@@@                                     1\n256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@     9\n512 |\n```\n"
  },
  {
    "path": "docs/guides/server.md",
    "content": "# Server Guide\n\nSetting up a server is quick and easy. Here is a barebones echo server:\n\n```js\nvar restify = require('restify');\n\nfunction respond(req, res, next) {\n  res.send('hello ' + req.params.name);\n  next();\n}\n\nvar server = restify.createServer();\nserver.get('/hello/:name', respond);\nserver.head('/hello/:name', respond);\n\nserver.listen(8080, function() {\n  console.log('%s listening at %s', server.name, server.url);\n});\n```\n\nTry hitting that with the following curl commands to get a feel for what\nrestify is going to turn that into:\n\n```sh\n$ curl -is http://localhost:8080/hello/mark -H 'accept: text/plain'\nHTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 10\nDate: Mon, 31 Dec 2012 01:32:44 GMT\nConnection: keep-alive\n\nhello mark\n\n\n$ curl -is http://localhost:8080/hello/mark\nHTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 12\nDate: Mon, 31 Dec 2012 01:33:33 GMT\nConnection: keep-alive\n\n\"hello mark\"\n\n\n$ curl -is http://localhost:8080/hello/mark -X HEAD -H 'connection: close'\nHTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 12\nDate: Mon, 31 Dec 2012 01:42:07 GMT\nConnection: close\n```\n\nNote that by default, curl uses `Connection: keep-alive`. In order to make the\nHEAD method return right away, you'll need to pass `Connection: close`.\n\nSince curl is often used with REST APIs, restify's plugins include a plugin to\nwork around this idiosyncrasy in curl. The plugin checks whether the user agent\nis curl. If it is, it sets the Connection header to \"close\" and removes the\n\"Content-Length\" header.\n\n```js\nserver.pre(restify.plugins.pre.userAgentConnection());\n```\n\n## Sinatra style handler chains\n\nLike many other Node.js based REST frameworks, restify leverages a Sinatra\nstyle syntax for defining routes and the function handlers that service those\nroutes:\n\n```js\nserver.get('/', function(req, res, next) {\n  res.send('home')\n  return next();\n});\n\nserver.post('/foo',\n  function(req, res, next) {\n    req.someData = 'foo';\n    return next();\n  },\n  function(req, res, next) {\n    res.send(req.someData);\n    return next();\n  }\n);\n```\n\nIn a restify server, there are three distinct handler chains:\n\n* `pre` - a handler chain executed prior to routing\n* `use` - a handler chain executed post routing\n* `{httpVerb}` - a handler chain executed specific to a route\n\nAll three handler chains accept either a single function, multiple functions,\nor an array of functions.\n\n\n## Universal pre-handlers: server.pre()\n\nThe `pre` handler chain is executed before routing. That means these handlers\nwill execute for an incoming request even if it's for a route that you did not\nregister. This can be useful for logging metrics or for cleaning up the\nincoming request before routing it.\n\n```js\n// dedupe slashes in URL before routing\nserver.pre(restify.plugins.dedupeSlashes());\n```\n\n\n## Universal handlers: server.use()\n\nThe `use` handler chains is executed after a route has been chosen to service\nthe request. Function handlers that are attached via the `use()` method will be\nrun for all routes. Since restify runs handlers in the order they are\nregistered, make sure that all your `use()` calls happen before defining any\nroutes.\n\n```js\nserver.use(function(req, res, next) {\n    console.warn('run for all routes!');\n    return next();\n});\n```\n\n\n## Using next()\n\nUpon completion of each function in the handler chain, you are responsible for\ncalling `next()`. Calling `next()` will move to the next function in the chain.\n\nUnlike other REST frameworks, calling `res.send()` does not trigger `next()`\nautomatically. In many applications, work can continue to happen after\n`res.send()`, so flushing the response is not synonmyous with completion of a\nrequest.\n\nIn the normal case, `next()` does not typically take any parameters. If for\nsome reason you want to stop processing the request, you can call `next(false)`\nto stop processing the request:\n\n```js\nserver.use([\n  function(req, res, next) {\n    if (someCondition) {\n      res.send('done!');\n      return next(false);\n    }\n    return next();\n  },\n  function(req, res, next) {\n    // if someCondition is true, this handler is never executed\n  }\n]);\n```\n\n`next()` also accepts any object for which `instanceof Error` is true, which\nwill cause restify to send that Error object as a response to the client. The\nstatus code for the response will be inferred from the Error object's\n`statusCode` property. If no `statusCode` is found, it will default to 500.\nSo the snippet below will send a serialized error to the client with an http\n500:\n\n```js\nserver.use(function(req, res, next) {\n  return next(new Error('boom!'));\n});\n```\n\nAnd this will send a 404, since the `NotFoundError` constructor provides a\nvalue of 404 for `statusCode`:\n\n```js\nserver.use(function(req, res, next) {\n  return next(new NotFoundError('not here!'));\n});\n```\n\nCalling `res.send()` with an Error object produces similar results, with this\nsnippet sending an http 500 with a serialized error the client:\n\n```js\nserver.use(function(req, res, next) {\n  res.send(new Error('boom!'));\n  return next();\n});\n```\n\nThe difference between the two is that invoking `next()` with an Error object\nallows you to leverage the server's event emitter.md#errors). This enables you\nto handle all occurrences of an error type using a common handler. See the\nServer API for more details.\n\n\n## Routing\n\nrestify routing, in 'basic' mode, is pretty much identical to express/sinatra,\nin that HTTP verbs are used with a parameterized resource to determine what\nchain of handlers to run. Values associated with named placeholders are\navailable in `req.params`. Those values will be URL-decoded before being\npassed to you.\n\n```js\nfunction send(req, res, next) {\n  res.send('hello ' + req.params.name);\n  return next();\n}\n\nserver.post('/hello', function create(req, res, next) {\n  res.send(201, Math.random().toString(36).substr(3, 8));\n  return next();\n});\nserver.put('/hello', send);\nserver.get('/hello/:name', send);\nserver.head('/hello/:name', send);\nserver.del('hello/:name', function rm(req, res, next) {\n  res.send(204);\n  return next();\n});\n```\n\nYou can also pass in a [RegExp](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/RegExp)\nobject and access the capture group with `req.params` (which will not\nbe interpreted in any way):\n\n```js\nserver.get(/^\\/([a-zA-Z0-9_\\.~-]+)\\/(.*)/, function(req, res, next) {\n  console.log(req.params[0]);\n  console.log(req.params[1]);\n  res.send(200);\n  return next();\n});\n```\n\nHere any request like:\n\n```sh\n$ curl localhost:8080/foo/my/cats/name/is/gandalf\n```\n\nWould result in `req.params[0]` being `foo` and `req.params[1]` being\n`my/cats/name/is/gandalf`.\n\n\nRoutes can be specified by any of the following http verbs - `del`, `get`,\n`head`, `opts`, `post`, `put`, and `patch`.\n\n\n```js\nserver.get(\n    '/foo/:id',\n    function(req, res, next) {\n        console.log('Authenticate');\n        return next();\n    },\n    function(req, res, next) {\n        res.send(200);\n        return next();\n    }\n);\n```\n\n### Hypermedia\n\nIf a parameterized route was defined with a string (not a regex), you can\nrender it from other places in the server. This is useful to have HTTP\nresponses that link to other resources, without having to hardcode URLs\nthroughout the codebase. Both path and query strings parameters get URL encoded\nappropriately.\n\n\n```js\nserver.get({name: 'city', path: '/cities/:slug'}, /* ... */);\n\n// in another route\nres.send({\n  country: 'Australia',\n  // render a URL by specifying the route name and parameters\n  capital: server.router.render('city', {slug: 'canberra'}, {details: true})\n});\n```\n\nWhich returns:\n\n```js\n{\n  \"country\": \"Australia\",\n  \"capital\": \"/cities/canberra?details=true\"\n}\n```\n\n### Versioned Routes\n\nMost REST APIs tend to need versioning, and restify ships with support\nfor [semver](http://semver.org/) versioning in an `Accept-Version`\nheader, the same way you specify NPM version dependencies:\n\n```js\nvar restify = require('restify');\n\nvar server = restify.createServer();\n\nfunction sendV1(req, res, next) {\n  res.send('hello: ' + req.params.name);\n  return next();\n}\n\nfunction sendV2(req, res, next) {\n  res.send({ hello: req.params.name });\n  return next();\n}\n\nserver.get('/hello/:name', restify.plugins.conditionalHandler([\n  { version: '1.1.3', handler: sendV1 },\n  { version: '2.0.0', handler: sendV2 }\n]));\n\nserver.listen(8080);\n```\n\nTry hitting with:\n\n```sh\n$ curl -s localhost:8080/hello/mark\n{\"hello\":\"mark\"}\n$ curl -s -H 'accept-version: ~1' localhost:8080/hello/mark\n\"hello: mark\"\n$ curl -s -H 'accept-version: ~2' localhost:8080/hello/mark\n{\"hello\":\"mark\"}\n$ curl -s -H 'accept-version: ~3' localhost:8080/hello/mark | json\n{\n  \"code\": \"InvalidVersion\",\n  \"message\": \"~3 is not supported by GET /hello/mark\"\n}\n```\n\nIn the first case, we didn't specify an `Accept-Version` header at all, so\nrestify treats that like sending a `*`. Much as not sending an `Accept` header\nmeans the client gets the server's choice. Restify will choose this highest\nmatching route. In the second case, we explicitly asked for for V1, which got\nus response a response from the version 1 handler function, but then we asked\nfor V2 and got back JSON. Finally, we asked for a version that doesn't exist\nand got an error.\n\nYou can default the versions on routes by passing in a version field at server\ncreation time.  Lastly, you can support multiple versions in the API by using\nan array:\n\n```js\nserver.get('/hello/:name' restify.plugins.conditionalHandler([\n  { version: ['2.0.0', '2.1.0', '2.2.0'], handler: sendV2 }\n]));\n```\n\nIn this case you may need to know more information such as what the original\nrequested version string was, and what the matching version from the routes\nsupported version array was. Two methods make this info available:\n\n```js\nserver.get('/version/test', restify.plugins.conditionalHandler([\n  {\n    version: ['2.0.0', '2.1.0', '2.2.0'],\n    handler: function (req, res, next) {\n      res.send(200, {\n        requestedVersion: req.version(),\n        matchedVersion: req.matchedVersion()\n      });\n      return next();\n    }\n  }\n]));\n```\n\nHitting this route will respond as below:\n\n```sh\n$ curl -s -H 'accept-version: <2.2.0' localhost:8080/version/test | json\n{\n  \"requestedVersion\": \"<2.2.0\",\n  \"matchedVersion\": \"2.1.0\"\n}\n```\n\n\n## Upgrade Requests\n\nIncoming HTTP requests that contain a `Connection: Upgrade` header are treated\nsomewhat differently by the node HTTP server.  If you want restify to push\nUpgrade requests through the regular routing chain, you need to enable\n`handleUpgrades` when creating the server.\n\nTo determine if a request is eligible for Upgrade, check for the existence of\n`res.claimUpgrade()`.  This method will return an object with two properties:\nthe `socket` of the underlying connection, and the first received data `Buffer`\nas `head` (may be zero-length).\n\nOnce `res.claimUpgrade()` is called, `res` itself is marked unusable for\nfurther HTTP responses; any later attempt to `send()` or `end()`, etc, will\nthrow an `Error`.  Likewise if `res` has already been used to send at least\npart of a response to the client, `res.claimUpgrade()` will throw an `Error`.\nUpgrades and regular HTTP Response behaviour are mutually exclusive on any\nparticular connection.\n\nUsing the Upgrade mechanism, you can use a library like\n[watershed](https://github.com/jclulow/node-watershed) to negotiate WebSockets\nconnections.  For example:\n\n```js\nvar ws = new Watershed();\nserver.get('/websocket/attach', function upgradeRoute(req, res, next) {\n  if (!res.claimUpgrade) {\n    next(new Error('Connection Must Upgrade For WebSockets'));\n    return;\n  }\n\n  var upgrade = res.claimUpgrade();\n  var shed = ws.accept(req, upgrade.socket, upgrade.head);\n  shed.on('text', function(msg) {\n    console.log('Received message from websocket client: ' + msg);\n  });\n  shed.send('hello there!');\n\n  next(false);\n});\n```\n\n## Responses' Content Negotiation And Formatting\n\nIf you're using `res.send()` restify will determine the content-type to respond\nwith by, from highest priority to lowest priority:\n\n1. using the value of `res.contentType` if present\n1. otherwise, using the value of the `Content-Type` response header if set\n1. otherwise, using `application/json` if the body is an object that is not a\n   Buffer instance\n1. otherwise, negotiating the content-type by matching available formatters with\n   the request's `accept` header\n\nIf a content-type can't be determined, then restify will respond with an error.\n\nIf a content-type can be negotiated, restify then determines what formatter to\nuse to format the response's content.\n\nIf no formatter matching the content-type can be found, restify will by default\noverride the response's content-type to `'application/octet-stream'` and then\nerror if no formatter is found for that content-type.\n\nThis default behavior can be changed by passing `strictFormatters: false`\n(default is true) when creating the restify server instance. In that case, if no\nformatter is found for the negotiated content-type, the response is flushed\nwithout applying any formatter.\n\nNote in the examples above we've not defined any formatters, so we've been\nleveraging the fact that restify ships with `application/json`, `text/plain` and\n`application/octet-stream` formatters. You can add additional formatters to\nrestify by passing in a hash of content-type -> parser at server creation time:\n\n```js\nvar server = restify.createServer({\n  formatters: {\n    'application/foo': function formatFoo(req, res, body) {\n      if (body instanceof Error)\n        return body.stack;\n\n      if (Buffer.isBuffer(body))\n        return body.toString('base64');\n\n      return util.inspect(body);\n    }\n  }\n});\n```\n\nFor example, attempting to send a content-type that does not have a defined\nformatter:\n\n```js\nserver.get('/foo', function(req, res, next) {\n  res.setHeader('content-type', 'text/css');\n  res.send('hi');\n  return next();\n});\n```\n\nWill result in a response with a content-type of `application/octet-stream`:\n\n```sh\n$ curl -i localhost:3000/\nHTTP/1.1 200 OK\nContent-Type: application/octet-stream\nContent-Length: 2\nDate: Thu, 02 Jun 2016 06:50:54 GMT\nConnection: keep-alive\n```\n\nHowever, if the server instance is created with `strictFormatters: false`:\n\n```js\nvar server = restify.createServer({\n  strictFormatters: false\n});\n```\n\nThe response has a content-type of `text/css` even though no `'text/css'`\nformatter is present:\n\n```sh\n$ curl -i localhost:3000/\nHTTP/1.1 200 OK\nContent-Type: text/css\nContent-Length: 2\nDate: Thu, 02 Jun 2016 06:50:54 GMT\nConnection: keep-alive\n```\n\nAs previously noted, restify ships with built-in formatters for json, text,\nand binary. When you override or append to this, the \"priority\" might change;\nto ensure that the priority is set to what you want, you should set a `q-value`\non your formatter definitions, which will ensure sorting happens the way you\nwant:\n\n```js\nrestify.createServer({\n  formatters: {\n    'application/foo; q=0.9': function formatFoo(req, res, body) {\n      if (body instanceof Error)\n        return body.stack;\n\n      if (Buffer.isBuffer(body))\n        return body.toString('base64');\n\n      return util.inspect(body);\n    }\n  }\n});\n```\n\nRestify ships with the following default formatters, which can be overridden\nwhen passing a formatters options to `createServer()`:\n\n* application/javascript\n* application/json\n* text/plain\n* application/octet-stream\n\n\nThe restify response object retains has all the \"raw\" methods of a node\n[ServerResponse](http://nodejs.org/docs/latest/api/http.html#http.ServerResponse)\n on it as well.\n\n```js\nvar body = 'hello world';\nres.writeHead(200, {\n  'Content-Length': Buffer.byteLength(body),\n  'Content-Type': 'text/plain'\n});\nres.write(body);\nres.end();\n```\n\n\n## Socket.IO\n\nTo use [socket.io](http://socket.io/) with restify, just treat your restify\nserver as if it were a \"raw\" node server:\n\n```js\nvar server = restify.createServer();\nvar io = socketio.listen(server.server);\n\nserver.get('/', function indexHTML(req, res, next) {\n    fs.readFile(__dirname + '/index.html', function (err, data) {\n        if (err) {\n            next(err);\n            return;\n        }\n\n        res.setHeader('Content-Type', 'text/html');\n        res.writeHead(200);\n        res.end(data);\n        next();\n    });\n});\n\n\nio.sockets.on('connection', function (socket) {\n    socket.emit('news', { hello: 'world' });\n    socket.on('my other event', function (data) {\n            console.log(data);\n    });\n});\n\nserver.listen(8080, function () {\n    console.log('socket.io server listening at %s', server.url);\n});\n```\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\ntitle: Quick Start\npermalink: /docs/home/\nredirect_from: /docs/\n---\n\nSetting up a server is quick and easy. Here is a barebones echo server:\n\n```js\nvar restify = require('restify');\n\nfunction respond(req, res, next) {\n  res.send('hello ' + req.params.name);\n  next();\n}\n\nvar server = restify.createServer();\nserver.get('/hello/:name', respond);\nserver.head('/hello/:name', respond);\n\nserver.listen(8080, function() {\n  console.log('%s listening at %s', server.name, server.url);\n});\n```\n\nTry hitting that with the following curl commands to get a feel for what\nrestify is going to turn that into:\n\n```sh\n$ curl -is http://localhost:8080/hello/mark -H 'accept: text/plain'\nHTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 10\nDate: Mon, 31 Dec 2012 01:32:44 GMT\nConnection: keep-alive\n\nhello mark\n\n\n$ curl -is http://localhost:8080/hello/mark\nHTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 12\nDate: Mon, 31 Dec 2012 01:33:33 GMT\nConnection: keep-alive\n\n\"hello mark\"\n\n\n$ curl -is http://localhost:8080/hello/mark -X HEAD -H 'connection: close'\nHTTP/1.1 200 OK\nContent-Type: application/json\nContent-Length: 12\nDate: Mon, 31 Dec 2012 01:42:07 GMT\nConnection: close\n```\n\nNote that by default, curl uses `Connection: keep-alive`. In order to make the\nHEAD method return right away, you'll need to pass `Connection: close`.\n\nSince curl is often used with REST APIs, restify's plugins include a plugin to\nwork around this idiosyncrasy in curl. The plugin checks whether the user agent\nis curl. If it is, it sets the Connection header to \"close\" and removes the\n\"Content-Length\" header.\n\n```js\nserver.pre(restify.plugins.pre.userAgentConnection());\n```\n\n## Sinatra style handler chains\n\nLike many other Node.js based REST frameworks, restify leverages a Sinatra\nstyle syntax for defining routes and the function handlers that service those\nroutes:\n\n```js\nserver.get('/', function(req, res, next) {\n  res.send('home')\n  return next();\n});\n\nserver.post('/foo',\n  function(req, res, next) {\n    req.someData = 'foo';\n    return next();\n  },\n  function(req, res, next) {\n    res.send(req.someData);\n    return next();\n  }\n);\n```\n\nIn a restify server, there are three distinct handler chains:\n\n* `pre` - a handler chain executed prior to routing\n* `use` - a handler chain executed post routing\n* `{httpVerb}` - a handler chain executed specific to a route\n\nAll three handler chains accept either a single function, multiple functions,\nor an array of functions.\n\n\n## Universal pre-handlers: server.pre()\n\nThe `pre` handler chain is executed before routing. That means these handlers\nwill execute for an incoming request even if it's for a route that you did not\nregister. This can be useful for logging metrics or for cleaning up the\nincoming request before routing it.\n\n```js\n// dedupe slashes in URL before routing\nserver.pre(restify.plugins.pre.dedupeSlashes());\n```\n\n\n## Universal handlers: server.use()\n\nThe `use` handler chains is executed after a route has been chosen to service\nthe request. Function handlers that are attached via the `use()` method will be\nrun for all routes. Since restify runs handlers in the order they are\nregistered, make sure that all your `use()` calls happen before defining any\nroutes.\n\n```js\nserver.use(function(req, res, next) {\n    console.warn('run for all routes!');\n    return next();\n});\n```\n\n\n## Using next()\n\nUpon completion of each function in the handler chain, you are responsible for\ncalling `next()`. Calling `next()` will move to the next function in the chain.\n\nUnlike other REST frameworks, calling `res.send()` does not trigger `next()`\nautomatically. In many applications, work can continue to happen after\n`res.send()`, so flushing the response is not synonymous with completion of a\nrequest.\n\nIn the normal case, `next()` does not typically take any parameters. If for\nsome reason you want to stop processing the request, you can call `next(false)`\nto stop processing the request:\n\n```js\nserver.use([\n  function(req, res, next) {\n    if (someCondition) {\n      res.send('done!');\n      return next(false);\n    }\n    return next();\n  },\n  function(req, res, next) {\n    // if someCondition is true, this handler is never executed\n  }\n]);\n```\n\n`next()` also accepts any object for which `instanceof Error` is true, which\nwill cause restify to send that Error object as a response to the client. The\nstatus code for the response will be inferred from the Error object's\n`statusCode` property. If no `statusCode` is found, it will default to 500.\nSo the snippet below will send a serialized error to the client with an http\n500:\n\n```js\nserver.use(function(req, res, next) {\n  return next(new Error('boom!'));\n});\n```\n\nAnd this will send a 404, since the `NotFoundError` constructor provides a\nvalue of 404 for `statusCode`:\n\n```js\nserver.use(function(req, res, next) {\n  return next(new NotFoundError('not here!'));\n});\n```\n\nCalling `res.send()` with an Error object produces similar results, with this\nsnippet sending an http 500 with a serialized error the client:\n\n```js\nserver.use(function(req, res, next) {\n  res.send(new Error('boom!'));\n  return next();\n});\n```\n\nThe difference between the two is that invoking `next()` with an Error object\nallows you to leverage the server's [event\nemitter](/components/server.md#errors). This enables you to handle all\noccurrences of an error type using a common handler. See the [error\nhandling](#error-handling) section for more details.\n\n\nLastly, you can call `next.ifError(err)` with an Error object to cause restify\nto throw, bringing down the process. This can be useful if you an Error is\nsurfaced that cannot be handled, requiring you to kill the process.\n\n\n## Routing\n\nrestify routing, in 'basic' mode, is pretty much identical to express/sinatra,\nin that HTTP verbs are used with a parameterized resource to determine what\nchain of handlers to run. Values associated with named placeholders are\navailable in `req.params`. Those values will be URL-decoded before being\npassed to you.\n\n```js\nfunction send(req, res, next) {\n  res.send('hello ' + req.params.name);\n  return next();\n}\n\nserver.post('/hello', function create(req, res, next) {\n  res.send(201, Math.random().toString(36).substr(3, 8));\n  return next();\n});\nserver.put('/hello', send);\nserver.get('/hello/:name', send);\nserver.head('/hello/:name', send);\nserver.del('hello/:name', function rm(req, res, next) {\n  res.send(204);\n  return next();\n});\n```\n\nYou can also pass in a [RegExp](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/RegExp)\nobject and access the capture group with `req.params` (which will not\nbe interpreted in any way):\n\n```js\nserver.get(/^\\/([a-zA-Z0-9_\\.~-]+)\\/(.*)/, function(req, res, next) {\n  console.log(req.params[0]);\n  console.log(req.params[1]);\n  res.send(200);\n  return next();\n});\n```\n\nHere any request like:\n\n```sh\n$ curl localhost:8080/foo/my/cats/name/is/gandalf\n```\n\nWould result in `req.params[0]` being `foo` and `req.params[1]` being\n`my/cats/name/is/gandalf`.\n\n\nRoutes can be specified by any of the following http verbs - `del`, `get`,\n`head`, `opts`, `post`, `put`, and `patch`.\n\n\n```js\nserver.get(\n    '/foo/:id',\n    function(req, res, next) {\n        console.log('Authenticate');\n        return next();\n    },\n    function(req, res, next) {\n        res.send(200);\n        return next();\n    }\n);\n```\n\n### Hypermedia\n\nIf a parameterized route was defined with a string (not a regex), you can\nrender it from other places in the server. This is useful to have HTTP\nresponses that link to other resources, without having to hardcode URLs\nthroughout the codebase. Both path and query strings parameters get URL encoded\nappropriately.\n\n\n```js\nserver.get({name: 'city', path: '/cities/:slug'}, /* ... */);\n\n// in another route\nres.send({\n  country: 'Australia',\n  // render a URL by specifying the route name and parameters\n  capital: server.router.render('city', {slug: 'canberra'}, {details: true})\n});\n```\n\nWhich returns:\n\n```js\n{\n  \"country\": \"Australia\",\n  \"capital\": \"/cities/canberra?details=true\"\n}\n```\n\n### Versioned Routes\n\nMost REST APIs tend to need versioning, and restify ships with support\nfor [semver](http://semver.org/) versioning in an `Accept-Version`\nheader, the same way you specify NPM version dependencies:\n\n```js\nvar restify = require('restify');\n\nvar server = restify.createServer();\n\nfunction sendV1(req, res, next) {\n  res.send('hello: ' + req.params.name);\n  return next();\n}\n\nfunction sendV2(req, res, next) {\n  res.send({ hello: req.params.name });\n  return next();\n}\n\nserver.get('/hello/:name', restify.plugins.conditionalHandler([\n  { version: '1.1.3', handler: sendV1 },\n  { version: '2.0.0', handler: sendV2 }\n]));\n\nserver.listen(8080);\n```\n\nTry hitting with:\n\n```sh\n$ curl -s localhost:8080/hello/mark\n{\"hello\":\"mark\"}\n$ curl -s -H 'accept-version: ~1' localhost:8080/hello/mark\n\"hello: mark\"\n$ curl -s -H 'accept-version: ~2' localhost:8080/hello/mark\n{\"hello\":\"mark\"}\n$ curl -s -H 'accept-version: ~3' localhost:8080/hello/mark | json\n{\n  \"code\": \"InvalidVersion\",\n  \"message\": \"~3 is not supported by GET /hello/mark\"\n}\n```\n\nIn the first case, we didn't specify an `Accept-Version` header at all, so\nrestify treats that like sending a `*`. Much as not sending an `Accept` header\nmeans the client gets the server's choice. Restify will choose this highest\nmatching route. In the second case, we explicitly asked for for V1, which got\nus response a response from the version 1 handler function, but then we asked\nfor V2 and got back JSON. Finally, we asked for a version that doesn't exist\nand got an error.\n\nYou can default the versions on routes by passing in a version field at server\ncreation time.  Lastly, you can support multiple versions in the API by using\nan array:\n\n```js\nserver.get('/hello/:name', restify.plugins.conditionalHandler([\n  { version: ['2.0.0', '2.1.0', '2.2.0'], handler: sendV2 }\n]));\n```\n\nIn this case you may need to know more information such as what the original\nrequested version string was, and what the matching version from the routes\nsupported version array was. Two methods make this info available:\n\n```js\nserver.get('/version/test', restify.plugins.conditionalHandler([\n  {\n    version: ['2.0.0', '2.1.0', '2.2.0'],\n    handler: function (req, res, next) {\n      res.send(200, {\n        requestedVersion: req.version(),\n        matchedVersion: req.matchedVersion()\n      });\n      return next();\n    }\n  }\n]));\n```\n\nHitting this route will respond as below:\n\n```sh\n$ curl -s -H 'accept-version: <2.2.0' localhost:8080/version/test | json\n{\n  \"requestedVersion\": \"<2.2.0\",\n  \"matchedVersion\": \"2.1.0\"\n}\n```\n\n\n## Upgrade Requests\n\nIncoming HTTP requests that contain a `Connection: Upgrade` header are treated\nsomewhat differently by the node HTTP server.  If you want restify to push\nUpgrade requests through the regular routing chain, you need to enable\n`handleUpgrades` when creating the server.\n\nTo determine if a request is eligible for Upgrade, check for the existence of\n`res.claimUpgrade()`.  This method will return an object with two properties:\nthe `socket` of the underlying connection, and the first received data `Buffer`\nas `head` (may be zero-length).\n\nOnce `res.claimUpgrade()` is called, `res` itself is marked unusable for\nfurther HTTP responses; any later attempt to `send()` or `end()`, etc, will\nthrow an `Error`.  Likewise if `res` has already been used to send at least\npart of a response to the client, `res.claimUpgrade()` will throw an `Error`.\nUpgrades and regular HTTP Response behaviour are mutually exclusive on any\nparticular connection.\n\nUsing the Upgrade mechanism, you can use a library like\n[watershed](https://github.com/jclulow/node-watershed) to negotiate WebSockets\nconnections.  For example:\n\n```js\nvar ws = new Watershed();\nserver.get('/websocket/attach', function upgradeRoute(req, res, next) {\n  if (!res.claimUpgrade) {\n    next(new Error('Connection Must Upgrade For WebSockets'));\n    return;\n  }\n\n  var upgrade = res.claimUpgrade();\n  var shed = ws.accept(req, upgrade.socket, upgrade.head);\n  shed.on('text', function(msg) {\n    console.log('Received message from websocket client: ' + msg);\n  });\n  shed.send('hello there!');\n\n  next(false);\n});\n```\n\n## Responses' Content Negotiation And Formatting\n\nIf you're using `res.send()` restify will determine the content-type to respond\nwith by, from highest priority to lowest priority:\n\n1. using the value of `res.contentType` if present\n1. otherwise, using the value of the `Content-Type` response header if set\n1. otherwise, using `application/json` if the body is an object that is not a\n   Buffer instance\n1. otherwise, negotiating the content-type by matching available formatters with\n   the request's `accept` header\n\nIf a content-type can't be determined, then restify will respond with an error.\n\nIf a content-type can be negotiated, restify then determines what formatter to\nuse to format the response's content.\n\nIf no formatter matching the content-type can be found, restify will by default\noverride the response's content-type to `'application/octet-stream'` and then\nerror if no formatter is found for that content-type.\n\nThis default behavior can be changed by passing `strictFormatters: false`\n(default is false) when creating the restify server instance. In that case, if\nno formatter is found for the negotiated content-type, the response is flushed\nwithout applying any formatter.\n\nNote in the examples above we've not defined any formatters, so we've been\nleveraging the fact that restify ships with `application/json`, `text/plain` and\n`application/octet-stream` formatters. You can add additional formatters to\nrestify by passing in a hash of content-type -> parser at server creation time:\n\n```js\nvar server = restify.createServer({\n  formatters: {\n    'application/foo': function formatFoo(req, res, body) {\n      if (body instanceof Error)\n        return body.stack;\n\n      if (Buffer.isBuffer(body))\n        return body.toString('base64');\n\n      return util.inspect(body);\n    }\n  }\n});\n```\n\nFor example, attempting to send a content-type that does not have a defined\nformatter:\n\n```js\nserver.get('/foo', function(req, res, next) {\n  res.setHeader('content-type', 'text/css');\n  res.send('hi');\n  return next();\n});\n```\n\nWill result in a response with a content-type of `application/octet-stream`:\n\n```sh\n$ curl -i localhost:3000/\nHTTP/1.1 200 OK\nContent-Type: application/octet-stream\nContent-Length: 2\nDate: Thu, 02 Jun 2016 06:50:54 GMT\nConnection: keep-alive\n```\n\nHowever, if the server instance is created with `strictFormatters:false`:\n\n```js\nvar server = restify.createServer({\n  strictFormatters: false\n});\n```\n\nThe response would has a content-type of `text/css` even though no `'text/css'`\nformatter is present:\n\n```sh\n$ curl -i localhost:3000/\nHTTP/1.1 200 OK\nContent-Type: text/css\nContent-Length: 2\nDate: Thu, 02 Jun 2016 06:50:54 GMT\nConnection: keep-alive\n```\n\nAs previously noted, restify ships with built-in formatters for json, text,\nand binary. When you override or append to this, the \"priority\" might change;\nto ensure that the priority is set to what you want, you should set a `q-value`\non your formatter definitions, which will ensure sorting happens the way you\nwant:\n\n```js\nrestify.createServer({\n  formatters: {\n    'application/foo; q=0.9': function formatFoo(req, res, body) {\n      if (body instanceof Error)\n        return body.stack;\n\n      if (Buffer.isBuffer(body))\n        return body.toString('base64');\n\n      return util.inspect(body);\n    }\n  }\n});\n```\n\nRestify ships with the following default formatters, which can be overridden\nwhen passing a formatters options to `createServer()`:\n\n* application/javascript\n* application/json\n* text/plain\n* application/octet-stream\n\nThe restify response object retains has all the \"raw\" methods of a node\n[ServerResponse](http://nodejs.org/docs/latest/api/http.html#http.ServerResponse)\n on it as well.\n\n```js\nvar body = 'hello world';\nres.writeHead(200, {\n  'Content-Length': Buffer.byteLength(body),\n  'Content-Type': 'text/plain'\n});\nres.write(body);\nres.end();\n```\n\n## Error handling\n\nIt is common to want to handle an error conditions the same way. As an example,\nyou may want to serve a 500 page on all `InternalServerErrors`. In this case,\nyou can add a listener for the `InternalServer` error event that is always\nfired when this Error is encountered by restify as part of a `next(error)`\nstatement. This gives you a way to handle all errors of the same class\nidentically across the server. You can also use a generic `restifyError` event\nwhich will catch errors of all types.\n\nAn example of sending a 404:\n\n```js\nvar errs = require('restify-errors');\n\nserver.get('/hello/:foo', function(req, res, next) {\n  // resource not found error\n  var err = new errs.NotFoundError('oh noes!');\n  return next(err);\n});\n\nserver.on('NotFound', function (req, res, err, cb) {\n  // do not call res.send! you are now in an error context and are outside\n  // of the normal next chain. you can log or do metrics here, and invoke\n  // the callback when you're done. restify will automtically render the\n  // NotFoundError depending on the content-type header you have set in your\n  // response.\n  return cb();\n});\n```\n\nFor customizing the error being sent back to the client:\n\n```js\nvar errs = require('restify-errors');\n\nserver.get('/hello/:name', function(req, res, next) {\n  // some internal unrecoverable error\n  var err = new errs.InternalServerError('oh noes!');\n  return next(err);\n});\n\nserver.on('InternalServer', function (req, res, err, cb) {\n  // by default, restify will usually render the Error object as plaintext or\n  // JSON depending on content negotiation. the default text formatter and JSON\n  // formatter are pretty simple, they just call toString() and toJSON() on the\n  // object being passed to res.send, which in this case, is the error object.\n  // so to customize what it sent back to the client when this error occurs,\n  // you would implement as follows:\n\n  // for any response that is text/plain\n  err.toString = function toString() {\n    return 'an internal server error occurred!';\n  };\n  // for any response that is application/json\n  err.toJSON = function toJSON() {\n    return {\n      message: 'an internal server error occurred!',\n      code: 'boom!'\n    }\n  };\n\n  return cb();\n});\n\nserver.on('restifyError', function (req, res, err, cb) {\n  // this listener will fire after both events above!\n  // `err` here is the same as the error that was passed to the above\n  // error handlers.\n  return cb();\n});\n```\n\nHere is another example of `InternalServerError`, but this time with a custom\nformatter:\n\n```js\nconst errs = require('restify-errors');\n\nconst server = restify.createServer({\n  formatters: {\n    'text/html': function(req, res, body) {\n      if (body instanceof Error) {\n        // body here is an instance of InternalServerError\n        return '<html><body>' + body.message + '</body></html>';\n      }\n    }\n  }\n});\n\nserver.get('/', function(req, res, next) {\n  res.header('content-type', 'text/html');\n  return next(new errs.InternalServerError('oh noes!'));\n});\n```\n\n\n### restify-errors\n\nA module called restify-errors exposes a suite of error constructors for many\ncommon http and REST related errors. These constructors can be used in\nconjunction with the `next(err)` pattern to easily leverage the server's event\nemitter. The full list of constructors can be viewed over at the\n[restify-errors](https://github.com/restify/errors) repository. Here are some\nexamples:\n\n\n```js\nvar errs = require('restify-errors');\n\nserver.get('/', function(req, res, next) {\n  return next(new errs.ConflictError(\"I just don't like you\"));\n});\n```\n\n```sh\n$ curl -is localhost:3000\nHTTP/1.1 409 Conflict\nContent-Type: application/json\nContent-Length: 53\nDate: Fri, 03 Jun 2016 20:29:45 GMT\nConnection: keep-alive\n\n{\"code\":\"Conflict\",\"message\":\"I just don't like you\"}\n```\n\nWhen using restify-errors, you can also directly call `res.send(err)`, and\nrestify will automatically serialize your error for you:\n\n```js\nvar errs = require('restify-errors');\n\nserver.get('/', function(req, res, next) {\n  res.send(new errs.GoneError('gone girl'));\n  return next();\n});\n```\n\n```sh\n$ curl -is localhost:8080/\nHTTP/1.1 410 Gone\nContent-Type: application/json\nContent-Length: 37\nDate: Fri, 03 Jun 2016 20:17:48 GMT\nConnection: keep-alive\n\n{\"code\":\"Gone\",\"message\":\"gone girl\"}\n```\n\nThis automatic serialization happens because the JSON formatter will call\n`JSON.stringify()` on the Error object, and all restify-errors have a `toJSON`\nmethod defined. Compare this to a standard Error object which does not have\n`toJSON` defined:\n\n```js\nserver.get('/sendErr', function(req, res, next) {\n  res.send(new Error('where is my msg?'));\n  return next();\n});\n\nserver.get('/nextErr', function(req, res, next) {\n  return next(new Error('where is my msg?'));\n});\n```\n\n```sh\n$ curl -is localhost:8080/sendErr\nHTTP/1.1 410 Gone\nContent-Type: application/json\nContent-Length: 37\nDate: Fri, 03 Jun 2016 20:17:48 GMT\nConnection: keep-alive\n\n{}\n\n$ curl -is localhost:8080/nextErr\nHTTP/1.1 410 Gone\nContent-Type: application/json\nContent-Length: 37\nDate: Fri, 03 Jun 2016 20:17:48 GMT\nConnection: keep-alive\n\n{}\n```\n\nIf you want to use custom errors, make sure you have `toJSON` defined, or use\nrestify-error's `makeConstructor()` method to automatically create Errors that\nare supported with with `toJSON`.\n\n\n#### HttpError\n\nrestify-errors provides constructors that inherit from either HttpError or\nRestError. All HttpErrors have a numeric http `statusCode` and `body`\nproperties. The statusCode will automatically set the HTTP response status\ncode, and the body attribute by default will be the message.\n\nAll status codes between 400 and 5xx are automatically converted into\nan HttpError with the name being 'PascalCase' and spaces removed.  For\nthe complete list, take a look at the\n[node source](https://github.com/nodejs/node/blob/master/lib/_http_server.js#L17).\n\nFrom that code above `418: I'm a teapot` would be `ImATeapotError`, as\nan example.\n\n\n#### RestError\n\nA common problem with REST APIs and HTTP is that they often end\nup needing to overload 400 and 409 to mean a bunch of different\nthings.  There's no real standard on what to do in these cases, but in\ngeneral you want machines to be able to (safely) parse these things\nout, and so restify defines a convention of a `RestError`.  A\n`RestError` is a subclass of one of the particular `HttpError` types,\nand additionally sets the body attribute to be a JS object with the\nattributes `code` and `message`.  For example, here's a built-in RestError:\n\n\n```js\nvar errs = require('restify-errors');\nvar server = restify.createServer();\n\nserver.get('/hello/:name', function(req, res, next) {\n  return next(new errs.InvalidArgumentError(\"I just don't like you\"));\n});\n\n$ curl -is localhost:8080/hello/mark | json\nHTTP/1.1 409 Conflict\nContent-Type: application/json\nAccess-Control-Allow-Origin: *\nAccess-Control-Allow-Methods: GET\nAccess-Control-Allow-Headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, Api-Version\nAccess-Control-Expose-Headers: Api-Version, Request-Id, Response-Time\nConnection: close\nContent-Length: 60\nContent-MD5: MpEcO5EQFUZ2MNeUB2VaZg==\nDate: Tue, 03 Jan 2012 00:50:21 GMT\nServer: restify\nRequest-Id: bda456dd-2fe4-478d-809c-7d159d58d579\nResponse-Time: 3\n\n{\n  \"code\": \"InvalidArgument\",\n  \"message\": \"I just don't like you\"\n}\n```\n\nThe built-in HttpErrors are:\n\n* BadRequestError (400 Bad Request)\n* UnauthorizedError (401 Unauthorized)\n* PaymentRequiredError (402 Payment Required)\n* ForbiddenError (403 Forbidden)\n* NotFoundError (404 Not Found)\n* MethodNotAllowedError (405 Method Not Allowed)\n* NotAcceptableError (406 Not Acceptable)\n* ProxyAuthenticationRequiredError (407 Proxy Authentication Required)\n* RequestTimeoutError (408 Request Time-out)\n* ConflictError (409 Conflict)\n* GoneError (410 Gone)\n* LengthRequiredError (411 Length Required)\n* PreconditionFailedError (412 Precondition Failed)\n* RequestEntityTooLargeError (413 Request Entity Too Large)\n* RequesturiTooLargeError (414 Request-URI Too Large)\n* UnsupportedMediaTypeError (415 Unsupported Media Type)\n* RequestedRangeNotSatisfiableError (416 Requested Range Not Satisfiable)\n* ExpectationFailedError (417 Expectation Failed)\n* ImATeapotError (418 I'm a teapot)\n* UnprocessableEntityError (422 Unprocessable Entity)\n* LockedError (423 Locked)\n* FailedDependencyError (424 Failed Dependency)\n* UnorderedCollectionError (425 Unordered Collection)\n* UpgradeRequiredError (426 Upgrade Required)\n* PreconditionRequiredError (428 Precondition Required)\n* TooManyRequestsError (429 Too Many Requests)\n* RequestHeaderFieldsTooLargeError (431 Request Header Fields Too Large)\n* InternalServerError (500 Internal Server Error)\n* NotImplementedError (501 Not Implemented)\n* BadGatewayError (502 Bad Gateway)\n* ServiceUnavailableError (503 Service Unavailable)\n* GatewayTimeoutError (504 Gateway Time-out)\n* HttpVersionNotSupportedError (505 HTTP Version Not Supported)\n* VariantAlsoNegotiatesError (506 Variant Also Negotiates)\n* InsufficientStorageError (507 Insufficient Storage)\n* BandwidthLimitExceededError (509 Bandwidth Limit Exceeded)\n* NotExtendedError (510 Not Extended)\n* NetworkAuthenticationRequiredError (511 Network Authentication Required)\n* BadDigestError (400 Bad Request)\n* BadMethodError (405 Method Not Allowed)\n* InternalError (500 Internal Server Error)\n* InvalidArgumentError (409 Conflict)\n* InvalidContentError (400 Bad Request)\n* InvalidCredentialsError (401 Unauthorized)\n* InvalidHeaderError (400 Bad Request)\n* InvalidVersionError (400 Bad Request)\n* MissingParameterError (409 Conflict)\n* NotAuthorizedError (403 Forbidden)\n* RequestExpiredError (400 Bad Request)\n* RequestThrottledError (429 Too Many Requests)\n* ResourceNotFoundError (404 Not Found)\n* WrongAcceptError (406 Not Acceptable)\n\nAnd the built in RestErrors are:\n\n* 400 BadDigestError\n* 405 BadMethodError\n* 500 InternalError\n* 409 InvalidArgumentError\n* 400 InvalidContentError\n* 401 InvalidCredentialsError\n* 400 InvalidHeaderError\n* 400 InvalidVersionError\n* 409 MissingParameterError\n* 403 NotAuthorizedError\n* 412 PreconditionFailedError\n* 400 RequestExpiredError\n* 429 RequestThrottledError\n* 404 ResourceNotFoundError\n* 406 WrongAcceptError\n\nYou can also create your own subclasses using the `makeConstructor` method:\n\n```js\nvar errs = require('restify-errors');\nvar restify = require('restify');\n\nerrs.makeConstructor('ZombieApocalypseError');\n\nvar myErr = new errs.ZombieApocalypseError('zomg!');\n```\n\nThe constructor takes `message`, `statusCode`, `restCode`, and `context`\noptions. Please check out the restify-errors repo for more information.\n\n\n## Socket.IO\n\nTo use [socket.io](http://socket.io/) with restify, just treat your restify\nserver as if it were a \"raw\" node server:\n\n```js\nvar server = restify.createServer();\nvar io = socketio.listen(server.server);\n\nserver.get('/', function indexHTML(req, res, next) {\n    fs.readFile(__dirname + '/index.html', function (err, data) {\n        if (err) {\n            next(err);\n            return;\n        }\n\n        res.setHeader('Content-Type', 'text/html');\n        res.writeHead(200);\n        res.end(data);\n        next();\n    });\n});\n\n\nio.sockets.on('connection', function (socket) {\n    socket.emit('news', { hello: 'world' });\n    socket.on('my other event', function (data) {\n            console.log(data);\n    });\n});\n\nserver.listen(8080, function () {\n    console.log('socket.io server listening at %s', server.url);\n});\n```\n"
  },
  {
    "path": "examples/dtrace/demo.js",
    "content": "'use strict';\n\n// There's an example D script here to showcase a \"slow\" handler where it's\n// wildcard'd by the route name.  In \"real life\" you'd probably start with a\n// d script that breaks down the route -start and -done, and then you'd want\n// to see which handler is taking longest from there.\n//\n// $ node demo.js\n// $ curl localhost:9080/foo/bar\n// $ sudo ./handler-timing.d\n// ^C\n//\n//   handler-6\n//            value  ------------- Distribution ------------- count\n//               -1 |                                         0\n//                0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n//                1 |                                         0\n//\n//   parseAccept\n//            value  ------------- Distribution ------------- count\n//               -1 |                                         0\n//                0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n//                1 |                                         0\n//\n//   parseAuthorization\n//            value  ------------- Distribution ------------- count\n//               -1 |                                         0\n//                0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n//                1 |                                         0\n//\n//   parseDate\n//            value  ------------- Distribution ------------- count\n//               -1 |                                         0\n//                0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n//                1 |                                         0\n//\n//   parseQueryString\n//            value  ------------- Distribution ------------- count\n//               -1 |                                         0\n//                0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n//                1 |                                         0\n//\n//   parseUrlEncodedBody\n//            value  ------------- Distribution ------------- count\n//               -1 |                                         0\n//                0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 10\n//                1 |                                         0\n//\n//   sendResult\n//            value  ------------- Distribution ------------- count\n//                1 |                                         0\n//                2 |@@@@                                     1\n//                4 |                                         0\n//                8 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@     9\n//               16 |                                         0\n//\n//   slowHandler\n//            value  ------------- Distribution ------------- count\n//               64 |                                         0\n//              128 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@     9\n//              256 |@@@@                                     1\n//              512 |                                         0\n//\n//   getfoo\n//            value  ------------- Distribution ------------- count\n//               64 |                                         0\n//              128 |@@@@                                     1\n//              256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@     9\n//              512 |                                         0\n\nvar restify = require('../../lib');\nvar Logger = require('pino');\n\n///--- Globals\n\nvar NAME = 'exampleapp';\n\n///--- Mainline\n\nvar log = new Logger({\n    name: NAME,\n    level: 'trace',\n    base: { service: NAME }\n});\n\nvar server = restify.createServer({\n    name: NAME,\n    Logger: log,\n    dtrace: true,\n    formatters: {\n        'application/foo': function(req, res, body) {\n            if (body instanceof Error) {\n                body = body.stack;\n            } else if (Buffer.isBuffer(body)) {\n                body = body.toString('base64');\n            } else {\n                switch (typeof body) {\n                    case 'boolean':\n                    case 'number':\n                    case 'string':\n                        body = body.toString();\n                        break;\n\n                    case 'undefined':\n                        body = '';\n                        break;\n\n                    default:\n                        body =\n                            body === null\n                                ? ''\n                                : 'Demoing application/foo formatter; ' +\n                                  JSON.stringify(body);\n                        break;\n                }\n            }\n            return body;\n        }\n    }\n});\n\nserver.use(restify.plugins.acceptParser(server.acceptable));\nserver.use(restify.plugins.authorizationParser());\nserver.use(restify.plugins.dateParser());\nserver.use(restify.plugins.queryParser());\nserver.use(restify.plugins.urlEncodedBodyParser());\n\nserver.use(function slowHandler(req, res, next) {\n    setTimeout(function() {\n        next();\n    }, 250);\n});\n\nserver.get(\n    { url: '/foo/:id', name: 'GetFoo' },\n    function(req, res, next) {\n        next();\n    },\n    function sendResult(req, res, next) {\n        res.contentType = 'application/foo';\n        res.send({\n            hello: req.params.id\n        });\n        next();\n    }\n);\n\nserver.head('/foo/:id', function(req, res, next) {\n    res.send({\n        hello: req.params.id\n    });\n    next();\n});\n\nserver.put('/foo/:id', function(req, res, next) {\n    res.send({\n        hello: req.params.id\n    });\n    next();\n});\n\nserver.post('/foo/:id', function(req, res, next) {\n    res.json(201, req.params);\n    next();\n});\n\nserver.del('/foo/:id', function(req, res, next) {\n    res.send(204);\n    next();\n});\n\nserver.on('after', function(req, res, name) {\n    req.log.info('%s just finished: %d.', name, res.code);\n});\n\nserver.on('NotFound', function(req, res) {\n    res.send(404, req.url + ' was not found');\n});\n\nserver.listen(9080, function() {\n    log.info('listening: %s', server.url);\n});\n"
  },
  {
    "path": "examples/dtrace/handler-timing.d",
    "content": "#!/usr/sbin/dtrace -s\n#pragma D option quiet\n\nrestify*:::route-start\n{\n        track[arg2] = timestamp;\n}\n\n\nrestify*:::handler-start\n/track[arg3]/\n{\n        h[arg3, copyinstr(arg2)] = timestamp;\n}\n\n\nrestify*:::handler-done\n/track[arg3] && h[arg3, copyinstr(arg2)]/\n{\n\n        @[copyinstr(arg2)] = quantize((timestamp - h[arg3, copyinstr(arg2)]) / 1000000);\n        h[arg3, copyinstr(arg2)] = 0;\n}\n\n\nrestify*:::route-done\n/track[arg2]/\n{\n        @[copyinstr(arg1)] = quantize((timestamp - track[arg2]) / 1000000);\n        track[arg2] = 0;\n}\n"
  },
  {
    "path": "examples/dtrace/hello.js",
    "content": "var restify = require('../../lib');\n\nvar server = restify.createServer({\n    name: 'helloworld',\n    dtrace: true\n});\n\nserver.use(restify.plugins.acceptParser(server.acceptable));\nserver.use(restify.plugins.authorizationParser());\nserver.use(restify.plugins.dateParser());\nserver.use(restify.plugins.queryParser());\nserver.use(restify.plugins.urlEncodedBodyParser());\n\nserver.use(function slowHandler(req, res, next) {\n    setTimeout(function() {\n        next();\n    }, 250);\n});\n\nserver.get(\n    {\n        path: '/hello/:name',\n        name: 'GetFoo'\n    },\n    function respond(req, res, next) {\n        res.send({\n            hello: req.params.name\n        });\n        next();\n    }\n);\n\nserver.listen(8080, function() {\n    console.log('listening: %s', server.url);\n});\n"
  },
  {
    "path": "examples/example.js",
    "content": "'use strict';\n\nvar restify = require('../lib');\nvar server = restify.createServer();\n\nserver.pre(function pre(req, res, next) {\n    console.log('pre');\n    next();\n});\n\nserver.use(function use(req, res, next) {\n    console.log('use');\n    next();\n});\n\nserver.on('after', function(req, res, route, err) {\n    console.log('after');\n});\n\nserver.get(\n    '/:userId',\n    function onRequest(req, res, next) {\n        console.log(req.url, '1');\n        next();\n    },\n    function onRequest(req, res, next) {\n        console.log(req.url, '2');\n        res.send({ hello: 'world' });\n        next();\n    }\n);\n\nserver.listen(3001);\n"
  },
  {
    "path": "examples/http2/http2.js",
    "content": "var path = require('path');\nvar fs = require('fs');\nvar pino = require('pino');\nvar restify = require('../../lib');\n\nvar srv = restify.createServer({\n    http2: {\n        cert: fs.readFileSync(path.join(__dirname, './keys/http2-cert.pem')),\n        key: fs.readFileSync(path.join(__dirname, './keys/http2-key.pem')),\n        ca: fs.readFileSync(path.join(__dirname, 'keys/http2-csr.pem')),\n        allowHTTP1: true //allow incoming connections that do not support HTTP/2 to be downgraded to HTTP/1.x\n    }\n});\n\nsrv.get('/', function(req, res, next) {\n    res.send({ hello: 'world' });\n    next();\n});\n\nsrv.on(\n    'after',\n    restify.plugins.auditLogger({\n        event: 'after',\n        body: true,\n        log: pino(\n            { name: 'audit' },\n            process.stdout\n        )\n    })\n);\n\nsrv.listen(8080, function() {\n    console.log('ready on %s', srv.url);\n});\n"
  },
  {
    "path": "examples/http2/keys/http2-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIICHzCCAYgCCQCPPSUAa8QZojANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJS\nVTETMBEGA1UECBMKU29tZS1TdGF0ZTENMAsGA1UEBxMET21zazEhMB8GA1UEChMY\nSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTExMDQwOTEwMDY0NVoXDTExMDUw\nOTEwMDY0NVowVDELMAkGA1UEBhMCUlUxEzARBgNVBAgTClNvbWUtU3RhdGUxDTAL\nBgNVBAcTBE9tc2sxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCB\nnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1bn25sPkv46wl70BffxradlkRd/x\np5Xf8HDhPSfzNNctERYslXT2fX7Dmfd5w1XTVqqGqJ4izp5VewoVOHA8uavo3ovp\ngNWasil5zADWaM1T0nnV0RsFbZWzOTmm1U3D48K8rW3F5kOZ6f4yRq9QT1gF/gN7\n5Pt494YyYyJu/a8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBuRZisIViI2G/R+w79\nvk21TzC/cJ+O7tKsseDqotXYTH8SuimEH5IWcXNgnWhNzczwN8s2362NixyvCipV\nyd4wzMpPbjIhnWGM0hluWZiK2RxfcqimIBjDParTv6CMUIuwGQ257THKY8hXGg7j\nUws6Lif3P9UbsuRiYPxMgg98wg==\n-----END CERTIFICATE-----\n\n"
  },
  {
    "path": "examples/http2/keys/http2-csr.pem",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBkzCB/QIBADBUMQswCQYDVQQGEwJSVTETMBEGA1UECBMKU29tZS1TdGF0ZTEN\nMAsGA1UEBxMET21zazEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF\n3/Gnld/wcOE9J/M01y0RFiyVdPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+je\ni+mA1ZqyKXnMANZozVPSedXRGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+\nA3vk+3j3hjJjIm79rwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAiNWhz6EppIVa\nFfUaB3sLeqfamb9tg9kBHtvqj/FJni0snqms0kPWaTySEPHZF0irIb7VVdq/sVCb\n3gseMVSyoDvPJ4lHC3PXqGQ7kM1mIPhDnR/4HDA3BhlGhTXSDIHgZnvI+HMBdsyC\nhC3dz5odyKqe4nmoofomALkBL9t4H8s=\n-----END CERTIFICATE REQUEST-----\n\n"
  },
  {
    "path": "examples/http2/keys/http2-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF3/Gnld/wcOE9J/M01y0RFiyV\ndPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+jei+mA1ZqyKXnMANZozVPSedXR\nGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+A3vk+3j3hjJjIm79rwIDAQAB\nAoGAAv2QI9h32epQND9TxwSCKD//dC7W/cZOFNovfKCTeZjNK6EIzKqPTGA6smvR\nC1enFl5adf+IcyWqAoe4lkqTvurIj+2EhtXdQ8DBlVuXKr3xvEFdYxXPautdTCF6\nKbXEyS/s1TZCRFjYftvCrXxc3pK45AQX/wg7z1K+YB5pyIECQQD0OJvLoxLYoXAc\nFZraIOZiDsEbGuSHqoCReFXH75EC3+XGYkH2bQ/nSIZ0h1buuwQ/ylKXOlTPT3Qt\nXm1OQEBvAkEA4AjWsIO/rRpOm/Q2aCrynWMpoUXTZSbL2yGf8pxp/+8r2br5ier0\nM1LeBb/OPY1+k39NWLXxQoo64xoSFYk2wQJAd2wDCwX4HkR7HNCXw1hZL9QFK6rv\n20NN0VSlpboJD/3KT0MW/FiCcVduoCbaJK0Au+zEjDyy4hj5N4I4Mw6KMwJAXVAx\nI+psTsxzS4/njXG+BgIEl/C+gRYsuMQDnAi8OebDq/et8l0Tg8ETSu++FnM18neG\nntmBeMacinUUbTXuwQJBAJp/onZdsMzeVulsGrqR1uS+Lpjc5Q1gt5ttt2cxj91D\nrio48C/ZvWuKNE8EYj2ALtghcVKRvgaWfOxt2GPguGg=\n-----END RSA PRIVATE KEY-----\n\n"
  },
  {
    "path": "examples/jsonp/jsonp.js",
    "content": "var restify = require('../../lib');\n\nvar srv = restify.createServer();\nsrv.use(restify.plugins.queryParser());\nsrv.use(restify.plugins.jsonp());\nsrv.get('/', function(req, res, next) {\n    res.send({ hello: 'world' });\n    next();\n});\n\nsrv.listen(8080, function() {\n    console.log('ready on %s', srv.url);\n});\n"
  },
  {
    "path": "examples/sockio/package.json",
    "content": "{\n  \"name\": \"restify-example\",\n  \"version\": \"0.0.0\",\n  \"description\": \"Socket.io example\",\n  \"main\": \"sockio.js\",\n  \"dependencies\": {\n    \"socket.io\": \"^4.5.0\"\n  },\n  \"scripts\": {\n    \"start\": \"node sockio.js\"\n  }\n}\n"
  },
  {
    "path": "examples/sockio/sockio.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\nvar socketio = require('socket.io');\n\nvar restify = require('../../lib');\n\n///--- Globals\n\nvar HTML =\n    '<script src=\"/socket.io/socket.io.js\"></script>\\n' +\n    '<script>\\n' +\n    'var socket = io(\"http://localhost:8080\");\\n' +\n    'socket.on(\"news\", function (data) {\\n' +\n    'console.log(data);\\n' +\n    'socket.emit(\"my other event\", { my: \"data\" });\\n' +\n    '});\\n' +\n    '</script>';\n\n///--- Mainline\n\nvar server = restify.createServer();\nvar io = socketio(server.server);\n\nserver.get('/', function indexHTML(req, res, next) {\n    res.setHeader('Content-Type', 'text/html');\n    res.setHeader('Content-Length', Buffer.byteLength(HTML));\n    res.writeHead(200);\n    res.write(HTML);\n    res.end();\n    next();\n});\n\nio.on('connection', function(socket) {\n    socket.emit('news', { hello: 'world' });\n    socket.on('my other event', function(data) {\n        console.log(data);\n    });\n});\n\nserver.listen(8080, function() {\n    console.log('socket.io server listening at %s', server.url);\n});\n"
  },
  {
    "path": "examples/spdy/keys/spdy-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIICHzCCAYgCCQCPPSUAa8QZojANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJS\nVTETMBEGA1UECBMKU29tZS1TdGF0ZTENMAsGA1UEBxMET21zazEhMB8GA1UEChMY\nSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTExMDQwOTEwMDY0NVoXDTExMDUw\nOTEwMDY0NVowVDELMAkGA1UEBhMCUlUxEzARBgNVBAgTClNvbWUtU3RhdGUxDTAL\nBgNVBAcTBE9tc2sxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCB\nnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1bn25sPkv46wl70BffxradlkRd/x\np5Xf8HDhPSfzNNctERYslXT2fX7Dmfd5w1XTVqqGqJ4izp5VewoVOHA8uavo3ovp\ngNWasil5zADWaM1T0nnV0RsFbZWzOTmm1U3D48K8rW3F5kOZ6f4yRq9QT1gF/gN7\n5Pt494YyYyJu/a8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBuRZisIViI2G/R+w79\nvk21TzC/cJ+O7tKsseDqotXYTH8SuimEH5IWcXNgnWhNzczwN8s2362NixyvCipV\nyd4wzMpPbjIhnWGM0hluWZiK2RxfcqimIBjDParTv6CMUIuwGQ257THKY8hXGg7j\nUws6Lif3P9UbsuRiYPxMgg98wg==\n-----END CERTIFICATE-----\n\n"
  },
  {
    "path": "examples/spdy/keys/spdy-csr.pem",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBkzCB/QIBADBUMQswCQYDVQQGEwJSVTETMBEGA1UECBMKU29tZS1TdGF0ZTEN\nMAsGA1UEBxMET21zazEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF\n3/Gnld/wcOE9J/M01y0RFiyVdPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+je\ni+mA1ZqyKXnMANZozVPSedXRGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+\nA3vk+3j3hjJjIm79rwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAiNWhz6EppIVa\nFfUaB3sLeqfamb9tg9kBHtvqj/FJni0snqms0kPWaTySEPHZF0irIb7VVdq/sVCb\n3gseMVSyoDvPJ4lHC3PXqGQ7kM1mIPhDnR/4HDA3BhlGhTXSDIHgZnvI+HMBdsyC\nhC3dz5odyKqe4nmoofomALkBL9t4H8s=\n-----END CERTIFICATE REQUEST-----\n\n"
  },
  {
    "path": "examples/spdy/keys/spdy-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF3/Gnld/wcOE9J/M01y0RFiyV\ndPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+jei+mA1ZqyKXnMANZozVPSedXR\nGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+A3vk+3j3hjJjIm79rwIDAQAB\nAoGAAv2QI9h32epQND9TxwSCKD//dC7W/cZOFNovfKCTeZjNK6EIzKqPTGA6smvR\nC1enFl5adf+IcyWqAoe4lkqTvurIj+2EhtXdQ8DBlVuXKr3xvEFdYxXPautdTCF6\nKbXEyS/s1TZCRFjYftvCrXxc3pK45AQX/wg7z1K+YB5pyIECQQD0OJvLoxLYoXAc\nFZraIOZiDsEbGuSHqoCReFXH75EC3+XGYkH2bQ/nSIZ0h1buuwQ/ylKXOlTPT3Qt\nXm1OQEBvAkEA4AjWsIO/rRpOm/Q2aCrynWMpoUXTZSbL2yGf8pxp/+8r2br5ier0\nM1LeBb/OPY1+k39NWLXxQoo64xoSFYk2wQJAd2wDCwX4HkR7HNCXw1hZL9QFK6rv\n20NN0VSlpboJD/3KT0MW/FiCcVduoCbaJK0Au+zEjDyy4hj5N4I4Mw6KMwJAXVAx\nI+psTsxzS4/njXG+BgIEl/C+gRYsuMQDnAi8OebDq/et8l0Tg8ETSu++FnM18neG\nntmBeMacinUUbTXuwQJBAJp/onZdsMzeVulsGrqR1uS+Lpjc5Q1gt5ttt2cxj91D\nrio48C/ZvWuKNE8EYj2ALtghcVKRvgaWfOxt2GPguGg=\n-----END RSA PRIVATE KEY-----\n\n"
  },
  {
    "path": "examples/spdy/spdy.js",
    "content": "var path = require('path');\nvar fs = require('fs');\nvar pino = require('pino');\nvar restify = require('../../lib');\n\nvar srv = restify.createServer({\n    spdy: {\n        cert: fs.readFileSync(path.join(__dirname, './keys/spdy-cert.pem')),\n        key: fs.readFileSync(path.join(__dirname, './keys/spdy-key.pem')),\n        ca: fs.readFileSync(path.join(__dirname, 'keys/spdy-csr.pem'))\n    }\n});\n\nsrv.get('/', function(req, res, next) {\n    res.send({ hello: 'world' });\n    next();\n});\n\nsrv.on(\n    'after',\n    restify.plugins.auditLogger({\n        event: 'after',\n        body: true,\n        log: pino({name: 'audit'})\n    })\n);\n\nsrv.listen(8080, function() {\n    console.log('ready on %s', srv.url);\n});\n"
  },
  {
    "path": "examples/todoapp/README.md",
    "content": "# tl;dr\n\nThis is a (small) sample app using a reasonable subset of restify components to\nillustrate how you go about structuring a restify application.  This is pretty\nminimal, and most logic is contained in `server.js`; in reality, you'd probably\nbreak up your logic into a set of files that are easier to maintain.  But for\nthe purpose of this app, that suffices.\n\n# What is it?\n\nI just cooked up a small \"TODO\" REST API.  You get a simple CRUD interface over\nJSON on managing TODOs, and TODOs are stored locally on the file system.  The\ncode should be commented enough to help you make sense of it.\n\n# What's included?\n\nI cooked up a small server, and a small client wrapper to illustrate how I\nusually use restify in my own projects;  typically, I have a server application\nthat does whatever API thing I need it to do, and I \"wrap\" the restify client(s)\nas appropriate to deliver a \"high-level\" SDK that users can code against; note\nI tend to try to hide HTTP when you're at that level so the system is easier to\nwork with.\n\nI also cooked up a small set of unit tests using\n[nodeunit](https://github.com/caolan/nodeunit), as several times questions have\ncome up as to how to mock, or unit test a restify service.  I typically\nstructure my app so that I can either:\n\n- Run it on a UNIX Domain Socket as part of the unit test\n- Just require an endpoint to be running, and pass it in as an env var\n\nHere I chose the former option; run with `npm test`.\n\n# How do I run this?\n\nFirst, this has a `package.json`, so you'll need to run `npm install` in the\ndirectory. Once you've done that, to get started _and_ see audit logs on your\nterminal, run it like this:\n\n    $ node main.js 2>&1 | npx pino-pretty\n\nIf you want to see all the built in restify tracing:\n\n    $ node main.js -vv 2>&1 | npx pino-pretty\n\nBy default, this program writes to a directory in `/tmp`, but you can override\nwith a `-d` option.  Additionally, by default it does not require\nauthentication, but you can require that with:\n\n    $ node main.js -u admin -z secret 2>&1 | npx pino-pretty\n\nLastly, re: the `2>&1 | npx pino-pretty` bit.  In production, you assuredly would *not*\nwant to pipe to the [pino-pretty](https://github.com/pinojs/pino-pretty) CLI, but\nrather capture the audit records in their raw form, so they would be easy to\npost process and perform analytics on.  Like all UNIX programs should, this\nexample writes \"informational\" messages to `stderr`, and `audit` records to\nstdout.  It's up to you to redirect them as appropriate.\n\n\n# Some sample curl requests\n\nLet's get the full magilla (i.e., with auth) running:\n\n    $ node main.js -u admin -z secret 2>&1 | npx pino-pretty\n\nAlso, before we go any further, install the\n[json](https://github.com/trentm/json) tool as all the examples below use that.\n\n## List Routes\n\n    $ curl -isS http://127.0.0.1:8080 | json\n    HTTP/1.1 200 OK\n    Content-Type: application/todo\n    Content-Length: 127\n    Date: Sat, 29 Dec 2012 23:05:05 GMT\n    Connection: keep-alive\n\n    [\n      \"GET     /\",\n      \"POST    /todo\",\n      \"GET     /todo\",\n      \"DELETE  /todo\",\n      \"PUT     /todo/:name\",\n      \"GET     /todo/:name\",\n      \"DELETE  /todo/:name\"\n    ]\n\n\n## List TODOs (empty)\n\n    $ curl -isS http://127.0.0.1:8080/todo | json\n    HTTP/1.1 200 OK\n    Content-Type: application/todo\n    Content-Length: 2\n    Date: Sat, 29 Dec 2012 23:07:05 GMT\n    Connection: keep-alive\n\n    []\n\n\n## Create TODO\n\n    $ curl -isS http://127.0.0.1:8080/todo -X POST -d name=demo -d task=\"buy milk\"\n    HTTP/1.1 201 Created\n    Content-Type: application/todo\n    Content-Length: 8\n    Date: Sat, 29 Dec 2012 23:08:04 GMT\n    Connection: keep-alive\n\n    buy milk\n\nAha! Note that here the `content-type` was `application/foo` because our server\nset the `q-val` highest for that, and curl sets the `accept` header to `*/*`.\n\n\n## List\n\n    $ curl -isS http://127.0.0.1:8080/todo | json\n    HTTP/1.1 200 OK\n    Content-Type: application/todo\n    Content-Length: 8\n    Date: Sat, 29 Dec 2012 23:09:45 GMT\n    Connection: keep-alive\n\n    [\n      \"demo\"\n    ]\n\n\n## Get TODO\n\nNote here our server was setup to use streaming, and we explicitly opted for\nJSON:\n\n    $ curl -isS http://127.0.0.1:8080/todo/demo | json\n    HTTP/1.1 200 OK\n    Content-Type: application/json\n    Date: Sat, 29 Dec 2012 23:11:19 GMT\n    Connection: keep-alive\n    Transfer-Encoding: chunked\n\n    {\n      \"name\": \"demo\",\n      \"task\": \"buy milk\"\n    }\n\nHowever, we still supported the full negotiation via another means:\n\n    $ curl -isS -H accept:application/todo http://127.0.0.1:8080/todo/demo\n    HTTP/1.1 200 OK\n    Content-Type: application/todo\n    Content-Length: 8\n    Date: Sat, 29 Dec 2012 23:14:31 GMT\n    Connection: keep-alive\n\n    buy milk\n\n\n## Delete all\n\n    $ curl -isS -X DELETE http://127.0.0.1:8080/todo/demo\n    HTTP/1.1 204 No Content\n    Date: Sat, 29 Dec 2012 23:15:50 GMT\n    Connection: keep-alive\n"
  },
  {
    "path": "examples/todoapp/lib/client.js",
    "content": "// Copyright (c) 2012 Mark Cavage. All rights reserved.\n\nvar util = require('util');\n\nvar assert = require('assert-plus');\nvar clients = require('restify-clients');\n\n///--- Globals\n\nvar sprintf = util.format;\n\n///--- API\n\nfunction TodoClient(options) {\n    assert.object(options, 'options');\n    assert.object(options.log, 'options.log');\n    assert.optionalString(options.socketPath, 'options.socketPath');\n    assert.optionalString(options.url, 'options.url');\n    assert.optionalString(options.version, 'options.version');\n\n    var ver = options.version || '~1.0';\n\n    this.client = clients.createJSONClient({\n        log: options.log,\n        name: 'TodoClient',\n        socketPath: options.socketPath,\n        url: options.url,\n        version: ver\n    });\n    this.log = options.log.child({ component: 'TodoClient' }, true);\n    this.url = options.url;\n    this.version = ver;\n\n    if (options.username && options.password) {\n        this.username = options.username;\n        this.client.basicAuth(options.username, options.password);\n    }\n}\n\nTodoClient.prototype.create = function create(task, cb) {\n    assert.string(task, 'task');\n    assert.func(cb, 'callback');\n\n    this.client.post('/todo', { task: task }, function(err, req, res, obj) {\n        if (err) {\n            cb(err);\n        } else {\n            cb(null, obj);\n        }\n    });\n};\n\nTodoClient.prototype.list = function list(cb) {\n    assert.func(cb, 'callback');\n\n    this.client.get('/todo', function(err, req, res, obj) {\n        if (err) {\n            cb(err);\n        } else {\n            cb(null, obj);\n        }\n    });\n};\n\nTodoClient.prototype.get = function get(name, cb) {\n    assert.string(name, 'name');\n    assert.func(cb, 'callback');\n\n    this.client.get('/todo/' + name, function(err, req, res, obj) {\n        if (err) {\n            cb(err);\n        } else {\n            cb(null, obj);\n        }\n    });\n};\n\nTodoClient.prototype.update = function update(todo, cb) {\n    assert.object(todo, 'todo');\n    assert.func(cb, 'callback');\n\n    this.client.put('/todo/' + todo.name, todo, function(err) {\n        if (err) {\n            cb(err);\n        } else {\n            cb(null);\n        }\n    });\n};\n\nTodoClient.prototype.del = function del(name, cb) {\n    if (typeof name === 'function') {\n        cb = name;\n        name = '';\n    }\n    assert.string(name, 'name');\n    assert.func(cb, 'callback');\n\n    var p = '/todo' + (name.length > 0 ? '/' + name : '');\n    this.client.del(p, function(err) {\n        if (err) {\n            cb(err);\n        } else {\n            cb(null);\n        }\n    });\n};\n\nTodoClient.prototype.toString = function toString() {\n    var str = sprintf(\n        '[object TodoClient<url=%s, username=%s, version=%s]',\n        this.url,\n        this.username || 'null',\n        this.version\n    );\n    return str;\n};\n\n///--- API\n\nmodule.exports = {\n    createClient: function createClient(options) {\n        return new TodoClient(options);\n    }\n};\n"
  },
  {
    "path": "examples/todoapp/lib/index.js",
    "content": "// Copyright (c) 2012 Mark Cavage. All rights reserved.\n\nmodule.exports = {\n    createClient: require('./client').createClient,\n    createServer: require('./server').createServer\n};\n"
  },
  {
    "path": "examples/todoapp/lib/server.js",
    "content": "// Copyright (c) 2012 Mark Cavage. All rights reserved.\n\nvar fs = require('fs');\nvar path = require('path');\nvar util = require('util');\n\nvar assert = require('assert-plus');\nvar pino = require('pino');\nvar restify = require('restify');\nvar errors = require('restify-errors');\n\n///--- Errors\n\nvar MissingTaskError = errors.makeConstructor('MissingTaskError', {\n    statusCode: 409,\n    restCode: 'MissingTask',\n    message: '\"task\" is a required parameter'\n});\n\nvar TodoExistsError = errors.makeConstructor('TodoExistsError', {\n    statusCode: 409,\n    restCode: 'TodoExists',\n    message: 'Todo already exists'\n});\n\nvar TodoNotFoundError = errors.makeConstructor('TodoNotFoundError', {\n    statusCode: 404,\n    restCode: 'TodoNotFound',\n    message: 'Todo was not found'\n});\n\n///--- Formatters\n\n/**\n * This is a nonsensical custom content-type 'application/todo', just to\n * demonstrate how to support additional content-types.  Really this is\n * the same as text/plain, where we pick out 'task' if available\n */\nfunction formatTodo(req, res, body, cb) {\n    if (body instanceof Error) {\n        res.statusCode = body.statusCode || 500;\n        body = body.message;\n    } else if (typeof body === 'object') {\n        body = body.task || JSON.stringify(body);\n    } else {\n        body = body.toString();\n    }\n\n    res.setHeader('Content-Length', Buffer.byteLength(body));\n    return cb(null, body);\n}\n\n///--- Handlers\n\n/**\n * Only checks for HTTP Basic Authenticaion\n *\n * Some handler before is expected to set the accepted user/pass combo\n * on req as:\n *\n * req.allow = { user: '', pass: '' };\n *\n * Or this will be skipped.\n */\nfunction authenticate(req, res, next) {\n    if (!req.allow) {\n        req.log.debug('skipping authentication');\n        next();\n        return;\n    }\n\n    var authz = req.authorization.basic;\n\n    if (!authz) {\n        res.setHeader('WWW-Authenticate', 'Basic realm=\"todoapp\"');\n        next(new errors.UnauthorizedError('authentication required'));\n        return;\n    }\n\n    if (\n        authz.username !== req.allow.user ||\n        authz.password !== req.allow.pass\n    ) {\n        next(new errors.ForbiddenError('invalid credentials'));\n        return;\n    }\n\n    next();\n}\n\n/**\n * Note this handler looks in `req.params`, which means we can load request\n * parameters in a \"mixed\" way, like:\n *\n * POST /todo?name=foo HTTP/1.1\n * Host: localhost\n * Content-Type: application/json\n * Content-Length: ...\n *\n * {\"task\": \"get milk\"}\n *\n * Which would have `name` and `task` available in req.params\n */\nfunction createTodo(req, res, next) {\n    if (!req.body.task) {\n        req.log.warn({ body: req.body }, 'createTodo: missing task');\n        next(new MissingTaskError());\n        return;\n    }\n\n    var todo = {\n        name: req.body.name || req.body.task.replace(/\\W+/g, '_'),\n        task: req.body.task\n    };\n\n    if (req.todos.indexOf(todo.name) !== -1) {\n        req.log.warn('%s already exists', todo.name);\n        next(new TodoExistsError(todo.name));\n        return;\n    }\n\n    var p = path.normalize(req.dir + '/' + todo.name);\n    fs.writeFile(p, JSON.stringify(todo), function(err) {\n        if (err) {\n            req.log.warn(err, 'createTodo: unable to save');\n            next(err);\n        } else {\n            req.log.debug({ todo: todo }, 'createTodo: done');\n            res.send(201, todo);\n            next();\n        }\n    });\n}\n\n/**\n * Deletes a TODO by name\n */\nfunction deleteTodo(req, res, next) {\n    fs.unlink(req.todo, function(err) {\n        if (err) {\n            req.log.warn(err, 'deleteTodo: unable to unlink %s', req.todo);\n            next(err);\n        } else {\n            res.send(204);\n            next();\n        }\n    });\n}\n\n/**\n * Deletes all TODOs (in parallel)\n */\nfunction deleteAll(req, res, next) {\n    var done = 0;\n\n    // Note this is safe, as restify ensures \"next\" is called\n    // only once\n    function cb(err) {\n        if (err) {\n            req.log.warn(err, 'unable to delete a TODO');\n            next(err);\n        } else if (++done === req.todos.length) {\n            next();\n        }\n    }\n\n    if (req.todos.length === 0) {\n        next();\n        return;\n    }\n\n    req.todos.forEach(function(t) {\n        var p = req.dir + '/' + t;\n        fs.unlink(p, cb);\n    });\n}\n\n/**\n * Simply checks that a todo on /todo/:name was loaded.\n * Requires loadTodos to have run.\n */\nfunction ensureTodo(req, res, next) {\n    assert.arrayOfString(req.todos, 'req.todos');\n\n    if (req.params.name && req.todos.indexOf(req.params.name) === -1) {\n        req.log.warn('%s not found', req.params.name);\n        next(new TodoNotFoundError(req.params.name));\n    } else {\n        next();\n    }\n}\n\n/**\n * Loads a TODO by name\n *\n * Requires `loadTodos` to have run.\n *\n * Note this function uses streaming, as that seems to come up a lot\n * on the mailing list and issue board.  restify lets you use the HTTP\n * objects as they are in \"raw\" node.\n *\n * Note by using the \"raw\" node APIs, you'll need to handle content\n * negotiation yourself.\n *\n */\nfunction getTodo(req, res, next) {\n    if (req.accepts('json')) {\n        var fstream = fs.createReadStream(req.todo);\n\n        fstream.once('open', function onOpen() {\n            res.setHeader('Content-Type', 'application/json');\n            res.writeHead(200);\n            fstream.pipe(res);\n            fstream.once('end', next);\n        });\n\n        // Really, you'd want to disambiguate the error code\n        // from 'err' here to return 403, 404, etc., but this\n        // is just a demo, so you get a 500\n        fstream.once('error', next);\n        return;\n    }\n\n    fs.readFile(req.todo, 'utf8', function(err, data) {\n        if (err) {\n            req.log.warn(err, 'get: unable to read %s', req.todo);\n            next(err);\n            return;\n        }\n\n        res.send(200, JSON.parse(data));\n        next();\n    });\n}\n\n/**\n * Loads up all the stored TODOs from our \"database\". Most of the downstream\n * handlers look for these and do some amount of enforcement on what's there.\n */\nfunction loadTodos(req, res, next) {\n    fs.readdir(req.dir, function(err, files) {\n        if (err) {\n            req.log.warn(err, 'loadTodo: unable to read %s', req.dir);\n            next(err);\n        } else {\n            req.todos = files;\n\n            if (req.params.name) {\n                req.todo = req.dir + '/' + req.params.name;\n            }\n\n            req.log.debug(\n                {\n                    todo: req.todo,\n                    todos: req.todos\n                },\n                'loadTODO: done'\n            );\n\n            next();\n        }\n    });\n}\n\n/**\n * Simple returns the list of TODOs that were loaded.\n * This requires loadTodo to have run already.\n */\nfunction listTodos(req, res, next) {\n    assert.arrayOfString(req.todos, 'req.todos');\n\n    res.send(200, req.todos);\n    next();\n}\n\n/**\n * Replaces a TODO completely\n */\nfunction putTodo(req, res, next) {\n    if (!req.body.task) {\n        req.log.warn({ params: req.params, body: req.body }, 'putTodo: missing task');\n        next(new MissingTaskError());\n        return;\n    }\n\n    fs.writeFile(req.todo, JSON.stringify(req.body), function(err) {\n        if (err) {\n            req.log.warn(err, 'putTodo: unable to save');\n            next(err);\n        } else {\n            req.log.debug({ todo: req.body }, 'putTodo: done');\n            res.send(204);\n            next();\n        }\n    });\n}\n\n///--- API\n\n/**\n * Returns a server with all routes defined on it\n */\nfunction createServer(options) {\n    assert.object(options, 'options');\n    assert.string(options.directory, 'options.directory');\n    assert.object(options.log, 'options.log');\n\n    // Create a server with our logger and custom formatter\n    // Note that 'version' means all routes will default to\n    // 1.0.0\n    var server = restify.createServer({\n        formatters: {\n            'application/todo; q=0.9': formatTodo\n        },\n        log: options.log,\n        name: 'todoapp',\n        version: '1.0.0'\n    });\n\n    // Ensure we don't drop data on uploads\n    server.pre(restify.plugins.pre.pause());\n\n    // Clean up sloppy paths like //todo//////1//\n    server.pre(restify.plugins.pre.sanitizePath());\n\n    // Handles annoying user agents (curl)\n    server.pre(restify.plugins.pre.userAgentConnection());\n\n    // Set a per request pino logger (with requestid filled in)\n    server.use(restify.plugins.requestLogger());\n\n    // Allow 5 requests/second by IP, and burst to 10\n    server.use(\n        restify.plugins.throttle({\n            burst: 10,\n            rate: 5,\n            ip: true\n        })\n    );\n\n    // Use the common stuff you probably want\n    server.use(restify.plugins.acceptParser(server.acceptable));\n    server.use(restify.plugins.dateParser());\n    server.use(restify.plugins.authorizationParser());\n    server.use(restify.plugins.queryParser());\n    server.use(restify.plugins.gzipResponse());\n    server.use(restify.plugins.bodyParser());\n\n    // Now our own handlers for authentication/authorization\n    // Here we only use basic auth, but really you should look\n    // at https://github.com/joyent/node-http-signature\n    server.use(function setup(req, res, next) {\n        req.dir = options.directory;\n\n        if (options.user && options.password) {\n            req.allow = {\n                user: options.user,\n                password: options.password\n            };\n        }\n        next();\n    });\n    server.use(authenticate);\n\n    /// Now the real handlers. Here we just CRUD on TODO blobs\n\n    server.use(loadTodos);\n\n    server.post('/todo', createTodo);\n    server.get('/todo', listTodos);\n    server.head('/todo', listTodos);\n\n    // everything else requires that the TODO exist\n    server.use(ensureTodo);\n\n    // Return a TODO by name\n\n    server.get('/todo/:name', getTodo);\n    server.head('/todo/:name', getTodo);\n\n    // Overwrite a complete TODO - here we require that the body\n    // be JSON - otherwise the caller will get a 415 if they try\n    // to send a different type\n    // With the body parser, req.body will be the fully JSON\n    // parsed document, so we just need to serialize and save\n    server.put(\n        {\n            path: '/todo/:name',\n            contentType: 'application/json'\n        },\n        putTodo\n    );\n\n    // Delete a TODO by name\n    server.del('/todo/:name', deleteTodo);\n\n    // Destroy everything\n    server.del('/todo', deleteAll, function respond(req, res, next) {\n        res.send(204);\n        next();\n    });\n\n    // Register a default '/' handler\n\n    server.get('/', function root(req, res, next) {\n        var routes = [\n            'GET     /',\n            'POST    /todo',\n            'GET     /todo',\n            'DELETE  /todo',\n            'PUT     /todo/:name',\n            'GET     /todo/:name',\n            'DELETE  /todo/:name'\n        ];\n        res.send(200, routes);\n        next();\n    });\n\n    // Setup an audit logger\n    if (!options.noAudit) {\n        server.on(\n            'after',\n            restify.auditLogger({\n                body: true,\n                log: pino({ level: 'info', name: 'todoapp-audit' })\n            })\n        );\n    }\n\n    return server;\n}\n\n///--- Exports\n\nmodule.exports = {\n    createServer: createServer\n};\n"
  },
  {
    "path": "examples/todoapp/main.js",
    "content": "// Copyright (c) 2012 Mark Cavage. All rights reserved.\n\nvar fs = require('fs');\nvar path = require('path');\n\nvar pino = require('pino');\nvar getopt = require('posix-getopt');\nvar restify = require('restify');\n\nvar todo = require('./lib');\n\n///--- Globals\n\nvar NAME = 'todoapp';\n\nvar LOG = pino({ name: NAME });\n\n///--- Helpers\n\n/**\n * Standard POSIX getopt-style options parser.\n *\n * Some options, like directory/user/port are pretty cut and dry, but note\n * the 'verbose' or '-v' option afflicts the log level, repeatedly. So you\n * can run something like:\n *\n * node main.js -p 80 -vv 2>&1 | npx pino-pretty\n *\n * And the log level will be set to TRACE.\n */\nfunction parseOptions() {\n    var option;\n    var opts = {};\n    var parser = new getopt.BasicParser('hvd:p:u:z:', process.argv);\n\n    while ((option = parser.getopt()) !== undefined) {\n        switch (option.option) {\n            case 'd':\n                opts.directory = path.normalize(option.optarg);\n                break;\n\n            case 'h':\n                usage();\n                break;\n\n            case 'p':\n                opts.port = parseInt(option.optarg, 10);\n                break;\n\n            case 'u':\n                opts.user = option.optarg;\n                break;\n\n            case 'v':\n                // Allows us to set -vvv -> this little hackery\n                // just ensures that we're never < TRACE\n                LOG.level(Math.max(pino.levels.values.trace, LOG.level - 10));\n\n                if (LOG.level <= pino.levels.values.debug) {\n                    LOG = LOG.child({ src: true });\n                }\n                break;\n\n            case 'z':\n                opts.password = option.optarg;\n                break;\n\n            default:\n                usage('invalid option: ' + option.option);\n                break;\n        }\n    }\n\n    return opts;\n}\n\nfunction usage(msg) {\n    if (msg) {\n        console.error(msg);\n    }\n\n    var str =\n        'usage: ' + NAME + ' [-v] [-d dir] [-p port] [-u user] [-z password]';\n    console.error(str);\n    process.exit(msg ? 1 : 0);\n}\n\n///--- Mainline\n\n(function main() {\n    var options = parseOptions();\n\n    LOG.debug(options, 'command line arguments parsed');\n\n    // First setup our 'database'\n    var dir = path.normalize((options.directory || '/tmp') + '/todos');\n\n    try {\n        fs.mkdirSync(dir);\n    } catch (e) {\n        if (e.code !== 'EEXIST') {\n            LOG.fatal(e, 'unable to create \"database\" %s', dir);\n            process.exit(1);\n        }\n    }\n\n    var server = todo.createServer({\n        directory: dir,\n        log: LOG\n    });\n\n    // At last, let's rock and roll\n    server.listen(options.port || 8080, function onListening() {\n        LOG.info('listening at %s', server.url);\n    });\n})();\n"
  },
  {
    "path": "examples/todoapp/package.json",
    "content": "{\n        \"name\": \"restify-example\",\n        \"version\": \"0.0.0\",\n        \"description\": \"Kitchen Sink App of restify\",\n        \"main\": \"main.js\",\n        \"dependencies\": {\n                \"assert-plus\": \"1.0.0\",\n                \"pino\": \"^7.11.0\",\n                \"posix-getopt\": \"^1.2.1\",\n                \"restify-errors\": \"^8.0.2\",\n                \"restify-clients\": \"^3.1.0\",\n                \"restify\": \"^8.6.1\"\n        },\n        \"devDependencies\": {\n                \"mocha\": \"^10.0.0\",\n                \"chai\": \"^4.3.6\",\n                \"pino-pretty\": \"^7.6.1\"\n        },\n        \"scripts\": {\n                \"start\": \"node main.js 2>&1 | pino-pretty\",\n                \"test\": \"mocha test\"\n        },\n        \"author\": \"Mark Cavage\",\n        \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "examples/todoapp/test/todo.test.js",
    "content": "// Copyright (c) 2012 Mark Cavage. All rights reserved.\n\nvar fs = require('fs');\n\nvar pino = require('pino');\nvar restify = require('restify');\nvar assert = require('chai').assert;\n\nvar todo = require('../lib');\n\n///--- Globals\n\nvar DIR = '/tmp/.todo_unit_test';\nvar SOCK = '/tmp/.todo_sock';\n\n///--- Tests\n\ndescribe('todoapp', function () {\n    var CLIENT;\n    var SERVER;\n\n    before(function (done) {\n        var log = pino({\n            name: 'todo_unit_test',\n            level: process.env.LOG_LEVEL || 'info'\n        });\n    \n        fs.mkdir(DIR, function(err) {\n            if (err && err.code !== 'EEXIST') {\n                console.error('unable to mkdir: ' + err.stack);\n                process.exit(1);\n            }\n    \n            SERVER = todo.createServer({\n                directory: DIR,\n                log: log.child({ component: 'server' }, true),\n                noAudit: true\n            });\n    \n            assert.ok(SERVER);\n            SERVER.listen(SOCK, function() {\n                CLIENT = todo.createClient({\n                    log: log.child({ component: 'client' }, true),\n                    socketPath: SOCK\n                });\n                assert.ok(CLIENT);\n                done();\n            });\n        });    \n    });\n\n    it('should return an empty list', function (done) {\n        CLIENT.list(function(err, todos) {\n            assert.ifError(err);\n            assert.ok(todos);\n            assert.ok(Array.isArray(todos));\n    \n            if (todos) {\n                assert.equal(todos.length, 0);\n            }\n            done();\n        });\n    });\n\n    it('should create a new task', function (done) {\n        var task = 'check that unit test works';\n        CLIENT.create(task, function(err, todo) {\n            assert.ifError(err);\n            assert.ok(todo);\n    \n            if (todo) {\n                assert.ok(todo.name);\n                assert.equal(todo.task, task);\n            }\n            done();\n        });\n    });\n\n    it('should list and get', function (done) {\n        CLIENT.list(function(err, todos) {\n            assert.ifError(err);\n            assert.ok(todos);\n            assert.ok(Array.isArray(todos));\n    \n            if (todos) {\n                assert.equal(todos.length, 1);\n                CLIENT.get(todos[0], function(err2, todo) {\n                    assert.ifError(err2);\n                    assert.ok(todo);\n                    done();\n                });\n            } else {\n                done();\n            }\n        });\n    });\n\n    it('should update', function (done) {\n        CLIENT.list(function(err, todos) {\n            assert.ifError(err);\n            assert.ok(todos);\n            assert.ok(Array.isArray(todos));\n    \n            if (todos) {\n                assert.equal(todos.length, 1);\n    \n                var todo = {\n                    name: todos[0],\n                    task: 'something else'\n                };\n                CLIENT.update(todo, function(err2) {\n                    assert.ifError(err2);\n                    done();\n                });\n            } else {\n                done();\n            }\n        });\n    });\n\n    after(function(done) {\n        CLIENT.del(function(err) {\n            assert.ifError(err);\n            CLIENT.client.close();\n            SERVER.close(function () {\n                fs.rmdir(DIR, function(err) {\n                    assert.ifError(err);\n                    done();\n                });\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "lib/chain.js",
    "content": "'use strict';\n\nvar assert = require('assert-plus');\nvar once = require('once');\nvar customErrorTypes = require('./errorTypes');\n\nmodule.exports = Chain;\n\n/**\n * Create a new middleware chain\n *\n * @public\n * @class Chain\n * @param {Object} [options] - options\n * @param {Boolean} [options.onceNext=false] - Prevents calling next multiple\n *  times\n * @param {Boolean} [options.strictNext=false] - Throws error when next() is\n *  called more than once, enables onceNext option\n * @example\n * var chain = new Chain();\n * chain.add(function (req, res, next) { next(); })\n * // chain.add(function (req, res, next) { next(new Error('Foo')); })\n * // chain.add(function (req, res, next) { next(false); })\n *\n * http.createServer((req, res) => {\n *    chain.run(req, res, function done(err) {\n *       res.end(err ? err.message : 'hello world');\n *    });\n * })\n */\nfunction Chain(options) {\n    assert.optionalObject(options, 'options');\n    options = options || {};\n    assert.optionalBool(options.onceNext, 'options.onceNext');\n    assert.optionalBool(options.strictNext, 'options.strictNext');\n\n    this.onceNext = !!options.onceNext;\n    this.strictNext = !!options.strictNext;\n\n    // strictNext next enforces onceNext\n    if (this.strictNext) {\n        this.onceNext = true;\n    }\n\n    this._stack = [];\n    this._once = this.strictNext === false ? once : once.strict;\n}\n\n/**\n * Public methods.\n * @private\n */\n\n/**\n * Get handlers of a chain instance\n *\n * @memberof Chain\n * @instance\n * @returns {Function[]} handlers\n */\nChain.prototype.getHandlers = function getHandlers() {\n    return this._stack;\n};\n\n/**\n * Utilize the given middleware `handler`\n *\n * @public\n * @memberof Chain\n * @instance\n * @param {Function} handler - handler\n * @returns {undefined} no return value\n */\nChain.prototype.add = function add(handler) {\n    assert.func(handler);\n    var handlerId = handler._identifier || handler._name || handler.name;\n    if (handler.length <= 2) {\n        // arity <= 2, must be AsyncFunction\n        assert.equal(\n            handler.constructor.name,\n            'AsyncFunction',\n            `Handler [${handlerId}] is missing a third argument (the ` +\n                '\"next\" callback) but is not an async function. Middleware ' +\n                'handlers can be either async/await or callback-based.' +\n                'Callback-based (non-async) handlers should accept three ' +\n                'arguments: (req, res, next). Async handler functions should ' +\n                'accept maximum of 2 arguments: (req, res).'\n        );\n    } else {\n        // otherwise shouldn't be AsyncFunction\n        assert.notEqual(\n            handler.constructor.name,\n            'AsyncFunction',\n            `Handler [${handlerId}] accepts a third argument (the 'next\" ` +\n                'callback) but is also an async function. Middleware ' +\n                'handlers can be either async/await or callback-based. Async ' +\n                'handler functions should accept maximum of 2 arguments: ' +\n                '(req, res). Non-async handlers should accept three ' +\n                'arguments: (req, res, next).'\n        );\n    }\n\n    // _name is assigned in the server and router\n    handler._name = handler._name || handler.name;\n\n    // add the middleware\n    this._stack.push(handler);\n};\n\n/**\n * Returns the number of handlers\n *\n * @public\n * @memberof Chain\n * @instance\n * @returns {Number} number of handlers in the stack\n */\nChain.prototype.count = function count() {\n    return this._stack.length;\n};\n\n/**\n * Handle server requests, punting them down\n * the middleware stack.\n *\n * @public\n * @memberof Chain\n * @instance\n * @param {Request} req - request\n * @param {Response} res - response\n * @param {Function} done - final handler\n * @returns {undefined} no return value\n */\nChain.prototype.run = function run(req, res, done) {\n    var self = this;\n    var index = 0;\n\n    function next(err) {\n        // next callback\n        var handler = self._stack[index++];\n\n        // all done or request closed\n        if (!handler || req.connectionState() === 'close') {\n            process.nextTick(function nextTick() {\n                return done(err, req, res);\n            });\n            return;\n        }\n\n        // call the handler\n        call(handler, err, req, res, self.onceNext ? self._once(next) : next);\n    }\n\n    next();\n    return;\n};\n\n/**\n * Helper functions\n * @private\n */\n\n/**\n * Invoke a handler.\n *\n * @private\n * @param {Function} handler - handler function\n * @param {Error|false|*} err - error, abort when true value or false\n * @param {Request} req - request\n * @param {Response} res - response\n * @param {Function} _next - next handler\n * @returns {undefined} no return value\n */\nfunction call(handler, err, req, res, _next) {\n    var arity = handler.length;\n    var hasError = err === false || Boolean(err);\n\n    // Meassure handler timings\n    // _name is assigned in the server and router\n    req._currentHandler = handler._name;\n    req.startHandlerTimer(handler._name);\n\n    function next(nextErr) {\n        req.endHandlerTimer(handler._name);\n        _next(nextErr, req, res);\n    }\n\n    function resolve(value) {\n        if (value && req.log) {\n            // logs resolved value\n            req.log.warn(\n                { value },\n                'Discarded returned value from async handler'\n            );\n        }\n\n        return next();\n    }\n\n    function reject(error) {\n        if (!(error instanceof Error)) {\n            error = new customErrorTypes.AsyncError(\n                {\n                    info: {\n                        cause: error,\n                        handler: handler._name,\n                        method: req.method,\n                        path: req.path ? req.path() : undefined\n                    }\n                },\n                'Async middleware rejected without an error'\n            );\n        }\n        return next(error);\n    }\n\n    if (hasError && arity === 4) {\n        // error-handling middleware\n        handler(err, req, res, next);\n        return;\n    } else if (!hasError && arity < 4) {\n        // request-handling middleware\n        process.nextTick(function nextTick() {\n            const result = handler(req, res, next);\n            if (result && typeof result.then === 'function') {\n                result.then(resolve, reject);\n            }\n        });\n        return;\n    }\n\n    // continue\n    next(err);\n}\n"
  },
  {
    "path": "lib/deprecationWarnings.js",
    "content": "'use strict';\n\nfunction deprecationWarnings(server) {\n    // deprecation for domains and next.ifError\n    if (server.handleUncaughtExceptions === true) {\n        server.log.warn(\n            [\n                'DEPRECATION WARNING: Due to deprecation of the domain module',\n                'in node.js, all features in restify that depend on it have',\n                'been deprecated as well.',\n                'This includes `handleUncaughtExceptions` and',\n                '`next.ifError()`. They will continue to work in 5.x, but',\n                'consider them unsupported and likely to be removed',\n                'from future versions of restify.'\n            ].join(' ')\n        );\n    }\n}\n\nmodule.exports = deprecationWarnings;\n"
  },
  {
    "path": "lib/dtrace.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n///--- Globals\n\n'use strict';\n\nvar ID = 0;\nvar MAX_INT = Math.pow(2, 32) - 1;\n\nvar PROBES = {\n    // server_name, route_name, id, method, url, headers (json)\n    'route-start': ['char *', 'char *', 'int', 'char *', 'char *', 'json'],\n\n    // server_name, route_name, handler_name, id\n    'handler-start': ['char *', 'char *', 'char *', 'int'],\n\n    // server_name, route_name, handler_name, id\n    'handler-done': ['char *', 'char *', 'char *', 'int'],\n\n    // server_name, route_name, id, statusCode, headers (json)\n    'route-done': ['char *', 'char *', 'int', 'int', 'json'],\n\n    // Client probes\n    // method, url, headers, id\n    'client-request': ['char *', 'char *', 'json', 'int'],\n\n    // id, statusCode, headers\n    'client-response': ['int', 'int', 'json'],\n\n    // id, Error.toString()\n    'client-error': ['id', 'char *']\n};\nvar PROVIDER;\n\n///--- API\n\n// eslint-disable-next-line wrap-iife\nmodule.exports = (function exportStaticProvider() {\n    if (!PROVIDER) {\n        try {\n            var dtrace = require('dtrace-provider');\n            PROVIDER = dtrace.createDTraceProvider('restify');\n        } catch (e) {\n            PROVIDER = {\n                fire: function fire() {},\n                enable: function enable() {},\n                addProbe: function addProbe() {\n                    var p = {\n                        fire: function fire() {}\n                    };\n                    return p;\n                },\n                removeProbe: function removeProbe() {},\n                disable: function disable() {}\n            };\n        }\n\n        PROVIDER._rstfy_probes = {};\n\n        Object.keys(PROBES).forEach(function forEach(p) {\n            var args = PROBES[p].splice(0);\n            args.unshift(p);\n\n            var probe = PROVIDER.addProbe.apply(PROVIDER, args);\n            PROVIDER._rstfy_probes[p] = probe;\n        });\n\n        PROVIDER.enable();\n\n        PROVIDER.nextId = function nextId() {\n            if (++ID >= MAX_INT) {\n                ID = 1;\n            }\n\n            return ID;\n        };\n    }\n\n    return PROVIDER;\n})();\n"
  },
  {
    "path": "lib/errorTypes.js",
    "content": "'use strict';\n\nvar errors = require('restify-errors');\n\n// This allows Restify to work with restify-errors v6+\nmodule.exports = {\n    RequestCloseError: errors.makeConstructor('RequestCloseError'),\n    RouteMissingError: errors.makeConstructor('RouteMissingError'),\n    AsyncError: errors.makeConstructor('AsyncError')\n};\n"
  },
  {
    "path": "lib/formatters/binary.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n///--- Exports\n\n/**\n * Binary formatter.\n *\n * @public\n * @function formatBinary\n * @param    {Object} req - the request object\n * @param    {Object} res - the response object\n * @param    {Object} body - response body\n * @returns  {Buffer} body\n */\nfunction formatBinary(req, res, body) {\n    if (!Buffer.isBuffer(body)) {\n        body = new Buffer(body.toString());\n    }\n\n    res.setHeader('Content-Length', body.length);\n\n    return body;\n}\n\nmodule.exports = formatBinary;\n"
  },
  {
    "path": "lib/formatters/index.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n///--- Exports\n\n/**\n * Format a response for being sent over the wire\n *\n * @public\n * @typedef {Function} formatter\n * @param    {Object} req - the request object (not used)\n * @param    {Object} res - the response object\n * @param    {Object} body - response body to format\n * @returns  {String} formatted response data\n */\n\nmodule.exports = {\n    'application/javascript; q=0.1': require('./jsonp'),\n    'application/json; q=0.4': require('./json'),\n    'text/plain; q=0.3': require('./text'),\n    'application/octet-stream; q=0.2': require('./binary')\n};\n"
  },
  {
    "path": "lib/formatters/json.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar errors = require('restify-errors');\n\n///--- Exports\n\n/**\n * JSON formatter. Will look for a toJson() method on the body. If one does not\n * exist then a JSON.stringify will be attempted.\n *\n * @public\n * @function formatJSON\n * @param    {Object} req - the request object (not used)\n * @param    {Object} res - the response object\n * @param    {Object} body - response body\n * @returns  {String} data\n */\nfunction formatJSON(req, res, body) {\n    var data = 'null';\n    if (body !== undefined) {\n        try {\n            data = JSON.stringify(body);\n        } catch (e) {\n            throw new errors.InternalServerError(\n                { cause: e, info: { formatter: 'json' } },\n                'could not format response body'\n            );\n        }\n    }\n\n    // Setting the content-length header is not a formatting feature and should\n    // be separated into another module\n    res.setHeader('Content-Length', Buffer.byteLength(data));\n\n    return data;\n}\n\nmodule.exports = formatJSON;\n"
  },
  {
    "path": "lib/formatters/jsonp.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n///--- Exports\n\n/**\n * JSONP formatter. like JSON, but with a callback invocation.\n * Unicode escapes line and paragraph separators.\n *\n * @public\n * @function formatJSONP\n * @param    {Object} req - the request object\n * @param    {Object} res - the response object\n * @param    {Object} body - response body\n * @returns  {String} data\n */\nfunction formatJSONP(req, res, body) {\n    if (!body) {\n        res.setHeader('Content-Length', 0);\n        return null;\n    }\n\n    if (Buffer.isBuffer(body)) {\n        body = body.toString('base64');\n    }\n\n    var _cb = req.query.callback || req.query.jsonp;\n    var data;\n\n    if (_cb) {\n        data =\n            'typeof ' +\n            _cb +\n            \" === 'function' && \" +\n            _cb +\n            '(' +\n            JSON.stringify(body) +\n            ');';\n    } else {\n        data = JSON.stringify(body);\n    }\n\n    data = data.replace(/\\u2028/g, '\\\\u2028').replace(/\\u2029/g, '\\\\u2029');\n\n    res.setHeader('Content-Length', Buffer.byteLength(data));\n    return data;\n}\n\nmodule.exports = formatJSONP;\n"
  },
  {
    "path": "lib/formatters/text.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n/**\n * Formats the body to 'text' by invoking a toString() on the body if it\n * exists. If it doesn't, then the response is a zero-length string.\n *\n * @public\n * @function formatText\n * @param    {Object} req - the request object (not used)\n * @param    {Object} res - the response object\n * @param    {Object} body - response body. If it has a toString() method this\n *                           will be used to make the string representation\n * @returns  {String} data\n */\nfunction formatText(req, res, body) {\n    // if body is null, default to empty string\n    var data = '';\n\n    data = body.toString();\n\n    // TODO: setting the content-length header is not a formatting\n    // feature and should be separated into another module\n    res.setHeader('Content-Length', Buffer.byteLength(data));\n    return data;\n}\n\nmodule.exports = formatText;\n"
  },
  {
    "path": "lib/helpers/chainComposer.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar Chain = require('../chain');\nvar _ = require('lodash');\n\nmodule.exports = composeHandlerChain;\n\n/**\n * Builds a function with the signature of a handler\n * function(req,resp,callback).\n * which internally executes the passed in array of handler function as a chain.\n *\n * @param {Array} [handlers] - handlers Array of\n * function(req,resp,callback) handlers.\n * @param {Object} [options] - options Optional option object that is\n * passed to Chain.\n * @returns {Function} Handler function that executes the handler chain when run\n */\nfunction composeHandlerChain(handlers, options) {\n    var chain = new Chain(options);\n    if (_.isArray(handlers)) {\n        handlers = _.flattenDeep(handlers);\n        handlers.forEach(function(handler) {\n            chain.add(handler);\n        });\n    } else {\n        chain.add(handlers);\n    }\n\n    return function handlerChain(req, resp, callback) {\n        chain.run(req, resp, callback);\n    };\n}\n"
  },
  {
    "path": "lib/http_date.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n/**\n * Takes an instance of a date object, formats it UTC\n * e.g., Wed, 17 Jun 2015 01:30:26 GMT\n *\n * @public\n * @function httpDate\n * @param    {Object} now - a date object\n * @returns  {String}       formatted dated object\n */\nmodule.exports = function httpDate(now) {\n    if (!now) {\n        now = new Date();\n    }\n\n    return now.toUTCString();\n};\n"
  },
  {
    "path": "lib/index.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\nvar errors = require('restify-errors');\n\nvar pino = require('pino');\nvar Router = require('./router');\nvar Server = require('./server');\nvar shallowCopy = require('./utils').shallowCopy;\n\nvar InternalError = errors.InternalError;\n\n/**\n * A restify server object is the main interface through which you will register\n * routes and handlers for incoming requests.\n *\n * @public\n * @function createServer\n * @param {Object} [options]  - an options object\n * @param {String} [options.name=\"restify\"] - Name of the server.\n * @param {Boolean} [options.dtrace=false] - enable DTrace support\n * @param {Router} [options.router=new Router(opts)] - Router\n * @param {Object} [options.log=pino({name:options.name || \"restify\"})]\n * - [pino](https://github.com/pinojs/pino) instance.\n * @param {String} [options.url] - Once listen() is called, this will be filled\n * in with where the server is running.\n * @param {String|Buffer} [options.certificate] - If you want to create an HTTPS\n * server, pass in a PEM-encoded certificate and key.\n * @param {String|Buffer} [options.key] - If you want to create an HTTPS server,\n * pass in a PEM-encoded certificate and key.\n * @param {Object} [options.formatters] - Custom response formatters for\n * `res.send()`.\n * @param {Boolean} [options.handleUncaughtExceptions=false] - When true restify\n * will use a domain to catch and respond to any uncaught\n * exceptions that occur in its handler stack.\n * Comes with significant negative performance impact.\n * @param {Object} [options.spdy] - Any options accepted by\n * [node-spdy](https://github.com/indutny/node-spdy).\n * @param {Object} [options.http2] - Any options accepted by\n * [http2.createSecureServer](https://nodejs.org/api/http2.html).\n * @param {Boolean} [options.handleUpgrades=false] - Hook the `upgrade` event\n * from the node HTTP server, pushing `Connection: Upgrade` requests through the\n *  regular request handling chain.\n * @param {Boolean} [options.onceNext=false] - Prevents calling next multiple\n *  times\n * @param {Boolean} [options.strictNext=false] - Throws error when next() is\n *  called more than once, enabled onceNext option\n * @param {Object} [options.httpsServerOptions] - Any options accepted by\n * [node-https Server](http://nodejs.org/api/https.html#https_https).\n * If provided the following restify server options will be ignored:\n * spdy, ca, certificate, key, passphrase, rejectUnauthorized, requestCert and\n * ciphers; however these can all be specified on httpsServerOptions.\n * @param {Boolean} [options.noWriteContinue=false] - prevents\n *  `res.writeContinue()` in `server.on('checkContinue')` when proxing\n * @param {Boolean} [options.ignoreTrailingSlash=false] - ignore trailing slash\n * on paths\n * @param {Boolean} [options.strictFormatters=true] - enables strict formatters\n * behavior: a formatter matching the response's content-type is required. If\n * not found, the response's content-type is automatically set to\n * 'application/octet-stream'. If a formatter for that content-type is not\n * found, sending the response errors.\n * @example\n * var restify = require('restify');\n * var server = restify.createServer();\n *\n * server.listen(8080, function () {\n *   console.log('ready on %s', server.url);\n * });\n * @returns  {Server} server\n */\nfunction createServer(options) {\n    assert.optionalObject(options, 'options');\n\n    var opts = shallowCopy(options || {});\n    var server;\n\n    // empty string should override default value.\n    opts.name = opts.hasOwnProperty('name') ? opts.name : 'restify';\n    opts.log = opts.log || pino({ name: opts.name || 'restify' });\n    opts.router = opts.router || new Router(opts);\n\n    server = new Server(opts);\n\n    if (opts.handleUncaughtExceptions) {\n        server.on('uncaughtException', function onUncaughtException(\n            req,\n            res,\n            route,\n            e\n        ) {\n            if (\n                this.listeners('uncaughtException').length > 1 ||\n                res.headersSent\n            ) {\n                return false;\n            }\n\n            res.send(new InternalError(e, e.message || 'unexpected error'));\n            return true;\n        });\n    }\n\n    return server;\n}\n\n///--- Exports\n\nmodule.exports.logger = pino;\nmodule.exports.createServer = createServer;\nmodule.exports.formatters = require('./formatters');\nmodule.exports.plugins = require('./plugins');\nmodule.exports.pre = require('./plugins').pre;\nmodule.exports.helpers = { compose: require('./helpers/chainComposer') };\n"
  },
  {
    "path": "lib/plugins/accept.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\nvar mime = require('mime');\n\nvar NotAcceptableError = require('restify-errors').NotAcceptableError;\n\n/**\n * Parses the `Accept` header, and ensures that the server can respond to what\n * the client asked for. In almost all cases passing in `server.acceptable` is\n * all that's required, as that's an array of content types the server knows\n * how to respond to (with the formatters you've registered). If the request is\n * for a non-handled type, this plugin will return a `NotAcceptableError` (406).\n *\n * Note you can get the set of types allowed from a restify server by doing\n * `server.acceptable`.\n *\n * @public\n * @function acceptParser\n * @throws   {NotAcceptableError}\n * @param    {String[]}    accepts - array of accept types.\n * @returns  {Function}              restify handler.\n * @example\n * server.use(restify.plugins.acceptParser(server.acceptable));\n */\nfunction acceptParser(accepts) {\n    var acceptable = accepts;\n\n    if (!Array.isArray(acceptable)) {\n        acceptable = [acceptable];\n    }\n    assert.arrayOfString(acceptable, 'acceptable');\n\n    acceptable = acceptable\n        .filter(function filter(a) {\n            return a;\n        })\n        .map(function map(a) {\n            return a.indexOf('/') === -1 ? mime.getType(a) : a;\n        })\n        .filter(function filter(a) {\n            return a;\n        });\n\n    var e = new NotAcceptableError('Server accepts: ' + acceptable.join());\n\n    function parseAccept(req, res, next) {\n        if (req.accepts(acceptable)) {\n            return next();\n        }\n        return next(e);\n    }\n\n    return parseAccept;\n}\n\nmodule.exports = acceptParser;\n"
  },
  {
    "path": "lib/plugins/audit.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\nvar pino = require('pino');\nvar HttpError = require('restify-errors').HttpError;\nvar errors = require('restify-errors');\nvar hrTimeDurationInMs = require('./utils/hrTimeDurationInMs');\n\n/**\n * Utility to get response headers from a given response.\n * Manually generates a POJO from `res.getHeaderNames` and `res.getHeader`,\n * if available, falling back to deprecated `res._headers`, otherwise.\n * Intentionally does not use `res.getHeaders` to avoid deserialization\n * issues with object returned by that method.\n *\n * @param {http.ServerResponse} res - the OutgoingMessage\n * @private\n * @function getResponseHeaders\n * @returns {object} map from header name to header value\n * @see https://github.com/restify/node-restify/issues/1370\n */\nfunction getResponseHeaders(res) {\n    if (res.getHeaderNames && res.getHeader) {\n        return res.getHeaderNames().reduce(function reduce(prev, curr) {\n            var header = {};\n            header[curr] = res.getHeader(curr);\n            return Object.assign({}, prev, header);\n        }, {});\n    }\n    return res._headers;\n}\n\n///--- API\n\n/**\n * @public\n * @function auditLogger\n * @param {Object} opts - The options object.\n * @param {Object} opts.log - The logger.\n * @param {String} opts.event - The event from the server which initiates the\n * log, one of 'pre', 'routed', or 'after'\n * @param {Function} [opts.context] - The optional context function of signature\n * f(req, res, route, err).  Invoked each time an audit log is generated. This\n * function can return an object that customizes the format of anything off the\n * req, res, route, and err objects. The output of this function will be\n * available on the `context` key in the audit object.\n * @param {Object} [opts.server] - The restify server, used to emit\n * the audit log object programmatically\n * @param {boolean} [opts.printLog=true] - Whether to print the log\n * via the logger.\n * @param {Object} [opts.serializers] - Override the default logger serializers\n * for err, req and res\n * @param {String} [opts.requestIdFieldName] - The name of the request id property attached\n * to log lines. Defaults to \"req_id\".\n * @returns {Function} Handler\n * @fires audit when an audit log has been generated\n * @example\n * <caption>\n * Audit logging is a special plugin, as you don't use it with `.use()`\n * but with the `after` event:\n * </caption>\n *\n * server.on('after', restify.plugins.auditLogger({\n *   log: pino(\n *     {name: 'audit'},\n *     process.stdout\n *   ),\n *   event: 'after',\n *   server: SERVER,\n *   logMetrics : logBuffer,\n *   printLog : true\n * }));\n *\n * @example\n * <caption>\n * You pass in the auditor a pino logger, optionally server object,\n * Ringbuffer and a flag printLog indicate if log needs to be print out at info\n * level or not.  By default, without specify printLog flag, it will write out\n * record lookling like this:\n * </caption>\n *\n * {\n *   \"name\": \"audit\",\n *   \"hostname\": \"your.host.name\",\n *   \"audit\": true,\n *   \"remoteAddress\": \"127.0.0.1\",\n *   \"remotePort\": 57692,\n *   \"req_id\": \"ed634c3e-1af0-40e4-ad1e-68c2fb67c8e1\",\n *   \"req\": {\n *     \"method\": \"GET\",\n *     \"url\": \"/foo\",\n *     \"headers\": {\n *       \"authorization\": \"Basic YWRtaW46am95cGFzczEyMw==\",\n *       \"user-agent\": \"curl/7.19.7 (universal-apple-darwin10.0)\n *          libcurl/7.19.7 OpenSSL/0.9.8r zlib/1.2.3\",\n *       \"host\": \"localhost:8080\",\n *       \"accept\": \"application/json\"\n *     },\n *     \"httpVersion\": \"1.1\",\n *     \"query\": {\n *         \"foo\": \"bar\"\n *     },\n *     \"trailers\": {},\n *     \"version\": \"*\",\n *     \"timers\": {\n *       \"requestLogger\": 52,\n *       \"saveAction\": 8,\n *       \"reqResTracker\": 213,\n *       \"addContext\": 8,\n *       \"addModels\": 4,\n *       \"resNamespaces\": 5,\n *       \"parseQueryString\": 11,\n *       \"instanceHeaders\": 20,\n *       \"xForwardedProto\": 7,\n *       \"httpsRedirector\": 14,\n *       \"readBody\": 21,\n *       \"parseBody\": 6,\n *       \"xframe\": 7,\n *       \"restifyCookieParser\": 15,\n *       \"fooHandler\": 23,\n *       \"barHandler\": 14,\n *       \"carHandler\": 14\n *     }\n *   },\n *   \"res\": {\n *     \"statusCode\": 200,\n *     \"headers\": {\n *       \"access-control-allow-origin\": \"*\",\n *       \"access-control-allow-headers\": \"Accept, Accept-Version,\n *          Content-Length, Content-MD5, Content-Type, Date, Api-Version\",\n *       \"access-control-expose-headers\": \"Api-Version, Request-Id,\n *          Response-Time\",\n *       \"server\": \"Joyent SmartDataCenter 7.0.0\",\n *       \"x-request-id\": \"ed634c3e-1af0-40e4-ad1e-68c2fb67c8e1\",\n *       \"access-control-allow-methods\": \"GET\",\n *       \"x-api-version\": \"1.0.0\",\n *       \"connection\": \"close\",\n *       \"content-length\": 158,\n *       \"content-md5\": \"zkiRn2/k3saflPhxXI7aXA==\",\n *       \"content-type\": \"application/json\",\n *       \"date\": \"Tue, 07 Feb 2012 20:30:31 GMT\",\n *       \"x-response-time\": 1639\n *     },\n *     \"trailer\": false\n *   },\n *   \"route\": {\n *   \"name\": \"GetFoo\",\n *   \"version\": [\"1.0.0\"]\n *   },\n *   \"secure\": false,\n *   \"level\": 30,\n *   \"msg\": \"GetFoo handled: 200\",\n *   \"time\": \"2012-02-07T20:30:31.896Z\",\n *   \"v\": 0\n * }\n *\n * @example\n * <caption>\n * The `timers` field shows the time each handler took to run in microseconds.\n * Restify by default will record this information for every handler for each\n * route. However, if you decide to include nested handlers, you can track the\n * timing yourself by utilizing the Request\n * [startHandlerTimer](#starthandlertimerhandlername) and\n * [endHandlerTimer](#endhandlertimerhandlername) API.\n * You can also listen to auditlog event and get same above log object when\n * log event emits. For example\n * </caption>\n * SERVER.on('auditlog', function (data) {\n *     //do some process with log\n * });\n *\n */\nfunction auditLogger(opts) {\n    assert.object(opts, 'opts');\n    assert.object(opts.log, 'opts.log');\n    assert.string(opts.event, 'opts.event');\n    assert.optionalFunc(opts.context, 'opts.context');\n    assert.optionalObject(opts.server, 'opts.server');\n    assert.optionalBool(opts.printLog, 'opts.printLog');\n    assert.optionalObject(opts.serializers, 'opts.serializers');\n\n    if (\n        opts.event !== 'after' &&\n        opts.event !== 'pre' &&\n        opts.event !== 'routed'\n    ) {\n        throw new errors.VError(\n            'opts.event must be %s, %s, or %s, but is %s',\n            'pre',\n            'routed',\n            'after',\n            opts.event\n        );\n    }\n\n    var server = opts.server;\n    var printLog = opts.printLog;\n    const requestIdFieldName = opts.requestIdFieldName || 'req_id';\n\n    if (typeof printLog === 'undefined') {\n        printLog = true;\n    }\n    var errSerializer = pino.stdSerializers.err;\n\n    // don't break legacy use, where there was no top level opts.serializer\n    if (opts.log.serializers && opts.log.serializers.err) {\n        errSerializer = opts.log.serializers.err;\n    }\n\n    var DEFAULT_SERIALIZERS = {\n        err: errSerializer,\n        req: function auditRequestSerializer(req) {\n            if (!req) {\n                return false;\n            }\n\n            var timers = {};\n            (req.timers || []).forEach(function forEach(time) {\n                var t = time.time;\n                var _t = Math.floor(1000000 * t[0] + t[1] / 1000);\n                timers[time.name] = (timers[time.name] || 0) + _t;\n            });\n            return {\n                // account for native and queryParser plugin usage\n                query:\n                    typeof req.query === 'function' ? req.query() : req.query,\n                method: req.method,\n                url: req.url,\n                headers: req.headers,\n                httpVersion: req.httpVersion,\n                trailers: req.trailers,\n                version: req.version(),\n                body: opts.body === true ? req.body : undefined,\n                timers: timers,\n                connectionState: req.connectionState && req.connectionState()\n            };\n        },\n        res: function auditResponseSerializer(res) {\n            if (!res) {\n                return false;\n            }\n\n            var body;\n\n            if (opts.body === true) {\n                if (res._body instanceof HttpError) {\n                    body = res._body.body;\n                } else {\n                    body = res._body;\n                }\n            }\n\n            return {\n                statusCode: res.statusCode,\n                headers: getResponseHeaders(res),\n                trailer: res._trailer || false,\n                body: body\n            };\n        }\n    };\n\n    var serializers = Object.assign({}, DEFAULT_SERIALIZERS, opts.serializers);\n\n    function audit(req, res, route, err) {\n        var latency = res.get('Response-Time');\n\n        if (typeof latency !== 'number') {\n            latency = hrTimeDurationInMs(req._timeStart, req._timeFlushed);\n        }\n\n        var obj = {\n            remoteAddress: req.connection.remoteAddress,\n            remotePort: req.connection.remotePort,\n            [requestIdFieldName]: req.getId(),\n            req: req,\n            res: res,\n            err: err,\n            latency: latency,\n            secure: req.secure,\n            _audit: true,\n            event: opts.event\n        };\n\n        // run through the custom context function\n        if (opts.context) {\n            obj.context = opts.context(req, res, route, err);\n        }\n\n        const origLog = (req && req.log) || opts.log;\n        if (printLog && origLog) {\n            const log = origLog.child(\n                {\n                    audit: true,\n                    component: opts.event\n                },\n                {\n                    serializers: serializers\n                }\n            );\n            switch (opts.event) {\n                case 'after':\n                    log.info(obj, 'handled: %d', res.statusCode);\n                    break;\n                case 'pre':\n                    log.info(obj, 'pre');\n                    break;\n                case 'routed':\n                    log.info(obj, 'routed');\n                    break;\n                default:\n                    throw new Error('Unexpected audit event: ' + opts.event);\n            }\n        }\n\n        if (server) {\n            server.emit('audit', obj);\n        }\n\n        return true;\n    }\n\n    return audit;\n}\n\n///-- Exports\n\nmodule.exports = auditLogger;\n"
  },
  {
    "path": "lib/plugins/authorization.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar httpSignature = require('http-signature');\nvar errors = require('restify-errors');\n\n///--- Globals\n\nvar InvalidHeaderError = errors.InvalidHeaderError;\n\nvar OPTIONS = {\n    algorithms: [\n        'rsa-sha1',\n        'rsa-sha256',\n        'rsa-sha512',\n        'dsa-sha1',\n        'hmac-sha1',\n        'hmac-sha256',\n        'hmac-sha512'\n    ]\n};\n\n///--- Helpers\n\nfunction parseBasic(string) {\n    var decoded;\n    var index;\n    var pieces;\n\n    decoded = new Buffer(string, 'base64').toString('utf8');\n\n    if (!decoded) {\n        throw new InvalidHeaderError('Authorization header invalid');\n    }\n\n    index = decoded.indexOf(':');\n\n    if (index === -1) {\n        pieces = [decoded];\n    } else {\n        pieces = [decoded.slice(0, index), decoded.slice(index + 1)];\n    }\n\n    if (!pieces || typeof pieces[0] !== 'string') {\n        throw new InvalidHeaderError('Authorization header invalid');\n    }\n\n    // Allows for usernameless authentication\n    if (!pieces[0]) {\n        pieces[0] = null;\n    }\n\n    // Allows for passwordless authentication\n    if (!pieces[1]) {\n        pieces[1] = null;\n    }\n\n    return {\n        username: pieces[0],\n        password: pieces[1]\n    };\n}\n\nfunction parseSignature(request, options) {\n    var opts = options || {};\n    opts.algorithms = OPTIONS.algorithms;\n\n    try {\n        return httpSignature.parseRequest(request, options);\n    } catch (e) {\n        throw new InvalidHeaderError(\n            'Authorization header invalid: ' + e.message\n        );\n    }\n}\n\n/**\n * Parses out the `Authorization` header as best restify can.\n * Currently only HTTP Basic Auth and\n * [HTTP Signature](https://github.com/joyent/node-http-signature)\n * schemes are supported.\n *\n * @public\n * @function authorizationParser\n * @throws   {InvalidArgumentError}\n * @param    {Object} [options] - an optional options object that is\n *                                passed to http-signature\n * @returns  {Function} Handler\n * @example\n * <caption>\n * Subsequent handlers will see `req.authorization`, which looks like above.\n *\n * `req.username` will also be set, and defaults to 'anonymous'.  If the scheme\n * is unrecognized, the only thing available in `req.authorization` will be\n * `scheme` and `credentials` - it will be up to you to parse out the rest.\n * </caption>\n * {\n *   scheme: \"<Basic|Signature|...>\",\n *   credentials: \"<Undecoded value of header>\",\n *   basic: {\n *     username: $user\n *     password: $password\n *   }\n * }\n */\nfunction authorizationParser(options) {\n    function parseAuthorization(req, res, next) {\n        req.authorization = {};\n        req.username = 'anonymous';\n\n        if (!req.headers.authorization) {\n            return next();\n        }\n\n        var pieces = req.headers.authorization.split(' ', 2);\n\n        if (!pieces || pieces.length !== 2) {\n            var e = new InvalidHeaderError('BasicAuth content is invalid.');\n            return next(e);\n        }\n\n        req.authorization.scheme = pieces[0];\n        req.authorization.credentials = pieces[1];\n\n        try {\n            switch (pieces[0].toLowerCase()) {\n                case 'basic':\n                    req.authorization.basic = parseBasic(pieces[1]);\n                    req.username = req.authorization.basic.username;\n                    break;\n\n                case 'signature':\n                    req.authorization.signature = parseSignature(req, options);\n                    req.username = req.authorization.signature.keyId;\n                    break;\n\n                default:\n                    break;\n            }\n        } catch (e2) {\n            return next(e2);\n        }\n\n        return next();\n    }\n\n    return parseAuthorization;\n}\n\nmodule.exports = authorizationParser;\n"
  },
  {
    "path": "lib/plugins/bodyParser.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\nvar errors = require('restify-errors');\n\nvar bodyReader = require('./bodyReader');\nvar jsonParser = require('./jsonBodyParser');\nvar formParser = require('./formBodyParser');\nvar multipartParser = require('./multipartBodyParser');\nvar fieldedTextParser = require('./fieldedTextBodyParser.js');\nvar regex = require('./utils/regex');\n\n///--- Globals\n\nvar UnsupportedMediaTypeError = errors.UnsupportedMediaTypeError;\n\n///--- API\n\n/**\n * Blocks your chain on reading and parsing the HTTP request body.  Switches on\n * `Content-Type` and does the appropriate logic.  `application/json`,\n * `application/x-www-form-urlencoded` and `multipart/form-data` are currently\n * supported.\n *\n * Parses `POST` bodies to `req.body`. automatically uses one of the following\n * parsers based on content type:\n * - `urlEncodedBodyParser(options)` - parses url encoded form bodies\n * - `jsonBodyParser(options)` - parses JSON POST bodies\n * - `multipartBodyParser(options)` - parses multipart form bodies\n *\n * All bodyParsers support the following options:\n * - `options.mapParams` - default false. copies parsed post body values onto\n * req.params\n * - `options.overrideParams` - default false. only applies when if\n * mapParams true. when true, will stomp on req.params value when\n * existing value is found.\n *\n * @public\n * @function bodyParser\n * @throws   {UnsupportedMediaTypeError}\n * @param {Object} [options] - an option object\n * @param {Number} [options.maxBodySize] - The maximum size in bytes allowed in\n * the HTTP body. Useful for limiting clients from hogging server memory.\n * @param {Boolean} [options.mapParams] - if `req.params` should be filled with\n * parsed parameters from HTTP body.\n * @param {Boolean} [options.mapFiles] - if `req.params` should be filled with\n * the contents of files sent through a multipart request.\n * [formidable](https://github.com/felixge/node-formidable) is used internally\n * for parsing, and a file is denoted as a multipart part with the `filename`\n * option set in its `Content-Disposition`. This will only be performed if\n * `mapParams` is true.\n * @param {Boolean} [options.overrideParams] - if an entry in `req.params`\n * should be overwritten by the value in the body if the names are the same.\n * For instance, if you have the route `/:someval`,\n * and someone posts an `x-www-form-urlencoded`\n * Content-Type with the body `someval=happy` to `/sad`, the value will be\n * `happy` if `overrideParams` is `true`, `sad` otherwise.\n * @param {Function} [options.multipartHandler] - a callback to handle any\n * multipart part which is not a file.\n * If this is omitted, the default handler is invoked which may\n * or may not map the parts into `req.params`, depending on\n * the `mapParams`-option.\n * @param {Function} [options.multipartFileHandler] - a callback to handle any\n * multipart file.\n * It will be a file if the part has a `Content-Disposition` with the\n * `filename` parameter set. This typically happens when a browser sends a\n * form and there is a parameter similar to `<input type=\"file\" />`.\n * If this is not provided, the default behaviour is to map the contents\n * into `req.params`.\n * @param {Boolean} [options.keepExtensions] - if you want the uploaded\n * files to include the extensions of the original files\n * (multipart uploads only).\n * Does nothing if `multipartFileHandler` is defined.\n * @param {String} [options.uploadDir] - Where uploaded files are\n * intermediately stored during transfer before the contents is mapped\n * into `req.params`.\n * Does nothing if `multipartFileHandler` is defined.\n * @param {Boolean} [options.multiples] - if you want to support html5 multiple\n * attribute in upload fields.\n * @param {String} [options.hash] - If you want checksums calculated for\n * incoming files, set this to either `sha1` or `md5`.\n * @param {Boolean} [options.rejectUnknown] - Set to `true` if you want to end\n * the request with a `UnsupportedMediaTypeError` when none of\n * the supported content types was given.\n * @param {Boolean} [options.requestBodyOnGet=false] -  Parse body of a GET\n * request.\n * @param {Function} [options.reviver] - `jsonParser` only. If a function,\n * this prescribes how the value originally produced by parsing is transformed,\n * before being returned. For more information check out\n * `JSON.parse(text[, reviver])`.\n * @param {Number} [options.maxFieldsSize=2 * 1024 * 1024] - `multipartParser`\n * only.\n * Limits the amount of memory all fields together (except files)\n * can allocate in bytes.\n * The default size is `2 * 1024 * 1024` bytes *(2MB)*.\n * @returns  {Function} Handler\n * @example\n * server.use(restify.plugins.bodyParser({\n *     maxBodySize: 0,\n *     mapParams: true,\n *     mapFiles: false,\n *     overrideParams: false,\n *     multipartHandler: function(part) {\n *         part.on('data', function(data) {\n *           // do something with the multipart data\n *         });\n *     },\n *    multipartFileHandler: function(part) {\n *         part.on('data', function(data) {\n *           // do something with the multipart file data\n *         });\n *     },\n *     keepExtensions: false,\n *     uploadDir: os.tmpdir(),\n *     multiples: true,\n *     hash: 'sha1',\n *     rejectUnknown: true,\n *     requestBodyOnGet: false,\n *     reviver: undefined,\n *     maxFieldsSize: 2 * 1024 * 1024\n *  }));\n */\nfunction bodyParser(options) {\n    assert.optionalObject(options, 'options');\n    var opts = options || {};\n    opts.bodyReader = true;\n\n    var read = bodyReader(opts);\n    var parseForm = formParser(opts);\n    var parseJson = jsonParser(opts);\n    var parseMultipart = multipartParser(opts);\n    var parseFieldedText = fieldedTextParser(opts);\n\n    function parseBody(req, res, next) {\n        // #100 don't parse the body again if we've read it once\n        if (req._parsedBody) {\n            next();\n            return;\n        } else {\n            req._parsedBody = true;\n        }\n        // Allow use of 'requestBodyOnGet' flag to allow for merging of\n        // the request body of a GET request into req.params\n        if (req.method === 'HEAD') {\n            next();\n            return;\n        }\n\n        if (req.method === 'GET') {\n            if (!opts.requestBodyOnGet) {\n                next();\n                return;\n            }\n        }\n\n        if (req.contentLength() === 0 && !req.isChunked()) {\n            next();\n            return;\n        }\n\n        var parser;\n        var type = req.contentType().toLowerCase();\n\n        switch (type) {\n            case 'application/json':\n                parser = parseJson[0];\n                break;\n            case 'application/x-www-form-urlencoded':\n                parser = parseForm[0];\n                break;\n            case 'multipart/form-data':\n                parser = parseMultipart;\n                break;\n            case 'text/tsv':\n                parser = parseFieldedText;\n                break;\n            case 'text/tab-separated-values':\n                parser = parseFieldedText;\n                break;\n            case 'text/csv':\n                parser = parseFieldedText;\n                break;\n\n            default:\n                break;\n        }\n\n        // if we find no matches from the direct string comparisons, perform\n        // more expensive regex matches. map any +json to application/json.\n        // theoretically these could be mapped to application/json prior to the\n        // switch statement, but putting it here allows us to skip the regex\n        // entirely unless absolutely necessary. additional types could be\n        // added later at some point.\n        if (!parser) {\n            if (regex.jsonContentType.test(type)) {\n                parser = parseJson[0];\n            }\n        }\n\n        if (parser) {\n            parser(req, res, next);\n        } else if (opts && opts.rejectUnknown) {\n            next(new UnsupportedMediaTypeError(type));\n        } else {\n            next();\n        }\n    }\n\n    return [read, parseBody];\n}\n\nmodule.exports = bodyParser;\n"
  },
  {
    "path": "lib/plugins/bodyReader.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar crypto = require('crypto');\nvar zlib = require('zlib');\n\nvar assert = require('assert-plus');\nvar once = require('once');\nvar errors = require('restify-errors');\n\n///--- Globals\n\nvar BadDigestError = errors.BadDigestError;\nvar RequestEntityTooLargeError = errors.RequestEntityTooLargeError;\nvar PayloadTooLargeError = errors.PayloadTooLargeError;\nvar UnsupportedMediaTypeError = errors.UnsupportedMediaTypeError;\n\nvar MD5_MSG = \"Content-MD5 '%s' didn't match '%s'\";\n\n///--- Helpers\n\nfunction createBodyWriter(req) {\n    var buffers = [];\n\n    var contentType = req.contentType();\n    var isText = false;\n\n    if (\n        !contentType ||\n        contentType === 'application/json' ||\n        contentType === 'application/x-www-form-urlencoded' ||\n        contentType === 'multipart/form-data' ||\n        contentType.substr(0, 5) === 'text/'\n    ) {\n        isText = true;\n    }\n\n    req.body = new Buffer(0);\n    return {\n        write: function write(chunk) {\n            buffers.push(chunk);\n        },\n        end: function end() {\n            req.body = Buffer.concat(buffers);\n\n            if (isText) {\n                req.body = req.body.toString('utf8');\n            }\n        }\n    };\n}\n\n///--- API\n\n/**\n * Reads the body of the request.\n *\n * @public\n * @function bodyReader\n * @throws   {BadDigestError | PayloadTooLargeError}\n * @param    {Object} options - an options object\n * @returns  {Function} Handler\n */\nfunction bodyReader(options) {\n    var opts = options || {};\n    assert.object(opts, 'opts');\n\n    var maxBodySize = opts.maxBodySize || 0;\n\n    function readBody(req, res, originalNext) {\n        var next = once(originalNext);\n\n        // #100 don't read the body again if we've read it once\n        if (req._readBody) {\n            next();\n            return;\n        } else {\n            req._readBody = true;\n        }\n\n        if (\n            (req.getContentLength() === 0 && !req.isChunked()) ||\n            req.contentType() === 'multipart/form-data' ||\n            req.contentType() === 'application/octet-stream'\n        ) {\n            next();\n            return;\n        }\n        var bodyWriter = createBodyWriter(req);\n\n        var bytesReceived = 0;\n        var digest;\n        var gz;\n        var hash;\n        var md5;\n\n        var unsupportedContentEncoding;\n\n        if ((md5 = req.headers['content-md5'])) {\n            hash = crypto.createHash('md5');\n        }\n\n        function done() {\n            bodyWriter.end();\n\n            if (unsupportedContentEncoding) {\n                next(\n                    new UnsupportedMediaTypeError(\n                        {\n                            info: {\n                                contentEncoding: unsupportedContentEncoding\n                            }\n                        },\n                        'content encoding not supported'\n                    )\n                );\n                return;\n            }\n\n            if (maxBodySize && bytesReceived > maxBodySize) {\n                var msg = 'Request body size exceeds ' + maxBodySize;\n                var err;\n\n                // Between Node 0.12 and 4 http status code messages changed\n                // RequestEntityTooLarge was changed to PayloadTooLarge\n                // this check is to maintain backwards compatibility\n                if (PayloadTooLargeError !== undefined) {\n                    err = new PayloadTooLargeError(msg);\n                } else {\n                    err = new RequestEntityTooLargeError(msg);\n                }\n\n                next(err);\n                return;\n            }\n\n            if (!req.body.length) {\n                next();\n                return;\n            }\n\n            if (hash && md5 !== (digest = hash.digest('base64'))) {\n                next(new BadDigestError(MD5_MSG, md5, digest));\n                return;\n            }\n\n            next();\n        }\n\n        if (req.headers['content-encoding'] === undefined) {\n            // This handles the original else branch\n            req.once('end', done);\n        } else if (req.headers['content-encoding'] === 'gzip') {\n            gz = zlib.createGunzip();\n            gz.on('data', bodyWriter.write);\n            gz.once('end', done);\n            req.once('end', gz.end.bind(gz));\n        } else {\n            unsupportedContentEncoding = req.headers['content-encoding'];\n            res.setHeader('Accept-Encoding', 'gzip');\n            req.once('end', done);\n        }\n\n        req.on('data', function onRequestData(chunk) {\n            if (maxBodySize) {\n                bytesReceived += chunk.length;\n\n                if (bytesReceived > maxBodySize) {\n                    return;\n                }\n            }\n\n            if (hash) {\n                hash.update(chunk, 'binary');\n            }\n\n            if (gz) {\n                gz.write(chunk);\n            } else {\n                bodyWriter.write(chunk);\n            }\n        });\n\n        req.once('error', next);\n        // add 'close and 'aborted' event handlers so that requests (and their\n        // corresponding memory) don't leak if client stops sending data half\n        // way through a POST request\n        res.once('close', next);\n        req.once('aborted', next);\n        req.resume();\n    }\n\n    return readBody;\n}\n\nmodule.exports = bodyReader;\n"
  },
  {
    "path": "lib/plugins/conditionalHandler.js",
    "content": "'use strict';\n\nvar errors = require('restify-errors');\nvar _ = require('lodash');\nvar assert = require('assert-plus');\nvar semver = require('semver');\nvar Negotiator = require('negotiator');\nvar Chain = require('../chain');\n\n///--- Globals\n\nvar InvalidVersionError = errors.InvalidVersionError;\nvar UnsupportedMediaTypeError = errors.UnsupportedMediaTypeError;\nvar DEF_CT = 'application/octet-stream';\n\n///--- Exports\n\n/**\n * Runs first handler that matches to the condition\n *\n * @public\n * @function conditionalHandler\n * @param {Object|Object[]} candidates - candidates\n * @param {Function|Function[]} candidates.handler - handler(s)\n * @param {String|String[]} [candidates.version] - '1.1.0', ['1.1.0', '1.2.0']\n * @param {String} [candidates.contentType] - accepted content type, '*\\/json'\n * @returns  {Function} Handler\n * @throws {InvalidVersionError}\n * @throws {UnsupportedMediaTypeError}\n * @example\n * server.use(restify.plugins.conditionalHandler({\n *    contentType: 'application/json',\n *    version: '1.0.0',\n *    handler: function (req, res, next) {\n *        next();\n *    })\n * });\n *\n * server.get('/hello/:name', restify.plugins.conditionalHandler([\n *   {\n *      version: '1.0.0',\n *      handler: function(req, res, next) { res.send('1.x'); }\n *   },\n *   {\n *      version: ['1.5.0', '2.0.0'],\n *      handler: function(req, res, next) { res.send('1.5.x, 2.x'); }\n *   },\n *   {\n *      version: '3.0.0',\n *      contentType: ['text/html', 'text/html']\n *      handler: function(req, res, next) { res.send('3.x, text'); }\n *   },\n *   {\n *      version: '3.0.0',\n *      contentType: 'application/json'\n *      handler: function(req, res, next) { res.send('3.x, json'); }\n *   },\n *   // Array of handlers\n *   {\n *      version: '4.0.0',\n *      handler: [\n *          function(req, res, next) { next(); },\n *          function(req, res, next) { next(); },\n *          function(req, res, next) { res.send('4.x') }\n *      ]\n *   },\n * ]);\n * // 'accept-version': '^1.1.0' => 1.5.x, 2.x'\n * // 'accept-version': '3.x', accept: 'application/json' => '3.x, json'\n */\nfunction conditionalHandler(candidates) {\n    var isVersioned = false;\n    var isContentTyped = false;\n\n    if (!_.isArray(candidates)) {\n        candidates = [candidates];\n    }\n\n    // Assert\n    assert.arrayOfObject(candidates, 'candidates');\n    candidates = candidates.map(function map(candidate) {\n        // Array of handlers, convert to chain\n        if (_.isArray(candidate.handler)) {\n            var chain = new Chain();\n            candidate.handler.forEach(function forEach(_handler) {\n                assert.func(_handler);\n                chain.add(_handler);\n            });\n            candidate.handler = chain.run.bind(chain);\n        }\n\n        assert.func(candidate.handler);\n\n        if (_.isString(candidate.version)) {\n            candidate.version = [candidate.version];\n        }\n        if (_.isString(candidate.contentType)) {\n            candidate.contentType = [candidate.contentType];\n        }\n\n        assert.optionalArrayOfString(candidate.version);\n        assert.optionalArrayOfString(candidate.contentType);\n\n        isVersioned = isVersioned || !!candidate.version;\n        isContentTyped = isContentTyped || !!candidate.contentType;\n\n        return candidate;\n    });\n\n    /**\n     * Conditional Handler\n     *\n     * @private\n     * @param  {Request}  req - request\n     * @param  {Response} res - response\n     * @param  {Function} next - next\n     * @returns {undefined} no return value\n     */\n    return function _conditionalHandlerFactory(req, res, next) {\n        var contentType = req.headers.accept || DEF_CT;\n        var reqCandidates = candidates;\n\n        // Content Type\n        if (isContentTyped) {\n            var contentTypes = contentType.split(/\\s*,\\s*/);\n            reqCandidates = candidates.filter(function filter(candidate) {\n                var neg = new Negotiator({\n                    headers: {\n                        accept: candidate.contentType.join(', ')\n                    }\n                });\n                var tmp = neg.preferredMediaType(contentTypes);\n                return tmp && tmp.length;\n            });\n\n            if (!reqCandidates.length) {\n                next(new UnsupportedMediaTypeError(contentType));\n                return;\n            }\n        }\n\n        // Accept Version\n        if (isVersioned) {\n            var reqVersion = req.version();\n            var maxVersion;\n            var maxVersionIndex;\n\n            reqCandidates.forEach(function forEach(candidate, idx) {\n                var version = semver.maxSatisfying(\n                    candidate.version,\n                    reqVersion\n                );\n\n                if (version) {\n                    if (!maxVersion || semver.gt(version, maxVersion)) {\n                        maxVersion = version;\n                        maxVersionIndex = idx;\n                    }\n                }\n            });\n\n            // No version find\n            if (_.isUndefined(maxVersionIndex)) {\n                next(\n                    new InvalidVersionError(\n                        '%s is not supported by %s %s',\n                        req.version() || '?',\n                        req.method,\n                        req.path()\n                    )\n                );\n                return;\n            }\n\n            // Add api-version response header\n            res.header('api-version', maxVersion);\n            // Store matched version on request internal\n            req._matchedVersion = maxVersion;\n            // Run handler\n            reqCandidates[maxVersionIndex].handler(req, res, next);\n            return;\n        }\n\n        // When not versioned\n        reqCandidates[0].handler(req, res, next);\n    };\n}\n\nmodule.exports = conditionalHandler;\n"
  },
  {
    "path": "lib/plugins/conditionalRequest.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar errors = require('restify-errors');\n\n///--- Globals\n\nvar BadRequestError = errors.BadRequestError;\nvar PreconditionFailedError = errors.PreconditionFailedError;\n\nvar IF_MATCH_FAIL = \"if-match '%s' didn't match etag '%s'\";\nvar IF_NO_MATCH_FAIL = \"if-none-match '%s' matched etag '%s'\";\nvar IF_MOD_FAIL = \"object was modified at '%s'; if-modified-since '%s'\";\nvar IF_UNMOD_FAIL = \"object was modified at '%s'; if-unmodified-since '%s'\";\n\n///--- API\n// Reference RFC2616 section 14 for an explanation of what this all does.\n\nfunction checkIfMatch(req, res, next) {\n    var clientETags;\n    var cur;\n    var etag = res.etag || res.getHeader('etag') || '';\n    var ifMatch;\n    var matched = false;\n\n    if ((ifMatch = req.headers['if-match'])) {\n        clientETags = ifMatch.split(/\\s*,\\s*/);\n\n        for (var i = 0; i < clientETags.length; i++) {\n            cur = clientETags[i];\n\n            // only strong comparison\n\n            cur = cur.replace(/^W\\//, '');\n            cur = cur.replace(/^\"(\\w*)\"$/, '$1');\n\n            if (cur === '*' || cur === etag) {\n                matched = true;\n                break;\n            }\n        }\n\n        if (!matched) {\n            var err = new PreconditionFailedError(IF_MATCH_FAIL, ifMatch, etag);\n            return next(err);\n        }\n    }\n\n    return next();\n}\n\nfunction checkIfNoneMatch(req, res, next) {\n    var clientETags;\n    var cur;\n    var etag = res.etag || res.getHeader('etag') || '';\n    var ifNoneMatch;\n    var matched = false;\n\n    if ((ifNoneMatch = req.headers['if-none-match'])) {\n        clientETags = ifNoneMatch.split(/\\s*,\\s*/);\n\n        for (var i = 0; i < clientETags.length; i++) {\n            cur = clientETags[i];\n\n            // ignore weak validation\n            cur = cur.replace(/^W\\//, '');\n            cur = cur.replace(/^\"(\\w*)\"$/, '$1');\n\n            if (cur === '*' || cur === etag) {\n                matched = true;\n                break;\n            }\n        }\n\n        if (!matched) {\n            return next();\n        }\n\n        if (req.method !== 'GET' && req.method !== 'HEAD') {\n            var err = new PreconditionFailedError(\n                IF_NO_MATCH_FAIL,\n                ifNoneMatch,\n                etag\n            );\n            return next(err);\n        }\n\n        res.send(304);\n        return next(false);\n    }\n\n    return next();\n}\n\nfunction checkIfModified(req, res, next) {\n    var code;\n    var err;\n    var ctime = req.header('if-modified-since');\n    var mtime = res.mtime || res.header('Last-Modified') || '';\n\n    if (!mtime || !ctime) {\n        next();\n        return;\n    }\n\n    try {\n        //\n        // TODO handle Range header modifications\n        //\n        // Note: this is not technically correct as per 2616 -\n        // 2616 only specifies semantics for GET requests, not\n        // any other method - but using if-modified-since with a\n        // PUT or DELETE seems like returning 412 is sane\n        //\n        if (Date.parse(mtime) <= Date.parse(ctime)) {\n            switch (req.method) {\n                case 'GET':\n                case 'HEAD':\n                    code = 304;\n                    break;\n\n                default:\n                    err = new PreconditionFailedError(\n                        IF_MOD_FAIL,\n                        mtime,\n                        ctime\n                    );\n                    break;\n            }\n        }\n    } catch (e) {\n        next(new BadRequestError(e.message));\n        return;\n    }\n\n    if (code !== undefined) {\n        res.send(code);\n        next(false);\n        return;\n    }\n\n    next(err);\n}\n\nfunction checkIfUnmodified(req, res, next) {\n    var err;\n    var ctime = req.headers['if-unmodified-since'];\n    var mtime = res.mtime || res.header('Last-Modified') || '';\n\n    if (!mtime || !ctime) {\n        next();\n        return;\n    }\n\n    try {\n        if (Date.parse(mtime) > Date.parse(ctime)) {\n            err = new PreconditionFailedError(IF_UNMOD_FAIL, mtime, ctime);\n        }\n    } catch (e) {\n        next(new BadRequestError(e.message));\n        return;\n    }\n\n    next(err);\n}\n\n///--- Exports\n\n/**\n * Returns a set of plugins that will compare an already set `ETag` header with\n * the client's `If-Match` and `If-None-Match` header, and an already set\n * Last-Modified header with the client's `If-Modified-Since` and\n * `If-Unmodified-Since` header.\n *\n * You can use this handler to let clients do nice HTTP semantics with the\n * \"match\" headers.  Specifically, with this plugin in place, you would set\n * `res.etag=$yourhashhere`, and then this plugin will do one of:\n *\n * - return `304` (Not Modified) [and stop the handler chain]\n * - return `412` (Precondition Failed) [and stop the handler chain]\n * - Allow the request to go through the handler chain.\n *\n * The specific headers this plugin looks at are:\n *\n * - `Last-Modified`\n * - `If-Match`\n * - `If-None-Match`\n * - `If-Modified-Since`\n * - `If-Unmodified-Since`\n *\n * @public\n * @throws {BadRequestError}\n * @throws {PreconditionFailedError}\n * @function conditionalRequest\n * @returns  {Function[]} Handlers\n * @example\n * server.use(restify.plugins.conditionalRequest());\n * @example\n * server.use(function setETag(req, res, next) {\n *   res.header('ETag', 'myETag');\n *   res.header('Last-Modified', new Date());\n * });\n *\n * server.use(restify.plugins.conditionalRequest());\n *\n * server.get('/hello/:name', function(req, res, next) {\n *   res.send('hello ' + req.params.name);\n * });\n */\nfunction conditionalRequest() {\n    var chain = [\n        checkIfMatch,\n        checkIfNoneMatch,\n        checkIfModified,\n        checkIfUnmodified\n    ];\n    return chain;\n}\n\nmodule.exports = conditionalRequest;\n"
  },
  {
    "path": "lib/plugins/cpuUsageThrottle.js",
    "content": "'use strict';\n\nvar assert = require('assert-plus');\nvar pidusage = require('pidusage');\nvar errors = require('restify-errors');\nvar EWMA = require('ewma');\n\n/**\n * cpuUsageThrottle is a middleware that rejects a variable number of requests\n * (between 0% and 100%) based on a historical view of CPU utilization of a\n * Node.js process. Essentially, this plugin allows you to define what\n * constitutes a saturated Node.js process via CPU utilization and it will\n * handle dropping a % of requests based on that definiton. This is useful when\n * you would like to keep CPU bound tasks from piling up causing an increased\n * per-request latency.\n *\n * The algorithm asks you for a maximum CPU utilization rate, which it uses to\n * determine at what point it should be rejecting 100% of traffic. For a normal\n * Node.js service, this is 1 since Node is single threaded. It uses this,\n * paired with a limit that you provide to determine the total % of traffic it\n * should be rejecting. For example, if you specify a limit of .5 and a max of\n * 1, and the current EWMA (next paragraph) value reads .75, this plugin will\n * reject approximately 50% of all requests.\n *\n * When looking at the process' CPU usage, this algorithm will take a load\n * average over a user specified interval. example, if given an interval of\n * 250ms, this plugin will attempt to record the average CPU utilization over\n * 250ms intervals. Due to contention for resources, the duration of each\n * average may be wider or narrower than 250ms. To compensate for this, we use\n * an exponentially weighted moving average. The EWMA algorithm is provided by\n * the ewma module. The parameter for configuring the EWMA is halfLife. This\n * value controls how quickly each load average measurment decays to half it's\n * value when being represented in the current average. For example, if you\n * have an interval of 250, and a halfLife of 250, you will take the previous\n * ewma value multiplied by 0.5 and add it to the new CPU utilization average\n * measurement multiplied by 0.5. The previous value and the new measurement\n * would each represent 50% of the new value. A good way of thinking about the\n * halfLife is in terms of how responsive this plugin will be to spikes in CPU\n * utilization. The higher the halfLife, the longer CPU utilization will have\n * to remain above your defined limit before this plugin begins rejecting\n * requests and, converserly, the longer it will have to drop below your limit\n * before the plugin begins accepting requests again. This is a knob you will\n * want to with play when trying to determine the ideal value for your use\n * case.\n *\n * For a better understanding of the EWMA algorithn, refer to the documentation\n * for the ewma module.\n *\n * @public\n * @function cpuUsageThrottle\n * @param {Object} opts - Configure this plugin.\n * @param {Number} [opts.limit] - The point at which restify will begin\n *    rejecting a % of all requests at the front door.\n *    This value is a percentage.\n *    For example 0.8 === 80% average CPU utilization. Defaults to 0.75.\n * @param {Number} [opts.max] - The point at which restify will reject 100% of\n *    all requests at the front door. This is used in conjunction with limit to\n *    determine what % of traffic restify needs to reject when attempting to\n *    bring the average load back to the user requested values. Since Node.js is\n *    single threaded, the default for this is 1. In some rare cases, a Node.js\n *    process can exceed 100% CPU usage and you will want to update this value.\n * @param {Number} [opts.interval] - How frequently we calculate the average CPU\n *    utilization. When we calculate an average CPU utilization, we calculate it\n *    over this interval, and this drives whether or not we should be shedding\n *    load. This can be thought of as a \"resolution\" where the lower this value,\n *    the higher the resolution our load average will be and the more frequently\n *    we will recalculate the % of traffic we should be shedding. This check\n *    is rather lightweight, while the default is 250ms, you should be able to\n *    decrease this value without seeing a significant impact to performance.\n * @param {Number} [opts.halfLife] - When we sample the CPU usage on an\n *    interval, we create a series of data points.\n *    We take these points and calculate a\n *    moving average. The halfLife indicates how quickly a point \"decays\" to\n *    half it's value in the moving average. The lower the halfLife, the more\n *    impact newer data points have on the average. If you want to be extremely\n *    responsive to spikes in CPU usage, set this to a lower value. If you want\n *    your process to put more emphasis on recent historical CPU usage when\n *    determininng whether it should shed load, set this to a higher value. The\n *    unit is in ms. Defaults to 250.\n * @returns {Function} middleware to be registered on server.pre\n * @example\n * var restify = require('restify');\n *\n * var server = restify.createServer();\n * const options = {\n *   limit: .75,\n *   max: 1,\n *   interval: 250,\n *   halfLife: 500,\n * }\n *\n * server.pre(restify.plugins.cpuUsageThrottle(options));\n * @example\n * <caption>\n * You can also update the plugin during runtime using the `.update()` function.\n * This function accepts the same `opts` object as a constructor.\n * </caption>\n * var plugin = restify.plugins.cpuUsageThrottle(options);\n * server.pre(plugin);\n *\n * plugin.update({ limit: .4, halfLife: 5000 });\n */\nfunction cpuUsageThrottlePlugin(opts) {\n    // Scrub input and populate our configuration\n    assert.object(opts, 'opts');\n    assert.optionalNumber(opts.limit, 'opts.limit');\n    assert.optionalNumber(opts.max, 'opts.max');\n    assert.optionalNumber(opts.interval, 'opts.interval');\n    assert.optionalNumber(opts.halfLife, 'opts.halfLife');\n\n    var plugin = {};\n    plugin._limit = typeof opts.limit === 'number' ? opts.limit : 0.75;\n    plugin._max = opts.max || 1;\n    plugin._interval = opts.interval || 250;\n    plugin._halfLife = typeof opts.halfLife === 'number' ? opts.halfLife : 250;\n    assert.ok(plugin._max > plugin._limit, 'limit must be less than max');\n\n    plugin._ewma = new EWMA(plugin._halfLife);\n\n    // plugin._reject represents the % of traffic that we should reject at the\n    // current point in time based on how much over our limit we are. This is\n    // updated on an interval by updateReject().\n    plugin._reject = 0;\n\n    // plugin._timeout keeps track of the current handle for the setTimeout we\n    // use to gather CPU load averages, this allows us to cancel the timeout\n    // when shutting down restify.\n    plugin._timeout = null;\n\n    // plugin._timeoutDelta represents the amount of time between when we\n    // _should_ have run updateReject and the actual time it was invoked.\n    // This allows us to monitor lag caused by both the event loop\n    // and pidusage\n    plugin._timeoutDelta = 0;\n    plugin._timeoutStart = Date.now();\n\n    // updateReject should be called on an interval, it checks the average CPU\n    // usage between two invocations of updateReject.\n    function updateReject() {\n        pidusage(process.pid, function pidusageStat(e, stat) {\n            // Requeue an updateReject irrespective of whether or not pidusage\n            // encountered an error\n            plugin._timeout = setTimeout(updateReject, plugin._interval);\n\n            // If we were unable to get cpu usage, don't make any new decisions.\n            if (\n                !stat ||\n                typeof stat.cpu !== 'number' ||\n                Number.isNaN(stat.cpu)\n            ) {\n                return;\n            }\n\n            // Divide by 100 to match Linux's `top` format\n            plugin._ewma.insert(stat.cpu / 100);\n            plugin._cpu = plugin._ewma.value();\n\n            // Update reject with the % of traffic we should be rejecting. This\n            // is safe since max > limit so the denominator can never be 0. If\n            // the current cpu usage is less that the limit, _reject will be\n            // negative and we will never shed load\n            plugin._reject =\n                (plugin._cpu - plugin._limit) / (plugin._max - plugin._limit);\n\n            // Calculate how long it took between when our interval should have\n            // updated the _reject value and how long it actually took. This\n            // metric accounts for the misbehaviour of pidusage\n            var now = Date.now();\n            plugin._timeoutDelta =\n                now - plugin._timeoutStart - plugin._interval;\n            plugin._timeoutStart = now;\n        });\n    }\n\n    // Kick off updating our _reject value\n    updateReject();\n\n    function cpuUsageThrottle(req, res, next) {\n        // Check to see if this request gets rejected. Since, in updateReject,\n        // we calculate a percentage of traffic we are planning to reject, we\n        // can use Math.random() (which picks from a uniform distribution in\n        // [0,1)) to give us a `plugin._reject`% chance of dropping any given\n        // request. This is a stateless was to drop approximatly\n        // `plugin._reject`% of traffic.\n        var probabilityDraw = Math.random();\n\n        if (probabilityDraw >= plugin._reject) {\n            return next(); // Don't reject this request\n        }\n\n        var err = new errors.ServiceUnavailableError({\n            context: {\n                plugin: 'cpuUsageThrottle',\n                cpuUsage: plugin._cpu,\n                limit: plugin._limit,\n                max: plugin._max,\n                reject: plugin._reject,\n                halfLife: plugin._halfLife,\n                interval: plugin._interval,\n                probabilityDraw: probabilityDraw,\n                lag: plugin._timeoutDelta\n            }\n        });\n\n        return next(err);\n    }\n\n    // Allow the app to clear the timeout for this plugin if necessary, without\n    // this we would never be able to clear the event loop when letting Node\n    // shut down gracefully\n    function close() {\n        clearTimeout(plugin._timeout);\n    }\n    cpuUsageThrottle.close = close;\n\n    // Expose internal plugin state for introspection\n    Object.defineProperty(cpuUsageThrottle, 'state', {\n        get: function get() {\n            // We intentionally do not expose ewma since we don't want the user\n            // to be able to update it's configuration, the current state of\n            // ewma is represented in plugin._cpu\n            return {\n                limit: plugin._limit,\n                max: plugin._max,\n                interval: plugin._interval,\n                halfLife: plugin._halfLife,\n                cpuUsage: plugin._cpu,\n                reject: plugin._reject,\n                lag: plugin._timeoutDelta\n            };\n        }\n    });\n\n    /**\n     * cpuUsageThrottle.update\n     *\n     * Allow the plugin's configuration to be updated during runtime.\n     *\n     * @private\n     * @param {Object} newOpts - The opts object for reconfiguring this plugin,\n     *    it follows the same format as the constructor for this plugin.\n     * @returns {undefined} no return value\n     */\n    cpuUsageThrottle.update = function update(newOpts) {\n        assert.object(newOpts, 'newOpts');\n        assert.optionalNumber(newOpts.limit, 'newOpts.limit');\n        assert.optionalNumber(newOpts.max, 'newOpts.max');\n        assert.optionalNumber(newOpts.interval, 'newOpts.interval');\n        assert.optionalNumber(newOpts.halfLife, 'newOpts.halfLife');\n\n        if (newOpts.limit !== undefined) {\n            plugin._limit = newOpts.limit;\n        }\n\n        if (newOpts.max !== undefined) {\n            plugin._max = newOpts.max;\n        }\n\n        if (newOpts.interval !== undefined) {\n            plugin._interval = newOpts.interval;\n        }\n\n        if (newOpts.halfLife !== undefined) {\n            plugin._halfLife = newOpts.halfLife;\n            // update our ewma with the new halfLife, we use the previous known\n            // state as the initial state for our new halfLife in lieu of\n            // having access to true historical data.\n            plugin._ewma = new EWMA(plugin._halfLife, plugin._cpu);\n        }\n\n        // Ensure new values are still valid\n        assert.ok(plugin._max > plugin._limit, 'limit must be less than max');\n\n        // Update _reject with the new settings\n        plugin._reject =\n            (plugin._cpu - plugin._limit) / (plugin._max - plugin._limit);\n    };\n\n    return cpuUsageThrottle;\n}\n\nmodule.exports = cpuUsageThrottlePlugin;\n"
  },
  {
    "path": "lib/plugins/date.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\nvar errors = require('restify-errors');\n\n///--- Globals\n\nvar InvalidHeaderError = errors.InvalidHeaderError;\nvar RequestExpiredError = errors.RequestExpiredError;\n\nvar BAD_MSG = 'Date header is invalid';\nvar OLD_MSG = 'Date header %s is too old';\n\n///--- API\n\n/**\n * Parses out the HTTP Date header (if present) and checks for clock skew.\n * If the header is invalid, a `InvalidHeaderError` (`400`) is returned.\n * If the clock skew exceeds the specified value,\n * a `RequestExpiredError` (`400`) is returned.\n * Where expired means the request originated at a time\n * before (`$now - $clockSkew`).\n * The default clockSkew allowance is 5m (thanks\n * Kerberos!)\n *\n * @public\n * @function dateParser\n * @throws   {RequestExpiredError}\n * @throws   {InvalidHeaderError}\n * @param    {Number}    [clockSkew=300] - allowed clock skew in seconds.\n * @returns  {Function}                    restify handler.\n * @example\n * // Allows clock skew of 1m\n * server.use(restify.plugins.dateParser(60));\n */\nfunction dateParser(clockSkew) {\n    var normalizedClockSkew = clockSkew || 300;\n    assert.number(normalizedClockSkew, 'normalizedClockSkew');\n\n    normalizedClockSkew = normalizedClockSkew * 1000;\n\n    function parseDate(req, res, next) {\n        if (!req.headers.date) {\n            return next();\n        }\n\n        var e;\n        var date = req.headers.date;\n        var log = req.log;\n\n        try {\n            var now = Date.now();\n            var sent = new Date(date).getTime();\n\n            if (log.trace()) {\n                log.trace(\n                    {\n                        allowedSkew: normalizedClockSkew,\n                        now: now,\n                        sent: sent\n                    },\n                    'Checking clock skew'\n                );\n            }\n\n            if (now - sent > normalizedClockSkew) {\n                e = new RequestExpiredError(OLD_MSG, date);\n                return next(e);\n            }\n        } catch (err) {\n            log.trace(\n                {\n                    err: err\n                },\n                'Bad Date header: %s',\n                date\n            );\n\n            e = new InvalidHeaderError(BAD_MSG, date);\n            return next(e);\n        }\n\n        return next();\n    }\n\n    return parseDate;\n}\n\nmodule.exports = dateParser;\n"
  },
  {
    "path": "lib/plugins/fieldedTextBodyParser.js",
    "content": "/**\n * Dependencies\n */\n\n'use strict';\n\nvar csv = require('csv');\nvar assert = require('assert-plus');\n\n///--- API\n\n/**\n * Returns a plugin that will parse the HTTP request body if the\n * contentType is `text/csv` or `text/tsv`.\n *\n * @public\n * @function fieldedTextParser\n * @param    {Object}    options - an options object\n * @returns  {Function} Handler\n */\nfunction fieldedTextParser(options) {\n    assert.optionalObject(options, 'options');\n\n    function parseFieldedText(req, res, next) {\n        // save original body on req.rawBody and req._body\n        req.rawBody = req._body = req.body;\n\n        var contentType = req.getContentType();\n\n        if (\n            (contentType !== 'text/csv' &&\n                contentType !== 'text/tsv' &&\n                contentType !== 'text/tab-separated-values') ||\n            !req.body\n        ) {\n            next();\n            return;\n        }\n\n        var hDelimiter = req.headers['x-content-delimiter'];\n        var hEscape = req.headers['x-content-escape'];\n        var hQuote = req.headers['x-content-quote'];\n        var hColumns = req.headers['x-content-columns'];\n\n        var delimiter = contentType === 'text/tsv' ? '\\t' : ',';\n        delimiter = hDelimiter ? hDelimiter : delimiter;\n        var escape = hEscape ? hEscape : '\\\\';\n        var quote = hQuote ? hQuote : '\"';\n        var columns = hColumns ? hColumns : true;\n\n        var parserOptions = {\n            delimiter: delimiter,\n            quote: quote,\n            escape: escape,\n            columns: columns\n        };\n\n        csv.parse(req.body, parserOptions, function parse(err, parsedBody) {\n            if (err) {\n                return next(err);\n            }\n\n            // Add an \"index\" property to every row\n            parsedBody.forEach(function forEach(row, index) {\n                row.index = index;\n            });\n            req.body = parsedBody;\n            return next();\n        });\n    }\n\n    return parseFieldedText;\n}\n\nmodule.exports = fieldedTextParser;\n"
  },
  {
    "path": "lib/plugins/formBodyParser.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\nvar querystring = require('qs');\n\nvar bodyReader = require('./bodyReader');\nvar errors = require('restify-errors');\n\n///--- Globals\n\nvar MIME_TYPE = 'application/x-www-form-urlencoded';\n\n///--- API\n\n/**\n * Returns a plugin that will parse the HTTP request body IFF the\n * contentType is application/x-www-form-urlencoded.\n *\n * If req.params already contains a given key, that key is skipped and an\n * error is logged.\n *\n * @public\n * @function urlEncodedBodyParser\n * @param   {Object}    options - an option sobject\n * @returns {Function} Handler\n */\nfunction urlEncodedBodyParser(options) {\n    var opts = options || {};\n    assert.object(opts, 'opts');\n\n    var override = opts.overrideParams;\n\n    function parseUrlEncodedBody(req, res, next) {\n        // save original body on req.rawBody and req._body\n        req.rawBody = req._body = req.body;\n\n        if (req.getContentType() !== MIME_TYPE || !req.body) {\n            next();\n            return;\n        }\n\n        try {\n            var params = querystring.parse(req.body);\n\n            if (opts.mapParams === true) {\n                var keys = Object.keys(params);\n                keys.forEach(function forEach(k) {\n                    var p = req.params[k];\n\n                    if (p && !override) {\n                        return;\n                    }\n                    req.params[k] = params[k];\n                });\n            }\n\n            req.body = params;\n        } catch (e) {\n            next(new errors.InvalidContentError(e.message));\n            return;\n        }\n\n        req.log.trace('req.params now: %j', req.params);\n        next();\n    }\n\n    var chain = [];\n\n    if (!opts.bodyReader) {\n        chain.push(bodyReader(opts));\n    }\n    chain.push(parseUrlEncodedBody);\n    return chain;\n}\n\nmodule.exports = urlEncodedBodyParser;\n"
  },
  {
    "path": "lib/plugins/fullResponse.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar crypto = require('crypto');\nvar httpDate = require('./utils/httpDate');\nvar hrTimeDurationInMs = require('./utils/hrTimeDurationInMs');\n\n///--- API\n\nfunction setHeaders(req, res) {\n    var hash;\n    var now = new Date();\n\n    if (!res.getHeader('Connection')) {\n        res.setHeader('Connection', req.isKeepAlive() ? 'Keep-Alive' : 'close');\n    }\n\n    if (res._data && !res.getHeader('Content-MD5')) {\n        hash = crypto.createHash('md5');\n        hash.update(res._data);\n        res.setHeader('Content-MD5', hash.digest('base64'));\n    }\n\n    if (!res.getHeader('Date')) {\n        res.setHeader('Date', httpDate(now));\n    }\n\n    if (res.etag && !res.getHeader('Etag')) {\n        res.setHeader('Etag', res.etag);\n    }\n\n    if (!res.getHeader('Server')) {\n        res.setHeader('Server', res.serverName);\n    }\n\n    if (res.version && !res.getHeader('Api-Version')) {\n        res.setHeader('Api-Version', res.version);\n    }\n\n    if (!res.getHeader('Request-Id')) {\n        res.setHeader('Request-Id', req.getId());\n    }\n\n    if (!res.getHeader('Response-Time')) {\n        // we cannot use req._timeFlushed here as\n        // the response is not flushed yet\n        res.setHeader(\n            'Response-Time',\n            hrTimeDurationInMs(req._timeStart, process.hrtime())\n        );\n    }\n}\n\n/**\n * handles disappeared CORS headers.\n * https://github.com/restify/node-restify/issues/284\n *\n * @public\n * @function fullResponse\n * @returns  {Function} Handler\n */\nfunction fullResponse() {\n    function restifyResponseHeaders(req, res, next) {\n        res.once('header', function onceHeader() {\n            // Restify 1.0 compatibility\n            if (res.defaultResponseFormatters) {\n                res.defaultResponseFormatters(res._data);\n            }\n\n            res.emit('beforeSend', res._data, res._body);\n\n            // end backwards-compatibility\n            return setHeaders(req, res);\n        });\n\n        return next();\n    }\n\n    return restifyResponseHeaders;\n}\n\n///--- Exports\n\nmodule.exports = fullResponse;\n"
  },
  {
    "path": "lib/plugins/gzip.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar zlib = require('zlib');\n\nvar assert = require('assert-plus');\n\n/**\n * @private\n * @function _writeHead\n * @param   {Function}  originalFunction - originalFunction\n * @returns {undefined} no return value\n */\nfunction _writeHead(originalFunction) {\n    this.removeHeader('Content-Length');\n    var argsLength = arguments.length;\n    var args = new Array(argsLength - 1);\n\n    for (var i = 1; i < argsLength; i++) {\n        args[i - 1] = arguments[i];\n    }\n    originalFunction.apply(this, args);\n}\n\n///--- API\n\n/**\n * If the client sends an `accept-encoding: gzip` header (or one with an\n * appropriate q-val), then the server will automatically gzip all\n * response data.\n * Note that only `gzip` is supported, as this is most widely supported by\n * clients in the wild.\n * This plugin will overwrite some of the internal streams, so any\n * calls to `res.send`, `res.write`, etc., will be compressed.  A side effect is\n * that the `content-length` header cannot be known, and so\n * `transfer-encoding: chunked` will *always* be set when this is in effect.\n * This plugin has no impact if the client does not send\n * `accept-encoding: gzip`.\n *\n * https://github.com/restify/node-restify/issues/284\n *\n * @public\n * @function gzipResponse\n * @param   {Object}   [opts] - an options object, see: zlib.createGzip\n * @returns {Function} Handler\n * @example\n * server.use(restify.plugins.gzipResponse());\n */\nfunction gzipResponse(opts) {\n    assert.optionalObject(opts, 'options');\n\n    function gzip(req, res, next) {\n        if (!req.acceptsEncoding('gzip')) {\n            next();\n            return;\n        }\n\n        var gz = zlib.createGzip(opts);\n\n        gz.on('data', res.write.bind(res));\n        gz.once('end', res.end.bind(res));\n        gz.on('drain', res.emit.bind(res, 'drain'));\n\n        var origWrite = res.write;\n        var origEnd = res.end;\n        var origWriteHead = res.writeHead;\n        res.handledGzip = function _handledGzip() {\n            res.write = origWrite;\n            res.end = origEnd;\n            res.writeHead = origWriteHead;\n        };\n\n        res.write = gz.write.bind(gz);\n        res.end = gz.end.bind(gz);\n\n        res.writeHead = _writeHead.bind(res, res.writeHead);\n        res.setHeader('Content-Encoding', 'gzip');\n        next();\n    }\n\n    return gzip;\n}\n\n///--- Exports\n\nmodule.exports = gzipResponse;\n"
  },
  {
    "path": "lib/plugins/index.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n///--- Exports\n\nmodule.exports = {\n    acceptParser: require('./accept'),\n    auditLogger: require('./audit'),\n    authorizationParser: require('./authorization'),\n    bodyParser: require('./bodyParser'),\n    bodyReader: require('./bodyReader'),\n    conditionalHandler: require('./conditionalHandler'),\n    conditionalRequest: require('./conditionalRequest'),\n    cpuUsageThrottle: require('./cpuUsageThrottle.js'),\n    dateParser: require('./date'),\n    fullResponse: require('./fullResponse'),\n    gzipResponse: require('./gzip'),\n    inflightRequestThrottle: require('./inflightRequestThrottle'),\n    jsonBodyParser: require('./jsonBodyParser'),\n    jsonp: require('./jsonp'),\n    multipartBodyParser: require('./multipartBodyParser'),\n    oauth2TokenParser: require('./oauth2TokenParser'),\n    queryParser: require('./query'),\n    metrics: require('./metrics'),\n    requestExpiry: require('./requestExpiry'),\n    requestLogger: require('./requestLogger'),\n    serveStatic: require('./static'),\n    serveStaticFiles: require('./staticFiles'),\n    throttle: require('./throttle'),\n    urlEncodedBodyParser: require('./formBodyParser'),\n\n    pre: {\n        context: require('./pre/context'),\n        dedupeSlashes: require('./pre/dedupeSlashes'),\n        pause: require('./pre/pause'),\n        reqIdHeaders: require('./pre/reqIdHeaders'),\n        sanitizePath: require('./pre/prePath'),\n        strictQueryParams: require('./pre/strictQueryParams'),\n        userAgentConnection: require('./pre/userAgent')\n    }\n};\n"
  },
  {
    "path": "lib/plugins/inflightRequestThrottle.js",
    "content": "'use strict';\n\nvar assert = require('assert-plus');\nvar ServiceUnavailableError = require('restify-errors').ServiceUnavailableError;\n\n/**\n * The `inflightRequestThrottle` module allows you to specify an upper limit to\n * the maximum number of inflight requests your server is able to handle. This\n * is a simple heuristic for protecting against event loop contention between\n * requests causing unacceptable latencies.\n *\n * The custom error is optional, and allows you to specify your own response\n * and status code when rejecting incoming requests due to too many inflight\n * requests. It defaults to `503 ServiceUnavailableError`.\n *\n * This plugin should be registered as early as possibly in the middleware stack\n * using `pre` to avoid performing unnecessary work.\n *\n * @public\n * @function inflightRequestThrottle\n * @param {Object} opts - configure this plugin\n * @param {Number} opts.limit - maximum number of inflight requests the server\n *    will handle before returning an error\n * @param {Error} opts.err - A restify error used as a response when the\n *    inflight request limit is exceeded\n * @param {Function} opts.server - the instance of the restify server this\n *    plugin will throttle.\n * @returns {Function} middleware to be registered on server.pre\n * @example\n * var errors = require('restify-errors');\n * var restify = require('restify');\n *\n * var server = restify.createServer();\n * const options = { limit: 600, server: server };\n * options.res = new errors.InternalServerError();\n * server.pre(restify.plugins.inflightRequestThrottle(options));\n */\nfunction inflightRequestThrottle(opts) {\n    // Scrub input and populate our configuration\n    assert.object(opts, 'opts');\n    assert.number(opts.limit, 'opts.limit');\n    assert.object(opts.server, 'opts.server');\n    assert.func(opts.server.inflightRequests, 'opts.server.inflightRequests');\n\n    if (opts.err !== undefined && opts.err !== null) {\n        assert.ok(opts.err instanceof Error, 'opts.err must be an error');\n        assert.optionalNumber(opts.err.statusCode, 'opts.err.statusCode');\n    }\n\n    var plugin = {};\n    plugin._err = opts.err || new ServiceUnavailableError('resource exhausted');\n    plugin._limit = opts.limit;\n    plugin._server = opts.server;\n\n    function onRequest(req, res, next) {\n        var inflightRequests = plugin._server.inflightRequests();\n\n        if (inflightRequests > plugin._limit) {\n            req.log.trace(\n                {\n                    plugin: 'inflightRequestThrottle',\n                    inflightRequests: inflightRequests,\n                    limit: plugin._limit\n                },\n                'maximum inflight requests exceeded, rejecting request'\n            );\n            return next(plugin._err);\n        }\n\n        return next();\n    }\n\n    return onRequest;\n}\n\nmodule.exports = inflightRequestThrottle;\n"
  },
  {
    "path": "lib/plugins/jsonBodyParser.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\nvar errors = require('restify-errors');\n\nvar bodyReader = require('./bodyReader');\nvar regex = require('./utils/regex');\n\n///--- API\n\n/**\n * Parses json body from the request.\n *\n * @public\n * @function jsonBodyParser\n * @param    {Object}               options - an options object\n * @throws   {InvalidContentError}            on bad input\n * @returns  {Function} Handler\n */\nfunction jsonBodyParser(options) {\n    assert.optionalObject(options, 'options');\n    var opts = options || {};\n\n    var override = opts.overrideParams;\n\n    function parseJson(req, res, next) {\n        // save original body on req.rawBody and req._body\n        req.rawBody = req._body = req.body;\n\n        var contentType = req.getContentType();\n\n        // check for empty body first, don't pay regex tax unless necessary.\n        // for content type, check for exact match and any of the *+json types\n        if (\n            !req.body ||\n            (contentType !== 'application/json' &&\n                !regex.jsonContentType.test(contentType))\n        ) {\n            return next();\n        }\n\n        var params;\n\n        try {\n            params = JSON.parse(req.body, opts.reviver);\n        } catch (e) {\n            return next(\n                new errors.InvalidContentError(\n                    '%s',\n                    'Invalid JSON: ' + e.message\n                )\n            );\n        }\n\n        if (opts.mapParams === true) {\n            if (Array.isArray(params)) {\n                // if req.params exists, we have url params. we can't map an\n                // array safely onto req.params, throw an error.\n                if (\n                    req.params &&\n                    Object.keys(req.params).length > 0 &&\n                    !(req.params instanceof Array)\n                ) {\n                    return next(\n                        new errors.InternalServerError(\n                            'Cannot map POST body of [Array array] onto ' +\n                                'req.params'\n                        )\n                    );\n                }\n                req.params = params;\n            } else if (typeof params === 'object' && params !== null) {\n                // else, try to merge the objects\n                Object.keys(params).forEach(function forEach(k) {\n                    var p = req.params[k];\n\n                    if (p && !override) {\n                        return;\n                    }\n                    req.params[k] = params[k];\n                });\n            } else {\n                // otherwise, do a wholesale stomp, no need to merge one by one.\n                req.params = params || req.params;\n            }\n        }\n\n        req.body = params;\n\n        return next();\n    }\n\n    var chain = [];\n\n    if (!opts.bodyReader) {\n        chain.push(bodyReader(opts));\n    }\n    chain.push(parseJson);\n    return chain;\n}\n\nmodule.exports = jsonBodyParser;\n"
  },
  {
    "path": "lib/plugins/jsonp.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar qs = require('qs');\n\n///--- API\n\n/**\n * Parses the jsonp callback out of the request.\n * Supports checking the query string for `callback` or `jsonp` and ensuring\n * that the content-type is appropriately set if JSONP params are in place.\n * There is also a default `application/javascript` formatter to handle this.\n *\n * You *should* set the `queryParser` plugin to run before this, but if you\n * don't this plugin will still parse the query string properly.\n *\n * @public\n * @function jsonp\n * @returns  {Function} Handler\n * @example\n * var server = restify.createServer();\n * server.use(restify.plugins.jsonp());\n */\nfunction jsonp() {\n    function _jsonp(req, res, next) {\n        var q = req.getQuery();\n\n        // If the query plugin wasn't used, we need to hack it in now\n        if (typeof q === 'string') {\n            req.query = qs.parse(q);\n        }\n\n        if (req.query.callback || req.query.jsonp) {\n            res.setHeader('Content-Type', 'application/javascript');\n        }\n\n        next();\n    }\n\n    return _jsonp;\n}\n\nmodule.exports = jsonp;\n"
  },
  {
    "path": "lib/plugins/metrics.js",
    "content": "'use strict';\n\nvar assert = require('assert-plus');\nvar hrTimeDurationInMs = require('./utils/hrTimeDurationInMs');\n\n/**\n * Timing internals\n *\n * Timings are also saved when there is no handler in the given category.\n * Some handler categories are optional, for example there is no\n * `use` and `route` for 404.\n *\n * @private\n *\n * req._timeStart      - request lifecycle started in restify\n * req._timePreStart   - pre handlers started\n * req._timePreEnd     - all pre handlers finished\n * req._timeUseStart   - use handlers started\n * req._timeUseEnd     - all use handlers finished\n * req._timeRouteStart - route handlers started\n * req._timeRouteEnd   - all route handlers finished\n * req._timeFlushed    - request flushed, may happens before handlers finished\n * req._timeFinished   - both all handlers finished and request flushed\n */\n\n///--- API\n\n/**\n * The module includes the following plugins to be used with restify's `after`\n * event, e.g., `server.on('after', restify.plugins.metrics());`:\n *\n * A plugin that listens to the server's after event and emits information\n * about that request.\n *\n * @public\n * @function metrics\n * @param {Object} opts - an options obj\n * @param {Server} opts.server - restify server\n * @param {createMetrics~callback} callback - a callback fn\n * @returns {Function} returns a function suitable to be used\n *   with restify server's `after` event\n * @example\n * server.on('after', restify.plugins.metrics({ server: server },\n *     function (err, metrics, req, res, route) {\n *         // metrics is an object containing information about the request\n * }));\n */\nfunction createMetrics(opts, callback) {\n    assert.object(opts, 'opts');\n    assert.object(opts.server, 'opts.server');\n    assert.func(callback, 'callback');\n\n    return function metrics(req, res, route, err) {\n        var data = {\n            // response status code. in most cases this should be a proper\n            // http status code, but in the case of an uncaughtException it can\n            // be undefined. otherwise, in most normal scenarios, even calling\n            // res.send() or res.end() should result in a 200 by default.\n            statusCode: res.statusCode,\n            // REST verb\n            method: req.method,\n            // overall request latency\n            totalLatency: hrTimeDurationInMs(req._timeStart, req._timeFinished),\n            latency: hrTimeDurationInMs(req._timeStart, req._timeFlushed),\n            preLatency: hrTimeDurationInMs(req._timePreStart, req._timePreEnd),\n            useLatency: hrTimeDurationInMs(req._timeUseStart, req._timeUseEnd),\n            routeLatency: hrTimeDurationInMs(\n                req._timeRouteStart,\n                req._timeRouteEnd\n            ),\n            // the cleaned up url path\n            // e.g., /foo?a=1 => /foo\n            path: req.path(),\n            // connection state can currently only have the following values:\n            // 'close' | undefined.\n            //\n            // if the connection state is 'close'\n            // the status code will be set to 444\n            // it is possible to get a 200 statusCode with a connectionState\n            // value of 'close'. i.e., the client timed out,\n            // but restify thinks it \"sent\" a response. connectionState should\n            // always be the primary source of truth here, and check it first\n            // before consuming statusCode. otherwise, it may result in skewed\n            // metrics.\n            connectionState: req.connectionState && req.connectionState(),\n            unfinishedRequests:\n                opts.server.inflightRequests && opts.server.inflightRequests(),\n            inflightRequests:\n                opts.server.inflightRequests && opts.server.inflightRequests()\n        };\n\n        return callback(err, data, req, res, route);\n    };\n}\n\n/**\n * Callback used by metrics plugin\n * @callback metrics~callback\n * @param {Error} err\n * @param {Object} metrics - metrics about the request\n * @param {Number} metrics.statusCode status code of the response. can be\n *   undefined in the case of an uncaughtException\n * @param {String} metrics.method http request verb\n * @param {Number} metrics.totalLatency latency includes both request is flushed\n *                                      and all handlers finished\n * @param {Number} metrics.latency latency when request is flushed\n * @param {Number|null} metrics.preLatency pre handlers latency\n * @param {Number|null} metrics.useLatency use handlers latency\n * @param {Number|null} metrics.routeLatency route handlers latency\n * @param {String} metrics.path `req.path()` value\n * @param {Number} metrics.inflightRequests Number of inflight requests pending\n *   in restify.\n * @param {Number} metrics.unifinishedRequests Same as `inflightRequests`\n * @param {String} metrics.connectionState can be either `'close'` or\n *  `undefined`. If this value is set, err will be a\n *   corresponding `RequestCloseError`.\n *   If connectionState is either\n *   `'close'`, then the `statusCode` is not applicable since the\n *   connection was severed before a response was written.\n * @param {Request} req the request obj\n * @param {Response} res the response obj\n * @param {Route} route the route obj that serviced the request\n */\n\n///-- Exports\n\nmodule.exports = createMetrics;\n"
  },
  {
    "path": "lib/plugins/multipartBodyParser.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar fs = require('fs');\n\nvar assert = require('assert-plus');\nvar formidable = require('formidable');\nvar once = require('once');\nvar errors = require('restify-errors');\nvar vasync = require('vasync');\n\n///--- API\n\n/**\n * Returns a plugin that will parse the HTTP request body IFF the\n * contentType is multipart/form-data\n *\n * If req.params already contains a given key, that key is skipped and an\n * error is logged.\n *\n * @public\n * @function multipartBodyParser\n * @param    {Object}          options - an options object\n * @throws   {BadRequestError}\n * @returns  {Function} Handler\n */\nfunction multipartBodyParser(options) {\n    var opts = options || {};\n    assert.object(opts, 'opts');\n    assert.optionalBool(opts.overrideParams, 'opts.overrideParams');\n    assert.optionalBool(opts.multiples, 'opts.multiples');\n    assert.optionalBool(opts.keepExtensions, 'opts.keepExtensions');\n    assert.optionalString(opts.uploadDir, 'opts.uploadDir');\n    assert.optionalNumber(opts.maxFieldsSize, 'opts.maxFieldsSize');\n    assert.optionalString(opts.hash, 'opts.hash');\n    assert.optionalFunc(opts.multipartFileHandler, 'opts.multipartFileHandler');\n    assert.optionalFunc(opts.multipartHandler, 'opts.multipartHandler');\n    assert.optionalBool(opts.mapParams, 'opts.mapParams');\n    assert.optionalBool(opts.mapFiles, 'opts.mapFiles');\n    assert.optionalNumber(opts.maxFileSize, 'opts.maxFileSize');\n\n    var override = opts.overrideParams;\n\n    function parseMultipartBody(req, res, originalNext) {\n        // save original body on req.rawBody and req._body\n        req.rawBody = req._body = undefined;\n\n        var next = once(originalNext);\n\n        if (\n            req.getContentType() !== 'multipart/form-data' ||\n            (req.getContentLength() === 0 && !req.isChunked())\n        ) {\n            return next();\n        }\n\n        var form = new formidable.IncomingForm();\n\n        // enable multiple files on a single upload field\n        // (html5 multiple attribute)\n        form.multiples = opts.multiples || false;\n        form.keepExtensions = opts.keepExtensions ? true : false;\n\n        if (opts.uploadDir) {\n            form.uploadDir = opts.uploadDir;\n        }\n\n        if (opts.maxFieldsSize) {\n            form.maxFieldsSize = opts.maxFieldsSize;\n        }\n\n        if (opts.maxFileSize) {\n            form.maxFileSize = opts.maxFileSize;\n        }\n\n        if (opts.hash) {\n            form.hash = opts.hash;\n        }\n\n        form.onPart = function onPart(part) {\n            if (part.filename && opts.multipartFileHandler) {\n                opts.multipartFileHandler(part, req);\n            } else if (!part.filename && opts.multipartHandler) {\n                opts.multipartHandler(part, req);\n            } else {\n                form.handlePart(part);\n            }\n        };\n\n        return form.parse(req, function parse(err, fields, files) {\n            if (err) {\n                return next(new errors.BadRequestError(err.message));\n            }\n\n            req.body = fields;\n            req.files = files;\n\n            if (opts.mapParams !== false) {\n                Object.keys(fields).forEach(function forEach(k) {\n                    if (req.params[k] && !override) {\n                        return;\n                    }\n\n                    req.params[k] = fields[k];\n                });\n\n                if (opts.mapFiles) {\n                    var barrier = vasync.barrier();\n                    barrier.on('drain', function onDrain() {\n                        return next();\n                    });\n\n                    barrier.start('fs');\n                    Object.keys(files).forEach(function forEach(f) {\n                        if (req.params[f] && !override) {\n                            return;\n                        }\n                        barrier.start('fs' + f);\n                        fs.readFile(files[f].path, function readFile(ex, data) {\n                            barrier.done('fs' + f);\n                            /*\n                             * We want to stop the request here, if there's\n                             * an error trying to read the file from disk.\n                             * Ideally we'd like to stop the other oustanding\n                             * file reads too, but there's no way to cancel\n                             * in flight fs reads.  So we just return an\n                             * error, and be grudgingly let the other file\n                             * reads finish.\n                             */\n                            if (ex) {\n                                return next(\n                                    new errors.InternalError(\n                                        ex,\n                                        'unable to read file' + f\n                                    )\n                                );\n                            }\n                            req.params[f] = data;\n                            return true;\n                        });\n                    });\n                    barrier.done('fs');\n                    return false;\n                } else {\n                    return next();\n                }\n            } else {\n                return next();\n            }\n        });\n    }\n\n    return parseMultipartBody;\n}\n\nmodule.exports = multipartBodyParser;\n"
  },
  {
    "path": "lib/plugins/oauth2TokenParser.js",
    "content": "/*\noauth2TokenParser - Parser oauth2 tokens from the authorization header\nor BODY of the request\n\nIf parsing from the BODY there is adependency on the bodyParser plugin:\n\nserver.use(plugins.bodyParser());\nserver.use(plugins.oauth2TokenParser());\n\n\n*/\n'use strict';\n\nvar errors = require('restify-errors');\n\n/*\n\n  Parses the header for the authorization: bearer\n\n*/\nfunction parseHeader(req) {\n    if (req.headers && req.headers.authorization) {\n        var credentialsIndex = 1;\n        var parts = req.headers.authorization.split(' ');\n        var partsExpectedLength = 2;\n        var schemeIndex = 0;\n\n        if (parts.length === partsExpectedLength) {\n            var credentials = parts[credentialsIndex];\n            var scheme = parts[schemeIndex];\n\n            if (/^Bearer$/i.test(scheme)) {\n                return credentials;\n            }\n        }\n    }\n\n    return null;\n}\n\n/**\n * Returns a plugin that will parse the client's request for an OAUTH2\n   access token\n *\n * Subsequent handlers will see `req.oauth2`, which looks like:\n *\n * ```js\n * {\n *   oauth2: {\n        accessToken: 'mF_9.B5f-4.1JqM&p=q'\n    }\n * }\n * ```\n *\n * @public\n * @function oauth2TokenParser\n * @throws   {InvalidArgumentError}\n * @param    {Object} options - an options object\n * @returns  {Function} Handler\n */\nfunction oauth2TokenParser(options) {\n    function parseOauth2Token(req, res, next) {\n        req.oauth2 = { accessToken: null };\n\n        var tokenFromHeader = parseHeader(req);\n\n        if (tokenFromHeader) {\n            req.oauth2.accessToken = tokenFromHeader;\n        }\n\n        var tokenFromBody = null;\n\n        if (typeof req.body === 'object') {\n            tokenFromBody = req.body.access_token;\n        }\n\n        // more than one method to transmit the token in each request\n        // is not allowed - return 400\n        if (tokenFromBody && tokenFromHeader) {\n            // eslint-disable-next-line new-cap\n            return next(\n                new errors.makeErrFromCode(400, 'multiple tokens disallowed')\n            );\n        }\n\n        if (\n            tokenFromBody &&\n            req.contentType().toLowerCase() ===\n                'application/x-www-form-urlencoded'\n        ) {\n            req.oauth2.accessToken = tokenFromBody;\n        }\n\n        return next();\n    }\n\n    return parseOauth2Token;\n}\n\nmodule.exports = oauth2TokenParser;\n"
  },
  {
    "path": "lib/plugins/pre/context.js",
    "content": "// Copyright 2018 Restify. All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\n\n///--- API\n\n/**\n * This plugin creates `req.set(key, val)` and `req.get(key)` methods for\n * setting and retrieving request specific data.\n *\n * @public\n * @function context\n * @returns {Function} Handler\n * @example\n * server.pre(restify.plugins.pre.context());\n * server.get('/', [\n *     function(req, res, next) {\n *         req.set(myMessage, 'hello world');\n *         return next();\n *     },\n *     function two(req, res, next) {\n *         res.send(req.get(myMessage)); // => sends 'hello world'\n *         return next();\n *     }\n * ]);\n */\nfunction ctx() {\n    return function context(req, res, next) {\n        var data = {};\n\n        /**\n         * Set context value by key\n         * Requires the context plugin.\n         *\n         * @public\n         * @memberof Request\n         * @instance\n         * @function req.set\n         * @param    {String} key - key\n         * @param    {*} value - value\n         * @returns  {undefined} no return value\n         */\n        req.set = function set(key, value) {\n            assert.string(key, 'key must be string');\n\n            if (key === '') {\n                assert.fail('key must not be empty string');\n            }\n            data[key] = value;\n        };\n\n        /**\n         * Get context value by key.\n         * Requires the context plugin.\n         *\n         * @public\n         * @memberof Request\n         * @instance\n         * @function req.get\n         * @param    {String} key - key\n         * @returns  {*} value stored in context\n         */\n        req.get = function get(key) {\n            assert.string(key, 'key must be string');\n\n            if (key === '') {\n                assert.fail('key must not be empty string');\n            }\n            return data[key];\n        };\n\n        /**\n         * Get all context\n         * Requires the context plugin.\n         *\n         * @public\n         * @memberof Request\n         * @instance\n         * @function req.getAll\n         * @returns  {*} value stored in context\n         */\n        req.getAll = function getAll() {\n            return data;\n        };\n\n        return next();\n    };\n}\n\n///--- Exports\n\nmodule.exports = ctx;\n"
  },
  {
    "path": "lib/plugins/pre/dedupeSlashes.js",
    "content": "'use strict';\n\n/**\n * This plugin deduplicates extra slashes found in the URL. This can help with\n * malformed URLs that might otherwise get misrouted.\n *\n * @public\n * @function dedupeSlashes\n * @returns {Function} Handler\n * @example\n * server.pre(restify.plugins.pre.dedupeSlashes());\n * server.get('/hello/:one', function(req, res, next) {\n *     res.send(200);\n *     return next();\n * });\n *\n * // the server will now convert requests to /hello//jake => /hello/jake\n */\nfunction createDedupeSlashes() {\n    return function dedupeSlashes(req, res, next) {\n        req.url = req.url.replace(/(\\/)\\/+/g, '$1');\n        return next();\n    };\n}\n\nmodule.exports = createDedupeSlashes;\n"
  },
  {
    "path": "lib/plugins/pre/pause.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n///--- Helpers\n\n/**\n * @private\n * @function pauseStream\n * @param    {Stream} stream - the stream to pause\n * @returns  {undefined} no return value\n */\nfunction pauseStream(stream) {\n    function _buffer(chunk) {\n        stream.__buffered.push(chunk);\n    }\n\n    function _catchEnd(chunk) {\n        stream.__rstfyEnded = true;\n    }\n\n    stream.__rstfyEnded = false;\n    stream.__rstfyPaused = true;\n    stream.__buffered = [];\n    stream.on('data', _buffer);\n    stream.once('end', _catchEnd);\n    stream.pause();\n\n    stream._resume = stream.resume;\n    stream.resume = function _rstfy_resume() {\n        if (!stream.__rstfyPaused) {\n            return;\n        }\n\n        stream.removeListener('data', _buffer);\n        stream.removeListener('end', _catchEnd);\n\n        stream.__buffered.forEach(stream.emit.bind(stream, 'data'));\n        stream.__buffered.length = 0;\n\n        stream._resume();\n        stream.resume = stream._resume;\n\n        if (stream.__rstfyEnded) {\n            stream.emit('end');\n        }\n    };\n}\n\n/**\n * This pre handler fixes issues with node hanging when an `asyncHandler` is\n * used prior to `bodyParser`.\n * https://github.com/restify/node-restify/issues/287\n * https://github.com/restify/node-restify/issues/409\n * https://github.com/restify/node-restify/wiki/1.4-to-2.0-Migration-Tips\n *\n * @public\n * @function pause\n * @returns  {Function} Handler\n */\nfunction pause() {\n    function prePause(req, res, next) {\n        pauseStream(req);\n        next();\n    }\n\n    return prePause;\n}\n\n///--- Exports\n\nmodule.exports = pause;\n"
  },
  {
    "path": "lib/plugins/pre/prePath.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n///--- Helpers\n\n/**\n * Cleans up sloppy URLs on the request object, like /foo////bar/// to /foo/bar.\n *\n * @private\n * @function strip\n * @param    {Object} path - a url path to clean up\n * @returns  {String} cleaned path\n */\nfunction strip(path) {\n    var cur;\n    var next;\n    var str = '';\n\n    for (var i = 0; i < path.length; i++) {\n        cur = path.charAt(i);\n\n        if (i !== path.length - 1) {\n            next = path.charAt(i + 1);\n        }\n\n        if (cur === '/' && (next === '/' || (next === '?' && i > 0))) {\n            continue;\n        }\n\n        str += cur;\n    }\n\n    return str;\n}\n\n/**\n * Cleans up sloppy URLs on the request object,\n * like `/foo////bar///` to `/foo/bar`.\n *\n * @public\n * @function sanitizePath\n * @returns  {Function} Handler\n */\nfunction sanitizePath() {\n    function _sanitizePath(req, res, next) {\n        req.url = strip(req.url);\n        next();\n    }\n\n    return _sanitizePath;\n}\n\n///--- Exports\n\nmodule.exports = sanitizePath;\n"
  },
  {
    "path": "lib/plugins/pre/reqIdHeaders.js",
    "content": "'use strict';\n\nvar assert = require('assert-plus');\n\nvar DEFAULT_HEADERS = ['request-id', 'x-request-id'];\n\n/**\n * This plugin pulls the value from an incoming request header and uses it\n * as the value of the request id. Subsequent calls to `req.id()`\n * will return the header values.\n *\n * @public\n * @function reqIdHeaders\n * @param {Object}   opts - an options object\n * @param {String[]} opts.headers - array of headers from where to pull existing\n *                                request id headers. Lookup precedence\n *                                is left to right (lowest index first)\n * @returns {Function} Handler\n */\nfunction createReqIdHeaders(opts) {\n    assert.object(opts, 'opts');\n    assert.arrayOfString(opts.headers, 'opts.headers');\n\n    var headers = opts.headers.concat(DEFAULT_HEADERS);\n\n    return function reqIdHeaders(req, res, next) {\n        for (var i = 0; i < headers.length; i++) {\n            var val = req.header(headers[i]);\n\n            if (val) {\n                req.id(val);\n                break;\n            }\n        }\n\n        return next();\n    };\n}\n\n///--- Exports\n\nmodule.exports = createReqIdHeaders;\n"
  },
  {
    "path": "lib/plugins/pre/strictQueryParams.js",
    "content": "'use strict';\n\nvar BadRequestError = require('restify-errors').BadRequestError;\nvar assert = require('assert-plus');\n\n///--- API\n\n/**\n * Prevents `req.urls` non-strict key-value query params\n *\n * The Request-URI is transmitted in the format specified in section 3.2.1.\n * If the Request-URI is encoded using the \"% HEX HEX\" encoding [42],\n * the origin server MUST decode the Request-URI\n * in order to properly interpret the request.\n * Servers SHOULD respond to invalid Request-URIs\n * with an appropriate status code.\n *\n * part of Hypertext Transfer Protocol -- HTTP/1.1 | 5.1.2 Request-URI\n * RFC 2616 Fielding, et al.\n *\n * @public\n * @function strictQueryParams\n * @param    {Object}   [options] - an options object\n * @param    {String}   [options.message] - a custom error message\n *                              default value:\n *                              \"Url query params does not meet strict format\"\n * @returns  {Function} Handler\n */\nfunction strictQueryParams(options) {\n    var opts = options || {};\n    assert.optionalObject(opts, 'options');\n    assert.optionalString(opts.message, 'options.message');\n\n    function _strictQueryParams(req, res, next) {\n        var keyValQParams = !/(\\&(?!(\\w+=\\w+)))/.test(req.url);\n\n        if (!keyValQParams) {\n            var msg = opts.message\n                ? opts.message\n                : 'Url query params does not meet strict format';\n            return next(new BadRequestError(msg));\n        }\n\n        return next();\n    }\n\n    return _strictQueryParams;\n}\n\n///--- Exports\n\nmodule.exports = strictQueryParams;\n"
  },
  {
    "path": "lib/plugins/pre/userAgent.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\n\n///--- API\n\n/**\n * This basically exists for `curl`. `curl` on `HEAD` requests usually\n * just sits there and hangs, unless you explicitly set\n * Connection:close. And in general, you probably want to set\n * Connection: close to curl anyway.\n *\n * Also, because curl spits out an annoying message to stderr about\n * remaining bytes if content-length is set, this plugin also drops\n * the `content-length` header (some user agents handle it and want it,\n * curl does not).\n *\n * To be slightly more generic, the options block takes a user\n * agent regexp, however.\n *\n * @public\n * @function userAgentConnection\n * @param    {Object} [options] - an options object\n * @param    {RegExp} [options.userAgentRegExp=/^curl.+/] - matching any\n *                                                        user-agents applicable\n * @returns  {Function} Handler\n */\nfunction userAgentConnection(options) {\n    var opts = options || {};\n    assert.optionalObject(opts, 'options');\n    assert.optionalObject(opts.userAgentRegExp, 'options.userAgentRegExp');\n\n    var re = opts.userAgentRegExp;\n\n    if (!re) {\n        re = /^curl.+/;\n    }\n\n    function handleUserAgent(req, res, next) {\n        var ua = req.headers['user-agent'];\n\n        if (ua && re.test(ua)) {\n            res.setHeader('Connection', 'close');\n\n            if (req.method === 'HEAD') {\n                res.once(\n                    'header',\n                    res.removeHeader.bind(res, 'content-length')\n                );\n            }\n        }\n\n        next();\n    }\n\n    return handleUserAgent;\n}\n\nmodule.exports = userAgentConnection;\n"
  },
  {
    "path": "lib/plugins/query.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar qs = require('qs');\nvar assert = require('assert-plus');\n\n/**\n * Parses the HTTP query string (i.e., `/foo?id=bar&name=mark`).\n * If you use this, the parsed content will always be available in `req.query`,\n * additionally params are merged into `req.params`.\n * You can disable by passing in `mapParams: false` in the options object.\n *\n * Many options correspond directly to option defined for the underlying\n * [`qs.parse`](https://github.com/ljharb/qs).\n *\n * @public\n * @function queryParser\n * @param    {Object}   [options] - an options object\n * @param    {Object}   [options.mapParams=true] - disable passing\n * @param {Boolean} [options.mapParams=false] - Copies parsed query parameters\n * into`req.params`.\n * @param {Boolean} [options.overrideParams=false] - Only applies when if\n * mapParams true.\n * When true, will stomp on req.params field when existing value is found.\n * @param {Boolean} [options.allowDots=false] - Transform `?foo.bar=baz` to a\n * nested object: `{foo: {bar: 'baz'}}`.\n * @param {Number} [options.arrayLimit=20] - Only transform `?a[$index]=b`\n * to an array if `$index` is less than `arrayLimit`.\n * @param {Number} [options.depth=5] - The depth limit for parsing\n * nested objects, e.g. `?a[b][c][d][e][f][g][h][i]=j`.\n * @param {Number} [options.parameterLimit=1000] - Maximum number of query\n * params parsed. Additional params are silently dropped.\n * @param {Boolean} [options.parseArrays=true] - Whether to parse\n * `?a[]=b&a[1]=c` to an array, e.g. `{a: ['b', 'c']}`.\n * @param {Boolean} [options.plainObjects=false] - Whether `req.query` is a\n * \"plain\" object -- does not inherit from `Object`.\n * This can be used to allow query params whose names collide with Object\n * methods, e.g. `?hasOwnProperty=blah`.\n * @param {Boolean} [options.strictNullHandling=false] - If true, `?a&b=`\n * results in `{a: null, b: ''}`. Otherwise, `{a: '', b: ''}`.\n * @returns  {Function} Handler\n * @example\n * server.use(restify.plugins.queryParser({ mapParams: false }));\n */\nfunction queryParser(options) {\n    var opts = options || {};\n    assert.object(opts, 'opts');\n\n    function parseQueryString(req, res, next) {\n        if (!req.getQuery()) {\n            req.query = {};\n            return next();\n        }\n\n        req.query = qs.parse(req.getQuery(), opts);\n\n        if (opts.mapParams === true) {\n            Object.keys(req.query).forEach(function forEach(k) {\n                if (req.params[k] && !opts.overrideParams) {\n                    return;\n                }\n                req.params[k] = req.query[k];\n            });\n        }\n\n        return next();\n    }\n\n    return parseQueryString;\n}\n\nmodule.exports = queryParser;\n"
  },
  {
    "path": "lib/plugins/requestExpiry.js",
    "content": "'use strict';\n\nvar assert = require('assert-plus');\nvar GatewayTimeoutError = require('restify-errors').GatewayTimeoutError;\n\n/**\n * Request Expiry can be used to throttle requests that have already exceeded\n * their client timeouts. Requests can be sent with a configurable client\n * timeout header, e.g. 'x-request-expiry-time', which gives in absolute ms\n * since epoch, when this request will be timed out by the client.\n *\n * This plugin will throttle all incoming requests via a 504 where\n * 'x-request-expiry-time' less than Date.now() -- since these incoming requests\n * have already been timed out by the client. This prevents the server from\n * processing unnecessary requests.\n *\n * Request expiry will use headers to tell if the incoming request has expired.\n * There are two options for this plugin:\n *  1. Absolute Time\n *     * Time in Milliseconds since Epoch when this request should be\n *     considered expired\n *  2. Timeout\n *     * The request start time is supplied\n *     * A timeout, in milliseconds, is given\n *     * The timeout is added to the request start time to arrive at the\n *       absolute time in which the request is considered expired\n *\n * #### Using an external storage mechanism for key/bucket mappings.\n *\n * By default, the restify throttling plugin uses an in-memory LRU to store\n * mappings between throttling keys (i.e., IP address) to the actual bucket that\n * key is consuming.  If this suits you, you can tune the maximum number of keys\n * to store in memory with `options.maxKeys`; the default is 10000.\n *\n * In some circumstances, you want to offload this into a shared system, such as\n * Redis, if you have a fleet of API servers and you're not getting steady\n * and/or uniform request distribution.  To enable this, you can pass in\n * `options.tokensTable`, which is simply any Object that supports `put` and\n * `get` with a `String` key, and an `Object` value.\n *\n * @public\n * @function requestExpiry\n * @param    {Object} opts - an options object\n * @param    {String} [opts.absoluteHeader] - The header key to be used for\n *                                   the expiry time of each request.\n * @param    {String} opts.startHeader - The header key for the start time\n *                                   of the request.\n * @param    {String} opts.timeoutHeader - The header key for the time in\n *                                   milliseconds that should ellapse before\n *                                   the request is considered expired.\n * @returns  {Function} Handler\n * @example\n * <caption>\n * The only option provided is `header` which is the request header used\n * to specify the client timeout.\n * </caption>\n * server.use(restify.plugins.requestExpiry({\n *     header: 'x-request-expiry-time'\n * });\n */\nfunction requestExpiry(opts) {\n    assert.object(opts, 'opts');\n    assert.optionalString(opts.absoluteHeader, 'opts.absoluteHeader');\n\n    if (!opts.absoluteHeader) {\n        assert.string(opts.startHeader, 'opts.startHeader');\n        assert.string(opts.timeoutHeader, 'opts.timeoutHeader');\n    }\n\n    var useAbsolute = opts.absoluteHeader !== undefined;\n    var absoluteHeaderKey = opts.absoluteHeader;\n    var startHeaderKey = opts.startHeader;\n    var timeoutHeaderKey = opts.timeoutHeader;\n\n    return function requestExpirationCheck(req, res, next) {\n        /*\n         * Add check expiry API to to req if it doesn't already exist. We only\n         * add this the first time this handler is run, since this handler\n         * could be used in multiple places in the handler chain.\n         */\n        if (!req._expiryTime) {\n            // if the headers don't exist, then the request will never expire.\n            req._expiryTime = Infinity;\n\n            if (useAbsolute) {\n                var expiryTime = parseInt(req.headers[absoluteHeaderKey], 10);\n\n                if (!isNaN(expiryTime)) {\n                    req._expiryTime = expiryTime;\n                }\n            } else {\n                // Use the start time header and add the timeout header to it\n                // to arrive at the expiration time\n                var startTime = parseInt(req.headers[startHeaderKey], 10);\n                var timeout = parseInt(req.headers[timeoutHeaderKey], 10);\n\n                if (!isNaN(startTime) && !isNaN(timeout)) {\n                    req._expiryTime = startTime + timeout;\n                }\n            }\n\n            req.isExpired = function isExpired() {\n                return Date.now() > req._expiryTime;\n            };\n        }\n\n        if (req.isExpired()) {\n            // The request has expired\n            return next(\n                new GatewayTimeoutError({\n                    message: 'Request has expired',\n                    context: {\n                        expiryTime: req._expiryTime,\n                        mode: opts.absoluteHeader ? 'absolute' : 'relative'\n                    }\n                })\n            );\n        } else {\n            // Happy case\n            return next();\n        }\n    };\n}\n\nmodule.exports = requestExpiry;\n"
  },
  {
    "path": "lib/plugins/requestLogger.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar assert = require('assert-plus');\n\nvar shallowCopy = require('./utils/shallowCopy');\n\n///--- API\n\n/**\n * Sets up a child [logger](https://github.com/pinojs/pino) logger with\n * the current request id filled in, along with any other parameters you define.\n *\n * You can pass in no options to this, in which case only the request id will be\n * appended, and no serializers appended (this is also the most performant); the\n * logger created at server creation time will be used as the parent logger.\n * This logger can be used normally, with [req.log](#request-api).\n *\n * This plugin does _not_ log each individual request. Use the Audit Logging\n * plugin or a custom middleware for that use.\n *\n * @public\n * @function requestLogger\n * @param    {Object}   [options] - an options object\n * @param    {Array}    [options.headers] - A list of headers to transfer from\n *                                  the request to top level props on the log.\n * @param    {Object}   [options.properties] - A set of key-values to pass to the child logger\n * @param    {Object}   [options.serializers] - Override serializers to use in the child logger\n * @param    {Object}   [options.log] - A logger to use as a fallback if req.log is missing\n * @param    {String}   [options.requestIdFieldName] - The name of the request id property attached\n *                                   to log lines. Defaults to \"req_id\".\n * @returns  {Function} Handler\n * @example\n * server.use(restify.plugins.requestLogger({\n *     properties: {\n *         foo: 'bar'\n *     },\n *     serializers: {...}\n * }));\n */\nfunction requestLogger(options) {\n    assert.optionalObject(options);\n    var opts = options || {};\n\n    var props;\n\n    if (opts.properties) {\n        props = shallowCopy(opts.properties);\n    } else {\n        props = {};\n    }\n\n    if (opts.serializers) {\n        props.serializers = opts.serializers;\n    }\n\n    var headersToCopy = opts.headers || [];\n    const requestIdFieldName = opts.requestIdFieldName || 'req_id';\n\n    return function logger(req, res, next) {\n        if (!req.log && !opts.log) {\n            next();\n            return;\n        }\n\n        var log = req.log || opts.log;\n\n        props[requestIdFieldName] = req.getId();\n\n        headersToCopy.forEach(function forEach(k) {\n            if (req.headers[k]) {\n                props[k] = req.headers[k];\n            }\n        });\n        const childOptions = {};\n        if (props.serializers) {\n            childOptions.serializers = props.serializers;\n        }\n        req.log = log.child(props, childOptions);\n\n        if (props[requestIdFieldName]) {\n            delete props[requestIdFieldName];\n        }\n\n        next();\n    };\n}\n\n///--- Exports\n\nmodule.exports = requestLogger;\n"
  },
  {
    "path": "lib/plugins/static.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar fs = require('fs');\nvar path = require('path');\nvar escapeRE = require('escape-regexp-component');\n\nvar assert = require('assert-plus');\nvar mime = require('mime');\nvar errors = require('restify-errors');\n\n///--- Globals\n\nvar MethodNotAllowedError = errors.MethodNotAllowedError;\nvar NotAuthorizedError = errors.NotAuthorizedError;\nvar ResourceNotFoundError = errors.ResourceNotFoundError;\n\n///--- Functions\n\n/**\n * Serves static files.\n *\n * @public\n * @function serveStatic\n * @param    {Object} options - an options object\n * @throws   {MethodNotAllowedError} |\n * @throws   {NotAuthorizedError}\n * @throws   {ResourceNotFoundError}\n * @returns  {Function} Handler\n * @example\n * <caption>\n * The serveStatic module is different than most of the other plugins, in that\n * it is expected that you are going to map it to a route, as below:\n * </caption>\n * server.get('/docs/current/*', restify.plugins.serveStatic({\n *   directory: './documentation/v1',\n *   default: 'index.html'\n * }));\n * @example\n * <caption>\n * The above `route` and `directory` combination will serve a file located in\n * `./documentation/v1/docs/current/index.html` when you attempt to hit\n * `http://localhost:8080/docs/current/`. If you want the serveStatic module to\n * serve files directly from the `/documentation/v1` directory\n * (and not append the request path `/docs/current/`),\n * you can set the `appendRequestPath` option to `false`, and the served file\n * would be `./documentation/v1/index.html`, in the previous example.\n *\n * The plugin will enforce that all files under `directory` are served.\n * The `directory` served is relative to the process working directory.\n * You can also provide a `default` parameter such as index.html for any\n * directory that lacks a direct file match.\n * You can specify additional restrictions by passing in a `match` parameter,\n * which is just a `RegExp` to check against the requested file name.\n * Additionally, you may set the `charSet` parameter, which will append a\n * character set to the content-type detected by the plugin.\n * For example, `charSet: 'utf-8'` will result in HTML being served with a\n * `Content-Type` of `text/html; charset=utf-8`.\n * Lastly, you can pass in a `maxAge` numeric, which will set the\n * `Cache-Control` header. Default is `3600` (1 hour).\n *\n * An additional option for serving a static file is to pass `file` in to the\n * serveStatic method as an option. The following will serve index.html from\n * the documentation/v1/ directory anytime a client requests `/home/`.\n * </caption>\n * server.get('/home/*', restify.plugins.serveStatic({\n *   directory: './documentation/v1',\n *   file: 'index.html'\n * }));\n * // or\n * server.get('/home/([a-z]+[.]html)', restify.plugins.serveStatic({\n *   directory: './documentation/v1',\n *   file: 'index.html'\n * }));\n */\nfunction serveStatic(options) {\n    var opts = options || {};\n\n    if (typeof opts.appendRequestPath === 'undefined') {\n        opts.appendRequestPath = true;\n    }\n\n    assert.object(opts, 'options');\n    assert.string(opts.directory, 'options.directory');\n    assert.optionalNumber(opts.maxAge, 'options.maxAge');\n    assert.optionalObject(opts.match, 'options.match');\n    assert.optionalString(opts.charSet, 'options.charSet');\n    assert.optionalString(opts.file, 'options.file');\n    assert.bool(opts.appendRequestPath, 'options.appendRequestPath');\n\n    var p = path.normalize(opts.directory).replace(/\\\\/g, '/');\n    var re = new RegExp('^' + escapeRE(p) + '/?.*');\n\n    function serveFileFromStats(file, err, stats, isGzip, req, res, next) {\n        if (\n            typeof req.connectionState === 'function' &&\n            req.connectionState() === 'close'\n        ) {\n            next(false);\n            return;\n        }\n\n        if (err) {\n            next(new ResourceNotFoundError(err, '%s', req.path()));\n            return;\n        } else if (!stats.isFile()) {\n            next(new ResourceNotFoundError('%s does not exist', req.path()));\n            return;\n        }\n\n        if (res.handledGzip && isGzip) {\n            res.handledGzip();\n        }\n\n        var fstream = fs.createReadStream(file + (isGzip ? '.gz' : ''));\n        var maxAge = opts.maxAge === undefined ? 3600 : opts.maxAge;\n        fstream.once('open', function onceOpen(fd) {\n            res.cache({ maxAge: maxAge });\n            res.set('Content-Length', stats.size);\n            res.set('Content-Type', mime.getType(file));\n            res.set('Last-Modified', stats.mtime);\n\n            if (opts.charSet) {\n                var type =\n                    res.getHeader('Content-Type') + '; charset=' + opts.charSet;\n                res.setHeader('Content-Type', type);\n            }\n\n            if (opts.etag) {\n                res.set('ETag', opts.etag(stats, opts));\n            }\n            res.writeHead(200);\n            fstream.pipe(res);\n            fstream.once('close', function onceClose() {\n                next(false);\n            });\n        });\n\n        res.once('close', function onceClose() {\n            fstream.close();\n        });\n    }\n\n    function serveNormal(file, req, res, next) {\n        fs.stat(file, function fileStat(err, stats) {\n            if (!err && stats.isDirectory() && opts.default) {\n                // Serve an index.html page or similar\n                var filePath = path.join(file, opts.default);\n                fs.stat(filePath, function dirStat(dirErr, dirStats) {\n                    serveFileFromStats(\n                        filePath,\n                        dirErr,\n                        dirStats,\n                        false,\n                        req,\n                        res,\n                        next\n                    );\n                });\n            } else {\n                serveFileFromStats(file, err, stats, false, req, res, next);\n            }\n        });\n    }\n\n    function serve(req, res, next) {\n        var file;\n\n        if (opts.file) {\n            //serves a direct file\n            file = path.join(opts.directory, decodeURIComponent(opts.file));\n        } else if (opts.appendRequestPath) {\n            file = path.join(opts.directory, decodeURIComponent(req.path()));\n        } else {\n            var dirBasename = path.basename(opts.directory);\n            var reqpathBasename = path.basename(req.path());\n\n            if (\n                path.extname(req.path()) === '' &&\n                dirBasename === reqpathBasename\n            ) {\n                file = opts.directory;\n            } else {\n                file = path.join(\n                    opts.directory,\n                    decodeURIComponent(path.basename(req.path()))\n                );\n            }\n        }\n\n        if (req.method !== 'GET' && req.method !== 'HEAD') {\n            next(new MethodNotAllowedError('%s', req.method));\n            return;\n        }\n\n        if (!re.test(file.replace(/\\\\/g, '/'))) {\n            next(new NotAuthorizedError('%s', req.path()));\n            return;\n        }\n\n        if (opts.match && !opts.match.test(file)) {\n            next(new NotAuthorizedError('%s', req.path()));\n            return;\n        }\n\n        if (opts.gzip && req.acceptsEncoding('gzip')) {\n            fs.stat(file + '.gz', function stat(err, stats) {\n                if (!err) {\n                    res.setHeader('Content-Encoding', 'gzip');\n                    serveFileFromStats(file, err, stats, true, req, res, next);\n                } else {\n                    serveNormal(file, req, res, next);\n                }\n            });\n        } else {\n            serveNormal(file, req, res, next);\n        }\n    }\n\n    return serve;\n}\n\nmodule.exports = serveStatic;\n"
  },
  {
    "path": "lib/plugins/staticFiles.js",
    "content": "'use strict';\nvar assert = require('assert-plus');\nvar errors = require('restify-errors');\nvar path = require('path');\nvar send = require('send');\nvar shallowCopy = require('./utils/shallowCopy');\n\n///--- Globals\nvar MethodNotAllowedError = errors.MethodNotAllowedError;\nvar NotAuthorizedError = errors.NotAuthorizedError;\nvar ResourceNotFoundError = errors.ResourceNotFoundError;\n\n/**\n * Serves static files, with API similar to expressjs\n *\n * @public\n * @function serveStaticFiles\n * @param    {String} directory - the directory to serve files from\n * @param    {Object} opts - an options object, which is optional\n * @param    {Number} [opts.maxAge=0] - specify max age in millisecs\n * @param    {Boolean} [opts.etag=true] - enable/disable etag, default = true\n * @param    {Function} [opts.setHeaders] - set custom headers for the Files\n * (synchronously), The function is called as `fn(res, path, stat)`,\n * where the arguments are:\n *      `res` the response object\n *      `path` the file path that is being sent\n *      `stat` the stat object of the file that is being sent\n * @throws   {MethodNotAllowedError}\n * @throws   {NotAuthorizedError}\n * @throws   {ResourceNotFoundError}\n * @returns  {Function} Handler\n * @example\n * <caption>\n * The serveStaticFiles plugin allows you to map a GET route to a\n * directory on the disk\n * </caption>\n * server.get('/public/*', // don't forget the `/*`\n *      restify.plugins.serveStaticFiles('./documentation/v1')\n * );\n * @example\n * <caption>\n * The GET `route` and `directory` combination will serve a file\n * located in `./documentation/v1/index.html` when you attempt to hit\n * `http://localhost:8080/public/index.html`\n *\n * The plugin uses [send](https://github.com/pillarjs/send) under the hood\n * which is also used by `expressjs` to serve static files. Most of the options\n * that work with `send` will work with this plugin.\n *\n * The default file the plugin looks for is `index.html`\n * </caption>\n * server.get('/public/*',\n *      restify.plugins.serveStaticFiles('./documentation/v1', {\n *      maxAge: 3600000, // this is in millisecs\n *      etag: false,\n *      setHeaders: function setCustomHeaders(response, requestedPath, stat) {\n *              response.setHeader('restify-plugin-x', 'awesome');\n *          }\n *      })\n * );\n */\nfunction serveStaticFiles(directory, opts) {\n    // make a copy of the options that will be passed to send\n    var optionsPlugin = shallowCopy(opts || {});\n    var optionsSend = shallowCopy(opts || {});\n    // lets assert some options\n    assert.object(optionsSend, 'options');\n    assert.object(optionsPlugin, 'options');\n    assert.string(directory, 'directory');\n\n    // `send` library relies on `root` to specify the root folder\n    // to look for files\n    optionsSend.root = path.resolve(directory);\n    // `setHeaders` is only understood by our plugin\n    if (optionsSend.setHeaders) {\n        delete optionsSend.setHeaders;\n    }\n\n    return function handleServeStaticFiles(req, res, next) {\n        // Check to make sure that this was either a GET or a HEAD request\n        if (req.method !== 'GET' && req.method !== 'HEAD') {\n            return next(new MethodNotAllowedError('%s', req.method));\n        }\n        // we expect the params to have `*`:\n        // This allows the router to accept any file path\n        var requestedFile = req.params['*'] || 'index.html';\n        // This is used only for sending back correct error message text\n        var requestedFullPath = req.url;\n        // Rely on `send` library to create a stream\n        var stream = send(req, requestedFile, optionsSend);\n\n        // Lets handle the various events being emitted by send module\n\n        // stream has ended, must call `next()`\n        stream.on('end', function handleEnd() {\n            return next();\n        });\n\n        // when `send` encounters any `error`, we have the opportunity\n        // to handle the errors here\n        stream.on('error', function handleError(err) {\n            var respondWithError = null;\n            // When file does not exist\n            if (err.statusCode === 404) {\n                respondWithError = new ResourceNotFoundError(requestedFullPath);\n            } else {\n                // or action is forbidden (like requesting a directory)\n                respondWithError = new NotAuthorizedError(requestedFullPath);\n            }\n            return next(respondWithError);\n        });\n\n        // If the request was for directory and that directory did not\n        // have index.html, this will be called\n        stream.on('directory', function handleDirectoryRequest() {\n            next(new NotAuthorizedError('%s', requestedFullPath));\n            return;\n        });\n\n        // stream is about to send headers, and custom headers must be\n        // set now\n        stream.on('headers', function handleCustomHeaders(\n            response,\n            requestedPath,\n            stat\n        ) {\n            if (\n                optionsPlugin.setHeaders &&\n                typeof optionsPlugin.setHeaders === 'function'\n            ) {\n                optionsPlugin.setHeaders(response, requestedPath, stat);\n            }\n        });\n\n        // pipe the stream into response\n        return stream.pipe(res);\n    };\n}\n\nmodule.exports = serveStaticFiles;\n"
  },
  {
    "path": "lib/plugins/throttle.js",
    "content": "// Copyright 2012 Mark Cavage <mcavage@gmail.com> All rights reserved.\n\n'use strict';\n\nvar sprintf = require('util').format;\n\nvar assert = require('assert-plus');\nvar LRU = require('lru-cache');\nvar errors = require('restify-errors');\n\n///--- Globals\n\nvar TooManyRequestsError = errors.TooManyRequestsError;\n\nvar MESSAGE = 'You have exceeded your request rate of %s r/s.';\n\n///--- Helpers\n\n/**\n * @private\n * @function xor\n * @returns {undefined} no return value\n */\nfunction xor() {\n    var x = false;\n\n    for (var i = 0; i < arguments.length; i++) {\n        if (arguments[i] && !x) {\n            x = true;\n        } else if (arguments[i] && x) {\n            return false;\n        }\n    }\n    return x;\n}\n\n///--- Internal Class (TokenBucket)\n\n/**\n * An implementation of the Token Bucket algorithm.\n *\n * Basically, in network throttling, there are two \"mainstream\"\n * algorithms for throttling requests, Token Bucket and Leaky Bucket.\n * For restify, I went with Token Bucket.  For a good description of the\n * algorithm, see: http://en.wikipedia.org/wiki/Token_bucket\n *\n * In the options object, you pass in the total tokens and the fill rate.\n * Practically speaking, this means \"allow `fill rate` requests/second,\n * with bursts up to `total tokens`\".  Note that the bucket is initialized\n * to full.\n *\n * Also, in googling, I came across a concise python implementation, so this\n * is just a port of that. Thanks http://code.activestate.com/recipes/511490 !\n *\n * @private\n * @class TokenBucket\n * @param {Object} options - contains the parameters:\n *                   - {Number} capacity the maximum burst.\n *                   - {Number} fillRate the rate to refill tokens.\n */\nfunction TokenBucket(options) {\n    assert.object(options, 'options');\n    assert.number(options.capacity, 'options.capacity');\n    assert.number(options.fillRate, 'options.fillRate');\n\n    this.tokens = this.capacity = options.capacity;\n    this.fillRate = options.fillRate;\n    this.time = Date.now();\n}\n\n/**\n * Consume N tokens from the bucket.\n *\n * If there is not capacity, the tokens are not pulled from the bucket.\n *\n * @private\n * @memberof TokenBucket\n * @instance\n * @function consume\n * @param    {Number}  tokens - the number of tokens to pull out.\n * @returns  {Boolean}        true if capacity, false otherwise.\n */\nTokenBucket.prototype.consume = function consume(tokens) {\n    if (tokens <= this._fill()) {\n        this.tokens -= tokens;\n        return true;\n    }\n\n    return false;\n};\n\n/**\n * Fills the bucket with more tokens.\n *\n * Rather than do some whacky setTimeout() deal, we just approximate refilling\n * the bucket by tracking elapsed time from the last time we touched the bucket.\n *\n * Simply, we set the bucket size to min(totalTokens,\n *                                       current + (fillRate * elapsed time)).\n *\n * @private\n * @memberof TokenBucket\n * @instance\n * @function _fill\n * @returns  {Number} the current number of tokens in the bucket.\n */\nTokenBucket.prototype._fill = function _fill() {\n    var now = Date.now();\n\n    // reset account for clock drift (like DST)\n    if (now < this.time) {\n        this.time = now - 1000;\n    }\n\n    if (this.tokens < this.capacity) {\n        var delta = this.fillRate * ((now - this.time) / 1000);\n        this.tokens = Math.min(this.capacity, this.tokens + delta);\n    }\n    this.time = now;\n\n    return this.tokens;\n};\n\n///--- Internal Class (TokenTable)\n/**\n * Just a wrapper over LRU that supports put/get to store token -> bucket\n * mappings.\n *\n * @private\n * @class TokenTable\n * @param {Object} options -      an options object\n * @param {Number} options.size - size of the LRU\n */\nfunction TokenTable(options) {\n    assert.object(options, 'options');\n\n    this.table = new LRU({ max: options.size || 10000 });\n}\n\n/**\n * Puts a value in the token table\n *\n * @private\n * @memberof TokenTable\n * @instance\n * @function put\n * @param {String}      key -   a name\n * @param {TokenBucket} value - a TokenBucket\n * @returns {undefined} no return value\n */\nTokenTable.prototype.put = function put(key, value) {\n    this.table.set(key, value);\n};\n\n/**\n * Puts a value in the token table\n *\n * @private\n * @memberof TokenTable\n * @instance\n * @function get\n * @param {String} key - a key\n * @returns {TokenBucket} token bucket instance\n */\nTokenTable.prototype.get = function get(key) {\n    return this.table.get(key);\n};\n\n///--- Exported API\n\n/**\n * Creates an API rate limiter that can be plugged into the standard\n * restify request handling pipeline.\n *\n * `restify` ships with a fairly comprehensive implementation of\n * [Token bucket](http://en.wikipedia.org/wiki/Token_bucket), with the ability\n * to throttle on IP (or x-forwarded-for) and username (from `req.username`).\n * You define \"global\" request rate and burst rate, and you can define\n * overrides for specific keys.\n * Note that you can always place this on per-URL routes to enable\n * different request rates to different resources (if for example, one route,\n * like `/my/slow/database` is much easier to overwhlem\n * than `/my/fast/memcache`).\n *\n * If a client has consumed all of their available rate/burst, an HTTP response\n * code of `429`\n * [Too Many Requests]\n * (http://tools.ietf.org/html/draft-nottingham-http-new-status-03#section-4)\n * is returned.\n *\n * This throttle gives you three options on which to throttle:\n * username, IP address and 'X-Forwarded-For'. IP/XFF is a /32 match,\n * so keep that in mind if using it.  Username takes the user specified\n * on req.username (which gets automagically set for supported Authorization\n * types; otherwise set it yourself with a filter that runs before this).\n *\n * In both cases, you can set a `burst` and a `rate` (in requests/seconds),\n * as an integer/float.  Those really translate to the `TokenBucket`\n * algorithm, so read up on that (or see the comments above...).\n *\n * In either case, the top level options burst/rate set a blanket throttling\n * rate, and then you can pass in an `overrides` object with rates for\n * specific users/IPs.  You should use overrides sparingly, as we make a new\n * TokenBucket to track each.\n *\n * On the `options` object ip and username are treated as an XOR.\n *\n * @public\n * @function throttle\n * @throws {TooManyRequestsError}\n * @param {Object} options - required options with:\n * @param {Number} options.burst - burst\n * @param {Number} options.rate - rate\n * @param {Boolean} [options.ip] - ip\n * @param {Boolean} [options.username] - username\n * @param {Boolean} [options.xff] - xff\n * @param {Boolean} [options.setHeaders=false] - Set response headers for rate,\n *                               limit (burst) and remaining.\n * @param {Object} [options.overrides] - overrides\n * @param {Object} options.tokensTable - a storage engine this plugin will\n *                              use to store throttling keys -> bucket mappings.\n *                              If you don't specify this, the default is to\n *                              use an in-memory O(1) LRU, with 10k distinct\n *                              keys.  Any implementation just needs to support\n *                              put/get.\n * @param {Number} [options.maxKeys=10000] - If using the default\n *                              implementation, you can specify how large you\n *                              want the table to be.\n * @returns  {Function} Handler\n * @example\n * <caption>\n * An example options object with overrides:\n * </caption>\n * {\n *   burst: 10,  // Max 10 concurrent requests (if tokens)\n *   rate: 0.5,  // Steady state: 1 request / 2 seconds\n *   ip: true,   // throttle per IP\n *   overrides: {\n *     '192.168.1.1': {\n *       burst: 0,\n *       rate: 0    // unlimited\n *   }\n * }\n */\nfunction throttle(options) {\n    assert.object(options, 'options');\n    assert.number(options.burst, 'options.burst');\n    assert.number(options.rate, 'options.rate');\n    assert.optionalBool(options.setHeaders, 'options.setHeaders');\n\n    if (!xor(options.ip, options.xff, options.username)) {\n        throw new Error('(ip ^ username ^ xff)');\n    }\n\n    var table =\n        options.tokensTable || new TokenTable({ size: options.maxKeys });\n\n    function rateLimit(req, res, next) {\n        var attr;\n        var burst = options.burst;\n        var rate = options.rate;\n\n        if (options.ip) {\n            attr = req.connection.remoteAddress;\n        } else if (options.xff) {\n            attr = req.headers['x-forwarded-for'];\n        } else if (options.username) {\n            attr = req.username;\n        } else {\n            req.log.warn({ config: options }, 'Invalid throttle configuration');\n            return next();\n        }\n\n        // Before bothering with overrides, see if this request\n        // even matches\n        if (!attr) {\n            return next();\n        }\n\n        // Check the overrides\n        if (\n            options.overrides &&\n            options.overrides[attr] &&\n            options.overrides[attr].burst !== undefined &&\n            options.overrides[attr].rate !== undefined\n        ) {\n            burst = options.overrides[attr].burst;\n            rate = options.overrides[attr].rate;\n        }\n\n        if (!rate || !burst) {\n            return next();\n        }\n\n        var bucket = table.get(attr);\n\n        if (!bucket) {\n            bucket = new TokenBucket({\n                capacity: burst,\n                fillRate: rate\n            });\n            table.put(attr, bucket);\n        }\n\n        req.log.trace('Throttle(%s): num_tokens= %d', attr, bucket.tokens);\n\n        var tooManyRequests = !bucket.consume(1);\n\n        // set throttle headers after consume which changes the remaining tokens\n        if (options.setHeaders) {\n            res.header('X-RateLimit-Remaining', Math.floor(bucket.tokens));\n            res.header('X-RateLimit-Limit', burst);\n            res.header('X-RateLimit-Rate', rate);\n        }\n\n        if (tooManyRequests) {\n            req.log.info(\n                {\n                    address: req.connection.remoteAddress || '?',\n                    method: req.method,\n                    url: req.url,\n                    user: req.username || '?'\n                },\n                'Throttling'\n            );\n\n            var msg = sprintf(MESSAGE, rate);\n            return next(new TooManyRequestsError(msg));\n        }\n\n        return next();\n    }\n\n    return rateLimit;\n}\n\nmodule.exports = throttle;\n"
  },
  {
    "path": "lib/plugins/utils/hrTimeDurationInMs.js",
    "content": "'use strict';\n\nvar NS_PER_SEC = 1e9;\nvar MS_PER_NS = 1e6;\n\n/**\n* Get duration in milliseconds from two process.hrtime()\n\n* @function hrTimeDurationInMs\n* @param {Array} startTime - [seconds, nanoseconds]\n* @param {Array} endTime - [seconds, nanoseconds]\n* @returns {Number|null} durationInMs\n*/\nfunction hrTimeDurationInMs(startTime, endTime) {\n    if (!Array.isArray(startTime) || !Array.isArray(endTime)) {\n        return null;\n    }\n\n    var secondDiff = endTime[0] - startTime[0];\n    var nanoSecondDiff = endTime[1] - startTime[1];\n    var diffInNanoSecond = secondDiff * NS_PER_SEC + nanoSecondDiff;\n\n    return Math.round(diffInNanoSecond / MS_PER_NS);\n}\n\nmodule.exports = hrTimeDurationInMs;\n"
  },
  {
    "path": "lib/plugins/utils/httpDate.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n/**\n * Takes an instance of a date object, formats it UTC\n * e.g., Wed, 17 Jun 2015 01:30:26 GMT.\n *\n * @public\n * @function httpDate\n * @param    {Object} now - a date object\n * @returns  {String}       formatted dated object\n */\nmodule.exports = function httpDate(now) {\n    return now.toUTCString();\n};\n"
  },
  {
    "path": "lib/plugins/utils/regex.js",
    "content": "'use strict';\n\nmodule.exports = {\n    jsonContentType: new RegExp('^application/[a-zA-Z.]+\\\\+json')\n};\n"
  },
  {
    "path": "lib/plugins/utils/shallowCopy.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n/**\n * Return a shallow copy of the given object\n *\n * @public\n * @function  shallowCopy\n * @param   {Object} obj - the object to copy\n * @returns {Object}       the new copy of the object\n */\nfunction shallowCopy(obj) {\n    if (!obj) {\n        return obj;\n    }\n    var copy = {};\n    Object.keys(obj).forEach(function forEach(k) {\n        copy[k] = obj[k];\n    });\n    return copy;\n}\n\n///--- Exports\n\nmodule.exports = shallowCopy;\n"
  },
  {
    "path": "lib/request.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar url = require('url');\nvar sprintf = require('util').format;\n\nvar assert = require('assert-plus');\nvar mime = require('mime');\nvar Negotiator = require('negotiator');\nvar uuid = require('uuid');\n\nvar dtrace = require('./dtrace');\n\n///-- Helpers\n/**\n * Creates and sets negotiator on request if one doesn't already exist,\n * then returns it.\n *\n * @private\n * @function negotiator\n * @param    {Object} req - the request object\n * @returns  {Object}     a negotiator\n */\nfunction negotiator(req) {\n    var h = req.headers;\n\n    if (!req._negotiator) {\n        req._negotiator = new Negotiator({\n            headers: {\n                accept: h.accept || '*/*',\n                'accept-encoding': h['accept-encoding'] || 'identity'\n            }\n        });\n    }\n\n    return req._negotiator;\n}\n\n///--- API\n\n/**\n * Patch Request object and extends with extra functionalities\n *\n * @private\n * @function patch\n * @param    {http.IncomingMessage|http2.Http2ServerRequest} Request -\n *                                                           Server Request\n * @returns  {undefined} No return value\n */\nfunction patch(Request) {\n    /**\n     * Wraps all of the node\n     * [http.IncomingMessage](https://nodejs.org/api/http.html)\n     * APIs, events and properties, plus the following.\n     * @class Request\n     * @extends http.IncomingMessage\n     */\n\n    ///--- Patches\n\n    /**\n     * Builds an absolute URI for the request.\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function absoluteUri\n     * @param    {String} path - a url path\n     * @returns  {String} uri\n     */\n    Request.prototype.absoluteUri = function absoluteUri(path) {\n        assert.string(path, 'path');\n\n        var protocol = this.isSecure() ? 'https://' : 'http://';\n        var hostname = this.headers.host;\n        return url.resolve(protocol + hostname + this.path() + '/', path);\n    };\n\n    /**\n     * Check if the Accept header is present, and includes the given type.\n     * When the Accept header is not present true is returned.\n     * Otherwise the given type is matched by an exact match, and then subtypes.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function accepts\n     * @param    {String | String[]} types - an array of accept type headers\n     * @returns  {Boolean} is accepteed\n     * @example\n     * <caption>\n     * You may pass the subtype such as html which is then converted internally\n     * to text/html using the mime lookup table:\n     * </caption>\n     * // Accept: text/html\n     * req.accepts('html');\n     * // => true\n     *\n     * // Accept: text/*; application/json\n     * req.accepts('html');\n     * req.accepts('text/html');\n     * req.accepts('text/plain');\n     * req.accepts('application/json');\n     * // => true\n     *\n     * req.accepts('image/png');\n     * req.accepts('png');\n     * // => false\n     */\n    Request.prototype.accepts = function accepts(types) {\n        if (typeof types === 'string') {\n            types = [types];\n        }\n\n        types = types.map(function map(t) {\n            assert.string(t, 'type');\n\n            if (t.indexOf('/') === -1) {\n                t = mime.getType(t);\n            }\n            return t;\n        });\n\n        negotiator(this);\n\n        return this._negotiator.preferredMediaType(types);\n    };\n\n    /**\n     * Checks if the request accepts the encoding type(s) specified.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function acceptsEncoding\n     * @param    {String | String[]} types - an array of accept type headers\n     * @returns  {Boolean} is accepted encoding\n     */\n    Request.prototype.acceptsEncoding = function acceptsEncoding(types) {\n        if (typeof types === 'string') {\n            types = [types];\n        }\n\n        assert.arrayOfString(types, 'types');\n\n        negotiator(this);\n\n        return this._negotiator.preferredEncoding(types);\n    };\n\n    /**\n     * Returns the value of the content-length header.\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function getContentLength\n     * @returns {Number} content length\n     */\n    Request.prototype.getContentLength = function getContentLength() {\n        if (this._clen !== undefined) {\n            return this._clen === false ? undefined : this._clen;\n        }\n\n        // We should not attempt to read and parse the body of an\n        // Upgrade request, so force Content-Length to zero:\n        if (this.isUpgradeRequest()) {\n            return 0;\n        }\n\n        var len = this.header('content-length');\n\n        if (!len) {\n            this._clen = false;\n        } else {\n            this._clen = parseInt(len, 10);\n        }\n\n        return this._clen === false ? undefined : this._clen;\n    };\n    /**\n     * Returns the value of the content-length header.\n     * @public\n     * @memberof Request\n     * @instance\n     * @function contentLength\n     * @returns {Number}\n     */\n    Request.prototype.contentLength = Request.prototype.getContentLength;\n\n    /**\n     * Returns the value of the content-type header. If a content-type is not\n     * set, this will return a default value of `application/octet-stream`.\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function getContentType\n     * @returns {String} content type\n     */\n    Request.prototype.getContentType = function getContentType() {\n        if (this._contentType !== undefined) {\n            return this._contentType;\n        }\n\n        var index;\n        var type = this.headers['content-type'];\n\n        if (!type) {\n            // RFC2616 section 7.2.1\n            this._contentType = 'application/octet-stream';\n        } else if ((index = type.indexOf(';')) === -1) {\n            this._contentType = type;\n        } else {\n            this._contentType = type.substring(0, index);\n        }\n\n        // #877 content-types need to be case insensitive.\n        this._contentType = this._contentType.toLowerCase();\n\n        return this._contentType;\n    };\n\n    /**\n     * Returns the value of the content-type header. If a content-type is not\n     * set, this will return a default value of `application/octet-stream`\n     * @public\n     * @memberof Request\n     * @instance\n     * @function getContentType\n     * @returns {String} content type\n     */\n    Request.prototype.contentType = Request.prototype.getContentType;\n\n    /**\n     * Returns a Date object representing when the request was setup.\n     * Like `time()`, but returns a Date object.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function date\n     * @returns  {Date} date when request began being processed\n     */\n    Request.prototype.date = function date() {\n        return this._date;\n    };\n\n    /**\n     * Retrieves the complete URI requested by the client.\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function getHref\n     * @returns {String} URI\n     */\n    Request.prototype.getHref = function getHref() {\n        return this.getUrl().href;\n    };\n\n    /**\n     * Returns the full requested URL.\n     * @public\n     * @memberof Request\n     * @instance\n     * @function href\n     * @returns {String}\n     * @example\n     * // incoming request is http://localhost:3000/foo/bar?a=1\n     * server.get('/:x/bar', function(req, res, next) {\n     *     console.warn(req.href());\n     *     // => /foo/bar/?a=1\n     * });\n     */\n    Request.prototype.href = Request.prototype.getHref;\n\n    /**\n     * Retrieves the request uuid. was created when the request was setup.\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function getId\n     * @returns  {String} id\n     */\n    Request.prototype.getId = function getId() {\n        if (this._id !== undefined) {\n            return this._id;\n        }\n\n        this._id = uuid.v4();\n\n        return this._id;\n    };\n\n    /**\n     * Returns the request id. If a `reqId` value is passed in,\n     * this will become the request’s new id. The request id is immutable,\n     * and can only be set once. Attempting to set the request id more than\n     * once will cause restify to throw.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function id\n     * @param {String} reqId - request id\n     * @returns {String} id\n     */\n    Request.prototype.id = function id(reqId) {\n        var self = this;\n\n        if (reqId) {\n            if (self._id) {\n                throw new Error(\n                    'request id is immutable, cannot be set again!'\n                );\n            } else {\n                assert.string(reqId, 'reqId');\n                self._id = reqId;\n                return self._id;\n            }\n        }\n\n        return self.getId();\n    };\n\n    /**\n     * Retrieves the cleaned up url path.\n     * e.g., /foo?a=1  =>  /foo\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function getPath\n     * @returns  {String} path\n     */\n    Request.prototype.getPath = function getPath() {\n        return this.getUrl().pathname;\n    };\n\n    /**\n     * Returns the cleaned up requested URL.\n     * @public\n     * @memberof Request\n     * @instance\n     * @function getPath\n     * @returns  {String}\n     * @example\n     * // incoming request is http://localhost:3000/foo/bar?a=1\n     * server.get('/:x/bar', function(req, res, next) {\n     *     console.warn(req.path());\n     *     // => /foo/bar\n     * });\n     */\n    Request.prototype.path = Request.prototype.getPath;\n\n    /**\n     * Returns the raw query string. Returns empty string\n     * if no query string is found.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function getQuery\n     * @returns  {String} query\n     * @example\n     * // incoming request is /foo?a=1\n     * req.getQuery();\n     * // => 'a=1'\n     * @example\n     * <caption>\n     * If the queryParser plugin is used, the parsed query string is\n     * available under the req.query:\n     * </caption>\n     * // incoming request is /foo?a=1\n     * server.use(restify.plugins.queryParser());\n     * req.query;\n     * // => { a: 1 }\n     */\n    Request.prototype.getQuery = function getQuery() {\n        // always return a string, because this is the raw query string.\n        // if the queryParser plugin is used, req.query will provide an empty\n        // object fallback.\n        return this.getUrl().query || '';\n    };\n\n    /**\n     * Returns the raw query string. Returns empty string\n     * if no query string is found\n     * @private\n     * @memberof Request\n     * @instance\n     * @function query\n     * @returns  {String}\n     */\n    Request.prototype.query = Request.prototype.getQuery;\n\n    /**\n     * The number of ms since epoch of when this request began being processed.\n     * Like date(), but returns a number.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function time\n     * @returns  {Number} time when request began being processed in epoch:\n     *                    ellapsed milliseconds since\n     *                    January 1, 1970, 00:00:00 UTC\n     */\n    Request.prototype.time = function time() {\n        return this._date.getTime();\n    };\n\n    /**\n     * returns a parsed URL object.\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function getUrl\n     * @returns  {Object} url\n     */\n    Request.prototype.getUrl = function getUrl() {\n        if (this._cacheURL !== this.url) {\n            this._url = url.parse(this.url);\n            this._cacheURL = this.url;\n        }\n        return this._url;\n    };\n\n    /**\n     * Returns the accept-version header.\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function getVersion\n     * @returns  {String} version\n     */\n    Request.prototype.getVersion = function getVersion() {\n        if (this._version !== undefined) {\n            return this._version;\n        }\n\n        this._version =\n            this.headers['accept-version'] ||\n            this.headers['x-api-version'] ||\n            '*';\n\n        return this._version;\n    };\n\n    /**\n     * Returns the accept-version header.\n     * @public\n     * @memberof Request\n     * @instance\n     * @function version\n     * @returns  {String}\n     */\n    Request.prototype.version = Request.prototype.getVersion;\n\n    /**\n     * Returns the version of the route that matched.\n     *\n     * @private\n     * @memberof Request\n     * @instance\n     * @function matchedVersion\n     * @returns {String} version\n     */\n    Request.prototype.matchedVersion = function matchedVersion() {\n        if (this._matchedVersion !== undefined) {\n            return this._matchedVersion;\n        } else {\n            return this.version();\n        }\n    };\n\n    /**\n     * Get the case-insensitive request header key,\n     * and optionally provide a default value (express-compliant).\n     * Returns any header off the request. also, 'correct' any\n     * correctly spelled 'referrer' header to the actual spelling used.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function header\n     * @param    {String} key - the key of the header\n     * @param    {String} [defaultValue] - default value if header isn't\n     *                                   found on the req\n     * @returns  {String} header value\n     * @example\n     * req.header('Host');\n     * req.header('HOST');\n     * req.header('Accept', '*\\/*');\n     */\n    Request.prototype.header = function header(key, defaultValue) {\n        assert.string(key, 'key');\n\n        key = key.toLowerCase();\n\n        if (key === 'referer' || key === 'referrer') {\n            key = 'referer';\n        }\n\n        return this.headers[key] || defaultValue;\n    };\n\n    /**\n     * Returns any trailer header off the request. Also, 'correct' any\n     * correctly spelled 'referrer' header to the actual spelling used.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function trailer\n     * @param    {String} name - the name of the header\n     * @param    {String} value - default value if header isn't found on the req\n     * @returns  {String} trailer value\n     */\n    Request.prototype.trailer = function trailer(name, value) {\n        assert.string(name, 'name');\n        name = name.toLowerCase();\n\n        if (name === 'referer' || name === 'referrer') {\n            name = 'referer';\n        }\n\n        return (this.trailers || {})[name] || value;\n    };\n\n    /**\n     * Check if the incoming request contains the `Content-Type` header field,\n     * and if it contains the given mime type.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function is\n     * @param    {String} type - a content-type header value\n     * @returns  {Boolean} is content-type header\n     * @example\n     * // With Content-Type: text/html; charset=utf-8\n     * req.is('html');\n     * req.is('text/html');\n     * // => true\n     *\n     * // When Content-Type is application/json\n     * req.is('json');\n     * req.is('application/json');\n     * // => true\n     *\n     * req.is('html');\n     * // => false\n     */\n    Request.prototype.is = function is(type) {\n        assert.string(type, 'type');\n\n        var contentType = this.getContentType();\n        var matches = true;\n\n        if (!contentType) {\n            return false;\n        }\n\n        if (type.indexOf('/') === -1) {\n            type = mime.getType(type);\n        }\n\n        if (type.indexOf('*') !== -1) {\n            type = type.split('/');\n            contentType = contentType.split('/');\n            matches &= type[0] === '*' || type[0] === contentType[0];\n            matches &= type[1] === '*' || type[1] === contentType[1];\n        } else {\n            matches = contentType === type;\n        }\n\n        return matches;\n    };\n\n    /**\n     * Check if the incoming request is chunked.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function isChunked\n     * @returns  {Boolean} is chunked\n     */\n    Request.prototype.isChunked = function isChunked() {\n        return this.headers['transfer-encoding'] === 'chunked';\n    };\n\n    /**\n     * Check if the incoming request is kept alive.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function isKeepAlive\n     * @returns  {Boolean} is keep alive\n     */\n    Request.prototype.isKeepAlive = function isKeepAlive() {\n        if (this._keepAlive !== undefined) {\n            return this._keepAlive;\n        }\n\n        if (this.headers.connection) {\n            this._keepAlive = /keep-alive/i.test(this.headers.connection);\n        } else {\n            this._keepAlive = this.httpVersion === '1.0' ? false : true;\n        }\n\n        return this._keepAlive;\n    };\n\n    /**\n     * Check if the incoming request is encrypted.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function isSecure\n     * @returns  {Boolean} is secure\n     */\n    Request.prototype.isSecure = function isSecure() {\n        if (this._secure !== undefined) {\n            return this._secure;\n        }\n\n        this._secure = this.connection.encrypted ? true : false;\n        return this._secure;\n    };\n\n    /**\n     * Check if the incoming request has been upgraded.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function isUpgradeRequest\n     * @returns  {Boolean} is upgraded\n     */\n    Request.prototype.isUpgradeRequest = function isUpgradeRequest() {\n        if (this._upgradeRequest !== undefined) {\n            return this._upgradeRequest;\n        } else {\n            return false;\n        }\n    };\n\n    /**\n     * Check if the incoming request is an upload verb.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function isUpload\n     * @returns  {Boolean} is upload\n     */\n    Request.prototype.isUpload = function isUpload() {\n        var m = this.method;\n        return m === 'PATCH' || m === 'POST' || m === 'PUT';\n    };\n\n    /**\n     * toString serialization\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function toString\n     * @returns  {String} serialized request\n     */\n    Request.prototype.toString = function toString() {\n        var headers = '';\n        var self = this;\n        var str;\n\n        Object.keys(this.headers).forEach(function forEach(k) {\n            headers += sprintf('%s: %s\\n', k, self.headers[k]);\n        });\n\n        str = sprintf(\n            '%s %s HTTP/%s\\n%s',\n            this.method,\n            this.url,\n            this.httpVersion,\n            headers\n        );\n\n        return str;\n    };\n\n    /**\n     * Returns the user-agent header.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function userAgent\n     * @returns  {String} user agent\n     */\n    Request.prototype.userAgent = function userAgent() {\n        return this.headers['user-agent'];\n    };\n\n    /**\n     * Start the timer for a request handler.\n     * By default, restify uses calls this automatically for all handlers\n     * registered in your handler chain.\n     * However, this can be called manually for nested functions inside the\n     * handler chain to record timing information.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function startHandlerTimer\n     * @param    {String}    handlerName - The name of the handler.\n     * @returns  {undefined} no return value\n     * @example\n     * <caption>\n     * You must explicitly invoke\n     * endHandlerTimer() after invoking this function. Otherwise timing\n     * information will be inaccurate.\n     * </caption>\n     * server.get('/', function fooHandler(req, res, next) {\n     *     vasync.pipeline({\n     *         funcs: [\n     *             function nestedHandler1(req, res, next) {\n     *                 req.startHandlerTimer('nestedHandler1');\n     *                 // do something\n     *                 req.endHandlerTimer('nestedHandler1');\n     *                 return next();\n     *             },\n     *             function nestedHandler1(req, res, next) {\n     *                 req.startHandlerTimer('nestedHandler2');\n     *                 // do something\n     *                 req.endHandlerTimer('nestedHandler2');\n     *                 return next();\n     *\n     *             }...\n     *        ]...\n     *     }, next);\n     * });\n     */\n    Request.prototype.startHandlerTimer = function startHandlerTimer(\n        handlerName\n    ) {\n        var self = this;\n\n        // For nested handlers, we prepend the top level handler func name\n        var name =\n            self._currentHandler === handlerName\n                ? handlerName\n                : self._currentHandler + '-' + handlerName;\n\n        if (!self._timerMap) {\n            self._timerMap = {};\n        }\n\n        self._timerMap[name] = process.hrtime();\n\n        if (self.dtrace) {\n            dtrace._rstfy_probes['handler-start'].fire(function fire() {\n                return [\n                    self.serverName,\n                    self._currentRoute, // set in server._run\n                    name,\n                    self._dtraceId\n                ];\n            });\n        }\n    };\n\n    /**\n     * End the timer for a request handler.\n     * You must invoke this function if you called `startRequestHandler` on a\n     * handler. Otherwise the time recorded will be incorrect.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function endHandlerTimer\n     * @param    {String}    handlerName - The name of the handler.\n     * @returns  {undefined} no return value\n     */\n    Request.prototype.endHandlerTimer = function endHandlerTimer(handlerName) {\n        var self = this;\n\n        // For nested handlers, we prepend the top level handler func name\n        var name =\n            self._currentHandler === handlerName\n                ? handlerName\n                : self._currentHandler + '-' + handlerName;\n\n        if (!self.timers) {\n            self.timers = [];\n        }\n\n        self._timerMap[name] = process.hrtime(self._timerMap[name]);\n        self.timers.push({\n            name: name,\n            time: self._timerMap[name]\n        });\n\n        if (self.dtrace) {\n            dtrace._rstfy_probes['handler-done'].fire(function fire() {\n                return [\n                    self.serverName,\n                    self._currentRoute, // set in server._run\n                    name,\n                    self._dtraceId\n                ];\n            });\n        }\n    };\n\n    /**\n     * Returns the connection state of the request. Current possible values are:\n     * - `close` - when the request has been closed by the clien\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function connectionState\n     * @returns {String} connection state (`\"close\"`)\n     */\n    Request.prototype.connectionState = function connectionState() {\n        var self = this;\n        return self._connectionState;\n    };\n\n    /**\n     * Returns the route object to which the current request was matched to.\n     *\n     * @public\n     * @memberof Request\n     * @instance\n     * @function getRoute\n     * @returns {Object} route\n     * @example\n     * <caption>Route info object structure:</caption>\n     * {\n     *  path: '/ping/:name',\n     *  method: 'GET',\n     *  versions: [],\n     *  name: 'getpingname'\n     * }\n     */\n    Request.prototype.getRoute = function getRoute() {\n        var self = this;\n        return self.route;\n    };\n}\n\nmodule.exports = patch;\n"
  },
  {
    "path": "lib/response.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar http = require('http');\nvar sprintf = require('util').format;\nvar url = require('url');\n\nvar assert = require('assert-plus');\nvar mime = require('mime');\nvar errors = require('restify-errors');\n\nvar httpDate = require('./http_date');\nvar utils = require('./utils');\n\n///--- Globals\n\nvar InternalServerError = errors.InternalServerError;\n\n/**\n * @private\n * Headers that cannot be multi-values.\n * @see #779, multiple set-cookie values are allowed only as multiple headers.\n * @see #986, multiple content-type values / headers disallowed.\n */\nvar HEADER_ARRAY_BLACKLIST = {\n    'content-type': true\n};\n\n///--- API\n\n/**\n * Patch Response object and extends with extra functionalities\n *\n * @private\n * @function patch\n * @param    {http.ServerResponse|http2.Http2ServerResponse} Response -\n *                                                           Server Response\n * @returns  {undefined} No return value\n */\nfunction patch(Response) {\n    assert.func(Response, 'Response');\n\n    /**\n     * Wraps all of the node\n     * [http.ServerResponse](https://nodejs.org/docs/latest/api/http.html)\n     * APIs, events and properties, plus the following.\n     * @class Response\n     * @extends http.ServerResponse\n     */\n\n    /**\n     * Sets the `cache-control` header.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function cache\n     * @param    {String} [type=\"public\"] - value of the header\n     *                                    (`\"public\"` or `\"private\"`)\n     * @param    {Object} [options] - an options object\n     * @param    {Number} options.maxAge - max-age in seconds\n     * @returns  {String}         the value set to the header\n     */\n    Response.prototype.cache = function cache(type, options) {\n        if (typeof type !== 'string') {\n            options = type;\n            type = 'public';\n        }\n\n        if (options && options.maxAge !== undefined) {\n            assert.number(options.maxAge, 'options.maxAge');\n            type += ', max-age=' + options.maxAge;\n        }\n\n        return this.setHeader('Cache-Control', type);\n    };\n\n    /**\n     * Turns off all cache related headers.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function noCache\n     * @returns  {Response} self, the response object\n     */\n    Response.prototype.noCache = function noCache() {\n        // HTTP 1.1\n        this.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');\n\n        // HTTP 1.0\n        this.setHeader('Pragma', 'no-cache');\n\n        // Proxies\n        this.setHeader('Expires', '0');\n\n        return this;\n    };\n\n    /**\n     * Appends the provided character set to the response's `Content-Type`.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function charSet\n     * @param    {String} type - char-set value\n     * @returns  {Response} self, the response object\n     * @example\n     * res.charSet('utf-8');\n     */\n    Response.prototype.charSet = function charSet(type) {\n        assert.string(type, 'charset');\n\n        this._charSet = type;\n\n        return this;\n    };\n\n    /**\n     * Retrieves a header off the response.\n     *\n     * @private\n     * @memberof Response\n     * @instance\n     * @function get\n     * @param    {Object} name - the header name\n     * @returns  {String} header value\n     */\n    Response.prototype.get = function get(name) {\n        assert.string(name, 'name');\n\n        return this.getHeader(name);\n    };\n\n    // If getHeaders is not provided by the Node platform, monkey patch our own.\n    // This is needed since versions of Node <7 did not come with a getHeaders.\n    // For more see GH-1408\n    if (typeof Response.prototype.getHeaders !== 'function') {\n        /**\n         * Retrieves all headers off the response.\n         *\n         * @private\n         * @memberof Response\n         * @instance\n         * @function getHeaders\n         * @returns  {Object} headers\n         */\n        Response.prototype.getHeaders = function getHeaders() {\n            return this._headers || {};\n        };\n    }\n\n    /**\n     * Sets headers on the response.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function header\n     * @param    {String} key - the name of the header\n     * @param    {String} value - the value of the header\n     * @returns  {Object} the retrieved value or the value that was set\n     * @example\n     * <caption>\n     * If only key is specified, return the value of the header.\n     * If both key and value are specified, set the response header.\n     * </caption>\n     * res.header('Content-Length');\n     * // => undefined\n     *\n     * res.header('Content-Length', 123);\n     * // => 123\n     *\n     * res.header('Content-Length');\n     * // => 123\n     *\n     * res.header('foo', new Date());\n     * // => Fri, 03 Feb 2012 20:09:58 GMT\n     * @example\n     * <caption>\n     * `header()` can also be used to automatically chain header values\n     * when applicable:\n     * </caption>\n     * res.header('x-foo', 'a');\n     * res.header('x-foo', 'b');\n     * // => { 'x-foo': ['a', 'b'] }\n     * @example\n     * <caption>\n     * Note that certain headers like `content-type`\n     * do not support multiple values, so calling `header()`\n     * twice for those headers will\n     * overwrite the existing value.\n     * </caption>\n     */\n    Response.prototype.header = function header(key, value) {\n        assert.string(key, 'name');\n\n        if (value === undefined) {\n            return this.getHeader(key);\n        }\n\n        if (value instanceof Date) {\n            value = httpDate(value);\n        } else if (arguments.length > 2) {\n            // Support res.header('foo', 'bar %s', 'baz');\n            var arg = Array.prototype.slice.call(arguments).slice(2);\n            value = sprintf(value, arg);\n        }\n\n        var current = this.getHeader(key);\n\n        // Check the header blacklist before changing a header to an array\n        var keyLc = key.toLowerCase();\n\n        if (current && !(keyLc in HEADER_ARRAY_BLACKLIST)) {\n            if (Array.isArray(current)) {\n                current.push(value);\n                value = current;\n            } else {\n                value = [current, value];\n            }\n        }\n\n        this.setHeader(key, value);\n        return value;\n    };\n\n    /**\n     * Syntatic sugar for:\n     * ```js\n     * res.contentType = 'json';\n     * res.send({hello: 'world'});\n     * ```\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function json\n     * @param    {Number} [code] -    http status code\n     * @param    {Object} [body] -    value to json.stringify\n     * @param    {Object} [headers] - headers to set on the response\n     * @returns  {Object} the response object\n     * @example\n     * res.header('content-type', 'json');\n     * res.send({hello: 'world'});\n     */\n    Response.prototype.json = function json(code, body, headers) {\n        this.setHeader('Content-Type', 'application/json');\n        return this.send(code, body, headers);\n    };\n\n    /**\n     * Sets the link header.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function link\n     * @param    {String} key -  the link key\n     * @param    {String} value - the link value\n     * @returns  {String}     the header value set to res\n     */\n    Response.prototype.link = function link(key, value) {\n        assert.string(key, 'key');\n        assert.string(value, 'value');\n\n        var _link = sprintf('<%s>; rel=\"%s\"', key, value);\n        return this.header('Link', _link);\n    };\n\n    /**\n     * Sends the response object. pass through to internal `__send` that uses a\n     * formatter based on the `content-type` header.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function send\n     * @param    {Number} [code] - http status code\n     * @param    {Object | Buffer | Error} [body] - the content to send\n     * @param    {Object} [headers] - any add'l headers to set\n     * @returns  {Object} the response object\n     * @example\n     * <caption>\n     * You can use send() to wrap up all the usual writeHead(), write(), end()\n     * calls on the HTTP API of node.\n     * You can pass send either a `code` and `body`, or just a body. body can be\n     * an `Object`, a `Buffer`, or an `Error`.\n     * When you call `send()`, restify figures out how to format the response\n     * based on the `content-type`.\n     * </caption>\n     * res.send({hello: 'world'});\n     * res.send(201, {hello: 'world'});\n     * res.send(new BadRequestError('meh'));\n     */\n    Response.prototype.send = function send(code, body, headers) {\n        var self = this;\n        var sendArgs;\n\n        if (typeof code === 'number') {\n            sendArgs = {\n                code: code,\n                body: body,\n                headers: headers\n            };\n        } else {\n            sendArgs = {\n                body: code,\n                headers: body\n            };\n        }\n\n        sendArgs.format = true;\n        return self.__send(sendArgs);\n    };\n\n    /**\n     * Like `res.send()`, but skips formatting. This can be useful when the\n     * payload has already been preformatted.\n     * Sends the response object. pass through to internal `__send` that skips\n     * formatters entirely and sends the content as is.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function sendRaw\n     * @param    {Number} [code] - http status code\n     * @param    {string | Buffer} [body] - the content to send\n     * @param    {Object} [headers] - any add'l headers to set\n     * @returns  {Object} the response object\n     */\n    Response.prototype.sendRaw = function sendRaw(code, body, headers) {\n        var self = this;\n        var sendArgs;\n\n        if (typeof code === 'number') {\n            sendArgs = {\n                code: code,\n                body: body,\n                headers: headers\n            };\n        } else {\n            sendArgs = {\n                body: code,\n                headers: body\n            };\n        }\n\n        assert.ok(\n            typeof sendArgs.body === 'string' || Buffer.isBuffer(sendArgs.body),\n            'res.sendRaw() accepts only strings or buffers'\n        );\n\n        sendArgs.format = false;\n        return self.__send(sendArgs);\n    };\n\n    // eslint-disable-next-line jsdoc/check-param-names\n    /**\n     * Internal implementation of send. convenience method that handles:\n     * writeHead(), write(), end().\n     *\n     * Both body and headers are optional, but you MUST provide body if you are\n     * providing headers.\n     *\n     * @private\n     * @param {Object} opts - an option sobject\n     * @param {Object | Buffer | String | Error} opts.body - the content to send\n     * @param {Boolean} opts.format - When false, skip formatting\n     * @param {Number} [opts.code] - http status code\n     * @param {Object} [opts.headers] - any add'l headers to set\n     * @returns {Object} - returns the response object\n     */\n    Response.prototype.__send = function __send(opts) {\n        var self = this;\n        var isHead = self.req.method === 'HEAD';\n        var log = self.log;\n        var code = opts.code;\n        var body = opts.body;\n        var headers = opts.headers || {};\n\n        self._sent = true;\n\n        // Now lets try to derive values for optional arguments that we were not\n        // provided, otherwise we choose sane defaults.\n\n        // If the body is an error object and we were not given a status code,\n        // try to derive it from the error object, otherwise default to 500\n        if (!code && body instanceof Error) {\n            code = body.statusCode || 500;\n        }\n\n        // Set sane defaults for optional arguments if they were not provided\n        // and we failed to derive their values\n        code = code || self.statusCode || 200;\n\n        // Populate our response object with the derived arguments\n        self.statusCode = code;\n        self._body = body;\n        Object.keys(headers).forEach(function forEach(k) {\n            self.setHeader(k, headers[k]);\n        });\n\n        // If log level is set to trace, output our constructed response object\n        if (log.trace()) {\n            var _props = {\n                code: self.statusCode,\n                headers: self._headers\n            };\n\n            if (body instanceof Error) {\n                _props.err = self._body;\n            } else {\n                _props.body = self._body;\n            }\n            log.trace(_props, 'response::send entered');\n        }\n\n        // 204 = No Content and 304 = Not Modified, we don't want to send the\n        // body in these cases. HEAD never provides a body.\n        if (isHead || code === 204 || code === 304) {\n            return flush(self);\n        }\n\n        if (opts.format === true) {\n            // if no body, then no need to format. if this was an error caught\n            // by a domain, don't send the domain error either.\n            if (body === undefined || (body instanceof Error && body.domain)) {\n                return flush(self);\n            }\n\n            // At this point we know we have a body that needs to be formatted,\n            // so lets derive the formatter based on the response object's\n            // properties\n\n            var formatter;\n            var type = self.contentType || self.getHeader('Content-Type');\n\n            // Set Content-Type to application/json when\n            // res.send is called with an Object instead of calling res.json\n            if (!type && typeof body === 'object' && !Buffer.isBuffer(body)) {\n                type = 'application/json';\n            }\n\n            // Derive type if not provided by the user\n            type = type || self.req.accepts(self.acceptable);\n\n            // Check to see if we could find a content type to use for the\n            // response.\n            if (!type) {\n                return formatterError(\n                    self,\n                    new errors.NotAcceptableError({\n                        message:\n                            'could not find suitable content-type to use ' +\n                            'for the response'\n                    })\n                );\n            }\n\n            type = type.split(';')[0];\n\n            if (!self.formatters[type] && type.indexOf('/') === -1) {\n                type = mime.getType(type);\n            }\n\n            // If finding a formatter matching the negotiated content-type is\n            // required, and we were unable to derive a valid type, default to\n            // treating it as arbitrary binary data per RFC 2046 Section 4.5.1\n            if (\n                this._strictFormatters &&\n                !self.formatters[type] &&\n                self.acceptable.indexOf(type) === -1\n            ) {\n                type = 'application/octet-stream';\n            }\n\n            formatter = self.formatters[type] || self.formatters['*/*'];\n\n            // If after the above attempts we were still unable to derive a\n            // formatter, provide a meaningful error message\n            if (this._strictFormatters && !formatter) {\n                return formatterError(\n                    self,\n                    new errors.InternalServerError({\n                        message:\n                            'could not find formatter for response ' +\n                            'content-type \"' +\n                            type +\n                            '\"'\n                    })\n                );\n            }\n\n            var formatterType = type;\n\n            if (self._charSet) {\n                type = type + '; charset=' + self._charSet;\n            }\n\n            // Update Content-Type header to the one originally set or to the\n            // type inferred from the most relevant formatter found.\n            self.setHeader('Content-Type', type);\n\n            if (formatter) {\n                // Finally, invoke the formatter and flush the request with it's\n                // results\n\n                var formattedBody;\n                try {\n                    formattedBody = formatter(self.req, self, body);\n                } catch (e) {\n                    if (\n                        e instanceof errors.RestError ||\n                        e instanceof errors.HttpError\n                    ) {\n                        var res = formatterError(\n                            self,\n                            e,\n                            'error in formatter (' +\n                                formatterType +\n                                ') formatting response body'\n                        );\n                        return res;\n                    }\n                    throw e;\n                }\n                return flush(self, formattedBody);\n            }\n        }\n\n        return flush(self, body);\n    };\n\n    /**\n     * Sets multiple header(s) on the response.\n     * Uses `header()` underneath the hood, enabling multi-value headers.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function set\n     * @param    {String|Object} name - name of the header or\n     *                                `Object` of headers\n     * @param    {String} val - value of the header\n     * @returns  {Object}      self, the response object\n     * @example\n     * res.header('x-foo', 'a');\n     * res.set({\n     *     'x-foo', 'b',\n     *     'content-type': 'application/json'\n     * });\n     * // =>\n     * // {\n     * //    'x-foo': [ 'a', 'b' ],\n     * //    'content-type': 'application/json'\n     * // }\n     */\n    Response.prototype.set = function set(name, val) {\n        var self = this;\n\n        if (arguments.length === 2) {\n            assert.string(\n                name,\n                'res.set(name, val) requires name to be a string'\n            );\n            this.header(name, val);\n        } else {\n            assert.object(\n                name,\n                'res.set(headers) requires headers to be an object'\n            );\n            Object.keys(name).forEach(function forEach(k) {\n                self.set(k, name[k]);\n            });\n        }\n\n        return this;\n    };\n\n    /**\n     * Sets the http status code on the response.\n     *\n     * @public\n     * @memberof Response\n     * @instance\n     * @function status\n     * @param    {Number} code - http status code\n     * @returns  {Number}        the status code passed in\n     * @example\n     * res.status(201);\n     */\n    Response.prototype.status = function status(code) {\n        assert.number(code, 'code');\n\n        this.statusCode = code;\n        return code;\n    };\n\n    /**\n     * toString() serialization.\n     *\n     * @private\n     * @memberof Response\n     * @instance\n     * @function toString\n     * @returns  {String} stringified response\n     */\n    Response.prototype.toString = function toString() {\n        var headers = this.getHeaders();\n        var headerString = '';\n        var str;\n\n        Object.keys(headers).forEach(function forEach(k) {\n            headerString += k + ': ' + headers[k] + '\\n';\n        });\n        str = sprintf(\n            'HTTP/1.1 %s %s\\n%s',\n            this.statusCode,\n            http.STATUS_CODES[this.statusCode],\n            headerString\n        );\n\n        return str;\n    };\n\n    if (!Response.prototype.hasOwnProperty('_writeHead')) {\n        Response.prototype._writeHead = Response.prototype.writeHead;\n    }\n\n    /**\n     * Pass through to native response.writeHead()\n     *\n     * @private\n     * @memberof Response\n     * @instance\n     * @function writeHead\n     * @fires    header\n     * @returns  {undefined} no return value\n     */\n    Response.prototype.writeHead = function restifyWriteHead() {\n        this.emit('header');\n\n        if (this.statusCode === 204 || this.statusCode === 304) {\n            this.removeHeader('Content-Length');\n            this.removeHeader('Content-MD5');\n            this.removeHeader('Content-Type');\n            this.removeHeader('Content-Encoding');\n        }\n\n        this._writeHead.apply(this, arguments);\n    };\n\n    /**\n     * Redirect is sugar method for redirecting.\n     * @public\n     * @memberof Response\n     * @instance\n     * @param {Object} options url or an options object to configure a redirect\n     * @param {Boolean} [options.secure] whether to redirect to http or https\n     * @param {String} [options.hostname] redirect location's hostname\n     * @param {String} [options.pathname] redirect location's pathname\n     * @param {String} [options.port] redirect location's port number\n     * @param {String} [options.query] redirect location's query string\n     *                                 parameters\n     * @param {Boolean} [options.overrideQuery] if true, `options.query`\n     *                                          stomps over any existing query\n     *                                          parameters on current URL.\n     *                                          by default, will merge the two.\n     * @param {Boolean} [options.permanent] if true, sets 301. defaults to 302.\n     * @param {Function} next mandatory, to complete the response and trigger\n     *                        audit logger.\n     * @fires    redirect\n     * @function redirect\n     * @returns  {undefined}\n     * @example\n     * res.redirect({...}, next);\n     * @example\n     * <caption>\n     * A convenience method for 301/302 redirects. Using this method will tell\n     * restify to stop execution of your handler chain.\n     * You can also use an options object. `next` is required.\n     * </caption>\n     * res.redirect({\n     *   hostname: 'www.foo.com',\n     *   pathname: '/bar',\n     *   port: 80,                 // defaults to 80\n     *   secure: true,             // sets https\n     *   permanent: true,\n     *   query: {\n     *     a: 1\n     *   }\n     * }, next);  // => redirects to 301 https://www.foo.com/bar?a=1\n     */\n\n    /**\n     * Redirect with code and url.\n     * @memberof Response\n     * @instance\n     * @param {Number} code http redirect status code\n     * @param {String} url redirect url\n     * @param {Function} next mandatory, to complete the response and trigger\n     *                        audit logger.\n     * @fires    redirect\n     * @function redirect\n     * @returns  {undefined}\n     * @example\n     * res.redirect(301, 'www.foo.com', next);\n     */\n\n    /**\n     * Redirect with url.\n     * @public\n     * @memberof Response\n     * @instance\n     * @param {String} url redirect url\n     * @param {Function} next mandatory, to complete the response and trigger\n     *                        audit logger.\n     * @fires    redirect\n     * @function redirect\n     * @returns  {undefined}\n     * @example\n     * res.redirect('www.foo.com', next);\n     * res.redirect('/foo', next);\n     */\n    Response.prototype.redirect = redirect;\n\n    /**\n     * @private\n     * @param {*} arg1 - arg1\n     * @param {*} arg2 - arg2\n     * @param {*} arg3 - arg3\n     * @fires    redirect\n     * @function redirect\n     * @returns  {undefined} no return value\n     */\n    function redirect(arg1, arg2, arg3) {\n        var self = this;\n        var statusCode = 302;\n        var finalUri;\n        var redirectLocation;\n        var next;\n\n        // 1) this is signature 1, where an explicit status code is passed in.\n        //    MUST guard against null here, passing null is likely indicative\n        //    of an attempt to call res.redirect(null, next);\n        //    as a way to do a reload of the current page.\n        if (arg1 && !isNaN(arg1)) {\n            statusCode = arg1;\n            finalUri = arg2;\n            next = arg3;\n        } else if (typeof arg1 === 'string') {\n            // 2) this is signaure number 2\n            // otherwise, it's a string, and use it directly\n            finalUri = arg1;\n            next = arg2;\n        } else if (typeof arg1 === 'object') {\n            // 3) signature number 3, using an options object.\n            // set next, then go to work.\n            next = arg2;\n\n            var req = self.req;\n            var opt = arg1 || {};\n            var currentFullPath = req.href();\n            var secure = opt.hasOwnProperty('secure')\n                ? opt.secure\n                : req.isSecure();\n\n            // if hostname is passed in, use that as the base,\n            // otherwise fall back on current url.\n            var parsedUri = url.parse(opt.hostname || currentFullPath, true);\n\n            // create the object we'll use to format for the final uri.\n            // this object will eventually get passed to url.format().\n            // can't use parsedUri to seed it, as it confuses the url module\n            // with some existing parsed state. instead, we'll pick the things\n            // we want and use that as a starting point.\n            finalUri = {\n                port: parsedUri.port,\n                hostname: parsedUri.hostname,\n                query: parsedUri.query,\n                pathname: parsedUri.pathname\n            };\n\n            // start building url based on options.\n            // start with the host\n            if (opt.hostname) {\n                finalUri.hostname = opt.hostname;\n            }\n\n            // then set protocol IFF hostname is set - otherwise we end up with\n            // malformed URL.\n            if (finalUri.hostname) {\n                finalUri.protocol = secure === true ? 'https' : 'http';\n            }\n\n            // then set current path after the host\n            if (opt.pathname) {\n                finalUri.pathname = opt.pathname;\n            }\n\n            // then set port\n            if (opt.port) {\n                finalUri.port = opt.port;\n            }\n\n            // then add query params\n            if (opt.query) {\n                if (opt.overrideQuery === true) {\n                    finalUri.query = opt.query;\n                } else {\n                    finalUri.query = utils.mergeQs(opt.query, finalUri.query);\n                }\n            }\n\n            // change status code to 301 permanent if specified\n            if (opt.permanent) {\n                statusCode = 301;\n            }\n        }\n\n        // if we're missing a next we should probably throw. if user wanted\n        // to redirect but we were unable to do so, we should not continue\n        // down the handler stack.\n        assert.func(next, 'res.redirect() requires a next param');\n\n        // if we are missing a finalized uri\n        // by this point, pass an error to next.\n        if (!finalUri) {\n            return next(new InternalServerError('could not construct url'));\n        }\n\n        redirectLocation = url.format(finalUri);\n\n        self.emit('redirect', redirectLocation);\n\n        // now we're done constructing url, send the res\n        self.send(statusCode, null, {\n            Location: redirectLocation\n        });\n\n        // tell server to stop processing the handler stack.\n        return next(false);\n    }\n}\n\n/**\n * Flush takes our constructed response object and sends it to the client\n *\n * @private\n * @function flush\n * @param {Response} res - response\n * @param {String|Buffer} body - response body\n * @returns {Response} response\n */\nfunction flush(res, body) {\n    assert.ok(\n        body === null ||\n            body === undefined ||\n            typeof body === 'string' ||\n            Buffer.isBuffer(body),\n        'body must be a string or a Buffer instance'\n    );\n\n    res._data = body;\n\n    // Flush headers\n    res.writeHead(res.statusCode);\n\n    // Send body if it was provided\n    if (res._data) {\n        res.write(res._data);\n    }\n\n    // Finish request\n    res.end();\n\n    // If log level is set to trace, log the entire response object\n    if (res.log.trace()) {\n        res.log.trace({ res: res }, 'response sent');\n    }\n\n    // Return the response object back out to the caller of __send\n    return res;\n}\n\n/**\n * formatterError is used to handle any case where we were unable to\n * properly format the provided body\n *\n * @private\n * @function formatterError\n * @param {Response} res - response\n * @param {Error} err - error\n * @param {String} [msg] - custom log message\n * @returns {Response} response\n */\nfunction formatterError(res, err, msg) {\n    // If the user provided a non-success error code, we don't want to\n    // mess with it since their error is probably more important than\n    // our inability to format their message.\n    if (res.statusCode >= 200 && res.statusCode < 300) {\n        res.statusCode = err.statusCode;\n    }\n\n    if (typeof msg !== 'string') {\n        msg = 'error retrieving formatter';\n    }\n\n    res.log.warn(\n        {\n            req: res.req,\n            err: err\n        },\n        msg\n    );\n\n    return flush(res);\n}\n\nmodule.exports = patch;\n"
  },
  {
    "path": "lib/router.js",
    "content": "'use strict';\n\nvar EventEmitter = require('events').EventEmitter;\nvar util = require('util');\nvar http = require('http');\n\nvar _ = require('lodash');\nvar assert = require('assert-plus');\nvar errors = require('restify-errors');\nvar uuid = require('uuid');\n\nvar Chain = require('./chain');\nvar RouterRegistryRadix = require('./routerRegistryRadix');\n\n///--- Globals\n\nvar MethodNotAllowedError = errors.MethodNotAllowedError;\nvar ResourceNotFoundError = errors.ResourceNotFoundError;\n\n///--- API\n\n/**\n * Router class handles mapping of http verbs and a regexp path,\n * to an array of handler functions.\n *\n * @class\n * @public\n * @param  {Object} options - an options object\n * @param  {Bunyan} options.log - Bunyan logger instance\n * @param {Boolean} [options.onceNext=false] - Prevents calling next multiple\n *  times\n * @param {Boolean} [options.strictNext=false] - Throws error when next() is\n *  called more than once, enabled onceNext option\n * @param {Object} [options.registry] - route registry\n * @param {Boolean} [options.ignoreTrailingSlash=false] - ignore trailing slash\n * on paths\n */\nfunction Router(options) {\n    assert.object(options, 'options');\n    assert.object(options.log, 'options.log');\n    assert.optionalBool(options.onceNext, 'options.onceNext');\n    assert.optionalBool(options.strictNext, 'options.strictNext');\n    assert.optionalBool(\n        options.ignoreTrailingSlash,\n        'options.ignoreTrailingSlash'\n    );\n\n    EventEmitter.call(this);\n\n    this.log = options.log;\n    this.onceNext = !!options.onceNext;\n    this.strictNext = !!options.strictNext;\n    this.name = 'RestifyRouter';\n\n    // Internals\n    this._anonymousHandlerCounter = 0;\n    this._registry = options.registry || new RouterRegistryRadix(options);\n}\nutil.inherits(Router, EventEmitter);\n\n/**\n * Lookup for route\n *\n * @public\n * @memberof Router\n * @instance\n * @function lookup\n * @param  {Request} req - request\n * @param  {Response} res - response\n * @returns {Chain|undefined} handler or undefined\n */\nRouter.prototype.lookup = function lookup(req, res) {\n    var pathname = req.getUrl().pathname;\n\n    // Find route\n    var registryRoute = this._registry.lookup(req.method, pathname);\n\n    // Not found\n    if (!registryRoute) {\n        return undefined;\n    }\n\n    // Decorate req\n    req.params = Object.assign(req.params, registryRoute.params);\n    req.route = registryRoute.route;\n\n    // Call handler chain\n    return registryRoute.handler;\n};\n\n/**\n * Lookup by name\n *\n * @public\n * @memberof Router\n * @instance\n * @function lookupByName\n * @param {String} name - route name\n * @param  {Request} req - request\n * @param  {Response} res - response\n * @returns {Chain|undefined} handler or undefined\n */\nRouter.prototype.lookupByName = function lookupByName(name, req, res) {\n    var self = this;\n    var route = self._registry.get()[name];\n\n    if (!route) {\n        return undefined;\n    }\n\n    // Decorate req\n    req.route = route;\n\n    return route.chain.run.bind(route.chain);\n};\n\n/**\n * Takes an object of route params and query params, and 'renders' a URL.\n *\n * @public\n * @function render\n * @param    {String} routeName - the route name\n * @param    {Object} params -    an object of route params\n * @param    {Object} query -     an object of query params\n * @returns  {String} URL\n * @example\n * server.get({\n *      name: 'cities',\n *      path: '/countries/:name/states/:state/cities'\n * }, (req, res, next) => ...));\n * let cities = server.router.render('cities', {\n *      name: 'Australia',\n *      state: 'New South Wales'\n * });\n * // cities:  '/countries/Australia/states/New%20South%20Wales/cities'\n */\nRouter.prototype.render = function render(routeName, params, query) {\n    var self = this;\n\n    function pathItem(match, key) {\n        if (params.hasOwnProperty(key) === false) {\n            throw new Error(\n                'Route <' + routeName + '> is missing parameter <' + key + '>'\n            );\n        }\n        return '/' + encodeURIComponent(params[key]);\n    }\n\n    function queryItem(key) {\n        return encodeURIComponent(key) + '=' + encodeURIComponent(query[key]);\n    }\n\n    var route = self._registry.get()[routeName];\n\n    if (!route) {\n        return null;\n    }\n\n    var _path = route.spec.path;\n    var _url = _path.replace(/\\/:([A-Za-z0-9_]+)(\\([^\\\\]+?\\))?/g, pathItem);\n    var items = Object.keys(query || {}).map(queryItem);\n    var queryString = items.length > 0 ? '?' + items.join('&') : '';\n    return _url + queryString;\n};\n\n/**\n * Adds a route.\n *\n * @public\n * @memberof Router\n * @instance\n * @function mount\n * @param    {Object} opts - an options object\n * @param    {String} opts.name - name\n * @param    {String} opts.method - method\n * @param    {String} opts.path - path can be any String\n * @param    {Function[]} handlers - handlers\n * @returns  {String} returns the route name if creation is successful.\n * @fires ...String#mount\n */\nRouter.prototype.mount = function mount(opts, handlers) {\n    var self = this;\n\n    assert.object(opts, 'opts');\n    assert.string(opts.method, 'opts.method');\n    assert.arrayOfFunc(handlers, 'handlers');\n    assert.optionalString(opts.name, 'opts.name');\n\n    var chain = new Chain({\n        onceNext: self.onceNext,\n        strictNext: self.strictNext\n    });\n\n    // Route\n    var route = {\n        name: self._getRouteName(opts.name, opts.method, opts.path),\n        method: opts.method,\n        path: opts.path,\n        spec: opts,\n        chain: chain\n    };\n\n    handlers.forEach(function forEach(handler) {\n        // Assign name to anonymous functions\n        handler._name =\n            handler.name || 'handler-' + self._anonymousHandlerCounter++;\n\n        handler._identifier = `${handler._name} on ${opts.method} ${opts.path}`;\n\n        // Attach to middleware chain\n        chain.add(handler);\n    });\n\n    self._registry.add(route);\n    self.emit('mount', route.method, route.path);\n\n    return route;\n};\n\n/**\n * Unmounts a route.\n *\n * @public\n * @memberof Router\n * @instance\n * @function unmount\n * @param    {String} name - the route name\n * @returns  {Object|undefined} removed route if found\n */\nRouter.prototype.unmount = function unmount(name) {\n    assert.string(name, 'name');\n\n    var route = this._registry.remove(name);\n    return route;\n};\n\n/**\n * toString() serialization.\n *\n * @public\n * @memberof Router\n * @instance\n * @function toString\n * @returns  {String} stringified router\n */\nRouter.prototype.toString = function toString() {\n    return this._registry.toString();\n};\n\n/**\n * Return information about the routes registered in the router.\n *\n * @public\n * @memberof Router\n * @instance\n * @returns {object} The routes in the router.\n */\nRouter.prototype.getDebugInfo = function getDebugInfo() {\n    var routes = this._registry.get();\n    return _.mapValues(routes, function mapValues(route, routeName) {\n        return {\n            name: route.name,\n            method: route.method.toLowerCase(),\n            path: route.path,\n            handlers: route.chain.getHandlers()\n        };\n    });\n};\n\n/**\n * Return mounted routes\n *\n * @public\n * @memberof Router\n * @instance\n * @returns {object} The routes in the router.\n */\nRouter.prototype.getRoutes = function getRoutes() {\n    return this._registry.get();\n};\n\n/**\n * Returns true if the router generated a 404 for an options request.\n *\n * TODO: this is relevant for CORS only. Should move this out eventually to a\n * userland middleware? This also seems a little like overreach, as there is no\n * option to opt out of this behavior today.\n *\n * @private\n * @static\n * @function _optionsError\n * @param    {Object}     req - the request object\n * @param    {Object}     res - the response object\n * @returns  {Boolean} is options error\n */\nRouter._optionsError = function _optionsError(req, res) {\n    var pathname = req.getUrl().pathname;\n    return req.method === 'OPTIONS' && pathname === '*';\n};\n\n/**\n * Default route, when no route found\n * Responds with a ResourceNotFoundError error.\n *\n * @private\n * @memberof Router\n * @instance\n * @function defaultRoute\n * @param  {Request} req - request\n * @param  {Response} res - response\n * @param  {Function} next - next\n * @returns {undefined} no return value\n */\nRouter.prototype.defaultRoute = function defaultRoute(req, res, next) {\n    var self = this;\n    var pathname = req.getUrl().pathname;\n\n    // Allow CORS\n    if (Router._optionsError(req, res, pathname)) {\n        res.send(200);\n        next(null, req, res);\n        return;\n    }\n\n    // Check for 405 instead of 404\n    var allowedMethods = http.METHODS.filter(function some(method) {\n        return method !== req.method && self._registry.lookup(method, pathname);\n    });\n\n    if (allowedMethods.length) {\n        res.methods = allowedMethods;\n        res.setHeader('Allow', allowedMethods.join(', '));\n        var methodErr = new MethodNotAllowedError(\n            '%s is not allowed',\n            req.method\n        );\n        next(methodErr, req, res);\n        return;\n    }\n\n    // clean up the url in case of potential xss\n    // https://github.com/restify/node-restify/issues/1018\n    var err = new ResourceNotFoundError('%s does not exist', pathname);\n    next(err, req, res);\n};\n\n/**\n * Generate route name\n *\n * @private\n * @memberof Router\n * @instance\n * @function _getRouteName\n * @param    {String|undefined} name - Name of the route\n * @param    {String} method - HTTP method\n * @param    {String} path - path\n * @returns  {String} name of the route\n */\nRouter.prototype._getRouteName = function _getRouteName(name, method, path) {\n    // Generate name\n    if (!name) {\n        name = method + '-' + path;\n        name = name.replace(/\\W/g, '').toLowerCase();\n    }\n\n    // Avoid name conflict: GH-401\n    if (this._registry.get()[name]) {\n        name += uuid.v4().substr(0, 7);\n    }\n\n    return name;\n};\n\nmodule.exports = Router;\n"
  },
  {
    "path": "lib/routerRegistryRadix.js",
    "content": "'use strict';\n\nvar assert = require('assert-plus');\nvar FindMyWay = require('find-my-way');\nvar Chain = require('./chain');\n\n/**\n * Radix tree based router registry backed by `find-my-way`\n *\n * @class RouterRegistryRadix\n * @public\n * @param  {Object} options - an options object\n * @param {Object} [options.ignoreTrailingSlash] - ignore trailing slash on\n *  paths\n */\nfunction RouterRegistryRadix(options) {\n    this._findMyWay = new FindMyWay(options);\n    this._routes = {};\n}\n\n/**\n * Adds a route.\n *\n * @public\n * @memberof Router\n * @instance\n * @function add\n * @param    {Object} route - an route object\n * @param    {String} route.name - name of the route\n * @param    {String} route.method - HTTP method\n * @param    {String} route.path - any String accepted by\n * [find-my-way](https://github.com/delvedor/find-my-way)\n * @param    {Chain} route.chain - Chain instance\n * @returns  {Boolean} true\n */\nRouterRegistryRadix.prototype.add = function add(route) {\n    assert.object(route, 'route');\n    assert.string(route.method, 'route.method');\n    assert.string(route.path, 'path');\n    assert.ok(route.chain instanceof Chain, 'route.chain');\n\n    this._findMyWay.on(\n        route.method,\n        route.path,\n        function onRoute(req, res, next) {\n            route.chain.run(req, res, next);\n        },\n        {\n            route: route\n        }\n    );\n\n    this._routes[route.name] = route;\n\n    return route;\n};\n\n/**\n * Removes a route.\n *\n * @public\n * @memberof RouterRegistryRadix\n * @instance\n * @function remove\n * @param    {String} name - the route name\n * @returns  {Object|undefined} removed route if found\n */\nRouterRegistryRadix.prototype.remove = function remove(name) {\n    assert.string(name, 'name');\n\n    // check for route\n    var route = this._routes[name];\n    if (!route) {\n        return undefined;\n    }\n\n    // remove from registry\n    this._findMyWay.off(route.method, route.path);\n    delete this._routes[name];\n\n    return route;\n};\n\n/**\n * Registry for route\n *\n * @public\n * @memberof RouterRegistryRadix\n * @instance\n * @function Registry\n * @param  {String} method - method\n * @param  {String} pathname - pathname\n * @returns {Chain|undefined} handler or undefined\n */\nRouterRegistryRadix.prototype.lookup = function lookup(method, pathname) {\n    assert.string(method, 'method');\n    assert.string(pathname, 'pathname');\n\n    var fmwRoute = this._findMyWay.find(method, pathname);\n\n    // Not found\n    if (!fmwRoute) {\n        return undefined;\n    }\n\n    // Call handler chain\n    return {\n        route: fmwRoute.store.route,\n        params: fmwRoute.params,\n        handler: fmwRoute.handler\n    };\n};\n\n/**\n * Get registry\n *\n * @public\n * @memberof RouterRegistryRadix\n * @instance\n * @function toString\n * @returns  {String} stringified RouterRegistryRadix\n */\nRouterRegistryRadix.prototype.get = function get() {\n    return this._routes;\n};\n\n/**\n * toString() serialization.\n *\n * @public\n * @memberof RouterRegistryRadix\n * @instance\n * @function toString\n * @returns  {String} stringified RouterRegistryRadix\n */\nRouterRegistryRadix.prototype.toString = function toString() {\n    return this._findMyWay.prettyPrint();\n};\n\nmodule.exports = RouterRegistryRadix;\n"
  },
  {
    "path": "lib/server.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\nvar EventEmitter = require('events').EventEmitter;\nvar http = require('http');\nvar https = require('https');\nvar util = require('util');\n\nvar _ = require('lodash');\nvar assert = require('assert-plus');\nvar errors = require('restify-errors');\nvar mime = require('mime');\nvar vasync = require('vasync');\n\nvar Chain = require('./chain');\nvar dtrace = require('./dtrace');\nvar formatters = require('./formatters');\nvar shallowCopy = require('./utils').shallowCopy;\nvar upgrade = require('./upgrade');\nvar deprecationWarnings = require('./deprecationWarnings');\nvar customErrorTypes = require('./errorTypes');\n\n// Ensure these are loaded\nvar patchRequest = require('./request');\nvar patchResponse = require('./response');\n\nvar domain;\nvar http2;\nvar spdy;\n\npatchResponse(http.ServerResponse);\npatchRequest(http.IncomingMessage);\n\n///--- Globals\n\nvar sprintf = util.format;\n\n// Run in domain to catch async errors\n// It has significant negative performance impact\n// Warning: this feature depends on the deprecated domains module\nfunction handleWithDomain(req, res, onError, next) {\n    // In Node v12.x requiring the domain module has a negative performance\n    // impact. As using domains in restify is optional and only required\n    // with the `handleUncaughtExceptions` options, we apply a singleton\n    // pattern to avoid any performance regression in the default scenario.\n    if (!domain) {\n        domain = require('domain');\n    }\n\n    var handlerDomain = domain.create();\n    handlerDomain.add(req);\n    handlerDomain.add(res);\n    handlerDomain.on('error', onError);\n    handlerDomain.run(function run() {\n        next(req, res);\n    });\n}\n\n///--- API\n\n/**\n * Creates a new Server.\n *\n * @public\n * @class\n * @param {Object} options  - an options object\n * @param {String} options.name - Name of the server.\n * @param {Boolean} [options.dtrace=false] - enable DTrace support\n * @param {Router} options.router - Router\n * @param {Object} options.log - [pino](https://github.com/pinojs/pino)\n * instance.\n * @param {String} [options.url] - Once listen() is called, this will be filled\n * in with where the server is running.\n * @param {String|Buffer} [options.certificate] - If you want to create an HTTPS\n * server, pass in a PEM-encoded certificate and key.\n * @param {String|Buffer} [options.key] - If you want to create an HTTPS server,\n * pass in a PEM-encoded certificate and key.\n * @param {Object} [options.formatters] - Custom response formatters for\n * `res.send()`.\n * @param {Boolean|Function} [options.handleUncaughtExceptions=false] - When\n * true restify will use a domain to catch and respond to any uncaught\n * exceptions that occur in its handler stack. Comes with significant negative\n * performance impact.\n * Can also receive a function with signature (req, res, onError, next),\n * allowing for domains alternatives. onError should be called by the custom\n * error handler, and next must be called at the end of this function. THIS\n * FUNCTION IS NOT INTENDED TO BE USED TO HANDLE ERRORS DIRECTLY, IT IS ONLY\n * INTENDED AS AN ALTERNATIVE TO `domains`.\n *   onError signature: (err, req, res)\n *   next signature: (res, res)\n * @param {Object} [options.spdy] - Any options accepted by\n * [node-spdy](https://github.com/indutny/node-spdy).\n * @param {Object} [options.http2] - Any options accepted by\n * [http2.createSecureServer](https://nodejs.org/api/http2.html).\n * @param {Boolean} [options.handleUpgrades=false] - Hook the `upgrade` event\n * from the node HTTP server, pushing `Connection: Upgrade` requests through the\n *  regular request handling chain.\n * @param {Boolean} [options.onceNext=false] - Prevents calling next multiple\n *  times\n * @param {Boolean} [options.strictNext=false] - Throws error when next() is\n *  called more than once, enabled onceNext option\n * @param {Object} [options.httpsServerOptions] - Any options accepted by\n * [node-https Server](http://nodejs.org/api/https.html#https_https).\n * If provided the following restify server options will be ignored:\n * spdy, ca, certificate, key, passphrase, rejectUnauthorized, requestCert and\n * ciphers; however these can all be specified on httpsServerOptions.\n * @param {Boolean} [options.noWriteContinue=false] - prevents\n *  `res.writeContinue()` in `server.on('checkContinue')` when proxing\n * @param {Boolean} [options.ignoreTrailingSlash=false] - ignore trailing slash\n * on paths\n * @param {Boolean} [options.strictFormatters=true] - enables strict formatters\n * behavior: a formatter matching the response's content-type is required. If\n * not found, the response's content-type is automatically set to\n * 'application/octet-stream'. If a formatter for that content-type is not\n * found, sending the response errors.\n * @example\n * var restify = require('restify');\n * var server = restify.createServer();\n *\n * server.listen(8080, function () {\n *   console.log('ready on %s', server.url);\n * });\n */\nfunction Server(options) {\n    assert.object(options, 'options');\n    assert.object(options.log, 'options.log');\n    assert.object(options.router, 'options.router');\n    assert.string(options.name, 'options.name');\n    assert.optionalBool(options.dtrace, 'options.dtrace');\n    assert.optionalBool(options.socketio, 'options.socketio');\n    assert.optionalBool(options.onceNext, 'options.onceNext');\n    assert.optionalBool(options.strictNext, 'options.strictNext');\n    assert.optionalBool(options.strictFormatters, 'options.strictFormatters');\n\n    var self = this;\n\n    EventEmitter.call(this);\n\n    this.onceNext = !!options.onceNext;\n    this.strictNext = !!options.strictNext;\n    this.firstChain = [];\n    this.preChain = new Chain({\n        onceNext: this.onceNext,\n        strictNext: this.strictNext\n    });\n    this.useChain = new Chain({\n        onceNext: this.onceNext,\n        strictNext: this.strictNext\n    });\n    this.log = options.log;\n    this.name = options.name;\n    this.handleUncaughtExceptions = false;\n    const { handleUncaughtExceptions } = options;\n    if (options.handleUncaughtExceptions) {\n        const handleType = typeof handleUncaughtExceptions;\n        if (handleType === 'boolean') {\n            this.handleUncaughtExceptions = handleWithDomain;\n        } else if (handleType === 'function') {\n            this.handleUncaughtExceptions = handleUncaughtExceptions;\n        } else {\n            // prettier-ignore\n            // eslint-disable-next-line max-len\n            assert.fail(`typeof opts.handleUncaughtException (${handleType}) should be boolean OR function`);\n        }\n    }\n    this.router = options.router;\n    this.secure = false;\n    this.socketio = options.socketio || false;\n    this.dtrace = options.dtrace || false;\n    this._inflightRequests = 0;\n\n    this.strictFormatters = true;\n    if (options.strictFormatters !== undefined) {\n        this.strictFormatters = options.strictFormatters;\n    }\n\n    var fmt = mergeFormatters(options.formatters);\n    this.acceptable = fmt.acceptable;\n    this.formatters = fmt.formatters;\n    this.proxyEvents = [\n        'close',\n        'connection',\n        'error',\n        'listening',\n        'secureConnection'\n    ];\n\n    if (options.spdy) {\n        spdy = require('spdy');\n        this.spdy = true;\n        this.server = spdy.createServer(options.spdy);\n    } else if (options.http2) {\n        // http2 module is not available < v8.4.0 (only with flag <= 8.8.0)\n        // load http2 module here to avoid experimental warning in other cases\n        if (!http2) {\n            try {\n                http2 = require('http2');\n                patchResponse(http2.Http2ServerResponse);\n                patchRequest(http2.Http2ServerRequest);\n                // eslint-disable-next-line no-empty\n            } catch (err) {}\n        }\n\n        assert(\n            http2,\n            'http2 module is not available, ' +\n                'upgrade your Node.js version to >= 8.8.0'\n        );\n\n        this.http2 = true;\n        this.server = http2.createSecureServer(options.http2);\n    } else if ((options.cert || options.certificate) && options.key) {\n        this.ca = options.ca;\n        this.certificate = options.certificate || options.cert;\n        this.key = options.key;\n        this.passphrase = options.passphrase || null;\n        this.secure = true;\n\n        this.server = https.createServer({\n            ca: self.ca,\n            cert: self.certificate,\n            key: self.key,\n            passphrase: self.passphrase,\n            rejectUnauthorized: options.rejectUnauthorized,\n            requestCert: options.requestCert,\n            ciphers: options.ciphers,\n            secureOptions: options.secureOptions\n        });\n    } else if (options.httpsServerOptions) {\n        this.server = https.createServer(options.httpsServerOptions);\n    } else {\n        this.server = http.createServer();\n    }\n\n    this.router.on('mount', this.emit.bind(this, 'mount'));\n\n    if (!options.handleUpgrades) {\n        this.proxyEvents.push('upgrade');\n    }\n    this.proxyEvents.forEach(function forEach(e) {\n        self.server.on(e, self.emit.bind(self, e));\n    });\n\n    // Now the things we can't blindly proxy\n    proxyEventWhenListenerAdded('clientError', this, this.server);\n\n    this.server.on('checkContinue', function onCheckContinue(req, res) {\n        if (self.listeners('checkContinue').length > 0) {\n            self.emit('checkContinue', req, res);\n            return;\n        }\n\n        if (!options.noWriteContinue) {\n            res.writeContinue();\n        }\n\n        self._onRequest(req, res);\n    });\n\n    if (options.handleUpgrades) {\n        this.server.on('upgrade', function onUpgrade(req, socket, head) {\n            req._upgradeRequest = true;\n            var res = upgrade.createResponse(req, socket, head);\n            self._onRequest(req, res);\n        });\n    }\n\n    this.server.on('request', this._onRequest.bind(this));\n\n    this.__defineGetter__('maxHeadersCount', function getMaxHeadersCount() {\n        return self.server.maxHeadersCount;\n    });\n\n    this.__defineSetter__('maxHeadersCount', function setMaxHeadersCount(c) {\n        self.server.maxHeadersCount = c;\n        return c;\n    });\n\n    this.__defineGetter__('url', function getUrl() {\n        if (self.socketPath) {\n            return 'http://' + self.socketPath;\n        }\n\n        var addr = self.address();\n        var str = '';\n\n        if (self.spdy) {\n            str += 'spdy://';\n        } else if (self.secure) {\n            str += 'https://';\n        } else {\n            str += 'http://';\n        }\n\n        if (addr) {\n            str +=\n                addr.family === 'IPv6' || addr.family === 6\n                    ? '[' + addr.address + ']'\n                    : addr.address;\n            str += ':';\n            str += addr.port;\n        } else {\n            str += '169.254.0.1:0000';\n        }\n\n        return str;\n    });\n\n    // print deprecation messages based on server configuration\n    deprecationWarnings(self);\n}\nutil.inherits(Server, EventEmitter);\n\nmodule.exports = Server;\n\n/**\n * Only add a listener on the wrappedEmitter when a listener for the event is\n * added to the wrapperEmitter. This is useful when just adding a listener to\n * the wrappedEmittter overrides/disables a default behavior.\n *\n * @param {string} eventName - The name of the event to proxy\n * @param {EventEmitter} wrapperEmitter - The emitter that proxies events from the wrappedEmitter\n * @param {EventEmitter} wrappedEmitter - The proxied emitter\n * @returns {undefined} NA\n */\nfunction proxyEventWhenListenerAdded(\n    eventName,\n    wrapperEmitter,\n    wrappedEmitter\n) {\n    var isEventProxied = false;\n    wrapperEmitter.on('newListener', function onNewListener(handledEventName) {\n        if (handledEventName === eventName && !isEventProxied) {\n            isEventProxied = true;\n            wrappedEmitter.on(\n                eventName,\n                wrapperEmitter.emit.bind(wrapperEmitter, eventName)\n            );\n        }\n    });\n}\n\n///--- Server lifecycle methods\n\n// eslint-disable-next-line jsdoc/check-param-names\n/**\n * Gets the server up and listening.\n * Wraps node's\n * [listen()](\n * http://nodejs.org/docs/latest/api/net.html#net_server_listen_path_callback).\n *\n * @public\n * @memberof Server\n * @instance\n * @function   listen\n * @throws   {TypeError}\n * @param    {Number} port - Port\n * @param    {Number} [host] - Host\n * @param    {Function} [callback] - optionally get notified when listening.\n * @returns  {undefined} no return value\n * @example\n * <caption>You can call like:</caption>\n * server.listen(80)\n * server.listen(80, '127.0.0.1')\n * server.listen('/tmp/server.sock')\n */\nServer.prototype.listen = function listen() {\n    var args = Array.prototype.slice.call(arguments);\n    return this.server.listen.apply(this.server, args);\n};\n\n/**\n * Shuts down this server, and invokes callback (optionally) when done.\n * Wraps node's\n * [close()](http://nodejs.org/docs/latest/api/net.html#net_event_close).\n *\n * @public\n * @memberof Server\n * @instance\n * @function   close\n * @param    {Function}  [callback] - callback to invoke when done\n * @returns  {undefined} no return value\n */\nServer.prototype.close = function close(callback) {\n    if (callback) {\n        assert.func(callback, 'callback');\n    }\n\n    this.server.once('close', function onClose() {\n        return callback ? callback() : false;\n    });\n\n    return this.server.close();\n};\n\n///--- Routing methods\n\n/**\n * Server method opts\n * @typedef  {String|Regexp |Object} Server~methodOpts\n * @type     {Object}\n * @property {String} name a name for the route\n * @property {String} path can be any String accepted by\n * [find-my-way](https://github.com/delvedor/find-my-way)\n * @example\n * // a static route\n * server.get('/foo', function(req, res, next) {});\n * // a parameterized route\n * server.get('/foo/:bar', function(req, res, next) {});\n * // a regular expression\n * server.get('/example/:file(^\\\\d+).png', function(req, res, next) {});\n * // an options object\n * server.get({\n *     path: '/foo',\n * }, function(req, res, next) {});\n */\n\n/**\n * Mounts a chain on the given path against this HTTP verb\n *\n * @public\n * @memberof Server\n * @instance\n * @function get\n * @param   {Server~methodOpts} opts - if string, the URL to handle.\n *                                 if options, the URL to handle, at minimum.\n * @returns {Route}                the newly created route.\n * @example\n * server.get('/', function (req, res, next) {\n *    res.send({ hello: 'world' });\n *    next();\n * });\n * @example\n * <caption>using with async/await</caption>\n * server.get('/', function (req, res) {\n *    await somethingAsync();\n *    res.send({ hello: 'world' });\n *    next();\n * }\n */\nServer.prototype.get = serverMethodFactory('GET');\n\n/**\n * Mounts a chain on the given path against this HTTP verb\n *\n * @public\n * @memberof Server\n * @instance\n * @function head\n * @param   {Server~methodOpts} opts - if string, the URL to handle.\n *                                 if options, the URL to handle, at minimum.\n * @returns {Route}                the newly created route.\n */\nServer.prototype.head = serverMethodFactory('HEAD');\n\n/**\n * Mounts a chain on the given path against this HTTP verb\n *\n * @public\n * @memberof Server\n * @instance\n * @function post\n * @param   {Server~methodOpts} post - if string, the URL to handle.\n *                                 if options, the URL to handle, at minimum.\n * @returns {Route}                the newly created route.\n */\nServer.prototype.post = serverMethodFactory('POST');\n\n/**\n * Mounts a chain on the given path against this HTTP verb\n *\n * @public\n * @memberof Server\n * @instance\n * @function put\n * @param   {Server~methodOpts} put - if string, the URL to handle.\n *                                 if options, the URL to handle, at minimum.\n * @returns {Route}                the newly created route.\n */\nServer.prototype.put = serverMethodFactory('PUT');\n\n/**\n * Mounts a chain on the given path against this HTTP verb\n *\n * @public\n * @memberof Server\n * @instance\n * @function patch\n * @param   {Server~methodOpts} patch - if string, the URL to handle.\n *                                 if options, the URL to handle, at minimum.\n * @returns {Route}                the newly created route.\n */\nServer.prototype.patch = serverMethodFactory('PATCH');\n\n/**\n * Mounts a chain on the given path against this HTTP verb\n *\n * @public\n * @memberof Server\n * @instance\n * @function del\n * @param   {Server~methodOpts} opts - if string, the URL to handle.\n *                                 if options, the URL to handle, at minimum.\n * @returns {Route}                the newly created route.\n */\nServer.prototype.del = serverMethodFactory('DELETE');\n\n/**\n * Mounts a chain on the given path against this HTTP verb\n *\n * @public\n * @memberof Server\n * @instance\n * @function opts\n * @param   {Server~methodOpts} opts - if string, the URL to handle.\n *                                 if options, the URL to handle, at minimum.\n * @returns {Route}                the newly created route.\n */\nServer.prototype.opts = serverMethodFactory('OPTIONS');\n\n///---  Request lifecycle and middleware methods\n\n// eslint-disable-next-line jsdoc/check-param-names\n/**\n * Gives you hooks to run _before_ any routes are located.  This gives you\n * a chance to intercept the request and change headers, etc., that routing\n * depends on.  Note that req.params will _not_ be set yet.\n *\n * @public\n * @memberof Server\n * @instance\n * @function pre\n * @param {...Function|Array} handler - Allows you to add handlers that\n * run for all routes. *before* routing occurs.\n * This gives you a hook to change request headers and the like if you need to.\n * Note that `req.params` will be undefined, as that's filled in *after*\n * routing.\n * Takes a function, or an array of functions.\n * variable number of nested arrays of handler functions\n * @returns {Object} returns self\n * @example\n * server.pre(function(req, res, next) {\n *   req.headers.accept = 'application/json';\n *   return next();\n * });\n * @example\n * <caption>using with async/await</caption>\n * server.pre(async function(req, res) {\n *   await somethingAsync();\n *   somethingSync();\n * }\n * @example\n * <caption>For example, `pre()` can be used to deduplicate slashes in\n * URLs</caption>\n * server.pre(restify.pre.dedupeSlashes());\n * @see {@link http://restify.com/docs/plugins-api/#serverpre-plugins|Restify pre() plugins}\n */\nServer.prototype.pre = function pre() {\n    var self = this;\n    var handlers = Array.prototype.slice.call(arguments);\n\n    argumentsToChain(handlers).forEach(function forEach(handler) {\n        handler._name = handler.name || 'pre-' + self.preChain.count();\n        self.preChain.add(handler);\n    });\n\n    return this;\n};\n\n// eslint-disable-next-line jsdoc/check-param-names\n/**\n * Gives you hooks that run before restify touches a request. These hooks\n * allow you to do processing early in the request/response life cycle without\n * the overhead of the restify framework. You can not yield the event loop in\n * this handler.\n *\n * The function handler accepts two parameters: req, res. If you want restify\n * to ignore this request, return false from your handler. Return true or\n * undefined to let restify continue handling the request.\n *\n * When false is returned, restify immediately stops handling the request. This\n * means that no further middleware will be executed for any chain and routing\n * will not occure. All request/response handling for an incoming request must\n * be done inside the first handler if you intend to return false. This\n * includes things like closing the response and returning a status code.\n *\n * The only work restify does for a first handler is to increment the number of\n * inflightRequests prior to calling the chain, and decrement that value if the\n * handler returns false. Returning anything other than true, false, undefined,\n * or null will cause an exception to be thrown.\n *\n * Since server.first is designed to bypass the restify framework, there are\n * naturally trade-offs you make when using this API:\n *  * Standard restify lifecycle events such as 'after' are not triggered for\n *    any request that you return false from a handler for\n *  * Invoking any of the restify req/res APIs from within a first handler is\n *    unspecified behavior, as the restify framework hasn't built up state for\n *    the request yet.\n *  * There are no request timers available at the time that the first chain\n *    runs.\n *  * And more! Beware doing anything with restify in these handlers. They are\n *    designed to give you similar access to the req/res as you would have if\n *    you were directly using node.js' http module, they are outside of the\n *    restify framework!\n *\n * @public\n * @memberof Server\n * @instance\n * @function first\n * @param {...Function} handler - Allows you to add handlers that\n * run for all requests, *before* restify touches the request.\n * This gives you a hook to change request headers and the like if you need to.\n * Note that `req.params` will be undefined, as that's filled in *after*\n * routing.\n\n * Takes one or more functions.\n * @returns {Object} returns self\n * @example\n * server.first(function(req, res) {\n *   if(server.inflightRequests() > 100) {\n *     res.statusCode = 503;\n *     res.end();\n *     return false\n *   }\n *   return true;\n * })\n */\nServer.prototype.first = function first() {\n    var args = Array.prototype.slice.call(arguments);\n    for (var i = 0; i < args.length; i++) {\n        assert.func(args[i]);\n        this.firstChain.push(args[i]);\n    }\n    return this;\n};\n\n// eslint-disable-next-line jsdoc/check-param-names\n/**\n * Allows you to add in handlers that run for all routes. Note that handlers\n * added\n * via `use()` will run only after the router has found a matching route. If no\n * match is found, these handlers will never run. Takes a function, or an array\n * of functions.\n *\n * You can pass in any combination of functions or array of functions.\n *\n * @public\n * @memberof Server\n * @instance\n * @function use\n * @param {...Function|Array} handler - A variable number of handler functions\n * * and/or a\n * variable number of nested arrays of handler functions\n * @returns {Object} returns self\n * @example\n * server.use(function(req, res, next) {\n *   // do something...\n *   return next();\n * });\n * @example\n * <caption>using with async/await</caption>\n * server.use(async function(req, res) {\n *   await somethingAsync();\n *   somethingSync();\n * }\n * @example\n * <caption>For example, `use()` can be used to attach a request logger\n * </caption>\n * server.pre(restify.plugins.requestLogger());\n * @see {@link http://restify.com/docs/plugins-api/#serveruse-plugins|Restify use() plugins}\n */\nServer.prototype.use = function use() {\n    var self = this;\n    var handlers = Array.prototype.slice.call(arguments);\n\n    argumentsToChain(handlers).forEach(function forEach(handler) {\n        handler._name = handler.name || 'use-' + self.useChain.count();\n        self.useChain.add(handler);\n    });\n\n    return this;\n};\n\n/**\n * Minimal port of the functionality offered by Express.js Route Param\n * Pre-conditions\n *\n * This basically piggy-backs on the `server.use` method. It attaches a\n * new middleware function that only fires if the specified parameter exists\n * in req.params\n *\n * @example\n * server.param(\"user\", function (req, res, next) {\n *   // load the user's information here, always making sure to call next()\n *   fetchUserInformation(req, function callback(user) {\n *     req.user = user;\n *     next();\n *   });\n * });\n * @example\n * <caption>using with async/await</caption>\n * server.param(\"user\", async function(req, res) {\n *   req.user = await fetchUserInformation(req);\n *   somethingSync();\n * }\n *\n * @see {@link http://expressjs.com/guide.html#route-param%20pre-conditions| Express route param pre-conditions}\n * @public\n * @memberof Server\n * @instance\n * @function param\n * @param    {String}   name - The name of the URL param to respond to\n * @param    {Function} fn -   The middleware function to execute\n * @returns  {Object}        returns self\n */\nServer.prototype.param = function param(name, fn) {\n    this.use(function _param(req, res, next) {\n        if (req.params && req.params.hasOwnProperty(name)) {\n            fn.call(this, req, res, next, req.params[name], name);\n        } else {\n            next();\n        }\n    });\n\n    return this;\n};\n\n/**\n * Removes a route from the server.\n * You pass in the route 'blob' you got from a mount call.\n *\n * @public\n * @memberof Server\n * @instance\n * @function rm\n * @throws   {TypeError} on bad input.\n * @param    {String}    routeName - the route name.\n * @returns  {Boolean}   true if route was removed, false if not.\n */\nServer.prototype.rm = function rm(routeName) {\n    var route = this.router.unmount(routeName);\n    return !!route;\n};\n\n///--- Info and debug methods\n\n/**\n * Returns the server address.\n * Wraps node's\n * [address()](http://nodejs.org/docs/latest/api/net.html#net_server_address).\n *\n * @public\n * @memberof Server\n * @instance\n * @function address\n * @returns  {Object} Address of server\n * @example\n * server.address()\n * @example\n * <caption>Output:</caption>\n * { address: '::', family: 'IPv6', port: 8080 }\n */\nServer.prototype.address = function address() {\n    return this.server.address();\n};\n\n/**\n * Returns the number of inflight requests currently being handled by the server\n *\n * @public\n * @memberof Server\n * @instance\n * @function inflightRequests\n * @returns  {number} number of inflight requests\n */\nServer.prototype.inflightRequests = function inflightRequests() {\n    var self = this;\n    return self._inflightRequests;\n};\n\n/**\n * Return debug information about the server.\n *\n * @public\n * @memberof Server\n * @instance\n * @function debugInfo\n * @returns {Object} debug info\n * @example\n * server.getDebugInfo()\n * @example\n * <caption>Output:</caption>\n * {\n *   routes: [\n *     {\n *       name: 'get',\n *       method: 'get',\n *       input: '/',\n *       compiledRegex: /^[\\/]*$/,\n *       compiledUrlParams: null,\n *       handlers: [Array]\n *      }\n *   ],\n *   server: {\n *     formatters: {\n *       'application/javascript': [Function: formatJSONP],\n *       'application/json': [Function: formatJSON],\n *       'text/plain': [Function: formatText],\n *       'application/octet-stream': [Function: formatBinary]\n *     },\n *     address: '::',\n *     port: 8080,\n *     inflightRequests: 0,\n *     pre: [],\n *     use: [ 'parseQueryString', '_jsonp' ],\n *     after: []\n *   }\n * }\n */\nServer.prototype.getDebugInfo = function getDebugInfo() {\n    var self = this;\n\n    // map an array of function to an array of function names\n    var funcNameMapper = function funcNameMapper(handler) {\n        if (handler.name === '') {\n            return 'anonymous';\n        } else {\n            return handler.name;\n        }\n    };\n\n    if (!self._debugInfo) {\n        var addressInfo = self.server.address();\n\n        // output the actual routes registered with restify\n        var routeInfo = self.router.getDebugInfo();\n\n        var preHandlers = self.preChain.getHandlers().map(funcNameMapper);\n        var useHandlers = self.useChain.getHandlers().map(funcNameMapper);\n\n        // get each route's handler chain\n        var routes = _.map(routeInfo, function mapValues(route) {\n            route.handlers = Array.prototype.concat.call(\n                // TODO: should it contain use handlers?\n                useHandlers,\n                route.handlers.map(funcNameMapper)\n            );\n            return route;\n        });\n\n        self._debugInfo = {\n            routes: routes,\n            server: {\n                formatters: self.formatters,\n                // if server is not yet listening, addressInfo may be null\n                address: addressInfo && addressInfo.address,\n                port: addressInfo && addressInfo.port,\n                inflightRequests: self.inflightRequests(),\n                pre: preHandlers,\n                use: useHandlers,\n                after: self.listeners('after').map(funcNameMapper)\n            }\n        };\n    }\n\n    return self._debugInfo;\n};\n\n/**\n * toString() the server for easy reading/output.\n *\n * @public\n * @memberof Server\n * @instance\n * @function toString\n * @returns  {String} stringified server\n * @example\n * server.toString()\n * @example\n * <caption>Output:</caption>\n *\tAccepts: application/json, text/plain, application/octet-stream,\n * application/javascript\n *\tName: restify\n *\tPre: []\n *\tRouter: RestifyRouter:\n *\t\tDELETE: []\n *\t\tGET: [get]\n *\t\tHEAD: []\n *\t\tOPTIONS: []\n *\t\tPATCH: []\n *\t\tPOST: []\n *\t\tPUT: []\n *\n *\tRoutes:\n *\t\tget: [parseQueryString, _jsonp, function]\n *\tSecure: false\n *\tUrl: http://[::]:8080\n *\tVersion:\n */\nServer.prototype.toString = function toString() {\n    var LINE_FMT = '\\t%s: %s\\n';\n    var SUB_LINE_FMT = '\\t\\t%s: %s\\n';\n    var str = '';\n\n    function handlersToString(arr) {\n        var s =\n            '[' +\n            arr\n                .map(function map(b) {\n                    return b.name || 'function';\n                })\n                .join(', ') +\n            ']';\n\n        return s;\n    }\n\n    str += sprintf(LINE_FMT, 'Accepts', this.acceptable.join(', '));\n    str += sprintf(LINE_FMT, 'Name', this.name);\n    str += sprintf(\n        LINE_FMT,\n        'Pre',\n        handlersToString(this.preChain.getHandlers())\n    );\n    str += sprintf(LINE_FMT, 'Router', '');\n    this.router\n        .toString()\n        .split('\\n')\n        .forEach(function forEach(line) {\n            str += sprintf('\\t\\t%s\\n', line);\n        });\n    str += sprintf(LINE_FMT, 'Routes', '');\n    _.forEach(this.router.getRoutes(), function forEach(route, routeName) {\n        var handlers = handlersToString(route.chain.getHandlers());\n        str += sprintf(SUB_LINE_FMT, routeName, handlers);\n    });\n    str += sprintf(LINE_FMT, 'Secure', this.secure);\n    str += sprintf(LINE_FMT, 'Url', this.url);\n\n    return str;\n};\n\n///--- Private methods\n\n// Lifecycle:\n//\n// 1. _onRequest (handle new request, setup request and triggers pre)\n//   2. _runPre\n//     3. _afterPre (runs after pre handlers are finisehd, triggers route)\n//       4. _runRoute (route lookup)\n//         5. _runUse (runs use handlers, if route exists)\n//            6. Runs route handlers\n//              7. _afterRoute (runs after route handlers are finised,\n//                          triggers use)\n// 8. _finishReqResCycle (on response \"finish\" and \"error\" events)\n//\n// Events:\n// e.1 after (triggered when response is flushed)\n//\n// Errors:\n// e.1 _onHandlerError (runs when next was called with an Error)\n//   e.2 _routeErrorResponse\n// e.1 _onHandlerError (when, next('string') called, trigger route by name)\n//  e.2 _afterRoute\n\n/**\n * Setup request and calls _onRequest to run middlewares and call router\n *\n * @private\n * @memberof Server\n * @instance\n * @function _onRequest\n * @param    {Object}    req - the request object\n * @param    {Object}    res - the response object\n * @returns  {undefined} no return value\n * @fires Request,Response#request\n */\nServer.prototype._onRequest = function _onRequest(req, res) {\n    var self = this;\n\n    // Increment the number of inflight requests prior to calling the firstChain\n    // handlers. This accomplishes two things. First, it gives earliest an\n    // accurate count of how many inflight requests there would be including\n    // this new request. Second, it intentionally winds up the inflight request\n    // accounting with the implementation of firstChain. Note how we increment\n    // here, but decrement down inside the for loop below. It's easy to end up\n    // with race conditions betwen inflight request accounting and inflight\n    // request load shedding, causing load shedding to reject/allow too many\n    // requests. The current implementation of firstChain is designed to\n    // remove those race conditions. By winding these implementations up with\n    // one another, it makes it clear that moving around the inflight request\n    // accounting will change the behavior of earliest.\n    self._inflightRequests++;\n\n    // Give the first chain the earliest possible opportunity to process\n    // this request before we do any work on it.\n    var firstChain = self.firstChain;\n    for (var i = 0; i < firstChain.length; i++) {\n        var handle = firstChain[i](req, res);\n        // Limit the range of values we will accept as return results of\n        // first handlers. This helps us maintain forward compatibility by\n        // ensuring users don't rely on undocumented/unspecified behavior.\n        assert.ok(\n            handle === true ||\n                handle === false ||\n                handle === undefined ||\n                handle === null,\n            'Return value of first[' +\n                i +\n                '] must be: ' +\n                'boolean, undefined, or null'\n        );\n        // If the first handler returns false, stop handling the request\n        // immediately.\n        if (handle === false) {\n            self._inflightRequests--;\n            return;\n        }\n    }\n\n    this.emit('request', req, res);\n\n    // Skip Socket.io endpoints\n    if (this.socketio && /^\\/socket\\.io.*/.test(req.url)) {\n        self._inflightRequests--;\n        return;\n    }\n\n    // Decorate req and res objects\n    self._setupRequest(req, res);\n\n    if (self.handleUncaughtExceptions) {\n        self.handleUncaughtExceptions(\n            req,\n            res,\n            err => this._onHandlerError(err, req, res, true),\n            self._runPre.bind(self)\n        );\n    } else {\n        self._runPre(req, res);\n    }\n};\n\n/**\n * Run pre handlers\n *\n * @private\n * @memberof Server\n * @instance\n * @function _runPre\n * @param    {Object}    req - the request object\n * @param    {Object}    res - the response object\n * @returns  {undefined} no return value\n * @fires Request,Response#request\n */\nServer.prototype._runPre = function _runPre(req, res) {\n    var self = this;\n\n    // emit 'pre' event before we run the pre handlers\n    self.emit('pre', req, res);\n\n    // Run \"pre\"\n    req._currentHandler = 'pre';\n    req._timePreStart = process.hrtime();\n\n    self.preChain.run(req, res, function preChainDone(err) {\n        // Execution time of a handler with error can be significantly lower\n        req._timePreEnd = process.hrtime();\n        self._afterPre(err, req, res);\n    });\n};\n\n/**\n * After pre handlers finished\n *\n * @private\n * @memberof Server\n * @instance\n * @function _afterPre\n * @param  {Error|false|undefined} err - pre handler error\n * @param  {Request} req - request\n * @param  {Response} res - response\n * @returns {undefined} no return value\n */\nServer.prototype._afterPre = function _afterPre(err, req, res) {\n    var self = this;\n\n    // Handle error\n    if (err) {\n        self._onHandlerError(err, req, res);\n        self._finishReqResCycle(req, res, err);\n        return;\n    }\n\n    // Stop\n    if (err === false) {\n        self._onHandlerStop(req, res);\n        return;\n    }\n\n    self._runRoute(req, res);\n};\n\n/**\n * Find route and run handlers\n *\n * @private\n * @memberof Server\n * @instance\n * @function _runRoute\n * @param    {Object}    req - the request object\n * @param    {Object}    res - the response object\n * @returns  {undefined} no return value\n * @fires Request,Response#request\n */\nServer.prototype._runRoute = function _runRoute(req, res) {\n    var self = this;\n\n    var routeHandler = self.router.lookup(req, res);\n\n    if (!routeHandler) {\n        self.router.defaultRoute(req, res, function afterRouter(err) {\n            self._afterRoute(err, req, res);\n        });\n        return;\n    }\n\n    // Emit routed\n    self.emit('routed', req, res, req.route);\n\n    self._runUse(req, res, function afterUse() {\n        // DTrace\n        if (self.dtrace) {\n            dtrace._rstfy_probes['route-start'].fire(function fire() {\n                return [\n                    self.name,\n                    req.route.name,\n                    req._dtraceId,\n                    req.method,\n                    req.href(),\n                    req.headers\n                ];\n            });\n        }\n\n        req._timeRouteStart = process.hrtime();\n        routeHandler(req, res, function afterRouter(err) {\n            // Execution time of a handler with error can be significantly lower\n            req._timeRouteEnd = process.hrtime();\n\n            // DTrace\n            if (self.dtrace) {\n                dtrace._rstfy_probes['route-done'].fire(function fire() {\n                    return [\n                        self.name,\n                        req.route.name,\n                        req._dtraceId,\n                        res.statusCode || 200,\n                        res.headers\n                    ];\n                });\n            }\n\n            self._afterRoute(err, req, res);\n        });\n    });\n};\n\n/**\n * After use handlers finished\n *\n * @private\n * @memberof Server\n * @instance\n * @function _afterRoute\n * @param  {Error|false|undefined} err - use handler error\n * @param  {Request} req - request\n * @param  {Response} res - response\n * @returns {undefined} no return value\n */\nServer.prototype._afterRoute = function _afterRoute(err, req, res) {\n    var self = this;\n\n    res._handlersFinished = true;\n\n    // Handle error\n    if (err) {\n        self._onHandlerError(err, req, res);\n        self._finishReqResCycle(req, res, err);\n        return;\n    }\n\n    // Trigger finish\n    self._finishReqResCycle(req, res, err);\n};\n\n/**\n * Run use handlers\n *\n * @private\n * @memberof Server\n * @instance\n * @function _runUse\n * @param    {Object}    req - the request object\n * @param    {Object}    res - the response object\n * @param    {Function}  next - next\n * @returns  {undefined} no return value\n * @fires Request,Response#request\n */\nServer.prototype._runUse = function _runUse(req, res, next) {\n    var self = this;\n\n    // Run \"use\"\n    req._currentHandler = 'use';\n    req._timeUseStart = process.hrtime();\n\n    self.useChain.run(req, res, function useChainDone(err) {\n        // Execution time of a handler with error can be significantly lower\n        req._timeUseEnd = process.hrtime();\n        self._afterUse(err, req, res, next);\n    });\n};\n\n/**\n * After use handlers finished\n *\n * @private\n * @memberof Server\n * @instance\n * @function _afterUse\n * @param  {Error|false|undefined} err - use handler error\n * @param  {Request} req - request\n * @param  {Response} res - response\n * @param  {Function}  next - next\n * @returns {undefined} no return value\n */\nServer.prototype._afterUse = function _afterUse(err, req, res, next) {\n    var self = this;\n\n    // Handle error\n    if (err) {\n        self._onHandlerError(err, req, res);\n        self._finishReqResCycle(req, res, err);\n        return;\n    }\n\n    // Stop\n    if (err === false) {\n        self._onHandlerStop(req, res);\n        return;\n    }\n\n    next();\n};\n\n/**\n * Runs after next(false) is called\n *\n * @private\n * @memberof Server\n * @instance\n * @function _onHandlerStop\n * @param  {Request} req - request\n * @param  {Response} res - response\n * @returns {undefined} no return value\n */\nServer.prototype._onHandlerStop = function _onHandlerStop(req, res) {\n    res._handlersFinished = true;\n    this._finishReqResCycle(req, res);\n};\n\n/**\n * After route handlers finished\n * NOTE: only called when last handler calls next([err])\n *\n * @private\n * @memberof Server\n * @instance\n * @function _onHandlerError\n * @param  {Error|String|undefined} err - router handler error or route name\n * @param  {Request} req - request\n * @param  {Response} res - response\n * @param  {boolean} isUncaught - whether the error is uncaught\n * @returns {undefined} no return value\n */\nServer.prototype._onHandlerError = function _onHandlerError(\n    err,\n    req,\n    res,\n    isUncaught\n) {\n    var self = this;\n\n    // Handlers don't continue when error happen\n    res._handlersFinished = true;\n\n    // Preserve handler err for finish event\n    res.err = res.err || err;\n\n    // Error happened in router handlers\n    self._routeErrorResponse(req, res, err, isUncaught);\n};\n\n/**\n * Set up the request before routing and execution of handler chain functions.\n *\n * @private\n * @memberof Server\n * @instance\n * @function _setupRequest\n * @param    {Object}    req - the request object\n * @param    {Object}    res - the response object\n * @returns  {undefined} no return value\n */\nServer.prototype._setupRequest = function _setupRequest(req, res) {\n    var self = this;\n\n    // Extend request\n    req._dtraceId = dtrace.nextId();\n    // if log is set on .first, don't override it\n    if (req.log === undefined) {\n        req.log = self.log;\n    }\n    if (res.log === undefined) {\n        res.log = req.log;\n    }\n    req._date = new Date();\n    req._timeStart = process.hrtime();\n    req.serverName = self.name;\n    req.params = {};\n    req.timers = [];\n    req.dtrace = self.dtrace;\n\n    // Extend response\n    res.acceptable = self.acceptable;\n    res.formatters = self.formatters;\n    res.req = req;\n    res.serverName = self.name;\n    res._handlersFinished = false;\n    res._flushed = false;\n    res._strictFormatters = this.strictFormatters;\n\n    // set header only if name isn't empty string\n    if (self.name !== '') {\n        res.setHeader('Server', self.name);\n    }\n\n    // Request lifecycle events\n\n    // attach a listener for 'aborted' events, this will let us set\n    // a flag so that we can stop processing the request if the client aborts\n    // the connection (or we lose the connection).\n    // we consider a closed request as flushed from metrics point of view\n    function onReqAborted() {\n        // Request was aborted, override the status code\n        var err = new customErrorTypes.RequestCloseError();\n        err.statusCode = 444;\n\n        // For backward compatibility we only set connection state to \"close\"\n        // for RequestCloseError, also aborted is always immediatly followed\n        // by a \"close\" event.\n        // We don't set _connectionState to \"close\" in the happy path\n        req._connectionState = 'close';\n\n        // Set status code and err for audit as req is already closed connection\n        res.statusCode = err.statusCode;\n        res.err = err;\n    }\n\n    // Response lifecycle events\n    function onResFinish() {\n        var processHrTime = process.hrtime();\n\n        res._flushed = true;\n        req._timeFlushed = processHrTime;\n\n        // Response may get flushed before handler callback is triggered\n        req._timeFlushed = processHrTime;\n        req._timePreEnd = req._timePreEnd || processHrTime;\n        req._timeUseEnd = req._timeUseEnd || processHrTime;\n        req._timeRouteEnd = req._timeRouteEnd || processHrTime;\n\n        // In Node < 10 \"close\" event dont fire always\n        // https://github.com/nodejs/node/pull/20611\n        self._finishReqResCycle(req, res);\n    }\n\n    // We are handling when connection is being closed prematurely outside of\n    // restify. It's not because the req is aborted.\n    function onResClose() {\n        res._flushed = true;\n\n        // Finish may already set the req._timeFlushed\n        req._timeFlushed = req._timeFlushed || process.hrtime();\n\n        self._finishReqResCycle(req, res, res.err);\n    }\n\n    // Request events\n    req.once('aborted', onReqAborted);\n\n    // Response events\n    res.once('finish', onResFinish);\n    res.once('close', onResClose);\n\n    // attach a listener for the response's 'redirect' event\n    res.on('redirect', function onRedirect(redirectLocation) {\n        self.emit('redirect', redirectLocation);\n    });\n};\n\n/**\n * Maintaining the end of the request-response cycle:\n *  - emitting after event\n *  - updating inflight requests metrics\n * Check if the response is finished, and if not, wait for it before firing the\n * response object.\n *\n * @private\n * @memberof Server\n * @instance\n * @function _finishReqResCycle\n * @param {Object} req - the request object\n * @param {Object} res - the response object\n * @param {Object} [err] - a possible error as a result of failed route matching\n * or failed execution of the handler array.\n * @returns {undefined} no return value\n */\nServer.prototype._finishReqResCycle = function _finishReqResCycle(\n    req,\n    res,\n    err\n) {\n    var self = this;\n    var route = req.route; // can be undefined when 404 or error\n\n    // if the returned err value was a string, then we're handling the\n    // next('foo') case where we redirect to another middleware stack. don't\n    // do anything here because we're not done yet.\n    if (res._finished || _.isString(err)) {\n        return;\n    }\n\n    if (res._flushed && res._handlersFinished) {\n        // decrement number of requests\n        self._inflightRequests--;\n        res._finished = true;\n        req._timeFinished = process.hrtime();\n\n        // after event has signature of function(req, res, route, err) {...}\n        var finalErr = err || res.err;\n        req.emit('restifyDone', route, finalErr);\n        self.emit('after', req, res, route, finalErr);\n    } else if (\n        res._handlersFinished === true &&\n        res.headersSent === false &&\n        !res.err\n    ) {\n        // if we reached the end of the handler chain and headers haven't been\n        // sent AND there isn't an existing res.err (e.g., req abort/close),\n        // it's possible it's a user error and a response was never written.\n        // send a 500.\n        res.send(\n            new errors.InternalServerError(\n                'reached the end of the handler chain without ' +\n                    'writing a response!'\n            )\n        );\n        return;\n    } else {\n        // Store error for when the response is flushed and we actually emit the\n        // 'after' event. The \"err\" object passed to this method takes\n        // precedence, but in case it's not set, \"res.err\" may have been already\n        // set by another code path and we want to preserve it. The caveat thus\n        // is that the 'after' event will be emitted with the latest error that\n        // was set before the response is fully flushed. While not ideal, this\n        // is on purpose and accepted as a reasonable trade-off for now.\n        res.err = err || res.err;\n    }\n};\n\n/**\n * Helper function to, when on router error, emit error events and then\n * flush the err.\n *\n * @private\n * @memberof Server\n * @instance\n * @function _routeErrorResponse\n * @param    {Request}     req -    the request object\n * @param    {Response}    res -    the response object\n * @param    {Error}       err -    error\n * @param    {boolean}     isUncaught - whether the error is uncaught\n * @returns  {undefined} no return value\n */\nServer.prototype._routeErrorResponse = function _routeErrorResponse(\n    req,\n    res,\n    err,\n    isUncaught\n) {\n    var self = this;\n\n    if (\n        isUncaught &&\n        self.handleUncaughtExceptions &&\n        self.listenerCount('uncaughtException') > 1\n    ) {\n        self.emit(\n            'uncaughtException',\n            req,\n            res,\n            req.route,\n            err,\n            function uncaughtExceptionCompleted() {\n                // We provide a callback to listeners of the 'uncaughtException'\n                // event and we call _finishReqResCycle when that callback is\n                // called so that, in case the actual request/response lifecycle\n                // was completed _before_ the error was thrown or emitted, and\n                // thus _before_ route handlers were marked as \"finished\", we\n                // can still mark the req/res lifecycle as complete.\n                // This edge case can occur when e.g. a client aborts a request\n                // and the route handler that handles that request throws an\n                // uncaught exception _after_ the request was aborted and the\n                // response was closed.\n                self._finishReqResCycle(req, res, err);\n            }\n        );\n        return;\n    }\n\n    self._emitErrorEvents(req, res, null, err, function emitError() {\n        // Prevent double handling\n        if (res._sent) {\n            return;\n        }\n\n        // only automatically send errors that are known (e.g., restify-errors)\n        if (err instanceof Error && _.isNumber(err.statusCode)) {\n            res.send(err);\n            return;\n        }\n\n        // if the thrown exception is not really an Error object, e.g.,\n        //    \"throw 'foo';\"\n        // try to do best effort here to pass on that value by casting it to a\n        // string. This should work even for falsy values like 0, false, null,\n        // or undefined.\n        res.send(new errors.InternalError(String(err)));\n    });\n};\n\n/**\n * Emit error events when errors are encountered either while attempting to\n * route the request (via router) or while executing the handler chain.\n *\n * @private\n * @memberof Server\n * @instance\n * @function _emitErrorEvents\n * @param    {Object} req -    the request object\n * @param    {Object} res -    the response object\n * @param    {Object} route -  the current route, if applicable\n * @param    {Object} err -    an error object\n * @param    {Object} cb -     callback function\n * @returns  {undefined} no return value\n * @fires Error#restifyError\n */\nServer.prototype._emitErrorEvents = function _emitErrorEvents(\n    req,\n    res,\n    route,\n    err,\n    cb\n) {\n    var self = this;\n    // Error can be of any type: undefined, Error, Number, etc. so we need\n    // to protect ourselves from trying to resolve names from non Error objects\n    var errName = err && err.name;\n    var normalizedErrName = errName && errEvtNameFromError(err);\n\n    req.log.trace(\n        {\n            err: err,\n            errName: normalizedErrName\n        },\n        'entering emitErrorEvents',\n        errName\n    );\n\n    var errEvtNames = [];\n\n    // if we have listeners for the specific error, fire those first.\n    // if there's no error name, we should not emit an event\n    if (normalizedErrName && self.listeners(normalizedErrName).length > 0) {\n        errEvtNames.push(normalizedErrName);\n    }\n\n    // or if we have a generic error listener. always fire generic error event\n    // listener afterwards.\n    if (self.listeners('restifyError').length > 0) {\n        errEvtNames.push('restifyError');\n    }\n\n    // kick off the async listeners\n    return vasync.forEachPipeline(\n        {\n            inputs: errEvtNames,\n            func: function emitError(errEvtName, vasyncCb) {\n                self.emit(errEvtName, req, res, err, function emitErrDone() {\n                    // the error listener may return arbitrary objects, throw\n                    // them away and continue on. don't want vasync to take\n                    // that error and stop, we want to emit every single event.\n                    return vasyncCb();\n                });\n            }\n        },\n        // eslint-disable-next-line handle-callback-err\n        function onResult(__, results) {\n            // vasync will never return error here since we throw them away.\n            return cb();\n        }\n    );\n};\n\n///--- Helpers\n\n/**\n * Verify and flatten a nested array of request handlers.\n *\n * @private\n * @function argumentsToChain\n * @throws   {TypeError}\n * @param    {Function[]} handlers - pass through of funcs from server.[method]\n * @returns  {Array} request handlers\n */\nfunction argumentsToChain(handlers) {\n    assert.array(handlers, 'handlers');\n\n    var chain = [];\n\n    // A recursive function for unwinding a nested array of handlers into a\n    // single chain.\n    function process(array) {\n        for (var i = 0; i < array.length; i++) {\n            if (Array.isArray(array[i])) {\n                // Recursively call on nested arrays\n                process(array[i]);\n                continue;\n            }\n            // If an element of the array isn't an array, ensure it is a\n            // handler function and then push it onto the chain of handlers\n            assert.func(array[i], 'handler');\n            chain.push(array[i]);\n        }\n\n        return chain;\n    }\n\n    // Return the chain, note that if `handlers` is an empty array, this will\n    // return an empty array.\n    return process(handlers);\n}\n\n/**\n * merge optional formatters with the default formatters to create a single\n * formatters object. the passed in optional formatters object looks like:\n * formatters: {\n *   'application/foo': function formatFoo(req, res, body) {...}\n * }\n * @private\n * @function mergeFormatters\n * @param    {Object} fmt user specified formatters object\n * @returns  {Object}\n */\n\nfunction mergeFormatters(fmt) {\n    var arr = [];\n    var obj = {};\n\n    function addFormatter(src, k) {\n        assert.func(src[k], 'formatter');\n\n        var q = 1.0; // RFC 2616 sec14 - The default value is q=1\n        var t = k;\n\n        if (k.indexOf(';') !== -1) {\n            var tmp = k.split(/\\s*;\\s*/);\n            t = tmp[0];\n\n            if (tmp[1].indexOf('q=') !== -1) {\n                q = parseFloat(tmp[1].split('=')[1]);\n            }\n        }\n\n        if (k.indexOf('/') === -1) {\n            k = mime.getType(k);\n        }\n\n        obj[t] = src[k];\n        arr.push({\n            q: q,\n            t: t\n        });\n    }\n\n    Object.keys(formatters).forEach(addFormatter.bind(this, formatters));\n    Object.keys(fmt || {}).forEach(addFormatter.bind(this, fmt || {}));\n\n    arr = arr\n        .sort(function sort(a, b) {\n            return b.q - a.q;\n        })\n        .map(function map(a) {\n            return a.t;\n        });\n\n    return {\n        formatters: obj,\n        acceptable: arr\n    };\n}\n\n/**\n * Map an Error's .name property into the actual event name that is emitted\n * by the restify server object.\n *\n * @function\n * @private errEvtNameFromError\n * @param {Object} err - an error object\n * @returns {String} an event name to emit\n */\nfunction errEvtNameFromError(err) {\n    if (err.name === 'ResourceNotFoundError') {\n        // remap the name for router errors\n        return 'NotFound';\n    } else if (err.name === 'InvalidVersionError') {\n        // remap the name for router errors\n        return 'VersionNotAllowed';\n    } else if (err.name) {\n        return err.name.replace(/Error$/, '');\n    }\n    // If the err is not an Error, then just return an empty string\n    return '';\n}\n\n/**\n * Mounts a chain on the given path against this HTTP verb\n *\n * @private\n * @function serverMethodFactory\n * @param {String} method - name of the HTTP method\n * @returns {Function} factory\n */\nfunction serverMethodFactory(method) {\n    return function serverMethod(opts) {\n        if (opts instanceof RegExp || typeof opts === 'string') {\n            opts = {\n                path: opts\n            };\n        } else if (typeof opts === 'object') {\n            opts = shallowCopy(opts);\n        } else {\n            throw new TypeError('path (string) required');\n        }\n\n        if (arguments.length < 2) {\n            throw new TypeError('handler (function) required');\n        }\n\n        opts.method = method;\n        opts.path = opts.path || opts.url;\n\n        // We accept both a variable number of handler functions, a\n        // variable number of nested arrays of handler functions, or a mix\n        // of both\n        var handlers = Array.prototype.slice.call(arguments, 1);\n        var chain = argumentsToChain(handlers);\n        var route = this.router.mount(opts, chain);\n\n        return route.name;\n    };\n}\n"
  },
  {
    "path": "lib/upgrade.js",
    "content": "// Copyright (c) 2013, Joyent, Inc. All rights reserved.\n\n'use strict';\n\nvar EventEmitter = require('events').EventEmitter;\nvar util = require('util');\nvar assert = require('assert-plus');\n\n/**\n * An custom error for capturing an invalid upgrade state.\n *\n * @public\n * @class\n * @param {String} msg - an error message\n */\nfunction InvalidUpgradeStateError(msg) {\n    if (Error.captureStackTrace) {\n        Error.captureStackTrace(this, InvalidUpgradeStateError);\n    }\n\n    this.message = msg;\n    this.name = 'InvalidUpgradeStateError';\n}\nutil.inherits(InvalidUpgradeStateError, Error);\n\n//\n// The Node HTTP Server will, if we handle the 'upgrade' event, swallow any\n// Request with the 'Connection: upgrade' header set.  While doing this it\n// detaches from the 'data' events on the Socket and passes the socket to\n// us, so that we may take over handling for the connection.\n//\n// Unfortunately, the API does not presently provide a http.ServerResponse\n// for us to use in the event that we do not wish to upgrade the connection.\n// This factory method provides a skeletal implementation of a\n// restify-compatible response that is sufficient to allow the existing\n// request handling path to work, while allowing us to perform _at most_ one\n// of either:\n//\n//   - Return a basic HTTP Response with a provided Status Code and\n//     close the socket.\n//   - Upgrade the connection and stop further processing.\n//\n// To determine if an upgrade is requested, a route handler would check for\n// the 'claimUpgrade' method on the Response.  The object this method\n// returns will have the 'socket' and 'head' Buffer emitted with the\n// 'upgrade' event by the http.Server.  If the upgrade is not possible, such\n// as when the HTTP head (or a full request) has already been sent by some\n// other handler, this method will throw.\n//\n\n/**\n * Create a new upgraded response.\n *\n * @public\n * @function createServerUpgradeResponse\n * @param    {Object} req -    the request object\n * @param    {Object} socket - the network socket\n * @param    {Object} head -   a buffer, the first packet of the upgraded stream\n * @returns  {Object}        an upgraded reponses\n */\nfunction createServerUpgradeResponse(req, socket, head) {\n    return new ServerUpgradeResponse(socket, head);\n}\n\n/**\n * Upgrade the http response\n *\n * @private\n * @class\n * @param   {Object}    socket - the network socket\n * @param   {Object}    head -   a buffer, the first packet of\n *                               the upgraded stream\n * @returns {undefined} no return value\n */\nfunction ServerUpgradeResponse(socket, head) {\n    assert.object(socket, 'socket');\n    assert.buffer(head, 'head');\n\n    EventEmitter.call(this);\n\n    this.sendDate = true;\n    this.statusCode = 400;\n\n    this._upgrade = {\n        socket: socket,\n        head: head\n    };\n\n    this._headWritten = false;\n    this._upgradeClaimed = false;\n}\nutil.inherits(ServerUpgradeResponse, EventEmitter);\n\n/**\n * A function generator for all programatically attaching methods on to\n * the ServerUpgradeResponse class.\n *\n * @private\n * @function notImplemented\n * @param    {Object}   method - an object containing configuration\n * @returns  {Function} function\n */\nfunction notImplemented(method) {\n    if (!method.throws) {\n        return function returns() {\n            return method.returns;\n        };\n    } else {\n        return function throws() {\n            throw new Error('Method ' + method.name + ' is not implemented!');\n        };\n    }\n}\n\nvar NOT_IMPLEMENTED = [\n    { name: 'writeContinue', throws: true },\n    { name: 'setHeader', throws: false, returns: null },\n    { name: 'getHeader', throws: false, returns: null },\n    { name: 'getHeaders', throws: false, returns: {} },\n    { name: 'removeHeader', throws: false, returns: null },\n    { name: 'addTrailer', throws: false, returns: null },\n    { name: 'cache', throws: false, returns: 'public' },\n    { name: 'format', throws: true },\n    { name: 'set', throws: false, returns: null },\n    { name: 'get', throws: false, returns: null },\n    { name: 'headers', throws: false, returns: {} },\n    { name: 'header', throws: false, returns: null },\n    { name: 'json', throws: false, returns: null },\n    { name: 'link', throws: false, returns: null }\n];\n\n// programatically add a bunch of methods to the ServerUpgradeResponse proto\nNOT_IMPLEMENTED.forEach(function forEach(method) {\n    ServerUpgradeResponse.prototype[method.name] = notImplemented(method);\n});\n\n/**\n * Internal implementation of `writeHead`\n *\n * @private\n * @function _writeHeadImpl\n * @param   {Number} statusCode - the http status code\n * @param   {String} reason -     a message\n * @returns {undefined} no return value\n */\nServerUpgradeResponse.prototype._writeHeadImpl = function _writeHeadImpl(\n    statusCode,\n    reason\n) {\n    if (this._headWritten) {\n        return;\n    }\n    this._headWritten = true;\n\n    if (this._upgradeClaimed) {\n        throw new InvalidUpgradeStateError('Upgrade already claimed!');\n    }\n\n    var head = ['HTTP/1.1 ' + statusCode + ' ' + reason, 'Connection: close'];\n\n    if (this.sendDate) {\n        head.push('Date: ' + new Date().toUTCString());\n    }\n\n    this._upgrade.socket.write(head.join('\\r\\n') + '\\r\\n');\n};\n\n/**\n * Set the status code of the response.\n *\n * @public\n * @function  status\n * @param     {Number} code - the http status code\n * @returns   {undefined} no return value\n */\nServerUpgradeResponse.prototype.status = function status(code) {\n    assert.number(code, 'code');\n    this.statusCode = code;\n    return code;\n};\n\n/**\n * Sends the response.\n *\n * @public\n * @function  send\n * @param     {Number}           code - the http status code\n * @param     {Object | String}  body - the response to send out\n * @returns   {undefined} no return value\n */\nServerUpgradeResponse.prototype.send = function send(code, body) {\n    if (typeof code === 'number') {\n        this.statusCode = code;\n    } else {\n        body = code;\n    }\n\n    if (typeof body === 'object') {\n        if (typeof body.statusCode === 'number') {\n            this.statusCode = body.statusCode;\n        }\n\n        if (typeof body.message === 'string') {\n            this.statusReason = body.message;\n        }\n    }\n\n    return this.end();\n};\n\n/**\n * End the response.\n *\n * @public\n * @function end\n * @returns  {Boolean} always returns true\n */\nServerUpgradeResponse.prototype.end = function end() {\n    this._writeHeadImpl(this.statusCode, 'Connection Not Upgraded');\n    this._upgrade.socket.end('\\r\\n');\n    return true;\n};\n\n/**\n * Write to the response.\n *\n * @public\n * @function write\n * @returns  {Boolean} always returns true\n */\nServerUpgradeResponse.prototype.write = function write() {\n    this._writeHeadImpl(this.statusCode, 'Connection Not Upgraded');\n    return true;\n};\n\n/**\n * Write to the head of the response.\n *\n * @public\n * @function writeHead\n * @param   {Number} statusCode - the http status code\n * @param   {String} reason -     a message\n * @returns {undefined} no return value\n */\nServerUpgradeResponse.prototype.writeHead = function writeHead(\n    statusCode,\n    reason\n) {\n    assert.number(statusCode, 'statusCode');\n    assert.optionalString(reason, 'reason');\n\n    this.statusCode = statusCode;\n\n    if (!reason) {\n        reason = 'Connection Not Upgraded';\n    }\n\n    if (this._headWritten) {\n        throw new Error('Head already written!');\n    }\n\n    return this._writeHeadImpl(statusCode, reason);\n};\n\n/**\n * Attempt to upgrade.\n *\n * @public\n * @function claimUpgrade\n * @returns  {Object}     an object containing the socket and head\n */\nServerUpgradeResponse.prototype.claimUpgrade = function claimUpgrade() {\n    if (this._upgradeClaimed) {\n        throw new InvalidUpgradeStateError('Upgrade already claimed!');\n    }\n\n    if (this._headWritten) {\n        throw new InvalidUpgradeStateError('Upgrade already aborted!');\n    }\n\n    this._upgradeClaimed = true;\n\n    return this._upgrade;\n};\n\nmodule.exports = {\n    createResponse: createServerUpgradeResponse,\n    InvalidUpgradeStateError: InvalidUpgradeStateError\n};\n"
  },
  {
    "path": "lib/utils.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n\n/**\n * Return a shallow copy of the given object;\n *\n * @public\n * @function  shallowCopy\n * @param   {Object} obj - the object to copy\n * @returns {Object}     the new copy of the object\n */\nfunction shallowCopy(obj) {\n    if (!obj) {\n        return obj;\n    }\n    var copy = {};\n    Object.keys(obj).forEach(function forEach(k) {\n        copy[k] = obj[k];\n    });\n    return copy;\n}\n\n/**\n * Merges two query parameter objects. Merges to array\n * if the same key is encountered.\n *\n * @public\n * @function  mergeQs\n * @param   {Object} obj1 - first qs object\n * @param   {Object} obj2 - second qs object\n * @returns {Object}        the merged object\n */\nfunction mergeQs(obj1, obj2) {\n    var merged = shallowCopy(obj1) || {};\n\n    // defend against null cause null is an object. yay js.\n    if (obj2 && typeof obj2 === 'object') {\n        Object.keys(obj2).forEach(function forEach(key) {\n            // if we already have this key and it isn't an array,\n            // make it one array of the same element.\n            if (merged.hasOwnProperty(key) && !(merged[key] instanceof Array)) {\n                merged[key] = [merged[key]];\n\n                // push the new value down\n                merged[key].push(obj2[key]);\n            } else {\n                // otherwise just set it\n                merged[key] = obj2[key];\n            }\n        });\n    }\n\n    return merged;\n}\n\n///--- Exports\n\nmodule.exports = {\n    shallowCopy: shallowCopy,\n    mergeQs: mergeQs\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"author\": \"Mark Cavage <mcavage@gmail.com>\",\n  \"contributors\": [\n    \"Adam Argo\",\n    \"Alex Liu\",\n    \"Alexander Olsson\",\n    \"Andrew Robinson\",\n    \"Andrew Sliwinski\",\n    \"Anro Robinson\",\n    \"Armin Tamzarian\",\n    \"Asa Ayers\",\n    \"Bastiaan Marinus van de Weerd\",\n    \"Ben Doerr\",\n    \"Ben Hale\",\n    \"Ben Howes\",\n    \"Ben Hutchison\",\n    \"Benjamine Coe\",\n    \"Benjamin Urban\",\n    \"Blake VanLandingham\",\n    \"Brian Pin\",\n    \"Bryan Donovan\",\n    \"Bryce Kahle\",\n    \"Christopher Cannell\",\n    \"Clément Désiles\",\n    \"Colin O'Brien\",\n    \"Corbin Uselton\",\n    \"Diego Torres\",\n    \"Domenic Denicola\",\n    \"Domikik Lessel\",\n    \"Dominic Barnes\",\n    \"Erik Kristensen\",\n    \"Falco Nogatz\",\n    \"Gergely Nemeth\",\n    \"Guillaume Chauvet\",\n    \"Ifiok Idiang\",\n    \"Isaac Schlueter\",\n    \"Jacob Quatier\",\n    \"James O'Cull\",\n    \"James Womack\",\n    \"Jonathan Dahan\",\n    \"Josh Clulow\",\n    \"Jorge Serrano\",\n    \"Jason Ghent\",\n    \"Khaja Naquiuddin\",\n    \"Lou Sacco\",\n    \"Matt Smillie\",\n    \"Mattijs Spierings\",\n    \"Micah Ransdell\",\n    \"Michal Moskal\",\n    \"Michael Paulson\",\n    \"Mike Williams\",\n    \"Nathanael Anderson\",\n    \"Patrick Mooney\",\n    \"Paul Bouzakis\",\n    \"Pedro Palazón\",\n    \"Quentin Buathier\",\n    \"Richardo Stuven\",\n    \"Scott Turnquest\",\n    \"Shaun Berryman\",\n    \"Steve Mason\",\n    \"Tim Kuijsten\",\n    \"Trent Mick\",\n    \"Tuure Kanuisto\",\n    \"Will Prater\",\n    \"Yunong Xiao\",\n    \"Zachary Snow\"\n  ],\n  \"name\": \"restify\",\n  \"homepage\": \"http://restify.com\",\n  \"description\": \"REST framework\",\n  \"keywords\": [\n    \"REST\",\n    \"framework\",\n    \"express\",\n    \"DTrace\"\n  ],\n  \"version\": \"11.2.0\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/restify/node-restify.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/restify/node-restify/issues\"\n  },\n  \"main\": \"lib/index.js\",\n  \"directories\": {\n    \"lib\": \"./lib\"\n  },\n  \"bin\": {\n    \"report-latency\": \"./bin/report-latency\"\n  },\n  \"engines\": {\n    \"node\": \">=10.0.0\"\n  },\n  \"dependencies\": {\n    \"assert-plus\": \"^1.0.0\",\n    \"csv\": \"^6.2.2\",\n    \"escape-regexp-component\": \"^1.0.2\",\n    \"ewma\": \"^2.0.1\",\n    \"find-my-way\": \"^7.6.0\",\n    \"formidable\": \"^1.2.1\",\n    \"http-signature\": \"^1.3.6\",\n    \"lodash\": \"^4.17.11\",\n    \"lru-cache\": \"^7.14.1\",\n    \"mime\": \"^3.0.0\",\n    \"negotiator\": \"^0.6.2\",\n    \"once\": \"^1.4.0\",\n    \"pidusage\": \"^3.0.2\",\n    \"pino\": \"^8.7.0\",\n    \"qs\": \"^6.7.0\",\n    \"restify-errors\": \"^8.0.2\",\n    \"semver\": \"^7.3.8\",\n    \"send\": \"^0.18.0\",\n    \"spdy\": \"^4.0.0\",\n    \"uuid\": \"^9.0.0\",\n    \"vasync\": \"^2.2.0\"\n  },\n  \"optionalDependencies\": {\n    \"dtrace-provider\": \"~0.8\"\n  },\n  \"devDependencies\": {\n    \"autocannon\": \"^4.0.0\",\n    \"autocannon-compare\": \"^0.4.0\",\n    \"chai\": \"^4.2.0\",\n    \"coveralls\": \"^3.0.3\",\n    \"documentation\": \"^11.0.0\",\n    \"eslint\": \"^5.16.0\",\n    \"eslint-config-prettier\": \"^4.3.0\",\n    \"eslint-plugin-jsdoc\": \"^3.15.1\",\n    \"eslint-plugin-prettier\": \"^3.1.0\",\n    \"glob\": \"^7.1.4\",\n    \"inquirer\": \"^3.3.0\",\n    \"mkdirp\": \"^0.5.1\",\n    \"mocha\": \"^7.1.1\",\n    \"nodeunit\": \"^0.11.3\",\n    \"nyc\": \"^15.0.0\",\n    \"ora\": \"^1.3.0\",\n    \"pre-commit\": \"^1.2.2\",\n    \"prettier\": \"^1.17.1\",\n    \"proxyquire\": \"^1.8.0\",\n    \"restify-clients\": \"^2.6.6\",\n    \"rimraf\": \"^2.6.3\",\n    \"sinon\": \"^7.5.0\",\n    \"validator\": \"^7.2.0\",\n    \"watershed\": \"^0.4.0\"\n  },\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"test\": \"make prepush\"\n  }\n}\n"
  },
  {
    "path": "test/.eslintrc",
    "content": "{\n    \"rules\": {\n        \"handle-callback-err\": [ 0 ]\n    }\n}\n"
  },
  {
    "path": "test/chain.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar domain = require('domain');\nvar Chain = require('../lib/chain');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar test = helper.test;\n\ntest('calls all the handlers', function(t) {\n    var chain = new Chain();\n    var counter = 0;\n\n    chain.add(function(req, res, next) {\n        counter++;\n        next();\n    });\n    chain.add(function(req, res, next) {\n        counter++;\n        next();\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function() {\n            t.equal(counter, 2);\n            t.done();\n        }\n    );\n});\n\ntest('abort with Error in next', function(t) {\n    var chain = new Chain();\n    var counter = 0;\n    var myError = new Error('Foo');\n\n    chain.add(function(req, res, next) {\n        counter++;\n        next(myError);\n    });\n    chain.add(function(req, res, next) {\n        counter++;\n        next();\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function(err) {\n            t.deepEqual(err, myError);\n            t.equal(counter, 1);\n            t.done();\n        }\n    );\n});\n\ntest('abort with false in next', function(t) {\n    var chain = new Chain();\n\n    chain.add(function(req, res, next) {\n        next(false);\n    });\n    chain.add(function(req, res, next) {\n        t.fail('Should not be here');\n        next();\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return false;\n            }\n        },\n        {},\n        function(err) {\n            t.equal(err, false);\n            t.done();\n        }\n    );\n});\n\ntest('abort with closed request', function(t) {\n    var chain = new Chain();\n    var closed = false;\n\n    chain.add(function(req, res, next) {\n        closed = true;\n        next();\n    });\n    chain.add(function(req, res, next) {\n        t.fail('Should not be here');\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return closed ? 'close' : '';\n            }\n        },\n        {},\n        function(err) {\n            t.ifError(err);\n            t.done();\n        }\n    );\n});\n\ntest('cals error middleware', function(t) {\n    t.expect(2);\n    var chain = new Chain();\n    var myError = new Error('Foo');\n\n    chain.add(function(req, res, next) {\n        next(myError);\n    });\n    chain.add(function(err, req, res, next) {\n        t.deepEqual(err, myError);\n        next(err);\n    });\n    chain.add(function(req, res, next) {\n        t.fail('Should not be here');\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function(err) {\n            t.deepEqual(err, myError);\n            t.done();\n        }\n    );\n});\n\ntest('onceNext prevents double next calls', function(t) {\n    var doneCalled = 0;\n    var chain = new Chain({\n        onceNext: true\n    });\n\n    chain.add(function foo(req, res, next) {\n        next();\n        next();\n    });\n\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function(err) {\n            t.ifError(err);\n            doneCalled++;\n            t.equal(doneCalled, 1);\n            t.done();\n        }\n    );\n});\n\ntest('throws error for double next calls in strictNext mode', function(t) {\n    t.expect(1);\n    var chain = new Chain({\n        strictNext: true\n    });\n\n    chain.add(function foo(req, res, next) {\n        next();\n        next();\n    });\n\n    var testDomain = domain.create();\n\n    testDomain.on('error', function onError(err) {\n        t.equal(err.message, \"next shouldn't be called more than once\");\n        testDomain.exit();\n        t.done();\n    });\n\n    testDomain.run(function run() {\n        chain.run(\n            {\n                startHandlerTimer: function() {},\n                endHandlerTimer: function() {},\n                connectionState: function() {\n                    return '';\n                }\n            },\n            {},\n            function(err) {\n                t.ifError(err);\n            }\n        );\n    });\n});\n\ntest('calls req.startHandlerTimer', function(t) {\n    var chain = new Chain();\n\n    chain.add(function foo(req, res, next) {\n        next();\n    });\n\n    chain.run(\n        {\n            startHandlerTimer: function(handleName) {\n                t.equal(handleName, 'foo');\n                t.done();\n            },\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function() {}\n    );\n});\n\ntest('calls req.endHandlerTimer', function(t) {\n    var chain = new Chain();\n\n    chain.add(function foo(req, res, next) {\n        next();\n    });\n\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function(handleName) {\n                t.equal(handleName, 'foo');\n                t.done();\n            },\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function() {}\n    );\n});\n\ntest('count returns with the number of registered handlers', function(t) {\n    var chain = new Chain();\n    chain.add(function(req, res, next) {});\n    chain.add(function(req, res, next) {});\n    t.equal(chain.count(), 2);\n    t.end();\n});\n\ntest('getHandlers returns with the array of handlers', function(t) {\n    var chain = new Chain();\n    var handlers = [function(req, res, next) {}, function(req, res, next) {}];\n    chain.add(handlers[0]);\n    chain.add(handlers[1]);\n    t.deepEqual(chain.getHandlers(), handlers);\n    t.end();\n});\n\ntest('waits async handlers', function(t) {\n    const chain = new Chain();\n    let counter = 0;\n\n    chain.add(async function(req, res) {\n        await helper.sleep(50);\n        counter++;\n    });\n    chain.add(function(req, res, next) {\n        counter++;\n        next();\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function() {\n            t.equal(counter, 2);\n            t.done();\n        }\n    );\n});\n\ntest('abort with rejected promise', function(t) {\n    const myError = new Error('Foo');\n    const chain = new Chain();\n    let counter = 0;\n\n    chain.add(async function(req, res) {\n        counter++;\n        await helper.sleep(10);\n        return Promise.reject(myError);\n    });\n    chain.add(function(req, res, next) {\n        counter++;\n        next();\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function(err) {\n            t.deepEqual(err, myError);\n            t.equal(counter, 1);\n            t.done();\n        }\n    );\n});\n\ntest('abort with rejected promise without error', function(t) {\n    const chain = new Chain();\n    let counter = 0;\n\n    chain.add(async function(req, res) {\n        counter++;\n        await helper.sleep(10);\n        return Promise.reject();\n    });\n    chain.add(function(req, res, next) {\n        counter++;\n        next();\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            },\n            path: function() {\n                return '/';\n            }\n        },\n        {},\n        function(err) {\n            t.ok(typeof err === 'object');\n            t.equal(err.name, 'AsyncError');\n            t.equal(err.jse_info.cause, undefined);\n            t.equal(counter, 1);\n            t.done();\n        }\n    );\n});\n\ntest('abort with throw inside async function', function(t) {\n    const myError = new Error('Foo');\n    const chain = new Chain();\n    let counter = 0;\n\n    chain.add(async function(req, res) {\n        counter++;\n        await helper.sleep(10);\n        throw myError;\n    });\n    chain.add(function(req, res, next) {\n        counter++;\n        next();\n    });\n    chain.run(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function(err) {\n            t.deepEqual(err, myError);\n            t.equal(counter, 1);\n            t.done();\n        }\n    );\n});\n\ntest('fails to add non async function with arity 2', function(t) {\n    var handler = function getLunch(req, res) {\n        res.send('ok');\n    };\n    var chain = new Chain();\n    t.throws(function() {\n        chain.add(handler);\n    }, /getLunch/);\n    t.end();\n});\n\ntest('fails to add async function with arity 3', function(t) {\n    var handler = async function getBreakfast(req, res, next) {\n        res.send('ok');\n    };\n\n    var chain = new Chain();\n    t.throws(function() {\n        chain.add(handler);\n    }, /getBreakfast/);\n    t.end();\n});\n"
  },
  {
    "path": "test/chainComposer.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar test = helper.test;\nvar composer = require('../lib/helpers/chainComposer');\n\ntest('chainComposer creates a valid chain for a handler array ', function(t) {\n    var counter = 0;\n    var handlers = [];\n    handlers.push(function(req, res, next) {\n        counter++;\n        next();\n    });\n\n    handlers.push(function(req, res, next) {\n        counter++;\n        next();\n    });\n\n    var chain = composer(handlers);\n    chain(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function() {\n            t.equal(counter, 2);\n            t.done();\n        }\n    );\n});\n\ntest('chainComposer creates a valid chain for a single handler', function(t) {\n    var counter = 0;\n    var handlers = function(req, res, next) {\n        counter++;\n        next();\n    };\n\n    var chain = composer(handlers);\n    chain(\n        {\n            startHandlerTimer: function() {},\n            endHandlerTimer: function() {},\n            connectionState: function() {\n                return '';\n            }\n        },\n        {},\n        function() {\n            t.equal(counter, 1);\n            t.done();\n        }\n    );\n});\n"
  },
  {
    "path": "test/formatter-optional.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar restifyClients = require('restify-clients');\n\nvar restify = require('../lib');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar after = helper.after;\nvar before = helper.before;\nvar test = helper.test;\n\nvar CLIENT;\nvar LOCALHOST;\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar SERVER;\n\n///--- Tests\n\nbefore(function(callback) {\n    try {\n        SERVER = restify.createServer({\n            handleUncaughtExceptions: true,\n            log: helper.getLog('server'),\n            strictFormatters: false\n        });\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n            LOCALHOST = 'http://' + '127.0.0.1:' + PORT;\n            callback();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\nafter(function(callback) {\n    try {\n        SERVER.close(callback);\n        CLIENT.close();\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\ntest('send 200 on formatter missing and strictFormatters false', function(t) {\n    // When server is passed \"strictFormatters: false\" at creation time,\n    // res.send still sends a successful response even when a formatter is\n    // not set up for a specific content-type.\n    SERVER.get('/11', function handle(req, res, next) {\n        res.header('content-type', 'application/hal+json');\n        res.send(200, JSON.stringify({ hello: 'world' }));\n        return next();\n    });\n\n    CLIENT.get(LOCALHOST + '/11', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(res.headers['content-type'], 'application/hal+json');\n        t.end();\n    });\n});\n"
  },
  {
    "path": "test/formatter.test.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n/* eslint-disable func-names */\n\nvar restifyClients = require('restify-clients');\nvar errors = require('restify-errors');\nvar sinon = require('sinon');\n\nvar restify = require('../lib');\nvar jsonFormatter = require('../lib/formatters/json');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar after = helper.after;\nvar before = helper.before;\nvar test = helper.test;\n\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar CLIENT;\nvar SERVER;\n\n///--- Tests\n\nbefore(function(callback) {\n    try {\n        SERVER = restify.createServer({\n            handleUncaughtExceptions: true,\n            formatters: {\n                'text/sync': function(req, res, body) {\n                    return 'sync fmt';\n                },\n                'text/syncerror': function(req, res, body) {\n                    // this is a bad formatter, on purpose.\n                    return x.toString(); // eslint-disable-line no-undef\n                },\n                'text/syncerror_expected': function(req, res, body) {\n                    throw new errors.InternalServerError('Errors happen');\n                },\n                'application/foo; q=0.9': function(req, res, body) {\n                    return 'foo!';\n                },\n                'application/bar; q=0.1': function(req, res, body) {\n                    return 'bar!';\n                }\n            },\n            dtrace: helper.dtrace,\n            log: helper.getLog('server'),\n            version: ['2.0.0', '0.5.4', '1.4.3']\n        });\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createStringClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false,\n                agent: false\n            });\n            SERVER.get('/sync', function(req, res, next) {\n                res.send('dummy response');\n                return next();\n            });\n            SERVER.get('/missingFormatter', function(req, res, next) {\n                delete res.formatters['application/octet-stream'];\n                res.setHeader('content-type', 'text/html');\n                res.send('dummy response');\n                return next();\n            });\n            SERVER.get('/jsonpSeparators', function(req, res, next) {\n                res.setHeader('content-type', 'application/javascript');\n                res.send(\n                    String.fromCharCode(0x2028) + String.fromCharCode(0x2029)\n                );\n                return next();\n            });\n            process.nextTick(callback);\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\nafter(function(callback) {\n    try {\n        SERVER.close(callback);\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\ntest('GH-845: sync formatter', function(t) {\n    CLIENT.get(\n        {\n            path: '/sync',\n            headers: {\n                accept: 'text/sync'\n            }\n        },\n        function(err, req, res, data) {\n            t.ifError(err);\n            t.ok(req);\n            t.ok(res);\n            t.equal(data, 'sync fmt');\n            t.end();\n        }\n    );\n});\n\ntest('GH-845: sync formatter should blow up', function(t) {\n    SERVER.once('uncaughtException', function(req, res, route, err) {\n        t.ok(err);\n        t.equal(err.name, 'ReferenceError');\n        t.equal(err.message, 'x is not defined');\n        res.write('uncaughtException');\n        res.end();\n    });\n\n    CLIENT.get(\n        {\n            path: '/sync',\n            headers: {\n                accept: 'text/syncerror'\n            }\n        },\n        function(err, req, res, data) {\n            t.equal(data, 'uncaughtException');\n            t.end();\n        }\n    );\n});\n\ntest('sync formatter should handle expected errors gracefully', function(t) {\n    SERVER.once('uncaughtException', function(req, res, route, err) {\n        throw new Error('Should not reach');\n    });\n\n    CLIENT.get(\n        {\n            path: '/sync',\n            headers: {\n                accept: 'text/syncerror_expected'\n            }\n        },\n        function(err, req, res, data) {\n            t.ok(err);\n            t.ok(req);\n            t.ok(res);\n            t.equal(res.statusCode, 500);\n            SERVER.removeAllListeners('uncaughtException');\n            t.end();\n        }\n    );\n});\n\ntest('q-val priority', function(t) {\n    var opts = {\n        path: '/sync',\n        headers: {\n            accept: 'application/*'\n        }\n    };\n    CLIENT.get(opts, function(err, req, res, data) {\n        t.ifError(err);\n        t.ok(req);\n        t.ok(res);\n        t.equal(data, 'foo!');\n        t.end();\n    });\n});\n\ntest('GH-771 q-val priority on */*', function(t) {\n    var opts = {\n        path: '/sync',\n        headers: {\n            accept: '*/*'\n        }\n    };\n\n    // this test is a little flaky - it will look for first formatter that\n    // satisfies q-val but in this test we have a bunch of bad formatters.\n    // it appears V8 will use the first found formatter (this case, text/sync).\n    CLIENT.get(opts, function(err, req, res, data) {\n        t.ifError(err);\n        t.ok(req);\n        t.ok(res);\n        t.equal(data, 'sync fmt');\n        t.end();\n    });\n});\n\ntest(\n    'GH-937 should return 406 when no content-type header set on response ' +\n        'matching an acceptable type found by matching client',\n    function(t) {\n        // ensure client accepts only a type not specified by server\n        var opts = {\n            path: '/sync',\n            headers: {\n                accept: 'text/html'\n            }\n        };\n\n        CLIENT.get(opts, function(err, req, res, data) {\n            t.ok(err);\n            t.ok(req);\n            t.ok(res);\n            t.equal(res.statusCode, 406);\n            t.end();\n        });\n    }\n);\n\ntest(\n    'GH-937 should return 500 when no default formatter found ' +\n        'and octet-stream is not available',\n    function(t) {\n        // ensure client accepts only a type not specified by server\n        var opts = {\n            path: '/missingFormatter',\n            headers: {\n                accept: 'text/html'\n            }\n        };\n\n        CLIENT.get(opts, function(err, req, res, data) {\n            t.ok(err);\n            t.ok(req);\n            t.ok(res);\n            t.equal(res.statusCode, 500);\n            t.end();\n        });\n    }\n);\n\n// eslint-disable-next-line\ntest('default jsonp formatter should escape line and paragraph separators', function(t) {\n    // ensure client accepts only a type not specified by server\n    var opts = {\n        path: '/jsonpSeparators',\n        headers: {\n            accept: 'application/javascript'\n        }\n    };\n\n    CLIENT.get(opts, function(err, req, res, data) {\n        t.ifError(err);\n        t.ok(req);\n        t.ok(res);\n        t.equal(data, '\"\\\\u2028\\\\u2029\"');\n        t.end();\n    });\n});\n\n// eslint-disable-next-line\ntest('default json formatter should wrap & throw InternalServer error on unserializable bodies', function(t) {\n    t.expect(2);\n\n    sinon.spy(JSON, 'stringify');\n\n    SERVER.once('uncaughtException', function(req, res, route, err) {\n        console.log(err.stack); // For convenience\n        throw new Error('Should not reach');\n    });\n\n    var opts = {\n        path: '/badJSON',\n        name: 'badJSON'\n    };\n\n    SERVER.get(opts, function(req, res, next) {\n        var body = {};\n        // Add unserializable circular reference\n        body.body = body;\n\n        try {\n            jsonFormatter(req, res, body);\n            throw new Error('Should not reach');\n        } catch (e) {\n            t.ok(e instanceof errors.InternalServerError);\n            t.ok(JSON.stringify.threw(e.cause()));\n        }\n\n        res.send();\n    });\n\n    CLIENT.get('/badJSON', function(err, req, res, data) {\n        SERVER.rm('badJSON');\n        SERVER.removeAllListeners('uncaughtException');\n        JSON.stringify.restore();\n        t.end();\n    });\n});\n"
  },
  {
    "path": "test/index.test.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n/* eslint-disable func-names */\n\nvar httpDate = require('../lib/http_date');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar test = helper.test;\n\n///--- Tests\n\ntest('httpDate', function(t) {\n    var d = httpDate();\n    var regex = /\\w{3}, \\d{1,2} \\w{3} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT/;\n    t.ok(regex.test(d));\n    t.end();\n});\n"
  },
  {
    "path": "test/keys/http2-cert.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIICHzCCAYgCCQCPPSUAa8QZojANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJS\nVTETMBEGA1UECBMKU29tZS1TdGF0ZTENMAsGA1UEBxMET21zazEhMB8GA1UEChMY\nSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTExMDQwOTEwMDY0NVoXDTExMDUw\nOTEwMDY0NVowVDELMAkGA1UEBhMCUlUxEzARBgNVBAgTClNvbWUtU3RhdGUxDTAL\nBgNVBAcTBE9tc2sxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCB\nnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1bn25sPkv46wl70BffxradlkRd/x\np5Xf8HDhPSfzNNctERYslXT2fX7Dmfd5w1XTVqqGqJ4izp5VewoVOHA8uavo3ovp\ngNWasil5zADWaM1T0nnV0RsFbZWzOTmm1U3D48K8rW3F5kOZ6f4yRq9QT1gF/gN7\n5Pt494YyYyJu/a8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBuRZisIViI2G/R+w79\nvk21TzC/cJ+O7tKsseDqotXYTH8SuimEH5IWcXNgnWhNzczwN8s2362NixyvCipV\nyd4wzMpPbjIhnWGM0hluWZiK2RxfcqimIBjDParTv6CMUIuwGQ257THKY8hXGg7j\nUws6Lif3P9UbsuRiYPxMgg98wg==\n-----END CERTIFICATE-----\n\n"
  },
  {
    "path": "test/keys/http2-csr.pem",
    "content": "-----BEGIN CERTIFICATE REQUEST-----\nMIIBkzCB/QIBADBUMQswCQYDVQQGEwJSVTETMBEGA1UECBMKU29tZS1TdGF0ZTEN\nMAsGA1UEBxMET21zazEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF\n3/Gnld/wcOE9J/M01y0RFiyVdPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+je\ni+mA1ZqyKXnMANZozVPSedXRGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+\nA3vk+3j3hjJjIm79rwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAiNWhz6EppIVa\nFfUaB3sLeqfamb9tg9kBHtvqj/FJni0snqms0kPWaTySEPHZF0irIb7VVdq/sVCb\n3gseMVSyoDvPJ4lHC3PXqGQ7kM1mIPhDnR/4HDA3BhlGhTXSDIHgZnvI+HMBdsyC\nhC3dz5odyKqe4nmoofomALkBL9t4H8s=\n-----END CERTIFICATE REQUEST-----\n\n"
  },
  {
    "path": "test/keys/http2-key.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF3/Gnld/wcOE9J/M01y0RFiyV\ndPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+jei+mA1ZqyKXnMANZozVPSedXR\nGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+A3vk+3j3hjJjIm79rwIDAQAB\nAoGAAv2QI9h32epQND9TxwSCKD//dC7W/cZOFNovfKCTeZjNK6EIzKqPTGA6smvR\nC1enFl5adf+IcyWqAoe4lkqTvurIj+2EhtXdQ8DBlVuXKr3xvEFdYxXPautdTCF6\nKbXEyS/s1TZCRFjYftvCrXxc3pK45AQX/wg7z1K+YB5pyIECQQD0OJvLoxLYoXAc\nFZraIOZiDsEbGuSHqoCReFXH75EC3+XGYkH2bQ/nSIZ0h1buuwQ/ylKXOlTPT3Qt\nXm1OQEBvAkEA4AjWsIO/rRpOm/Q2aCrynWMpoUXTZSbL2yGf8pxp/+8r2br5ier0\nM1LeBb/OPY1+k39NWLXxQoo64xoSFYk2wQJAd2wDCwX4HkR7HNCXw1hZL9QFK6rv\n20NN0VSlpboJD/3KT0MW/FiCcVduoCbaJK0Au+zEjDyy4hj5N4I4Mw6KMwJAXVAx\nI+psTsxzS4/njXG+BgIEl/C+gRYsuMQDnAi8OebDq/et8l0Tg8ETSu++FnM18neG\nntmBeMacinUUbTXuwQJBAJp/onZdsMzeVulsGrqR1uS+Lpjc5Q1gt5ttt2cxj91D\nrio48C/ZvWuKNE8EYj2ALtghcVKRvgaWfOxt2GPguGg=\n-----END RSA PRIVATE KEY-----\n\n"
  },
  {
    "path": "test/lib/helper.js",
    "content": "// Copyright 2012 Mark Cavage.  All rights reserved.\n//\n// Just a simple wrapper over nodeunit's exports syntax. Also exposes\n// a common logger for all tests.\n//\n\n'use strict';\n/* eslint-disable func-names */\n\nvar domain = require('domain');\n\nvar pino = require('pino');\nvar once = require('once');\n\n///--- Exports\n\nmodule.exports = {\n    after: function after(teardown) {\n        module.parent.exports.tearDown = function _teardown(callback) {\n            var d = domain.create();\n            var self = this;\n\n            d.once('error', function(err) {\n                console.error('after: uncaught error\\n', err.stack);\n                process.exit(1);\n            });\n\n            d.run(function() {\n                teardown.call(self, once(callback));\n            });\n        };\n    },\n\n    before: function before(setup) {\n        module.parent.exports.setUp = function _setup(callback) {\n            var d = domain.create();\n            var self = this;\n\n            d.once('error', function(err) {\n                console.error('before: uncaught error\\n' + err.stack);\n                process.exit(1);\n            });\n\n            d.run(function() {\n                setup.call(self, once(callback));\n            });\n        };\n    },\n\n    test: function test(name, tester) {\n        module.parent.exports[name] = function _(t) {\n            var d = domain.create();\n            var self = this;\n\n            d.once('error', function(err) {\n                t.ifError(err);\n                t.end();\n            });\n\n            d.add(t);\n            d.run(function() {\n                t.end = once(function() {\n                    t.done();\n                });\n                t.notOk = function notOk(ok, message) {\n                    return t.ok(!ok, message);\n                };\n\n                tester.call(self, t);\n            });\n        };\n    },\n\n    getLog: function(name, streams, level) {\n        return pino(\n            {\n                level: process.env.LOG_LEVEL || level || 'fatal',\n                name: name || process.argv[1],\n                serializers: pino.stdSerializers\n            },\n            streams || process.stdout\n        );\n    },\n\n    get dtrace() {\n        return true;\n    },\n\n    sleep: function sleep(timeInMs) {\n        return new Promise(function sleepPromise(resolve) {\n            setTimeout(function timeout() {\n                resolve();\n            }, timeInMs);\n        });\n    }\n};\n"
  },
  {
    "path": "test/lib/server-withDisableUncaughtException.js",
    "content": "// A simple node process that will start a restify server with the\n// uncaughtException handler disabled. Responds to a 'serverPortRequest' message\n// and sends back the server's bound port number.\n\n'use strict';\n/* eslint-disable func-names */\n\nvar restify = require('../../lib');\n\nfunction main() {\n    var port = process.env.UNIT_TEST_PORT || 0;\n    var server = restify.createServer({ handleUncaughtExceptions: false });\n    server.get('/', function(req, res, next) {\n        throw new Error('Catch me!');\n    });\n    server.listen(0, function() {\n        port = server.address().port;\n        console.log('port: ', port);\n\n        process.on('message', function(msg) {\n            if (msg.task !== 'serverPortRequest') {\n                process.send({ error: 'Unexpected message: ' + msg });\n                return;\n            }\n            process.send({ task: 'serverPortResponse', port: port });\n        });\n    });\n}\n\nif (require.main === module) {\n    main();\n}\n"
  },
  {
    "path": "test/lib/streamRecorder.js",
    "content": "'use strict';\n\nconst stream = require('stream');\n\nclass StreamRecorder extends stream.Writable {\n    constructor(options) {\n        options = options || {};\n        super(options);\n        this.flushRecords();\n    }\n\n    _write(chunk, encoding, callback) {\n        const record = JSON.parse(chunk.toString());\n        this.records.push(record);\n        callback();\n    }\n\n    flushRecords() {\n        this.records = [];\n    }\n}\n\nmodule.exports = StreamRecorder;\n"
  },
  {
    "path": "test/plugins/.eslintrc",
    "content": "{\n    env: {\n        mocha: true\n    }\n}\n"
  },
  {
    "path": "test/plugins/accept.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('accept parser', function() {\n    before(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.use(restify.plugins.acceptParser(SERVER.acceptable));\n\n        SERVER.get('/', function respond(req, res, next) {\n            res.send();\n            next();\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    after(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('accept ok', function(done) {\n        CLIENT.get('/', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('accept not ok (406)', function(done) {\n        var opts = {\n            path: '/',\n            headers: {\n                accept: 'foo/bar'\n            }\n        };\n\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ok(err);\n            assert.equal(err.name, 'NotAcceptableError');\n            assert.equal(res.statusCode, 406);\n            done();\n        });\n    });\n\n    it('GH-1619: should fire NotAcceptable event on server', function(done) {\n        var opts = {\n            path: '/',\n            headers: {\n                accept: 'foo/bar'\n            }\n        };\n        var evtFired = false;\n\n        SERVER.on('NotAcceptable', function(req, res, err, cb) {\n            evtFired = true;\n            return cb();\n        });\n\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ok(err);\n            assert.equal(err.name, 'NotAcceptableError');\n            assert.equal(res.statusCode, 406);\n            assert.isTrue(evtFired);\n            return done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/audit.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// runtime modules\nvar PassThrough = require('stream').PassThrough;\n\n// external requires\nvar assert = require('chai').assert;\nvar pino = require('pino');\nvar lodash = require('lodash');\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\nvar StreamRecorder = require('../lib/streamRecorder');\nvar vasync = require('vasync');\n\n// local globals\nvar MILLISECOND_IN_MICROSECONDS = 1000;\nvar TOLERATED_MICROSECONDS = MILLISECOND_IN_MICROSECONDS;\nvar SERVER;\nvar CLIENT;\nvar PORT;\nlet LOG_BUFFER;\n\nfunction assertIsAtLeastWithTolerate(num1, num2, tolerate, msg) {\n    assert.isAtLeast(num1, num2 - tolerate, msg + 'should be >= ' + num2);\n}\n\ndescribe('audit logger', function() {\n    beforeEach(function(done) {\n        LOG_BUFFER = new StreamRecorder();\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server', LOG_BUFFER, 'info')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('audit logger should print log by default', function(done) {\n        var collectLog;\n        SERVER.on(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                server: SERVER,\n                event: 'after'\n            })\n        );\n\n        SERVER.get('/foo', function(req, res, next) {\n            res.send(200, { testdata: 'foo' });\n            next();\n        });\n\n        SERVER.get('/bar', function(req, res, next) {\n            res.send(200, { testdata: 'bar' });\n            next();\n        });\n\n        SERVER.get('/auditrecords', function(req, res, next) {\n            // strip log records of req/res as they will cause\n            // serialization issues.\n            var data = LOG_BUFFER.records.map(function(record) {\n                return lodash.omit(record, 'req', 'res');\n            }, []);\n            res.send(200, data);\n            next();\n        });\n\n        collectLog = function() {\n            CLIENT.get('/auditrecords', function(err, req, res) {\n                assert.ifError(err);\n                var data = JSON.parse(res.body);\n                assert.ok(data);\n                data.forEach(function(d) {\n                    assert.isNumber(d.latency);\n                });\n                done();\n            });\n        };\n\n        vasync.forEachParallel(\n            {\n                func: function clientRequest(urlPath, callback) {\n                    CLIENT.get(urlPath, function(err, req, res) {\n                        assert.ifError(err);\n                        assert.ok(JSON.parse(res.body));\n                        return callback(err, JSON.parse(res.body));\n                    });\n                },\n                inputs: ['/foo', '/bar']\n            },\n            function(err, results) {\n                assert.ifError(err);\n                collectLog();\n            }\n        );\n    });\n\n    it('test audit logger emit', function(done) {\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                server: SERVER,\n                event: 'after'\n            })\n        );\n\n        SERVER.once('audit', function(data) {\n            assert.ok(data);\n            assert.ok(data.req_id);\n            assert.equal(\n                data.req.url,\n                '/audit',\n                'request url should be /audit'\n            );\n            assert.isNumber(data.latency);\n            done();\n        });\n\n        SERVER.get('/audit', [\n            restify.plugins.queryParser(),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        ]);\n\n        CLIENT.get('/audit', function(err, req, res) {\n            assert.ifError(err);\n        });\n    });\n\n    it('test custom serializers', function(done) {\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                event: 'after',\n                serializers: {\n                    req: function(req) {\n                        return { fooReq: 'barReq' };\n                    },\n                    res: function(res) {\n                        return { fooRes: 'barRes' };\n                    }\n                }\n            })\n        );\n\n        SERVER.get('/audit', function aTestHandler(req, res, next) {\n            res.send('');\n            return next();\n        });\n\n        SERVER.on('after', function() {\n            var record = LOG_BUFFER.records && LOG_BUFFER.records[0];\n            assert.equal(record.req.fooReq, 'barReq');\n            assert.equal(record.res.fooRes, 'barRes');\n            done();\n        });\n\n        CLIENT.get('/audit', function(err, req, res) {\n            assert.ifError(err);\n        });\n    });\n\n    it('should log handler timers', function(done) {\n        var WAIT_IN_MILLISECONDS = 1100;\n\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                event: 'after'\n            })\n        );\n\n        SERVER.get('/audit', function aTestHandler(req, res, next) {\n            req.startHandlerTimer('audit-sub');\n\n            setTimeout(function() {\n                req.endHandlerTimer('audit-sub');\n                res.send('');\n                return next();\n            }, WAIT_IN_MILLISECONDS);\n            // this really should be 1000 but make it 1100 so that the tests\n            // don't sporadically fail due to timing issues.\n        });\n\n        SERVER.on('after', function() {\n            var record = LOG_BUFFER.records && LOG_BUFFER.records[0];\n\n            // check timers\n            assert.ok(record, 'no log records');\n            assert.equal(\n                LOG_BUFFER.records.length,\n                1,\n                'should only have 1 log record'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers.aTestHandler,\n                WAIT_IN_MILLISECONDS * MILLISECOND_IN_MICROSECONDS,\n                TOLERATED_MICROSECONDS,\n                'atestHandler'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['aTestHandler-audit-sub'],\n                WAIT_IN_MILLISECONDS * MILLISECOND_IN_MICROSECONDS,\n                TOLERATED_MICROSECONDS,\n                'aTestHandler-audit-sub'\n            );\n\n            var handlers = Object.keys(record.req.timers);\n            assert.equal(\n                handlers[handlers.length - 2],\n                'aTestHandler-audit-sub',\n                'sub handler timer not in order'\n            );\n            assert.equal(\n                handlers[handlers.length - 1],\n                'aTestHandler',\n                'aTestHandler not last'\n            );\n            done();\n        });\n\n        CLIENT.get('/audit', function(err, req, res) {\n            assert.ifError(err);\n        });\n    });\n\n    it('should log anonymous handler timers', function(done) {\n        this.timeout(5000);\n\n        var WAIT_IN_MILLISECONDS = 1000;\n\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                event: 'after'\n            })\n        );\n\n        SERVER.pre(function(req, res, next) {\n            next();\n        });\n        SERVER.pre(function(req, res, next) {\n            next();\n        });\n\n        SERVER.use(function(req, res, next) {\n            next();\n        });\n        SERVER.use(function(req, res, next) {\n            next();\n        });\n\n        SERVER.get(\n            '/audit',\n            function(req, res, next) {\n                setTimeout(function() {\n                    return next();\n                }, WAIT_IN_MILLISECONDS);\n            },\n            function(req, res, next) {\n                req.startHandlerTimer('audit-sub');\n\n                setTimeout(function() {\n                    req.endHandlerTimer('audit-sub');\n                    res.send('');\n                    return next();\n                }, WAIT_IN_MILLISECONDS);\n            }\n        );\n\n        SERVER.on('after', function() {\n            // check timers\n            var record = LOG_BUFFER.records && LOG_BUFFER.records[0];\n            assert.ok(record, 'no log records');\n            assert.equal(\n                LOG_BUFFER.records.length,\n                1,\n                'should only have 1 log record'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['pre-0'],\n                0,\n                TOLERATED_MICROSECONDS,\n                'pre-0'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['pre-1'],\n                0,\n                TOLERATED_MICROSECONDS,\n                'pre-1'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['use-0'],\n                0,\n                TOLERATED_MICROSECONDS,\n                'use-0'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['use-1'],\n                0,\n                TOLERATED_MICROSECONDS,\n                'use-1'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['handler-0'],\n                WAIT_IN_MILLISECONDS * MILLISECOND_IN_MICROSECONDS,\n                TOLERATED_MICROSECONDS,\n                'handler-0'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['handler-1'],\n                WAIT_IN_MILLISECONDS * MILLISECOND_IN_MICROSECONDS,\n                TOLERATED_MICROSECONDS,\n                'handler-1'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['handler-1-audit-sub'],\n                WAIT_IN_MILLISECONDS * MILLISECOND_IN_MICROSECONDS,\n                TOLERATED_MICROSECONDS,\n                'handler-0-audit-sub'\n            );\n\n            var handlers = Object.keys(record.req.timers);\n            assert.equal(\n                handlers[handlers.length - 2],\n                'handler-1-audit-sub',\n                'sub handler timer not in order'\n            );\n            assert.equal(\n                handlers[handlers.length - 1],\n                'handler-1',\n                'handler-1 not last'\n            );\n            done();\n        });\n\n        CLIENT.get('/audit', function(err, req, res) {\n            assert.ifError(err);\n        });\n    });\n\n    it('restify-GH-1435 should accumulate log handler timers', function(done) {\n        // capture the log record\n        var WAIT_IN_MILLISECONDS = 1100;\n\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                event: 'after'\n            })\n        );\n\n        SERVER.get('/audit', function aTestHandler(req, res, next) {\n            req.startHandlerTimer('audit-acc');\n\n            setTimeout(function() {\n                req.endHandlerTimer('audit-acc');\n                // Very brief timing for same name\n                req.startHandlerTimer('audit-acc');\n                req.endHandlerTimer('audit-acc');\n                res.send('');\n                return next();\n            }, WAIT_IN_MILLISECONDS);\n            // this really should be 1000 but make it 1100 so that the tests\n            // don't sporadically fail due to timing issues.\n        });\n\n        SERVER.on('after', function() {\n            var record = LOG_BUFFER.records && LOG_BUFFER.records[0];\n\n            // check timers\n            assert.ok(record, 'no log records');\n            assert.equal(\n                LOG_BUFFER.records.length,\n                1,\n                'should only have 1 log record'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers.aTestHandler,\n                WAIT_IN_MILLISECONDS * MILLISECOND_IN_MICROSECONDS,\n                TOLERATED_MICROSECONDS,\n                'atestHandler'\n            );\n            assertIsAtLeastWithTolerate(\n                record.req.timers['aTestHandler-audit-acc'],\n                WAIT_IN_MILLISECONDS * MILLISECOND_IN_MICROSECONDS,\n                TOLERATED_MICROSECONDS,\n                'aTestHandler-audit-acc'\n            );\n            done();\n        });\n\n        CLIENT.get('/audit', function(err, req, res) {\n            assert.ifError(err);\n        });\n    });\n\n    it('restify-GH-812 audit logger has query params string', function(done) {\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                event: 'after'\n            })\n        );\n\n        SERVER.get('/audit', function(req, res, next) {\n            res.send();\n            next();\n        });\n\n        SERVER.on('after', function() {\n            // check timers\n            assert.ok(LOG_BUFFER.records[0], 'no log records');\n            assert.equal(\n                LOG_BUFFER.records.length,\n                1,\n                'should only have 1 log record'\n            );\n            assert.ok(LOG_BUFFER.records[0].req.query, 'a=1&b=2');\n            done();\n        });\n\n        CLIENT.get('/audit?a=1&b=2', function(err, req, res) {\n            assert.ifError(err);\n        });\n    });\n\n    it('restify-GH-812 audit logger has query params obj', function(done) {\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                event: 'after'\n            })\n        );\n\n        SERVER.get('/audit', [\n            restify.plugins.queryParser(),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        ]);\n\n        SERVER.on('after', function() {\n            // check timers\n            assert.ok(LOG_BUFFER.records[0], 'no log records');\n            assert.equal(\n                LOG_BUFFER.records.length,\n                1,\n                'should only have 1 log record'\n            );\n            assert.deepEqual(LOG_BUFFER.records[0].req.query, {\n                a: '1',\n                b: '2'\n            });\n            done();\n        });\n\n        CLIENT.get('/audit?a=1&b=2', function(err, req, res) {\n            assert.ifError(err);\n        });\n    });\n\n    it('should work with pre events', function(done) {\n        var ptStream = new PassThrough();\n\n        SERVER.once(\n            'pre',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }, ptStream),\n                event: 'pre'\n            })\n        );\n\n        SERVER.get('/audit', [\n            restify.plugins.queryParser(),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        ]);\n\n        ptStream.on('data', function(data) {\n            var log = JSON.parse(data);\n            assert.equal('pre', log.component);\n            assert.ok(log.req_id);\n            assert.ok(log.req);\n            assert.ok(log.res);\n        });\n\n        CLIENT.get('/audit?a=1&b=2', function(err, req, res) {\n            assert.ifError(err);\n            done();\n        });\n    });\n\n    it('should work with routed events', function(done) {\n        var ptStream = new PassThrough();\n\n        SERVER.once(\n            'routed',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }, ptStream),\n                event: 'routed'\n            })\n        );\n\n        SERVER.get('/audit', [\n            restify.plugins.queryParser(),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        ]);\n\n        ptStream.on('data', function(data) {\n            var log = JSON.parse(data);\n            assert.equal('routed', log.component);\n            assert.ok(log.req_id);\n            assert.ok(log.req);\n            assert.ok(log.res);\n        });\n\n        CLIENT.get('/audit?a=1&b=2', function(err, req, res) {\n            assert.ifError(err);\n            done();\n        });\n    });\n\n    it('should work with custom context functions', function(done) {\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                context: function(req, res, route, err) {\n                    return {\n                        qs: req.getQuery()\n                    };\n                },\n                server: SERVER,\n                event: 'after'\n            })\n        );\n\n        SERVER.once('audit', function(data) {\n            assert.ok(data);\n            assert.ok(data.req_id);\n            assert.isNumber(data.latency);\n            assert.ok(data.context);\n            assert.equal(data.context.qs, 'foo=bar');\n            done();\n        });\n\n        SERVER.get('/audit', [\n            restify.plugins.queryParser(),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        ]);\n\n        CLIENT.get('/audit?foo=bar', function(err, req, res) {\n            assert.ifError(err);\n        });\n    });\n\n    it('should log 444 for closed request', function(done) {\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                server: SERVER,\n                event: 'after'\n            })\n        );\n\n        SERVER.once('audit', function(data) {\n            assert.ok(data);\n            assert.ok(data.req_id);\n            assert.isNumber(data.latency);\n            assert.equal(data.res.statusCode, 444);\n            done();\n        });\n\n        SERVER.get('/audit', function(req, res, next) {\n            setTimeout(function() {\n                res.send();\n                next();\n            }, 150);\n        });\n\n        CLIENT.get(\n            {\n                path: '/audit',\n                requestTimeout: 50\n            },\n            function(err, req, res) {}\n        );\n    });\n\n    it('should set request id using supplied field name', function(done) {\n        SERVER.once(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                server: SERVER,\n                event: 'after',\n                requestIdFieldName: 'traceId'\n            })\n        );\n\n        SERVER.once('audit', function(data) {\n            assert.ok(data);\n            assert.ok(data.traceId);\n            assert.notOk(data.req_id);\n            done();\n        });\n\n        SERVER.get('/audit', function(req, res, next) {\n            setTimeout(function() {\n                res.send();\n                next();\n            }, 150);\n        });\n\n        CLIENT.get(\n            {\n                path: '/audit',\n                requestTimeout: 50\n            },\n            function(err, req, res) {}\n        );\n    });\n});\n"
  },
  {
    "path": "test/plugins/authorization.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('authorization parser', function() {\n    before(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.use(restify.plugins.authorizationParser());\n\n        SERVER.get('/', function respond(req, res, next) {\n            res.send();\n            next();\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    after(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('should accept basic authorization', function(done) {\n        var authz = 'Basic ' + new Buffer('user:secret').toString('base64');\n        var opts = {\n            path: '/',\n            headers: {\n                authorization: authz\n            }\n        };\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should reject basic authorization', function(done) {\n        var opts = {\n            path: '/',\n            headers: {\n                authorization: 'Basic '\n            }\n        };\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ok(err);\n            assert.equal(err.name, 'InvalidHeaderError');\n            assert.equal(res.statusCode, 400);\n            done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/bodyReader.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// core requires\nvar http = require('http');\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('body reader', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    describe('gzip content encoding', function() {\n        it('should parse gzip encoded content', function(done) {\n            SERVER.use(restify.plugins.bodyParser());\n\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                retry: false,\n                gzip: {}\n            });\n\n            SERVER.post('/compressed', function(req, res, next) {\n                assert.equal(req.body.apple, 'red');\n                res.send();\n                next();\n            });\n\n            CLIENT.post(\n                '/compressed',\n                {\n                    apple: 'red'\n                },\n                function(err, _, res) {\n                    assert.ifError(err);\n                    assert.equal(res.statusCode, 200);\n                    done();\n                }\n            );\n        });\n\n        it('should not accept unsupported content encoding', function(done) {\n            SERVER.use(restify.plugins.bodyParser());\n\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                retry: false,\n                headers: {\n                    'content-encoding': 'unsupported'\n                }\n            });\n\n            SERVER.post('/compressed', function(req, res, next) {\n                assert.equal(req.body.apple, 'red');\n                res.send();\n                next();\n            });\n\n            CLIENT.post(\n                '/compressed',\n                {\n                    apple: 'red'\n                },\n                function(err, _, res) {\n                    assert.isOk(err, 'should fail');\n                    assert.equal(res.statusCode, 415);\n                    assert.equal(res.headers['accept-encoding'], 'gzip');\n                    done();\n                }\n            );\n        });\n\n        it('should parse unencoded content', function(done) {\n            SERVER.use(restify.plugins.bodyParser());\n\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                retry: false\n            });\n\n            SERVER.post('/compressed', function(req, res, next) {\n                assert.equal(req.body.apple, 'red');\n                res.send();\n                next();\n            });\n\n            CLIENT.post(\n                '/compressed',\n                {\n                    apple: 'red'\n                },\n                function(err, _, res) {\n                    assert.ifError(err);\n                    assert.equal(res.statusCode, 200);\n                    done();\n                }\n            );\n        });\n\n        it('should handle client timeout', function(done) {\n            SERVER.use(restify.plugins.bodyParser());\n\n            SERVER.post('/compressed', function(req, res, next) {\n                res.send('ok');\n                next();\n            });\n\n            // set timeout to 100ms so test runs faster, when client stops\n            // sending POST data\n            SERVER.on('connection', function(socket) {\n                socket.setTimeout(100);\n            });\n\n            var postData = 'hello world';\n\n            var options = {\n                hostname: '127.0.0.1',\n                port: PORT,\n                path: '/compressed?v=1',\n                method: 'POST',\n                headers: {\n                    'Content-Type': 'application/json',\n                    // report postData + 1 so that request isn't sent\n                    'Content-Length': Buffer.byteLength(postData) + 1\n                }\n            };\n\n            var req = http.request(options, function(res) {\n                // should never receive a response\n                assert.isNotOk(res);\n            });\n\n            SERVER.on('after', function(req2) {\n                if (req2.href() === '/compressed?v=2') {\n                    assert.equal(SERVER.inflightRequests(), 0);\n                    done();\n                }\n            });\n\n            // will get a req error after 100ms timeout\n            req.on('error', function(e) {\n                // make another request to verify in flight request is only 1\n                CLIENT = restifyClients.createJsonClient({\n                    url: 'http://127.0.0.1:' + PORT,\n                    retry: false\n                });\n\n                CLIENT.post(\n                    '/compressed?v=2',\n                    {\n                        apple: 'red'\n                    },\n                    function(err, _, res, obj) {\n                        assert.ifError(err);\n                        assert.equal(res.statusCode, 200);\n                    }\n                );\n            });\n\n            // write data to request body, but don't req.send()\n            req.write(postData);\n        });\n    });\n\n    it('should not add a listener for each call on same socket', done => {\n        SERVER.use(restify.plugins.bodyParser());\n\n        let serverReq, serverRes, serverReqSocket;\n        SERVER.post('/meals', function(req, res, next) {\n            serverReq = req;\n            serverRes = res;\n            serverReqSocket = req.socket;\n            res.send();\n            next();\n        });\n\n        CLIENT = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + PORT,\n            retry: false,\n            agent: new http.Agent({ keepAlive: true })\n        });\n\n        CLIENT.post('/meals', { breakfast: 'pancakes' }, (err, _, res) => {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n\n            const firstReqSocket = serverReqSocket;\n            const numReqListeners = listenerCount(serverReq);\n            const numResListeners = listenerCount(serverRes);\n            const numReqSocketListeners = listenerCount(serverReq.socket);\n\n            // Without setImmediate, the second request will not reuse the socket.\n            setImmediate(() => {\n                CLIENT.post('/meals', { lunch: 'salad' }, (err2, __, res2) => {\n                    assert.ifError(err2);\n                    assert.equal(res2.statusCode, 200);\n                    assert.equal(\n                        serverReqSocket,\n                        firstReqSocket,\n                        'This test should issue two requests that share the ' +\n                            'same socket.'\n                    );\n                    // The number of listeners on each emitter should not have\n                    // increased since the first request.\n                    assert.equal(listenerCount(serverReq), numReqListeners);\n                    assert.equal(listenerCount(serverRes), numResListeners);\n                    assert.equal(\n                        listenerCount(serverReq.socket),\n                        numReqSocketListeners\n                    );\n                    done();\n                });\n            });\n        });\n    });\n\n    it('should call next for each successful request on same socket', done => {\n        let nextCallCount = 0;\n        SERVER.use(restify.plugins.bodyParser());\n        SERVER.use((req, res, next) => {\n            nextCallCount += 1;\n            next();\n        });\n\n        let serverReqSocket;\n        SERVER.post('/meals', function(req, res, next) {\n            res.send();\n            next();\n        });\n\n        CLIENT = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + PORT,\n            retry: false,\n            agent: new http.Agent({ keepAlive: true })\n        });\n\n        CLIENT.post('/meals', { breakfast: 'waffles' }, (err, _, res) => {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            const firstReqSocket = serverReqSocket;\n            assert.equal(nextCallCount, 1);\n\n            // Without setImmediate, the second request will not reuse the socket.\n            setImmediate(() => {\n                CLIENT.post('/meals', { lunch: 'candy' }, (err2, __, res2) => {\n                    assert.ifError(err2);\n                    assert.equal(res2.statusCode, 200);\n                    assert.equal(\n                        serverReqSocket,\n                        firstReqSocket,\n                        'This test should issue two requests that share the ' +\n                            'same socket.'\n                    );\n                    assert.equal(nextCallCount, 2);\n                    done();\n                });\n            });\n        });\n    });\n});\n\n/**\n * @param {EventEmitter} emitter - An emitter\n * @returns {number} - The total number of listeners across all events\n */\nfunction listenerCount(emitter) {\n    let numListeners = 0;\n    for (const eventName of emitter.eventNames()) {\n        numListeners += emitter.listenerCount(eventName);\n    }\n    return numListeners;\n}\n"
  },
  {
    "path": "test/plugins/conditionalHandler.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\nvar parallel = require('vasync').parallel;\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\nfunction handlerFactory(response, version) {\n    return function handler(req, res, next) {\n        res.send(response);\n        if (version) {\n            assert.equal(req.matchedVersion(), version);\n        }\n        next();\n    };\n}\n\ndescribe('conditional request', function() {\n    describe('version', function() {\n        beforeEach(function(done) {\n            SERVER = restify.createServer({\n                dtrace: helper.dtrace,\n                log: helper.getLog('server')\n            });\n\n            SERVER.listen(0, '127.0.0.1', function() {\n                PORT = SERVER.address().port;\n                CLIENT = restifyClients.createJsonClient({\n                    url: 'http://127.0.0.1:' + PORT,\n                    dtrace: helper.dtrace,\n                    retry: false\n                });\n\n                done();\n            });\n        });\n\n        afterEach(function(done) {\n            CLIENT.close();\n            SERVER.close(done);\n        });\n\n        it('should find handler by string version', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('v1.1.0', 'v1.1.0'),\n                        version: 'v1.1.0'\n                    },\n                    {\n                        handler: handlerFactory('v1.2.0', 'v1.2.0'),\n                        version: 'v1.2.0'\n                    }\n                ])\n            );\n\n            parallel(\n                {\n                    funcs: [\n                        function v1(callback) {\n                            var opts = {\n                                path: '/',\n                                headers: {\n                                    'accept-version': '1.1.0'\n                                }\n                            };\n                            CLIENT.get(opts, function(err, _, res, response) {\n                                assert.ifError(err);\n                                assert.equal(res.statusCode, 200);\n                                assert.equal(response, 'v1.1.0');\n                                callback();\n                            });\n                        },\n                        function v2(callback) {\n                            var opts = {\n                                path: '/',\n                                headers: {\n                                    'accept-version': '1.2.0'\n                                }\n                            };\n                            CLIENT.get(opts, function(err, _, res, response) {\n                                assert.ifError(err);\n                                assert.equal(res.statusCode, 200);\n                                assert.equal(\n                                    res.headers['api-version'],\n                                    'v1.2.0'\n                                );\n                                assert.equal(response, 'v1.2.0');\n                                callback();\n                            });\n                        }\n                    ]\n                },\n                function parallelDone(err) {\n                    assert.ifError(err);\n                    done();\n                }\n            );\n        });\n\n        it('should find handler by array of versions', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('v1.x, v2.x'),\n                        version: ['v1.1.0', 'v2.0.0']\n                    },\n                    {\n                        handler: handlerFactory('v3.x'),\n                        version: 'v3.0.0'\n                    }\n                ])\n            );\n\n            parallel(\n                {\n                    funcs: [\n                        function v1(callback) {\n                            var opts = {\n                                path: '/',\n                                headers: {\n                                    'accept-version': '2.x'\n                                }\n                            };\n                            CLIENT.get(opts, function(err, _, res, response) {\n                                assert.ifError(err);\n                                assert.equal(res.statusCode, 200);\n                                assert.equal(response, 'v1.x, v2.x');\n                                callback();\n                            });\n                        },\n                        function v2(callback) {\n                            var opts = {\n                                path: '/',\n                                headers: {\n                                    'accept-version': '3.x'\n                                }\n                            };\n                            CLIENT.get(opts, function(err, _, res, response) {\n                                assert.ifError(err);\n                                assert.equal(res.statusCode, 200);\n                                assert.equal(response, 'v3.x');\n                                callback();\n                            });\n                        }\n                    ]\n                },\n                function parallelDone(err) {\n                    assert.ifError(err);\n                    done();\n                }\n            );\n        });\n\n        it('should find handler with 1.x', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('v1.1.0'),\n                        version: 'v1.1.0'\n                    },\n                    {\n                        handler: handlerFactory('v1.2.0'),\n                        version: 'v1.2.0'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    'accept-version': '1.x'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(response, 'v1.2.0');\n                done();\n            });\n        });\n\n        it('should find handler with ~1.1.0', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('v1.1.1'),\n                        version: 'v1.1.1'\n                    },\n                    {\n                        handler: handlerFactory('v1.2.0'),\n                        version: 'v1.2.0'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    'accept-version': '~1.1.0'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(response, 'v1.1.1');\n                done();\n            });\n        });\n\n        it('should find handler with ^1.1.0', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('v1.1.1'),\n                        version: 'v1.1.1'\n                    },\n                    {\n                        handler: handlerFactory('v1.2.0'),\n                        version: 'v1.2.0'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    'accept-version': '^1.1.0'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(response, 'v1.2.0');\n                done();\n            });\n        });\n\n        it('should find largest version with missing header', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('v1.1.0'),\n                        version: 'v1.1.0'\n                    },\n                    {\n                        handler: handlerFactory('v1.2.0'),\n                        version: 'v1.2.0'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {}\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(response, 'v1.2.0');\n                done();\n            });\n        });\n\n        it('should throw invalid version error', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('v1.1.0'),\n                        version: 'v1.1.0'\n                    },\n                    {\n                        handler: handlerFactory('v1.2.0'),\n                        version: 'v1.2.0'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    'accept-version': '1.3.0'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.equal(err.name, 'InvalidVersionError');\n                assert.equal(err.message, '1.3.0 is not supported by GET /');\n                assert.equal(res.statusCode, 400);\n                done();\n            });\n        });\n    });\n\n    describe('content type', function() {\n        beforeEach(function(done) {\n            SERVER = restify.createServer({\n                dtrace: helper.dtrace,\n                log: helper.getLog('server')\n            });\n\n            SERVER.listen(0, '127.0.0.1', function() {\n                PORT = SERVER.address().port;\n                CLIENT = restifyClients.createStringClient({\n                    url: 'http://127.0.0.1:' + PORT,\n                    dtrace: helper.dtrace,\n                    retry: false\n                });\n\n                done();\n            });\n        });\n\n        afterEach(function(done) {\n            CLIENT.close();\n            SERVER.close(done);\n        });\n\n        it('should find handler by content type by string', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('application/json'),\n                        contentType: 'application/json'\n                    },\n                    {\n                        handler: handlerFactory('text/plain'),\n                        contentType: 'text/plain'\n                    }\n                ])\n            );\n\n            parallel(\n                {\n                    funcs: [\n                        function v1(callback) {\n                            var opts = {\n                                path: '/',\n                                headers: {\n                                    accept: 'application/json'\n                                }\n                            };\n                            CLIENT.get(opts, function(err, _, res, response) {\n                                assert.ifError(err);\n                                assert.equal(res.statusCode, 200);\n                                assert.equal(response, '\"application/json\"');\n                                callback();\n                            });\n                        },\n                        function v2(callback) {\n                            var opts = {\n                                path: '/',\n                                headers: {\n                                    accept: 'text/plain'\n                                }\n                            };\n                            CLIENT.get(opts, function(err, _, res, response) {\n                                assert.ifError(err);\n                                assert.equal(res.statusCode, 200);\n                                assert.equal(response, 'text/plain');\n                                callback();\n                            });\n                        }\n                    ]\n                },\n                function parallelDone(err) {\n                    assert.ifError(err);\n                    done();\n                }\n            );\n        });\n\n        it('should find handler by array of content types', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('application/*'),\n                        contentType: [\n                            'application/json',\n                            'application/javascript'\n                        ]\n                    },\n                    {\n                        handler: handlerFactory('text/plain'),\n                        contentType: 'text/plain'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    accept: 'application/javascript'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(response, '\"application/*\"');\n                done();\n            });\n        });\n\n        it('should find handler with multiple accept', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('application/*'),\n                        contentType: 'application/json'\n                    },\n                    {\n                        handler: handlerFactory('text/plain'),\n                        contentType: 'text/plain'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    accept: 'text/html,text/plain'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(response, 'text/plain');\n                done();\n            });\n        });\n\n        it('should find handler with application/*', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('application/*'),\n                        contentType: 'application/json'\n                    },\n                    {\n                        handler: handlerFactory('text/plain'),\n                        contentType: 'text/plain'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    accept: 'application/json'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(response, '\"application/*\"');\n                done();\n            });\n        });\n\n        it('should find handler with content type and version', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('application/json, 1.1.0'),\n                        contentType: 'application/json',\n                        version: '1.1.0'\n                    },\n                    {\n                        handler: handlerFactory('application/json, 1.2.0'),\n                        contentType: 'application/json',\n                        version: '1.2.0'\n                    },\n                    {\n                        handler: handlerFactory('text/plain'),\n                        contentType: 'text/plain'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    accept: 'application/json',\n                    'accept-version': '1.2.0'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(response, '\"application/json, 1.2.0\"');\n                done();\n            });\n        });\n\n        it('should throw invalid media type error', function(done) {\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: handlerFactory('application/json'),\n                        contentType: 'application/json'\n                    },\n                    {\n                        handler: handlerFactory('text/plain'),\n                        contentType: 'text/plain'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    accept: 'text/html'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.equal(err.name, 'UnsupportedMediaTypeError');\n                assert.equal(\n                    err.message,\n                    '{\"code\":\"UnsupportedMediaType\",\"message\":\"text/html\"}'\n                );\n                assert.equal(res.statusCode, 415);\n                done();\n            });\n        });\n    });\n\n    describe('multiple handlers', function() {\n        beforeEach(function(done) {\n            SERVER = restify.createServer({\n                dtrace: helper.dtrace,\n                log: helper.getLog('server')\n            });\n\n            SERVER.listen(0, '127.0.0.1', function() {\n                PORT = SERVER.address().port;\n                CLIENT = restifyClients.createJsonClient({\n                    url: 'http://127.0.0.1:' + PORT,\n                    dtrace: helper.dtrace,\n                    retry: false\n                });\n\n                done();\n            });\n        });\n\n        afterEach(function(done) {\n            CLIENT.close();\n            SERVER.close(done);\n        });\n\n        it('should run each of the handlers', function(done) {\n            var counter = 0;\n\n            SERVER.get(\n                '/',\n                restify.plugins.conditionalHandler([\n                    {\n                        handler: [\n                            function handler1(req, res, next) {\n                                counter += 1;\n                                next();\n                            },\n                            function handler2(req, res, next) {\n                                counter += 1;\n                                next();\n                            },\n                            function handler3(req, res, next) {\n                                counter += 1;\n                                res.send('v1.2.0');\n                            }\n                        ],\n                        version: 'v1.2.0'\n                    }\n                ])\n            );\n\n            var opts = {\n                path: '/',\n                headers: {\n                    'accept-version': '1.2.0'\n                }\n            };\n            CLIENT.get(opts, function(err, _, res, response) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.equal(counter, 3, 'calls all of the handlers');\n                assert.equal(response, 'v1.2.0');\n                done();\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/conditionalRequest.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('conditional request', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('Correct Etag and headers', function(done) {\n        SERVER.get(\n            '/etag/:id',\n            function(req, res, next) {\n                res.etag = 'testETag';\n                next();\n            },\n            restify.plugins.conditionalRequest(),\n            function(req, res, next) {\n                res.body = 'testing 304';\n                res.send();\n                next();\n            }\n        );\n\n        var opts = {\n            path: '/etag/foo',\n            headers: {\n                'If-Match': 'testETag',\n                'If-None-Match': 'testETag'\n            }\n        };\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 304);\n            done();\n        });\n    });\n\n    it('mismatched Etag and If-Match', function(done) {\n        SERVER.get(\n            '/etag/:id',\n            function setEtag(req, res, next) {\n                res.etag = 'testEtag';\n                next();\n            },\n            restify.plugins.conditionalRequest(),\n            function respond(req, res, next) {\n                res.send();\n                next();\n            }\n        );\n\n        var opts = {\n            path: '/etag/foo',\n            headers: {\n                'If-Match': 'testETag2'\n            }\n        };\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ok(err);\n            assert.equal(res.statusCode, 412);\n            done();\n        });\n    });\n\n    it('If-Modified header & !modified content', function(done) {\n        var now = new Date();\n        var yesterday = new Date(now.setDate(now.getDate() - 1));\n        SERVER.get(\n            '/etag/:id',\n            function(req, res, next) {\n                res.header('Last-Modified', yesterday);\n                next();\n            },\n            restify.plugins.conditionalRequest(),\n            function(req, res, next) {\n                res.send('testing 304');\n                next();\n            }\n        );\n\n        var opts = {\n            path: '/etag/foo',\n            headers: {\n                'If-Modified-Since': new Date()\n            }\n        };\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 304);\n            done();\n        });\n    });\n\n    it('If-Unmodified-Since header,modified content', function(done) {\n        var now = new Date();\n        var yesterday = new Date(now.setDate(now.getDate() - 1));\n        SERVER.get(\n            '/etag/:id',\n            function(req, res, next) {\n                res.header('Last-Modified', new Date());\n                next();\n            },\n            restify.plugins.conditionalRequest(),\n            function(req, res, next) {\n                res.send('testing 412');\n                next();\n            }\n        );\n\n        var opts = {\n            path: '/etag/foo',\n            headers: {\n                'If-Unmodified-Since': yesterday\n            }\n        };\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ok(err);\n            assert.equal(res.statusCode, 412);\n            done();\n        });\n    });\n\n    it('valid headers, ahead time, unmodified OK', function(done) {\n        var now = new Date();\n        var ahead = new Date(now.getTime() + 1000);\n        SERVER.get(\n            '/etag/:id',\n            function(req, res, next) {\n                res.header('Last-Modified', now);\n                next();\n            },\n            restify.plugins.conditionalRequest(),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        );\n\n        var opts = {\n            path: '/etag/foo',\n            headers: {\n                'If-Modified-Since': ahead\n            }\n        };\n\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 304);\n            done();\n        });\n    });\n\n    it('valid headers, ahead Timezone, modified content', function(done) {\n        var now = new Date();\n        var ahead = new Date(now.setHours(now.getHours() + 5));\n        SERVER.get(\n            '/etag/:id',\n            function(req, res, next) {\n                res.header('Last-Modified', now);\n                next();\n            },\n            restify.plugins.conditionalRequest(),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        );\n\n        var opts = {\n            path: '/etag/foo',\n            headers: {\n                'If-Unmodified-Since': ahead\n            }\n        };\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('PUT with matched Etag and headers', function(done) {\n        SERVER.put(\n            '/etag/:id',\n            function(req, res, next) {\n                res.etag = 'testETag';\n                next();\n            },\n            restify.plugins.conditionalRequest(),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        );\n\n        var opts = {\n            path: '/etag/foo',\n            headers: {\n                'If-Match': 'testETag',\n                'If-None-Match': 'testETag'\n            }\n        };\n        CLIENT.put(opts, {}, function(err, _, res) {\n            assert.ok(err);\n            assert.equal(res.statusCode, 412);\n            done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/context.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('accept parser', function() {\n    before(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.use(restify.plugins.pre.context());\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    after(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('should use context', function(done) {\n        SERVER.get('/', [\n            function one(req, res, next) {\n                req.set('foo', {\n                    a: 1\n                });\n                return next();\n            },\n            function two(req, res, next) {\n                assert.deepEqual(req.get('foo'), {\n                    a: 1\n                });\n                req.get('foo').b = 2;\n                req.set('bar', [1]);\n                return next();\n            },\n            function three(req, res, next) {\n                assert.deepEqual(req.get('foo'), {\n                    a: 1,\n                    b: 2\n                });\n                assert.deepEqual(req.get('bar'), [1]);\n\n                assert.deepEqual(req.getAll(), {\n                    foo: {\n                        a: 1,\n                        b: 2\n                    },\n                    bar: [1]\n                });\n\n                res.send();\n                return next();\n            }\n        ]);\n\n        CLIENT.get('/', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            return done();\n        });\n    });\n\n    it('should not share context', function(done) {\n        SERVER.get('/1', function one(req, res, next) {\n            // ensure we don't get context from previous request\n            assert.equal(req.get('foo', null));\n            res.end();\n            return next();\n        });\n\n        CLIENT.get('/1', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            return done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/cpuUsageThrottle.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar assert = require('chai').assert;\nvar proxyquire = require('proxyquire');\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\nvar pidusage = require('pidusage');\n\n// Allow tests to set the CPU usage returned by pidUsage\nvar CPU = 50;\n\nvar cpuUsageThrottle = proxyquire('../../lib/plugins/cpuUsageThrottle.js', {\n    pidusage: function(pid, cb) {\n        return cb(null, { cpu: CPU });\n    }\n});\n\nvar MR = Math.random;\ndescribe('cpuUsageThrottle', function() {\n    var plugin;\n\n    before('Setup: stub math.random', function(done) {\n        Math.random = function() {\n            return 0;\n        };\n        done();\n    });\n\n    it('Unit: Should shed load', function(done) {\n        var opts = { limit: 0, interval: 500 };\n        plugin = cpuUsageThrottle(opts);\n        function next(cont) {\n            assert(cont instanceof Error, 'Should call next with error');\n            assert.equal(cont.statusCode, 503, 'Defaults to 503 status');\n            done();\n        }\n        plugin({}, {}, next);\n    });\n\n    it('Unit: Should let request through when not under load', function(done) {\n        var opts = { interval: 500, limit: 0.9 };\n        plugin = cpuUsageThrottle(opts);\n        function next(cont) {\n            assert.isUndefined(cont, 'Should call next');\n            done();\n        }\n        plugin({}, {}, next);\n    });\n\n    it('Unit: Update should update state', function(done) {\n        var opts = {\n            max: 1,\n            limit: 0.9,\n            halfLife: 50,\n            interval: 50\n        };\n        plugin = cpuUsageThrottle(opts);\n        opts = {\n            max: 0.5,\n            limit: 0.1,\n            halfLife: 1000,\n            interval: 1000\n        };\n        plugin.update(opts);\n        assert.equal(plugin.state.limit, opts.limit, 'opts.limit');\n        assert.equal(plugin.state.max, opts.max, 'opts.max');\n        assert.equal(plugin.state.halfLife, opts.halfLife, 'opts.halfLife');\n        assert.equal(plugin.state.interval, opts.interval, 'opts.interval');\n        done();\n    });\n\n    it('Unit: Should have proper name', function(done) {\n        var opts = {\n            max: 1,\n            limit: 0.9,\n            halfLife: 50,\n            interval: 50\n        };\n        plugin = cpuUsageThrottle(opts);\n        assert.equal(plugin.name, 'cpuUsageThrottle');\n        done();\n    });\n\n    it('Unit: Should report proper lag', function(done) {\n        var opts = { max: 1, limit: 0.9, halfLife: 50, interval: 50 };\n        var dn = Date.now;\n        var now = 0;\n        // First timer will be 0, all future timers will be interval\n        Date.now = function() {\n            return (now++ > 0) * opts.interval;\n        };\n        plugin = cpuUsageThrottle(opts);\n        Date.now = dn;\n        assert.equal(plugin.state.lag, 0);\n        done();\n    });\n\n    it('Integration: Should shed load', function(done) {\n        var server = restify.createServer();\n        var client = {\n            close: function() {}\n        };\n        var opts = { interval: 500, limit: 0 };\n        plugin = cpuUsageThrottle(opts);\n        server.pre(plugin);\n        server.get('/foo', function(req, res, next) {\n            res.send(200);\n            next();\n        });\n        server.listen(0, '127.0.0.1', function() {\n            client = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + server.address().port,\n                retry: false\n            });\n            client.get({ path: '/foo' }, function(e, _, res) {\n                assert(e, 'Second request is shed');\n                assert.equal(\n                    res.statusCode,\n                    503,\n                    'Default shed status code returned'\n                );\n                clearTimeout(plugin._timeout);\n                // we should close the server else mocha wont exit\n                server.close();\n                done();\n            });\n        });\n    });\n\n    it('Integration: pidusage should report CPU usage', function(done) {\n        assert.isFunction(pidusage, 'pidusage can be invoked');\n        pidusage(process.pid, function(e, stat) {\n            assert.ifError(e);\n            assert.isObject(stat);\n            assert.isNumber(stat.cpu);\n            pidusage.clear();\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        if (plugin) {\n            plugin.close();\n        }\n        plugin = undefined;\n        done();\n    });\n\n    after('Teardown: Reset Math.random', function(done) {\n        Math.random = MR;\n        done();\n    });\n});\n"
  },
  {
    "path": "test/plugins/dedupeSlashes.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('dedupe forward slashes in URL', function() {\n    before(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.pre(restify.plugins.pre.dedupeSlashes());\n\n        SERVER.get('/foo/bar/', function respond(req, res, next) {\n            res.send(req.url);\n            next();\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    after(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('should not remove single slashes', function(done) {\n        CLIENT.get('/foo/bar/', function(err, _, res, data) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            assert.equal(data, '/foo/bar/');\n            done();\n        });\n    });\n\n    it('should remove duplicate slashes', function(done) {\n        CLIENT.get('//////foo///bar///////', function(err, _, res, data) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            assert.equal(data, '/foo/bar/');\n            done();\n        });\n    });\n\n    // eslint-disable-next-line\n    it('should remove duplicate slashes including trailing slashes', function(done) {\n        CLIENT.get('//foo//bar//', function(err, _, res, data) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            assert.equal(data, '/foo/bar/');\n            done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/fieldedTextParser.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// core requires\nvar fs = require('fs');\nvar path = require('path');\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\nvar fsOptions = { encoding: 'utf8' };\nvar PORT = process.env.UNIT_TEST_PORT || 3333;\nvar CLIENT;\nvar SERVER;\nvar DATA_CSV = fs.readFileSync(\n    path.join(__dirname, '/files/data-csv.txt'),\n    fsOptions\n);\nvar DATA_TSV = fs.readFileSync(\n    path.join(__dirname, '/files/data-tsv.txt'),\n    fsOptions\n);\nvar OBJECT_CSV = require(path.join(__dirname, '/files/object-csv.json'));\nvar OBJECT_TSV = require(path.join(__dirname, '/files/object-tsv.json'));\n\n/**\n * Tests\n */\n\ndescribe('fielded text parser', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n        SERVER.use(restify.plugins.bodyParser());\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            CLIENT = restifyClients.createClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('should parse CSV body', function(done) {\n        var options = {\n            path: '/data',\n            headers: {\n                'Content-Type': 'text/csv'\n            }\n        };\n\n        SERVER.post('/data', function respond(req, res, next) {\n            res.send({\n                status: 'okay',\n                parsedReq: req.body\n            });\n            return next();\n        });\n\n        CLIENT.post(options, function(err, req) {\n            assert.ifError(err);\n            req.on('result', function(errReq, res) {\n                assert.ifError(errReq);\n                res.body = '';\n                res.setEncoding('utf8');\n                res.on('data', function(chunk) {\n                    res.body += chunk;\n                });\n                res.on('end', function() {\n                    res.body = JSON.parse(res.body);\n                    var parsedReqStr = JSON.stringify(res.body.parsedReq);\n                    var objectStr = JSON.stringify(OBJECT_CSV);\n                    assert.equal(parsedReqStr, objectStr);\n                    done();\n                });\n            });\n            req.write(DATA_CSV);\n            req.end();\n        });\n    });\n\n    // eslint-disable-next-line\n    it('#100 should parse CSV body even if bodyparser declared twice', function(done) {\n        SERVER.use(restify.plugins.bodyParser());\n        var options = {\n            path: '/data',\n            headers: {\n                'Content-Type': 'text/csv'\n            }\n        };\n\n        SERVER.post('/data', function respond(req, res, next) {\n            res.send({\n                status: 'okay',\n                parsedReq: req.body\n            });\n            return next();\n        });\n\n        CLIENT.post(options, function(err, req) {\n            assert.ifError(err);\n            req.on('result', function(errReq, res) {\n                assert.ifError(errReq);\n                res.body = '';\n                res.setEncoding('utf8');\n                res.on('data', function(chunk) {\n                    res.body += chunk;\n                });\n                res.on('end', function() {\n                    res.body = JSON.parse(res.body);\n                    var parsedReqStr = JSON.stringify(res.body.parsedReq);\n                    var objectStr = JSON.stringify(OBJECT_CSV);\n                    assert.equal(parsedReqStr, objectStr);\n                    done();\n                });\n            });\n            req.write(DATA_CSV);\n            req.end();\n        });\n    });\n\n    it('should parse TSV body', function(done) {\n        var options = {\n            path: '/data',\n            headers: {\n                'Content-Type': 'text/tsv'\n            }\n        };\n\n        SERVER.post('/data', function respond(req, res, next) {\n            res.send({\n                status: 'okay',\n                parsedReq: req.body\n            });\n            return next();\n        });\n\n        CLIENT.post(options, function(err, req) {\n            assert.ifError(err);\n            req.on('result', function(errReq, res) {\n                assert.ifError(errReq);\n                res.body = '';\n                res.setEncoding('utf8');\n                res.on('data', function(chunk) {\n                    res.body += chunk;\n                });\n                res.on('end', function() {\n                    res.body = JSON.parse(res.body);\n                    var parsedReqStr = JSON.stringify(res.body.parsedReq);\n                    var objectStr = JSON.stringify(OBJECT_TSV);\n                    assert.equal(parsedReqStr, objectStr);\n                    done();\n                });\n            });\n            req.write(DATA_TSV);\n            req.end();\n        });\n    });\n\n    it('plugins-GH-6: should expose rawBody on request', function(done) {\n        var options = {\n            path: '/data',\n            headers: {\n                'Content-Type': 'text/csv'\n            }\n        };\n\n        SERVER.post('/data', function respond(req, res, next) {\n            assert.ok(req.rawBody);\n            res.send();\n            return next();\n        });\n\n        CLIENT.post(options, function(err, req) {\n            assert.ifError(err);\n            req.on('result', function(errReq, res) {\n                assert.ifError(errReq);\n                res.body = '';\n                res.setEncoding('utf8');\n                res.on('data', function(chunk) {\n                    res.body += chunk;\n                });\n                res.on('end', done);\n            });\n            req.write(DATA_TSV);\n            req.end();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/files/data-csv.txt",
    "content": "field1,field2,field3\n1,2,3\n3,2,1\n\"a\",\"b\",\"c\"\n\"\\\"c\",\"b\",\"a\""
  },
  {
    "path": "test/plugins/files/data-tsv.txt",
    "content": "field1\tfield2\tfield3\n1\t2\t3\n3\t2\t1"
  },
  {
    "path": "test/plugins/files/object-csv.json",
    "content": "[\n  { \"field1\": \"1\", \"field2\": \"2\", \"field3\": \"3\", \"index\": 0 },\n  { \"field1\": \"3\", \"field2\": \"2\", \"field3\": \"1\", \"index\": 1 },\n  { \"field1\": \"a\", \"field2\": \"b\", \"field3\": \"c\", \"index\": 2 },\n  { \"field1\": \"\\\"c\", \"field2\": \"b\", \"field3\": \"a\", \"index\": 3 }\n]"
  },
  {
    "path": "test/plugins/files/object-tsv.json",
    "content": "[\n  { \"field1\": \"1\", \"field2\": \"2\", \"field3\": \"3\", \"index\": 0 },\n  { \"field1\": \"3\", \"field2\": \"2\", \"field3\": \"1\", \"index\": 1 }\n]"
  },
  {
    "path": "test/plugins/formBodyParser.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// core requires\nvar http = require('http');\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar PORT;\n\ndescribe('form body parser', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        SERVER.close(done);\n    });\n\n    it('should parse req.body, req.query, req.params', function(done) {\n        SERVER.use(restify.plugins.queryParser());\n        SERVER.use(restify.plugins.bodyParser());\n\n        SERVER.post('/bodyurl2/:id', function(req, res, next) {\n            assert.equal(req.query.name, 'markc');\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.body.name, 'somethingelse');\n            assert.equal(req.body.phone, '(206) 555-1212');\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/bodyurl2/foo?name=markc',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write('phone=(206)%20555-1212&name=somethingelse');\n        client.end();\n    });\n\n    it('should map req.body & req.query onto req.params', function(done) {\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.post('/bodyurl2/:id', function(req, res, next) {\n            assert.equal(req.query.name, 'markc');\n\n            assert.equal(req.body.phone, '(206) 555-1212');\n            assert.equal(req.body.name, 'somethingelse');\n\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.name, 'markc');\n            assert.equal(req.params.phone, '(206) 555-1212');\n\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/bodyurl2/foo?name=markc',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write('phone=(206)%20555-1212&name=somethingelse');\n        client.end();\n    });\n\n    it('should take req.body and stomp on req.params', function(done) {\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.post('/bodyurl2/:id', function(req, res, next) {\n            assert.equal(req.query.name, 'markc');\n\n            assert.equal(req.body.phone, '(206) 555-1212');\n            assert.equal(req.body.name, 'somethingelse');\n\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.name, 'somethingelse');\n            assert.equal(req.params.phone, '(206) 555-1212');\n\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/bodyurl2/foo?name=markc',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write('phone=(206)%20555-1212&name=somethingelse');\n        client.end();\n    });\n\n    it('should parse associative array syntax', function(done) {\n        SERVER.use(restify.plugins.bodyParser());\n\n        SERVER.post('/bodyurl2/:id', function(req, res, next) {\n            assert.isObject(req.body.name);\n            assert.equal(req.body.name.first, 'alex');\n            assert.equal(req.body.name.last, 'liu');\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/bodyurl2/foo',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write('name[first]=alex&name[last]=liu');\n        client.end();\n    });\n\n    it('should parse array syntax', function(done) {\n        SERVER.use(restify.plugins.bodyParser());\n\n        SERVER.post('/bodyurl2/:id', function(req, res, next) {\n            assert.isArray(req.body.meat);\n            assert.deepEqual(req.body.meat, ['ham', 'bacon']);\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/bodyurl2/foo',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write('meat[]=ham&meat[]=bacon');\n        client.end();\n    });\n\n    it('should parse nested array syntax', function(done) {\n        SERVER.use(restify.plugins.bodyParser());\n\n        SERVER.post('/bodyurl2/:id', function(req, res, next) {\n            assert.isObject(req.body.pizza);\n            assert.isArray(req.body.pizza.left);\n            assert.isArray(req.body.pizza.right);\n            assert.deepEqual(req.body.pizza.left, ['ham', 'bacon']);\n            assert.deepEqual(req.body.pizza.right, ['pineapple']);\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/bodyurl2/foo',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        var p =\n            'pizza[left][]=ham&pizza[left][]=bacon&' +\n            'pizza[right][]=pineapple';\n        client.write(p);\n        client.end();\n    });\n\n    it('plugins-GH-6: should expose rawBody', function(done) {\n        var input = 'name[first]=alex&name[last]=liu';\n\n        SERVER.use(restify.plugins.bodyParser());\n\n        SERVER.post('/bodyurl2/:id', function(req, res, next) {\n            assert.equal(req.rawBody, input);\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/bodyurl2/foo',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write(input);\n        client.end();\n    });\n});\n"
  },
  {
    "path": "test/plugins/gzip.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('gzip parser', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('should gzip response', function(done) {\n        SERVER.use(restify.plugins.gzipResponse());\n\n        SERVER.get('/gzip/:id', function(req, res, next) {\n            res.send({\n                hello: 'world'\n            });\n            next();\n        });\n\n        var opts = {\n            path: '/gzip/foo',\n            headers: {\n                'Accept-Encoding': 'gzip'\n            }\n        };\n        CLIENT.get(opts, function(err, _, res, obj) {\n            assert.ifError(err);\n            assert.deepEqual({ hello: 'world' }, obj);\n            done();\n        });\n    });\n\n    it('gzip large response', function(done) {\n        var testResponseSize = 65536 * 3;\n        var TestStream = function() {\n            this.readable = true;\n            this.sentSize = 0;\n            this.totalSize = testResponseSize;\n            this.interval = null;\n        };\n        require('util').inherits(TestStream, require('stream'));\n        TestStream.prototype.resume = function() {\n            var self = this;\n\n            if (!this.interval) {\n                this.interval = setInterval(function() {\n                    var chunkSize = Math.min(\n                        self.totalSize - self.sentSize,\n                        65536\n                    );\n\n                    if (chunkSize > 0) {\n                        var chunk = new Array(chunkSize + 1);\n                        chunk = chunk.join('a');\n                        self.emit('data', chunk);\n                        self.sentSize += chunkSize;\n                    } else {\n                        self.emit('data', '\"}');\n                        self.emit('end');\n                        self.pause();\n                    }\n                }, 1);\n            }\n        };\n\n        TestStream.prototype.pause = function() {\n            clearInterval(this.interval);\n            this.interval = null;\n        };\n\n        var bodyStream = new TestStream();\n\n        SERVER.use(restify.plugins.gzipResponse());\n        SERVER.get('/gzip/:id', function(req, res, next) {\n            bodyStream.resume();\n            res.write('{\"foo\":\"');\n            bodyStream.pipe(res);\n            bodyStream.on('end', function() {\n                next();\n            });\n        });\n\n        var opts = {\n            path: '/gzip/foo',\n            headers: {\n                'Accept-Encoding': 'gzip'\n            }\n        };\n        CLIENT.get(opts, function(err, _, res, obj) {\n            assert.ifError(err);\n            var expectedResponse = {\n                foo: new Array(testResponseSize + 1).join('a')\n            };\n            assert.deepEqual(expectedResponse, obj);\n            done();\n        });\n    });\n\n    it('gzip body json ok', function(done) {\n        SERVER.use(restify.plugins.gzipResponse());\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapParams: true\n            })\n        );\n        SERVER.post('/body/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.name, 'markc');\n            assert.equal(req.params.phone, '(206) 555-1212');\n            res.send();\n            next();\n        });\n\n        var obj = {\n            phone: '(206) 555-1212',\n            name: 'somethingelse'\n        };\n        CLIENT.gzip = {};\n        CLIENT.post('/body/foo?name=markc', obj, function(err, _, res) {\n            assert.ifError(err);\n            assert.ok(res);\n\n            if (res) {\n                assert.equal(res.statusCode, 200);\n            }\n            done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/inflightRequestThrottle.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\nvar inflightRequestThrottle = restify.plugins.inflightRequestThrottle;\n\nfunction fakeServer(count) {\n    return {\n        inflightRequests: function() {\n            return count;\n        }\n    };\n}\n\ndescribe('inlfightRequestThrottle', function() {\n    it('Unit: Should shed load', function(done) {\n        var logged = false;\n        var opts = { server: fakeServer(10), limit: 1 };\n        var plugin = inflightRequestThrottle(opts);\n        function send(body) {\n            assert(logged, 'Should have emitted a log');\n            assert.equal(body.statusCode, 503, 'Defaults to 503 status');\n            assert(body instanceof Error, 'Defaults to error body');\n            done();\n        }\n        function next(err) {\n            assert.equal(err.name, 'ServiceUnavailableError');\n            done();\n        }\n        function trace() {\n            logged = true;\n        }\n        var log = { trace: trace };\n        var fakeReq = { log: log };\n        plugin(fakeReq, { send: send }, next);\n    });\n\n    it('Unit: Should support custom response', function(done) {\n        var server = fakeServer(10);\n        var err = new Error('foo');\n        var opts = { server: server, limit: 1, err: err };\n        var plugin = inflightRequestThrottle(opts);\n        function send(body) {\n            assert.equal(body, err, 'Overrides body');\n        }\n        function next(nextErr) {\n            assert.equal(err, nextErr);\n            done();\n        }\n        var fakeReq = { log: { trace: function() {} } };\n        plugin(fakeReq, { send: send }, next);\n    });\n\n    it('Unit: Should let request through when not under load', function(done) {\n        var opts = { server: fakeServer(1), limit: 2 };\n        var plugin = inflightRequestThrottle(opts);\n        function send() {\n            assert(false, 'Should not call send');\n        }\n        function next(cont) {\n            assert.isUndefined(cont, 'Should call next');\n            done();\n        }\n        var fakeReq = { log: { trace: function() {} } };\n        plugin(fakeReq, { send: send }, next);\n    });\n\n    it('Integration: Should shed load', function(done) {\n        var server = restify.createServer();\n        var client = {\n            close: function() {}\n        };\n        var isDone = false;\n        var to;\n        function finish() {\n            if (isDone) {\n                return null;\n            }\n            clearTimeout(to);\n            isDone = true;\n            client.close();\n            server.close();\n            return done();\n        }\n        to = setTimeout(finish, 2000);\n        var err = new Error('foo');\n        err.statusCode = 555;\n        var opts = { server: server, limit: 1, err: err };\n        server.pre(inflightRequestThrottle(opts));\n        var RES;\n        server.get('/foo', function(req, res, next) {\n            if (RES) {\n                res.send(999);\n            } else {\n                RES = res;\n            }\n        });\n        server.listen(0, '127.0.0.1', function() {\n            client = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + server.address().port,\n                retry: false\n            });\n            client.get({ path: '/foo' }, function(e, _, res) {\n                assert(\n                    e === null || e === undefined,\n                    'First request isnt shed'\n                );\n                assert.equal(res.statusCode, 200, '200 returned on success');\n                finish();\n            });\n            client.get({ path: '/foo' }, function(e, _, res) {\n                assert(e, 'Second request is shed');\n                assert.equal(\n                    e.name,\n                    'InternalServerError',\n                    'Default err returned'\n                );\n                assert.equal(\n                    res.statusCode,\n                    555,\n                    'Default shed status code returned'\n                );\n\n                if (RES) {\n                    RES.send(200);\n                }\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/jsonBodyParser.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// core requires\nvar net = require('net');\nvar http = require('http');\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar STRING_CLIENT;\nvar PORT;\n\ndescribe('JSON body parser', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n            STRING_CLIENT = restifyClients.createStringClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false,\n                agent: false,\n                contentType: 'application/json',\n                accept: 'application/json'\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        STRING_CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('should parse null JSON body', function(done) {\n        SERVER.use(\n            restify.plugins.jsonBodyParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.post('/body/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.body, null);\n            res.send();\n            next();\n        });\n\n        STRING_CLIENT.post('/body/foo?name=markc', 'null', function(\n            err,\n            _,\n            res\n        ) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should parse empty JSON body', function(done) {\n        SERVER.use(restify.plugins.jsonBodyParser());\n\n        SERVER.post('/body/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.deepEqual(req.body, {});\n            res.send();\n            next();\n        });\n\n        CLIENT.post('/body/foo', null, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should parse req.body and req.params independently', function(done) {\n        SERVER.use(restify.plugins.jsonBodyParser());\n\n        SERVER.post('/body/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.body.id, 'bar');\n            assert.equal(req.body.name, 'alex');\n            assert.notDeepEqual(req.body, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.post(\n            '/body/foo',\n            {\n                id: 'bar',\n                name: 'alex'\n            },\n            function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                done();\n            }\n        );\n    });\n\n    it('should fail to map array req.body onto req.params', function(done) {\n        SERVER.use(\n            restify.plugins.jsonBodyParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.post('/body/:id', function(req, res, next) {\n            // this handler should never be reached\n            res.send();\n            next();\n        });\n\n        CLIENT.post('/body/foo', [1, 2, 3], function(err, _, res) {\n            assert.ok(err);\n            assert.equal(err.name, 'InternalServerError');\n            assert.equal(res.statusCode, 500);\n            done();\n        });\n    });\n\n    // TODO: router param mapping runs later\n    it('should map req.body onto req.params', function(done) {\n        SERVER.use(\n            restify.plugins.jsonBodyParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.post('/body/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.name, 'alex');\n            assert.notDeepEqual(req.body, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.post(\n            '/body/foo',\n            {\n                id: 'bar',\n                name: 'alex'\n            },\n            function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                done();\n            }\n        );\n    });\n\n    it('should take req.body and stomp on req.params', function(done) {\n        SERVER.use(\n            restify.plugins.jsonBodyParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.post('/body/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'bar');\n            assert.equal(req.params.name, 'alex');\n            assert.deepEqual(req.body, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.post(\n            '/body/foo',\n            {\n                id: 'bar',\n                name: 'alex'\n            },\n            function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                done();\n            }\n        );\n    });\n\n    it('should parse JSON body with reviver', function(done) {\n        SERVER.use(\n            restify.plugins.jsonBodyParser({\n                reviver: function reviver(key, value) {\n                    if (key === '') {\n                        return value;\n                    }\n                    return value + value;\n                }\n            })\n        );\n\n        SERVER.post('/body/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.body.apple, 'redred');\n            assert.equal(req.body.orange, 'orangeorange');\n            assert.equal(req.body.banana, 'yellowyellow');\n            res.send();\n            next();\n        });\n\n        CLIENT.post(\n            '/body/foo',\n            {\n                apple: 'red',\n                orange: 'orange',\n                banana: 'yellow'\n            },\n            function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                done();\n            }\n        );\n    });\n\n    it('restify-GH-318 get request with body (default)', function(done) {\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.get('/getWithoutBody', function(req, res, next) {\n            assert.notEqual(req.params.foo, 'bar');\n            res.send();\n            next();\n        });\n\n        var request =\n            'GET /getWithoutBody HTTP/1.1\\r\\n' +\n            'Content-Type: application/json\\r\\n' +\n            'Content-Length: 13\\r\\n' +\n            '\\r\\n' +\n            '{\"foo\":\"bar\"}';\n\n        var client = net.connect({ host: '127.0.0.1', port: PORT }, function() {\n            client.write(request);\n        });\n        client.once('data', function(data) {\n            client.end();\n        });\n        client.once('end', function() {\n            done();\n        });\n    });\n\n    // eslint-disable-next-line\n    it('restify-GH-318 get request with body (requestBodyOnGet=true)', function(done) {\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapParams: true,\n                requestBodyOnGet: true\n            })\n        );\n\n        SERVER.get('/getWithBody', function(req, res, next) {\n            assert.equal(req.params.foo, 'bar');\n            res.send();\n            next();\n        });\n\n        var request =\n            'GET /getWithBody HTTP/1.1\\r\\n' +\n            'Content-Type: application/json\\r\\n' +\n            'Content-Length: 13\\r\\n' +\n            '\\r\\n' +\n            '{\"foo\":\"bar\"}';\n\n        var client = net.connect({ host: '127.0.0.1', port: PORT }, function() {\n            client.write(request);\n        });\n\n        client.once('data', function(data) {\n            client.end();\n        });\n\n        client.once('end', function() {\n            done();\n        });\n    });\n\n    it('restify-GH-111 JSON Parser not right for arrays', function(done) {\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.post('/gh111', function(req, res, next) {\n            assert.ok(Array.isArray(req.params));\n            assert.equal(req.params[0], 'foo');\n            assert.equal(req.params[1], 'bar');\n            res.send();\n            next();\n        });\n\n        var obj = ['foo', 'bar'];\n        CLIENT.post('/gh111', obj, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('restify-GH-279 more JSON Arrays', function(done) {\n        SERVER.use(\n            restify.plugins.jsonBodyParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.post('/gh279', function respond(req, res, next) {\n            assert.ok(Array.isArray(req.params));\n            assert.equal(req.params[0].id, '123654');\n            assert.ok(req.params[0].name, 'mimi');\n            assert.ok(req.params[1].id, '987654');\n            assert.ok(req.params[1].name, 'pijama');\n            res.send(200);\n            next();\n        });\n\n        var obj = [\n            {\n                id: '123654',\n                name: 'mimi'\n            },\n            {\n                id: '987654',\n                name: 'pijama'\n            }\n        ];\n        CLIENT.post('/gh279', obj, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('restify-GH-774 utf8 corruption in body parser', function(done) {\n        var slen = 100000;\n        SERVER.use(restify.plugins.bodyParser());\n        SERVER.post('/utf8', function(req, res, next) {\n            assert.notOk(/\\ufffd/.test(req.body.text));\n            assert.equal(req.body.text.length, slen);\n            res.send({ len: req.body.text.length });\n            next();\n        });\n\n        // create a long string of unicode characters\n        var tx = '';\n\n        for (var i = 0; i < slen; ++i) {\n            tx += '\\u2661';\n        }\n\n        CLIENT.post('/utf8', { text: tx }, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('restify-GH-149 limit request body size', function(done) {\n        SERVER.use(restify.plugins.bodyParser({ maxBodySize: 1024 }));\n\n        SERVER.post('/', function(req, res, next) {\n            res.send(200, { length: req.body.length });\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/',\n            method: 'POST',\n            agent: false,\n            headers: {\n                accept: 'application/json',\n                'content-type': 'application/x-www-form-urlencoded',\n                'transfer-encoding': 'chunked'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 413);\n            res.once('end', done);\n            res.resume();\n        });\n        client.write(new Array(1028).join('x'));\n        client.end();\n    });\n\n    it('restify-GH-149 limit request body size (json)', function(done) {\n        SERVER.use(restify.plugins.bodyParser({ maxBodySize: 1024 }));\n\n        SERVER.post('/', function(req, res, next) {\n            res.send(200, { length: req.body.length });\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/',\n            method: 'POST',\n            agent: false,\n            headers: {\n                accept: 'application/json',\n                'content-type': 'application/json',\n                'transfer-encoding': 'chunked'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 413);\n            res.once('end', done);\n            res.resume();\n        });\n        client.write('{\"a\":[' + new Array(512).join('1,') + '0]}');\n        client.end();\n    });\n\n    it('plugins-GH-6: should expose rawBody', function(done) {\n        var payload = {\n            id: 'bar',\n            name: 'alex'\n        };\n\n        SERVER.use(restify.plugins.jsonBodyParser());\n\n        SERVER.post('/body/:id', function(req, res, next) {\n            assert.equal(req.rawBody, JSON.stringify(payload));\n            assert.equal(req.body.id, 'bar');\n            assert.equal(req.body.name, 'alex');\n            res.send();\n            next();\n        });\n\n        CLIENT.post('/body/foo', payload, done);\n    });\n\n    it('should not throw uncaught \"too few args to sprintf\"', function(done) {\n        // https://github.com/restify/node-restify/issues/1411\n        SERVER.use(restify.plugins.bodyParser());\n\n        SERVER.post('/', function(req, res, next) {\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/',\n            method: 'POST',\n            agent: false,\n            headers: {\n                accept: 'application/json',\n                'content-type': 'application/json'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 400);\n            res.once('end', done);\n            res.resume();\n        });\n        client.write('{\"malformedJsonWithPercentSign\":30%}');\n        client.end();\n    });\n\n    it('should handle application/*+json as application/json', function(done) {\n        SERVER.use(restify.plugins.bodyParser({ maxBodySize: 1024 }));\n\n        SERVER.post('/', function(req, res, next) {\n            res.send(200, { length: req.body.length });\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/',\n            method: 'POST',\n            agent: false,\n            headers: {\n                accept: 'application/json',\n                'content-type': 'application/hal+json',\n                'transfer-encoding': 'chunked'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 413);\n            res.once('end', done);\n            res.resume();\n        });\n        client.write('{\"a\":[' + new Array(512).join('1,') + '0]}');\n        client.end();\n    });\n});\n"
  },
  {
    "path": "test/plugins/metrics.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\n\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\nfunction adjustExpectedLatency(expectedLatency, nbTimers) {\n    // Expected latencies are adjusted to substract 1 ms per timer, because each\n    // timer may have fired 1ms earlier for Node.js versions < 11.0. See\n    // https://github.com/nodejs/node/issues/10154 for more info.\n    return expectedLatency - nbTimers;\n}\n\ndescribe('request metrics plugin', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server'),\n            handleUncaughtExceptions: true\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.removeAllListeners();\n        SERVER.close(done);\n    });\n\n    it('should return metrics for a given request', function(done) {\n        SERVER.on('uncaughtException', function(req, res, route, err) {\n            assert.ifError(err);\n        });\n\n        SERVER.on(\n            'after',\n            restify.plugins.metrics(\n                {\n                    server: SERVER\n                },\n                function(err, metrics, req, res, route) {\n                    assert.ifError(err);\n\n                    assert.isObject(metrics, 'metrics');\n                    assert.equal(metrics.statusCode, 202);\n                    assert.isAtLeast(\n                        metrics.preLatency,\n                        adjustExpectedLatency(50, 1)\n                    );\n                    assert.isAtLeast(\n                        metrics.useLatency,\n                        adjustExpectedLatency(50, 1)\n                    );\n                    assert.isAtLeast(\n                        metrics.routeLatency,\n                        adjustExpectedLatency(50, 1)\n                    );\n                    assert.isAtLeast(\n                        metrics.latency,\n                        adjustExpectedLatency(150, 3)\n                    );\n                    assert.isAtLeast(\n                        metrics.totalLatency,\n                        adjustExpectedLatency(150, 3)\n                    );\n                    assert.equal(metrics.path, '/foo');\n                    assert.equal(metrics.connectionState, undefined);\n                    assert.equal(metrics.method, 'GET');\n                    assert.isNumber(metrics.inflightRequests);\n\n                    assert.isObject(req, 'req');\n                    assert.isObject(res, 'res');\n                    assert.isObject(route, 'route');\n                }\n            )\n        );\n\n        SERVER.pre(function(req, res, next) {\n            setTimeout(function() {\n                return next();\n            }, 50);\n        });\n\n        SERVER.use(function(req, res, next) {\n            setTimeout(function() {\n                return next();\n            }, 50);\n        });\n\n        SERVER.get('/foo', function(req, res, next) {\n            setTimeout(function() {\n                res.send(202, 'hello world');\n                return next();\n            }, 50);\n        });\n\n        CLIENT.get('/foo?a=1', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 202);\n            return done();\n        });\n    });\n\n    it('should return metrics with pre error', function(done) {\n        SERVER.on('uncaughtException', function(req, res, route, err) {\n            assert.ok(err);\n            res.send(err);\n        });\n\n        SERVER.on(\n            'after',\n            restify.plugins.metrics(\n                {\n                    server: SERVER\n                },\n                function(err, metrics, req, res, route) {\n                    assert.ok(err);\n\n                    assert.isObject(metrics, 'metrics');\n                    assert.isAtLeast(\n                        metrics.preLatency,\n                        adjustExpectedLatency(50, 1)\n                    );\n                    assert.equal(metrics.useLatency, null);\n                    assert.equal(metrics.routeLatency, null);\n                    assert.isAtLeast(\n                        metrics.latency,\n                        adjustExpectedLatency(50, 1)\n                    );\n\n                    return done();\n                }\n            )\n        );\n\n        SERVER.pre(function(req, res, next) {\n            setTimeout(function() {\n                return next(new Error('My Error'));\n            }, 50);\n        });\n\n        CLIENT.get('/foo?a=1', function(err, _, res) {\n            assert.ok(err);\n        });\n    });\n\n    it('should return metrics with use error', function(done) {\n        SERVER.on('uncaughtException', function(req, res, route, err) {\n            assert.ok(err);\n            res.send(err);\n        });\n\n        SERVER.on(\n            'after',\n            restify.plugins.metrics(\n                {\n                    server: SERVER\n                },\n                function(err, metrics, req, res, route) {\n                    assert.ok(err);\n\n                    assert.isObject(metrics, 'metrics');\n                    assert.isAtLeast(metrics.preLatency, 0);\n                    assert.isAtLeast(\n                        metrics.useLatency,\n                        adjustExpectedLatency(50, 1)\n                    );\n                    assert.equal(metrics.routeLatency, null);\n                    assert.isAtLeast(\n                        metrics.latency,\n                        adjustExpectedLatency(50, 1)\n                    );\n\n                    return done();\n                }\n            )\n        );\n\n        SERVER.use(function(req, res, next) {\n            setTimeout(function() {\n                return next(new Error('My Error'));\n            }, 50);\n        });\n\n        SERVER.get('/foo', function(req, res, next) {\n            res.send(202, 'hello world');\n            return next();\n        });\n\n        CLIENT.get('/foo?a=1', function(err, _, res) {\n            assert.ok(err);\n        });\n    });\n\n    it(\"should return 'RequestCloseError' err\", function(done) {\n        // we test that the client times out and closes the request. server\n        // flushes request responsibly but connectionState should indicate it\n        // was closed by the server.\n\n        SERVER.on('uncaughtException', function(req, res, route, err) {\n            assert.ifError(err);\n        });\n\n        SERVER.on(\n            'after',\n            restify.plugins.metrics(\n                {\n                    server: SERVER\n                },\n                function(err, metrics, req, res, route) {\n                    assert.ok(err);\n                    assert.equal(err.name, 'RequestCloseError');\n\n                    assert.isObject(metrics, 'metrics');\n                    // router doesn't run\n                    assert.equal(metrics.statusCode, 444);\n\n                    assert.isAtLeast(\n                        metrics.preLatency,\n                        adjustExpectedLatency(50, 1)\n                    );\n                    assert.isAtLeast(\n                        metrics.useLatency,\n                        adjustExpectedLatency(50, 1)\n                    );\n                    assert.isAtLeast(\n                        metrics.routeLatency,\n                        adjustExpectedLatency(250, 1)\n                    );\n\n                    // The request timeout value is 200 client side, but the\n                    // overall latency is computed on the server, so we're\n                    // tolerating a 10ms difference. This is inherently flaky.\n                    assert.isAtLeast(metrics.latency, 200 - 10);\n\n                    // latency should be lower as request timeouts\n                    assert.isAbove(metrics.routeLatency, metrics.latency);\n                    assert.equal(metrics.path, '/foo');\n                    assert.equal(metrics.method, 'GET');\n                    assert.equal(metrics.connectionState, 'close');\n                    assert.isNumber(metrics.inflightRequests);\n                    return done();\n                }\n            )\n        );\n\n        SERVER.pre(function(req, res, next) {\n            setTimeout(function() {\n                return next();\n            }, 50);\n        });\n\n        SERVER.use(function(req, res, next) {\n            setTimeout(function() {\n                return next();\n            }, 50);\n        });\n\n        SERVER.get(\n            '/foo',\n            function(req, res, next) {\n                setTimeout(function() {\n                    return next();\n                }, 250);\n            },\n            function(req, res, next) {\n                assert.fail('Client has already closed request');\n                res.send(202, 'hello world');\n                return next();\n            }\n        );\n\n        CLIENT.get(\n            {\n                path: '/foo?a=1',\n                requestTimeout: 200\n            },\n            function(err, _, res) {\n                // request should timeout\n                assert.ok(err);\n                assert.equal(err.name, 'RequestTimeoutError');\n            }\n        );\n    });\n\n    it('should handle uncaught exceptions', function(done) {\n        // we test that the client times out and closes the request. server\n        // flushes request responsibly but connectionState should indicate it\n        // was closed by the server.\n\n        SERVER.on(\n            'after',\n            restify.plugins.metrics(\n                {\n                    server: SERVER\n                },\n                // TODO: test timeouts if any of the following asserts fails\n                function(err, metrics, req, res, route) {\n                    assert.ok(err);\n                    assert.equal(err.name, 'Error');\n                    assert.equal(err.message, 'boom');\n                    assert.isObject(err.domain);\n\n                    assert.isObject(metrics, 'metrics');\n                    assert.equal(metrics.statusCode, 500);\n                    assert.isNumber(metrics.latency);\n                    assert.equal(metrics.path, '/foo');\n                    assert.equal(metrics.method, 'GET');\n                    assert.equal(metrics.connectionState, undefined);\n                    assert.isNumber(metrics.inflightRequests);\n                    return done();\n                }\n            )\n        );\n\n        SERVER.get('/foo', function(req, res, next) {\n            throw new Error('boom');\n        });\n\n        CLIENT.get('/foo?a=1', function(err, _, res) {\n            assert.ok(err);\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/multipart.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// core requires\nvar http = require('http');\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar PORT;\n\ndescribe('multipart parser', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.get('/', function respond(req, res, next) {\n            res.send();\n            next();\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        SERVER.close(done);\n    });\n\n    it('body multipart ok', function(done) {\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.post('/multipart/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.mood, 'happy');\n            assert.equal(req.params.endorphins, '9000');\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/multipart/foo?mood=happy',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'multipart/form-data; boundary=huff'\n            }\n        };\n\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n\n        client.write(\n            '--huff\\r\\nContent-Disposition: form-data; ' +\n                'name=\"endorphins\"\\r\\n\\r\\n9000\\r\\n--huff--'\n        );\n        client.end();\n    });\n\n    it('gh-847 body multipart no files ok', function(done) {\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapFiles: true,\n                mapParams: true,\n                keepExtensions: true,\n                uploadDir: '/tmp/',\n                override: true\n            })\n        );\n\n        SERVER.post('/multipart/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.mood, 'happy');\n            assert.equal(req.params.endorphins, '9000');\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/multipart/foo?mood=happy',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'multipart/form-data; boundary=huff'\n            }\n        };\n\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n\n        // don't actually upload a file\n        client.write(\n            '--huff\\r\\nContent-Disposition: form-data; ' +\n                'name=\"endorphins\"\\r\\n\\r\\n9000\\r\\n--huff--'\n        );\n        client.end();\n    });\n\n    it('gh-847 body multipart files ok', function(done) {\n        var shine =\n            'Well you wore out your welcome with random precision, ' +\n            'rode on the steel breeze. Come on you raver, you seer of ' +\n            'visions, come on you painter, you piper, you prisoner, and shine!';\n        var echoes =\n            'Overhead the albatross hangs motionless upon the air ' +\n            'And deep beneath the rolling waves in labyrinths of coral ' +\n            'caves The echo of a distant tide Comes willowing across the ' +\n            'sand And everything is green and submarine';\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapFiles: true,\n                mapParams: true,\n                keepExtensions: true,\n                uploadDir: '/tmp/',\n                override: true\n            })\n        );\n        SERVER.post('/multipart/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.mood, 'happy');\n            assert.equal(req.params.endorphins, '12');\n            assert.ok(req.params.shine);\n            assert.ok(req.params.echoes);\n            assert.equal(req.params.shine.toString('utf8'), shine);\n            assert.equal(req.params.echoes.toString('utf8'), echoes);\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/multipart/foo?mood=happy',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'multipart/form-data; boundary=huff'\n            }\n        };\n\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n\n        client.write('--huff\\r\\n');\n        client.write(\n            'Content-Disposition: form-data; name=\"endorphins\"\\r\\n\\r\\n'\n        );\n        client.write('12\\r\\n');\n\n        client.write('--huff\\r\\n');\n\n        client.write(\n            'Content-Disposition: form-data; name=\"shine\"; ' +\n                'filename=\"shine.txt\"\\r\\n'\n        );\n        client.write('Content-Type: text/plain\\r\\n\\r\\n');\n        client.write(shine + '\\r\\n');\n        client.write('--huff\\r\\n');\n\n        client.write(\n            'Content-Disposition: form-data; name=\"echoes\"; ' +\n                'filename=\"echoes.txt\"\\r\\n'\n        );\n        client.write('Content-Type: text/plain\\r\\n\\r\\n');\n        client.write(echoes + '\\r\\n');\n        client.write('--huff--');\n\n        client.end();\n    });\n\n    it('body multipart ok custom handling', function(done) {\n        var detailsString =\n            'High endorphin levels make you happy. ' +\n            'Mostly... I guess. Whatever.';\n        SERVER.post(\n            '/multipart/:id',\n            restify.plugins.bodyParser({\n                multipartHandler: function(part) {\n                    var buffer = new Buffer(0);\n                    part.on('data', function(data) {\n                        buffer = Buffer.concat([data]);\n                    });\n\n                    part.on('end', function() {\n                        assert.equal(part.name, 'endorphins');\n                        assert.equal(buffer.toString('ascii'), '12');\n                    });\n                },\n                multipartFileHandler: function(part) {\n                    var buffer = new Buffer(0);\n                    part.on('data', function(data) {\n                        buffer = Buffer.concat([data]);\n                    });\n\n                    part.on('end', function() {\n                        assert.equal(part.name, 'details');\n                        assert.equal(part.filename, 'mood_details.txt');\n                        assert.equal(buffer.toString('ascii'), detailsString);\n                    });\n                },\n                mapParams: false\n            }),\n            function(req, res, next) {\n                res.send();\n                next();\n            }\n        );\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/multipart/foo?mood=sad',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'multipart/form-data; boundary=huff'\n            }\n        };\n\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n\n        client.write('--huff\\r\\n');\n        client.write(\n            'Content-Disposition: form-data; name=\"endorphins\"\\r\\n\\r\\n'\n        );\n        client.write('12\\r\\n');\n\n        client.write('--huff\\r\\n');\n\n        // jscs:disable maximumLineLength\n        client.write(\n            'Content-Disposition: form-data; name=\"details\"; ' +\n                'filename=\"mood_details.txt\"\\r\\n'\n        );\n\n        // jscs:enable maximumLineLength\n        client.write('Content-Type: text/plain\\r\\n\\r\\n');\n        client.write(detailsString + '\\r\\n');\n        client.write('--huff--');\n\n        client.end();\n    });\n\n    it('restify-GH-694 pass hash option through to Formidable', function(done) {\n        var content = 'Hello World!';\n        var hash = '2ef7bde608ce5404e97d5f042f95f89f1c232871';\n        SERVER.post(\n            '/multipart',\n            restify.plugins.bodyParser({ hash: 'sha1' }),\n            function(req, res, next) {\n                assert.equal(req.files.details.hash, hash);\n                res.send();\n                next();\n            }\n        );\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/multipart',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'multipart/form-data; boundary=huff'\n            }\n        };\n\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n\n        client.write('--huff\\r\\n');\n\n        // jscs:disable maximumLineLength\n        client.write(\n            'Content-Disposition: form-data; name=\"details\"; ' +\n                'filename=\"mood_details.txt\"\\r\\n'\n        );\n\n        // jscs:enable maximumLineLength\n        client.write('Content-Type: text/plain\\r\\n\\r\\n');\n        client.write(content + '\\r\\n');\n        client.write('--huff--');\n\n        client.end();\n    });\n\n    it('Ensure maxFileSize change is enforced', function(done) {\n        var shine =\n            'Well you wore out your welcome with random precision, ' +\n            'rode on the steel breeze. Come on you raver, you seer of ' +\n            'visions, come on you painter, you piper, you prisoner, and shine!';\n        var echoes =\n            'Overhead the albatross hangs motionless upon the air ' +\n            'And deep beneath the rolling waves in labyrinths of coral ' +\n            'caves The echo of a distant tide Comes willowing across the ' +\n            'sand And everything is green and submarine';\n\n        var shortest = Math.min(shine.length, echoes.length);\n\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.use(\n            restify.plugins.bodyParser({\n                mapFiles: true,\n                mapParams: true,\n                keepExtensions: true,\n                uploadDir: '/tmp/',\n                override: true,\n                // Set limit to shortest of the 'files',\n                //  longer will trigger an error.\n                maxFileSize: shortest\n            })\n        );\n        SERVER.post('/multipart/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.mood, 'happy');\n            assert.equal(req.params.endorphins, '12');\n            assert.ok(req.params.shine);\n            assert.ok(req.params.echoes);\n            assert.equal(req.params.shine.toString('utf8'), shine);\n            assert.equal(req.params.echoes.toString('utf8'), echoes);\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: '/multipart/foo?mood=happy',\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'multipart/form-data; boundary=huff'\n            }\n        };\n\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 400);\n            var body = '';\n            res.on('data', function(d) {\n                body += d;\n            });\n            res.on('end', function() {\n                var rsp = JSON.parse(body);\n                assert.equal(rsp.code, 'BadRequest');\n                assert.equal(\n                    rsp.message.substring(0, 30),\n                    'maxFileSize exceeded, received'\n                );\n                done();\n            });\n        });\n\n        client.write('--huff\\r\\n');\n        client.write(\n            'Content-Disposition: form-data; name=\"endorphins\"\\r\\n\\r\\n'\n        );\n        client.write('12\\r\\n');\n\n        client.write('--huff\\r\\n');\n\n        client.write(\n            'Content-Disposition: form-data; name=\"shine\"; ' +\n                'filename=\"shine.txt\"\\r\\n'\n        );\n        client.write('Content-Type: text/plain\\r\\n\\r\\n');\n        client.write(shine + '\\r\\n');\n        client.write('--huff\\r\\n');\n\n        client.write(\n            'Content-Disposition: form-data; name=\"echoes\"; ' +\n                'filename=\"echoes.txt\"\\r\\n'\n        );\n        client.write('Content-Type: text/plain\\r\\n\\r\\n');\n        client.write(echoes + '\\r\\n');\n        client.write('--huff--');\n\n        client.end();\n    });\n});\n"
  },
  {
    "path": "test/plugins/oauth2.test.js",
    "content": "// Copyright 2016 Brian Aabel, Inc.  All rights reserved.\n\n'use strict';\n/* eslint-disable func-names */\n\nvar http = require('http');\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\nvar TEST_TOKEN = '18926970-A-nMnSHDqg8Fsunm6Qx1cF1APp';\n\ndescribe('oauth2 token parser', function() {\n    before(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.use(restify.plugins.bodyParser());\n        SERVER.use(restify.plugins.oauth2TokenParser());\n\n        SERVER.get('/', function respond(req, res, next) {\n            res.send();\n            next();\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    after(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('should parse oauth2 token from authorization header', function(done) {\n        var opts = {\n            path: '/test1/auth-header',\n            headers: {\n                Authorization: 'Bearer ' + TEST_TOKEN\n            }\n        };\n        SERVER.get('/test1/auth-header', function(req, res, next) {\n            assert.isNotNull(req.oauth2.accessToken);\n            assert.equal(req.oauth2.accessToken, TEST_TOKEN);\n            res.send();\n            next();\n        });\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    // eslint-disable-next-line\n    it('should do nothing (token is null) if there is no oauth2 token set', function(done) {\n        var opts = {\n            path: '/test2/do/nothing'\n        };\n        SERVER.get(opts, function(req, res, next) {\n            assert.isNull(req.oauth2.accessToken);\n            assert.equal(res.statusCode, 200);\n            res.send();\n            next();\n        });\n        CLIENT.get(opts, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should parse from request body', function(done) {\n        var test3Url = '/test3/contenttype/ok';\n\n        SERVER.post(test3Url, function(req, res, next) {\n            assert.isNotNull(req.oauth2.accessToken);\n            assert.equal(req.oauth2.accessToken, TEST_TOKEN);\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: test3Url,\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write('access_token=' + TEST_TOKEN);\n        client.end();\n    });\n\n    // eslint-disable-next-line\n    it('should parse oauth2 token from request body(case-insensitive)', function(done) {\n        var test4Url = '/test4/contenttype/mixedcase';\n\n        SERVER.post(test4Url, function(req, res, next) {\n            assert.isNotNull(req.oauth2.accessToken);\n            assert.equal(req.oauth2.accessToken, TEST_TOKEN);\n            res.send();\n            next();\n        });\n\n        var opts = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: test4Url,\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'APPLICATION/x-www-form-urlencoded'\n            }\n        };\n        var client = http.request(opts, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write('access_token=' + TEST_TOKEN);\n        client.end();\n    });\n\n    it('should ignore token from request body', function(done) {\n        var test5Url = '/test5/contenttype/missing/1';\n\n        SERVER.post(test5Url, function(req, res, next) {\n            assert.isNull(req.oauth2.accessToken);\n            res.send(200);\n            next();\n        });\n\n        var opts5 = {\n            hostname: '127.0.0.1',\n            port: PORT,\n            path: test5Url,\n            agent: false,\n            method: 'POST',\n            headers: {\n                'Content-Type': 'text/xml'\n            }\n        };\n        var client = http.request(opts5, function(res) {\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n        client.write('access_token=' + TEST_TOKEN);\n        client.end();\n    });\n\n    // eslint-disable-next-line\n    it('should fail if more than one method is used to set the oauth2 token', function(done) {\n        SERVER.post('/test6/multi/method/fail', function(req, res, next) {\n            assert.isNull(req.oauth2.accessToken);\n            res.send();\n            next();\n        });\n        var opts = {\n            path: '/test6/multi/method/fail',\n            headers: {\n                Authorization: 'Bearer ' + TEST_TOKEN,\n                'Content-Type': 'application/x-www-form-urlencoded'\n            }\n        };\n\n        CLIENT.post(opts, { access_token: TEST_TOKEN }, function(err, _, res) {\n            assert.ok(err);\n            assert.equal(res.statusCode, 400);\n            done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/plugins.test.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar pino = require('pino');\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\nvar sanitizePath = require('../../lib/plugins/pre/prePath.js');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar CLIENT;\nvar SERVER;\n\ndescribe('all other plugins', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server'),\n            version: ['2.0.0', '0.5.4', '1.4.3']\n        });\n\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    describe('date parser', function() {\n        it('should reject expired request', function(done) {\n            SERVER.use(restify.plugins.dateParser());\n\n            SERVER.get('/', function respond(req, res, next) {\n                res.send();\n                next();\n            });\n\n            var opts = {\n                path: '/',\n                headers: {\n                    date: 'Tue, 15 Nov 1994 08:12:31 GMT'\n                }\n            };\n\n            CLIENT.get(opts, function(err, _, res) {\n                assert.ok(err);\n                assert.ok(/Date header .+ is too old/.test(err.message));\n                assert.equal(res.statusCode, 400);\n                done();\n            });\n        });\n    });\n\n    describe('request logger', function() {\n        it('tests the requestLoggers extra header properties', function(done) {\n            var key = 'x-request-uuid';\n            var badKey = 'x-foo-bar';\n            var getPath = '/requestLogger/extraHeaders';\n            var headers = [key, badKey];\n\n            SERVER.use(restify.plugins.requestLogger({ headers: headers }));\n            SERVER.get(getPath, function(req, res, next) {\n                var childings = req.log[pino.symbols.chindingsSym];\n                assert.match(childings, /\"x-request-uuid\":\"foo-for-eva\"/);\n                assert.notMatch(childings, /x-foo-bar/);\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {}\n            };\n            obj.headers[key] = 'foo-for-eva';\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.ifError(err);\n                done();\n            });\n        });\n\n        it('adds the request id to logs', function(done) {\n            SERVER.use(restify.plugins.requestLogger());\n            SERVER.get('/requestLogger/test', function(req, res, next) {\n                var childings = req.log[pino.symbols.chindingsSym];\n                assert.match(childings, /\"req_id\":\"[0-9A-F-]+\"/i);\n                res.send();\n                next();\n            });\n            CLIENT.get('/requestLogger/test', function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.ifError(err);\n                done();\n            });\n        });\n\n        it('adds the request id with a custom field name', function(done) {\n            SERVER.use(\n                restify.plugins.requestLogger({ requestIdFieldName: 'traceId' })\n            );\n            SERVER.get('/requestLogger/test', function(req, res, next) {\n                var childings = req.log[pino.symbols.chindingsSym];\n                assert.match(childings, /\"traceId\":\"[0-9A-F-]+\"/i);\n                assert.notMatch(childings, /\"req_id\"/);\n                res.send();\n                next();\n            });\n            CLIENT.get('/requestLogger/test', function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.ifError(err);\n                done();\n            });\n        });\n    });\n\n    describe('full response', function() {\n        it('full response', function(done) {\n            SERVER.use(restify.plugins.fullResponse());\n            SERVER.get('/bar/:id', function tester2(req, res, next) {\n                assert.ok(req.params);\n                assert.equal(req.params.id, 'bar');\n                res.send();\n                next();\n            });\n\n            CLIENT.get('/bar/bar', function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                var headers = res.headers;\n                assert.ok(headers, 'headers ok');\n                assert.ok(headers.date);\n                assert.ok(headers['request-id']);\n                assert.ok(headers['response-time'] >= 0);\n                assert.equal(headers.server, 'restify');\n                assert.equal(headers.connection, 'Keep-Alive');\n                done();\n            });\n        });\n    });\n\n    describe('context', function() {\n        it('set and get request context', function(done) {\n            SERVER.pre(restify.plugins.pre.context());\n\n            var asserted = false;\n            var expectedData = {\n                pink: 'floyd'\n            };\n            SERVER.get('/context', [\n                function(req, res, next) {\n                    req.set('pink', 'floyd');\n                    return next();\n                },\n                function(req, res, next) {\n                    assert.equal('floyd', req.get('pink'));\n                    assert.deepEqual(expectedData, req.getAll());\n                    asserted = true;\n                    res.send(200);\n                    return next();\n                }\n            ]);\n\n            CLIENT.get('/context', function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.ok(asserted);\n                done();\n            });\n        });\n\n        it('should throw if set key is not string', function(done) {\n            SERVER.pre(restify.plugins.pre.context());\n\n            var asserted = false;\n\n            SERVER.get('/context', [\n                function(req, res, next) {\n                    try {\n                        req.set({}, 'floyd');\n                    } catch (e) {\n                        asserted = true;\n                        res.send(200);\n                    }\n                    return next();\n                }\n            ]);\n\n            CLIENT.get('/context', function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.ok(asserted);\n                done();\n            });\n        });\n\n        it('should throw if set key is empty string', function(done) {\n            SERVER.pre(restify.plugins.pre.context());\n\n            var asserted = false;\n\n            SERVER.get('/context', [\n                function(req, res, next) {\n                    try {\n                        req.set('', 'floyd');\n                    } catch (e) {\n                        asserted = true;\n                        res.send(200);\n                    }\n                    return next();\n                }\n            ]);\n\n            CLIENT.get('/context', function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.ok(asserted);\n                done();\n            });\n        });\n\n        it('should throw if get key is not string', function(done) {\n            SERVER.pre(restify.plugins.pre.context());\n\n            var asserted = false;\n\n            SERVER.get('/context', [\n                function(req, res, next) {\n                    try {\n                        req.get({});\n                    } catch (e) {\n                        asserted = true;\n                        res.send(200);\n                    }\n                    return next();\n                }\n            ]);\n\n            CLIENT.get('/context', function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.ok(asserted);\n                done();\n            });\n        });\n\n        it('should throw if get key is empty string', function(done) {\n            SERVER.pre(restify.plugins.pre.context());\n\n            var asserted = false;\n\n            SERVER.get('/context', [\n                function(req, res, next) {\n                    try {\n                        req.get('');\n                    } catch (e) {\n                        asserted = true;\n                        res.send(200);\n                    }\n                    return next();\n                }\n            ]);\n\n            CLIENT.get('/context', function(err, _, res) {\n                assert.ifError(err);\n                assert.equal(res.statusCode, 200);\n                assert.ok(asserted);\n                done();\n            });\n        });\n    });\n\n    describe('sanitizePath', function() {\n        // Ensure it santizies potential edge cases correctly\n        var tests = {\n            input: [\n                '////foo////', //excess padding on both ends\n                'bar/foo/', // trailing slash\n                'bar/foo/////', // multiple trailing slashes\n                'foo////bar', // multiple slashes inbetween\n                '////foo', // multiple at beginning\n                '/foo/bar' // don't mutate\n            ],\n            output: [\n                '/foo',\n                'bar/foo',\n                'bar/foo',\n                'foo/bar',\n                '/foo',\n                '/foo/bar'\n            ],\n            description: [\n                'should clean excess padding on both ends',\n                'should clean trailing slash',\n                'should clean multiple trailing slashes',\n                'should clean multiple slashes inbetween',\n                'should clean multiple at beginning',\n                'dont mutate correct urls'\n            ]\n        };\n\n        for (var i = 0; i < tests.input.length; i++) {\n            // eslint-disable-next-line wrap-iife\n            (function() {\n                var index = i;\n                it(tests.description[index], function(done) {\n                    var req = { url: tests.input[index] };\n                    sanitizePath()(req, null, function() {\n                        assert.equal(req.url, tests.output[index]);\n                        done();\n                    });\n                });\n            })();\n        }\n    });\n});\n"
  },
  {
    "path": "test/plugins/query.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('query parser', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('restify-GH-124 should return empty query', function(done) {\n        SERVER.use(restify.plugins.queryParser());\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.getQuery(), '');\n            assert.deepEqual(req.query, {});\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('req.getQuery() should return with raw query string', function(done) {\n        SERVER.use(restify.plugins.queryParser());\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.getQuery(), 'a=1');\n            assert.deepEqual(req.query, { a: '1' });\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?a=1', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should parse req.query and req.params independently', function(done) {\n        SERVER.use(restify.plugins.queryParser());\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.query.id, 'bar');\n            assert.equal(req.query.name, 'markc');\n            assert.equal(req.params.id, 'foo');\n            assert.notDeepEqual(req.query, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=markc', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should map req.query onto req.params', function(done) {\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.equal(req.params.name, 'markc');\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=markc', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should take req.query and stomp on req.params', function(done) {\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'bar');\n            assert.equal(req.params.name, 'markc');\n            assert.deepEqual(req.query, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=markc', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should parse associative array syntax', function(done) {\n        SERVER.use(restify.plugins.queryParser());\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.isObject(req.query.name);\n            assert.equal(req.query.name.first, 'mark');\n            assert.equal(req.query.name.last, 'cavage');\n            res.send();\n            next();\n        });\n\n        var p = '/query/foo?name[first]=mark&name[last]=cavage';\n        CLIENT.get(p, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should parse array syntax', function(done) {\n        SERVER.use(restify.plugins.queryParser());\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.isArray(req.query.char);\n            assert.deepEqual(req.query.char, ['a', 'b', 'c']);\n            res.send();\n            next();\n        });\n\n        var p = '/query/foo?char[]=a&char[]=b&char[]=c';\n        CLIENT.get(p, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should parse nested array syntax', function(done) {\n        SERVER.use(restify.plugins.queryParser());\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'foo');\n            assert.isObject(req.query.pizza);\n            assert.isArray(req.query.pizza.left);\n            assert.isArray(req.query.pizza.right);\n            assert.deepEqual(req.query.pizza.left, ['ham', 'bacon']);\n            assert.deepEqual(req.query.pizza.right, ['pineapple']);\n            res.send();\n            next();\n        });\n\n        var p =\n            '/query/foo?pizza[left][]=ham&pizza[left][]=bacon&' +\n            'pizza[right][]=pineapple';\n        CLIENT.get(p, function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('restify-GH-59 Query params with / result in a 404', function(done) {\n        SERVER.use(restify.plugins.queryParser());\n\n        SERVER.get('/', function tester(req, res, next) {\n            res.send('hello world');\n            next();\n        });\n\n        CLIENT.get('/?foo=bar/foo', function(err, _, res, obj) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            assert.equal(obj, 'hello world');\n            done();\n        });\n    });\n\n    it('restify-GH-323: <url>/<path>/?<queryString> broken', function(done) {\n        SERVER.pre(restify.plugins.pre.sanitizePath());\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.get('/hello/:name', function(req, res, next) {\n            res.send(req.params);\n        });\n\n        CLIENT.get('/hello/foo/?bar=baz', function(err, _, __, obj) {\n            assert.ifError(err);\n            assert.deepEqual(obj, { name: 'foo', bar: 'baz' });\n            done();\n        });\n    });\n\n    it('<url>/?<queryString> broken', function(done) {\n        SERVER.pre(restify.plugins.pre.sanitizePath());\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true\n            })\n        );\n        SERVER.get('/', function(req, res, next) {\n            res.send(req.params);\n        });\n\n        CLIENT.get('/?bar=baz', function(err, _, __, obj) {\n            assert.ifError(err);\n            assert.deepEqual(obj, { bar: 'baz' });\n            done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/reqIdHeaders.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external modules\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\nvar validator = require('validator');\n\n// internal files\nvar helper = require('../lib/helper');\n\ndescribe('request id headers', function() {\n    var SERVER;\n    var CLIENT;\n    var PORT;\n\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.pre(\n            restify.plugins.pre.reqIdHeaders({\n                headers: ['x-req-id-a', 'x-req-id-b']\n            })\n        );\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n            return done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(function() {\n            CLIENT = null;\n            SERVER = null;\n            return done();\n        });\n    });\n\n    it('GH-1086: should reuse request id when available', function(done) {\n        SERVER.get('/1', function(req, res, next) {\n            // the 12345 value is set when the client is created.\n            assert.ok(req.headers.hasOwnProperty('x-req-id-a'));\n            assert.equal(req.getId(), req.headers['x-req-id-a']);\n            res.send('hello world');\n            return next();\n        });\n\n        // create new client since we new specific headers\n        CLIENT = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + PORT,\n            headers: {\n                'x-req-id-a': 12345\n            }\n        });\n\n        CLIENT.get('/1', function(err, req, res, data) {\n            assert.ifError(err);\n            assert.equal(data, 'hello world');\n            return done();\n        });\n    });\n\n    it('GH-1086: should use second request id when available', function(done) {\n        SERVER.get('/1', function(req, res, next) {\n            assert.ok(req.headers.hasOwnProperty('x-req-id-b'));\n            assert.equal(req.getId(), req.headers['x-req-id-b']);\n            res.send('hello world');\n            return next();\n        });\n\n        // create new client since we new specific headers\n        CLIENT = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + PORT,\n            headers: {\n                'x-req-id-b': 678910\n            }\n        });\n\n        CLIENT.get('/1', function(err, req, res, data) {\n            assert.ifError(err);\n            assert.equal(data, 'hello world');\n            return done();\n        });\n    });\n\n    // eslint-disable-next-line\n    it('GH-1086: should use default uuid request id if none provided', function(done) {\n        SERVER.get('/1', function(req, res, next) {\n            assert.ok(req.getId());\n            assert.ok(validator.isUUID(req.getId()));\n            res.send('hello world');\n            return next();\n        });\n\n        // create new client since we new specific headers\n        CLIENT = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + PORT\n        });\n\n        CLIENT.get('/1', function(err, req, res, data) {\n            assert.ifError(err);\n            assert.equal(data, 'hello world');\n            return done();\n        });\n    });\n\n    it('GH-1086: empty request id should be ignored', function(done) {\n        SERVER.get('/1', function(req, res, next) {\n            assert.ok(req.headers.hasOwnProperty('x-req-id-b'));\n            assert.equal(req.getId(), req.headers['x-req-id-b']);\n            res.send('hello world');\n            return next();\n        });\n\n        // create new client since we new specific headers\n        CLIENT = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + PORT,\n            headers: {\n                'x-req-id-a': '',\n                'x-req-id-b': 12345\n            }\n        });\n\n        CLIENT.get('/1', function(err, req, res, data) {\n            assert.ifError(err);\n            assert.equal(data, 'hello world');\n            return done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/requestExpiry.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external modules\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local modules\nvar helper = require('../lib/helper');\n\n// globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('request expiry parser', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    describe('constructor', function() {\n        it('should throw if no headers passed in', function(done) {\n            try {\n                SERVER.use(restify.plugins.requestExpiry({}));\n            } catch (e) {\n                done();\n            }\n        });\n\n        it('should throw if only timeout header passed in', function(done) {\n            try {\n                SERVER.use(\n                    restify.plugins.requestExpiry({\n                        timeoutHeader: 'foo'\n                    })\n                );\n            } catch (e) {\n                done();\n            }\n        });\n\n        it('should throw if only timeout header passed in', function(done) {\n            try {\n                SERVER.use(\n                    restify.plugins.requestExpiry({\n                        startHeader: 'foo'\n                    })\n                );\n            } catch (e) {\n                done();\n            }\n        });\n    });\n\n    describe('absolute header', function() {\n        it('should timeout due to request expiry', function(done) {\n            var key = 'x-request-expiry';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(restify.plugins.requestExpiry({ absoluteHeader: key }));\n            SERVER.get(getPath, function(req, res, next) {\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-expiry': Date.now() - 100\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.ok(err);\n                assert.equal(res.statusCode, 504);\n                assert.equal(called, false);\n                done();\n            });\n        });\n\n        it('should not timeout due to request expiry', function(done) {\n            var key = 'x-request-expiry';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(restify.plugins.requestExpiry({ absoluteHeader: key }));\n            SERVER.get(getPath, function(req, res, next) {\n                assert.isFalse(req.isExpired());\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-expiry': Date.now() + 100\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.equal(called, true);\n                assert.ifError(err);\n                done();\n            });\n        });\n\n        it('should be ok without request expiry header', function(done) {\n            var key = 'x-request-expiry';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(restify.plugins.requestExpiry({ absoluteHeader: key }));\n            SERVER.get(getPath, function(req, res, next) {\n                // requests never expire if the header is not set\n                assert.isFalse(req.isExpired());\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-expiry': Date.now() + 100\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.equal(called, true);\n                assert.ifError(err);\n                done();\n            });\n        });\n\n        it('should be ok if request expiry header is NaN', function(done) {\n            var key = 'x-request-expiry';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(restify.plugins.requestExpiry({ absoluteHeader: key }));\n            SERVER.get(getPath, function(req, res, next) {\n                // requests never expire if the header is not set\n                assert.isFalse(req.isExpired());\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-expiry':\n                        'I am just a poor boy with my story seldom told'\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.equal(called, true);\n                assert.ifError(err);\n                done();\n            });\n        });\n    });\n\n    describe('timeout header', function() {\n        it('should timeout due to request expiry', function(done) {\n            var startKey = 'x-request-starttime';\n            var timeoutKey = 'x-request-timeout';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(\n                restify.plugins.requestExpiry({\n                    startHeader: startKey,\n                    timeoutHeader: timeoutKey\n                })\n            );\n            SERVER.get(getPath, function(req, res, next) {\n                assert.isFalse(req.isExpired());\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-starttime': Date.now() - 200,\n                    'x-request-timeout': 100\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.ok(err);\n                assert.equal(res.statusCode, 504);\n                assert.equal(called, false);\n                done();\n            });\n        });\n\n        it('should not timeout due to request expiry', function(done) {\n            var startKey = 'x-request-starttime';\n            var timeoutKey = 'x-request-timeout';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(\n                restify.plugins.requestExpiry({\n                    startHeader: startKey,\n                    timeoutHeader: timeoutKey\n                })\n            );\n            SERVER.get(getPath, function(req, res, next) {\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-starttime': Date.now(),\n                    'x-request-timeout': 100\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.equal(called, true);\n                assert.ifError(err);\n                done();\n            });\n        });\n\n        it('should be ok without request expiry header', function(done) {\n            var startKey = 'x-request-starttime';\n            var timeoutKey = 'x-request-timeout';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(\n                restify.plugins.requestExpiry({\n                    startHeader: startKey,\n                    timeoutHeader: timeoutKey\n                })\n            );\n            SERVER.get(getPath, function(req, res, next) {\n                // requests never expire if the header is not set\n                assert.isFalse(req.isExpired());\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {}\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.equal(called, true);\n                assert.ifError(err);\n                done();\n            });\n        });\n\n        it('should be ok if start header is NaN', function(done) {\n            var startKey = 'x-request-starttime';\n            var timeoutKey = 'x-request-timeout';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(\n                restify.plugins.requestExpiry({\n                    startHeader: startKey,\n                    timeoutHeader: timeoutKey\n                })\n            );\n            SERVER.get(getPath, function(req, res, next) {\n                // requests never expire if the header is not set\n                assert.isFalse(req.isExpired());\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-starttime': 'I have squandered my resistance',\n                    'x-request-timeout': 100\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.equal(called, true);\n                assert.ifError(err);\n                done();\n            });\n        });\n\n        it('should be ok if timeout header is NaN', function(done) {\n            var startKey = 'x-request-starttime';\n            var timeoutKey = 'x-request-timeout';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(\n                restify.plugins.requestExpiry({\n                    startHeader: startKey,\n                    timeoutHeader: timeoutKey\n                })\n            );\n            SERVER.get(getPath, function(req, res, next) {\n                // requests never expire if the header is not set\n                assert.isFalse(req.isExpired());\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-starttime': Date.now(),\n                    'x-request-timeout': 'For a pocked full of mumbles'\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.equal(called, true);\n                assert.ifError(err);\n                done();\n            });\n        });\n\n        it('should be ok if both headers are NaN', function(done) {\n            var startKey = 'x-request-starttime';\n            var timeoutKey = 'x-request-timeout';\n            var getPath = '/request/expiry';\n            var called = false;\n\n            SERVER.use(\n                restify.plugins.requestExpiry({\n                    startHeader: startKey,\n                    timeoutHeader: timeoutKey\n                })\n            );\n            SERVER.get(getPath, function(req, res, next) {\n                // requests never expire if the header is not set\n                assert.isFalse(req.isExpired());\n                called = true;\n                res.send();\n                next();\n            });\n\n            var obj = {\n                path: getPath,\n                headers: {\n                    'x-request-starttime': 'Such are promises',\n                    'x-request-timeout': 'All lies and jests'\n                }\n            };\n\n            CLIENT.get(obj, function(err, _, res) {\n                assert.equal(res.statusCode, 200);\n                assert.equal(called, true);\n                assert.ifError(err);\n                done();\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/static.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// core requires\nvar fs = require('fs');\nvar path = require('path');\nvar net = require('net');\n\n// external requires\nvar assert = require('chai').assert;\nvar mkdirp = require('mkdirp');\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\nvar rimraf = require('rimraf');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\nvar FILES_TO_DELETE = [];\nvar DIRS_TO_DELETE = [];\n\ndescribe('static resource plugin', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        var i;\n\n        for (i = 0; i < FILES_TO_DELETE.length; ++i) {\n            try {\n                fs.unlinkSync(FILES_TO_DELETE[i]);\n            } catch (err) {\n                /* normal */\n            }\n        }\n\n        for (i = 0; i < DIRS_TO_DELETE.length; ++i) {\n            try {\n                rimraf.sync(DIRS_TO_DELETE[i]);\n            } catch (err) {\n                /* normal */\n            }\n        }\n\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    function serveStaticTest(done, testDefault, tmpDir, regex, staticFile) {\n        var staticContent = '{\"content\": \"abcdefg\"}';\n        var staticObj = JSON.parse(staticContent);\n        var testDir = 'public';\n        var testFileName = 'index.json';\n        var routeName = 'GET wildcard';\n        var tmpPath = path.join(__dirname, '../', tmpDir);\n\n        mkdirp(tmpPath, function(err) {\n            assert.ifError(err);\n            DIRS_TO_DELETE.push(tmpPath);\n            var folderPath = path.join(tmpPath, testDir);\n\n            mkdirp(folderPath, function(err2) {\n                assert.ifError(err2);\n\n                DIRS_TO_DELETE.push(folderPath);\n                var file = path.join(folderPath, testFileName);\n\n                fs.writeFile(file, staticContent, function(err3) {\n                    assert.ifError(err3);\n                    FILES_TO_DELETE.push(file);\n                    var p = '/' + testDir + '/' + testFileName;\n                    var opts = { directory: tmpPath };\n\n                    if (staticFile) {\n                        opts.file = p;\n                    }\n\n                    if (testDefault) {\n                        p = '/' + testDir + '/';\n                        opts.default = testFileName;\n                        routeName += ' with default';\n                    }\n\n                    SERVER.get(\n                        {\n                            path: '/' + testDir + '/*',\n                            name: routeName\n                        },\n                        restify.plugins.serveStatic(opts)\n                    );\n\n                    CLIENT.get(p, function(err4, req, res, obj) {\n                        assert.ifError(err4);\n                        assert.equal(\n                            res.headers['cache-control'],\n                            'public, max-age=3600'\n                        );\n                        assert.deepEqual(obj, staticObj);\n                        done();\n                    });\n                });\n            });\n        });\n    }\n\n    function testNoAppendPath(done, testDefault, tmpDir, regex, staticFile) {\n        var staticContent = '{\"content\": \"abcdefg\"}';\n        var staticObj = JSON.parse(staticContent);\n        var testDir = 'public';\n        var testFileName = 'index.json';\n        var routeName = 'GET wildcard';\n        var tmpPath = path.join(__dirname, '../', tmpDir);\n\n        mkdirp(tmpPath, function(err) {\n            assert.ifError(err);\n            DIRS_TO_DELETE.push(tmpPath);\n            var folderPath = path.join(tmpPath, testDir);\n\n            mkdirp(folderPath, function(err2) {\n                assert.ifError(err2);\n\n                DIRS_TO_DELETE.push(folderPath);\n                var file = path.join(folderPath, testFileName);\n\n                fs.writeFile(file, staticContent, function(err3) {\n                    assert.ifError(err3);\n                    FILES_TO_DELETE.push(file);\n                    var p = '/' + testDir + '/' + testFileName;\n                    var opts = { directory: folderPath };\n                    opts.appendRequestPath = false;\n\n                    if (staticFile) {\n                        opts.file = testFileName;\n                    }\n\n                    if (testDefault) {\n                        p = '/' + testDir + '/';\n                        opts.default = testFileName;\n                        routeName += ' with default';\n                    }\n\n                    SERVER.get(\n                        {\n                            path: '/' + testDir + '/*',\n                            name: routeName\n                        },\n                        restify.plugins.serveStatic(opts)\n                    );\n\n                    CLIENT.get(p, function(err4, req, res, obj) {\n                        assert.ifError(err4);\n                        assert.equal(\n                            res.headers['cache-control'],\n                            'public, max-age=3600'\n                        );\n                        assert.deepEqual(obj, staticObj);\n                        done();\n                    });\n                });\n            });\n        });\n    }\n\n    it('static serves static files', function(done) {\n        serveStaticTest(done, false, '.tmp');\n    });\n\n    it('static serves static files in nested folders', function(done) {\n        serveStaticTest(done, false, '.tmp/folder');\n    });\n\n    it('static serves static files in with a root regex', function(done) {\n        serveStaticTest(done, false, '.tmp', '/.*');\n    });\n\n    // eslint-disable-next-line\n    it('static serves static files with a root, !greedy, regex', function(done) {\n        serveStaticTest(done, false, '.tmp', '/?.*');\n    });\n\n    it('static serves default file', function(done) {\n        serveStaticTest(done, true, '.tmp');\n    });\n\n    // eslint-disable-next-line\n    it('restify-GH-379 static serves file with parentheses in path', function(done) {\n        serveStaticTest(done, false, '.(tmp)');\n    });\n\n    it('restify-GH-719 serve a specific static file', function(done) {\n        // serve the same default file .tmp/public/index.json\n        // but get it from opts.file\n        serveStaticTest(done, false, '.tmp', null, true);\n    });\n\n    // eslint-disable-next-line\n    it('static serves static file with appendRequestPath = false', function(done) {\n        testNoAppendPath(done, false, '.tmp');\n    });\n\n    // eslint-disable-next-line\n    it('static serves default file with appendRequestPath = false', function(done) {\n        testNoAppendPath(done, true, '.tmp');\n    });\n\n    // eslint-disable-next-line\n    it('restify serve a specific static file with appendRequestPath = false', function(done) {\n        testNoAppendPath(done, false, '.tmp', null, true);\n    });\n\n    it('static responds 404 for missing file', function(done) {\n        var p = '/public/no-such-file.json';\n        var tmpPath = path.join(process.cwd(), '.tmp');\n\n        SERVER.get(\n            '/public/.*',\n            restify.plugins.serveStatic({ directory: tmpPath })\n        );\n\n        CLIENT.get(p, function(err, req, res, obj) {\n            assert.ok(err);\n            assert.strictEqual(err.statusCode, 404);\n            assert.strictEqual(err.restCode, 'ResourceNotFound');\n            done();\n        });\n    });\n\n    // eslint-disable-next-line\n    it('GH-1382 static responds 404 for missing file with percent-codes', function(done) {\n        var p = '/public/no-%22such-file.json';\n        var tmpPath = path.join(process.cwd(), '.tmp');\n\n        SERVER.get(\n            '/public/.*',\n            restify.plugins.serveStatic({ directory: tmpPath })\n        );\n\n        CLIENT.get(p, function(err, req, res, obj) {\n            assert.ok(err);\n            assert.equal(err.statusCode, 404);\n            assert.equal(err.restCode, 'ResourceNotFound');\n            done();\n        });\n    });\n\n    // To ensure this will always get properly restored (even in case of a test\n    // failure) we do it here.\n    var originalCreateReadStream = fs.createReadStream;\n    afterEach(function() {\n        fs.createReadStream = originalCreateReadStream;\n    });\n\n    var TMP_PATH = path.join(__dirname, '../', '.tmp');\n    var RAW_REQUEST =\n        'GET /index.html HTTP/1.1\\r\\n' +\n        'Host: 127.0.0.1:' +\n        PORT +\n        '\\r\\n' +\n        'User-Agent: curl/7.48.0\\r\\n' +\n        'Accept: */*\\r\\n' +\n        '\\r\\n';\n\n    it(\n        'static does not leak the file stream and next() is properly called ' +\n            'when the client disconnects before receiving a reply',\n        function(done) {\n            var streamWasAlreadyCreated = false;\n            var streamWasClosed = false;\n            var socket = new net.Socket();\n\n            fs.createReadStream = function() {\n                // Just in case the code would open more streams in the future.\n                assert.strictEqual(streamWasAlreadyCreated, false);\n                streamWasAlreadyCreated = true;\n\n                var stream = originalCreateReadStream.apply(this, arguments);\n                stream.once('close', function() {\n                    streamWasClosed = true;\n                });\n\n                socket.end();\n                return stream;\n            };\n\n            mkdirp(TMP_PATH, function(err) {\n                assert.ifError(err);\n                DIRS_TO_DELETE.push(TMP_PATH);\n                fs.writeFileSync(\n                    path.join(TMP_PATH, 'index.html'),\n                    'Hello world!'\n                );\n\n                var serve = restify.plugins.serveStatic({\n                    directory: TMP_PATH\n                });\n\n                SERVER.get('/index.html', function(req, res, next) {\n                    serve(req, res, function(nextRoute) {\n                        assert.strictEqual(streamWasClosed, true);\n                        assert.strictEqual(nextRoute, false);\n                        done();\n                    });\n                });\n\n                socket.connect({ host: '127.0.0.1', port: PORT }, function() {\n                    socket.write(RAW_REQUEST, 'utf-8', function(err2, data) {\n                        assert.ifError(err2);\n                    });\n                });\n            });\n        }\n    );\n\n    it(\n        'static does not open a file stream and next() is properly called ' +\n            'when the client disconnects immediately after sending a request',\n        function(done) {\n            fs.createReadStream = function() {\n                assert(false);\n            };\n\n            mkdirp(TMP_PATH, function(err) {\n                assert.ifError(err);\n                DIRS_TO_DELETE.push(TMP_PATH);\n                fs.writeFileSync(\n                    path.join(TMP_PATH, 'index.html'),\n                    'Hello world!'\n                );\n\n                var serve = restify.plugins.serveStatic({\n                    directory: TMP_PATH\n                });\n\n                var socket = new net.Socket();\n                SERVER.get('/index.html', function(req, res, next) {\n                    // closed before serve\n                    socket.on('end', () => {\n                        serve(req, res, function(nextRoute) {\n                            assert.strictEqual(nextRoute, false);\n                            done();\n                        });\n                    });\n                });\n                SERVER.on('after', function(req, res, route, afterErr) {\n                    assert(afterErr.name, 'RequestCloseError');\n                    done();\n                });\n\n                socket.connect({ host: '127.0.0.1', port: PORT }, function() {\n                    socket.write(RAW_REQUEST, 'utf-8', function(err2, data) {\n                        assert.ifError(err2);\n                        socket.end();\n                    });\n                });\n            });\n        }\n    );\n\n    it('static responds 404 for missing file', function(done) {\n        var p = '/public/no-such-file.json';\n        var tmpPath = path.join(process.cwd(), '.tmp');\n\n        SERVER.get(\n            '/public/.*',\n            restify.plugins.serveStatic({ directory: tmpPath })\n        );\n\n        CLIENT.get(p, function(err, req, res, obj) {\n            assert.ok(err);\n            assert.equal(err.statusCode, 404);\n            assert.equal(err.restCode, 'ResourceNotFound');\n            return done();\n        });\n    });\n\n    // eslint-disable-next-line\n    it('GH-1382 static responds 404 for missing file with percent-codes', function(done) {\n        var p = '/public/no-%22such-file.json';\n        var tmpPath = path.join(process.cwd(), '.tmp');\n\n        SERVER.get(\n            '/public/.*',\n            restify.plugins.serveStatic({ directory: tmpPath })\n        );\n\n        CLIENT.get(p, function(err, req, res, obj) {\n            assert.ok(err);\n            assert.equal(err.statusCode, 404);\n            assert.equal(err.restCode, 'ResourceNotFound');\n            return done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/staticFiles.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// core requires\nvar fs = require('fs');\nvar path = require('path');\nvar net = require('net');\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\n\ndescribe('staticFiles plugin - no options', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createStringClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    var STATIC_FILES_PATH = __dirname + '/testStaticFiles';\n    function simpleTests(endpoint, filePath, contentType, done) {\n        var ENDPOINT = endpoint;\n        var fileSuffixPath = filePath;\n        var requestPath = path.join(ENDPOINT, fileSuffixPath);\n        var fileOnDisk = path.join(STATIC_FILES_PATH, fileSuffixPath);\n        if (fileSuffixPath.endsWith('/')) {\n            fileOnDisk = path.join(fileOnDisk, 'index.html');\n        }\n        var fileContent = fs.readFileSync(fileOnDisk, 'utf8');\n        var fileStat = fs.statSync(fileOnDisk);\n        SERVER.get(\n            ENDPOINT + '/*',\n            restify.plugins.serveStaticFiles(path.resolve(STATIC_FILES_PATH))\n        );\n\n        CLIENT.get(encodeURI(requestPath), function(err, req, res, obj) {\n            assert.ifError(err);\n            assert.equal(fileContent, obj);\n            // Verify headers\n            assert.equal(res.headers['cache-control'], 'public, max-age=0');\n            assert.equal(\n                res.headers['content-type'],\n                contentType //'text/html; charset=UTF-8'\n            );\n            assert.exists(res.headers.etag);\n            assert.equal(\n                res.headers['last-modified'],\n                fileStat.mtime.toUTCString()\n            );\n            done();\n        });\n    }\n    it('serve static file', function(done) {\n        simpleTests('/public', 'index.html', 'text/html; charset=UTF-8', done);\n    });\n    it('serve default static file(index.html)', function(done) {\n        simpleTests('/public', '/', 'text/html; charset=UTF-8', done);\n    });\n    it('serve static file(file1.txt)', function(done) {\n        simpleTests('/public', 'file1.txt', 'text/plain; charset=UTF-8', done);\n    });\n    it('serve nested static files in request', function(done) {\n        simpleTests(\n            '/public',\n            'docs/doc.md',\n            'text/markdown; charset=UTF-8',\n            done\n        );\n    });\n    it('serve default nested static file(index.html)', function(done) {\n        simpleTests('/public', 'docs/', 'text/html; charset=UTF-8', done);\n    });\n    it('serve file paths with special chars', function(done) {\n        simpleTests(\n            '/public',\n            'special/$_$/bad (file).txt',\n            'text/plain; charset=UTF-8',\n            done\n        );\n    });\n});\n\ndescribe('staticFiles plugin - with options', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createStringClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    var STATIC_FILES_PATH = __dirname + '/testStaticFiles';\n\n    var OPTIONS = {\n        maxAge: 3600000, // this is in millisecs\n        etag: false,\n        setHeaders: function setCustomHeaders(response, requestedPath, stat) {\n            response.setHeader('restify-plugin-x', 'awesome');\n        }\n    };\n\n    function testsWithOptions(endpoint, filePath, options, contentType, done) {\n        var ENDPOINT = endpoint;\n        var fileSuffixPath = filePath;\n        var requestPath = path.join(ENDPOINT, fileSuffixPath);\n        var fileOnDisk = path.join(STATIC_FILES_PATH, fileSuffixPath);\n        if (fileSuffixPath.endsWith('/')) {\n            fileOnDisk = path.join(fileOnDisk, 'index.html');\n        }\n        var fileContent = fs.readFileSync(fileOnDisk, 'utf8');\n        var fileStat = fs.statSync(fileOnDisk);\n        SERVER.get(\n            ENDPOINT + '/*',\n            restify.plugins.serveStaticFiles(\n                path.resolve(STATIC_FILES_PATH),\n                options\n            )\n        );\n\n        CLIENT.get(encodeURI(requestPath), function(err, req, res, obj) {\n            assert.ifError(err);\n            assert.equal(fileContent, obj);\n            // Verify headers\n            assert.equal(res.headers['cache-control'], 'public, max-age=3600');\n            assert.equal(\n                res.headers['content-type'],\n                contentType //'text/html; charset=UTF-8'\n            );\n            assert.notExists(res.headers.etag);\n            assert.equal(\n                res.headers['last-modified'],\n                fileStat.mtime.toUTCString()\n            );\n            assert.equal(res.headers['restify-plugin-x'], 'awesome');\n            done();\n        });\n    }\n\n    it('serve static file', function(done) {\n        testsWithOptions(\n            '/public',\n            'index.html',\n            OPTIONS,\n            'text/html; charset=UTF-8',\n            done\n        );\n    });\n    it('serve default static file(index.html)', function(done) {\n        testsWithOptions(\n            '/public',\n            '/',\n            OPTIONS,\n            'text/html; charset=UTF-8',\n            done\n        );\n    });\n    it('serve static file(file1.txt)', function(done) {\n        testsWithOptions(\n            '/public',\n            'file1.txt',\n            OPTIONS,\n            'text/plain; charset=UTF-8',\n            done\n        );\n    });\n    it('serve nested static files in request', function(done) {\n        testsWithOptions(\n            '/public',\n            'docs/doc.md',\n            OPTIONS,\n            'text/markdown; charset=UTF-8',\n            done\n        );\n    });\n    it('serve default nested static file(index.html)', function(done) {\n        testsWithOptions(\n            '/public',\n            'docs/',\n            OPTIONS,\n            'text/html; charset=UTF-8',\n            done\n        );\n    });\n    it('serve file paths with special chars', function(done) {\n        testsWithOptions(\n            '/public',\n            'special/$_$/bad (file).txt',\n            OPTIONS,\n            'text/plain; charset=UTF-8',\n            done\n        );\n    });\n});\n\ndescribe('staticFiles plugin - negative cases', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createStringClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    var STATIC_FILES_PATH = __dirname + '/testStaticFiles';\n    var OPTIONS = {\n        maxAge: 3600000, // this is in millisecs\n        etag: false,\n        setHeaders: function setCustomHeaders(response, requestedPath, stat) {\n            response.setHeader('restify-plugin-x', 'awesome');\n        }\n    };\n    function negativeTests(\n        endpoint,\n        filePath,\n        expectedStatusCode,\n        expectedStatusMsg,\n        done\n    ) {\n        var ENDPOINT = endpoint;\n        var fileSuffixPath = filePath;\n        var requestPath = path.join(ENDPOINT, fileSuffixPath);\n        SERVER.get(\n            ENDPOINT + '/*',\n            restify.plugins.serveStaticFiles(\n                path.resolve(STATIC_FILES_PATH),\n                OPTIONS\n            )\n        );\n\n        CLIENT.get(encodeURI(requestPath), function(err, req, res, obj) {\n            assert.exists(err);\n            assert.equal(res.statusCode, expectedStatusCode);\n            assert.equal(res.statusMessage, expectedStatusMsg);\n            done();\n        });\n    }\n    it('fail to serve root directory', function(done) {\n        negativeTests('/public', '', 404, 'Not Found', done);\n    });\n    it('fail to serve nested directory', function(done) {\n        negativeTests('/public', 'docs', 403, 'Forbidden', done);\n    });\n    it('fail on file not found', function(done) {\n        negativeTests('/public', 'file2.txt', 404, 'Not Found', done);\n    });\n    it('fail on nested file not found', function(done) {\n        negativeTests(\n            '/public',\n            'docs/doc_not_there.md',\n            404,\n            'Not Found',\n            done\n        );\n    });\n    it('fail on missing file special characters', function(done) {\n        negativeTests(\n            '/public',\n            'special/$_$/bad (file)~notExists.txt',\n            404,\n            'Not Found',\n            done\n        );\n    });\n    it('fail on POST', function(done) {\n        var ENDPOINT = '/public';\n        var fileSuffixPath = 'docs/';\n        var requestPath = path.join(ENDPOINT, fileSuffixPath);\n        SERVER.post(\n            ENDPOINT + '/*',\n            restify.plugins.serveStaticFiles(\n                path.resolve(STATIC_FILES_PATH),\n                OPTIONS\n            )\n        );\n\n        CLIENT.get(encodeURI(requestPath), function(err, req, res, obj) {\n            assert.exists(err);\n            assert.equal(res.statusCode, 405);\n            assert.equal(res.statusMessage, 'Method Not Allowed');\n            done();\n        });\n    });\n});\n\ndescribe('staticFiles plugin - with sockets', function() {\n    // for some reason the server.close with socket\n    // takes longer to close\n    this.timeout(15000);\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        SERVER.close(done);\n    });\n\n    var STATIC_FILES_PATH = __dirname + '/testStaticFiles';\n\n    var OPTIONS = {\n        maxAge: 3600000, // this is in millisecs\n        etag: false,\n        setHeaders: function setCustomHeaders(response, requestedPath, stat) {\n            response.setHeader('restify-plugin-x', 'awesome');\n        }\n    };\n\n    function testsWithOptions(endpoint, filePath, done) {\n        var ENDPOINT = endpoint;\n        var fileSuffixPath = filePath;\n        var requestPath = path.join(ENDPOINT, fileSuffixPath);\n        var socket = new net.Socket();\n\n        SERVER.get(\n            ENDPOINT + '/*',\n            restify.plugins.serveStaticFiles(\n                path.resolve(STATIC_FILES_PATH),\n                OPTIONS\n            )\n        );\n\n        var RAW_REQUEST =\n            'GET ' +\n            requestPath +\n            ' HTTP/1.1\\r\\n' +\n            'Host: 127.0.0.1:' +\n            PORT +\n            '\\r\\n' +\n            'User-Agent: curl/7.48.0\\r\\n' +\n            'Accept: */*\\r\\n' +\n            '\\r\\n';\n\n        socket.connect({ host: '127.0.0.1', port: PORT }, function() {\n            socket.write(RAW_REQUEST, 'utf8', function(err2, data) {\n                assert.ifError(err2);\n            });\n        });\n\n        socket.on('data', function(data) {\n            var stringData = data.toString('utf8');\n            assert.isTrue(stringData.indexOf('restify-plugin-x') > 0);\n            done();\n        });\n    }\n\n    it('serve static file', function(done) {\n        testsWithOptions('/public', 'index.html', done);\n    });\n});\n"
  },
  {
    "path": "test/plugins/strictQueryParams.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar CLIENT;\nvar PORT;\nvar MESSAGE = 'Malformed request syntax';\n\ndescribe('strictQueryParams', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server'),\n            handleUncaughtExceptions: true\n        });\n\n        SERVER.listen(0, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('should respond 200 without plugin', function(done) {\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'bar');\n            assert.notEqual(req.params.name, 'josep&jorge');\n            assert.deepEqual(req.query, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=josep&jorge', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should respond 400 to non-strict key/val query param', function(done) {\n        SERVER.pre(\n            restify.plugins.pre.strictQueryParams({\n                message: MESSAGE\n            })\n        );\n\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=\"josep&jorge', function(\n            err,\n            _,\n            res\n        ) {\n            assert.equal(typeof err, 'object');\n            assert.equal(res.statusCode, 400);\n            assert.deepEqual(JSON.parse(res.body), {\n                code: 'BadRequest',\n                message: MESSAGE\n            });\n            done();\n        });\n    });\n\n    it('should respond 400 without message opt', function(done) {\n        SERVER.pre(restify.plugins.pre.strictQueryParams());\n\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=\"josep&jorge', function(\n            err,\n            _,\n            res\n        ) {\n            assert.equal(typeof err, 'object');\n            assert.equal(res.statusCode, 400);\n            assert.deepEqual(JSON.parse(res.body), {\n                code: 'BadRequest',\n                message: 'Url query params does not meet strict format'\n            });\n            done();\n        });\n    });\n\n    it('should respond 400 to query param with amp and plus', function(done) {\n        SERVER.pre(\n            restify.plugins.pre.strictQueryParams({\n                message: MESSAGE\n            })\n        );\n\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'bar');\n            assert.equal(req.params.name, 'josep & jorge');\n            assert.deepEqual(req.query, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=josep+&+jorge', function(\n            err,\n            _,\n            res\n        ) {\n            assert.equal(typeof err, 'object');\n            assert.equal(res.statusCode, 400);\n            assert.deepEqual(JSON.parse(res.body), {\n                code: 'BadRequest',\n                message: MESSAGE\n            });\n            done();\n        });\n    });\n\n    // eslint-disable-next-line\n    it('should respond to non-strict key/val query param value with 400', function(done) {\n        SERVER.pre(\n            restify.plugins.pre.strictQueryParams({\n                message: MESSAGE\n            })\n        );\n\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=josep&jorge', function(err, _, res) {\n            assert.equal(typeof err, 'object');\n            assert.equal(res.statusCode, 400);\n            assert.deepEqual(JSON.parse(res.body), {\n                code: 'BadRequest',\n                message: MESSAGE\n            });\n            done();\n        });\n    });\n\n    it('should respond to valid query param value with 200', function(done) {\n        SERVER.pre(\n            restify.plugins.pre.strictQueryParams({\n                message: MESSAGE\n            })\n        );\n\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.get('/query/:id', function(req, res, next) {\n            assert.equal(req.params.id, 'bar');\n            assert.equal(req.params.name, 'josep & jorge');\n            assert.deepEqual(req.query, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query/foo?id=bar&name=josep+%26+jorge', function(\n            err,\n            _,\n            res\n        ) {\n            assert.equal(typeof err, 'object');\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('should respond 200 with scaped amp and s', function(done) {\n        SERVER.pre(\n            restify.plugins.pre.strictQueryParams({\n                message: MESSAGE\n            })\n        );\n\n        SERVER.use(\n            restify.plugins.queryParser({\n                mapParams: true,\n                overrideParams: true\n            })\n        );\n\n        SERVER.get('/query', function(req, res, next) {\n            assert.equal(req.params.id, 'bar');\n            assert.equal(req.params.name, 'josep & jorge');\n            assert.deepEqual(req.query, req.params);\n            res.send();\n            next();\n        });\n\n        CLIENT.get('/query?id=bar&name=josep%20%26%20jorge', function(\n            err,\n            _,\n            res\n        ) {\n            assert.equal(typeof err, 'object');\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/testStaticFiles/docs/doc.md",
    "content": "#This is doc.md"
  },
  {
    "path": "test/plugins/testStaticFiles/docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n        <title>Document</title>\n    </head>\n    <body>\n        <h1>testStaticFiles/docs/index.html</h1>\n    </body>\n</html>\n"
  },
  {
    "path": "test/plugins/testStaticFiles/file1.txt",
    "content": "This is file1.txt"
  },
  {
    "path": "test/plugins/testStaticFiles/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n        <title>Document</title>\n    </head>\n    <body>\n        <h1>testStaticFiles/index.html</h1>\n    </body>\n</html>\n"
  },
  {
    "path": "test/plugins/testStaticFiles/special/$_$/bad (file).txt",
    "content": "This is a very badly named file."
  },
  {
    "path": "test/plugins/throttle.test.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n/* eslint-disable func-names */\n\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\nvar restifyClients = require('restify-clients');\n\n///--- Globals\n\nvar helper = require('../lib/helper');\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar CLIENT;\nvar SERVER;\n\nvar errorMessage = 'Error message should include rate 0.5 r/s. Received: ';\n\nfunction setupClientServer(ip, throttleOptions, done) {\n    var server = restify.createServer({\n        dtrace: helper.dtrace,\n        log: helper.getLog('server')\n    });\n\n    server.use(function ghettoAuthenticate(req, res, next) {\n        var username = req.url.match(/test\\/([a-z]+)/)[1];\n\n        if (username) {\n            req.username = username;\n        }\n\n        next();\n    });\n\n    server.use(restify.plugins.throttle(throttleOptions));\n\n    server.get('/test/:name', function(req, res, next) {\n        res.send();\n        next();\n    });\n\n    server.listen(PORT, ip, function() {\n        PORT = server.address().port;\n        var client = restifyClients.createJsonClient({\n            url: 'http://' + ip + ':' + PORT,\n            dtrace: helper.dtrace,\n            retry: false\n        });\n\n        done(client, server);\n    });\n}\n\n///--- Tests\n\ndescribe('throttle plugin', function() {\n    before(function setup(done) {\n        setupClientServer(\n            '127.0.0.1',\n            {\n                burst: 1,\n                rate: 0.5,\n                username: true,\n                overrides: {\n                    admin: {\n                        burst: 0,\n                        rate: 0\n                    },\n                    special: {\n                        burst: 3,\n                        rate: 1\n                    }\n                }\n            },\n            function setupGlobal(client, server) {\n                CLIENT = client;\n                SERVER = server;\n                done();\n            }\n        );\n    });\n\n    after(function teardown(done) {\n        CLIENT.close();\n        SERVER.close(done);\n    });\n\n    it('ok', function(done) {\n        CLIENT.get('/test/throttleMe', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('throttled', function(done) {\n        this.timeout(3000);\n\n        CLIENT.get('/test/throttleMe', function(err, _, res) {\n            assert.ok(err);\n            assert.equal(err.statusCode, 429);\n            assert.ok(\n                err.message.indexOf('0.5 r/s') !== -1,\n                errorMessage + (err && err.message)\n            );\n            assert.equal(res.statusCode, 429);\n\n            setTimeout(function() {\n                done();\n            }, 2100);\n        });\n    });\n\n    it('ok after tokens', function(done) {\n        CLIENT.get('/test/throttleMe', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('override limited', function(done) {\n        CLIENT.get('/test/special', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('override limited (not throttled)', function(done) {\n        CLIENT.get('/test/special', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('throttled after limited override', function(done) {\n        CLIENT.get('/test/throttleMe', function() {\n            CLIENT.get('/test/throttleMe', function(err, _, res) {\n                assert.ok(err);\n                assert.equal(res.statusCode, 429);\n                assert.ok(\n                    err.message.indexOf('0.5 r/s') !== -1,\n                    errorMessage + (err && err.message)\n                );\n                done();\n            });\n        });\n    });\n\n    it('override unlimited', function(done) {\n        CLIENT.get('/test/admin', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('override unlimited (not throttled)', function(done) {\n        CLIENT.get('/test/admin', function(err, _, res) {\n            assert.ifError(err);\n            assert.equal(res.statusCode, 200);\n            done();\n        });\n    });\n\n    it('throttled after unlimited override', function(done) {\n        CLIENT.get('/test/throttleMe', function() {\n            CLIENT.get('/test/throttleMe', function(err, _, res) {\n                assert.ok(err);\n                assert.equal(res.statusCode, 429);\n                assert.ok(\n                    err.message.indexOf('0.5 r/s') !== -1,\n                    errorMessage + (err && err.message)\n                );\n                done();\n            });\n        });\n    });\n\n    it('should not expose rate limit headers per default', function(done) {\n        CLIENT.get('/test/throttleMe', function(err, _, res) {\n            assert.isUndefined(res.headers['x-ratelimit-limit']);\n            assert.isUndefined(res.headers['x-ratelimit-rate']);\n            assert.isUndefined(res.headers['x-ratelimit-rate']);\n\n            done();\n        });\n    });\n\n    describe('expose headers', function() {\n        before(function(done) {\n            // close global server before creating a new to avoid port conflicts\n            CLIENT.close();\n            SERVER.close(done);\n        });\n\n        it('should expose headers on options set', function(done) {\n            // setup a new server with headers set to true since we cant\n            // change throttle options after init\n            setupClientServer(\n                '127.0.0.1',\n                {\n                    burst: 17,\n                    rate: 0.1,\n                    username: true,\n                    setHeaders: true\n                },\n                function setupWithHeaders(client, server) {\n                    client.get('/test/throttleMe', function(err, req, res) {\n                        assert.equal(res.headers['x-ratelimit-limit'], '17');\n                        assert.equal(res.headers['x-ratelimit-rate'], '0.1');\n                        assert.equal(\n                            res.headers['x-ratelimit-remaining'],\n                            '16'\n                        );\n\n                        // it should count down\n                        client.get('/test/throttleMe', function(\n                            nextErr,\n                            nextReq,\n                            nextRes\n                        ) {\n                            assert.equal(\n                                nextRes.headers['x-ratelimit-limit'],\n                                '17'\n                            );\n                            assert.equal(\n                                nextRes.headers['x-ratelimit-rate'],\n                                '0.1'\n                            );\n                            assert.equal(\n                                nextRes.headers['x-ratelimit-remaining'],\n                                '15'\n                            );\n\n                            client.close();\n                            server.close(done);\n                        });\n                    });\n                }\n            );\n        });\n    });\n});\n"
  },
  {
    "path": "test/plugins/userAgent.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\n// core requires\nvar child_process = require('child_process');\nvar http = require('http');\n\n// external requires\nvar assert = require('chai').assert;\nvar restify = require('../../lib/index.js');\n\n// local files\nvar helper = require('../lib/helper');\n\n// local globals\nvar SERVER;\nvar SERVER_PORT;\nvar SERVER_ADDRESS = '127.0.0.1';\nvar SERVER_ENDPOINT;\nvar TEST_ENDPOINT;\nvar TEST_RESPONSE_DATA = 'foobar';\nvar TEST_RESPONSE_DATA_LENGTH = TEST_RESPONSE_DATA.length;\nvar TEST_PATH = '/test/userAgent';\n\ndescribe('userAgent pre-route handler', function() {\n    beforeEach(function(done) {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n\n        // Enable the user agent pre-route handler, since this is the component\n        // under test.\n        SERVER.use(restify.plugins.pre.userAgentConnection());\n\n        SERVER.head('/test/:name', function(req, res, next) {\n            // Explicitly set Content-Length response header so that we can test\n            // for its removal (or lack thereof) by the userAgentConnection\n            // pre-route handler in tests below.\n            res.setHeader('Content-Length', TEST_RESPONSE_DATA_LENGTH);\n            res.send(200, TEST_RESPONSE_DATA);\n            next();\n        });\n\n        SERVER.listen(0, SERVER_ADDRESS, function() {\n            SERVER_PORT = SERVER.address().port;\n            SERVER_ENDPOINT = SERVER_ADDRESS + ':' + SERVER_PORT;\n            TEST_ENDPOINT = SERVER_ENDPOINT + TEST_PATH;\n            done();\n        });\n    });\n\n    afterEach(function(done) {\n        SERVER.close(done);\n    });\n\n    // By default, the userAgentConnection pre-route handler must:\n    //\n    // 1. set the 'connection' header to 'close'\n    //\n    // 2. remove the content-length header from the response\n    //\n    // when a HEAD request is handled and the client's user agent is curl.\n    it('sets proper headers for HEAD requests from curl', function(done) {\n        var CURL_CMD = ['curl', '-sS', '-i', TEST_ENDPOINT, '-X', 'HEAD'].join(\n            ' '\n        );\n\n        child_process.exec(CURL_CMD, function onExec(err, stdout, stderr) {\n            assert.ifError(err);\n\n            var lines = stdout.split(/\\n/);\n\n            var contentLengthHeaderNotPresent = lines.every(\n                function checkContentLengthNotPresent(line) {\n                    return /Content-Length:.*/.test(line) === false;\n                }\n            );\n            var connectionCloseHeaderPresent = lines.some(\n                function checkConnectionClosePresent(line) {\n                    return /Connection: close/.test(line);\n                }\n            );\n\n            assert.ok(contentLengthHeaderNotPresent);\n            assert.ok(connectionCloseHeaderPresent);\n\n            done();\n        });\n    });\n\n    // When handling a HEAD request, and if the client's user agent is not curl,\n    // the userAgentConnection should not remove the content-length header from\n    // the response, and it should not replace the value of the 'connection'\n    // header by 'close'.\n    // eslint-disable-next-line\n    it('sets proper headers for HEAD requests from non-curl clients', function(done) {\n        var req = http.request(\n            {\n                hostname: SERVER_ADDRESS,\n                port: SERVER_PORT,\n                path: TEST_PATH,\n                method: 'HEAD',\n                headers: {\n                    'user-agent': 'foobar',\n                    connection: 'keep-alive'\n                }\n            },\n            function onResponse(res) {\n                var responseHeaders = res.headers;\n\n                assert.ok(responseHeaders.hasOwnProperty('content-length'));\n                assert.equal(responseHeaders.connection, 'keep-alive');\n\n                // destroy the socket explicitly now since the request was\n                // explicitly requesting to not destroy the socket by setting\n                // its connection header to 'keep-alive'.\n                req.abort();\n\n                done();\n            }\n        );\n\n        req.on('error', function onReqError(err) {\n            assert.ifError(err);\n            done();\n        });\n\n        req.end();\n    });\n});\n"
  },
  {
    "path": "test/plugins/utilsHrTimeDurationInMs.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar assert = require('chai').assert;\nvar hrTimeDurationInMs = require('../../lib/plugins/utils/hrTimeDurationInMs');\n\ndescribe('utils #hrTimeDurationInMs', function() {\n    it('should return with duration', function() {\n        var startTime = [0, 0];\n        var endTime = [1, 1e6];\n\n        var duration = hrTimeDurationInMs(startTime, endTime);\n\n        assert.equal(duration, 1001);\n    });\n});\n"
  },
  {
    "path": "test/request.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar restifyClients = require('restify-clients');\nvar validator = require('validator');\n\nvar restify = require('../lib');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar after = helper.after;\nvar before = helper.before;\nvar test = helper.test;\n\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar CLIENT;\nvar SERVER;\n\nbefore(function(cb) {\n    try {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server')\n        });\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\nafter(function(cb) {\n    try {\n        CLIENT.close();\n        SERVER.close(function() {\n            CLIENT = null;\n            SERVER = null;\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\ntest('query should return empty string', function(t) {\n    SERVER.get('/emptyQs', function(req, res, next) {\n        t.equal(req.query(), '');\n        t.equal(req.getQuery(), '');\n        res.send();\n        next();\n    });\n\n    CLIENT.get('/emptyQs', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('query should return raw query string string', function(t) {\n    SERVER.get('/qs', function(req, res, next) {\n        t.equal(req.query(), 'a=1&b=2');\n        t.equal(req.getQuery(), 'a=1&b=2');\n        res.send();\n        next();\n    });\n\n    CLIENT.get('/qs?a=1&b=2', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('should generate request id on first req.id() call', function(t) {\n    SERVER.get('/ping', function(req, res, next) {\n        t.equal(typeof req.id(), 'string');\n        t.equal(validator.isUUID(req.id(), 4), true);\n        res.send();\n        return next();\n    });\n\n    CLIENT.get('/ping', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('should set request id', function(t) {\n    SERVER.pre(function setId(req, res, next) {\n        var newId = req.id('lagavulin');\n        t.equal(newId, 'lagavulin');\n        return next();\n    });\n\n    SERVER.get('/ping', function(req, res, next) {\n        t.equal(typeof req.id(), 'string');\n        t.equal(req.id(), 'lagavulin');\n        res.send();\n        return next();\n    });\n\n    CLIENT.get('/ping', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('should throw when setting request id after autogeneration', function(t) {\n    SERVER.get('/ping', function(req, res, next) {\n        t.equal(typeof req.id(), 'string');\n        t.equal(validator.isUUID(req.id(), 4), true);\n        t.throws(\n            function() {\n                req.id('blowup');\n            },\n            Error,\n            'request id is immutable, cannot be set again!'\n        );\n        res.send();\n        return next();\n    });\n\n    CLIENT.get('/ping', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('should throw when setting request id twice', function(t) {\n    SERVER.get('/ping', function(req, res, next) {\n        req.id('lagavulin');\n        t.throws(\n            function() {\n                req.id('blowup');\n            },\n            Error,\n            'request id is immutable, cannot be set again!'\n        );\n        res.send();\n        return next();\n    });\n\n    CLIENT.get('/ping', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('should provide route object', function(t) {\n    SERVER.get('/ping/:name', function(req, res, next) {\n        /*\n         req.getRoute() should return something like this :\n             {\n                path: '/ping/:name',\n                method: 'GET',\n                versions: [],\n                name: 'getpingname'\n             }\n         */\n        var routeInfo = req.getRoute();\n        t.equal(routeInfo.path, '/ping/:name');\n        t.equal(routeInfo.method, 'GET');\n        res.send({ name: req.params.name });\n        return next();\n    });\n\n    CLIENT.get('/ping/lagavulin', function(err, _, res, parsedBody) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.deepEqual(parsedBody, { name: 'lagavulin' });\n        t.end();\n    });\n});\n\ntest('should provide time when request started', function(t) {\n    SERVER.get('/ping/:name', function(req, res, next) {\n        t.equal(typeof req.time(), 'number');\n        t.ok(req.time() > Date.now() - 1000);\n        t.ok(req.time() <= Date.now());\n        res.send('ok');\n        return next();\n    });\n\n    CLIENT.get('/ping/lagavulin', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('should provide date when request started', function(t) {\n    SERVER.get('/ping/:name', function(req, res, next) {\n        t.ok(req.date() instanceof Date);\n        t.ok(req.date().getTime() > Date.now() - 1000);\n        t.ok(req.date().getTime() <= Date.now());\n        res.send('ok');\n        return next();\n    });\n\n    CLIENT.get('/ping/lagavulin', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\n// restifyDone is emitted at the same time when server's after event is emitted,\n// you can find more comprehensive testing for `after` lives in server tests.\ntest('should emit restifyDone event when request is fully served', function(t) {\n    var restifyDoneCalled = false;\n\n    SERVER.get('/', function(req, res, next) {\n        req.on('restifyDone', function(route, err) {\n            t.ifError(err);\n            t.ok(route);\n            setImmediate(function() {\n                restifyDoneCalled = true;\n            });\n        });\n\n        res.send('hello');\n        return next();\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.ok(restifyDoneCalled);\n        t.end();\n    });\n});\n\n// eslint-disable-next-line max-len\ntest('should emit restifyDone event when request is fully served with error', function(t) {\n    var clientDone = false;\n\n    SERVER.get('/', function(req, res, next) {\n        var myErr = new Error('My Error');\n\n        req.on('restifyDone', function(route, err) {\n            t.ok(route);\n            t.deepEqual(err, myErr);\n            setImmediate(function() {\n                t.ok(clientDone);\n                t.end();\n            });\n        });\n\n        return next(myErr);\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        clientDone = true;\n    });\n});\n"
  },
  {
    "path": "test/response.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar url = require('url');\nvar restifyClients = require('restify-clients');\nvar errs = require('restify-errors');\n\nvar restify = require('../lib');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar after = helper.after;\nvar before = helper.before;\nvar test = helper.test;\n\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar CLIENT;\nvar STRING_CLIENT;\nvar SERVER;\n\nvar LOCALHOST;\nvar SLOCALHOST;\n\nbefore(function(cb) {\n    try {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            handleUncaughtExceptions: true,\n            log: helper.getLog('server'),\n            version: ['2.0.0', '0.5.4', '1.4.3']\n        });\n        SERVER.use(restify.plugins.queryParser());\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n            STRING_CLIENT = restifyClients.createStringClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n            LOCALHOST = 'http://' + '127.0.0.1:' + PORT;\n            SLOCALHOST = 'https://' + '127.0.0.1:' + PORT;\n\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\nafter(function(cb) {\n    try {\n        CLIENT.close();\n        STRING_CLIENT.close();\n        SERVER.close(function() {\n            CLIENT = null;\n            SERVER = null;\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\n// helper for joining array into strings\nfunction join() {\n    var args = [].slice.call(arguments, 0);\n    return args.join('');\n}\n\ntest('redirect to new string url as-is', function(t) {\n    SERVER.get('/1', function(req, res, next) {\n        res.redirect('www.foo.com', next);\n    });\n\n    CLIENT.get(join(LOCALHOST, '/1'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, 'www.foo.com');\n        t.end();\n    });\n});\n\ntest('redirect to new relative string url as-is', function(t) {\n    SERVER.get('/20', function(req, res, next) {\n        res.redirect('/1', next);\n    });\n\n    CLIENT.get(join(LOCALHOST, '/20?a=1'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, '/1');\n        t.end();\n    });\n});\n\ntest('redirect to current url (reload)', function(t) {\n    SERVER.get('/2', function(req, res, next) {\n        res.redirect(\n            {\n                reload: true\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/2'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, join(LOCALHOST, '/2'));\n        t.end();\n    });\n});\n\ntest('redirect to current url from http -> https', function(t) {\n    SERVER.get('/3', function(req, res, next) {\n        res.redirect(\n            {\n                secure: true\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/3'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, join(SLOCALHOST, '/3'));\n        t.end();\n    });\n});\n\ntest('redirect to current url from https -> http', function(t) {\n    SERVER.get('/3', function(req, res, next) {\n        res.redirect(\n            {\n                reload: true,\n                secure: false\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/3'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, join(LOCALHOST, '/3'));\n        t.end();\n    });\n});\n\ntest('redirect by changing path', function(t) {\n    SERVER.get('/4', function(req, res, next) {\n        res.redirect(\n            {\n                pathname: '1'\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/4'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, join(LOCALHOST, '/1'));\n        t.end();\n    });\n});\n\ntest(\n    'GH-1494: redirect should succeed even if req.url does not specify host' +\n        ' or protocol',\n    function(t) {\n        SERVER.get('/5', function(req, res, next) {\n            res.redirect(\n                {\n                    pathname: '/'\n                },\n                next\n            );\n        });\n\n        // use a relative URL here instead of request with full\n        // protocol and host.\n        // this causes node to receive different values for req.url,\n        // which affects\n        // how reconstruction of the redirect URL is done. for example including\n        // full host will result in a req.url value of:\n        //         http://127.0.0.1:57824/5\n        // using relative URL results in a req.url value of:\n        //         /5\n        // this causes a bug as documented in GH-1494\n        CLIENT.get('/5', function(err, _, res) {\n            t.ifError(err);\n            t.equal(res.statusCode, 302);\n            t.equal(res.headers.location, '/');\n            t.end();\n        });\n    }\n);\n\ntest('redirect should add query params', function(t) {\n    SERVER.get('/5', function(req, res, next) {\n        res.redirect(\n            {\n                query: {\n                    a: 1\n                }\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/5'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, join(LOCALHOST, '/5?a=1'));\n        t.end();\n    });\n});\n\ntest('redirect should extend existing query params', function(t) {\n    SERVER.get('/6', function(req, res, next) {\n        res.redirect(\n            {\n                query: {\n                    b: 2\n                }\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/6?a=1'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        var parsedUrl = url.parse(res.headers.location, true);\n        t.deepEqual(parsedUrl.query, {\n            a: 1,\n            b: 2\n        });\n        t.equal(parsedUrl.query.b, 2);\n        t.equal(parsedUrl.pathname, '/6');\n\n        // t.equal(res.headers.location, join(LOCALHOST, '/6?a=1&b=2'));\n        t.end();\n    });\n});\n\ntest('redirect should stomp over existing query params', function(t) {\n    SERVER.get('/7', function(req, res, next) {\n        res.redirect(\n            {\n                overrideQuery: true,\n                query: {\n                    b: 2\n                }\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/7?a=1'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, join(LOCALHOST, '/7?b=2'));\n        t.end();\n    });\n});\n\ntest('redirect with 301 status code', function(t) {\n    SERVER.get('/8', function(req, res, next) {\n        res.redirect(\n            {\n                permanent: true\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/8'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 301);\n        t.equal(res.headers.location, join(LOCALHOST, '/8'));\n        t.end();\n    });\n});\n\ntest('redirect with 301 status code ising string url', function(t) {\n    SERVER.get('/30', function(req, res, next) {\n        res.redirect(301, '/foo', next);\n    });\n\n    CLIENT.get(join(LOCALHOST, '/30'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 301);\n        t.equal(res.headers.location, '/foo');\n        t.end();\n    });\n});\n\ntest('redirect using options.url', function(t) {\n    SERVER.get('/8', function(req, res, next) {\n        res.redirect(\n            {\n                hostname: 'www.foo.com',\n                pathname: '/8',\n                query: {\n                    a: 1\n                }\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/8'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, 'http://www.foo.com/8?a=1');\n        t.end();\n    });\n});\n\ntest('redirect using opts.port', function(t) {\n    SERVER.get('/9', function(req, res, next) {\n        res.redirect(\n            {\n                port: 3000\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/9'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        var parsedUrl = url.parse(res.headers.location, true);\n        t.equal(parsedUrl.port, 3000);\n        t.end();\n    });\n});\n\ntest('redirect using external url and custom port', function(t) {\n    SERVER.get('/9', function(req, res, next) {\n        res.redirect(\n            {\n                hostname: 'www.foo.com',\n                pathname: '/99',\n                port: 3000\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/9'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        var parsedUrl = url.parse(res.headers.location, true);\n        t.equal(parsedUrl.port, 3000);\n        t.equal(parsedUrl.hostname, 'www.foo.com');\n        t.equal(parsedUrl.pathname, '/99');\n        t.end();\n    });\n});\n\ntest('redirect using default hostname with custom port', function(t) {\n    SERVER.get('/9', function(req, res, next) {\n        res.redirect(\n            {\n                pathname: '/99',\n                port: 3000\n            },\n            next\n        );\n    });\n\n    CLIENT.get(join(LOCALHOST, '/9'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        var parsedUrl = url.parse(res.headers.location, true);\n        t.equal(parsedUrl.port, 3000);\n        t.equal(parsedUrl.pathname, '/99');\n        t.equal(res.headers.location, 'http://127.0.0.1:3000/99');\n        t.end();\n    });\n});\n\n// eslint-disable-next-line\ntest('redirect should cause InternalError when invoked without next', function(t) {\n    SERVER.get('/9', function(req, res, next) {\n        res.redirect();\n    });\n\n    CLIENT.get(join(LOCALHOST, '/9'), function(err, _, res, body) {\n        t.equal(res.statusCode, 500);\n\n        // json parse the response\n        t.equal(body.code, 'Internal');\n        t.end();\n    });\n});\n\n// eslint-disable-next-line\ntest('redirect should call next with false to stop handler stack execution', function(t) {\n    var wasRun = false;\n\n    function A(req, res, next) {\n        req.a = 1;\n        next();\n    }\n    function B(req, res, next) {\n        req.b = 2;\n        wasRun = true;\n        next();\n    }\n    function redirect(req, res, next) {\n        res.redirect('/10', next);\n    }\n\n    SERVER.get('/10', [A, redirect, B]);\n\n    CLIENT.get(join(LOCALHOST, '/10'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, '/10');\n\n        // handler B should not be executed\n        t.equal(wasRun, false);\n        t.end();\n    });\n});\n\ntest('redirect should emit a redirect event', function(t) {\n    var wasEmitted = false;\n    var redirectLocation;\n\n    function preRedirectHandler(req, res, next) {\n        res.on('redirect', function(payload) {\n            wasEmitted = true;\n            redirectLocation = payload;\n        });\n        next();\n    }\n    function redirect(req, res, next) {\n        res.redirect('/10', next);\n    }\n\n    SERVER.get('/10', [preRedirectHandler, redirect]);\n\n    CLIENT.get(join(LOCALHOST, '/10'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n        t.equal(res.headers.location, '/10');\n\n        // event 'redirect' should have been emitted\n        t.equal(wasEmitted, true);\n        t.equal(redirectLocation, '/10');\n        t.end();\n    });\n});\n\ntest('writeHead should emit a header event', function(t) {\n    var wasEmitted = false;\n    var payloadPlaceholder;\n\n    // writeHead is called on each request\n    function handler(req, res, next) {\n        res.on('header', function(payload) {\n            wasEmitted = true;\n            payloadPlaceholder = payload;\n        });\n        res.send(302);\n        next();\n    }\n\n    SERVER.get('/10', [handler]);\n\n    CLIENT.get(join(LOCALHOST, '/10'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 302);\n\n        // event 'header' should have been emitted\n        t.equal(wasEmitted, true);\n        t.equal(payloadPlaceholder, undefined);\n        t.end();\n    });\n});\n\ntest('should fail to set header due to missing formatter', function(t) {\n    // when a formatter is not set up for a specific content-type, restify will\n    // default to octet-stream.\n\n    SERVER.get('/11', function handle(req, res, next) {\n        res.header('content-type', 'application/hal+json');\n        res.send(200, JSON.stringify({ hello: 'world' }));\n        return next();\n    });\n\n    CLIENT.get(join(LOCALHOST, '/11'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(res.headers['content-type'], 'application/octet-stream');\n        t.end();\n    });\n});\n\ntest('should not fail to send null as body', function(t) {\n    SERVER.get('/12', function handle(req, res, next) {\n        res.send(200, null);\n        return next();\n    });\n\n    CLIENT.get(join(LOCALHOST, '/12'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('should not fail to send null as body without status code', function(t) {\n    SERVER.get('/13', function handle(req, res, next) {\n        res.send(null);\n        return next();\n    });\n\n    CLIENT.get(join(LOCALHOST, '/13'), function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('should prefer explicit status code over error status code', function(t) {\n    SERVER.get('/14', function handle(req, res, next) {\n        res.send(200, new errs.InternalServerError('boom'));\n        return next();\n    });\n\n    CLIENT.get(join(LOCALHOST, '/14'), function(err, _, res, body) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        // ensure error body was still sent\n        t.equal(body.code, 'InternalServer');\n        t.equal(body.message, 'boom');\n        t.end();\n    });\n});\n\ntest('GH-951: should send without formatting', function(t) {\n    SERVER.get('/15', function handle(req, res, next) {\n        res.header('content-type', 'application/json');\n        res.sendRaw(\n            200,\n            JSON.stringify({\n                hello: 'world'\n            })\n        );\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/15'), function(err, _, res, body) {\n        t.ifError(err);\n        t.equal(\n            body,\n            JSON.stringify({\n                hello: 'world'\n            })\n        );\n        t.end();\n    });\n});\n\ntest('GH-951: sendRaw accepts only strings or buffers', function(t) {\n    SERVER.on('uncaughtException', function(req, res, route, err) {\n        t.ok(err);\n        // Node v8 uses static error codes\n        // and `name` includes the error name and error code as well which\n        // caused this test to break. Just changing the logic to check for\n        // string instead\n        t.equal(err.name.indexOf('AssertionError') >= 0, true);\n        t.equal(err.message, 'res.sendRaw() accepts only strings or buffers');\n        t.end();\n    });\n\n    SERVER.get('/16', function handle(req, res, next) {\n        res.header('content-type', 'application/json');\n        res.sendRaw(200, {\n            hello: 'world'\n        });\n        return next();\n    });\n\n    // throw away response, we don't need it.\n    STRING_CLIENT.get(join(LOCALHOST, '/16'));\n});\n\ntest('GH-1429: setting code with res.status not respected', function(t) {\n    SERVER.get('/404', function(req, res, next) {\n        res.status(404);\n        res.send(null);\n    });\n\n    CLIENT.get(join(LOCALHOST, '/404'), function(err, _, res) {\n        t.equal(res.statusCode, 404);\n        t.end();\n    });\n});\n\ntest('should support multiple set-cookie headers', function(t) {\n    SERVER.get('/set-cookie', function(req, res, next) {\n        res.header('Set-Cookie', 'a=1');\n        res.header('Set-Cookie', 'b=2');\n        res.send(null);\n    });\n\n    CLIENT.get(join(LOCALHOST, '/set-cookie'), function(err, _, res) {\n        t.equal(res.headers['set-cookie'].length, 2);\n        t.end();\n    });\n});\n\ntest('GH-1607: should send bools with explicit status code', function(t) {\n    SERVER.get('/bool/:value', function(req, res, next) {\n        res.send(200, req.params.value === 'true' ? true : false);\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/bool/false'), function(\n        err,\n        req,\n        res,\n        data\n    ) {\n        t.equal(data, 'false');\n\n        STRING_CLIENT.get(join(LOCALHOST, '/bool/true'), function(\n            err2,\n            req2,\n            res2,\n            data2\n        ) {\n            t.equal(data2, 'true');\n            t.end();\n        });\n    });\n});\n\ntest('GH-1607: should send numbers with explicit status code', function(t) {\n    SERVER.get('/zero', function(req, res, next) {\n        res.send(200, 0);\n        return next();\n    });\n\n    SERVER.get('/one', function(req, res, next) {\n        res.send(200, 1);\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/zero'), function(err, req, res, data) {\n        t.equal(data, '0');\n        STRING_CLIENT.get(join(LOCALHOST, '/one'), function(\n            err2,\n            req2,\n            res2,\n            data2\n        ) {\n            t.equal(data2, '1');\n            t.end();\n        });\n    });\n});\n\ntest('GH-1791: should send 0 as 0 with application/json', function(t) {\n    SERVER.get('/zero', function(req, res, next) {\n        res.contentType = 'application/json';\n        res.send(200, 0);\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/zero'), function(err, req, res, data) {\n        t.equal(data, '0');\n        t.end();\n    });\n});\n\ntest('GH-1791: should send false as false with application/json', function(t) {\n    SERVER.get('/false', function(req, res, next) {\n        res.contentType = 'application/json';\n        res.send(200, false);\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/false'), function(err, req, res, data) {\n        t.equal(data, 'false');\n        t.end();\n    });\n});\n\n// eslint-disable-next-line\ntest('GH-1791: should send empty string as \"\" with application/json', function(t) {\n    SERVER.get('/empty', function(req, res, next) {\n        res.contentType = 'application/json';\n        res.send(200, '');\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/empty'), function(err, req, res, data) {\n        t.equal(data, '\"\"');\n        t.end();\n    });\n});\n\ntest('GH-1791: should send null as null with application/json', function(t) {\n    SERVER.get('/null', function(req, res, next) {\n        res.contentType = 'application/json';\n        res.send(200, null);\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/null'), function(err, req, res, data) {\n        t.equal(data, 'null');\n        t.end();\n    });\n});\n\n// eslint-disable-next-line\ntest('GH-1791: should send undefined as empty with application/json', function(t) {\n    SERVER.get('/undef', function(req, res, next) {\n        res.contentType = 'application/json';\n        res.send(200, undefined);\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/undef'), function(err, req, res, data) {\n        t.equal(data, '');\n        t.end();\n    });\n});\n\ntest('GH-1791: should send NaN as null with application/json', function(t) {\n    SERVER.get('/nan', function(req, res, next) {\n        res.contentType = 'application/json';\n        res.send(200, NaN);\n        return next();\n    });\n\n    STRING_CLIENT.get(join(LOCALHOST, '/nan'), function(err, req, res, data) {\n        t.equal(data, 'null');\n        t.end();\n    });\n});\n"
  },
  {
    "path": "test/router.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar restify = require('../lib');\nvar Router = require('../lib/router');\nvar clients = require('restify-clients');\nvar _ = require('lodash');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar test = helper.test;\nvar mockReq = {\n    params: {},\n    connectionState: function() {\n        return '';\n    },\n    startHandlerTimer: function() {},\n    endHandlerTimer: function() {}\n};\nvar mockRes = {\n    setHeader: function() {},\n    send: function() {}\n};\n\n///--- Tests\n\ntest('mounts a route', function(t) {\n    function handler(req, res, next) {\n        res.send('Hello world');\n    }\n\n    var router = new Router({\n        log: {}\n    });\n    router.mount({ method: 'GET', path: '/' }, [handler]);\n    router.mount({ method: 'POST', path: '/' }, [handler]);\n    router.mount({ method: 'GET', path: '/ab' }, [handler]);\n\n    t.deepEqual(Object.keys(router.getRoutes()), ['get', 'post', 'getab']);\n\n    // Route names are unique\n    router.mount({ name: 'get', method: 'GET', path: '/get' }, [handler]);\n    router.mount({ method: 'GET', path: '/a/b' }, [handler]);\n    t.deepEqual(\n        _.uniq(Object.keys(router.getRoutes())),\n        Object.keys(router.getRoutes())\n    );\n\n    t.done();\n});\n\ntest('unmounts a route', function(t) {\n    function handler(req, res, next) {\n        res.send('Hello world');\n    }\n\n    var router = new Router({\n        log: {}\n    });\n\n    // Mount\n    router.mount({ method: 'GET', path: '/a' }, [handler]);\n    router.mount({ method: 'POST', path: '/b' }, [handler]);\n    t.deepEqual(Object.keys(router.getRoutes()), ['geta', 'postb']);\n\n    // Unmount\n    var route = router.unmount('geta');\n    t.ok(route);\n    t.equal(route.name, 'geta');\n\n    // Removes from mounted routes\n    t.deepEqual(Object.keys(router.getRoutes()), ['postb']);\n\n    // 404\n    var handlerFound = router.lookup(\n        Object.assign(\n            {\n                getUrl: function() {\n                    return { pathname: '/a' };\n                },\n                method: 'GET'\n            },\n            mockReq\n        ),\n        mockRes\n    );\n\n    t.notOk(handlerFound);\n    t.end();\n});\n\ntest('unmounts a route that does not exist', function(t) {\n    function handler(req, res, next) {\n        res.send('Hello world');\n    }\n\n    var router = new Router({\n        log: {}\n    });\n\n    // Mount\n    router.mount({ method: 'GET', path: '/a' }, [handler]);\n    t.notOk(router.unmount('non-existing'));\n    t.end();\n});\n\ntest('clean up xss for 404', function(t) {\n    var server = restify.createServer();\n\n    server.listen(3000, function(listenErr) {\n        t.ifError(listenErr);\n\n        var client = clients.createStringClient({\n            url: 'http://127.0.0.1:3000/'\n        });\n\n        client.get(\n            {\n                path:\n                    '/no5_such3_file7.pl?%22%3E%3Cscript%3Ealert(73541);%3C/' +\n                    'script%3E',\n                headers: {\n                    connection: 'close'\n                }\n            },\n            function(clientErr, req, res, data) {\n                t.ok(clientErr);\n                t.ok(\n                    data.indexOf('%22%3E%3Cscript%3Ealert(73541)') === -1,\n                    'should not reflect raw url'\n                );\n\n                server.close(function() {\n                    t.end();\n                });\n            }\n        );\n    });\n});\n\ntest('lookupByName runs a route by name and calls next', function(t) {\n    var router = new Router({\n        log: {}\n    });\n\n    function handler(req, res, next) {\n        res.send('hello world');\n        next();\n    }\n\n    router.mount({ method: 'GET', path: '/', name: 'my-route' }, [handler]);\n\n    var handlerFound = router.lookupByName('my-route', mockReq, mockRes);\n    t.ok(handlerFound);\n\n    handlerFound(mockReq, mockRes, function next(err) {\n        t.ifError(err);\n        t.end();\n    });\n});\n\ntest('lookupByName calls next with err', function(t) {\n    var router = new Router({\n        log: {}\n    });\n    var myErr = new Error('My Error');\n    router.mount({ method: 'GET', path: '/', name: 'my-route' }, [\n        function(req, res, next) {\n            next(myErr);\n        }\n    ]);\n\n    var handlerFound = router.lookupByName('my-route', mockReq, mockRes);\n    t.ok(handlerFound);\n\n    handlerFound(mockReq, mockRes, function next(err) {\n        t.deepEqual(err, myErr);\n        t.end();\n    });\n});\n\ntest('lookup runs a route chain by path and calls next', function(t) {\n    var router = new Router({\n        log: {}\n    });\n    router.mount({ method: 'GET', path: '/', name: 'my-route' }, [\n        function(req, res, next) {\n            res.send('Hello world');\n            next(); // no _afterRoute without next()\n        }\n    ]);\n\n    var handlerFound = router.lookup(\n        Object.assign(\n            {\n                getUrl: function() {\n                    return { pathname: '/' };\n                },\n                method: 'GET'\n            },\n            mockReq\n        ),\n        mockRes\n    );\n    t.ok(handlerFound);\n\n    handlerFound(mockReq, mockRes, function next(err) {\n        t.ifError(err);\n        t.end();\n    });\n});\n\ntest('lookup calls next with err', function(t) {\n    var router = new Router({\n        log: {}\n    });\n    var myErr = new Error('My Error');\n    router.mount({ method: 'GET', path: '/', name: 'my-route' }, [\n        function(req, res, next) {\n            next(myErr);\n        }\n    ]);\n\n    var handlerFound = router.lookup(\n        Object.assign(\n            {\n                getUrl: function() {\n                    return { pathname: '/' };\n                },\n                method: 'GET'\n            },\n            mockReq\n        ),\n        mockRes\n    );\n    t.ok(handlerFound);\n\n    handlerFound(mockReq, mockRes, function next(err) {\n        t.deepEqual(err, myErr);\n        t.end();\n    });\n});\n\ntest('route handles 404', function(t) {\n    var router = new Router({\n        log: {}\n    });\n    router.defaultRoute(\n        Object.assign(\n            {\n                getUrl: function() {\n                    return { pathname: '/' };\n                },\n                method: 'GET'\n            },\n            mockReq\n        ),\n        mockRes,\n        function next(err) {\n            t.equal(err.statusCode, 404);\n            t.end();\n        }\n    );\n});\n\ntest('route handles method not allowed (405)', function(t) {\n    var router = new Router({\n        log: {}\n    });\n    router.mount({ method: 'GET', path: '/', name: 'my-route' }, [\n        function(req, res, next) {\n            res.send('Hello world');\n        }\n    ]);\n\n    router.defaultRoute(\n        Object.assign(\n            {\n                getUrl: function() {\n                    return { pathname: '/' };\n                },\n                method: 'POST'\n            },\n            mockReq\n        ),\n        mockRes,\n        function next(err) {\n            t.equal(err.statusCode, 405);\n            t.end();\n        }\n    );\n});\n\ntest('prints debug info', function(t) {\n    function handler1(req, res, next) {\n        res.send('Hello world');\n    }\n    function handler2(req, res, next) {\n        res.send('Hello world');\n    }\n\n    var router = new Router({\n        log: {}\n    });\n    router.mount({ method: 'GET', path: '/' }, [handler1]);\n    router.mount({ method: 'POST', path: '/' }, [handler1, handler2]);\n\n    t.deepEqual(router.getDebugInfo(), {\n        get: {\n            name: 'get',\n            method: 'get',\n            path: '/',\n            handlers: [handler1]\n        },\n        post: {\n            name: 'post',\n            method: 'post',\n            path: '/',\n            handlers: [handler1, handler2]\n        }\n    });\n    t.end();\n});\n\ntest('toString()', function(t) {\n    function handler(req, res, next) {\n        res.send('Hello world');\n    }\n\n    var router = new Router({\n        log: {}\n    });\n    router.mount({ method: 'GET', path: '/' }, [handler]);\n    router.mount({ method: 'GET', path: '/a' }, [handler]);\n    router.mount({ method: 'GET', path: '/a/b' }, [handler]);\n    router.mount({ method: 'POST', path: '/' }, [handler]);\n\n    t.deepEqual(\n        router.toString(),\n        // prettier-ignore\n        '└── / (GET, POST)\\n' +\n        '    └── a (GET)\\n' +\n        '        └── /b (GET)\\n'\n    );\n    t.end();\n});\n\ntest('toString() with ignoreTrailingSlash', function(t) {\n    function handler(req, res, next) {\n        res.send('Hello world');\n    }\n\n    var router = new Router({\n        log: {},\n        ignoreTrailingSlash: true\n    });\n    router.mount({ method: 'GET', path: '/' }, [handler]);\n    router.mount({ method: 'GET', path: '/a' }, [handler]);\n    router.mount({ method: 'GET', path: '/a/b' }, [handler]);\n    router.mount({ method: 'POST', path: '/' }, [handler]);\n\n    t.deepEqual(\n        router.toString(),\n        // prettier-ignore\n        '└── / (GET, POST)\\n' +\n        '    └── a (GET)\\n' +\n        '        └── /b (GET)\\n'\n    );\n    t.end();\n});\n\n// Tests router.render()\nvar mockResponse = function respond(req, res, next) {\n    res.send(200);\n};\n\ntest('render route', function(t) {\n    var server = restify.createServer();\n    server.get({ name: 'countries', path: '/countries' }, mockResponse);\n    server.get({ name: 'country', path: '/countries/:name' }, mockResponse);\n    server.get(\n        { name: 'cities', path: '/countries/:name/states/:state/cities' },\n        mockResponse\n    );\n\n    var countries = server.router.render('countries', {});\n    t.equal(countries, '/countries');\n\n    var country = server.router.render('country', { name: 'Australia' });\n    t.equal(country, '/countries/Australia');\n\n    var cities = server.router.render('cities', {\n        name: 'Australia',\n        state: 'New South Wales'\n    });\n    t.equal(cities, '/countries/Australia/states/New%20South%20Wales/cities');\n\n    t.end();\n});\n\ntest('render route (missing params)', function(t) {\n    var server = restify.createServer();\n    server.get(\n        { name: 'cities', path: '/countries/:name/states/:state/cities' },\n        mockResponse\n    );\n\n    try {\n        server.router.render('cities', { name: 'Australia' });\n    } catch (ex) {\n        // server is expected to throw an error\n        // hence catching it here\n        t.equal(ex, 'Error: Route <cities> is missing parameter <state>');\n    }\n\n    t.end();\n});\n\ntest('GH #704: render route (special charaters)', function(t) {\n    var server = restify.createServer();\n    server.get({ name: 'my-route', path: '/countries/:name' }, mockResponse);\n\n    var link = server.router.render('my-route', { name: 'AustraliaIsC@@!' });\n    // special charaacters are URI encoded\n    t.equal(link, '/countries/AustraliaIsC%40%40!');\n\n    t.end();\n});\n\ntest('GH #704: render route (with sub-regex param)', function(t) {\n    var server = restify.createServer();\n    server.get(\n        {\n            name: 'my-route',\n            path: '/countries/:code([A-Z]{2,3})'\n        },\n        mockResponse\n    );\n\n    var link = server.router.render('my-route', { code: 'FR' });\n    t.equal(link, '/countries/FR');\n\n    link = server.router.render('my-route', { code: '111' });\n    t.equal(link, '/countries/111');\n\n    t.end();\n});\n\ntest('GH-796: render route (with multiple sub-regex param)', function(t) {\n    var server = restify.createServer();\n    server.get(\n        {\n            name: 'my-route',\n            path: '/countries/:code([A-Z]{2,3})/:area([0-9]+)'\n        },\n        mockResponse\n    );\n\n    var link = server.router.render('my-route', { code: '111', area: 42 });\n    t.equal(link, '/countries/111/42');\n    t.end();\n});\n\ntest('render route (with encode)', function(t) {\n    var server = restify.createServer();\n    server.get({ name: 'my-route', path: '/countries/:name' }, mockResponse);\n\n    var link = server.router.render('my-route', { name: 'Trinidad & Tobago' });\n    t.equal(link, '/countries/Trinidad%20%26%20Tobago');\n\n    t.end();\n});\n\ntest('render route (query string)', function(t) {\n    var server = restify.createServer();\n    server.get({ name: 'country', path: '/countries/:name' }, mockResponse);\n\n    var country1 = server.router.render(\n        'country',\n        {\n            name: 'Australia'\n        },\n        {\n            state: 'New South Wales',\n            'cities/towns': 5\n        }\n    );\n\n    t.equal(\n        country1,\n        '/countries/Australia?state=New%20South%20Wales&cities%2Ftowns=5'\n    );\n\n    var country2 = server.router.render(\n        'country',\n        {\n            name: 'Australia'\n        },\n        {\n            state: 'NSW & VIC',\n            'cities&towns': 5\n        }\n    );\n\n    t.equal(\n        country2,\n        '/countries/Australia?state=NSW%20%26%20VIC&cities%26towns=5'\n    );\n\n    t.end();\n});\n"
  },
  {
    "path": "test/routerRegistryRadix.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar RouterRegistryRadix = require('../lib/routerRegistryRadix');\nvar Chain = require('../lib/chain');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar test = helper.test;\n\nfunction getTestRoute(opts) {\n    var chain = new Chain();\n    var name = opts.method + '-' + opts.path;\n    name = name.replace(/\\W/g, '').toLowerCase();\n\n    return {\n        name: name,\n        method: opts.method,\n        path: opts.path,\n        spec: opts,\n        chain: chain\n    };\n}\n\n///--- Tests\n\ntest('adds a route', function(t) {\n    var registry = new RouterRegistryRadix();\n    registry.add(getTestRoute({ method: 'GET', path: '/' }));\n    registry.add(getTestRoute({ method: 'POST', path: '/' }));\n    registry.add(getTestRoute({ method: 'GET', path: '/ab' }));\n\n    t.deepEqual(Object.keys(registry.get()), ['get', 'post', 'getab']);\n\n    t.done();\n});\n\ntest('removes a route', function(t) {\n    var registry = new RouterRegistryRadix();\n\n    // Mount\n    registry.add(getTestRoute({ method: 'GET', path: '/a' }));\n    registry.add(getTestRoute({ method: 'POST', path: '/b' }));\n    t.deepEqual(Object.keys(registry.get()), ['geta', 'postb']);\n\n    // Unmount\n    var route = registry.remove('geta');\n    t.ok(route);\n    t.equal(route.name, 'geta');\n\n    // Removes from registry\n    t.deepEqual(Object.keys(registry.get()), ['postb']);\n\n    t.end();\n});\n\ntest('lookups a route', function(t) {\n    var registry = new RouterRegistryRadix();\n    var route = getTestRoute({ method: 'GET', path: '/a/:b' });\n    registry.add(route);\n\n    var result = registry.lookup('GET', '/a/b');\n\n    t.deepEqual(result, {\n        route: route,\n        params: { b: 'b' },\n        handler: result.handler\n    });\n\n    t.done();\n});\n\ntest('get registered routes', function(t) {\n    var registry = new RouterRegistryRadix();\n    registry.add(getTestRoute({ method: 'GET', path: '/' }));\n    registry.add(getTestRoute({ method: 'GET', path: '/a' }));\n    registry.add(getTestRoute({ method: 'GET', path: '/a/b' }));\n    registry.add(getTestRoute({ method: 'POST', path: '/' }));\n\n    t.deepEqual(Object.keys(registry.get()), ['get', 'geta', 'getab', 'post']);\n    t.end();\n});\n\ntest('toString()', function(t) {\n    var registry = new RouterRegistryRadix();\n    registry.add(getTestRoute({ method: 'GET', path: '/' }));\n    registry.add(getTestRoute({ method: 'GET', path: '/a' }));\n    registry.add(getTestRoute({ method: 'GET', path: '/a/b' }));\n    registry.add(getTestRoute({ method: 'POST', path: '/' }));\n\n    t.deepEqual(\n        registry.toString(),\n        // prettier-ignore\n        '└── / (GET, POST)\\n' +\n        '    └── a (GET)\\n' +\n        '        └── /b (GET)\\n'\n    );\n    t.end();\n});\n\ntest('toString() with ignoreTrailingSlash', function(t) {\n    var registry = new RouterRegistryRadix({ ignoreTrailingSlash: true });\n    registry.add(getTestRoute({ method: 'GET', path: '/' }));\n    registry.add(getTestRoute({ method: 'GET', path: '/a' }));\n    registry.add(getTestRoute({ method: 'GET', path: '/a/b' }));\n    registry.add(getTestRoute({ method: 'POST', path: '/' }));\n\n    t.deepEqual(\n        registry.toString(),\n        // prettier-ignore\n        '└── / (GET, POST)\\n' +\n        '    └── a (GET)\\n' +\n        '        └── /b (GET)\\n'\n    );\n    t.end();\n});\n"
  },
  {
    "path": "test/server.test.js",
    "content": "// Copyright 2012 Mark Cavage, Inc.  All rights reserved.\n\n'use strict';\n/* eslint-disable func-names */\n\nconst { AsyncLocalStorage } = require('async_hooks');\nvar assert = require('assert-plus');\nvar childprocess = require('child_process');\nvar http = require('http');\n\nvar pino = require('pino');\nvar errors = require('restify-errors');\nvar restifyClients = require('restify-clients');\nvar uuid = require('uuid');\n\nvar RestError = errors.RestError;\nvar restify = require('../lib');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\nvar StreamRecorder = require('./lib/streamRecorder');\n\n///--- Globals\n\nvar after = helper.after;\nvar before = helper.before;\nvar test = helper.test;\n\nvar SKIP_IP_V6 = !!process.env.TEST_SKIP_IP_V6;\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar CLIENT;\nvar FAST_CLIENT;\nvar SERVER;\nlet LOG_BUFFER;\n\nvar NODE_MAJOR_VERSION = process.versions.node.split('.')[0];\n\nif (SKIP_IP_V6) {\n    console.warn('IPv6 tests are skipped: No IPv6 network is available');\n}\n\n///--- Tests\n\nbefore(function(cb) {\n    try {\n        LOG_BUFFER = new StreamRecorder();\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            handleUncaughtExceptions: true,\n            log: helper.getLog('server', LOG_BUFFER, 'info'),\n            version: ['2.0.0', '0.5.4', '1.4.3'],\n            ignoreTrailingSlash: true\n        });\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n            FAST_CLIENT = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false,\n                requestTimeout: 500\n            });\n\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\nafter(function(cb) {\n    try {\n        CLIENT.close();\n        FAST_CLIENT.close();\n        SERVER.close(function() {\n            CLIENT = null;\n            FAST_CLIENT = null;\n            SERVER = null;\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\ntest('listen and close (port only)', function(t) {\n    var server = restify.createServer();\n    server.listen(0, function() {\n        server.close(function() {\n            t.end();\n        });\n    });\n});\n\ntest('listen and close (port only) w/ port number as string', function(t) {\n    var server = restify.createServer();\n    server.listen(String(0), function() {\n        server.close(function() {\n            t.end();\n        });\n    });\n});\n\ntest('listen and close (socketPath)', function(t) {\n    var server = restify.createServer();\n    server.listen('/tmp/.' + uuid.v4(), function() {\n        server.close(function() {\n            t.end();\n        });\n    });\n});\n\n// Run IPv6 tests only if IPv6 network is available\nif (!SKIP_IP_V6) {\n    test('gh-751 IPv4/IPv6 server URL', function(t) {\n        t.equal(SERVER.url, 'http://127.0.0.1:' + PORT, 'ipv4 url');\n\n        var server = restify.createServer();\n        server.listen(PORT + 1, '::1', function() {\n            t.equal(server.url, 'http://[::1]:' + (PORT + 1), 'ipv6 url');\n\n            server.close(function() {\n                t.end();\n            });\n        });\n    });\n}\n\ntest('get (path only)', function(t) {\n    var r = SERVER.get('/foo/:id', function echoId(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), false);\n        res.send();\n        next();\n    });\n\n    var count = 0;\n    SERVER.once('after', function(req, res, route) {\n        t.ok(req);\n        t.ok(res);\n        t.equal(r, route.name);\n\n        if (++count === 2) {\n            t.end();\n        }\n    });\n\n    CLIENT.get('/foo/bar', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n\n        if (++count === 2) {\n            t.end();\n        }\n    });\n});\n\ntest('get (path only - with trailing slash)', function(t) {\n    SERVER.get('/foo/', function echoId(req, res, next) {\n        res.send();\n        next();\n    });\n\n    var count = 0;\n\n    CLIENT.get('/foo/', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n\n        if (++count === 2) {\n            t.end();\n        }\n    });\n\n    CLIENT.get('/foo', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n\n        if (++count === 2) {\n            t.end();\n        }\n    });\n});\n\ntest('get (path only - with trailing slash and nested route)', function(t) {\n    SERVER.get('/foo/', function echoId(req, res, next) {\n        res.statusCode = 200;\n        res.send();\n        next();\n    });\n\n    SERVER.get('/foo/bar', function echoId(req, res, next) {\n        res.statusCode = 201;\n        res.send();\n        next();\n    });\n\n    var count = 0;\n\n    CLIENT.get('/foo/', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n\n        if (++count === 4) {\n            t.end();\n        }\n    });\n\n    CLIENT.get('/foo', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n\n        if (++count === 4) {\n            t.end();\n        }\n    });\n\n    CLIENT.get('/foo/bar/', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 201);\n\n        if (++count === 4) {\n            t.end();\n        }\n    });\n\n    CLIENT.get('/foo/bar', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 201);\n\n        if (++count === 4) {\n            t.end();\n        }\n    });\n});\n\ntest('use + get (path only)', function(t) {\n    SERVER.use(function(req, res, next) {\n        next();\n    });\n    SERVER.get('/foo/:id', function tester(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        res.send();\n        next();\n    });\n\n    CLIENT.get('/foo/bar', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('rm', function(t) {\n    var routeName = SERVER.get('/foo/:id', function foosy(req, res, next) {\n        next();\n    });\n\n    SERVER.get('/bar/:id', function barsy(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'foo');\n        res.send();\n        next();\n    });\n\n    t.ok(SERVER.rm(routeName));\n\n    CLIENT.get('/foo/bar', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 404);\n        CLIENT.get('/bar/foo', function(err2, __, res2) {\n            t.ifError(err2);\n            t.equal(res2.statusCode, 200);\n            t.end();\n        });\n    });\n});\n\ntest(\n    '_routeErrorResponse does not cause uncaughtException when called when' +\n        'header has already been sent',\n    function(t) {\n        SERVER.on('MethodNotAllowed', function(req, res, error, next) {\n            res.json(405, { status: 'MethodNotAllowed' });\n            try {\n                next();\n            } catch (err) {\n                t.fail(\n                    'next() should not throw error' +\n                        'when header has already been sent'\n                );\n            }\n            t.end();\n        });\n\n        SERVER.post('/routePostOnly', function tester(req, res, next) {\n            next();\n        });\n\n        CLIENT.get('/routePostOnly', function(err, _, res) {\n            t.ok(err);\n            t.equal(res.statusCode, 405);\n        });\n    }\n);\n\ntest('use - throws TypeError on non function as argument', function(t) {\n    var errMsg = 'handler (function) is required';\n\n    t.throws(\n        function() {\n            SERVER.use('/nonfn');\n        },\n        assert.AssertionError,\n        errMsg\n    );\n\n    t.throws(\n        function() {\n            SERVER.use({ an: 'object' });\n        },\n        assert.AssertionError,\n        errMsg\n    );\n\n    t.throws(\n        function() {\n            SERVER.use(\n                function good(req, res, next) {\n                    next();\n                },\n                '/bad',\n                {\n                    really: 'bad'\n                }\n            );\n        },\n        assert.AssertionError,\n        errMsg\n    );\n\n    t.end();\n});\n\ntest('405', function(t) {\n    SERVER.post('/foo/:id', function posty(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        res.send();\n        next();\n    });\n\n    CLIENT.get('/foo/bar', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 405);\n        t.equal(res.headers.allow, 'POST');\n        t.end();\n    });\n});\n\ntest('PUT ok', function(t) {\n    SERVER.put('/foo/:id', function tester(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), true);\n        res.send();\n        next();\n    });\n\n    CLIENT.put('/foo/bar', {}, function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('PATCH ok', function(t) {\n    SERVER.patch('/foo/:id', function tester(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), true);\n        res.send();\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/foo/bar',\n        method: 'PATCH',\n        agent: false\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 200);\n        res.on('end', function() {\n            t.end();\n        });\n        res.resume();\n    }).end();\n});\n\ntest('HEAD ok', function(t) {\n    SERVER.head('/foo/:id', function tester(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), false);\n        res.send('hi there');\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/foo/bar',\n        method: 'HEAD',\n        agent: false\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 200);\n        res.on('data', function(chunk) {\n            t.fail('Data was sent on HEAD');\n        });\n        res.on('end', function() {\n            t.end();\n        });\n    }).end();\n});\n\ntest('DELETE ok', function(t) {\n    SERVER.del('/foo/:id', function tester(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), false);\n        res.send(204, 'hi there');\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/foo/bar',\n        method: 'DELETE',\n        agent: false\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 204);\n        res.on('data', function(chunk) {\n            t.fail('Data was sent on 204');\n        });\n        t.end();\n    }).end();\n});\n\ntest('OPTIONS', function(t) {\n    ['get', 'post', 'put', 'del'].forEach(function(method) {\n        SERVER[method]('/foo/:id', function tester(req, res, next) {\n            t.ok(req.params);\n            t.equal(req.params.id, 'bar');\n            res.send();\n            next();\n        });\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '*',\n        method: 'OPTIONS',\n        agent: false\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 200);\n        t.end();\n    }).end();\n});\n\ntest('RegExp ok', function(t) {\n    SERVER.get('/example/:file(^\\\\d+).png', function tester(req, res, next) {\n        t.deepEqual(req.params, {\n            file: '12'\n        });\n        res.send('hi there');\n        next();\n    });\n\n    CLIENT.get('/example/12.png', function(err, _, res, obj) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(obj, 'hi there');\n        t.end();\n    });\n});\n\ntest('get (path and version ok)', function(t) {\n    SERVER.get(\n        {\n            url: '/foo/:id',\n            version: '1.2.3'\n        },\n        function tester(req, res, next) {\n            t.ok(req.params);\n            t.equal(req.params.id, 'bar');\n            res.send();\n            next();\n        }\n    );\n\n    var opts = {\n        path: '/foo/bar',\n        headers: {\n            'accept-version': '~1.2'\n        }\n    };\n    CLIENT.get(opts, function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('GH-63 res.send 204 is sending a body', function(t) {\n    SERVER.del('/hello/:name', function tester(req, res, next) {\n        res.send(204);\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/hello/mark',\n        method: 'DELETE',\n        agent: false,\n        headers: {\n            accept: 'text/plain'\n        }\n    };\n\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 204);\n        var body = '';\n        res.setEncoding('utf8');\n        res.on('data', function(chunk) {\n            body += chunk;\n        });\n        res.on('end', function() {\n            t.notOk(body);\n            t.end();\n        });\n    }).end();\n});\n\ntest('GH-64 prerouting chain', function(t) {\n    SERVER.pre(function(req, res, next) {\n        req.log.debug('testing log is set');\n        req.headers.accept = 'application/json';\n        next();\n    });\n\n    SERVER.get('/hello/:name', function tester(req, res, next) {\n        res.send(req.params.name);\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/hello/mark',\n        method: 'GET',\n        agent: false,\n        headers: {\n            accept: 'text/plain'\n        }\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 200);\n        var body = '';\n        res.setEncoding('utf8');\n        res.on('data', function(chunk) {\n            body += chunk;\n        });\n        res.on('end', function() {\n            t.equal(body, '\"mark\"');\n            t.end();\n        });\n    }).end();\n});\n\ntest('GH-64 prerouting chain with error', function(t) {\n    SERVER.pre(function(req, res, next) {\n        next(\n            new RestError(\n                {\n                    statusCode: 400,\n                    restCode: 'BadRequest'\n                },\n                'screw you client'\n            )\n        );\n    });\n\n    SERVER.get('/hello/:name', function tester(req, res, next) {\n        res.send(req.params.name);\n        next();\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 400);\n        t.end();\n    });\n});\n\ntest('GH-67 extend access-control headers', function(t) {\n    SERVER.get('/hello/:name', function tester(req, res, next) {\n        res.header(\n            'Access-Control-Allow-Headers',\n            res.header('Access-Control-Allow-Headers') +\n                ', If-Match, If-None-Match'\n        );\n\n        res.send(req.params.name);\n        next();\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.ok(res.headers['access-control-allow-headers'].indexOf('If-Match'));\n        t.end();\n    });\n});\n\ntest('GH-77 uncaughtException (default behavior)', function(t) {\n    SERVER.get('/', function(req, res, next) {\n        throw new Error('Catch me!');\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\n// eslint-disable-next-line\ntest('handleUncaughtExceptions should not call handler for internal errors', function(t) {\n    SERVER.get('/', function(req, res, next) {\n        // This route is not used for the test but at least one route needs to\n        // be registered to Restify in order for routing logic to be run\n        assert.fail('should not run');\n    });\n\n    SERVER.on('uncaughtException', function throwError(err) {\n        t.ifError(err);\n        t.end();\n    });\n\n    CLIENT.head('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 405);\n        t.end();\n    });\n});\n\n// eslint-disable-next-line\ntest('handleUncaughtExceptions should not call handler for next(new Error())', function(t) {\n    SERVER.get('/', function(req, res, next) {\n        next(new Error('I am not fatal'));\n    });\n\n    SERVER.on('uncaughtException', function throwError(err) {\n        t.ifError(err);\n        t.end();\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('GH-77 uncaughtException (with custom handler)', function(t) {\n    SERVER.on('uncaughtException', function(req, res, route, err) {\n        res.send(204);\n    });\n    SERVER.get('/', function(req, res, next) {\n        throw new Error('Catch me!');\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 204);\n        t.end();\n    });\n});\n\ntest('GH-180 can parse DELETE body', function(t) {\n    SERVER.use(restify.plugins.bodyParser({ mapParams: false }));\n\n    SERVER.del('/', function(req, res, next) {\n        res.send(200, req.body);\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/',\n        method: 'DELETE',\n        agent: false,\n        headers: {\n            accept: 'application/json',\n            'content-type': 'application/json',\n            'transfer-encoding': 'chunked'\n        }\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 200);\n        res.setEncoding('utf8');\n        res.body = '';\n        res.on('data', function(chunk) {\n            res.body += chunk;\n        });\n        res.on('end', function() {\n            t.equal(res.body, '{\"param1\":1234}');\n            t.end();\n        });\n    }).end('{\"param1\": 1234}');\n});\n\ntest('returning error from a handler (with domains)', function(t) {\n    SERVER.get('/', function(req, res, next) {\n        next(new errors.InternalError('bah!'));\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('emitting error from a handler (with domains)', function(t) {\n    SERVER.get('/', function(req, res, next) {\n        req.emit('error', new Error('bah!'));\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('re-emitting redirect from a response', function(t) {\n    var redirectLocation;\n\n    SERVER.on('redirect', function(payload) {\n        redirectLocation = payload;\n    });\n\n    SERVER.get('/', function(req, res, next) {\n        res.redirect('/10', next);\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.equal(redirectLocation, '/10');\n        t.end();\n    });\n});\n\ntest('throwing error from a handler (with domains)', function(t) {\n    SERVER.get('/', function(req, res, next) {\n        process.nextTick(function() {\n            throw new Error('bah!');\n        });\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('gh-278 missing router error events (404)', function(t) {\n    SERVER.once('NotFound', function(req, res) {\n        res.send(404, 'foo');\n    });\n\n    CLIENT.get('/' + uuid.v4(), function(err, _, res) {\n        t.ok(err);\n        t.equal(err.message, '\"foo\"');\n        t.equal(res.statusCode, 404);\n        t.end();\n    });\n});\n\ntest('gh-278 missing router error events (405)', function(t) {\n    var p = '/' + uuid.v4();\n    SERVER.post(p, function(req, res, next) {\n        res.send(201);\n        next();\n    });\n    SERVER.once('MethodNotAllowed', function(req, res) {\n        res.send(405, 'foo');\n    });\n\n    CLIENT.get(p, function(err, _, res) {\n        t.ok(err);\n        t.equal(err.message, '\"foo\"');\n        t.equal(res.statusCode, 405);\n        t.end();\n    });\n});\n\ntest('gh-329 wrong values in res.methods', function(t) {\n    function route(req, res, next) {\n        res.send(200);\n        next();\n    }\n\n    SERVER.get('/stuff', route);\n    SERVER.post('/stuff', route);\n    SERVER.get('/stuff/:id', route);\n    SERVER.put('/stuff/:id', route);\n    SERVER.del('/stuff/:id', route);\n\n    SERVER.once('MethodNotAllowed', function(req, res, cb) {\n        t.ok(res.methods);\n        t.deepEqual(res.methods, ['DELETE', 'GET', 'PUT']);\n        res.send(405);\n    });\n\n    CLIENT.post('/stuff/foo', {}, function(err, _, res) {\n        t.ok(err);\n        t.end();\n    });\n});\n\ntest('GH #704: Route with a valid RegExp params', function(t) {\n    SERVER.get(\n        {\n            name: 'regexp_param1',\n            path: '/foo/:id([0-9]+)'\n        },\n        function(req, res, next) {\n            t.equal(req.params.id, '0123456789');\n            res.send();\n            next();\n        }\n    );\n\n    CLIENT.get('/foo/0123456789', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('GH #704: Route with an invalid RegExp params', function(t) {\n    SERVER.get(\n        {\n            name: 'regexp_param2',\n            path: '/foo/:id([0-9]+)'\n        },\n        function(req, res, next) {\n            t.equal(req.params.id, 'A__M');\n            res.send();\n            next();\n        }\n    );\n\n    CLIENT.get('/foo/A__M', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 404);\n        t.end();\n    });\n});\n\ntest('run param only with existing req.params', function(t) {\n    var count = 0;\n\n    SERVER.param('name', function(req, res, next) {\n        count++;\n        next();\n    });\n\n    SERVER.param('userId', function(req, res, next) {\n        count++;\n        next();\n    });\n\n    SERVER.get('/users/:userId', function(req, res, next) {\n        res.send(200);\n    });\n\n    CLIENT.get('/users/1', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(count, 1);\n        t.end();\n    });\n});\n\ntest('run param only with existing req.params', function(t) {\n    var count = 0;\n\n    SERVER.param('name', function(req, res, next) {\n        count++;\n        next();\n    });\n\n    SERVER.param('userId', function(req, res, next, param, name) {\n        t.equal(param, '1');\n        t.equal(name, 'userId');\n        count++;\n        next();\n    });\n\n    SERVER.get('/users/:userId', function(req, res, next) {\n        res.send(200);\n    });\n\n    CLIENT.get('/users/1', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(count, 1);\n        t.end();\n    });\n});\n\ntest('next(\"string\") returns InternalServer', function(t) {\n    var count = 0;\n\n    SERVER.use(function(req, res, next) {\n        count++;\n        next();\n    });\n\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo/:id'\n        },\n        function(req, res, next) {\n            t.equal(req.params.id, 'blah');\n            next('bar');\n        }\n    );\n\n    CLIENT.get('/foo/blah', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.equal(count, 1);\n        t.end();\n    });\n});\n\ntest('next(\"string\") from a use plugin returns InternalServer', function(t) {\n    var count = 0;\n\n    SERVER.use(function plugin(req, res, next) {\n        count++;\n        next('bar');\n    });\n\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo'\n        },\n        function getFoo(req, res, next) {\n            res.send(200);\n            next();\n        }\n    );\n\n    CLIENT.get('/foo', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.equal(count, 1);\n        t.end();\n    });\n});\n\ntest('res.charSet', function(t) {\n    SERVER.get('/foo', function getFoo(req, res, next) {\n        res.charSet('ISO-8859-1');\n        res.set('Content-Type', 'text/plain');\n        // send a string instead of JSON\n        res.send(200, JSON.stringify({ foo: 'bar' }));\n        next();\n    });\n\n    CLIENT.get('/foo', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(res.headers['content-type'], 'text/plain; charset=ISO-8859-1');\n        t.end();\n    });\n});\n\ntest('res.charSet override', function(t) {\n    SERVER.get('/foo', function getFoo(req, res, next) {\n        res.charSet('ISO-8859-1');\n        res.set('Content-Type', 'text/plain;charset=utf-8');\n        // send a string instead of JSON\n        res.send(200, JSON.stringify({ foo: 'bar' }));\n        next();\n    });\n\n    CLIENT.get('/foo', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(res.headers['content-type'], 'text/plain; charset=ISO-8859-1');\n        t.end();\n    });\n});\n\ntest('GH-384 res.json(200, {}) broken', function(t) {\n    SERVER.get('/foo', function(req, res, next) {\n        res.json(200, { foo: 'bar' });\n        next();\n    });\n\n    CLIENT.get('/foo', function(err, _, res, obj) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.ok(obj);\n        t.equal((obj || {}).foo, 'bar');\n        t.end();\n    });\n});\n\ntest('explicitly sending a 403 with custom error', function(t) {\n    function MyCustomError() {}\n\n    MyCustomError.prototype = Object.create(Error.prototype);\n\n    SERVER.get('/', function(req, res, next) {\n        res.send(403, new MyCustomError('bah!'));\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 403);\n        t.end();\n    });\n});\n\ntest('explicitly sending a 403 on error', function(t) {\n    SERVER.get('/', function(req, res, next) {\n        res.send(403, new Error('bah!'));\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 403);\n        t.end();\n    });\n});\n\ntest('fire event on error', function(t) {\n    SERVER.once('InternalServer', function(req, res, err, cb) {\n        t.ok(req);\n        t.ok(res);\n        t.ok(err);\n        t.ok(cb);\n        t.equal(typeof cb, 'function');\n        return cb();\n    });\n\n    SERVER.get('/', function(req, res, next) {\n        return next(new errors.InternalServerError('bah!'));\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.expect(7);\n        t.end();\n    });\n});\n\ntest('error handler defers \"after\" event', async function(t) {\n    let afterResolve;\n    let clientResolve;\n    t.expect(9);\n    SERVER.once('NotFound', function(req, res, err, cb) {\n        t.ok(req);\n        t.ok(res);\n        t.ok(cb);\n        t.equal(typeof cb, 'function');\n        t.ok(err);\n\n        SERVER.removeAllListeners('after');\n        SERVER.once('after', function(req2, res2) {\n            t.ok(req2);\n            t.ok(res2);\n            afterResolve();\n        });\n        return cb();\n    });\n    SERVER.once('after', function() {\n        // do not fire prematurely\n        t.notOk(true);\n    });\n    CLIENT.get('/' + uuid.v4(), function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 404);\n        clientResolve();\n    });\n\n    await Promise.all([\n        new Promise(resolve => {\n            afterResolve = resolve;\n        }),\n        new Promise(resolve => {\n            clientResolve = resolve;\n        })\n    ]);\n    t.end();\n});\n\n// eslint-disable-next-line\ntest('gh-757 req.absoluteUri() defaults path segment to req.path()', function(t) {\n    SERVER.get('/the-original-path', function(req, res, next) {\n        var prefix = 'http://127.0.0.1:' + PORT;\n        t.equal(\n            req.absoluteUri('?key=value'),\n            prefix + '/the-original-path/?key=value'\n        );\n        t.equal(\n            req.absoluteUri('#fragment'),\n            prefix + '/the-original-path/#fragment'\n        );\n        t.equal(\n            req.absoluteUri('?key=value#fragment'),\n            prefix + '/the-original-path/?key=value#fragment'\n        );\n        res.send();\n        next();\n    });\n\n    CLIENT.get('/the-original-path', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('GH-693 sending multiple response header values', function(t) {\n    SERVER.get('/', function(req, res, next) {\n        res.link('/', 'self');\n        res.link('/foo', 'foo');\n        res.link('/bar', 'bar');\n        res.send(200, 'root');\n    });\n\n    CLIENT.get('/', function(err, _, res) {\n        t.equal(res.statusCode, 200);\n        t.equal(res.headers.link.split(',').length, 3);\n        t.end();\n    });\n});\n\ntest('gh-762 res.noCache()', function(t) {\n    SERVER.get('/some-path', function(req, res, next) {\n        res.noCache();\n        res.send('data');\n    });\n\n    CLIENT.get('/some-path', function(err, _, res) {\n        t.equal(\n            res.headers['cache-control'],\n            'no-cache, no-store, must-revalidate'\n        );\n        t.equal(res.headers.pragma, 'no-cache');\n        t.equal(res.headers.expires, '0');\n        t.end();\n    });\n});\n\ntest('gh-779 set-cookie fields should never have commas', function(t) {\n    SERVER.get('/set-cookie', function(req, res, next) {\n        res.header('set-cookie', 'foo');\n        res.header('set-cookie', 'bar');\n        res.send(200);\n    });\n\n    CLIENT.get('/set-cookie', function(err, _, res) {\n        t.ifError(err);\n        t.equal(\n            res.rawHeaders.filter(function(keyOrValue) {\n                return keyOrValue === 'set-cookie';\n            }).length,\n            2,\n            'multiple set-cookie headers should not be merged'\n        );\n        t.equal(res.headers['set-cookie'][0], 'foo');\n        t.equal(res.headers['set-cookie'][1], 'bar');\n        t.end();\n    });\n});\n\ntest(\n    'gh-986 content-type fields should never have commas' +\n        ' (via `res.header(...)`)',\n    function(t) {\n        SERVER.get('/content-type', function(req, res, next) {\n            res.header('content-type', 'foo');\n            res.header('content-type', 'bar');\n            res.send(200);\n        });\n\n        CLIENT.get('/content-type', function(err, _, res) {\n            t.ifError(err);\n            t.equal(\n                Array.isArray(res.headers['content-type']),\n                false,\n                'content-type header should not be an array'\n            );\n            t.equal(res.headers['content-type'], 'bar');\n            t.end();\n        });\n    }\n);\n\ntest(\n    'gh-986 content-type fields should never have commas' +\n        ' (via `res.setHeader(...)`)',\n    function(t) {\n        SERVER.get('/content-type', function(req, res, next) {\n            res.setHeader('content-type', 'foo');\n            res.setHeader('content-type', 'bar');\n            res.send(200);\n        });\n\n        CLIENT.get('/content-type', function(err, _, res) {\n            t.ifError(err);\n            t.equal(\n                Array.isArray(res.headers['content-type']),\n                false,\n                'content-type header should not be an array'\n            );\n            t.equal(res.headers['content-type'], 'bar');\n            t.end();\n        });\n    }\n);\n\ntest('GH-877 content-type should be case insensitive', function(t) {\n    SERVER.use(restify.plugins.bodyParser({ maxBodySize: 1024 }));\n\n    SERVER.get('/cl', function(req, res, next) {\n        t.equal(req.getContentType(), 'application/json');\n        res.send(200);\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/cl',\n        method: 'GET',\n        agent: false,\n        headers: {\n            accept: 'application/json',\n            'content-type': 'APPLicatioN/JSon',\n            'transfer-encoding': 'chunked'\n        }\n    };\n    var client = http.request(opts, function(res) {\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n    client.end();\n});\n\ntest('GH-882: route name is same as specified', function(t) {\n    SERVER.get(\n        {\n            name: 'my-r$-%-x',\n            path: '/m1'\n        },\n        function(req, res, next) {\n            res.send({ name: req.route.name });\n        }\n    );\n\n    CLIENT.get('/m1', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.body, '{\"name\":\"my-r$-%-x\"}');\n        t.end();\n    });\n});\n\ntest(\n    'GH-733 if request closed early, stop processing. ensure only ' +\n        'relevant audit logs output.',\n    function(t) {\n        // Dirty hack to capture the log record using a ring buffer.\n        var numCount = 0;\n\n        // FAST_CLIENT times out at 500ms, should capture two records then close\n        // the request.\n        SERVER.get('/audit', [\n            function first(req, res, next) {\n                req.startHandlerTimer('first');\n                setTimeout(function() {\n                    numCount++;\n                    req.endHandlerTimer('first');\n                    return next();\n                }, 300);\n            },\n            function second(req, res, next) {\n                req.startHandlerTimer('second');\n                numCount++;\n                req.endHandlerTimer('second');\n                setTimeout(function() {\n                    return next();\n                }, 300);\n            },\n            function third(req, res, next) {\n                req.endHandlerTimer('third');\n                numCount++;\n                res.send({ hello: 'world' });\n                return next();\n            }\n        ]);\n\n        // set up audit logs\n        SERVER.on(\n            'after',\n            restify.plugins.auditLogger({\n                log: pino({ name: 'audit' }),\n                event: 'after'\n            })\n        );\n\n        SERVER.on('after', function(req, res, route, err) {\n            if (req.href() === '/audit?v=2') {\n                // should request timeout error\n                t.ok(err);\n                t.equal(err.name, 'RequestCloseError');\n\n                // check records\n                t.ok(LOG_BUFFER.records[0], 'no log records');\n                t.equal(\n                    LOG_BUFFER.records.length,\n                    1,\n                    'should only have 1 log record'\n                );\n\n                // check timers\n                var handlers = Object.keys(LOG_BUFFER.records[0].req.timers);\n                t.equal(handlers.length, 2, 'should only have 2 req timers');\n                t.equal(\n                    handlers[0],\n                    'first',\n                    'first handler timer not in order'\n                );\n                t.equal(\n                    handlers[handlers.length - 1],\n                    'second',\n                    'second handler not last'\n                );\n                t.end();\n\n                // ensure third handler never ran\n                t.equal(numCount, 2);\n\n                t.end();\n            }\n        });\n\n        CLIENT.get('/audit?v=1', function(err, req, res, data) {\n            t.ifError(err);\n            t.deepEqual(data, { hello: 'world' });\n            t.equal(numCount, 3);\n\n            // reset numCount\n            numCount = 0;\n            //reset stream-recorder\n            LOG_BUFFER.flushRecords();\n\n            FAST_CLIENT.get('/audit?v=2', function(err2, req2, res2, data2) {\n                t.ok(err2);\n                t.equal(err2.name, 'RequestTimeoutError');\n            });\n        });\n    }\n);\n\ntest('GH-667 emit error event for generic Errors', function(t) {\n    var restifyErrorFired = 0;\n    var notFoundFired = 0;\n    var myErr = new errors.NotFoundError('foobar');\n\n    SERVER.get('/1', function(req, res, next) {\n        return next(new Error('foobar'));\n    });\n\n    SERVER.get('/2', function(req, res, next) {\n        return next(myErr);\n    });\n\n    SERVER.get('/3', function(req, res, next) {\n        SERVER.on('NotFound', function(req2, res2, err, cb) {\n            notFoundFired++;\n            t.ok(err);\n            t.equal(err, myErr);\n            t.end();\n            return cb();\n        });\n        return next(myErr);\n    });\n\n    SERVER.on('restifyError', function(req, res, err, cb) {\n        restifyErrorFired++;\n        t.ok(err);\n        t.equal(err instanceof Error, true);\n\n        if (err instanceof errors.NotFoundError) {\n            t.equal(err, myErr);\n        }\n        return cb();\n    });\n\n    /*eslint-disable no-shadow*/\n    CLIENT.get('/1', function(err, req, res, data) {\n        // should get regular error\n        // fail here. But why?\n        t.ok(err);\n        t.equal(restifyErrorFired, 1);\n\n        CLIENT.get('/2', function(err, req, res, data) {\n            // should get not found error\n            t.ok(err);\n            t.equal(restifyErrorFired, 2);\n\n            CLIENT.get('/3', function(err, req, res, data) {\n                // should get notfounderror\n                t.ok(err);\n                t.equal(restifyErrorFired, 3);\n                t.equal(notFoundFired, 1);\n            });\n        });\n    });\n    /*eslint-enable no-shadow*/\n});\n\n// eslint-disable-next-line\ntest('GH-667 returning error in error handler should not do anything', function(t) {\n    SERVER.on('ImATeapot', function(req, res, err, cb) {\n        // attempt to pass a new error back\n        return cb(new errors.LockedError('oh noes'));\n    });\n\n    SERVER.get('/1', function(req, res, next) {\n        return next(new errors.ImATeapotError('foobar'));\n    });\n\n    CLIENT.get('/1', function(err, req, res, data) {\n        t.ok(err);\n        // should still get the original error\n        t.equal(err.name, 'ImATeapotError');\n        t.end();\n    });\n});\n\ntest('GH-1024 disable uncaughtException handler', function(t) {\n    // With uncaughtException handling disabled, the node process will abort,\n    // so testing of this feature must occur in a separate node process.\n\n    var allStderr = '';\n    var serverPath = __dirname + '/lib/server-withDisableUncaughtException.js';\n    var serverProc = childprocess.fork(serverPath, { silent: true });\n\n    // Record stderr, to check for the correct exception stack.\n    serverProc.stderr.on('data', function(data) {\n        allStderr += String(data);\n    });\n\n    // Handle serverPortResponse and then make the client request - the request\n    // should receive a connection closed error (because the server aborts).\n    serverProc.on('message', function(msg) {\n        if (msg.task !== 'serverPortResponse') {\n            serverProc.kill();\n            t.end();\n            return;\n        }\n\n        var port = msg.port;\n        var client = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + port,\n            dtrace: helper.dtrace,\n            retry: false\n        });\n\n        client.get('/', function(err, _, res) {\n            // Should get a connection closed error, but no response object.\n            t.ok(err);\n            t.equal(err.code, 'ECONNRESET');\n            t.equal(res, undefined);\n\n            serverProc.kill(); // Ensure it's dead.\n\n            t.ok(allStderr.indexOf('Error: Catch me!') > 0);\n\n            t.end();\n        });\n    });\n\n    serverProc.send({ task: 'serverPortRequest' });\n});\n\ntest('GH-999 Custom 404 handler does not send response', function(t) {\n    // make the 404 handler act like other error handlers - must modify\n    // err.body to send a custom response.\n\n    SERVER.on('NotFound', function(req, res, err, cb) {\n        err.body = {\n            message: 'my custom not found'\n        };\n        return cb();\n    });\n\n    CLIENT.get('/notfound', function(err, _, res) {\n        t.ok(err);\n        t.deepEqual(\n            res.body,\n            JSON.stringify({\n                message: 'my custom not found'\n            })\n        );\n        t.end();\n    });\n});\n\ntest('calling next(false) should early exit from pre handlers', function(t) {\n    var afterFired = false;\n\n    SERVER.pre(function(req, res, next) {\n        res.send('early exit');\n        return next(false);\n    });\n\n    SERVER.get('/1', function(req, res, next) {\n        res.send('hello world');\n        return next();\n    });\n\n    SERVER.on('after', function() {\n        afterFired = true;\n    });\n\n    CLIENT.get('/1', function(err, req, res, data) {\n        t.ifError(err);\n        t.equal(data, 'early exit');\n        // ensure after event fired\n        t.ok(afterFired);\n        t.end();\n    });\n});\n\ntest('calling next(false) should early exit from use handlers', function(t) {\n    var steps = 0;\n\n    SERVER.use(function(req, res, next) {\n        res.send('early exit');\n        return next(false);\n    });\n\n    SERVER.get('/1', function(req, res, next) {\n        res.send('hello world');\n        return next();\n    });\n\n    SERVER.on('after', function() {\n        steps++;\n        t.equal(steps, 1);\n        t.end();\n    });\n\n    CLIENT.get('/1', function(err, req, res, data) {\n        t.ifError(err);\n        t.equal(data, 'early exit');\n        steps++;\n    });\n});\n\ntest('calling next(err) from pre should still emit after event', function(t) {\n    setTimeout(function() {\n        t.fail('Timed out');\n        t.end();\n    }, 2000);\n    var error = new Error();\n    SERVER.pre(function(req, res, next) {\n        next(error);\n    });\n    SERVER.get('/', function(req, res, next) {\n        t.fail('should have aborted stack before routing');\n    });\n    SERVER.on('after', function(req, res, route, err) {\n        t.equal(err, error);\n        t.end();\n    });\n    CLIENT.get('/', function() {});\n});\n\ntest('GH-1078: server name should default to restify', function(t) {\n    var myServer = restify.createServer();\n    var port = 3000;\n\n    myServer.get('/', function(req, res, next) {\n        res.send('hi');\n        return next();\n    });\n\n    var myClient = restifyClients.createStringClient({\n        url: 'http://127.0.0.1:' + port,\n        headers: {\n            connection: 'close'\n        }\n    });\n\n    myServer.listen(port, function() {\n        myClient.get('/', function(err, req, res, data) {\n            t.ifError(err);\n            t.equal(res.headers.server, 'restify');\n            myServer.close(t.end);\n        });\n    });\n});\n\ntest('GH-1078: server name should be customizable', function(t) {\n    var myServer = restify.createServer({\n        name: 'foo'\n    });\n    var port = 3000;\n\n    myServer.get('/', function(req, res, next) {\n        res.send('hi');\n        return next();\n    });\n\n    var myClient = restifyClients.createStringClient({\n        url: 'http://127.0.0.1:' + port,\n        headers: {\n            connection: 'close'\n        }\n    });\n\n    myServer.listen(port, function() {\n        myClient.get('/', function(err, req, res, data) {\n            t.ifError(err);\n            t.equal(res.headers.server, 'foo');\n            myServer.close(t.end);\n        });\n    });\n});\n\n// eslint-disable-next-line\ntest('GH-1078: server name should be overridable and not sent down', function(t) {\n    var myServer = restify.createServer({\n        name: ''\n    });\n    var port = 3000;\n\n    myServer.get('/', function(req, res, next) {\n        res.send('hi');\n        return next();\n    });\n\n    var myClient = restifyClients.createStringClient({\n        url: 'http://127.0.0.1:' + port,\n        headers: {\n            connection: 'close'\n        }\n    });\n\n    myServer.listen(port, function() {\n        myClient.get('/', function(err, req, res, data) {\n            t.ifError(err);\n            t.equal(res.headers.hasOwnProperty('server'), false);\n            myServer.close(t.end);\n        });\n    });\n});\n\ntest(\"should emit 'after' on successful request\", function(t) {\n    SERVER.on('after', function(req, res, route, err) {\n        t.ifError(err);\n        t.end();\n    });\n\n    SERVER.get('/foobar', function(req, res, next) {\n        res.send('hello world');\n        next();\n    });\n\n    CLIENT.get('/foobar', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n    });\n});\n\ntest(\"should emit 'after' on successful request with work\", function(t) {\n    SERVER.on('after', function(req, res, route, err) {\n        t.ifError(err);\n        t.end();\n    });\n\n    SERVER.get('/foobar', function(req, res, next) {\n        // with timeouts we are testing that request lifecycle\n        // events are firing in the correct order\n        setTimeout(function() {\n            res.send('hello world');\n            setTimeout(function() {\n                next();\n            }, 500);\n        }, 500);\n    });\n\n    CLIENT.get('/foobar', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n    });\n});\n\ntest(\"should emit 'after' on errored request\", function(t) {\n    SERVER.on('after', function(req, res, route, err) {\n        t.ok(err);\n        t.end();\n    });\n\n    SERVER.get('/foobar', function(req, res, next) {\n        next(new Error('oh noes'));\n    });\n\n    CLIENT.get('/foobar', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n    });\n});\n\ntest(\"should emit 'after' on uncaughtException\", function(t) {\n    SERVER.on('after', function(req, res, route, err) {\n        t.ok(err);\n        t.equal(err.message, 'oh noes');\n    });\n\n    SERVER.get('/foobar', function(req, res, next) {\n        throw new Error('oh noes');\n    });\n\n    CLIENT.get('/foobar', function(err, _, res) {\n        t.ok(err);\n        t.equal(err.name, 'InternalError');\n        t.end();\n    });\n});\n\ntest(\"should emit 'after' when sending res on uncaughtException\", function(t) {\n    SERVER.on('after', function(req, res, route, err) {\n        t.ok(err);\n        t.equal(err.message, 'oh noes');\n    });\n\n    SERVER.on('uncaughtException', function(req, res, route, err) {\n        res.send(504, 'boom');\n    });\n\n    SERVER.get('/foobar', function(req, res, next) {\n        throw new Error('oh noes');\n    });\n\n    CLIENT.get('/foobar', function(err, _, res) {\n        t.ok(err);\n        t.equal(err.name, 'GatewayTimeoutError');\n        t.end();\n    });\n});\n\ntest(\n    \"should emit 'after' on client closed request \" +\n        \"(req.connectionState(): 'close')\",\n    function(t) {\n        SERVER.on('after', function(req, res, route, err) {\n            t.ok(err);\n            t.equal(req.connectionState(), 'close');\n            t.equal(res.statusCode, 444);\n            t.equal(err.name, 'RequestCloseError');\n            t.end();\n        });\n\n        SERVER.get('/foobar', function(req, res, next) {\n            // fast client times out at 500ms, wait for 800ms which should cause\n            // client to timeout\n            setTimeout(function() {\n                return next();\n            }, 800);\n        });\n\n        FAST_CLIENT.get('/foobar', function(err, _, res) {\n            t.ok(err);\n            t.equal(err.name, 'RequestTimeoutError');\n        });\n    }\n);\n\n// This test reproduces https://github.com/restify/node-restify/issues/1765. It\n// specifically tests the edge case of an exception being thrown from a route\n// handler _after_ the response is considered to be \"flushed\" (for instance when\n// the request is aborted before a response is sent and an exception is thrown).\n// eslint-disable-next-line max-len\ntest(\"should emit 'after' on uncaughtException after response closed with custom uncaughtException listener\", function(t) {\n    var ERR_MSG = 'foo';\n    var gotAfter = false;\n    var gotReqCallback = false;\n\n    SERVER.on('after', function(req, res, route, err) {\n        gotAfter = true;\n        t.ok(err);\n        t.equal(req.connectionState(), 'close');\n        t.equal(res.statusCode, 444);\n        t.equal(err.name, 'Error');\n        t.equal(err.message, ERR_MSG);\n        if (gotReqCallback) {\n            t.end();\n        }\n    });\n\n    SERVER.on('uncaughtException', function(req, res, route, err, callback) {\n        callback();\n    });\n\n    SERVER.get('/foobar', function(req, res, next) {\n        res.on('close', function onResClose() {\n            // We throw this error in the response's close event handler on\n            // purpose to exercise the code path where we mark the route\n            // handlers as finished _after_ the response is marked as flushed.\n            throw new Error(ERR_MSG);\n        });\n    });\n\n    FAST_CLIENT.get('/foobar', function(err, _, res) {\n        gotReqCallback = true;\n        t.ok(err);\n        t.equal(err.name, 'RequestTimeoutError');\n        if (gotAfter) {\n            t.end();\n        }\n    });\n});\n\ntest('should increment/decrement inflight request count', function(t) {\n    SERVER.get('/foo', function(req, res, next) {\n        t.equal(SERVER.inflightRequests(), 1);\n        res.send();\n        return next();\n    });\n\n    SERVER.on('after', function() {\n        t.equal(SERVER.inflightRequests(), 0);\n        t.end();\n    });\n\n    CLIENT.get('/foo', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(SERVER.inflightRequests(), 0);\n    });\n});\n\n// eslint-disable-next-line\ntest('should increment/decrement inflight request count for concurrent reqs', function(t) {\n    SERVER.get('/foo1', function(req, res, next) {\n        // other request is already sent\n        t.equal(SERVER.inflightRequests() >= 1, true);\n        setTimeout(function() {\n            res.send();\n            return next();\n        }, 250);\n    });\n\n    SERVER.get('/foo2', function(req, res, next) {\n        t.equal(SERVER.inflightRequests(), 2);\n        res.send();\n        return next();\n    });\n\n    CLIENT.get('/foo1', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(SERVER.inflightRequests(), 0);\n        t.end();\n    });\n\n    CLIENT.get('/foo2', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(SERVER.inflightRequests(), 1);\n    });\n});\n\ntest(\"should emit 'close' on server close\", function(t) {\n    var server = restify.createServer();\n\n    server.listen(PORT + 1, '127.0.0.1', function() {\n        server.on('close', function() {\n            t.end();\n        });\n        server.close();\n    });\n});\n\ntest('should cleanup inflight requests count for 404s', async function(t) {\n    let afterResolve;\n    let clientResolve;\n    SERVER.get('/foo1', function(req, res, next) {\n        t.equal(SERVER.inflightRequests(), 1);\n        res.send();\n        return next();\n    });\n\n    SERVER.on('after', function(req) {\n        if (req.path() === '/doesnotexist') {\n            t.equal(SERVER.inflightRequests(), 0);\n            afterResolve();\n        }\n    });\n\n    CLIENT.get('/foo1', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(SERVER.inflightRequests(), 0);\n\n        CLIENT.get('/doesnotexist', function(err2, _2, res2) {\n            t.ok(err2);\n            t.equal(res2.statusCode, 404);\n            t.equal(SERVER.inflightRequests(), 0);\n            clientResolve();\n        });\n    });\n\n    await Promise.all([\n        new Promise(resolve => {\n            afterResolve = resolve;\n        }),\n        new Promise(resolve => {\n            clientResolve = resolve;\n        })\n    ]);\n    t.end();\n});\n\ntest('should cleanup inflight requests count for timeouts', function(t) {\n    t.equal(SERVER.inflightRequests(), 0);\n\n    SERVER.get('/foo1', function(req, res, next) {\n        // othr request is already sent\n        t.equal(SERVER.inflightRequests() >= 1, true);\n        setTimeout(function() {\n            res.send();\n            return next();\n        }, 1000);\n    });\n\n    SERVER.get('/foo2', function(req, res, next) {\n        t.equal(SERVER.inflightRequests(), 2);\n        res.send();\n        return next();\n    });\n\n    SERVER.on('after', function(req) {\n        if (req.path() === '/foo1') {\n            t.equal(SERVER.inflightRequests(), 0);\n            t.end();\n        } else if (req.path() === '/foo2') {\n            t.equal(SERVER.inflightRequests(), 1);\n        }\n    });\n\n    FAST_CLIENT.get('/foo1', function(err, _, res) {\n        t.ok(err);\n        t.equal(SERVER.inflightRequests(), 1);\n    });\n\n    CLIENT.get('/foo2', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.equal(SERVER.inflightRequests(), 1);\n    });\n});\n\n// eslint-disable-next-line\ntest('should cleanup inflight requests count on uncaughtExceptions', function(t) {\n    SERVER.on('uncaughtException', function(req, res, route, err) {\n        res.send(500, 'asplode');\n    });\n\n    SERVER.get('/foo1', function(req, res, next) {\n        t.equal(SERVER.inflightRequests(), 1);\n        throw new Error('oh noes');\n    });\n\n    CLIENT.get('/foo1', function(err, _, res) {\n        t.ok(err);\n        t.equal(SERVER.inflightRequests(), 0);\n        t.end();\n    });\n});\n\ntest('should show debug information', function(t) {\n    SERVER.pre(function pre(req, res, next) {\n        return next();\n    });\n    SERVER.pre(function pre2(req, res, next) {\n        return next();\n    });\n    SERVER.use(function use(req, res, next) {\n        return next();\n    });\n    SERVER.use(function use2(req, res, next) {\n        return next();\n    });\n    SERVER.on('after', function aft() {});\n    SERVER.on('after', function aft2() {});\n\n    SERVER.get(\n        '/foo',\n        function(req, res, next) {\n            return next();\n        },\n        function foo(req, res, next) {\n            res.end();\n            return next();\n        }\n    );\n\n    SERVER.get('/bar/:a/:b', function bar(req, res, next) {\n        res.end();\n        return next();\n    });\n\n    SERVER.get('/example/:file(^\\\\d+).png', function freeform(req, res, next) {\n        res.end();\n        return next();\n    });\n\n    var debugInfo = SERVER.getDebugInfo();\n\n    t.ok(debugInfo);\n    t.ok(debugInfo.routes);\n\n    debugInfo.routes.forEach(function(route) {\n        t.ok(route);\n        t.equal(typeof route.name, 'string');\n        t.equal(typeof route.method, 'string');\n\n        t.equal(route.handlers instanceof Array, true);\n        route.handlers.forEach(function(handlerFn) {\n            t.equal(typeof handlerFn, 'string');\n        });\n    });\n\n    // // check /foo\n    // TODO: should it contain use handlers?\n    t.equal(debugInfo.routes[0].handlers[0], 'use');\n    t.equal(debugInfo.routes[0].handlers[1], 'use2');\n    t.equal(debugInfo.routes[0].handlers[2], 'anonymous');\n    t.equal(debugInfo.routes[0].handlers[3], 'foo');\n\n    // check /bar\n    t.equal(debugInfo.routes[0].handlers[0], 'use');\n    t.equal(debugInfo.routes[0].handlers[1], 'use2');\n    t.equal(debugInfo.routes[1].handlers[2], 'bar');\n\n    // check use, pre, and after handlers\n    t.ok(debugInfo.server.use);\n    t.equal(debugInfo.server.use[0], 'use');\n    t.equal(debugInfo.server.use[1], 'use2');\n\n    t.ok(debugInfo.server.pre);\n    t.equal(debugInfo.server.pre[0], 'pre');\n    t.equal(debugInfo.server.pre[1], 'pre2');\n\n    t.ok(debugInfo.server.after);\n    t.equal(debugInfo.server.after[0], 'aft');\n    t.equal(debugInfo.server.after[1], 'aft2');\n\n    // detailed test for compiled regex\n    // verify url parameter regex\n    t.deepEqual(debugInfo.routes[1].name, 'getbarab');\n    t.deepEqual(debugInfo.routes[1].method, 'get');\n\n    // verify freeform regex\n    t.deepEqual(debugInfo.routes[2].name, 'getexamplefiledpng');\n    t.deepEqual(debugInfo.routes[2].method, 'get');\n\n    // verify other server details\n    t.deepEqual(Object.keys(debugInfo.server.formatters), [\n        'application/javascript',\n        'application/json',\n        'text/plain',\n        'application/octet-stream'\n    ]);\n    t.equal(debugInfo.server.address, '127.0.0.1');\n    t.equal(typeof debugInfo.server.port, 'number');\n    t.equal(typeof debugInfo.server.inflightRequests, 'number');\n\n    t.end();\n});\n\ntest(\"should emit 'pre' event on a 200\", function(t) {\n    SERVER.get('/foo/:id', function echoId(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), false);\n        res.send();\n        next();\n    });\n\n    SERVER.once('pre', function(req, res) {\n        t.ok(req);\n        t.ok(res);\n    });\n\n    CLIENT.get('/foo/bar', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest(\"should emit 'pre' event on 404\", function(t) {\n    SERVER.get('/foo/:id', function echoId(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), false);\n        res.send();\n        next();\n    });\n\n    SERVER.once('pre', function(req, res) {\n        t.ok(req);\n        t.ok(res);\n    });\n\n    CLIENT.get('/badroute', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 404);\n        t.end();\n    });\n});\n\ntest(\"should emit 'routed' event on a 200\", function(t) {\n    SERVER.get('/foo/:id', function echoId(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), false);\n        res.send();\n        next();\n    });\n\n    SERVER.once('routed', function(req, res, route) {\n        t.ok(req);\n        t.ok(res);\n        t.ok(route);\n    });\n\n    CLIENT.get('/foo/bar', function(err, _, res) {\n        t.ifError(err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest(\"should not emit 'routed' event on 404\", function(t) {\n    SERVER.get('/foo/:id', function echoId(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), false);\n        res.send();\n        next();\n    });\n\n    SERVER.once('routed', function(req, res, route) {\n        t.fail();\n    });\n\n    CLIENT.get('/badroute', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 404);\n        t.end();\n    });\n});\n\ntest('should emit restifyError even for router errors', function(t) {\n    var notFoundFired = false;\n    var restifyErrFired = false;\n\n    SERVER.once('NotFound', function(req, res, err, cb) {\n        notFoundFired = true;\n        t.ok(err);\n        t.equal(err instanceof Error, true);\n        t.equal(err.name, 'ResourceNotFoundError');\n        return cb();\n    });\n\n    SERVER.once('restifyError', function(req, res, err, cb) {\n        restifyErrFired = true;\n        t.ok(err);\n        t.equal(err instanceof Error, true);\n        t.equal(err.name, 'ResourceNotFoundError');\n        return cb();\n    });\n\n    /*eslint-disable no-shadow*/\n    CLIENT.get('/dne', function(err, req, res, data) {\n        t.ok(err);\n        t.equal(err.name, 'ResourceNotFoundError');\n        t.equal(notFoundFired, true);\n        t.equal(restifyErrFired, true);\n        t.done();\n    });\n});\n\ntest('should emit error with multiple next calls with strictNext', function(t) {\n    var server = restify.createServer({\n        dtrace: helper.dtrace,\n        strictNext: true,\n        handleUncaughtExceptions: true,\n        log: helper.getLog('server')\n    });\n    var client;\n    var port;\n\n    server.listen(PORT + 1, '127.0.0.1', function() {\n        port = server.address().port;\n        client = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + port,\n            dtrace: helper.dtrace,\n            retry: false\n        });\n\n        server.get('/strict-next', function(req, res, next) {\n            next();\n            next();\n        });\n\n        server.on('uncaughtException', function(req, res, route, err) {\n            t.ok(err);\n            t.equal(err.message, \"next shouldn't be called more than once\");\n            res.send(err);\n        });\n\n        client.get('/strict-next', function(err, _, res) {\n            t.ok(err);\n            t.equal(res.statusCode, 500);\n\n            client.close();\n            server.close(function() {\n                t.end();\n            });\n        });\n    });\n});\n\ntest(\n    'should send 500 if we reached the end of handler chain w/o sending ' +\n        'headers',\n    function(t) {\n        var server = restify.createServer({\n            dtrace: helper.dtrace,\n            strictNext: true,\n            log: helper.getLog('server')\n        });\n        var client;\n        var port;\n\n        server.listen(PORT + 1, '127.0.0.1', function() {\n            port = server.address().port;\n            client = restifyClients.createJsonClient({\n                url: 'http://127.0.0.1:' + port,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            server.get('/noResponse', function(req, res, next) {\n                next();\n            });\n\n            client.get('/noResponse', function(err, _, res) {\n                t.ok(err);\n                t.equal(res.statusCode, 500);\n                t.equal(err.name, 'InternalServerError');\n                t.equal(\n                    err.message,\n                    'reached the end of the handler chain without ' +\n                        'writing a response!'\n                );\n                client.close();\n                server.close(function() {\n                    t.end();\n                });\n            });\n        });\n    }\n);\n\ntest('uncaughtException should not trigger named routeHandler', function(t) {\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo'\n        },\n        function(req, res, next) {\n            throw 'bar'; //eslint-disable-line no-throw-literal\n        }\n    );\n\n    SERVER.get(\n        {\n            name: 'bar',\n            path: '/bar'\n        },\n        function(req, res, next) {\n            // This code should not run, but we can test against the status code\n            res.send(200);\n            next();\n        }\n    );\n\n    CLIENT.get('/foo', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('uncaughtException should handle thrown null', function(t) {\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo'\n        },\n        function(req, res, next) {\n            throw null; //eslint-disable-line no-throw-literal\n        }\n    );\n\n    SERVER.get(\n        {\n            name: 'bar',\n            path: '/bar'\n        },\n        function(req, res, next) {\n            // This code should not run, but we can test against the status code\n            res.send(200);\n            next();\n        }\n    );\n\n    CLIENT.get('/foo', function(err, _, res, data) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.equal(data.message, 'null');\n        t.end();\n    });\n});\n\ntest('uncaughtException should handle thrown undefined literal', function(t) {\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo'\n        },\n        function(req, res, next) {\n            throw undefined; //eslint-disable-line no-throw-literal\n        }\n    );\n\n    SERVER.get(\n        {\n            name: 'bar',\n            path: '/bar'\n        },\n        function(req, res, next) {\n            // This code should not run, but we can test against the status code\n            res.send(200);\n            next();\n        }\n    );\n\n    CLIENT.get('/foo', function(err, _, res, data) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.equal(data.message, 'undefined');\n        t.end();\n    });\n});\n\ntest('uncaughtException should handle thrown falsy number', function(t) {\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo'\n        },\n        function(req, res, next) {\n            throw 0; //eslint-disable-line no-throw-literal\n        }\n    );\n\n    SERVER.get(\n        {\n            name: 'bar',\n            path: '/bar'\n        },\n        function(req, res, next) {\n            // This code should not run, but we can test against the status code\n            res.send(200);\n            next();\n        }\n    );\n\n    CLIENT.get('/foo', function(err, _, res, data) {\n        t.ok(err);\n        t.equal(data.message, '0');\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('uncaughtException should handle thrown non falsy number', function(t) {\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo'\n        },\n        function(req, res, next) {\n            throw 1; //eslint-disable-line no-throw-literal\n        }\n    );\n\n    SERVER.get(\n        {\n            name: 'bar',\n            path: '/bar'\n        },\n        function(req, res, next) {\n            // This code should not run, but we can test against the status code\n            res.send(200);\n            next();\n        }\n    );\n\n    CLIENT.get('/foo', function(err, _, res, data) {\n        t.ok(err);\n        t.equal(data.message, '1');\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('uncaughtException should handle thrown boolean', function(t) {\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo'\n        },\n        function(req, res, next) {\n            throw true; //eslint-disable-line no-throw-literal\n        }\n    );\n\n    SERVER.get(\n        {\n            name: 'bar',\n            path: '/bar'\n        },\n        function(req, res, next) {\n            // This code should not run, but we can test against the status code\n            res.send(200);\n            next();\n        }\n    );\n\n    CLIENT.get('/foo', function(err, _, res, data) {\n        t.ok(err);\n        t.equal(data.message, 'true');\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('uncaughtException should handle thrown falsy boolean', function(t) {\n    SERVER.get(\n        {\n            name: 'foo',\n            path: '/foo'\n        },\n        function(req, res, next) {\n            throw false; //eslint-disable-line no-throw-literal\n        }\n    );\n\n    SERVER.get(\n        {\n            name: 'bar',\n            path: '/bar'\n        },\n        function(req, res, next) {\n            // This code should not run, but we can test against the status code\n            res.send(200);\n            next();\n        }\n    );\n\n    CLIENT.get('/foo', function(err, _, res, data) {\n        t.ok(err);\n        t.equal(data.message, 'false');\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('should have proxy event handlers as instance', function(t) {\n    var server = restify.createServer({\n        handleUpgrades: false\n    });\n    t.equal(server.proxyEvents.length, 6);\n\n    server = restify.createServer({\n        handleUpgrades: true\n    });\n\n    t.equal(server.proxyEvents.length, 5);\n    server.close(function() {\n        t.end();\n    });\n});\n\ntest('first chain should get to reject requests', function(t) {\n    SERVER.get('/foobar', function(req, res, next) {\n        t.fail('should not call handler');\n    });\n\n    SERVER.first(function(req, res) {\n        res.statusCode = 413; // I'm a teapot!\n        res.end();\n        return false;\n    });\n\n    CLIENT.get('/foobar', function(_, __, res) {\n        t.equal(res.statusCode, 413);\n        t.end();\n    });\n});\n\ntest('first chain should get to allow requests', function(t) {\n    SERVER.get('/foobar', function(req, res, next) {\n        res.send(413, 'Im a teapot');\n        return next();\n    });\n\n    SERVER.first(function(req, res) {\n        return true;\n    });\n\n    CLIENT.get('/foobar', function(_, __, res) {\n        t.equal(res.statusCode, 413);\n        t.end();\n    });\n});\n\ntest('first chain should allow multiple handlers', function(t) {\n    SERVER.get('/foobar', function(req, res, next) {\n        res.send(413, 'Im a teapot');\n        return next();\n    });\n\n    var count = 0;\n    var handler = function() {\n        count++;\n    };\n\n    SERVER.first(handler, handler, handler);\n    SERVER.first(handler, handler, handler);\n\n    CLIENT.get('/foobar', function(_, __, res) {\n        t.equal(res.statusCode, 413);\n        t.equal(count, 6, 'invoked 6 handlers');\n        t.end();\n    });\n});\n\ntest('first chain should allow any handler to reject', function(t) {\n    SERVER.get('/foobar', function(req, res, next) {\n        res.send(200, 'Handled');\n        return next();\n    });\n\n    var count = 0;\n    var handler = function() {\n        count++;\n    };\n\n    var handlerAbort = function(req, res) {\n        count++;\n        res.statusCode = 413;\n        res.end();\n        return false;\n    };\n\n    SERVER.first(handler, handler, handler);\n    // Should append these handlers and abort the chain on the second\n    SERVER.first(handler, handlerAbort, handler);\n    // These should never run\n    SERVER.first(handler, handlerAbort);\n\n    CLIENT.get('/foobar', function(_, __, res) {\n        t.equal(res.statusCode, 413);\n        t.equal(count, 5, 'invoked 5 handlers');\n        t.end();\n    });\n});\n\ntest('inflightRequest accounting stable with firstChain', function(t) {\n    // Make 3 requests, shed the second, and ensure inflightRequest accounting\n    // for all the requests\n    var request = 0;\n    SERVER.first(function(req, res) {\n        request++;\n\n        if (request === 1) {\n            t.equal(SERVER._inflightRequests, 1);\n            return true;\n        }\n        if (request === 2) {\n            t.equal(SERVER._inflightRequests, 2);\n            res.statusCode = 413;\n            res.end();\n            return false;\n        }\n        if (request === 3) {\n            // Since the second request was shed, and inflightRequest accounting\n            // should be happening synchronously, this should still be 2 for\n            // the third request\n            t.equal(SERVER._inflightRequests, 2);\n            return true;\n        }\n\n        t.fail('Too many requests for test');\n        return false;\n    });\n    var nexts = [];\n    SERVER.get('/foobar', function(req, res, next) {\n        res.send(200, 'success');\n        nexts.push(next);\n        if (nexts.length === 2) {\n            nexts.forEach(function(finishRequest) {\n                finishRequest();\n            });\n        }\n    });\n\n    var results = [];\n    function getDone(_, __, res) {\n        results.push(res);\n        if (results.length < 3) {\n            return;\n        }\n        for (var i = 0; i < results.length; i++) {\n            // The shed request should always be returned first, since it isn't\n            // handled by SERVER.get\n            if (i === 1) {\n                t.equal(\n                    results[i].statusCode,\n                    413,\n                    'results[' + i + '] === 413'\n                );\n            } else {\n                t.equal(\n                    results[i].statusCode,\n                    200,\n                    'results[' + i + '] === 200'\n                );\n            }\n        }\n        t.end();\n    }\n\n    // kick off all 3 at the same time to see if we can trigger a race condition\n    CLIENT.get('/foobar', getDone);\n    CLIENT.get('/foobar', getDone);\n    CLIENT.get('/foobar', getDone);\n});\n\ntest('async prerouting chain with error', function(t) {\n    SERVER.pre(async function(req, res) {\n        await helper.sleep(10);\n        throw new RestError({ statusCode: 400, restCode: 'BadRequest' }, 'bum');\n    });\n\n    SERVER.get('/hello/:name', function tester(req, res, next) {\n        res.send(req.params.name);\n        next();\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 400);\n        t.end();\n    });\n});\n\ntest('async prerouting chain with empty rejection', function(t) {\n    SERVER.pre(async function(req, res) {\n        await helper.sleep(10);\n        return Promise.reject();\n    });\n\n    SERVER.get('/hello/:name', function tester(req, res, next) {\n        res.send(req.params.name);\n        next();\n    });\n\n    SERVER.on('Async', function(req, res, err, callback) {\n        t.equal(err.jse_info.cause, undefined);\n        t.equal(err.jse_info.method, 'GET');\n        t.equal(err.jse_info.path, '/hello/mark');\n        callback();\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 500);\n        t.end();\n    });\n});\n\ntest('async use chain with error', function(t) {\n    SERVER.use(async function(req, res) {\n        await helper.sleep(10);\n        throw new RestError({ statusCode: 400, restCode: 'BadRequest' }, 'bum');\n    });\n\n    SERVER.get('/hello/:name', function tester(req, res, next) {\n        res.send(req.params.name);\n        next();\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 400);\n        t.end();\n    });\n});\n\ntest('async handler with error', function(t) {\n    SERVER.get('/hello/:name', async function tester(req, res) {\n        await helper.sleep(10);\n        throw new RestError({ statusCode: 400, restCode: 'BadRequest' }, 'bum');\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(err);\n        t.equal(res.statusCode, 400);\n        t.end();\n    });\n});\n\ntest('async handler with error after send succeeds', function(t) {\n    SERVER.get('/hello/:name', async function tester(req, res) {\n        await helper.sleep(10);\n        res.send(req.params.name);\n        throw new RestError({ statusCode: 400, restCode: 'BadRequest' }, 'bum');\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(!err);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n});\n\ntest('async handler with error after send succeeds', function(t) {\n    SERVER.get('/hello/:name', async function tester(req, res) {\n        res.send(req.params.name);\n        await helper.sleep(20);\n        throw new RestError({ statusCode: 400, restCode: 'BadRequest' }, 'bum');\n    });\n\n    SERVER.on('after', function(req, res, route, error) {\n        t.ok(error);\n        t.end();\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(!err);\n        t.equal(res.statusCode, 200);\n    });\n});\n\ntest('async handler without next', function(t) {\n    SERVER.get('/hello/:name', async function tester(req, res) {\n        await helper.sleep(10);\n        res.send(req.params.name);\n    });\n\n    SERVER.on('after', function(req, res, route, error) {\n        t.ok(!error);\n        t.equal(res.statusCode, 200);\n        t.end();\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(!err);\n        t.equal(res.statusCode, 200);\n    });\n});\n\ntest('async handler should discard value', function(t) {\n    SERVER.get('/hello/:name', async function tester(req, res) {\n        await helper.sleep(10);\n        res.send(req.params.name);\n        return 'foo';\n    });\n\n    CLIENT.get('/hello/mark', function(err, _, res) {\n        t.ok(!err);\n        t.equal(res.statusCode, 200);\n        t.equal(res.body, '\"mark\"');\n        t.end();\n    });\n});\n\ntest('Server returns 400 on invalid method', function(t) {\n    SERVER.get('/snickers/bar', function echoId(req, res, next) {\n        res.send();\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/snickers/bar',\n        method: 'CANDYBARS',\n        agent: false\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 400);\n        t.equal(res.statusMessage, 'Bad Request');\n        res.on('data', function() {\n            t.fail('Data was sent on 400 error');\n        });\n        res.on('end', function() {\n            t.end();\n        });\n    }).end();\n});\n\ntest('Server returns 4xx when header size is too large', function(t) {\n    SERVER.get('/jellybeans', function echoId(req, res, next) {\n        res.send();\n        next();\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/jellybeans',\n        method: 'GET',\n        agent: false,\n        headers: {\n            'jellybean-colors': 'purple,green,red,black,pink,'.repeat(1000)\n        }\n    };\n    http.request(opts, function(res) {\n        if (NODE_MAJOR_VERSION > '10') {\n            t.equal(res.statusCode, 431);\n            t.equal(res.statusMessage, 'Request Header Fields Too Large');\n        } else {\n            t.equal(res.statusCode, 400);\n            t.equal(res.statusMessage, 'Bad Request');\n        }\n        res.on('data', function() {\n            t.fail('Data was sent on 431 error');\n        });\n        res.on('end', function() {\n            t.end();\n        });\n    }).end();\n});\n\ntest('Server supports adding custom clientError listener', function(t) {\n    SERVER.get('/popcorn', function echoId(req, res, next) {\n        res.send();\n        next();\n    });\n\n    SERVER.on('clientError', function(err, socket) {\n        if (err.code !== 'HPE_HEADER_OVERFLOW') {\n            t.fail('Expected HPE_HEADER_OVERFLOW but err.code was ' + err.code);\n        }\n        socket.write(\"HTTP/1.1 418 I'm a teapot\\r\\nConnection: close\\r\\n\\r\\n\");\n        socket.destroy(err);\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/popcorn',\n        method: 'GET',\n        agent: false,\n        headers: {\n            'jellybean-colors': 'purple,green,red,black,pink,'.repeat(1000)\n        }\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 418);\n        t.equal(res.statusMessage, \"I'm a teapot\");\n        res.on('data', function() {});\n        res.on('end', function() {\n            t.end();\n        });\n    }).end();\n});\n\ntest('Server correctly handles multiple clientError listeners', function(t) {\n    SERVER.get('/popcorn', function echoId(req, res, next) {\n        res.send();\n        next();\n    });\n\n    let numListenerCalls = 0;\n    SERVER.on('clientError', function(err, socket) {\n        socket.write(\"HTTP/1.1 418 I'm a teapot\\r\\nConnection: close\\r\\n\\r\\n\");\n        numListenerCalls += 1;\n    });\n    SERVER.on('clientError', function(err, socket) {\n        if (numListenerCalls !== 1) {\n            t.fail('listener was called ' + numListenerCalls + ' times');\n        }\n        socket.destroy(err);\n    });\n\n    var opts = {\n        hostname: '127.0.0.1',\n        port: PORT,\n        path: '/popcorn',\n        method: 'GET',\n        agent: false,\n        headers: {\n            'jellybean-colors': 'purple,green,red,black,pink,'.repeat(1000)\n        }\n    };\n    http.request(opts, function(res) {\n        t.equal(res.statusCode, 418);\n        t.equal(res.statusMessage, \"I'm a teapot\");\n        res.on('data', function() {});\n        res.on('end', function() {\n            t.end();\n        });\n    }).end();\n});\n\ntest('req and res should use server logger by default', function(t) {\n    SERVER.get('/ping', function echoId(req, res, next) {\n        t.ok(req.log);\n        t.strictEqual(req.log, SERVER.log);\n        req.log.info('foo');\n        t.equal(LOG_BUFFER.records[LOG_BUFFER.length - 1].msg, 'foo');\n        res.log.info('bar');\n        t.equal(LOG_BUFFER.records[LOG_BUFFER.length - 1].msg, 'bar');\n        res.send();\n        next();\n    });\n\n    CLIENT.get('/ping', function() {\n        t.end();\n    });\n});\n\ntest('req and res should use own logger by if set during .first', function(t) {\n    const buffer = new StreamRecorder();\n    SERVER.first(function first(req, res) {\n        req.log = helper.getLog('server', buffer, 'info');\n    });\n\n    SERVER.get('/ping', function echoId(req, res, next) {\n        LOG_BUFFER.flushRecords();\n        t.ok(req.log);\n        t.notStrictEqual(req.log, SERVER.log);\n        req.log.info('foo');\n        t.equal(buffer.records[buffer.length - 1].msg, 'foo');\n        res.log.info('bar');\n        t.equal(buffer.records[buffer.length - 1].msg, 'bar');\n        t.equal(LOG_BUFFER.records.length, 0);\n        res.send();\n        next();\n    });\n\n    CLIENT.get('/ping', function() {\n        t.end();\n    });\n});\n\ntest('should throw if handleUncaughtExceptions is invalid', function(t) {\n    t.throws(() => {\n        restify.createServer({\n            handleUncaughtExceptions: 'this is invalid'\n        });\n    });\n    t.end();\n});\n\ntest('should use custom function for error handling', function(t) {\n    const asl = new AsyncLocalStorage();\n    let callOnError;\n    var server = restify.createServer({\n        dtrace: helper.dtrace,\n        strictNext: true,\n        handleUncaughtExceptions: (req, res, onError, next) => {\n            callOnError = err => {\n                const newErr = new Error('new error');\n                newErr.orig = err;\n                onError(newErr);\n            };\n            asl.run({}, next, req, res);\n        },\n        log: helper.getLog('server')\n    });\n    var client;\n    var port;\n\n    server.listen(PORT + 1, '127.0.0.1', function() {\n        port = server.address().port;\n        client = restifyClients.createJsonClient({\n            url: 'http://127.0.0.1:' + port,\n            dtrace: helper.dtrace,\n            retry: false\n        });\n\n        const expectedErr = new Error('foo');\n        server.get('/throw', function(req, res, next) {\n            // We don't really need to throw to test, and catching the throw is\n            // hard because nodeunit messes with uncaughtException event\n            callOnError(expectedErr);\n        });\n\n        server.on('uncaughtException', function(req, res, route, err) {\n            t.ok(err);\n            t.strictEqual(err.orig, expectedErr);\n            t.equal(err.message, 'new error');\n            res.send(err);\n        });\n\n        client.get('/throw', function(err, _, res) {\n            t.ok(err);\n            t.equal(res.statusCode, 500);\n\n            client.close();\n            server.close(function() {\n                t.end();\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/serverHttp2.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar path = require('path');\nvar fs = require('fs');\nvar http2;\n\n// http2 module is not available < v8.4.0 (only with flag <= 8.8.0)\ntry {\n    http2 = require('http2');\n} catch (err) {\n    console.log('HTTP2 module is not available');\n    console.log(\n        'Node.js version >= v8.8.8 required, current: ' + process.versions.node\n    );\n    return;\n}\n\nvar restify = require('../lib');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar after = helper.after;\nvar before = helper.before;\nvar test = helper.test;\n\nvar CERT = fs.readFileSync(path.join(__dirname, './keys/http2-cert.pem'));\nvar KEY = fs.readFileSync(path.join(__dirname, './keys/http2-key.pem'));\nvar CA = fs.readFileSync(path.join(__dirname, 'keys/http2-csr.pem'));\n\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar CLIENT;\nvar SERVER;\n\n///--- Tests\n\nbefore(function(cb) {\n    try {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            handleUncaughtExceptions: true,\n            http2: {\n                cert: CERT,\n                key: KEY,\n                ca: CA\n            },\n            log: helper.getLog('server')\n        });\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = http2.connect('https://127.0.0.1:' + PORT, {\n                rejectUnauthorized: false\n            });\n\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\nafter(function(cb) {\n    try {\n        CLIENT.destroy();\n        SERVER.close(function() {\n            CLIENT = null;\n            SERVER = null;\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\ntest('get (path only)', function(t) {\n    SERVER.get('/foo/:id', function echoId(req, res, next) {\n        t.ok(req.params);\n        t.equal(req.params.id, 'bar');\n        t.equal(req.isUpload(), false);\n        res.json({ hello: 'world' });\n        next();\n    });\n\n    var req = CLIENT.request({\n        ':path': '/foo/bar',\n        ':method': 'GET'\n    });\n\n    req.on('response', function(headers, flags) {\n        var data = '';\n        t.equal(headers[':status'], 200);\n\n        req.on('data', function(chunk) {\n            data += chunk;\n        });\n        req.on('end', function() {\n            t.deepEqual(JSON.parse(data), { hello: 'world' });\n            t.end();\n        });\n    });\n    req.on('error', function(err) {\n        t.ifError(err);\n    });\n});\n"
  },
  {
    "path": "test/upgrade.test.js",
    "content": "// Copyright (c) 2013, Joyent, Inc. All rights reserved.\n// vim: set ts=8 sts=8 sw=8 et:\n\n'use strict';\n/* eslint-disable func-names */\n\nvar restifyClients = require('restify-clients');\nvar Watershed = require('watershed').Watershed;\nvar restify = require('../lib');\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar after = helper.after;\nvar before = helper.before;\nvar test = helper.test;\n\nvar PORT = process.env.UNIT_TEST_PORT || 0;\nvar CLIENT;\nvar SERVER;\nvar WATERSHED = new Watershed();\nvar SHEDLIST = [];\n\nvar TIMEOUT = 15000;\n\n///--- Test Helper\n\nfunction finish_latch(_test, _names) {\n    var complete = false;\n    var t = _test;\n    var names = _names;\n    var iv = setTimeout(function() {\n        if (complete) {\n            return;\n        }\n\n        complete = true;\n        t.ok(false, 'timeout after ' + TIMEOUT + 'ms');\n        t.ok(false, 'remaining latches: ' + Object.keys(names).join(', '));\n        t.done();\n    }, TIMEOUT);\n    return function(name, err) {\n        if (complete) {\n            return;\n        }\n\n        if (names[name] === undefined) {\n            complete = true;\n            t.ok(false, 'latch name \"' + name + '\" not expected');\n            t.ok(false, 'remaining latches: ' + Object.keys(names).join(', '));\n            t.done();\n            return;\n        }\n\n        if (--names[name] === 0) {\n            delete names[name];\n        }\n\n        /*\n         * Check that all latch names are done, and if so,\n         * end the test:\n         */\n        if (Object.keys(names).length === 0) {\n            complete = true;\n            clearTimeout(iv);\n            iv = null;\n            t.done();\n        }\n    };\n}\n\n///--- Tests\n\nbefore(function(cb) {\n    try {\n        SERVER = restify.createServer({\n            dtrace: helper.dtrace,\n            log: helper.getLog('server'),\n            version: ['2.0.0', '0.5.4', '1.4.3'],\n            handleUpgrades: true\n        });\n        SERVER.listen(PORT, '127.0.0.1', function() {\n            PORT = SERVER.address().port;\n            CLIENT = restifyClients.createHttpClient({\n                url: 'http://127.0.0.1:' + PORT,\n                dtrace: helper.dtrace,\n                retry: false\n            });\n\n            cb();\n        });\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\nafter(function(cb) {\n    try {\n        CLIENT.close();\n        SERVER.close(function() {\n            CLIENT = null;\n            SERVER = null;\n            cb();\n        });\n\n        while (SHEDLIST.length > 0) {\n            SHEDLIST.pop().destroy();\n        }\n    } catch (e) {\n        console.error(e.stack);\n        process.exit(1);\n    }\n});\n\ntest('GET without upgrade headers', function(t) {\n    var done = finish_latch(t, {\n        'client response': 1,\n        'server response': 1\n    });\n\n    SERVER.get('/attach', function(req, res, next) {\n        t.ok(!res.claimUpgrade, 'res.claimUpgrade not present');\n        res.send(400);\n        next();\n        done('server response');\n    });\n\n    var options = {\n        headers: {\n            uprgade: 'ebfrockets' // this is intentional misspelling of upgrade\n        },\n        path: '/attach'\n    };\n    CLIENT.get(options, function(err, req) {\n        t.ifError(err);\n        req.on('error', function(err2) {\n            t.ifError(err2);\n            done('client error');\n        });\n        req.on('result', function(err2, res) {\n            if (err2 && err2.name !== 'BadRequestError') {\n                t.ifError(err2);\n            }\n            t.equal(res.statusCode, 400);\n            res.on('end', function() {\n                done('client response');\n            });\n            res.resume();\n        });\n        req.on('upgradeResult', function(err2, res) {\n            done('server upgraded unexpectedly');\n        });\n    });\n});\n\ntest('Dueling upgrade and response handling 1', function(t) {\n    var done = finish_latch(t, {\n        'expected requestUpgrade error': 1,\n        'client response': 1\n    });\n\n    SERVER.get('/attach', function(req, res, next) {\n        try {\n            res.send(400);\n        } catch (ex) {\n            t.ifError(ex);\n            done('unxpected res.send error');\n            return;\n        }\n\n        try {\n            res.claimUpgrade();\n        } catch (ex) {\n            done('expected requestUpgrade error');\n        }\n        next(false);\n    });\n\n    var wskey = WATERSHED.generateKey();\n    var options = {\n        headers: {\n            connection: 'upgrade',\n            upgrade: 'websocket',\n            'sec-websocket-key': wskey\n        },\n        path: '/attach'\n    };\n    CLIENT.get(options, function(err, req) {\n        t.ifError(err);\n        req.on('error', function(err2) {\n            t.ifError(err2);\n            done('client error');\n        });\n        req.on('result', function(err2, res) {\n            if (err2 && err2.name !== 'BadRequestError') {\n                t.ifError(err2);\n            }\n            t.equal(res.statusCode, 400);\n            res.on('end', function() {\n                done('client response');\n            });\n            // noop data listener required for resume to take effect\n            res.on('data', function() {});\n            res.resume();\n        });\n        req.on('upgradeResult', function(err2, res) {\n            done('server upgraded unexpectedly');\n        });\n    });\n});\n\ntest('Dueling upgrade and response handling 2', function(t) {\n    var done = finish_latch(t, {\n        'expected res.send error': 1,\n        'expected server to reset': 1\n    });\n\n    SERVER.get('/attach', function(req, res, next) {\n        try {\n            var upg = res.claimUpgrade();\n            upg.socket.destroy();\n        } catch (ex) {\n            t.ifError(ex);\n            done('unexpected requestUpgrade error');\n        }\n\n        try {\n            res.send(400);\n        } catch (ex) {\n            if (ex.name !== 'InvalidUpgradeStateError') {\n                t.ifError(ex);\n            }\n            done('expected res.send error');\n            return;\n        }\n\n        next(false);\n    });\n\n    var wskey = WATERSHED.generateKey();\n    var options = {\n        headers: {\n            connection: 'upgrade',\n            upgrade: 'websocket',\n            'sec-websocket-key': wskey\n        },\n        path: '/attach'\n    };\n    CLIENT.get(options, function(err, req) {\n        t.ifError(err);\n        done('expected server to reset');\n        return;\n    });\n});\n\ntest('GET with upgrade headers', function(t) {\n    var done = finish_latch(t, {\n        'client shed end': 1,\n        'server shed end': 1\n    });\n\n    SERVER.get('/attach', function(req, res, next) {\n        t.ok(res.claimUpgrade, 'res.claimUpgrade present');\n        t.doesNotThrow(function() {\n            var upgrade = res.claimUpgrade();\n            var shed = WATERSHED.accept(req, upgrade.socket, upgrade.head);\n            SHEDLIST.push(shed);\n            shed.end(\"ok we're done here\");\n            shed.on('error', function(err) {\n                t.ifError(err);\n                done('server shed error');\n            });\n            shed.on('end', function() {\n                done('server shed end');\n            });\n            next(false);\n        });\n    });\n\n    var wskey = WATERSHED.generateKey();\n    var options = {\n        headers: {\n            connection: 'upgrade',\n            upgrade: 'websocket',\n            'sec-websocket-key': wskey\n        },\n        path: '/attach'\n    };\n    CLIENT.get(options, function(err, req) {\n        t.ifError(err);\n        req.on('result', function(err2, res) {\n            t.ifError(err2);\n            t.ok(false, 'server did not upgrade');\n            done(true);\n        });\n        req.on('upgradeResult', function(err2, res, socket, head) {\n            t.ifError(err2);\n            t.ok(true, 'server upgraded');\n            t.equal(res.statusCode, 101);\n            t.equal(typeof socket, 'object');\n            t.ok(Buffer.isBuffer(head), 'head is Buffer');\n            t.doesNotThrow(function() {\n                var shed = WATERSHED.connect(res, socket, head, wskey);\n                SHEDLIST.push(shed);\n                shed.end('ok, done');\n                shed.on('error', function(err3) {\n                    t.ifError(err3);\n                    done('client shed error');\n                });\n                shed.on('end', function() {\n                    done('client shed end');\n                });\n            });\n        });\n    });\n});\n\ntest('GET with some websocket traffic', function(t) {\n    var done = finish_latch(t, {\n        'client shed end': 1,\n        'server shed end': 1,\n        'server receive message': 5,\n        'client receive message': 3\n    });\n\n    SERVER.get('/attach', function(req, res, next) {\n        t.ok(res.claimUpgrade, 'res.claimUpgrade present');\n        t.doesNotThrow(function() {\n            var upgrade = res.claimUpgrade();\n            var shed = WATERSHED.accept(req, upgrade.socket, upgrade.head);\n            SHEDLIST.push(shed);\n            shed.on('error', function(err) {\n                t.ifError(err);\n                done('server shed error');\n            });\n            shed.on('text', function(msg) {\n                if (msg === 'to server') {\n                    done('server receive message');\n                }\n            });\n            shed.on('end', function() {\n                done('server shed end');\n            });\n            shed.send('to client');\n            shed.send('to client');\n            shed.send('to client');\n            next(false);\n        });\n    });\n\n    var wskey = WATERSHED.generateKey();\n    var options = {\n        headers: {\n            connection: 'upgrade',\n            upgrade: 'websocket',\n            'sec-websocket-key': wskey\n        },\n        path: '/attach'\n    };\n    CLIENT.get(options, function(err, req) {\n        t.ifError(err);\n        req.on('result', function(err2, res) {\n            t.ifError(err2);\n            t.ok(false, 'server did not upgrade');\n            done(true);\n        });\n        req.on('upgradeResult', function(err2, res, socket, head) {\n            t.ifError(err2);\n            t.ok(true, 'server upgraded');\n            t.equal(res.statusCode, 101);\n            t.equal(typeof socket, 'object');\n            t.ok(Buffer.isBuffer(head), 'head is Buffer');\n            t.doesNotThrow(function() {\n                var shed = WATERSHED.connect(res, socket, head, wskey);\n                SHEDLIST.push(shed);\n                shed.on('error', function(err3) {\n                    t.ifError(err3);\n                    done('client shed error');\n                });\n                shed.on('end', function() {\n                    done('client shed end');\n                });\n                shed.on('text', function(msg) {\n                    if (msg === 'to client') {\n                        done('client receive message');\n                    }\n                });\n                var count = 5;\n                var iv = setInterval(function() {\n                    if (--count < 0) {\n                        clearInterval(iv);\n                        shed.end();\n                    } else {\n                        shed.send('to server');\n                    }\n                }, 100);\n            });\n        });\n    });\n});\n"
  },
  {
    "path": "test/utils.test.js",
    "content": "'use strict';\n/* eslint-disable func-names */\n\nvar mergeQs = require('../lib/utils').mergeQs;\n\nif (require.cache[__dirname + '/lib/helper.js']) {\n    delete require.cache[__dirname + '/lib/helper.js'];\n}\nvar helper = require('./lib/helper.js');\n\n///--- Globals\n\nvar test = helper.test;\n\ntest('merge qs', function(t) {\n    var qs1 = mergeQs(undefined, { a: 1 });\n    t.deepEqual(qs1, { a: 1 });\n\n    var qs2 = mergeQs({ a: 1 }, null);\n    t.deepEqual(qs2, { a: 1 });\n\n    var qs3 = mergeQs({ a: 1 }, { a: 2 });\n    t.deepEqual(qs3, { a: [1, 2] });\n\n    var qs4 = mergeQs({ a: 1 }, { b: 2 });\n    t.deepEqual(qs4, { a: 1, b: 2 });\n\n    var qs5 = mergeQs(null, null);\n    t.deepEqual(qs5, {});\n\n    t.done();\n});\n"
  },
  {
    "path": "tools/docsBuild.js",
    "content": "'use strict';\n\nvar glob = require('glob');\nvar path = require('path');\nvar util = require('util');\nvar fs = require('fs');\nvar documentation = require('documentation');\n\nvar DOCS_PATH = '../docs';\nvar OUTPUT_PATH = '../docs/_api';\nvar LIB_PATH = '../lib';\nvar JEKYLL_HEADER_TEMPLATE = '---\\ntitle: %s\\npermalink: %s\\n---\\n\\n%s';\n\nvar docsConfig = [\n    {\n        title: 'Server API',\n        permalink: '/docs/server-api/',\n        output: path.join(__dirname, OUTPUT_PATH, 'server.md'),\n        files: [\n            path.join(__dirname, LIB_PATH, 'index.js'),\n            path.join(__dirname, LIB_PATH, 'server.js')\n        ],\n        config: path.join(__dirname, DOCS_PATH, 'config/server.yaml')\n    },\n    {\n        title: 'Request API',\n        permalink: '/docs/request-api/',\n        output: path.join(__dirname, OUTPUT_PATH, 'request.md'),\n        files: [path.join(__dirname, LIB_PATH, 'request.js')],\n        config: path.join(__dirname, DOCS_PATH, 'config/request.yaml')\n    },\n    {\n        title: 'Response API',\n        permalink: '/docs/response-api/',\n        output: path.join(__dirname, OUTPUT_PATH, 'response.md'),\n        files: [path.join(__dirname, LIB_PATH, 'response.js')]\n    },\n    {\n        title: 'Plugins API',\n        permalink: '/docs/plugins-api/',\n        output: path.join(__dirname, OUTPUT_PATH, 'plugins.md'),\n        files: [\n            // Pre plugins\n            path.join(__dirname, LIB_PATH, 'plugins/pre/context.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/pre/dedupeSlashes.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/pre/pause.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/pre/prePath.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/pre/reqIdHeaders.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/pre/strictQueryParams.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/pre/userAgent.js'),\n            // Use plugins\n            path.join(__dirname, LIB_PATH, 'plugins/accept.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/authorization.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/date.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/query.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/jsonp.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/bodyParser.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/requestLogger.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/gzip.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/static.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/staticFiles.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/throttle.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/requestExpiry.js'),\n            path.join(\n                __dirname,\n                LIB_PATH,\n                'plugins/inflightRequestThrottle.js'\n            ),\n            path.join(__dirname, LIB_PATH, 'plugins/cpuUsageThrottle.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/conditionalHandler.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/conditionalRequest.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/audit.js'),\n            path.join(__dirname, LIB_PATH, 'plugins/metrics.js')\n        ],\n        config: path.join(__dirname, DOCS_PATH, 'config/plugins.yaml')\n    },\n    {\n        title: 'Formatters API',\n        permalink: '/docs/formatters-api/',\n        output: path.join(__dirname, OUTPUT_PATH, 'formatters.md'),\n        files: glob.sync(path.join(__dirname, LIB_PATH, 'formatters', '*')),\n        config: path.join(__dirname, DOCS_PATH, 'config/formatters.yaml')\n    }\n];\n\n/**\n * @function build\n * @param {Object} options - Options\n * @param {Array} options.files - Array of file paths [\"./foo.js\"]\n * @param {String} options.config - Path to \"config.yaml\"\n * @param {String} options.output - Path to output dir\n * @param {String} options.title - Jekyll title\n * @param {String} options.permalink - Jekyll permalink\n * @returns {Promise} - Promise\n */\nfunction build(options) {\n    return documentation\n        .build(options.files, {\n            shallow: true,\n            config: options.config\n        })\n        .then(function docsFormat(docs) {\n            return documentation.formats.md(docs, {\n                markdownToc: true\n            });\n        })\n        .then(function docsWrite(docsContent) {\n            var output = util.format(\n                JEKYLL_HEADER_TEMPLATE,\n                options.title,\n                options.permalink,\n                docsContent\n            );\n\n            fs.writeFileSync(options.output, output);\n        });\n}\n\n// eslint-disable-next-line\nPromise.all(docsConfig.map(build))\n    .then(function onSucceed(res) {\n        console.log('Docs built');\n        process.exit(0);\n    })\n    .catch(function onError(err) {\n        console.error(err);\n        process.exit(1);\n    });\n"
  },
  {
    "path": "tools/mk/Makefile.defs",
    "content": "# -*- mode: makefile -*-\n#\n# Copyright (c) 2012, Joyent, Inc. All rights reserved.\n#\n# Makefile.defs: common defines.\n#\n# NOTE: This makefile comes from the \"eng\" repo. It's designed to be dropped\n# into other repos as-is without requiring any modifications. If you find\n# yourself changing this file, you should instead update the original copy in\n# eng.git and then update your repo to use the new version.\n#\n# This makefile defines some useful defines. Include it at the top of\n# your Makefile.\n#\n# Definitions in this Makefile:\n#\n#\tTOP \t\tThe absolute path to the project directory. The top dir.\n#\tBRANCH \t\tThe current git branch.\n#\tTIMESTAMP\tThe timestamp for the build. This can be set via\n#\t\t\tthe TIMESTAMP envvar (used by MG-based builds).\n#\tSTAMP\t\tA build stamp to use in built package names.\n#\n\nTOP := $(shell pwd)\n\n#\n# Mountain Gorilla-spec'd versioning.\n# See \"Package Versioning\" in MG's README.md:\n# <https://mo.joyent.com/mountain-gorilla/blob/master/README.md#L139-200>\n#\n# Need GNU awk for multi-char arg to \"-F\".\n_AWK := $(shell (which gawk >/dev/null && echo gawk) \\\n\t|| (which nawk >/dev/null && echo nawk) \\\n\t|| echo awk)\nBRANCH := $(shell git log -n 1 --pretty=%d HEAD | $(_AWK) '{print $$NF}' | sed -e 's/.$$//' | cut -d/ -f2)\nifeq ($(TIMESTAMP),)\n\tTIMESTAMP := $(shell date -u \"+%Y%m%dT%H%M%SZ\")\nendif\n_GITDESCRIBE := g$(shell git describe --all --long --dirty | $(_AWK) -F'-g' '{print $$NF}')\nSTAMP := $(BRANCH)-$(TIMESTAMP)-$(_GITDESCRIBE)\n"
  },
  {
    "path": "tools/mk/Makefile.deps",
    "content": "# -*- mode: makefile -*-\n#\n# Copyright (c) 2012, Joyent, Inc. All rights reserved.\n#\n# Makefile.deps: Makefile for including common tools as dependencies\n#\n# NOTE: This makefile comes from the \"eng\" repo. It's designed to be dropped\n# into other repos as-is without requiring any modifications. If you find\n# yourself changing this file, you should instead update the original copy in\n# eng.git and then update your repo to use the new version.\n#\n# This file is separate from Makefile.targ so that teams can choose\n# independently whether to use the common targets in Makefile.targ and the\n# common tools here.\n#\n\n\n#\n# restdown\n#\nRESTDOWN_EXEC\t?= deps/restdown/bin/restdown\nRESTDOWN\t?= python $(RESTDOWN_EXEC)\n$(RESTDOWN_EXEC): | deps/restdown/.git\n"
  },
  {
    "path": "tools/mk/Makefile.targ",
    "content": "# -*- mode: makefile -*-\n#\n# Copyright (c) 2012, Joyent, Inc. All rights reserved.\n#\n# Makefile.targ: common targets.\n#\n# NOTE: This makefile comes from the \"eng\" repo. It's designed to be dropped\n# into other repos as-is without requiring any modifications. If you find\n# yourself changing this file, you should instead update the original copy in\n# eng.git and then update your repo to use the new version.\n#\n# This Makefile defines several useful targets and rules. You can use it by\n# including it from a Makefile that specifies some of the variables below.\n#\n# Targets defined in this Makefile:\n#\n#\tcheck\tChecks JavaScript files for lint and style\n#\t\tChecks bash scripts for syntax\n#\t\tChecks SMF manifests for validity against the SMF DTD\n#\n#\tclean\tRemoves built files\n#\n#\tdocs\tBuilds restdown documentation in docs/\n#\n#\tprepush\tDepends on \"check\" and \"test\"\n#\n#\ttest\tDoes nothing (you should override this)\n#\n#\txref\tGenerates cscope (source cross-reference index)\n#\n# For details on what these targets are supposed to do, see the Joyent\n# Engineering Guide.\n#\n# To make use of these targets, you'll need to set some of these variables. Any\n# variables left unset will simply not be used.\n#\n#\tBASH_FILES\tBash scripts to check for syntax\n#\t\t\t(paths relative to top-level Makefile)\n#\n#\tCLEAN_FILES\tFiles to remove as part of the \"clean\" target.  Note\n#\t\t\tthat files generated by targets in this Makefile are\n#\t\t\tautomatically included in CLEAN_FILES.  These include\n#\t\t\trestdown-generated HTML and JSON files.\n#\n#\tDOC_FILES\tRestdown (documentation source) files. These are\n#\t\t\tassumed to be contained in \"docs/\", and must NOT\n#\t\t\tcontain the \"docs/\" prefix.\n##\n# You can also override these variables:\n#\n#\tBASH\t\tPath to bash (default: bash)\n#\n#\tCSCOPE_DIRS\tDirectories to search for source files for the cscope\n#\t\t\tindex. (default: \".\")\n#\n\n#\n# Defaults for the various tools we use.\n#\nBASH\t\t?= bash\nBASHSTYLE\t?= tools/bashstyle\nCP\t\t?= cp\nCSCOPE\t\t?= cscope\nCSCOPE_DIRS\t?= .\nMKDIR\t\t?= mkdir -p\nMV\t\t?= mv\nRESTDOWN_FLAGS\t?= -b docs/branding\nRMTREE\t\t?= rm -rf\n\nifeq ($(shell uname -s),SunOS)\n\tTAR\t?= gtar\nelse\n\tTAR\t?= tar\nendif\n\n\n#\n# Defaults for other fixed values.\n#\nBUILD\t\t= build\nDISTCLEAN_FILES += $(BUILD)\nDOC_BUILD\t= $(BUILD)/docs/public\n\n\n#\n# Targets. For descriptions on what these are supposed to do, see the\n# Joyent Engineering Guide.\n#\n\n#\n# Instruct make to keep around temporary files. We have rules below that\n# automatically update git submodules as needed, but they employ a deps/*/.git\n# temporary file. Without this directive, make tries to remove these .git\n# directories after the build has completed.\n#\n.SECONDARY: $($(wildcard deps/*):%=%/.git)\n\n#\n# This rule enables other rules that use files from a git submodule to have\n# those files depend on deps/module/.git and have \"make\" automatically check\n# out the submodule as needed.\n#\ndeps/%/.git:\n\tgit submodule update --init deps/$*\n\n#\n# These recipes make heavy use of dynamically-created phony targets. The parent\n# Makefile defines a list of input files like BASH_FILES. We then say that each\n# of these files depends on a fake target called filename.bashchk, and then we\n# define a pattern rule for those targets that runs bash in check-syntax-only\n# mode. This mechanism has the nice properties that if you specify zero files,\n# the rule becomes a noop (unlike a single rule to check all bash files, which\n# would invoke bash with zero files), and you can check individual files from\n# the command line with \"make filename.bashchk\".\n#\n.PHONY: check-bash\ncheck-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle)\n\n%.bashchk: %\n\t$(BASH) -n $^\n\n%.bashstyle: %\n\t$(BASHSTYLE) $^\n\n.PHONY: check-eslint\ncheck-eslint::\n\t$(ESLINT) $(JS_FILES)\n\n.PHONY: check-lint\ncheck-lint::\n\t@(echo 'Running \"make check-lint\"'; NO_STYLE=true $(ESLINT) $(JS_FILES))\n\n.PHONY: check-style\ncheck-style::\n\t @(echo 'Running \"make check-style\"'; NO_LINT=true $(ESLINT) $(JS_FILES) || (echo 'Run \"make fix-style\" to auto-fix styling issues'; exit 1))\n\n.PHONY: fix-style\nfix-style::\n\t$(PRETTIER) --write \"**/*.js\"\n\n.PHONY: check\ncheck: check-lint check-style check-bash\n\t@echo check ok\n\n.PHONY: clean\nclean::\n\t-$(RMTREE) $(CLEAN_FILES)\n\n.PHONY: distclean\ndistclean:: clean\n\t-$(RMTREE) $(DISTCLEAN_FILES)\n\nCSCOPE_FILES = cscope.in.out cscope.out cscope.po.out\nCLEAN_FILES += $(CSCOPE_FILES)\n\n.PHONY: xref\nxref: cscope.files\n\t$(CSCOPE) -bqR\n\n.PHONY: cscope.files\ncscope.files:\n\tfind $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \\\n\t    -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@\n\n#\n# The \"docs\" target is complicated because we do several things here:\n#\n#    (1) Use restdown to build HTML and JSON files from each of DOC_FILES.\n#\n#    (2) Copy these files into $(DOC_BUILD) (build/docs/public), which\n#        functions as a complete copy of the documentation that could be\n#        mirrored or served over HTTP.\n#\n#    (3) Then copy any directories and media from docs/media into\n#        $(DOC_BUILD)/media. This allows projects to include their own media,\n#        including files that will override same-named files provided by\n#        restdown.\n#\n# Step (3) is the surprisingly complex part: in order to do this, we need to\n# identify the subdirectories in docs/media, recreate them in\n# $(DOC_BUILD)/media, then do the same with the files.\n#\nDOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v \"^docs/media$$\")\nDOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%)\nDOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%)\n\nDOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null)\nDOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%)\nDOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%)\n\n#\n# Like the other targets, \"docs\" just depends on the final files we want to\n# create in $(DOC_BUILD), leveraging other targets and recipes to define how\n# to get there.\n#\n.PHONY: docs\ndocs:\t\t\t\t\t\t\t\\\n    $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.html)\t\t\\\n    $(DOC_FILES:%.restdown=$(DOC_BUILD)/%.json)\t\t\\\n    $(DOC_MEDIA_FILES_BUILD)\n\n#\n# We keep the intermediate files so that the next build can see whether the\n# files in DOC_BUILD are up to date.\n#\n.PRECIOUS:\t\t\t\t\t\\\n    $(DOC_FILES:%.restdown=docs/%.html)\t\t\\\n    $(DOC_FILES:%.restdown=docs/%json)\n\n#\n# We do clean those intermediate files, as well as all of DOC_BUILD.\n#\nCLEAN_FILES +=\t\t\t\t\t\\\n    $(DOC_BUILD)\t\t\t\t\\\n    $(DOC_FILES:%.restdown=docs/%.html)\t\t\\\n    $(DOC_FILES:%.restdown=docs/%.json)\n\n#\n# Before installing the files, we must make sure the directories exist. The |\n# syntax tells make that the dependency need only exist, not be up to date.\n# Otherwise, it might try to rebuild spuriously because the directory itself\n# appears out of date.\n#\n$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD)\n\n$(DOC_BUILD)/%: docs/% | $(DOC_BUILD)\n\t$(CP) $< $@\n\ndocs/%.json docs/%.html: docs/%.restdown | $(DOC_BUILD) $(RESTDOWN_EXEC)\n\t$(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $<\n\n$(DOC_BUILD):\n\t$(MKDIR) $@\n\n$(DOC_MEDIA_DIRS_BUILD):\n\t$(MKDIR) $@\n\n#\n# The default \"test\" target does nothing. This should usually be overridden by\n# the parent Makefile. It's included here so we can define \"prepush\" without\n# requiring the repo to define \"test\".\n#\n.PHONY: test\ntest:\n\n.PHONY: prepush\nprepush: check test\n"
  }
]