The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .dir-locals.el
├── .eslintignore
├── .eslintrc.js
├── .github
    ├── ISSUE_TEMPLATE
    │   ├── 1-bug-report.md
    │   └── 2-feature-request.md
    ├── PULL_REQUEST_TEMPLATE.md
    ├── stale.yml
    └── workflows
    │   ├── ci.yml
    │   └── release-please.yml
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc
├── .tern-project
├── CHANGELOG.md
├── CHANGES.md
├── CONTRIBUTING.md
├── FEATURE_REQUESTS.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── benchmark
    ├── benchmarks
    │   ├── middleware.js
    │   ├── response-json.js
    │   ├── response-text.js
    │   └── router-heavy.js
    ├── index.js
    ├── lib
    │   ├── autocannon.js
    │   └── bench.js
    └── package.json
├── bin
    └── report-latency
├── docs
    ├── _api
    │   ├── formatters.md
    │   ├── plugins.md
    │   ├── request.md
    │   ├── response.md
    │   └── server.md
    ├── api
    │   ├── formatters-usage.md
    │   ├── plugins-usage.md
    │   ├── request-events.md
    │   ├── request-log.md
    │   ├── server-errors.md
    │   └── server-events.md
    ├── config
    │   ├── formatters.yaml
    │   ├── plugins.yaml
    │   ├── request.yaml
    │   └── server.yaml
    ├── guides
    │   ├── 4TO5GUIDE.md
    │   ├── 6to7guide.md
    │   ├── 8to9guide.md
    │   ├── client.md
    │   ├── dtrace.md
    │   └── server.md
    └── index.md
├── examples
    ├── dtrace
    │   ├── demo.js
    │   ├── handler-timing.d
    │   └── hello.js
    ├── example.js
    ├── http2
    │   ├── http2.js
    │   └── keys
    │   │   ├── http2-cert.pem
    │   │   ├── http2-csr.pem
    │   │   └── http2-key.pem
    ├── jsonp
    │   └── jsonp.js
    ├── sockio
    │   ├── package.json
    │   └── sockio.js
    ├── spdy
    │   ├── keys
    │   │   ├── spdy-cert.pem
    │   │   ├── spdy-csr.pem
    │   │   └── spdy-key.pem
    │   └── spdy.js
    └── todoapp
    │   ├── README.md
    │   ├── lib
    │       ├── client.js
    │       ├── index.js
    │       └── server.js
    │   ├── main.js
    │   ├── package.json
    │   └── test
    │       └── todo.test.js
├── lib
    ├── chain.js
    ├── deprecationWarnings.js
    ├── dtrace.js
    ├── errorTypes.js
    ├── formatters
    │   ├── binary.js
    │   ├── index.js
    │   ├── json.js
    │   ├── jsonp.js
    │   └── text.js
    ├── helpers
    │   └── chainComposer.js
    ├── http_date.js
    ├── index.js
    ├── plugins
    │   ├── accept.js
    │   ├── audit.js
    │   ├── authorization.js
    │   ├── bodyParser.js
    │   ├── bodyReader.js
    │   ├── conditionalHandler.js
    │   ├── conditionalRequest.js
    │   ├── cpuUsageThrottle.js
    │   ├── date.js
    │   ├── fieldedTextBodyParser.js
    │   ├── formBodyParser.js
    │   ├── fullResponse.js
    │   ├── gzip.js
    │   ├── index.js
    │   ├── inflightRequestThrottle.js
    │   ├── jsonBodyParser.js
    │   ├── jsonp.js
    │   ├── metrics.js
    │   ├── multipartBodyParser.js
    │   ├── oauth2TokenParser.js
    │   ├── pre
    │   │   ├── context.js
    │   │   ├── dedupeSlashes.js
    │   │   ├── pause.js
    │   │   ├── prePath.js
    │   │   ├── reqIdHeaders.js
    │   │   ├── strictQueryParams.js
    │   │   └── userAgent.js
    │   ├── query.js
    │   ├── requestExpiry.js
    │   ├── requestLogger.js
    │   ├── static.js
    │   ├── staticFiles.js
    │   ├── throttle.js
    │   └── utils
    │   │   ├── hrTimeDurationInMs.js
    │   │   ├── httpDate.js
    │   │   ├── regex.js
    │   │   └── shallowCopy.js
    ├── request.js
    ├── response.js
    ├── router.js
    ├── routerRegistryRadix.js
    ├── server.js
    ├── upgrade.js
    └── utils.js
├── package.json
├── test
    ├── .eslintrc
    ├── chain.test.js
    ├── chainComposer.test.js
    ├── formatter-optional.test.js
    ├── formatter.test.js
    ├── index.test.js
    ├── keys
    │   ├── http2-cert.pem
    │   ├── http2-csr.pem
    │   └── http2-key.pem
    ├── lib
    │   ├── helper.js
    │   ├── server-withDisableUncaughtException.js
    │   └── streamRecorder.js
    ├── plugins
    │   ├── .eslintrc
    │   ├── accept.test.js
    │   ├── audit.test.js
    │   ├── authorization.test.js
    │   ├── bodyReader.test.js
    │   ├── conditionalHandler.test.js
    │   ├── conditionalRequest.test.js
    │   ├── context.test.js
    │   ├── cpuUsageThrottle.test.js
    │   ├── dedupeSlashes.test.js
    │   ├── fieldedTextParser.test.js
    │   ├── files
    │   │   ├── data-csv.txt
    │   │   ├── data-tsv.txt
    │   │   ├── object-csv.json
    │   │   └── object-tsv.json
    │   ├── formBodyParser.test.js
    │   ├── gzip.test.js
    │   ├── inflightRequestThrottle.test.js
    │   ├── jsonBodyParser.test.js
    │   ├── metrics.test.js
    │   ├── multipart.test.js
    │   ├── oauth2.test.js
    │   ├── plugins.test.js
    │   ├── query.test.js
    │   ├── reqIdHeaders.test.js
    │   ├── requestExpiry.test.js
    │   ├── static.test.js
    │   ├── staticFiles.test.js
    │   ├── strictQueryParams.test.js
    │   ├── testStaticFiles
    │   │   ├── docs
    │   │   │   ├── doc.md
    │   │   │   └── index.html
    │   │   ├── file1.txt
    │   │   ├── index.html
    │   │   └── special
    │   │   │   └── $_$
    │   │   │       └── bad (file).txt
    │   ├── throttle.test.js
    │   ├── userAgent.test.js
    │   └── utilsHrTimeDurationInMs.test.js
    ├── request.test.js
    ├── response.test.js
    ├── router.test.js
    ├── routerRegistryRadix.test.js
    ├── server.test.js
    ├── serverHttp2.test.js
    ├── upgrade.test.js
    └── utils.test.js
└── tools
    ├── docsBuild.js
    └── mk
        ├── Makefile.defs
        ├── Makefile.deps
        └── Makefile.targ


/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ((nil . ((indent-tabs-mode . nil)
2 |          (tab-width . 4)
3 |          (fill-column . 80)))
4 |  (js-mode . ((js-indent-level . 4)
5 |              (indent-tabs-mode . nil)
6 |              )))
7 | 


--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
 1 | # node_modules ignored by default
 2 | node_modules/
 3 | 
 4 | # other ignored directories
 5 | bin/
 6 | deps/
 7 | docs/
 8 | examples/
 9 | cover_html/
10 | 
11 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/1-bug-report.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: "\U0001F41B Bug report"
 3 | about: Create a report to help us improve
 4 | 
 5 | ---
 6 | 
 7 | <!-- Thank you for taking the time to open an issue for restify! If this is
 8 | your first time here, welcome to our community! We are a group of developers
 9 | who work on restify in our free-time. Some of us do it as a hobby, others are
10 | using restify at work. When asking for help here, keep in mind most of us are
11 | volunteers contributing our daily work back to the community at no cost (and
12 | often for no reward). Please be respectful!
13 | 
14 | Below you will find two templates, one for filing a bug report, and the other
15 | for requesting a feature. Remove the comments from around the template that is
16 | applicable to your case and fill it out accordingly. This standardization helps
17 | the maintainers gather the information they need up front to verify and respond
18 | to problems accordingly, ensuring you get the fastest response possible! -->
19 | 
20 | <!-- REQUIRED: Pre-Submission Checklist -->
21 | 
22 | - [ ] Used appropriate template for the issue type
23 | - [ ] Searched both open and closed issues for duplicates of this issue
24 | - [ ] Title adequately and _concisely_ reflects the feature or the bug
25 | 
26 | **Restify Version**: 
27 | **Node.js Version**: 
28 | 
29 | ## Expected behaviour
30 | <!-- This section details what you expected restify to do based on the code
31 | that you wrote -->
32 | 
33 | ## Actual behaviour
34 | <!-- This section details what restify actually did when you ran your code -->
35 | 
36 | ## Repro case
37 | <!-- Please include a simple and concise example reproducing this bug. Please
38 | _do not_ just dump your application here. By either not providing a repro case
39 | or by providing an overly complicated repro case, you are offloading the work
40 | of isolating your bug to other developers, many of which are here voluntarily.
41 | Good repro cases are single file Node.js applications, where the only logic
42 | present is logic necessary to expose the undesired behaviour. You will often
43 | find that when creating your repro case, you will solve the problem yourself!
44 | -->
45 | 
46 | ## Cause
47 | <!--
48 | If you have been able to trace the bug back to it source(s) in the code base,
49 | please link to them here. -->
50 | 
51 | ## Are you willing and able to fix this?
52 | <!-- "Yes" or, if "no", what can current contributors do to help you create a
53 | PR?  If this issue is unique, as the checklist you completed above suggests,
54 | then you are one of the few people who have encountered this bug in the wild.
55 | While contributors will often help work on issues out of the kindness of their
56 | hearts, its important to remember that you are the largest stakeholder in
57 | seeing this bug resolved as you are the one experiencing it. Kindness and
58 | contributions are what make Free Software go round, help pay it forward! -->
59 | 
60 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/2-feature-request.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: "\U0001F680 Feature request"
 3 | about: Suggest an idea for this project
 4 | 
 5 | ---
 6 | 
 7 | <!-- Thank you for taking the time to open an issue for restify! If this is
 8 | your first time here, welcome to our community! We are a group of developers
 9 | who work on restify in our free-time. Some of us do it as a hobby, others are
10 | using restify at work. When asking for help here, keep in mind most of us are
11 | volunteers contributing our daily work back to the community at no cost (and
12 | often for no reward). Please be respectful!
13 | 
14 | Below you will find two templates, one for filing a bug report, and the other
15 | for requesting a feature. Remove the comments from around the template that is
16 | applicable to your case and fill it out accordingly. This standardization helps
17 | the maintainers gather the information they need up front to verify and respond
18 | to problems accordingly, ensuring you get the fastest response possible! -->
19 | 
20 | <!-- REQUIRED: Pre-Submission Checklist -->
21 | 
22 | - [ ] Used appropriate template for the issue type
23 | - [ ] Searched both open and closed issues for duplicates of this issue
24 | - [ ] Title adequately and _concisely_ reflects the feature or the bug
25 | 
26 | # Feature Request
27 | 
28 | ## Use Case
29 | <!-- Why do you want this? -->
30 | 
31 | ## Example API
32 | <!-- This should include code snippets and documentation for the proposed
33 | feature -->
34 | 
35 | ## Are you willing and able to implement this?
36 | <!-- "Yes" or, if "no", what can current contributors do to help you create a
37 | PR? -->
38 | 


--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
 1 | <!--
 2 | Thank you for taking the time to open an PR for restify! If this is your first
 3 | time here, welcome to our community! We are a group of developers who work on
 4 | restify in our free-time. Some of us do it as a hobby, others are using restify
 5 | at work. When asking for help here, keep in mind most of us are volunteers
 6 | contributing our daily work back to the community at no cost (and often for no
 7 | reward). Please be respectful!
 8 | 
 9 | Below you will find a checklist to help you create the best PR possible. While
10 | the checklist items aren't all _strictly_ required, they dramatically increase
11 | the probability of your PR getting a response and getting merged. Often times,
12 | the least time consuming part of maintaining and open source project is writing
13 | code, its the process and discussions that happen around the code base that
14 | consume a majority of the maintainers' time. By spending a few moments to
15 | adhere to this template, you are not only improve the quality of your PR, you
16 | are also helping save the maintainers a considerable amount of time when trying
17 | to understand and review your changes.
18 | 
19 | And remember, positive vibes are met with positive vibes. Kindness helps Free
20 | Software go round, pay it forward!
21 | -->
22 | 
23 | ## Pre-Submission Checklist
24 | 
25 | - [ ] Opened an issue discussing these changes before opening the PR
26 | - [ ] Ran the linter and tests via `make prepush`
27 | - [ ] Included comprehensive and convincing tests for changes
28 | 
29 | ## Issues
30 | 
31 | Closes:
32 | 
33 | * Issue #
34 | * Issue #
35 | * Issue #
36 | 
37 | > Summarize the issues that discussed these changes
38 | 
39 | # Changes
40 | 
41 | > What does this PR do?
42 | 


--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
 1 | daysUntilStale: 60
 2 | daysUntilClose: 14
 3 | exemptLabels:
 4 |   - Critical
 5 |   - Serve
 6 | staleLabel: Stale
 7 | markComment: >
 8 |   This issue has been automatically marked as stale because it has not had
 9 |   recent activity. It will be closed if no further activity occurs. Thank you
10 |   for your contributions.
11 | closeComment: >
12 |   This issue has been automatically closed as stale because it has not had
13 |   recent activity.
14 | 


--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
 1 | on:
 2 |   push:
 3 |     branches:
 4 |       - master
 5 |   pull_request:
 6 |     branches:
 7 |       - master
 8 | name: ci
 9 | jobs:
10 |   lint:
11 |     name: lint
12 |     runs-on: ubuntu-latest
13 |     steps:
14 |       - uses: actions/checkout@v2
15 |       - name: install node v16
16 |         uses: actions/setup-node@v1
17 |         with:
18 |           node-version: v16.x
19 |       - name: install dependencies
20 |         run: npm install
21 |       - name: check lint
22 |         run: make check-lint
23 |   test:
24 |     name: test node ${{ matrix.node-version }} on ${{ matrix.os }}
25 |     strategy:
26 |       fail-fast: false
27 |       matrix:
28 |         os:
29 |           - ubuntu-latest
30 |         node-version:
31 |           - 14.x
32 |           - 16.x
33 |           - 18.x
34 |           - 20.x
35 |     runs-on: ${{matrix.os}}
36 |     steps:
37 |       - uses: actions/checkout@v2
38 |       - name: use node ${{ matrix.node-version }}
39 |         uses: actions/setup-node@v1
40 |         with:
41 |           node-version: ${{ matrix.node-version }}
42 |       - name: install dependencies
43 |         run: npm install
44 |       - name: test
45 |         run: make test
46 |         env:
47 |           TEST_SKIP_IP_V6: true
48 | 


--------------------------------------------------------------------------------
/.github/workflows/release-please.yml:
--------------------------------------------------------------------------------
 1 | on:
 2 |   push:
 3 |     branches:
 4 |       - master
 5 |       - 9.x
 6 | name: release-please
 7 | jobs:
 8 |   release-please:
 9 |     runs-on: ubuntu-latest
10 |     steps:
11 |       - uses: GoogleCloudPlatform/release-please-action@v3.6.1
12 |         with:
13 |           token: ${{ secrets.GITHUB_TOKEN }}
14 |           release-type: node
15 |           package-name: restify
16 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | node_modules
 2 | docs/*.html
 3 | docs/pkg
 4 | examples/todoapp/node_modules
 5 | *.log
 6 | *.tar.gz
 7 | *.tgz
 8 | build
 9 | docs/*.json
10 | nbproject
11 | deps/javascriptlint
12 | deps/jsstyle
13 | package-lock.json
14 | benchmark/results
15 | .nyc_output/
16 | coverage/
17 | cover_html/
18 | 


--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
 1 | .coverage_data
 2 | .dir-locals.el
 3 | .gitmodules
 4 | .github
 5 | .travis.yml
 6 | Makefile
 7 | cover_html
 8 | deps
 9 | docs
10 | examples
11 | test
12 | tools
13 | .vscode
14 | .idea
15 | benchmark
16 | .dir-locals.el
17 | .eslintignore
18 | .eslintrc.js
19 | .gitignore
20 | .prettierignore
21 | .prettierrc
22 | .tern-project
23 | .travis.yml
24 | CONTRIBUTING.md
25 | FEATURE_REQUESTS.md
26 | *.log
27 | *.tar.gz
28 | *.tgz
29 | node_modules
30 | 


--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | cover_html
2 | 


--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 |   "tabWidth": 4,
3 |   "singleQuote": true
4 | }
5 | 


--------------------------------------------------------------------------------
/.tern-project:
--------------------------------------------------------------------------------
 1 | {
 2 |   "libs": [
 3 |      "ecma5",
 4 |      "chai"
 5 |   ],
 6 |   "plugins": {
 7 |     "node": {},
 8 |     "complete_strings": {},
 9 |     "doc_comment": {},
10 |     "node_resolve": {}
11 |   }
12 | }
13 | 


--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
 1 | # Contributing to Restify
 2 | 
 3 | Welcome to the restify community! This document is written both for maintainers and community members!
 4 | 
 5 | ## Issues and PRs
 6 | 
 7 | ### Commit Messages
 8 | 
 9 | When 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.
10 | 
11 | ### Labels and Templates
12 | 
13 | We 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.
14 | 
15 | ## Website
16 | 
17 | ### Design
18 | 
19 | The website templates are maintained at https://github.com/restify/restify.github.io and are populated from the docs directory in this repo.
20 | 
21 | ### Releasing a change
22 | 
23 | To update the documentaiton on the website to reflect the latest version of 5.x simply:
24 | 
25 | ```
26 | git clone --recursive git@github.com:restify/restify.github.io
27 | cd restify.github.io
28 | git submodule update --remote && git add _docs && git commit -m 'bump' && git push origin master
29 | ```
30 | 
31 | The website will automatically deploy itself with the new changes.
32 | 
33 | ### Updating a documentation page
34 | 
35 | To update docs, simply run:  
36 | 
37 | ```
38 | make docs-build
39 | ```
40 | 
41 | ### Adding a documentation page
42 | 
43 | To 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.
44 | 
45 | ## Running a benchmark
46 | 
47 | ```
48 | make benchmark
49 | ```
50 | 
51 | ## Cutting a release
52 | 
53 | Cutting 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:
54 | 
55 | ```
56 | $ git fetch
57 | $ git pull origin master # ensure you have the latest changes
58 | $ npx unleash [-p for patch, -m for minor, -M for major] --no-publish -d # do a dry run to verify
59 | $ npx unleash [-p for patch, -m for minor, -M for major] --no-publish 
60 | # Unleash doesnt support 2FA, hence we use --no-publish flag here.
61 | # This ensures we have the package.json updated, changelog generated, tag created
62 | # and all the changes into origin
63 | # Next, publish to npm manually and do not forget to provide the 2FA code.
64 | $ npm publish
65 | ```
66 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2011 Mark Cavage, All rights reserved.
 2 | 
 3 | Permission is hereby granted, free of charge, to any person obtaining a copy
 4 | of this software and associated documentation files (the "Software"), to deal
 5 | in the Software without restriction, including without limitation the rights
 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 7 | copies of the Software, and to permit persons to whom the Software is
 8 | furnished to do so, subject to the following conditions:
 9 | 
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 | 
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE
20 | 


--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
 1 | #
 2 | # Copyright (c) 2012, Joyent, Inc. All rights reserved.
 3 | #
 4 | # Makefile: basic Makefile for template API service
 5 | #
 6 | # This Makefile is a template for new repos. It contains only repo-specific
 7 | # logic and uses included makefiles to supply common targets (javascriptlint,
 8 | # jsstyle, restdown, etc.), which are used by other repos as well. You may well
 9 | # need to rewrite most of this file, but you shouldn't need to touch the
10 | # included makefiles.
11 | #
12 | # If you find yourself adding support for new targets that could be useful for
13 | # other projects too, you should add these to the original versions of the
14 | # included Makefiles (in eng.git) so that other teams can use them too.
15 | #
16 | 
17 | #
18 | # Tools
19 | #
20 | ESLINT		:= ./node_modules/.bin/eslint
21 | DOCUMENTATION		:= ./node_modules/.bin/documentation
22 | NODEUNIT	:= ./node_modules/.bin/nodeunit
23 | MOCHA		:= ./node_modules/.bin/mocha
24 | NODECOVER	:= ./node_modules/.bin/nyc
25 | DOCS_BUILD	:= ./tools/docsBuild.js
26 | NPM		:= npm
27 | NODE		:= node
28 | PRETTIER		:= ./node_modules/.bin/prettier
29 | 
30 | #
31 | # Files
32 | #
33 | JS_FILES	 = '.'
34 | 
35 | CLEAN_FILES	+= node_modules cscope.files
36 | 
37 | include ./tools/mk/Makefile.defs
38 | 
39 | #
40 | # Repo-specific targets
41 | #
42 | .PHONY: all
43 | all: $(NODEUNIT) $(REPO_DEPS)
44 | 	$(NPM) rebuild
45 | 
46 | $(NODEUNIT): | $(NPM_EXEC)
47 | 	$(NPM) install
48 | 
49 | $(NODECOVER): | $(NPM_EXEC)
50 | 	$(NPM) install
51 | 
52 | .PHONY: cover
53 | cover: $(NODECOVER)
54 | 	@rm -fr ./.coverage_data
55 | 	$(NODECOVER) --reporter=html --reporter=text-summary --reporter=lcov $(NODEUNIT) ./test/*.js
56 | 
57 | CLEAN_FILES += $(TAP) ./node_modules/nodeunit
58 | 
59 | .PHONY: test
60 | test: $(NODEUNIT)
61 | 	$(NODEUNIT) test/*.test.js
62 | 	$(MOCHA) --full-trace --no-exit test/plugins/*.test.js
63 | 
64 | .PHONY: docs-build
65 | docs-build:
66 | 	@($(NODE) $(DOCS_BUILD))
67 | 
68 | .PHONY: benchmark
69 | benchmark:
70 | 	@(cd ./benchmark && $(NPM) i && $(NODE) index.js)
71 | 
72 | include ./tools/mk/Makefile.deps
73 | include ./tools/mk/Makefile.targ
74 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | <!-- Please don't remove this: Grab your social icons from https://github.com/carlsednaoui/gitsocial -->
  2 | 
  3 | <!-- display the social media buttons in your README -->
  4 | 
  5 | [![alt text][1.1]][1]
  6 | 
  7 | 
  8 | <!-- links to social media icons -->
  9 | <!-- no need to change these -->
 10 | 
 11 | <!-- icons with padding -->
 12 | 
 13 | [1.1]: http://i.imgur.com/tXSoThF.png (twitter icon with padding)
 14 | 
 15 | <!-- icons without padding -->
 16 | 
 17 | [1.2]: http://i.imgur.com/wWzX9uB.png (twitter icon without padding)
 18 | 
 19 | 
 20 | <!-- links to your social media accounts -->
 21 | <!-- update these accordingly -->
 22 | 
 23 | [1]: http://www.twitter.com/restifyjs
 24 | 
 25 | <!-- Please don't remove this: Grab your social icons from https://github.com/carlsednaoui/gitsocial -->
 26 | 
 27 | ![restify](/../gh-images/logo/png/restify_logo_black_transp_288x288.png?raw=true "restify")
 28 | 
 29 | [![Build Status](https://travis-ci.org/restify/node-restify.svg?branch=master)](https://travis-ci.org/restify/node-restify)
 30 | [![Dependency Status](https://david-dm.org/restify/node-restify.svg)](https://david-dm.org/restify/node-restify)
 31 | [![devDependency Status](https://david-dm.org/restify/node-restify/dev-status.svg)](https://david-dm.org/restify/node-restify#info=devDependencies)
 32 | [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
 33 | 
 34 | [restify](http://restify.com) is a framework, utilizing
 35 | [connect](https://github.com/senchalabs/connect) style middleware for building
 36 | REST APIs.  For full details, see http://restify.com
 37 | 
 38 | Follow restify on [![alt text][1.2]][1]
 39 | 
 40 | # Usage
 41 | 
 42 | ## Server
 43 | ```javascript
 44 | var restify = require('restify');
 45 | 
 46 | const server = restify.createServer({
 47 |   name: 'myapp',
 48 |   version: '1.0.0'
 49 | });
 50 | 
 51 | server.use(restify.plugins.acceptParser(server.acceptable));
 52 | server.use(restify.plugins.queryParser());
 53 | server.use(restify.plugins.bodyParser());
 54 | 
 55 | server.get('/echo/:name', function (req, res, next) {
 56 |   res.send(req.params);
 57 |   return next();
 58 | });
 59 | 
 60 | server.listen(8080, function () {
 61 |   console.log('%s listening at %s', server.name, server.url);
 62 | });
 63 | ```
 64 | 
 65 | ## Client
 66 | ```javascript
 67 | var assert = require('assert');
 68 | var clients = require('restify-clients');
 69 | 
 70 | var client = clients.createJsonClient({
 71 |   url: 'http://localhost:8080',
 72 |   version: '~1.0'
 73 | });
 74 | 
 75 | client.get('/echo/mark', function (err, req, res, obj) {
 76 |   assert.ifError(err);
 77 |   console.log('Server returned: %j', obj);
 78 | });
 79 | ```
 80 | 
 81 | # Installation
 82 | ```bash
 83 | $ npm install restify
 84 | ```
 85 | 
 86 | ## Supported Node Versions
 87 | 
 88 | Restify currently works on Node.js v14.x and v16.x.
 89 | 
 90 | ## License
 91 | 
 92 | The MIT License (MIT)
 93 | 
 94 | Copyright (c) 2018 restify
 95 | 
 96 | Permission is hereby granted, free of charge, to any person obtaining a copy of
 97 | this software and associated documentation files (the "Software"), to deal in
 98 | the Software without restriction, including without limitation the rights to
 99 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
100 | the Software, and to permit persons to whom the Software is furnished to do so,
101 | subject to the following conditions:
102 | 
103 | The above copyright notice and this permission notice shall be included in all
104 | copies or substantial portions of the Software.
105 | 
106 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
107 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
108 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
109 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
110 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
111 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
112 | SOFTWARE.
113 | 
114 | ## Bugs
115 | 
116 | See <https://github.com/restify/node-restify/issues>.
117 | 
118 | ## Other repositories
119 | 
120 | - For the errors module, please go [here](https://github.com/restify/errors).
121 | 
122 | 
123 | ## Mailing list
124 | 
125 | See the
126 | [Google group](https://groups.google.com/forum/?hl=en&fromgroups#!forum/restify)
127 | .
128 | 


--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 | 
3 | ## Reporting a Vulnerability
4 | 
5 | Do not disclose vulnerabilities in public issues. Please report vulnerabilities to
6 | security@restify.com with steps to reproduce the vulnerability, and a patch to fix
7 | it if possible.
8 | 


--------------------------------------------------------------------------------
/benchmark/benchmarks/middleware.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var restify = process.argv.includes('version=head')
 4 |     ? require('../../lib')
 5 |     : require('restify');
 6 | 
 7 | var server = restify.createServer();
 8 | var path = '/';
 9 | var port = 3000;
10 | 
11 | module.exports = {
12 |     url: 'http://localhost:' + port + path
13 | };
14 | 
15 | function handler(req, res, next) {
16 |     next();
17 | }
18 | 
19 | for (var i = 0; i < 10; i++) {
20 |     server.pre(handler);
21 | }
22 | 
23 | for (var j = 0; j < 10; j++) {
24 |     server.use(handler);
25 | }
26 | 
27 | server.get(path, function get(req, res) {
28 |     res.send('hello world');
29 | });
30 | 
31 | if (!module.parent) {
32 |     server.listen(port);
33 | }
34 | 


--------------------------------------------------------------------------------
/benchmark/benchmarks/response-json.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var restify = process.argv.includes('version=head')
 4 |     ? require('../../lib')
 5 |     : require('restify');
 6 | 
 7 | var server = restify.createServer();
 8 | var path = '/';
 9 | var port = 3000;
10 | 
11 | module.exports = {
12 |     url: 'http://localhost:' + port + path
13 | };
14 | 
15 | server.get(path, function onRequest(req, res) {
16 |     res.send({ hello: 'world' });
17 | });
18 | 
19 | if (!module.parent) {
20 |     server.listen(port);
21 | }
22 | 


--------------------------------------------------------------------------------
/benchmark/benchmarks/response-text.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var restify = process.argv.includes('version=head')
 4 |     ? require('../../lib')
 5 |     : require('restify');
 6 | 
 7 | var server = restify.createServer();
 8 | var path = '/';
 9 | var port = 3000;
10 | 
11 | module.exports = {
12 |     url: 'http://localhost:' + port + path
13 | };
14 | 
15 | server.get(path, function onRequest(req, res) {
16 |     res.send('hello world');
17 | });
18 | 
19 | if (!module.parent) {
20 |     server.listen(port);
21 | }
22 | 


--------------------------------------------------------------------------------
/benchmark/benchmarks/router-heavy.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | 
  3 | var restify = process.argv.includes('version=head')
  4 |     ? require('../../lib')
  5 |     : require('restify');
  6 | 
  7 | var server = restify.createServer();
  8 | var path = '/whiskeys/scotch/islay/lagavulin/16-years/50';
  9 | var methods = ['post', 'put', 'get', 'del', 'patch'];
 10 | var _ = require('lodash');
 11 | var port = 3000;
 12 | 
 13 | // Disabling cache: it's not fair as it aims to the worst case, when
 14 | // cache hit ratio is 0%. However, it's still better than the worst
 15 | // as it doesn't require extra time to maintain the LRU cache.
 16 | // There is no other way to simulate 100+ different endpoint
 17 | // calls with the current benchmark suite.
 18 | if (!process.argv.includes('version=head')) {
 19 |     server.router.cache = {
 20 |         get: function get() {
 21 |             return null;
 22 |         },
 23 |         set: function get() {
 24 |             return null;
 25 |         },
 26 |         dump: function get() {
 27 |             return [];
 28 |         }
 29 |     };
 30 | }
 31 | 
 32 | module.exports = {
 33 |     url: 'http://localhost:' + port + path
 34 | };
 35 | 
 36 | var routes = {
 37 |     beers: {
 38 |         ale: {
 39 |             'pale-ale': {
 40 |                 'american-pale-ale': [],
 41 |                 'indian-pale-ale': []
 42 |             },
 43 |             lambic: [],
 44 |             stout: {
 45 |                 'american-porter': [],
 46 |                 'imperial-stout': [],
 47 |                 'irish-stout': []
 48 |             }
 49 |         },
 50 |         lager: {
 51 |             'german-lager': {
 52 |                 marzen: []
 53 |             },
 54 |             pilsner: {
 55 |                 'german-pilsner': []
 56 |             }
 57 |         }
 58 |     },
 59 | 
 60 |     whiskeys: {
 61 |         american: {
 62 |             bourbon: {
 63 |                 kentchuky: {
 64 |                     'jim-beam': ['jim-beam', 'bookers', 'old-crow'],
 65 |                     'makers-mark': ['makers-mark'],
 66 |                     'woodford-reserve': ['woodford-reserve']
 67 |                 },
 68 |                 tennessee: {
 69 |                     'jack-daniels': ['jack-daniels']
 70 |                 }
 71 |             },
 72 |             rye: {
 73 |                 'beam-suntory': ['jim-beam-rye', 'knob-creek']
 74 |             }
 75 |         },
 76 |         irish: {
 77 |             'single-malt': {
 78 |                 bushmills: ['bushmills'],
 79 |                 connemare: ['connemare']
 80 |             },
 81 |             'single-pot': {
 82 |                 redbreast: ['redbreast'],
 83 |                 jameson: ['jameson-15-year']
 84 |             }
 85 |         },
 86 |         japanese: {
 87 |             nikka: ['coffeey-malt', 'blended', 'from-the-barrel'],
 88 |             hibiki: ['japanese-harmony'],
 89 |             yamazakura: ['blended']
 90 |         },
 91 |         scotch: {
 92 |             islay: {
 93 |                 bruichladdich: ['25-years', 'islay-barley-2009'],
 94 |                 octomore: ['7.2', 'islay-barley-8.3'],
 95 |                 laphroaig: ['lore', '15-years', 'four-oak'],
 96 |                 lagavulin: ['distillers-edition', '8-years', '16-years']
 97 |             }
 98 |         }
 99 |     }
100 | };
101 | 
102 | function handler(req, res) {
103 |     res.send('hello');
104 | }
105 | 
106 | function attachRoute(parent, routeConfig) {
107 |     _.map(routeConfig, function map(route, routeKey) {
108 |         var pathChunk = _.isString(routeKey) ? routeKey : route;
109 |         var routePath = parent + '/' + pathChunk;
110 | 
111 |         methods.forEach(function forEach(method) {
112 |             server[method](routePath, handler);
113 |         });
114 | 
115 |         if (_.isObject(route) || _.isArray(route)) {
116 |             attachRoute(routePath, route);
117 |         }
118 |         if (_.isString(route)) {
119 |             for (var i = 0; i <= 100; i++) {
120 |                 methods.forEach(function forEach(method) {
121 |                     server[method](routePath + '/' + i, handler);
122 |                 });
123 |             }
124 |         }
125 |     });
126 | }
127 | 
128 | attachRoute('', routes);
129 | 
130 | if (!module.parent) {
131 |     server.listen(port);
132 | }
133 | 


--------------------------------------------------------------------------------
/benchmark/index.js:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env node
  2 | 'use strict';
  3 | 
  4 | var inquirer = require('inquirer');
  5 | var bench = require('./lib/bench');
  6 | var stableVersion = require('restify/package.json').version;
  7 | 
  8 | var BENCHMARKS = [
  9 |     'response-json',
 10 |     'response-text',
 11 |     'router-heavy',
 12 |     'middleware'
 13 | ];
 14 | 
 15 | function select(callback) {
 16 |     var choices = BENCHMARKS.map(function map(name) {
 17 |         return {
 18 |             name: name,
 19 |             checked: true
 20 |         };
 21 |     });
 22 | 
 23 |     choices.unshift(new inquirer.Separator(' = The usual ='));
 24 | 
 25 |     inquirer
 26 |         .prompt([
 27 |             {
 28 |                 type: 'checkbox',
 29 |                 message: 'Select packages',
 30 |                 name: 'list',
 31 |                 choices: choices,
 32 |                 validate: function validate(answer) {
 33 |                     if (answer.length < 1) {
 34 |                         return 'You must choose at least one package.';
 35 |                     }
 36 |                     return true;
 37 |                 }
 38 |             }
 39 |         ])
 40 |         .then(function onPrompted(answers) {
 41 |             callback(answers.list);
 42 |         });
 43 | }
 44 | 
 45 | inquirer
 46 |     .prompt([
 47 |         {
 48 |             type: 'confirm',
 49 |             name: 'track',
 50 |             message: 'Do you want to track progress?',
 51 |             default: false
 52 |         },
 53 |         {
 54 |             type: 'confirm',
 55 |             name: 'compare',
 56 |             message:
 57 |                 'Do you want to compare HEAD with the stable release (' +
 58 |                 stableVersion +
 59 |                 ')?',
 60 |             default: true
 61 |         },
 62 |         {
 63 |             type: 'confirm',
 64 |             name: 'all',
 65 |             message: 'Do you want to run all benchmark tests?',
 66 |             default: true
 67 |         },
 68 |         {
 69 |             type: 'input',
 70 |             name: 'connection',
 71 |             message: 'How many connections do you need?',
 72 |             default: 100,
 73 |             validate: function validate(value) {
 74 |                 return (
 75 |                     !Number.isNaN(parseFloat(value)) || 'Please enter a number'
 76 |                 );
 77 |             },
 78 |             filter: Number
 79 |         },
 80 |         {
 81 |             type: 'input',
 82 |             name: 'pipelining',
 83 |             message: 'How many pipelining do you need?',
 84 |             default: 10,
 85 |             validate: function validate(value) {
 86 |                 return (
 87 |                     !Number.isNaN(parseFloat(value)) || 'Please enter a number'
 88 |                 );
 89 |             },
 90 |             filter: Number
 91 |         },
 92 |         {
 93 |             type: 'input',
 94 |             name: 'duration',
 95 |             message: 'How long does it take?',
 96 |             default: 30,
 97 |             validate: function validate(value) {
 98 |                 return (
 99 |                     !Number.isNaN(parseFloat(value)) || 'Please enter a number'
100 |                 );
101 |             },
102 |             filter: Number
103 |         }
104 |     ])
105 |     .then(function validate(opts) {
106 |         if (!opts.all) {
107 |             select(function onSelected(list) {
108 |                 bench(opts, list);
109 |             });
110 |         } else {
111 |             bench(opts, BENCHMARKS);
112 |         }
113 |     });
114 | 


--------------------------------------------------------------------------------
/benchmark/lib/autocannon.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var autocannon = require('autocannon');
 4 | var fs = require('fs');
 5 | var autocannonCompare = require('autocannon-compare');
 6 | var path = require('path');
 7 | 
 8 | var resultsDirectory = path.join(__dirname, '../results');
 9 | 
10 | function writeResult(handler, version, result) {
11 |     try {
12 |         fs.accessSync(resultsDirectory);
13 |     } catch (e) {
14 |         fs.mkdirSync(resultsDirectory);
15 |     }
16 | 
17 |     result.server = handler;
18 | 
19 |     var dest = path.join(resultsDirectory, handler + '-' + version + '.json');
20 |     return fs.writeFileSync(dest, JSON.stringify(result, null, 4));
21 | }
22 | 
23 | function fire(opts, handler, version, save, cb) {
24 |     opts = opts || {};
25 |     opts.url = opts.url || 'http://localhost:3000';
26 | 
27 |     var instance = autocannon(opts, function onResult(err, result) {
28 |         if (err) {
29 |             cb(err);
30 |             return;
31 |         }
32 | 
33 |         if (save) {
34 |             writeResult(handler, version, result);
35 |         }
36 | 
37 |         cb();
38 |     });
39 | 
40 |     if (opts.track && save) {
41 |         autocannon.track(instance);
42 |     }
43 | }
44 | 
45 | function compare(handler) {
46 |     var resStable = require(resultsDirectory + '/' + handler + '-stable.json');
47 |     var resHead = require(resultsDirectory + '/' + handler + '-head.json');
48 |     var comp = autocannonCompare(resStable, resHead);
49 |     var result = {
50 |         throughput: {
51 |             significant: comp.throughput.significant
52 |         }
53 |     };
54 | 
55 |     if (comp.equal) {
56 |         result.throughput.equal = true;
57 |     } else if (comp.aWins) {
58 |         result.throughput.equal = false;
59 |         result.throughput.wins = 'stable';
60 |         result.throughput.diff = comp.throughput.difference;
61 |     } else {
62 |         result.throughput.equal = false;
63 |         result.throughput.wins = 'head';
64 |         result.throughput.diff = autocannonCompare(
65 |             resHead,
66 |             resStable
67 |         ).throughput.difference;
68 |     }
69 | 
70 |     return result;
71 | }
72 | 
73 | module.exports = {
74 |     fire: fire,
75 |     compare: compare
76 | };
77 | 


--------------------------------------------------------------------------------
/benchmark/lib/bench.js:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env node
  2 | 'use strict';
  3 | 
  4 | var fork = require('child_process').fork;
  5 | var ora = require('ora');
  6 | var path = require('path');
  7 | var autocannon = require('./autocannon');
  8 | var pipeline = require('vasync').pipeline;
  9 | 
 10 | function runBenchmark(opts, handler, version, cb) {
 11 |     if (opts.track) {
 12 |         console.log(version.toUpperCase() + ':');
 13 |     }
 14 | 
 15 |     var spinner = ora('Started ' + version + '/' + handler).start();
 16 |     var modulePath = path.join(__dirname, '../benchmarks', handler);
 17 |     var url = require(modulePath).url;
 18 |     var forked = fork(modulePath, ['version=' + version]);
 19 | 
 20 |     pipeline(
 21 |         {
 22 |             funcs: [
 23 |                 function warm(_, callback) {
 24 |                     spinner.color = 'magenta';
 25 |                     spinner.text =
 26 |                         'Warming ' + version + '/' + handler + ' for 5s';
 27 | 
 28 |                     var fireOpts = Object.assign({}, opts, {
 29 |                         duration: 5,
 30 |                         url: url
 31 |                     });
 32 |                     autocannon.fire(
 33 |                         fireOpts,
 34 |                         handler,
 35 |                         version,
 36 |                         false,
 37 |                         callback
 38 |                     );
 39 |                 },
 40 | 
 41 |                 function benchmark(_, callback) {
 42 |                     if (opts.track) {
 43 |                         spinner.stop();
 44 |                     } else {
 45 |                         spinner.color = 'yellow';
 46 |                         spinner.text =
 47 |                             'Benchmarking ' +
 48 |                             version +
 49 |                             '/' +
 50 |                             handler +
 51 |                             ' for ' +
 52 |                             opts.duration +
 53 |                             's';
 54 |                     }
 55 | 
 56 |                     var fireOpts = Object.assign({}, opts, { url: url });
 57 |                     autocannon.fire(fireOpts, handler, version, true, callback);
 58 |                 }
 59 |             ]
 60 |         },
 61 |         function onPipelineFinished(err) {
 62 |             forked.kill('SIGINT');
 63 | 
 64 |             if (err) {
 65 |                 spinner.fail();
 66 |                 cb(err);
 67 |                 return;
 68 |             }
 69 | 
 70 |             spinner.text = 'Results saved for ' + version + '/' + handler;
 71 |             spinner.succeed();
 72 | 
 73 |             cb();
 74 |         }
 75 |     );
 76 | }
 77 | 
 78 | function start(opts, list, index) {
 79 |     index = index || 0;
 80 | 
 81 |     // No more item
 82 |     if (list.length === index) {
 83 |         return;
 84 |     }
 85 | 
 86 |     var handler = list[index];
 87 |     console.log('---- ' + handler + ' ----');
 88 | 
 89 |     pipeline(
 90 |         {
 91 |             funcs: [
 92 |                 function head(_, callback) {
 93 |                     runBenchmark(opts, handler, 'head', callback);
 94 |                 },
 95 |                 function stable(_, callback) {
 96 |                     if (!opts.compare) {
 97 |                         callback();
 98 |                         return;
 99 |                     }
100 |                     runBenchmark(opts, handler, 'stable', callback);
101 |                 }
102 |             ]
103 |         },
104 |         function onPipelineFinished(err) {
105 |             if (err) {
106 |                 console.log(err);
107 |                 return;
108 |             }
109 | 
110 |             // Compare versions
111 |             if (opts.compare) {
112 |                 var result = autocannon.compare(handler);
113 | 
114 |                 console.log(handler + ' throughput:');
115 |                 console.log(JSON.stringify(result.throughput, null, 4) + '\n');
116 |             }
117 | 
118 |             // Benchmark next handler
119 |             start(opts, list, ++index);
120 |         }
121 |     );
122 | }
123 | 
124 | module.exports = start;
125 | 


--------------------------------------------------------------------------------
/benchmark/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "restify-benchmark",
 3 |   "homepage": "http://restifyjs.com",
 4 |   "description": "Restify benchmark",
 5 |   "version": "0.0.0",
 6 |   "private": true,
 7 |   "main": "index.js",
 8 |   "engines": {
 9 |     "node": ">=0.10"
10 |   },
11 |   "dependencies": {
12 |     "restify": "latest"
13 |   },
14 |   "devDependencies": {},
15 |   "license": "MIT",
16 |   "scripts": {
17 |     "start": "node indec"
18 |   }
19 | }
20 | 


--------------------------------------------------------------------------------
/docs/_api/formatters.md:
--------------------------------------------------------------------------------
  1 | ---
  2 | title: Formatters API
  3 | permalink: /docs/formatters-api/
  4 | ---
  5 | 
  6 | <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
  7 | 
  8 | ### Table of Contents
  9 | 
 10 | -   [Usage][1]
 11 | -   [Types][2]
 12 |     -   [formatter][3]
 13 | -   [Included formatters][4]
 14 |     -   [formatText][5]
 15 |     -   [formatJSON][6]
 16 |     -   [formatJSONP][7]
 17 |     -   [formatBinary][8]
 18 | 
 19 | ## Usage
 20 | 
 21 | Restify comes bundled with a selection of useful formatters that prepare your
 22 | responses for being sent over the wire, but you are free to include your own!
 23 | 
 24 | ```js
 25 | function formatGraphQL(req, res, body) {
 26 |     var data = body;
 27 |     /* Do a thing to data */
 28 |     res.setHeader('Content-Length', Buffer.byteLength(data));
 29 |     return data;
 30 | }
 31 | 
 32 | var server = restify.createServer({
 33 |     formatters: {
 34 |         'application/graphql': formatGraphQL
 35 |     }
 36 | });
 37 | 
 38 | // Your application now supports content-type 'application/graphql'
 39 | ```
 40 | 
 41 | 
 42 | ## Types
 43 | 
 44 | 
 45 | 
 46 | 
 47 | ### formatter
 48 | 
 49 | Format a response for being sent over the wire
 50 | 
 51 | Type: [Function][9]
 52 | 
 53 | **Parameters**
 54 | 
 55 | -   `req` **[Object][10]** the request object (not used)
 56 | -   `res` **[Object][10]** the response object
 57 | -   `body` **[Object][10]** response body to format
 58 | 
 59 | Returns **[String][11]** formatted response data
 60 | 
 61 | ## Included formatters
 62 | 
 63 | restify comes pre-loaded with a standard set of formatters for common
 64 | use cases.
 65 | 
 66 | 
 67 | ### formatText
 68 | 
 69 | Formats the body to 'text' by invoking a toString() on the body if it
 70 | exists. If it doesn't, then the response is a zero-length string.
 71 | 
 72 | **Parameters**
 73 | 
 74 | -   `req` **[Object][10]** the request object (not used)
 75 | -   `res` **[Object][10]** the response object
 76 | -   `body` **[Object][10]** response body. If it has a toString() method this
 77 |                               will be used to make the string representation
 78 | 
 79 | Returns **[String][11]** data
 80 | 
 81 | ### formatJSON
 82 | 
 83 | JSON formatter. Will look for a toJson() method on the body. If one does not
 84 | exist then a JSON.stringify will be attempted.
 85 | 
 86 | **Parameters**
 87 | 
 88 | -   `req` **[Object][10]** the request object (not used)
 89 | -   `res` **[Object][10]** the response object
 90 | -   `body` **[Object][10]** response body
 91 | 
 92 | Returns **[String][11]** data
 93 | 
 94 | ### formatJSONP
 95 | 
 96 | JSONP formatter. like JSON, but with a callback invocation.
 97 | Unicode escapes line and paragraph separators.
 98 | 
 99 | **Parameters**
100 | 
101 | -   `req` **[Object][10]** the request object
102 | -   `res` **[Object][10]** the response object
103 | -   `body` **[Object][10]** response body
104 | 
105 | Returns **[String][11]** data
106 | 
107 | ### formatBinary
108 | 
109 | Binary formatter.
110 | 
111 | **Parameters**
112 | 
113 | -   `req` **[Object][10]** the request object
114 | -   `res` **[Object][10]** the response object
115 | -   `body` **[Object][10]** response body
116 | 
117 | Returns **[Buffer][12]** body
118 | 
119 | [1]: #usage
120 | 
121 | [2]: #types
122 | 
123 | [3]: #formatter
124 | 
125 | [4]: #included-formatters
126 | 
127 | [5]: #formattext
128 | 
129 | [6]: #formatjson
130 | 
131 | [7]: #formatjsonp
132 | 
133 | [8]: #formatbinary
134 | 
135 | [9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
136 | 
137 | [10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
138 | 
139 | [11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
140 | 
141 | [12]: https://nodejs.org/api/buffer.html
142 | 


--------------------------------------------------------------------------------
/docs/api/formatters-usage.md:
--------------------------------------------------------------------------------
 1 | Restify comes bundled with a selection of useful formatters that prepare your
 2 | responses for being sent over the wire, but you are free to include your own!
 3 | 
 4 | ```js
 5 | function formatGraphQL(req, res, body) {
 6 |     var data = body;
 7 |     /* Do a thing to data */
 8 |     res.setHeader('Content-Length', Buffer.byteLength(data));
 9 |     return data;
10 | }
11 | 
12 | var server = restify.createServer({
13 |     formatters: {
14 |         'application/graphql': formatGraphQL
15 |     }
16 | });
17 | 
18 | // Your application now supports content-type 'application/graphql'
19 | ```
20 | 


--------------------------------------------------------------------------------
/docs/api/plugins-usage.md:
--------------------------------------------------------------------------------
 1 | Restify comes bundled with a selection of useful plugins. These are accessible
 2 | off of `restify.plugins` and `restify.pre`.
 3 | 
 4 | ```js
 5 | var server = restify.createServer();
 6 | server.use(restify.plugins.acceptParser(server.acceptable));
 7 | server.use(restify.plugins.authorizationParser());
 8 | server.use(restify.plugins.dateParser());
 9 | server.use(restify.plugins.queryParser());
10 | server.use(restify.plugins.jsonp());
11 | server.use(restify.plugins.gzipResponse());
12 | server.use(restify.plugins.bodyParser());
13 | server.use(restify.plugins.requestExpiry());
14 | server.use(restify.plugins.throttle({
15 |   burst: 100,
16 |   rate: 50,
17 |   ip: true,
18 |   overrides: {
19 |     '192.168.1.1': {
20 |       rate: 0,        // unlimited
21 |       burst: 0
22 |     }
23 |   }
24 | }));
25 | server.use(restify.plugins.conditionalRequest());
26 | 


--------------------------------------------------------------------------------
/docs/api/request-events.md:
--------------------------------------------------------------------------------
 1 | ### restifyDone
 2 | 
 3 | After request has been fully serviced, an `restifyDone` event is fired.
 4 | restify considers a request to be fully serviced when either:
 5 | 
 6 | 1) The handler chain for a route has been fully completed
 7 | 2) An error was returned to `next()`, and the corresponding error events have
 8 |    been fired for that error type
 9 | 
10 | The signature for the `restifyDone` event is as follows:
11 | 
12 | ```js
13 | function(route, error) { }
14 | ```
15 | 
16 | * `route` - the route object that serviced the request
17 | * `error` - the error passed to `next()`, if applicable
18 | 
19 | Note that when the server automatically responds with a
20 | `NotFound`/`MethodNotAllowed`/`VersionNotAllowed`, this event will still be
21 | fired.
22 | 


--------------------------------------------------------------------------------
/docs/api/request-log.md:
--------------------------------------------------------------------------------
 1 | If you are using the [RequestLogger](#bundled-plugins) plugin, the child logger
 2 | will be available on `req.log`:
 3 | 
 4 | ```js
 5 | function myHandler(req, res, next) {
 6 |   var log = req.log;
 7 | 
 8 |   log.debug({params: req.params}, 'Hello there %s', 'foo');
 9 | }
10 | ```
11 | 
12 | The child logger will inject the request's UUID in the `req._id` attribute of
13 | each log statement. Since the logger lasts for the life of the request, you can
14 | use this to correlate statements for an individual request across any number of
15 | separate handlers.


--------------------------------------------------------------------------------
/docs/api/server-events.md:
--------------------------------------------------------------------------------
  1 | In additional to emitting all the events from node's
  2 | [http.Server](http://nodejs.org/docs/latest/api/http.html#http_class_http_server),
  3 | restify servers also emit a number of additional events that make building REST
  4 | and web applications much easier.
  5 | 
  6 | ### restifyError
  7 | 
  8 | This event is emitted following all error events as a generic catch all. It is
  9 | recommended to use specific error events to handle specific errors, but this
 10 | event can be useful for metrics or logging. If you use this in conjunction with
 11 | other error events, the most specific event will be fired first, followed by
 12 | this one:
 13 | 
 14 | ```js
 15 | server.get('/', function(req, res, next) {
 16 |   return next(new InternalServerError('boom'));
 17 | });
 18 | 
 19 | server.on('InternalServer', function(req, res, err, callback) {
 20 |   // this will get fired first, as it's the most relevant listener
 21 |   return callback();
 22 | });
 23 | 
 24 | server.on('restifyError', function(req, res, err, callback) {
 25 |   // this is fired second.
 26 |   return callback();
 27 | });
 28 | ```
 29 | 
 30 | 
 31 | ### after
 32 | 
 33 | After each request has been fully serviced, an `after` event is fired. This
 34 | event can be hooked into to handle audit logs and other metrics. Note that
 35 | flushing a response does not necessarily correspond with an `after` event.
 36 | restify considers a request to be fully serviced when either:
 37 | 
 38 | 1) The handler chain for a route has been fully completed
 39 | 2) An error was returned to `next()`, and the corresponding error events have
 40 |    been fired for that error type
 41 | 
 42 | The signature is for the after event is as follows:
 43 | 
 44 | ```js
 45 | function(req, res, route, error) { }
 46 | ```
 47 | 
 48 | * `req` - the request object
 49 | * `res` - the response object
 50 | * `route` - the route object that serviced the request
 51 | * `error` - the error passed to `next()`, if applicable
 52 | 
 53 | Note that when the server automatically responds with a
 54 | NotFound/MethodNotAllowed/VersionNotAllowed, this event will still be fired.
 55 | 
 56 | 
 57 | ### pre
 58 | 
 59 | Before each request has been routed, a `pre` event is fired. This event can be
 60 | hooked into handle audit logs and other metrics. Since this event fires
 61 | *before* routing has occured, it will fire regardless of whether the route is
 62 | supported or not, e.g. requests that result in a `404`.
 63 | 
 64 | The signature for the `pre` event is as follows:
 65 | 
 66 | ```js
 67 | function(req, res) {}
 68 | ```
 69 | * `req` - the request object
 70 | * `res` - the response object
 71 | 
 72 | Note that when the server automatically responds with a
 73 | NotFound/MethodNotAllowed/VersionNotAllowed, this event will still be fired.
 74 | 
 75 | 
 76 | ### routed
 77 | 
 78 | A `routed` event is fired after a request has been routed by the router, but
 79 | before handlers specific to that route has run.
 80 | 
 81 | The signature for the `routed` event is as follows:
 82 | 
 83 | ```js
 84 | function(req, res, route) {}
 85 | ```
 86 | 
 87 | * `req` - the request object
 88 | * `res` - the response object
 89 | * `route` - the route object that serviced the request
 90 | 
 91 | Note that this event will *not* fire if a requests comes in that are not
 92 | routable, i.e. one that would result in a `404`.
 93 | 
 94 | 
 95 | ### uncaughtException
 96 | 
 97 | If the restify server was created with `handleUncaughtExceptions: true`,
 98 | restify will leverage [domains](https://nodejs.org/api/domain.html) to handle
 99 | thrown errors in the handler chain. Thrown errors are a result of an explicit
100 | `throw` statement, or as a result of programmer errors like a typo or a null
101 | ref. These thrown errors are caught by the domain, and will be emitted via this
102 | event. For example:
103 | 
104 | ```js
105 | server.get('/', function(req, res, next) {
106 |     res.send(x);  // this will cause a ReferenceError
107 |     return next();
108 | });
109 | 
110 | server.on('uncaughtException', function(req, res, route, err, callback) {
111 |     // this event will be fired, with the error object from above:
112 |     // ReferenceError: x is not defined
113 |     res.send(504, 'boom');
114 |     callback();
115 | });
116 | ```
117 | 
118 | If you listen to this event, you __must__:
119 | 
120 | 1. send a response to the client _and_
121 | 2. call the callback function passed as the fourth argument of the event listener
122 | 
123 | This behavior is different from the standard error events. If you do not listen
124 | to this event, restify's default behavior is to call `res.send()` with the error
125 | that was thrown.
126 | 
127 | The signature is for the after event is as follows:
128 | 
129 | ```js
130 | function(req, res, route, error) { }
131 | ```
132 | 
133 | * `req` - the request object
134 | * `res` - the response object
135 | * `route` - the route object that serviced the request
136 | * `error` - the error passed to `next()`, if applicable
137 | 
138 | ### close
139 | 
140 | Emitted when the server closes.


--------------------------------------------------------------------------------
/docs/config/formatters.yaml:
--------------------------------------------------------------------------------
 1 | toc:
 2 |   - name: Usage
 3 |     file: ../api/formatters-usage.md
 4 |   - name: Types
 5 |     children:
 6 |       - formatter
 7 |   - name: Included formatters
 8 |     description: |
 9 |       restify comes pre-loaded with a standard set of formatters for common
10 |       use cases.
11 |     children:
12 |       - formatText
13 |       - formatJSON
14 |       - formatJSONP
15 |       - formatBinary
16 | 


--------------------------------------------------------------------------------
/docs/config/plugins.yaml:
--------------------------------------------------------------------------------
 1 | toc:
 2 |   - name: Usage
 3 |     file: ../api/plugins-usage.md
 4 |   - name: server.pre() plugins
 5 |     description: |
 6 |       This module includes various pre plugins, which are intended to be used prior
 7 |       to routing of the URL. To use a plugin before routing, use the `server.pre()`
 8 |       method.
 9 |     children:
10 |       - context
11 |       - dedupeSlashes
12 |       - pause
13 |       - sanitizePath
14 |       - reqIdHeaders
15 |       - strictQueryParams
16 |       - userAgentConnection
17 |   - name: server.use() plugins
18 |     children:
19 |       - acceptParser
20 |       - authorizationParser
21 |       - dateParser
22 |       - queryParser
23 |       - jsonp
24 |       - bodyParser
25 |       - requestLogger
26 |       - gzipResponse
27 |       - serveStatic
28 |       - serveStaticFiles
29 |       - throttle
30 |       - requestExpiry
31 |       - inflightRequestThrottle
32 |       - cpuUsageThrottle
33 |       - conditionalHandler
34 |       - conditionalRequest
35 |       - auditLogger
36 |       - metrics
37 |   - name: Types
38 |     children:
39 |       - metrics~callback
40 | 


--------------------------------------------------------------------------------
/docs/config/request.yaml:
--------------------------------------------------------------------------------
1 | toc:
2 |   - Request
3 |   - name: Events
4 |     file: ../api/server-events.md
5 |   - name: Log
6 |     file: ../api/request-log.md
7 | 


--------------------------------------------------------------------------------
/docs/config/server.yaml:
--------------------------------------------------------------------------------
 1 | toc:
 2 |   - createServer
 3 |   - Server
 4 |   - name: Events
 5 |     file: ../api/server-events.md
 6 |   - name: Errors
 7 |     file: ../api/server-errors.md
 8 |   - name: Types
 9 |     children:
10 |       - Server~methodOpts
11 | 


--------------------------------------------------------------------------------
/docs/guides/8to9guide.md:
--------------------------------------------------------------------------------
  1 | ---
  2 | title: restify 8.x to 9.x migration guide
  3 | permalink: /docs/8to9/
  4 | ---
  5 | 
  6 | ## Introduction
  7 | 
  8 | Restify `9.x` comes with `async/await` support, `pino` and more!
  9 | 
 10 | ## Breaking Changes
 11 | 
 12 | ### Drops support for Node.js `8.x`
 13 | 
 14 | Restify requires Node.js version `>=10.0.0`. 
 15 | 
 16 | ### Async/await support
 17 | 
 18 | `async/await` basic support for `.pre()`, `.use()` and route handlers.
 19 | 
 20 | #### Example
 21 | 
 22 | ```js
 23 | const restify = require('restify');
 24 | 
 25 | const server = restify.createServer({});
 26 | 
 27 | server.use(async (req, res) => {
 28 |   req.something = await doSomethingAsync();
 29 | });
 30 | 
 31 | server.get('/params', async (req, res) => {
 32 |   const value = await asyncOperation(req.something);
 33 |    res.send(value);
 34 | });
 35 | ```
 36 | 
 37 | #### Middleware API (`.pre()` and `.use()`)
 38 | 
 39 | ```js
 40 | server.use(async (req, res) => {
 41 |   req.something = await doSomethingAsync();
 42 | });
 43 | ```
 44 | - `fn.length === 2` (arity 2);
 45 | - `fn instanceof AsyncFunction`;
 46 | - if the async function resolves, it calls `next()`;
 47 | - any value returned by the async function will be discarded;
 48 | - if it rejects with an `Error` instance it calls `next(err)`;
 49 | - if it rejects with anything else it wraps in a `AsyncError` and calls `next(err)`;
 50 | 
 51 | #### Route handler API
 52 | 
 53 | ```js
 54 | server.get('/something', async (req, res) => {
 55 |   const someData = await fetchSomeDataAsync();
 56 |   res.send({ data: someData });
 57 | });
 58 | ```
 59 | - `fn.length === 2` (arity 2);
 60 | - `fn instanceof AsyncFunction`;
 61 | - if the async function resolves without a value, it calls `next()`;
 62 | - if the async function resolves with a string value, it calls `next(string)` (re-routes*);
 63 | - if the async function resolves with a value other than string, it calls `next(any)`;
 64 | - if it rejects with an `Error` instance it calls `next(err)`;
 65 | - if it rejects with anything else it wraps in a `AsyncError` and calls `next(err)` (error-handing**);
 66 | 
 67 | ##### (*) Note about re-routing:
 68 | The `8.x` API allows re-routing when calling `next()` with a string value. If the string matches a valid route,
 69 | it will re-route to the given handler. The same is valid for resolving a async function. If the value returned by
 70 | the async function is a string, it will try to re-route to the given handler.
 71 | 
 72 | ##### (**) Note about error handling:
 73 | Although it is recommended to always reject with an instance of Error, in a async function it is possible to
 74 | throw or reject without returning an `Error` instance or even anything at all. In such cases, the value rejected
 75 | will be wrapped on a `AsyncError`.
 76 | 
 77 | ### Handler arity check
 78 | Handlers expecting 2 or fewer parameters added to a `.pre()`, `.use()` or route chain must be async functions, as:
 79 | 
 80 | ```js
 81 | server.use(async (req, res) => {
 82 |   req.something = await doSomethingAsync();
 83 | });
 84 | ```
 85 | 
 86 | Handlers expecting more than 2 parameters shouldn't be async functions, as:
 87 | 
 88 | ````js
 89 | // This middleware will be rejected and restify will throw
 90 | server.use(async (req, res, next) => {
 91 |   doSomethingAsync(function callback(val) {
 92 |     req.something = val;
 93 |     next();
 94 |   });
 95 | });
 96 | ````
 97 | 
 98 | ### Remove `RequestCaptureStream`
 99 | 
100 | Removes `RequestCaptureStream` from Restify core.
101 | 
102 | ### Use `Pino` as default logger (removes dependency on `Bunyan`)
103 | 
104 | [Pino](https://github.com/pinojs/pino) is well maintained, performance-focused, 
105 | compatible API. It does have a few key differences from `Bunyan`:
106 | 
107 | - As a performance optimization, it stores bindings a single serialized `string`, 
108 | while `Bunyan` stores them as object keys;
109 | - It uses a `setter` to set the log level, `Bunyan` uses a method;
110 | - It only accepts one stream. If you need the multi-stream functionality, you
111 | must use [pino-multistream](https://github.com/pinojs/pino-multi-stream).  
112 | 


--------------------------------------------------------------------------------
/examples/dtrace/handler-timing.d:
--------------------------------------------------------------------------------
 1 | #!/usr/sbin/dtrace -s
 2 | #pragma D option quiet
 3 | 
 4 | restify*:::route-start
 5 | {
 6 |         track[arg2] = timestamp;
 7 | }
 8 | 
 9 | 
10 | restify*:::handler-start
11 | /track[arg3]/
12 | {
13 |         h[arg3, copyinstr(arg2)] = timestamp;
14 | }
15 | 
16 | 
17 | restify*:::handler-done
18 | /track[arg3] && h[arg3, copyinstr(arg2)]/
19 | {
20 | 
21 |         @[copyinstr(arg2)] = quantize((timestamp - h[arg3, copyinstr(arg2)]) / 1000000);
22 |         h[arg3, copyinstr(arg2)] = 0;
23 | }
24 | 
25 | 
26 | restify*:::route-done
27 | /track[arg2]/
28 | {
29 |         @[copyinstr(arg1)] = quantize((timestamp - track[arg2]) / 1000000);
30 |         track[arg2] = 0;
31 | }
32 | 


--------------------------------------------------------------------------------
/examples/dtrace/hello.js:
--------------------------------------------------------------------------------
 1 | var restify = require('../../lib');
 2 | 
 3 | var server = restify.createServer({
 4 |     name: 'helloworld',
 5 |     dtrace: true
 6 | });
 7 | 
 8 | server.use(restify.plugins.acceptParser(server.acceptable));
 9 | server.use(restify.plugins.authorizationParser());
10 | server.use(restify.plugins.dateParser());
11 | server.use(restify.plugins.queryParser());
12 | server.use(restify.plugins.urlEncodedBodyParser());
13 | 
14 | server.use(function slowHandler(req, res, next) {
15 |     setTimeout(function() {
16 |         next();
17 |     }, 250);
18 | });
19 | 
20 | server.get(
21 |     {
22 |         path: '/hello/:name',
23 |         name: 'GetFoo'
24 |     },
25 |     function respond(req, res, next) {
26 |         res.send({
27 |             hello: req.params.name
28 |         });
29 |         next();
30 |     }
31 | );
32 | 
33 | server.listen(8080, function() {
34 |     console.log('listening: %s', server.url);
35 | });
36 | 


--------------------------------------------------------------------------------
/examples/example.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var restify = require('../lib');
 4 | var server = restify.createServer();
 5 | 
 6 | server.pre(function pre(req, res, next) {
 7 |     console.log('pre');
 8 |     next();
 9 | });
10 | 
11 | server.use(function use(req, res, next) {
12 |     console.log('use');
13 |     next();
14 | });
15 | 
16 | server.on('after', function(req, res, route, err) {
17 |     console.log('after');
18 | });
19 | 
20 | server.get(
21 |     '/:userId',
22 |     function onRequest(req, res, next) {
23 |         console.log(req.url, '1');
24 |         next();
25 |     },
26 |     function onRequest(req, res, next) {
27 |         console.log(req.url, '2');
28 |         res.send({ hello: 'world' });
29 |         next();
30 |     }
31 | );
32 | 
33 | server.listen(3001);
34 | 


--------------------------------------------------------------------------------
/examples/http2/http2.js:
--------------------------------------------------------------------------------
 1 | var path = require('path');
 2 | var fs = require('fs');
 3 | var pino = require('pino');
 4 | var restify = require('../../lib');
 5 | 
 6 | var srv = restify.createServer({
 7 |     http2: {
 8 |         cert: fs.readFileSync(path.join(__dirname, './keys/http2-cert.pem')),
 9 |         key: fs.readFileSync(path.join(__dirname, './keys/http2-key.pem')),
10 |         ca: fs.readFileSync(path.join(__dirname, 'keys/http2-csr.pem')),
11 |         allowHTTP1: true //allow incoming connections that do not support HTTP/2 to be downgraded to HTTP/1.x
12 |     }
13 | });
14 | 
15 | srv.get('/', function(req, res, next) {
16 |     res.send({ hello: 'world' });
17 |     next();
18 | });
19 | 
20 | srv.on(
21 |     'after',
22 |     restify.plugins.auditLogger({
23 |         event: 'after',
24 |         body: true,
25 |         log: pino(
26 |             { name: 'audit' },
27 |             process.stdout
28 |         )
29 |     })
30 | );
31 | 
32 | srv.listen(8080, function() {
33 |     console.log('ready on %s', srv.url);
34 | });
35 | 


--------------------------------------------------------------------------------
/examples/http2/keys/http2-cert.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE-----
 2 | MIICHzCCAYgCCQCPPSUAa8QZojANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJS
 3 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTENMAsGA1UEBxMET21zazEhMB8GA1UEChMY
 4 | SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTExMDQwOTEwMDY0NVoXDTExMDUw
 5 | OTEwMDY0NVowVDELMAkGA1UEBhMCUlUxEzARBgNVBAgTClNvbWUtU3RhdGUxDTAL
 6 | BgNVBAcTBE9tc2sxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCB
 7 | nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1bn25sPkv46wl70BffxradlkRd/x
 8 | p5Xf8HDhPSfzNNctERYslXT2fX7Dmfd5w1XTVqqGqJ4izp5VewoVOHA8uavo3ovp
 9 | gNWasil5zADWaM1T0nnV0RsFbZWzOTmm1U3D48K8rW3F5kOZ6f4yRq9QT1gF/gN7
10 | 5Pt494YyYyJu/a8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBuRZisIViI2G/R+w79
11 | vk21TzC/cJ+O7tKsseDqotXYTH8SuimEH5IWcXNgnWhNzczwN8s2362NixyvCipV
12 | yd4wzMpPbjIhnWGM0hluWZiK2RxfcqimIBjDParTv6CMUIuwGQ257THKY8hXGg7j
13 | Uws6Lif3P9UbsuRiYPxMgg98wg==
14 | -----END CERTIFICATE-----
15 | 
16 | 


--------------------------------------------------------------------------------
/examples/http2/keys/http2-csr.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE REQUEST-----
 2 | MIIBkzCB/QIBADBUMQswCQYDVQQGEwJSVTETMBEGA1UECBMKU29tZS1TdGF0ZTEN
 3 | MAsGA1UEBxMET21zazEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
 4 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF
 5 | 3/Gnld/wcOE9J/M01y0RFiyVdPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+je
 6 | i+mA1ZqyKXnMANZozVPSedXRGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+
 7 | A3vk+3j3hjJjIm79rwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAiNWhz6EppIVa
 8 | FfUaB3sLeqfamb9tg9kBHtvqj/FJni0snqms0kPWaTySEPHZF0irIb7VVdq/sVCb
 9 | 3gseMVSyoDvPJ4lHC3PXqGQ7kM1mIPhDnR/4HDA3BhlGhTXSDIHgZnvI+HMBdsyC
10 | hC3dz5odyKqe4nmoofomALkBL9t4H8s=
11 | -----END CERTIFICATE REQUEST-----
12 | 
13 | 


--------------------------------------------------------------------------------
/examples/http2/keys/http2-key.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN RSA PRIVATE KEY-----
 2 | MIICXAIBAAKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF3/Gnld/wcOE9J/M01y0RFiyV
 3 | dPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+jei+mA1ZqyKXnMANZozVPSedXR
 4 | GwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+A3vk+3j3hjJjIm79rwIDAQAB
 5 | AoGAAv2QI9h32epQND9TxwSCKD//dC7W/cZOFNovfKCTeZjNK6EIzKqPTGA6smvR
 6 | C1enFl5adf+IcyWqAoe4lkqTvurIj+2EhtXdQ8DBlVuXKr3xvEFdYxXPautdTCF6
 7 | KbXEyS/s1TZCRFjYftvCrXxc3pK45AQX/wg7z1K+YB5pyIECQQD0OJvLoxLYoXAc
 8 | FZraIOZiDsEbGuSHqoCReFXH75EC3+XGYkH2bQ/nSIZ0h1buuwQ/ylKXOlTPT3Qt
 9 | Xm1OQEBvAkEA4AjWsIO/rRpOm/Q2aCrynWMpoUXTZSbL2yGf8pxp/+8r2br5ier0
10 | M1LeBb/OPY1+k39NWLXxQoo64xoSFYk2wQJAd2wDCwX4HkR7HNCXw1hZL9QFK6rv
11 | 20NN0VSlpboJD/3KT0MW/FiCcVduoCbaJK0Au+zEjDyy4hj5N4I4Mw6KMwJAXVAx
12 | I+psTsxzS4/njXG+BgIEl/C+gRYsuMQDnAi8OebDq/et8l0Tg8ETSu++FnM18neG
13 | ntmBeMacinUUbTXuwQJBAJp/onZdsMzeVulsGrqR1uS+Lpjc5Q1gt5ttt2cxj91D
14 | rio48C/ZvWuKNE8EYj2ALtghcVKRvgaWfOxt2GPguGg=
15 | -----END RSA PRIVATE KEY-----
16 | 
17 | 


--------------------------------------------------------------------------------
/examples/jsonp/jsonp.js:
--------------------------------------------------------------------------------
 1 | var restify = require('../../lib');
 2 | 
 3 | var srv = restify.createServer();
 4 | srv.use(restify.plugins.queryParser());
 5 | srv.use(restify.plugins.jsonp());
 6 | srv.get('/', function(req, res, next) {
 7 |     res.send({ hello: 'world' });
 8 |     next();
 9 | });
10 | 
11 | srv.listen(8080, function() {
12 |     console.log('ready on %s', srv.url);
13 | });
14 | 


--------------------------------------------------------------------------------
/examples/sockio/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "restify-example",
 3 |   "version": "0.0.0",
 4 |   "description": "Socket.io example",
 5 |   "main": "sockio.js",
 6 |   "dependencies": {
 7 |     "socket.io": "^4.5.0"
 8 |   },
 9 |   "scripts": {
10 |     "start": "node sockio.js"
11 |   }
12 | }
13 | 


--------------------------------------------------------------------------------
/examples/sockio/sockio.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | var socketio = require('socket.io');
 4 | 
 5 | var restify = require('../../lib');
 6 | 
 7 | ///--- Globals
 8 | 
 9 | var HTML =
10 |     '<script src="/socket.io/socket.io.js"></script>\n' +
11 |     '<script>\n' +
12 |     'var socket = io("http://localhost:8080");\n' +
13 |     'socket.on("news", function (data) {\n' +
14 |     'console.log(data);\n' +
15 |     'socket.emit("my other event", { my: "data" });\n' +
16 |     '});\n' +
17 |     '</script>';
18 | 
19 | ///--- Mainline
20 | 
21 | var server = restify.createServer();
22 | var io = socketio(server.server);
23 | 
24 | server.get('/', function indexHTML(req, res, next) {
25 |     res.setHeader('Content-Type', 'text/html');
26 |     res.setHeader('Content-Length', Buffer.byteLength(HTML));
27 |     res.writeHead(200);
28 |     res.write(HTML);
29 |     res.end();
30 |     next();
31 | });
32 | 
33 | io.on('connection', function(socket) {
34 |     socket.emit('news', { hello: 'world' });
35 |     socket.on('my other event', function(data) {
36 |         console.log(data);
37 |     });
38 | });
39 | 
40 | server.listen(8080, function() {
41 |     console.log('socket.io server listening at %s', server.url);
42 | });
43 | 


--------------------------------------------------------------------------------
/examples/spdy/keys/spdy-cert.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE-----
 2 | MIICHzCCAYgCCQCPPSUAa8QZojANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJS
 3 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTENMAsGA1UEBxMET21zazEhMB8GA1UEChMY
 4 | SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTExMDQwOTEwMDY0NVoXDTExMDUw
 5 | OTEwMDY0NVowVDELMAkGA1UEBhMCUlUxEzARBgNVBAgTClNvbWUtU3RhdGUxDTAL
 6 | BgNVBAcTBE9tc2sxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCB
 7 | nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1bn25sPkv46wl70BffxradlkRd/x
 8 | p5Xf8HDhPSfzNNctERYslXT2fX7Dmfd5w1XTVqqGqJ4izp5VewoVOHA8uavo3ovp
 9 | gNWasil5zADWaM1T0nnV0RsFbZWzOTmm1U3D48K8rW3F5kOZ6f4yRq9QT1gF/gN7
10 | 5Pt494YyYyJu/a8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBuRZisIViI2G/R+w79
11 | vk21TzC/cJ+O7tKsseDqotXYTH8SuimEH5IWcXNgnWhNzczwN8s2362NixyvCipV
12 | yd4wzMpPbjIhnWGM0hluWZiK2RxfcqimIBjDParTv6CMUIuwGQ257THKY8hXGg7j
13 | Uws6Lif3P9UbsuRiYPxMgg98wg==
14 | -----END CERTIFICATE-----
15 | 
16 | 


--------------------------------------------------------------------------------
/examples/spdy/keys/spdy-csr.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE REQUEST-----
 2 | MIIBkzCB/QIBADBUMQswCQYDVQQGEwJSVTETMBEGA1UECBMKU29tZS1TdGF0ZTEN
 3 | MAsGA1UEBxMET21zazEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
 4 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF
 5 | 3/Gnld/wcOE9J/M01y0RFiyVdPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+je
 6 | i+mA1ZqyKXnMANZozVPSedXRGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+
 7 | A3vk+3j3hjJjIm79rwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAiNWhz6EppIVa
 8 | FfUaB3sLeqfamb9tg9kBHtvqj/FJni0snqms0kPWaTySEPHZF0irIb7VVdq/sVCb
 9 | 3gseMVSyoDvPJ4lHC3PXqGQ7kM1mIPhDnR/4HDA3BhlGhTXSDIHgZnvI+HMBdsyC
10 | hC3dz5odyKqe4nmoofomALkBL9t4H8s=
11 | -----END CERTIFICATE REQUEST-----
12 | 
13 | 


--------------------------------------------------------------------------------
/examples/spdy/keys/spdy-key.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN RSA PRIVATE KEY-----
 2 | MIICXAIBAAKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF3/Gnld/wcOE9J/M01y0RFiyV
 3 | dPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+jei+mA1ZqyKXnMANZozVPSedXR
 4 | GwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+A3vk+3j3hjJjIm79rwIDAQAB
 5 | AoGAAv2QI9h32epQND9TxwSCKD//dC7W/cZOFNovfKCTeZjNK6EIzKqPTGA6smvR
 6 | C1enFl5adf+IcyWqAoe4lkqTvurIj+2EhtXdQ8DBlVuXKr3xvEFdYxXPautdTCF6
 7 | KbXEyS/s1TZCRFjYftvCrXxc3pK45AQX/wg7z1K+YB5pyIECQQD0OJvLoxLYoXAc
 8 | FZraIOZiDsEbGuSHqoCReFXH75EC3+XGYkH2bQ/nSIZ0h1buuwQ/ylKXOlTPT3Qt
 9 | Xm1OQEBvAkEA4AjWsIO/rRpOm/Q2aCrynWMpoUXTZSbL2yGf8pxp/+8r2br5ier0
10 | M1LeBb/OPY1+k39NWLXxQoo64xoSFYk2wQJAd2wDCwX4HkR7HNCXw1hZL9QFK6rv
11 | 20NN0VSlpboJD/3KT0MW/FiCcVduoCbaJK0Au+zEjDyy4hj5N4I4Mw6KMwJAXVAx
12 | I+psTsxzS4/njXG+BgIEl/C+gRYsuMQDnAi8OebDq/et8l0Tg8ETSu++FnM18neG
13 | ntmBeMacinUUbTXuwQJBAJp/onZdsMzeVulsGrqR1uS+Lpjc5Q1gt5ttt2cxj91D
14 | rio48C/ZvWuKNE8EYj2ALtghcVKRvgaWfOxt2GPguGg=
15 | -----END RSA PRIVATE KEY-----
16 | 
17 | 


--------------------------------------------------------------------------------
/examples/spdy/spdy.js:
--------------------------------------------------------------------------------
 1 | var path = require('path');
 2 | var fs = require('fs');
 3 | var pino = require('pino');
 4 | var restify = require('../../lib');
 5 | 
 6 | var srv = restify.createServer({
 7 |     spdy: {
 8 |         cert: fs.readFileSync(path.join(__dirname, './keys/spdy-cert.pem')),
 9 |         key: fs.readFileSync(path.join(__dirname, './keys/spdy-key.pem')),
10 |         ca: fs.readFileSync(path.join(__dirname, 'keys/spdy-csr.pem'))
11 |     }
12 | });
13 | 
14 | srv.get('/', function(req, res, next) {
15 |     res.send({ hello: 'world' });
16 |     next();
17 | });
18 | 
19 | srv.on(
20 |     'after',
21 |     restify.plugins.auditLogger({
22 |         event: 'after',
23 |         body: true,
24 |         log: pino({name: 'audit'})
25 |     })
26 | );
27 | 
28 | srv.listen(8080, function() {
29 |     console.log('ready on %s', srv.url);
30 | });
31 | 


--------------------------------------------------------------------------------
/examples/todoapp/lib/client.js:
--------------------------------------------------------------------------------
  1 | // Copyright (c) 2012 Mark Cavage. All rights reserved.
  2 | 
  3 | var util = require('util');
  4 | 
  5 | var assert = require('assert-plus');
  6 | var clients = require('restify-clients');
  7 | 
  8 | ///--- Globals
  9 | 
 10 | var sprintf = util.format;
 11 | 
 12 | ///--- API
 13 | 
 14 | function TodoClient(options) {
 15 |     assert.object(options, 'options');
 16 |     assert.object(options.log, 'options.log');
 17 |     assert.optionalString(options.socketPath, 'options.socketPath');
 18 |     assert.optionalString(options.url, 'options.url');
 19 |     assert.optionalString(options.version, 'options.version');
 20 | 
 21 |     var ver = options.version || '~1.0';
 22 | 
 23 |     this.client = clients.createJSONClient({
 24 |         log: options.log,
 25 |         name: 'TodoClient',
 26 |         socketPath: options.socketPath,
 27 |         url: options.url,
 28 |         version: ver
 29 |     });
 30 |     this.log = options.log.child({ component: 'TodoClient' }, true);
 31 |     this.url = options.url;
 32 |     this.version = ver;
 33 | 
 34 |     if (options.username && options.password) {
 35 |         this.username = options.username;
 36 |         this.client.basicAuth(options.username, options.password);
 37 |     }
 38 | }
 39 | 
 40 | TodoClient.prototype.create = function create(task, cb) {
 41 |     assert.string(task, 'task');
 42 |     assert.func(cb, 'callback');
 43 | 
 44 |     this.client.post('/todo', { task: task }, function(err, req, res, obj) {
 45 |         if (err) {
 46 |             cb(err);
 47 |         } else {
 48 |             cb(null, obj);
 49 |         }
 50 |     });
 51 | };
 52 | 
 53 | TodoClient.prototype.list = function list(cb) {
 54 |     assert.func(cb, 'callback');
 55 | 
 56 |     this.client.get('/todo', function(err, req, res, obj) {
 57 |         if (err) {
 58 |             cb(err);
 59 |         } else {
 60 |             cb(null, obj);
 61 |         }
 62 |     });
 63 | };
 64 | 
 65 | TodoClient.prototype.get = function get(name, cb) {
 66 |     assert.string(name, 'name');
 67 |     assert.func(cb, 'callback');
 68 | 
 69 |     this.client.get('/todo/' + name, function(err, req, res, obj) {
 70 |         if (err) {
 71 |             cb(err);
 72 |         } else {
 73 |             cb(null, obj);
 74 |         }
 75 |     });
 76 | };
 77 | 
 78 | TodoClient.prototype.update = function update(todo, cb) {
 79 |     assert.object(todo, 'todo');
 80 |     assert.func(cb, 'callback');
 81 | 
 82 |     this.client.put('/todo/' + todo.name, todo, function(err) {
 83 |         if (err) {
 84 |             cb(err);
 85 |         } else {
 86 |             cb(null);
 87 |         }
 88 |     });
 89 | };
 90 | 
 91 | TodoClient.prototype.del = function del(name, cb) {
 92 |     if (typeof name === 'function') {
 93 |         cb = name;
 94 |         name = '';
 95 |     }
 96 |     assert.string(name, 'name');
 97 |     assert.func(cb, 'callback');
 98 | 
 99 |     var p = '/todo' + (name.length > 0 ? '/' + name : '');
100 |     this.client.del(p, function(err) {
101 |         if (err) {
102 |             cb(err);
103 |         } else {
104 |             cb(null);
105 |         }
106 |     });
107 | };
108 | 
109 | TodoClient.prototype.toString = function toString() {
110 |     var str = sprintf(
111 |         '[object TodoClient<url=%s, username=%s, version=%s]',
112 |         this.url,
113 |         this.username || 'null',
114 |         this.version
115 |     );
116 |     return str;
117 | };
118 | 
119 | ///--- API
120 | 
121 | module.exports = {
122 |     createClient: function createClient(options) {
123 |         return new TodoClient(options);
124 |     }
125 | };
126 | 


--------------------------------------------------------------------------------
/examples/todoapp/lib/index.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2012 Mark Cavage. All rights reserved.
2 | 
3 | module.exports = {
4 |     createClient: require('./client').createClient,
5 |     createServer: require('./server').createServer
6 | };
7 | 


--------------------------------------------------------------------------------
/examples/todoapp/main.js:
--------------------------------------------------------------------------------
  1 | // Copyright (c) 2012 Mark Cavage. All rights reserved.
  2 | 
  3 | var fs = require('fs');
  4 | var path = require('path');
  5 | 
  6 | var pino = require('pino');
  7 | var getopt = require('posix-getopt');
  8 | var restify = require('restify');
  9 | 
 10 | var todo = require('./lib');
 11 | 
 12 | ///--- Globals
 13 | 
 14 | var NAME = 'todoapp';
 15 | 
 16 | var LOG = pino({ name: NAME });
 17 | 
 18 | ///--- Helpers
 19 | 
 20 | /**
 21 |  * Standard POSIX getopt-style options parser.
 22 |  *
 23 |  * Some options, like directory/user/port are pretty cut and dry, but note
 24 |  * the 'verbose' or '-v' option afflicts the log level, repeatedly. So you
 25 |  * can run something like:
 26 |  *
 27 |  * node main.js -p 80 -vv 2>&1 | npx pino-pretty
 28 |  *
 29 |  * And the log level will be set to TRACE.
 30 |  */
 31 | function parseOptions() {
 32 |     var option;
 33 |     var opts = {};
 34 |     var parser = new getopt.BasicParser('hvd:p:u:z:', process.argv);
 35 | 
 36 |     while ((option = parser.getopt()) !== undefined) {
 37 |         switch (option.option) {
 38 |             case 'd':
 39 |                 opts.directory = path.normalize(option.optarg);
 40 |                 break;
 41 | 
 42 |             case 'h':
 43 |                 usage();
 44 |                 break;
 45 | 
 46 |             case 'p':
 47 |                 opts.port = parseInt(option.optarg, 10);
 48 |                 break;
 49 | 
 50 |             case 'u':
 51 |                 opts.user = option.optarg;
 52 |                 break;
 53 | 
 54 |             case 'v':
 55 |                 // Allows us to set -vvv -> this little hackery
 56 |                 // just ensures that we're never < TRACE
 57 |                 LOG.level(Math.max(pino.levels.values.trace, LOG.level - 10));
 58 | 
 59 |                 if (LOG.level <= pino.levels.values.debug) {
 60 |                     LOG = LOG.child({ src: true });
 61 |                 }
 62 |                 break;
 63 | 
 64 |             case 'z':
 65 |                 opts.password = option.optarg;
 66 |                 break;
 67 | 
 68 |             default:
 69 |                 usage('invalid option: ' + option.option);
 70 |                 break;
 71 |         }
 72 |     }
 73 | 
 74 |     return opts;
 75 | }
 76 | 
 77 | function usage(msg) {
 78 |     if (msg) {
 79 |         console.error(msg);
 80 |     }
 81 | 
 82 |     var str =
 83 |         'usage: ' + NAME + ' [-v] [-d dir] [-p port] [-u user] [-z password]';
 84 |     console.error(str);
 85 |     process.exit(msg ? 1 : 0);
 86 | }
 87 | 
 88 | ///--- Mainline
 89 | 
 90 | (function main() {
 91 |     var options = parseOptions();
 92 | 
 93 |     LOG.debug(options, 'command line arguments parsed');
 94 | 
 95 |     // First setup our 'database'
 96 |     var dir = path.normalize((options.directory || '/tmp') + '/todos');
 97 | 
 98 |     try {
 99 |         fs.mkdirSync(dir);
100 |     } catch (e) {
101 |         if (e.code !== 'EEXIST') {
102 |             LOG.fatal(e, 'unable to create "database" %s', dir);
103 |             process.exit(1);
104 |         }
105 |     }
106 | 
107 |     var server = todo.createServer({
108 |         directory: dir,
109 |         log: LOG
110 |     });
111 | 
112 |     // At last, let's rock and roll
113 |     server.listen(options.port || 8080, function onListening() {
114 |         LOG.info('listening at %s', server.url);
115 |     });
116 | })();
117 | 


--------------------------------------------------------------------------------
/examples/todoapp/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |         "name": "restify-example",
 3 |         "version": "0.0.0",
 4 |         "description": "Kitchen Sink App of restify",
 5 |         "main": "main.js",
 6 |         "dependencies": {
 7 |                 "assert-plus": "1.0.0",
 8 |                 "pino": "^7.11.0",
 9 |                 "posix-getopt": "^1.2.1",
10 |                 "restify-errors": "^8.0.2",
11 |                 "restify-clients": "^3.1.0",
12 |                 "restify": "^8.6.1"
13 |         },
14 |         "devDependencies": {
15 |                 "mocha": "^10.0.0",
16 |                 "chai": "^4.3.6",
17 |                 "pino-pretty": "^7.6.1"
18 |         },
19 |         "scripts": {
20 |                 "start": "node main.js 2>&1 | pino-pretty",
21 |                 "test": "mocha test"
22 |         },
23 |         "author": "Mark Cavage",
24 |         "license": "MIT"
25 | }
26 | 


--------------------------------------------------------------------------------
/examples/todoapp/test/todo.test.js:
--------------------------------------------------------------------------------
  1 | // Copyright (c) 2012 Mark Cavage. All rights reserved.
  2 | 
  3 | var fs = require('fs');
  4 | 
  5 | var pino = require('pino');
  6 | var restify = require('restify');
  7 | var assert = require('chai').assert;
  8 | 
  9 | var todo = require('../lib');
 10 | 
 11 | ///--- Globals
 12 | 
 13 | var DIR = '/tmp/.todo_unit_test';
 14 | var SOCK = '/tmp/.todo_sock';
 15 | 
 16 | ///--- Tests
 17 | 
 18 | describe('todoapp', function () {
 19 |     var CLIENT;
 20 |     var SERVER;
 21 | 
 22 |     before(function (done) {
 23 |         var log = pino({
 24 |             name: 'todo_unit_test',
 25 |             level: process.env.LOG_LEVEL || 'info'
 26 |         });
 27 |     
 28 |         fs.mkdir(DIR, function(err) {
 29 |             if (err && err.code !== 'EEXIST') {
 30 |                 console.error('unable to mkdir: ' + err.stack);
 31 |                 process.exit(1);
 32 |             }
 33 |     
 34 |             SERVER = todo.createServer({
 35 |                 directory: DIR,
 36 |                 log: log.child({ component: 'server' }, true),
 37 |                 noAudit: true
 38 |             });
 39 |     
 40 |             assert.ok(SERVER);
 41 |             SERVER.listen(SOCK, function() {
 42 |                 CLIENT = todo.createClient({
 43 |                     log: log.child({ component: 'client' }, true),
 44 |                     socketPath: SOCK
 45 |                 });
 46 |                 assert.ok(CLIENT);
 47 |                 done();
 48 |             });
 49 |         });    
 50 |     });
 51 | 
 52 |     it('should return an empty list', function (done) {
 53 |         CLIENT.list(function(err, todos) {
 54 |             assert.ifError(err);
 55 |             assert.ok(todos);
 56 |             assert.ok(Array.isArray(todos));
 57 |     
 58 |             if (todos) {
 59 |                 assert.equal(todos.length, 0);
 60 |             }
 61 |             done();
 62 |         });
 63 |     });
 64 | 
 65 |     it('should create a new task', function (done) {
 66 |         var task = 'check that unit test works';
 67 |         CLIENT.create(task, function(err, todo) {
 68 |             assert.ifError(err);
 69 |             assert.ok(todo);
 70 |     
 71 |             if (todo) {
 72 |                 assert.ok(todo.name);
 73 |                 assert.equal(todo.task, task);
 74 |             }
 75 |             done();
 76 |         });
 77 |     });
 78 | 
 79 |     it('should list and get', function (done) {
 80 |         CLIENT.list(function(err, todos) {
 81 |             assert.ifError(err);
 82 |             assert.ok(todos);
 83 |             assert.ok(Array.isArray(todos));
 84 |     
 85 |             if (todos) {
 86 |                 assert.equal(todos.length, 1);
 87 |                 CLIENT.get(todos[0], function(err2, todo) {
 88 |                     assert.ifError(err2);
 89 |                     assert.ok(todo);
 90 |                     done();
 91 |                 });
 92 |             } else {
 93 |                 done();
 94 |             }
 95 |         });
 96 |     });
 97 | 
 98 |     it('should update', function (done) {
 99 |         CLIENT.list(function(err, todos) {
100 |             assert.ifError(err);
101 |             assert.ok(todos);
102 |             assert.ok(Array.isArray(todos));
103 |     
104 |             if (todos) {
105 |                 assert.equal(todos.length, 1);
106 |     
107 |                 var todo = {
108 |                     name: todos[0],
109 |                     task: 'something else'
110 |                 };
111 |                 CLIENT.update(todo, function(err2) {
112 |                     assert.ifError(err2);
113 |                     done();
114 |                 });
115 |             } else {
116 |                 done();
117 |             }
118 |         });
119 |     });
120 | 
121 |     after(function(done) {
122 |         CLIENT.del(function(err) {
123 |             assert.ifError(err);
124 |             CLIENT.client.close();
125 |             SERVER.close(function () {
126 |                 fs.rmdir(DIR, function(err) {
127 |                     assert.ifError(err);
128 |                     done();
129 |                 });
130 |             });
131 |         });
132 |     });
133 | });
134 | 


--------------------------------------------------------------------------------
/lib/deprecationWarnings.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | function deprecationWarnings(server) {
 4 |     // deprecation for domains and next.ifError
 5 |     if (server.handleUncaughtExceptions === true) {
 6 |         server.log.warn(
 7 |             [
 8 |                 'DEPRECATION WARNING: Due to deprecation of the domain module',
 9 |                 'in node.js, all features in restify that depend on it have',
10 |                 'been deprecated as well.',
11 |                 'This includes `handleUncaughtExceptions` and',
12 |                 '`next.ifError()`. They will continue to work in 5.x, but',
13 |                 'consider them unsupported and likely to be removed',
14 |                 'from future versions of restify.'
15 |             ].join(' ')
16 |         );
17 |     }
18 | }
19 | 
20 | module.exports = deprecationWarnings;
21 | 


--------------------------------------------------------------------------------
/lib/dtrace.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | ///--- Globals
 4 | 
 5 | 'use strict';
 6 | 
 7 | var ID = 0;
 8 | var MAX_INT = Math.pow(2, 32) - 1;
 9 | 
10 | var PROBES = {
11 |     // server_name, route_name, id, method, url, headers (json)
12 |     'route-start': ['char *', 'char *', 'int', 'char *', 'char *', 'json'],
13 | 
14 |     // server_name, route_name, handler_name, id
15 |     'handler-start': ['char *', 'char *', 'char *', 'int'],
16 | 
17 |     // server_name, route_name, handler_name, id
18 |     'handler-done': ['char *', 'char *', 'char *', 'int'],
19 | 
20 |     // server_name, route_name, id, statusCode, headers (json)
21 |     'route-done': ['char *', 'char *', 'int', 'int', 'json'],
22 | 
23 |     // Client probes
24 |     // method, url, headers, id
25 |     'client-request': ['char *', 'char *', 'json', 'int'],
26 | 
27 |     // id, statusCode, headers
28 |     'client-response': ['int', 'int', 'json'],
29 | 
30 |     // id, Error.toString()
31 |     'client-error': ['id', 'char *']
32 | };
33 | var PROVIDER;
34 | 
35 | ///--- API
36 | 
37 | // eslint-disable-next-line wrap-iife
38 | module.exports = (function exportStaticProvider() {
39 |     if (!PROVIDER) {
40 |         try {
41 |             var dtrace = require('dtrace-provider');
42 |             PROVIDER = dtrace.createDTraceProvider('restify');
43 |         } catch (e) {
44 |             PROVIDER = {
45 |                 fire: function fire() {},
46 |                 enable: function enable() {},
47 |                 addProbe: function addProbe() {
48 |                     var p = {
49 |                         fire: function fire() {}
50 |                     };
51 |                     return p;
52 |                 },
53 |                 removeProbe: function removeProbe() {},
54 |                 disable: function disable() {}
55 |             };
56 |         }
57 | 
58 |         PROVIDER._rstfy_probes = {};
59 | 
60 |         Object.keys(PROBES).forEach(function forEach(p) {
61 |             var args = PROBES[p].splice(0);
62 |             args.unshift(p);
63 | 
64 |             var probe = PROVIDER.addProbe.apply(PROVIDER, args);
65 |             PROVIDER._rstfy_probes[p] = probe;
66 |         });
67 | 
68 |         PROVIDER.enable();
69 | 
70 |         PROVIDER.nextId = function nextId() {
71 |             if (++ID >= MAX_INT) {
72 |                 ID = 1;
73 |             }
74 | 
75 |             return ID;
76 |         };
77 |     }
78 | 
79 |     return PROVIDER;
80 | })();
81 | 


--------------------------------------------------------------------------------
/lib/errorTypes.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var errors = require('restify-errors');
 4 | 
 5 | // This allows Restify to work with restify-errors v6+
 6 | module.exports = {
 7 |     RequestCloseError: errors.makeConstructor('RequestCloseError'),
 8 |     RouteMissingError: errors.makeConstructor('RouteMissingError'),
 9 |     AsyncError: errors.makeConstructor('AsyncError')
10 | };
11 | 


--------------------------------------------------------------------------------
/lib/formatters/binary.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | ///--- Exports
 6 | 
 7 | /**
 8 |  * Binary formatter.
 9 |  *
10 |  * @public
11 |  * @function formatBinary
12 |  * @param    {Object} req - the request object
13 |  * @param    {Object} res - the response object
14 |  * @param    {Object} body - response body
15 |  * @returns  {Buffer} body
16 |  */
17 | function formatBinary(req, res, body) {
18 |     if (!Buffer.isBuffer(body)) {
19 |         body = new Buffer(body.toString());
20 |     }
21 | 
22 |     res.setHeader('Content-Length', body.length);
23 | 
24 |     return body;
25 | }
26 | 
27 | module.exports = formatBinary;
28 | 


--------------------------------------------------------------------------------
/lib/formatters/index.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | ///--- Exports
 5 | 
 6 | /**
 7 |  * Format a response for being sent over the wire
 8 |  *
 9 |  * @public
10 |  * @typedef {Function} formatter
11 |  * @param    {Object} req - the request object (not used)
12 |  * @param    {Object} res - the response object
13 |  * @param    {Object} body - response body to format
14 |  * @returns  {String} formatted response data
15 |  */
16 | 
17 | module.exports = {
18 |     'application/javascript; q=0.1': require('./jsonp'),
19 |     'application/json; q=0.4': require('./json'),
20 |     'text/plain; q=0.3': require('./text'),
21 |     'application/octet-stream; q=0.2': require('./binary')
22 | };
23 | 


--------------------------------------------------------------------------------
/lib/formatters/json.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var errors = require('restify-errors');
 6 | 
 7 | ///--- Exports
 8 | 
 9 | /**
10 |  * JSON formatter. Will look for a toJson() method on the body. If one does not
11 |  * exist then a JSON.stringify will be attempted.
12 |  *
13 |  * @public
14 |  * @function formatJSON
15 |  * @param    {Object} req - the request object (not used)
16 |  * @param    {Object} res - the response object
17 |  * @param    {Object} body - response body
18 |  * @returns  {String} data
19 |  */
20 | function formatJSON(req, res, body) {
21 |     var data = 'null';
22 |     if (body !== undefined) {
23 |         try {
24 |             data = JSON.stringify(body);
25 |         } catch (e) {
26 |             throw new errors.InternalServerError(
27 |                 { cause: e, info: { formatter: 'json' } },
28 |                 'could not format response body'
29 |             );
30 |         }
31 |     }
32 | 
33 |     // Setting the content-length header is not a formatting feature and should
34 |     // be separated into another module
35 |     res.setHeader('Content-Length', Buffer.byteLength(data));
36 | 
37 |     return data;
38 | }
39 | 
40 | module.exports = formatJSON;
41 | 


--------------------------------------------------------------------------------
/lib/formatters/jsonp.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | ///--- Exports
 6 | 
 7 | /**
 8 |  * JSONP formatter. like JSON, but with a callback invocation.
 9 |  * Unicode escapes line and paragraph separators.
10 |  *
11 |  * @public
12 |  * @function formatJSONP
13 |  * @param    {Object} req - the request object
14 |  * @param    {Object} res - the response object
15 |  * @param    {Object} body - response body
16 |  * @returns  {String} data
17 |  */
18 | function formatJSONP(req, res, body) {
19 |     if (!body) {
20 |         res.setHeader('Content-Length', 0);
21 |         return null;
22 |     }
23 | 
24 |     if (Buffer.isBuffer(body)) {
25 |         body = body.toString('base64');
26 |     }
27 | 
28 |     var _cb = req.query.callback || req.query.jsonp;
29 |     var data;
30 | 
31 |     if (_cb) {
32 |         data =
33 |             'typeof ' +
34 |             _cb +
35 |             " === 'function' && " +
36 |             _cb +
37 |             '(' +
38 |             JSON.stringify(body) +
39 |             ');';
40 |     } else {
41 |         data = JSON.stringify(body);
42 |     }
43 | 
44 |     data = data.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
45 | 
46 |     res.setHeader('Content-Length', Buffer.byteLength(data));
47 |     return data;
48 | }
49 | 
50 | module.exports = formatJSONP;
51 | 


--------------------------------------------------------------------------------
/lib/formatters/text.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | /**
 6 |  * Formats the body to 'text' by invoking a toString() on the body if it
 7 |  * exists. If it doesn't, then the response is a zero-length string.
 8 |  *
 9 |  * @public
10 |  * @function formatText
11 |  * @param    {Object} req - the request object (not used)
12 |  * @param    {Object} res - the response object
13 |  * @param    {Object} body - response body. If it has a toString() method this
14 |  *                           will be used to make the string representation
15 |  * @returns  {String} data
16 |  */
17 | function formatText(req, res, body) {
18 |     // if body is null, default to empty string
19 |     var data = '';
20 | 
21 |     data = body.toString();
22 | 
23 |     // TODO: setting the content-length header is not a formatting
24 |     // feature and should be separated into another module
25 |     res.setHeader('Content-Length', Buffer.byteLength(data));
26 |     return data;
27 | }
28 | 
29 | module.exports = formatText;
30 | 


--------------------------------------------------------------------------------
/lib/helpers/chainComposer.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | /* eslint-disable func-names */
 3 | 
 4 | var Chain = require('../chain');
 5 | var _ = require('lodash');
 6 | 
 7 | module.exports = composeHandlerChain;
 8 | 
 9 | /**
10 |  * Builds a function with the signature of a handler
11 |  * function(req,resp,callback).
12 |  * which internally executes the passed in array of handler function as a chain.
13 |  *
14 |  * @param {Array} [handlers] - handlers Array of
15 |  * function(req,resp,callback) handlers.
16 |  * @param {Object} [options] - options Optional option object that is
17 |  * passed to Chain.
18 |  * @returns {Function} Handler function that executes the handler chain when run
19 |  */
20 | function composeHandlerChain(handlers, options) {
21 |     var chain = new Chain(options);
22 |     if (_.isArray(handlers)) {
23 |         handlers = _.flattenDeep(handlers);
24 |         handlers.forEach(function(handler) {
25 |             chain.add(handler);
26 |         });
27 |     } else {
28 |         chain.add(handlers);
29 |     }
30 | 
31 |     return function handlerChain(req, resp, callback) {
32 |         chain.run(req, resp, callback);
33 |     };
34 | }
35 | 


--------------------------------------------------------------------------------
/lib/http_date.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | /**
 6 |  * Takes an instance of a date object, formats it UTC
 7 |  * e.g., Wed, 17 Jun 2015 01:30:26 GMT
 8 |  *
 9 |  * @public
10 |  * @function httpDate
11 |  * @param    {Object} now - a date object
12 |  * @returns  {String}       formatted dated object
13 |  */
14 | module.exports = function httpDate(now) {
15 |     if (!now) {
16 |         now = new Date();
17 |     }
18 | 
19 |     return now.toUTCString();
20 | };
21 | 


--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
  1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
  2 | 
  3 | 'use strict';
  4 | 
  5 | var assert = require('assert-plus');
  6 | var errors = require('restify-errors');
  7 | 
  8 | var pino = require('pino');
  9 | var Router = require('./router');
 10 | var Server = require('./server');
 11 | var shallowCopy = require('./utils').shallowCopy;
 12 | 
 13 | var InternalError = errors.InternalError;
 14 | 
 15 | /**
 16 |  * A restify server object is the main interface through which you will register
 17 |  * routes and handlers for incoming requests.
 18 |  *
 19 |  * @public
 20 |  * @function createServer
 21 |  * @param {Object} [options]  - an options object
 22 |  * @param {String} [options.name="restify"] - Name of the server.
 23 |  * @param {Boolean} [options.dtrace=false] - enable DTrace support
 24 |  * @param {Router} [options.router=new Router(opts)] - Router
 25 |  * @param {Object} [options.log=pino({name:options.name || "restify"})]
 26 |  * - [pino](https://github.com/pinojs/pino) instance.
 27 |  * @param {String} [options.url] - Once listen() is called, this will be filled
 28 |  * in with where the server is running.
 29 |  * @param {String|Buffer} [options.certificate] - If you want to create an HTTPS
 30 |  * server, pass in a PEM-encoded certificate and key.
 31 |  * @param {String|Buffer} [options.key] - If you want to create an HTTPS server,
 32 |  * pass in a PEM-encoded certificate and key.
 33 |  * @param {Object} [options.formatters] - Custom response formatters for
 34 |  * `res.send()`.
 35 |  * @param {Boolean} [options.handleUncaughtExceptions=false] - When true restify
 36 |  * will use a domain to catch and respond to any uncaught
 37 |  * exceptions that occur in its handler stack.
 38 |  * Comes with significant negative performance impact.
 39 |  * @param {Object} [options.spdy] - Any options accepted by
 40 |  * [node-spdy](https://github.com/indutny/node-spdy).
 41 |  * @param {Object} [options.http2] - Any options accepted by
 42 |  * [http2.createSecureServer](https://nodejs.org/api/http2.html).
 43 |  * @param {Boolean} [options.handleUpgrades=false] - Hook the `upgrade` event
 44 |  * from the node HTTP server, pushing `Connection: Upgrade` requests through the
 45 |  *  regular request handling chain.
 46 |  * @param {Boolean} [options.onceNext=false] - Prevents calling next multiple
 47 |  *  times
 48 |  * @param {Boolean} [options.strictNext=false] - Throws error when next() is
 49 |  *  called more than once, enabled onceNext option
 50 |  * @param {Object} [options.httpsServerOptions] - Any options accepted by
 51 |  * [node-https Server](http://nodejs.org/api/https.html#https_https).
 52 |  * If provided the following restify server options will be ignored:
 53 |  * spdy, ca, certificate, key, passphrase, rejectUnauthorized, requestCert and
 54 |  * ciphers; however these can all be specified on httpsServerOptions.
 55 |  * @param {Boolean} [options.noWriteContinue=false] - prevents
 56 |  *  `res.writeContinue()` in `server.on('checkContinue')` when proxing
 57 |  * @param {Boolean} [options.ignoreTrailingSlash=false] - ignore trailing slash
 58 |  * on paths
 59 |  * @param {Boolean} [options.strictFormatters=true] - enables strict formatters
 60 |  * behavior: a formatter matching the response's content-type is required. If
 61 |  * not found, the response's content-type is automatically set to
 62 |  * 'application/octet-stream'. If a formatter for that content-type is not
 63 |  * found, sending the response errors.
 64 |  * @example
 65 |  * var restify = require('restify');
 66 |  * var server = restify.createServer();
 67 |  *
 68 |  * server.listen(8080, function () {
 69 |  *   console.log('ready on %s', server.url);
 70 |  * });
 71 |  * @returns  {Server} server
 72 |  */
 73 | function createServer(options) {
 74 |     assert.optionalObject(options, 'options');
 75 | 
 76 |     var opts = shallowCopy(options || {});
 77 |     var server;
 78 | 
 79 |     // empty string should override default value.
 80 |     opts.name = opts.hasOwnProperty('name') ? opts.name : 'restify';
 81 |     opts.log = opts.log || pino({ name: opts.name || 'restify' });
 82 |     opts.router = opts.router || new Router(opts);
 83 | 
 84 |     server = new Server(opts);
 85 | 
 86 |     if (opts.handleUncaughtExceptions) {
 87 |         server.on('uncaughtException', function onUncaughtException(
 88 |             req,
 89 |             res,
 90 |             route,
 91 |             e
 92 |         ) {
 93 |             if (
 94 |                 this.listeners('uncaughtException').length > 1 ||
 95 |                 res.headersSent
 96 |             ) {
 97 |                 return false;
 98 |             }
 99 | 
100 |             res.send(new InternalError(e, e.message || 'unexpected error'));
101 |             return true;
102 |         });
103 |     }
104 | 
105 |     return server;
106 | }
107 | 
108 | ///--- Exports
109 | 
110 | module.exports.logger = pino;
111 | module.exports.createServer = createServer;
112 | module.exports.formatters = require('./formatters');
113 | module.exports.plugins = require('./plugins');
114 | module.exports.pre = require('./plugins').pre;
115 | module.exports.helpers = { compose: require('./helpers/chainComposer') };
116 | 


--------------------------------------------------------------------------------
/lib/plugins/accept.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var assert = require('assert-plus');
 6 | var mime = require('mime');
 7 | 
 8 | var NotAcceptableError = require('restify-errors').NotAcceptableError;
 9 | 
10 | /**
11 |  * Parses the `Accept` header, and ensures that the server can respond to what
12 |  * the client asked for. In almost all cases passing in `server.acceptable` is
13 |  * all that's required, as that's an array of content types the server knows
14 |  * how to respond to (with the formatters you've registered). If the request is
15 |  * for a non-handled type, this plugin will return a `NotAcceptableError` (406).
16 |  *
17 |  * Note you can get the set of types allowed from a restify server by doing
18 |  * `server.acceptable`.
19 |  *
20 |  * @public
21 |  * @function acceptParser
22 |  * @throws   {NotAcceptableError}
23 |  * @param    {String[]}    accepts - array of accept types.
24 |  * @returns  {Function}              restify handler.
25 |  * @example
26 |  * server.use(restify.plugins.acceptParser(server.acceptable));
27 |  */
28 | function acceptParser(accepts) {
29 |     var acceptable = accepts;
30 | 
31 |     if (!Array.isArray(acceptable)) {
32 |         acceptable = [acceptable];
33 |     }
34 |     assert.arrayOfString(acceptable, 'acceptable');
35 | 
36 |     acceptable = acceptable
37 |         .filter(function filter(a) {
38 |             return a;
39 |         })
40 |         .map(function map(a) {
41 |             return a.indexOf('/') === -1 ? mime.getType(a) : a;
42 |         })
43 |         .filter(function filter(a) {
44 |             return a;
45 |         });
46 | 
47 |     var e = new NotAcceptableError('Server accepts: ' + acceptable.join());
48 | 
49 |     function parseAccept(req, res, next) {
50 |         if (req.accepts(acceptable)) {
51 |             return next();
52 |         }
53 |         return next(e);
54 |     }
55 | 
56 |     return parseAccept;
57 | }
58 | 
59 | module.exports = acceptParser;
60 | 


--------------------------------------------------------------------------------
/lib/plugins/authorization.js:
--------------------------------------------------------------------------------
  1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
  2 | 
  3 | 'use strict';
  4 | 
  5 | var httpSignature = require('http-signature');
  6 | var errors = require('restify-errors');
  7 | 
  8 | ///--- Globals
  9 | 
 10 | var InvalidHeaderError = errors.InvalidHeaderError;
 11 | 
 12 | var OPTIONS = {
 13 |     algorithms: [
 14 |         'rsa-sha1',
 15 |         'rsa-sha256',
 16 |         'rsa-sha512',
 17 |         'dsa-sha1',
 18 |         'hmac-sha1',
 19 |         'hmac-sha256',
 20 |         'hmac-sha512'
 21 |     ]
 22 | };
 23 | 
 24 | ///--- Helpers
 25 | 
 26 | function parseBasic(string) {
 27 |     var decoded;
 28 |     var index;
 29 |     var pieces;
 30 | 
 31 |     decoded = new Buffer(string, 'base64').toString('utf8');
 32 | 
 33 |     if (!decoded) {
 34 |         throw new InvalidHeaderError('Authorization header invalid');
 35 |     }
 36 | 
 37 |     index = decoded.indexOf(':');
 38 | 
 39 |     if (index === -1) {
 40 |         pieces = [decoded];
 41 |     } else {
 42 |         pieces = [decoded.slice(0, index), decoded.slice(index + 1)];
 43 |     }
 44 | 
 45 |     if (!pieces || typeof pieces[0] !== 'string') {
 46 |         throw new InvalidHeaderError('Authorization header invalid');
 47 |     }
 48 | 
 49 |     // Allows for usernameless authentication
 50 |     if (!pieces[0]) {
 51 |         pieces[0] = null;
 52 |     }
 53 | 
 54 |     // Allows for passwordless authentication
 55 |     if (!pieces[1]) {
 56 |         pieces[1] = null;
 57 |     }
 58 | 
 59 |     return {
 60 |         username: pieces[0],
 61 |         password: pieces[1]
 62 |     };
 63 | }
 64 | 
 65 | function parseSignature(request, options) {
 66 |     var opts = options || {};
 67 |     opts.algorithms = OPTIONS.algorithms;
 68 | 
 69 |     try {
 70 |         return httpSignature.parseRequest(request, options);
 71 |     } catch (e) {
 72 |         throw new InvalidHeaderError(
 73 |             'Authorization header invalid: ' + e.message
 74 |         );
 75 |     }
 76 | }
 77 | 
 78 | /**
 79 |  * Parses out the `Authorization` header as best restify can.
 80 |  * Currently only HTTP Basic Auth and
 81 |  * [HTTP Signature](https://github.com/joyent/node-http-signature)
 82 |  * schemes are supported.
 83 |  *
 84 |  * @public
 85 |  * @function authorizationParser
 86 |  * @throws   {InvalidArgumentError}
 87 |  * @param    {Object} [options] - an optional options object that is
 88 |  *                                passed to http-signature
 89 |  * @returns  {Function} Handler
 90 |  * @example
 91 |  * <caption>
 92 |  * Subsequent handlers will see `req.authorization`, which looks like above.
 93 |  *
 94 |  * `req.username` will also be set, and defaults to 'anonymous'.  If the scheme
 95 |  * is unrecognized, the only thing available in `req.authorization` will be
 96 |  * `scheme` and `credentials` - it will be up to you to parse out the rest.
 97 |  * </caption>
 98 |  * {
 99 |  *   scheme: "<Basic|Signature|...>",
100 |  *   credentials: "<Undecoded value of header>",
101 |  *   basic: {
102 |  *     username: $user
103 |  *     password: $password
104 |  *   }
105 |  * }
106 |  */
107 | function authorizationParser(options) {
108 |     function parseAuthorization(req, res, next) {
109 |         req.authorization = {};
110 |         req.username = 'anonymous';
111 | 
112 |         if (!req.headers.authorization) {
113 |             return next();
114 |         }
115 | 
116 |         var pieces = req.headers.authorization.split(' ', 2);
117 | 
118 |         if (!pieces || pieces.length !== 2) {
119 |             var e = new InvalidHeaderError('BasicAuth content is invalid.');
120 |             return next(e);
121 |         }
122 | 
123 |         req.authorization.scheme = pieces[0];
124 |         req.authorization.credentials = pieces[1];
125 | 
126 |         try {
127 |             switch (pieces[0].toLowerCase()) {
128 |                 case 'basic':
129 |                     req.authorization.basic = parseBasic(pieces[1]);
130 |                     req.username = req.authorization.basic.username;
131 |                     break;
132 | 
133 |                 case 'signature':
134 |                     req.authorization.signature = parseSignature(req, options);
135 |                     req.username = req.authorization.signature.keyId;
136 |                     break;
137 | 
138 |                 default:
139 |                     break;
140 |             }
141 |         } catch (e2) {
142 |             return next(e2);
143 |         }
144 | 
145 |         return next();
146 |     }
147 | 
148 |     return parseAuthorization;
149 | }
150 | 
151 | module.exports = authorizationParser;
152 | 


--------------------------------------------------------------------------------
/lib/plugins/date.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var assert = require('assert-plus');
 6 | var errors = require('restify-errors');
 7 | 
 8 | ///--- Globals
 9 | 
10 | var InvalidHeaderError = errors.InvalidHeaderError;
11 | var RequestExpiredError = errors.RequestExpiredError;
12 | 
13 | var BAD_MSG = 'Date header is invalid';
14 | var OLD_MSG = 'Date header %s is too old';
15 | 
16 | ///--- API
17 | 
18 | /**
19 |  * Parses out the HTTP Date header (if present) and checks for clock skew.
20 |  * If the header is invalid, a `InvalidHeaderError` (`400`) is returned.
21 |  * If the clock skew exceeds the specified value,
22 |  * a `RequestExpiredError` (`400`) is returned.
23 |  * Where expired means the request originated at a time
24 |  * before (`$now - $clockSkew`).
25 |  * The default clockSkew allowance is 5m (thanks
26 |  * Kerberos!)
27 |  *
28 |  * @public
29 |  * @function dateParser
30 |  * @throws   {RequestExpiredError}
31 |  * @throws   {InvalidHeaderError}
32 |  * @param    {Number}    [clockSkew=300] - allowed clock skew in seconds.
33 |  * @returns  {Function}                    restify handler.
34 |  * @example
35 |  * // Allows clock skew of 1m
36 |  * server.use(restify.plugins.dateParser(60));
37 |  */
38 | function dateParser(clockSkew) {
39 |     var normalizedClockSkew = clockSkew || 300;
40 |     assert.number(normalizedClockSkew, 'normalizedClockSkew');
41 | 
42 |     normalizedClockSkew = normalizedClockSkew * 1000;
43 | 
44 |     function parseDate(req, res, next) {
45 |         if (!req.headers.date) {
46 |             return next();
47 |         }
48 | 
49 |         var e;
50 |         var date = req.headers.date;
51 |         var log = req.log;
52 | 
53 |         try {
54 |             var now = Date.now();
55 |             var sent = new Date(date).getTime();
56 | 
57 |             if (log.trace()) {
58 |                 log.trace(
59 |                     {
60 |                         allowedSkew: normalizedClockSkew,
61 |                         now: now,
62 |                         sent: sent
63 |                     },
64 |                     'Checking clock skew'
65 |                 );
66 |             }
67 | 
68 |             if (now - sent > normalizedClockSkew) {
69 |                 e = new RequestExpiredError(OLD_MSG, date);
70 |                 return next(e);
71 |             }
72 |         } catch (err) {
73 |             log.trace(
74 |                 {
75 |                     err: err
76 |                 },
77 |                 'Bad Date header: %s',
78 |                 date
79 |             );
80 | 
81 |             e = new InvalidHeaderError(BAD_MSG, date);
82 |             return next(e);
83 |         }
84 | 
85 |         return next();
86 |     }
87 | 
88 |     return parseDate;
89 | }
90 | 
91 | module.exports = dateParser;
92 | 


--------------------------------------------------------------------------------
/lib/plugins/fieldedTextBodyParser.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Dependencies
 3 |  */
 4 | 
 5 | 'use strict';
 6 | 
 7 | var csv = require('csv');
 8 | var assert = require('assert-plus');
 9 | 
10 | ///--- API
11 | 
12 | /**
13 |  * Returns a plugin that will parse the HTTP request body if the
14 |  * contentType is `text/csv` or `text/tsv`.
15 |  *
16 |  * @public
17 |  * @function fieldedTextParser
18 |  * @param    {Object}    options - an options object
19 |  * @returns  {Function} Handler
20 |  */
21 | function fieldedTextParser(options) {
22 |     assert.optionalObject(options, 'options');
23 | 
24 |     function parseFieldedText(req, res, next) {
25 |         // save original body on req.rawBody and req._body
26 |         req.rawBody = req._body = req.body;
27 | 
28 |         var contentType = req.getContentType();
29 | 
30 |         if (
31 |             (contentType !== 'text/csv' &&
32 |                 contentType !== 'text/tsv' &&
33 |                 contentType !== 'text/tab-separated-values') ||
34 |             !req.body
35 |         ) {
36 |             next();
37 |             return;
38 |         }
39 | 
40 |         var hDelimiter = req.headers['x-content-delimiter'];
41 |         var hEscape = req.headers['x-content-escape'];
42 |         var hQuote = req.headers['x-content-quote'];
43 |         var hColumns = req.headers['x-content-columns'];
44 | 
45 |         var delimiter = contentType === 'text/tsv' ? '\t' : ',';
46 |         delimiter = hDelimiter ? hDelimiter : delimiter;
47 |         var escape = hEscape ? hEscape : '\\';
48 |         var quote = hQuote ? hQuote : '"';
49 |         var columns = hColumns ? hColumns : true;
50 | 
51 |         var parserOptions = {
52 |             delimiter: delimiter,
53 |             quote: quote,
54 |             escape: escape,
55 |             columns: columns
56 |         };
57 | 
58 |         csv.parse(req.body, parserOptions, function parse(err, parsedBody) {
59 |             if (err) {
60 |                 return next(err);
61 |             }
62 | 
63 |             // Add an "index" property to every row
64 |             parsedBody.forEach(function forEach(row, index) {
65 |                 row.index = index;
66 |             });
67 |             req.body = parsedBody;
68 |             return next();
69 |         });
70 |     }
71 | 
72 |     return parseFieldedText;
73 | }
74 | 
75 | module.exports = fieldedTextParser;
76 | 


--------------------------------------------------------------------------------
/lib/plugins/formBodyParser.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var assert = require('assert-plus');
 6 | var querystring = require('qs');
 7 | 
 8 | var bodyReader = require('./bodyReader');
 9 | var errors = require('restify-errors');
10 | 
11 | ///--- Globals
12 | 
13 | var MIME_TYPE = 'application/x-www-form-urlencoded';
14 | 
15 | ///--- API
16 | 
17 | /**
18 |  * Returns a plugin that will parse the HTTP request body IFF the
19 |  * contentType is application/x-www-form-urlencoded.
20 |  *
21 |  * If req.params already contains a given key, that key is skipped and an
22 |  * error is logged.
23 |  *
24 |  * @public
25 |  * @function urlEncodedBodyParser
26 |  * @param   {Object}    options - an option sobject
27 |  * @returns {Function} Handler
28 |  */
29 | function urlEncodedBodyParser(options) {
30 |     var opts = options || {};
31 |     assert.object(opts, 'opts');
32 | 
33 |     var override = opts.overrideParams;
34 | 
35 |     function parseUrlEncodedBody(req, res, next) {
36 |         // save original body on req.rawBody and req._body
37 |         req.rawBody = req._body = req.body;
38 | 
39 |         if (req.getContentType() !== MIME_TYPE || !req.body) {
40 |             next();
41 |             return;
42 |         }
43 | 
44 |         try {
45 |             var params = querystring.parse(req.body);
46 | 
47 |             if (opts.mapParams === true) {
48 |                 var keys = Object.keys(params);
49 |                 keys.forEach(function forEach(k) {
50 |                     var p = req.params[k];
51 | 
52 |                     if (p && !override) {
53 |                         return;
54 |                     }
55 |                     req.params[k] = params[k];
56 |                 });
57 |             }
58 | 
59 |             req.body = params;
60 |         } catch (e) {
61 |             next(new errors.InvalidContentError(e.message));
62 |             return;
63 |         }
64 | 
65 |         req.log.trace('req.params now: %j', req.params);
66 |         next();
67 |     }
68 | 
69 |     var chain = [];
70 | 
71 |     if (!opts.bodyReader) {
72 |         chain.push(bodyReader(opts));
73 |     }
74 |     chain.push(parseUrlEncodedBody);
75 |     return chain;
76 | }
77 | 
78 | module.exports = urlEncodedBodyParser;
79 | 


--------------------------------------------------------------------------------
/lib/plugins/fullResponse.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var crypto = require('crypto');
 6 | var httpDate = require('./utils/httpDate');
 7 | var hrTimeDurationInMs = require('./utils/hrTimeDurationInMs');
 8 | 
 9 | ///--- API
10 | 
11 | function setHeaders(req, res) {
12 |     var hash;
13 |     var now = new Date();
14 | 
15 |     if (!res.getHeader('Connection')) {
16 |         res.setHeader('Connection', req.isKeepAlive() ? 'Keep-Alive' : 'close');
17 |     }
18 | 
19 |     if (res._data && !res.getHeader('Content-MD5')) {
20 |         hash = crypto.createHash('md5');
21 |         hash.update(res._data);
22 |         res.setHeader('Content-MD5', hash.digest('base64'));
23 |     }
24 | 
25 |     if (!res.getHeader('Date')) {
26 |         res.setHeader('Date', httpDate(now));
27 |     }
28 | 
29 |     if (res.etag && !res.getHeader('Etag')) {
30 |         res.setHeader('Etag', res.etag);
31 |     }
32 | 
33 |     if (!res.getHeader('Server')) {
34 |         res.setHeader('Server', res.serverName);
35 |     }
36 | 
37 |     if (res.version && !res.getHeader('Api-Version')) {
38 |         res.setHeader('Api-Version', res.version);
39 |     }
40 | 
41 |     if (!res.getHeader('Request-Id')) {
42 |         res.setHeader('Request-Id', req.getId());
43 |     }
44 | 
45 |     if (!res.getHeader('Response-Time')) {
46 |         // we cannot use req._timeFlushed here as
47 |         // the response is not flushed yet
48 |         res.setHeader(
49 |             'Response-Time',
50 |             hrTimeDurationInMs(req._timeStart, process.hrtime())
51 |         );
52 |     }
53 | }
54 | 
55 | /**
56 |  * handles disappeared CORS headers.
57 |  * https://github.com/restify/node-restify/issues/284
58 |  *
59 |  * @public
60 |  * @function fullResponse
61 |  * @returns  {Function} Handler
62 |  */
63 | function fullResponse() {
64 |     function restifyResponseHeaders(req, res, next) {
65 |         res.once('header', function onceHeader() {
66 |             // Restify 1.0 compatibility
67 |             if (res.defaultResponseFormatters) {
68 |                 res.defaultResponseFormatters(res._data);
69 |             }
70 | 
71 |             res.emit('beforeSend', res._data, res._body);
72 | 
73 |             // end backwards-compatibility
74 |             return setHeaders(req, res);
75 |         });
76 | 
77 |         return next();
78 |     }
79 | 
80 |     return restifyResponseHeaders;
81 | }
82 | 
83 | ///--- Exports
84 | 
85 | module.exports = fullResponse;
86 | 


--------------------------------------------------------------------------------
/lib/plugins/gzip.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var zlib = require('zlib');
 6 | 
 7 | var assert = require('assert-plus');
 8 | 
 9 | /**
10 |  * @private
11 |  * @function _writeHead
12 |  * @param   {Function}  originalFunction - originalFunction
13 |  * @returns {undefined} no return value
14 |  */
15 | function _writeHead(originalFunction) {
16 |     this.removeHeader('Content-Length');
17 |     var argsLength = arguments.length;
18 |     var args = new Array(argsLength - 1);
19 | 
20 |     for (var i = 1; i < argsLength; i++) {
21 |         args[i - 1] = arguments[i];
22 |     }
23 |     originalFunction.apply(this, args);
24 | }
25 | 
26 | ///--- API
27 | 
28 | /**
29 |  * If the client sends an `accept-encoding: gzip` header (or one with an
30 |  * appropriate q-val), then the server will automatically gzip all
31 |  * response data.
32 |  * Note that only `gzip` is supported, as this is most widely supported by
33 |  * clients in the wild.
34 |  * This plugin will overwrite some of the internal streams, so any
35 |  * calls to `res.send`, `res.write`, etc., will be compressed.  A side effect is
36 |  * that the `content-length` header cannot be known, and so
37 |  * `transfer-encoding: chunked` will *always* be set when this is in effect.
38 |  * This plugin has no impact if the client does not send
39 |  * `accept-encoding: gzip`.
40 |  *
41 |  * https://github.com/restify/node-restify/issues/284
42 |  *
43 |  * @public
44 |  * @function gzipResponse
45 |  * @param   {Object}   [opts] - an options object, see: zlib.createGzip
46 |  * @returns {Function} Handler
47 |  * @example
48 |  * server.use(restify.plugins.gzipResponse());
49 |  */
50 | function gzipResponse(opts) {
51 |     assert.optionalObject(opts, 'options');
52 | 
53 |     function gzip(req, res, next) {
54 |         if (!req.acceptsEncoding('gzip')) {
55 |             next();
56 |             return;
57 |         }
58 | 
59 |         var gz = zlib.createGzip(opts);
60 | 
61 |         gz.on('data', res.write.bind(res));
62 |         gz.once('end', res.end.bind(res));
63 |         gz.on('drain', res.emit.bind(res, 'drain'));
64 | 
65 |         var origWrite = res.write;
66 |         var origEnd = res.end;
67 |         var origWriteHead = res.writeHead;
68 |         res.handledGzip = function _handledGzip() {
69 |             res.write = origWrite;
70 |             res.end = origEnd;
71 |             res.writeHead = origWriteHead;
72 |         };
73 | 
74 |         res.write = gz.write.bind(gz);
75 |         res.end = gz.end.bind(gz);
76 | 
77 |         res.writeHead = _writeHead.bind(res, res.writeHead);
78 |         res.setHeader('Content-Encoding', 'gzip');
79 |         next();
80 |     }
81 | 
82 |     return gzip;
83 | }
84 | 
85 | ///--- Exports
86 | 
87 | module.exports = gzipResponse;
88 | 


--------------------------------------------------------------------------------
/lib/plugins/index.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | ///--- Exports
 6 | 
 7 | module.exports = {
 8 |     acceptParser: require('./accept'),
 9 |     auditLogger: require('./audit'),
10 |     authorizationParser: require('./authorization'),
11 |     bodyParser: require('./bodyParser'),
12 |     bodyReader: require('./bodyReader'),
13 |     conditionalHandler: require('./conditionalHandler'),
14 |     conditionalRequest: require('./conditionalRequest'),
15 |     cpuUsageThrottle: require('./cpuUsageThrottle.js'),
16 |     dateParser: require('./date'),
17 |     fullResponse: require('./fullResponse'),
18 |     gzipResponse: require('./gzip'),
19 |     inflightRequestThrottle: require('./inflightRequestThrottle'),
20 |     jsonBodyParser: require('./jsonBodyParser'),
21 |     jsonp: require('./jsonp'),
22 |     multipartBodyParser: require('./multipartBodyParser'),
23 |     oauth2TokenParser: require('./oauth2TokenParser'),
24 |     queryParser: require('./query'),
25 |     metrics: require('./metrics'),
26 |     requestExpiry: require('./requestExpiry'),
27 |     requestLogger: require('./requestLogger'),
28 |     serveStatic: require('./static'),
29 |     serveStaticFiles: require('./staticFiles'),
30 |     throttle: require('./throttle'),
31 |     urlEncodedBodyParser: require('./formBodyParser'),
32 | 
33 |     pre: {
34 |         context: require('./pre/context'),
35 |         dedupeSlashes: require('./pre/dedupeSlashes'),
36 |         pause: require('./pre/pause'),
37 |         reqIdHeaders: require('./pre/reqIdHeaders'),
38 |         sanitizePath: require('./pre/prePath'),
39 |         strictQueryParams: require('./pre/strictQueryParams'),
40 |         userAgentConnection: require('./pre/userAgent')
41 |     }
42 | };
43 | 


--------------------------------------------------------------------------------
/lib/plugins/inflightRequestThrottle.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var assert = require('assert-plus');
 4 | var ServiceUnavailableError = require('restify-errors').ServiceUnavailableError;
 5 | 
 6 | /**
 7 |  * The `inflightRequestThrottle` module allows you to specify an upper limit to
 8 |  * the maximum number of inflight requests your server is able to handle. This
 9 |  * is a simple heuristic for protecting against event loop contention between
10 |  * requests causing unacceptable latencies.
11 |  *
12 |  * The custom error is optional, and allows you to specify your own response
13 |  * and status code when rejecting incoming requests due to too many inflight
14 |  * requests. It defaults to `503 ServiceUnavailableError`.
15 |  *
16 |  * This plugin should be registered as early as possibly in the middleware stack
17 |  * using `pre` to avoid performing unnecessary work.
18 |  *
19 |  * @public
20 |  * @function inflightRequestThrottle
21 |  * @param {Object} opts - configure this plugin
22 |  * @param {Number} opts.limit - maximum number of inflight requests the server
23 |  *    will handle before returning an error
24 |  * @param {Error} opts.err - A restify error used as a response when the
25 |  *    inflight request limit is exceeded
26 |  * @param {Function} opts.server - the instance of the restify server this
27 |  *    plugin will throttle.
28 |  * @returns {Function} middleware to be registered on server.pre
29 |  * @example
30 |  * var errors = require('restify-errors');
31 |  * var restify = require('restify');
32 |  *
33 |  * var server = restify.createServer();
34 |  * const options = { limit: 600, server: server };
35 |  * options.res = new errors.InternalServerError();
36 |  * server.pre(restify.plugins.inflightRequestThrottle(options));
37 |  */
38 | function inflightRequestThrottle(opts) {
39 |     // Scrub input and populate our configuration
40 |     assert.object(opts, 'opts');
41 |     assert.number(opts.limit, 'opts.limit');
42 |     assert.object(opts.server, 'opts.server');
43 |     assert.func(opts.server.inflightRequests, 'opts.server.inflightRequests');
44 | 
45 |     if (opts.err !== undefined && opts.err !== null) {
46 |         assert.ok(opts.err instanceof Error, 'opts.err must be an error');
47 |         assert.optionalNumber(opts.err.statusCode, 'opts.err.statusCode');
48 |     }
49 | 
50 |     var plugin = {};
51 |     plugin._err = opts.err || new ServiceUnavailableError('resource exhausted');
52 |     plugin._limit = opts.limit;
53 |     plugin._server = opts.server;
54 | 
55 |     function onRequest(req, res, next) {
56 |         var inflightRequests = plugin._server.inflightRequests();
57 | 
58 |         if (inflightRequests > plugin._limit) {
59 |             req.log.trace(
60 |                 {
61 |                     plugin: 'inflightRequestThrottle',
62 |                     inflightRequests: inflightRequests,
63 |                     limit: plugin._limit
64 |                 },
65 |                 'maximum inflight requests exceeded, rejecting request'
66 |             );
67 |             return next(plugin._err);
68 |         }
69 | 
70 |         return next();
71 |     }
72 | 
73 |     return onRequest;
74 | }
75 | 
76 | module.exports = inflightRequestThrottle;
77 | 


--------------------------------------------------------------------------------
/lib/plugins/jsonBodyParser.js:
--------------------------------------------------------------------------------
  1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
  2 | 
  3 | 'use strict';
  4 | 
  5 | var assert = require('assert-plus');
  6 | var errors = require('restify-errors');
  7 | 
  8 | var bodyReader = require('./bodyReader');
  9 | var regex = require('./utils/regex');
 10 | 
 11 | ///--- API
 12 | 
 13 | /**
 14 |  * Parses json body from the request.
 15 |  *
 16 |  * @public
 17 |  * @function jsonBodyParser
 18 |  * @param    {Object}               options - an options object
 19 |  * @throws   {InvalidContentError}            on bad input
 20 |  * @returns  {Function} Handler
 21 |  */
 22 | function jsonBodyParser(options) {
 23 |     assert.optionalObject(options, 'options');
 24 |     var opts = options || {};
 25 | 
 26 |     var override = opts.overrideParams;
 27 | 
 28 |     function parseJson(req, res, next) {
 29 |         // save original body on req.rawBody and req._body
 30 |         req.rawBody = req._body = req.body;
 31 | 
 32 |         var contentType = req.getContentType();
 33 | 
 34 |         // check for empty body first, don't pay regex tax unless necessary.
 35 |         // for content type, check for exact match and any of the *+json types
 36 |         if (
 37 |             !req.body ||
 38 |             (contentType !== 'application/json' &&
 39 |                 !regex.jsonContentType.test(contentType))
 40 |         ) {
 41 |             return next();
 42 |         }
 43 | 
 44 |         var params;
 45 | 
 46 |         try {
 47 |             params = JSON.parse(req.body, opts.reviver);
 48 |         } catch (e) {
 49 |             return next(
 50 |                 new errors.InvalidContentError(
 51 |                     '%s',
 52 |                     'Invalid JSON: ' + e.message
 53 |                 )
 54 |             );
 55 |         }
 56 | 
 57 |         if (opts.mapParams === true) {
 58 |             if (Array.isArray(params)) {
 59 |                 // if req.params exists, we have url params. we can't map an
 60 |                 // array safely onto req.params, throw an error.
 61 |                 if (
 62 |                     req.params &&
 63 |                     Object.keys(req.params).length > 0 &&
 64 |                     !(req.params instanceof Array)
 65 |                 ) {
 66 |                     return next(
 67 |                         new errors.InternalServerError(
 68 |                             'Cannot map POST body of [Array array] onto ' +
 69 |                                 'req.params'
 70 |                         )
 71 |                     );
 72 |                 }
 73 |                 req.params = params;
 74 |             } else if (typeof params === 'object' && params !== null) {
 75 |                 // else, try to merge the objects
 76 |                 Object.keys(params).forEach(function forEach(k) {
 77 |                     var p = req.params[k];
 78 | 
 79 |                     if (p && !override) {
 80 |                         return;
 81 |                     }
 82 |                     req.params[k] = params[k];
 83 |                 });
 84 |             } else {
 85 |                 // otherwise, do a wholesale stomp, no need to merge one by one.
 86 |                 req.params = params || req.params;
 87 |             }
 88 |         }
 89 | 
 90 |         req.body = params;
 91 | 
 92 |         return next();
 93 |     }
 94 | 
 95 |     var chain = [];
 96 | 
 97 |     if (!opts.bodyReader) {
 98 |         chain.push(bodyReader(opts));
 99 |     }
100 |     chain.push(parseJson);
101 |     return chain;
102 | }
103 | 
104 | module.exports = jsonBodyParser;
105 | 


--------------------------------------------------------------------------------
/lib/plugins/jsonp.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var qs = require('qs');
 6 | 
 7 | ///--- API
 8 | 
 9 | /**
10 |  * Parses the jsonp callback out of the request.
11 |  * Supports checking the query string for `callback` or `jsonp` and ensuring
12 |  * that the content-type is appropriately set if JSONP params are in place.
13 |  * There is also a default `application/javascript` formatter to handle this.
14 |  *
15 |  * You *should* set the `queryParser` plugin to run before this, but if you
16 |  * don't this plugin will still parse the query string properly.
17 |  *
18 |  * @public
19 |  * @function jsonp
20 |  * @returns  {Function} Handler
21 |  * @example
22 |  * var server = restify.createServer();
23 |  * server.use(restify.plugins.jsonp());
24 |  */
25 | function jsonp() {
26 |     function _jsonp(req, res, next) {
27 |         var q = req.getQuery();
28 | 
29 |         // If the query plugin wasn't used, we need to hack it in now
30 |         if (typeof q === 'string') {
31 |             req.query = qs.parse(q);
32 |         }
33 | 
34 |         if (req.query.callback || req.query.jsonp) {
35 |             res.setHeader('Content-Type', 'application/javascript');
36 |         }
37 | 
38 |         next();
39 |     }
40 | 
41 |     return _jsonp;
42 | }
43 | 
44 | module.exports = jsonp;
45 | 


--------------------------------------------------------------------------------
/lib/plugins/oauth2TokenParser.js:
--------------------------------------------------------------------------------
  1 | /*
  2 | oauth2TokenParser - Parser oauth2 tokens from the authorization header
  3 | or BODY of the request
  4 | 
  5 | If parsing from the BODY there is adependency on the bodyParser plugin:
  6 | 
  7 | server.use(plugins.bodyParser());
  8 | server.use(plugins.oauth2TokenParser());
  9 | 
 10 | 
 11 | */
 12 | 'use strict';
 13 | 
 14 | var errors = require('restify-errors');
 15 | 
 16 | /*
 17 | 
 18 |   Parses the header for the authorization: bearer
 19 | 
 20 | */
 21 | function parseHeader(req) {
 22 |     if (req.headers && req.headers.authorization) {
 23 |         var credentialsIndex = 1;
 24 |         var parts = req.headers.authorization.split(' ');
 25 |         var partsExpectedLength = 2;
 26 |         var schemeIndex = 0;
 27 | 
 28 |         if (parts.length === partsExpectedLength) {
 29 |             var credentials = parts[credentialsIndex];
 30 |             var scheme = parts[schemeIndex];
 31 | 
 32 |             if (/^Bearer$/i.test(scheme)) {
 33 |                 return credentials;
 34 |             }
 35 |         }
 36 |     }
 37 | 
 38 |     return null;
 39 | }
 40 | 
 41 | /**
 42 |  * Returns a plugin that will parse the client's request for an OAUTH2
 43 |    access token
 44 |  *
 45 |  * Subsequent handlers will see `req.oauth2`, which looks like:
 46 |  *
 47 |  * ```js
 48 |  * {
 49 |  *   oauth2: {
 50 |         accessToken: 'mF_9.B5f-4.1JqM&p=q'
 51 |     }
 52 |  * }
 53 |  * ```
 54 |  *
 55 |  * @public
 56 |  * @function oauth2TokenParser
 57 |  * @throws   {InvalidArgumentError}
 58 |  * @param    {Object} options - an options object
 59 |  * @returns  {Function} Handler
 60 |  */
 61 | function oauth2TokenParser(options) {
 62 |     function parseOauth2Token(req, res, next) {
 63 |         req.oauth2 = { accessToken: null };
 64 | 
 65 |         var tokenFromHeader = parseHeader(req);
 66 | 
 67 |         if (tokenFromHeader) {
 68 |             req.oauth2.accessToken = tokenFromHeader;
 69 |         }
 70 | 
 71 |         var tokenFromBody = null;
 72 | 
 73 |         if (typeof req.body === 'object') {
 74 |             tokenFromBody = req.body.access_token;
 75 |         }
 76 | 
 77 |         // more than one method to transmit the token in each request
 78 |         // is not allowed - return 400
 79 |         if (tokenFromBody && tokenFromHeader) {
 80 |             // eslint-disable-next-line new-cap
 81 |             return next(
 82 |                 new errors.makeErrFromCode(400, 'multiple tokens disallowed')
 83 |             );
 84 |         }
 85 | 
 86 |         if (
 87 |             tokenFromBody &&
 88 |             req.contentType().toLowerCase() ===
 89 |                 'application/x-www-form-urlencoded'
 90 |         ) {
 91 |             req.oauth2.accessToken = tokenFromBody;
 92 |         }
 93 | 
 94 |         return next();
 95 |     }
 96 | 
 97 |     return parseOauth2Token;
 98 | }
 99 | 
100 | module.exports = oauth2TokenParser;
101 | 


--------------------------------------------------------------------------------
/lib/plugins/pre/context.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2018 Restify. All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var assert = require('assert-plus');
 6 | 
 7 | ///--- API
 8 | 
 9 | /**
10 |  * This plugin creates `req.set(key, val)` and `req.get(key)` methods for
11 |  * setting and retrieving request specific data.
12 |  *
13 |  * @public
14 |  * @function context
15 |  * @returns {Function} Handler
16 |  * @example
17 |  * server.pre(restify.plugins.pre.context());
18 |  * server.get('/', [
19 |  *     function(req, res, next) {
20 |  *         req.set(myMessage, 'hello world');
21 |  *         return next();
22 |  *     },
23 |  *     function two(req, res, next) {
24 |  *         res.send(req.get(myMessage)); // => sends 'hello world'
25 |  *         return next();
26 |  *     }
27 |  * ]);
28 |  */
29 | function ctx() {
30 |     return function context(req, res, next) {
31 |         var data = {};
32 | 
33 |         /**
34 |          * Set context value by key
35 |          * Requires the context plugin.
36 |          *
37 |          * @public
38 |          * @memberof Request
39 |          * @instance
40 |          * @function req.set
41 |          * @param    {String} key - key
42 |          * @param    {*} value - value
43 |          * @returns  {undefined} no return value
44 |          */
45 |         req.set = function set(key, value) {
46 |             assert.string(key, 'key must be string');
47 | 
48 |             if (key === '') {
49 |                 assert.fail('key must not be empty string');
50 |             }
51 |             data[key] = value;
52 |         };
53 | 
54 |         /**
55 |          * Get context value by key.
56 |          * Requires the context plugin.
57 |          *
58 |          * @public
59 |          * @memberof Request
60 |          * @instance
61 |          * @function req.get
62 |          * @param    {String} key - key
63 |          * @returns  {*} value stored in context
64 |          */
65 |         req.get = function get(key) {
66 |             assert.string(key, 'key must be string');
67 | 
68 |             if (key === '') {
69 |                 assert.fail('key must not be empty string');
70 |             }
71 |             return data[key];
72 |         };
73 | 
74 |         /**
75 |          * Get all context
76 |          * Requires the context plugin.
77 |          *
78 |          * @public
79 |          * @memberof Request
80 |          * @instance
81 |          * @function req.getAll
82 |          * @returns  {*} value stored in context
83 |          */
84 |         req.getAll = function getAll() {
85 |             return data;
86 |         };
87 | 
88 |         return next();
89 |     };
90 | }
91 | 
92 | ///--- Exports
93 | 
94 | module.exports = ctx;
95 | 


--------------------------------------------------------------------------------
/lib/plugins/pre/dedupeSlashes.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | /**
 4 |  * This plugin deduplicates extra slashes found in the URL. This can help with
 5 |  * malformed URLs that might otherwise get misrouted.
 6 |  *
 7 |  * @public
 8 |  * @function dedupeSlashes
 9 |  * @returns {Function} Handler
10 |  * @example
11 |  * server.pre(restify.plugins.pre.dedupeSlashes());
12 |  * server.get('/hello/:one', function(req, res, next) {
13 |  *     res.send(200);
14 |  *     return next();
15 |  * });
16 |  *
17 |  * // the server will now convert requests to /hello//jake => /hello/jake
18 |  */
19 | function createDedupeSlashes() {
20 |     return function dedupeSlashes(req, res, next) {
21 |         req.url = req.url.replace(/(\/)\/+/g, '$1');
22 |         return next();
23 |     };
24 | }
25 | 
26 | module.exports = createDedupeSlashes;
27 | 


--------------------------------------------------------------------------------
/lib/plugins/pre/pause.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | ///--- Helpers
 6 | 
 7 | /**
 8 |  * @private
 9 |  * @function pauseStream
10 |  * @param    {Stream} stream - the stream to pause
11 |  * @returns  {undefined} no return value
12 |  */
13 | function pauseStream(stream) {
14 |     function _buffer(chunk) {
15 |         stream.__buffered.push(chunk);
16 |     }
17 | 
18 |     function _catchEnd(chunk) {
19 |         stream.__rstfyEnded = true;
20 |     }
21 | 
22 |     stream.__rstfyEnded = false;
23 |     stream.__rstfyPaused = true;
24 |     stream.__buffered = [];
25 |     stream.on('data', _buffer);
26 |     stream.once('end', _catchEnd);
27 |     stream.pause();
28 | 
29 |     stream._resume = stream.resume;
30 |     stream.resume = function _rstfy_resume() {
31 |         if (!stream.__rstfyPaused) {
32 |             return;
33 |         }
34 | 
35 |         stream.removeListener('data', _buffer);
36 |         stream.removeListener('end', _catchEnd);
37 | 
38 |         stream.__buffered.forEach(stream.emit.bind(stream, 'data'));
39 |         stream.__buffered.length = 0;
40 | 
41 |         stream._resume();
42 |         stream.resume = stream._resume;
43 | 
44 |         if (stream.__rstfyEnded) {
45 |             stream.emit('end');
46 |         }
47 |     };
48 | }
49 | 
50 | /**
51 |  * This pre handler fixes issues with node hanging when an `asyncHandler` is
52 |  * used prior to `bodyParser`.
53 |  * https://github.com/restify/node-restify/issues/287
54 |  * https://github.com/restify/node-restify/issues/409
55 |  * https://github.com/restify/node-restify/wiki/1.4-to-2.0-Migration-Tips
56 |  *
57 |  * @public
58 |  * @function pause
59 |  * @returns  {Function} Handler
60 |  */
61 | function pause() {
62 |     function prePause(req, res, next) {
63 |         pauseStream(req);
64 |         next();
65 |     }
66 | 
67 |     return prePause;
68 | }
69 | 
70 | ///--- Exports
71 | 
72 | module.exports = pause;
73 | 


--------------------------------------------------------------------------------
/lib/plugins/pre/prePath.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | ///--- Helpers
 6 | 
 7 | /**
 8 |  * Cleans up sloppy URLs on the request object, like /foo////bar/// to /foo/bar.
 9 |  *
10 |  * @private
11 |  * @function strip
12 |  * @param    {Object} path - a url path to clean up
13 |  * @returns  {String} cleaned path
14 |  */
15 | function strip(path) {
16 |     var cur;
17 |     var next;
18 |     var str = '';
19 | 
20 |     for (var i = 0; i < path.length; i++) {
21 |         cur = path.charAt(i);
22 | 
23 |         if (i !== path.length - 1) {
24 |             next = path.charAt(i + 1);
25 |         }
26 | 
27 |         if (cur === '/' && (next === '/' || (next === '?' && i > 0))) {
28 |             continue;
29 |         }
30 | 
31 |         str += cur;
32 |     }
33 | 
34 |     return str;
35 | }
36 | 
37 | /**
38 |  * Cleans up sloppy URLs on the request object,
39 |  * like `/foo////bar///` to `/foo/bar`.
40 |  *
41 |  * @public
42 |  * @function sanitizePath
43 |  * @returns  {Function} Handler
44 |  */
45 | function sanitizePath() {
46 |     function _sanitizePath(req, res, next) {
47 |         req.url = strip(req.url);
48 |         next();
49 |     }
50 | 
51 |     return _sanitizePath;
52 | }
53 | 
54 | ///--- Exports
55 | 
56 | module.exports = sanitizePath;
57 | 


--------------------------------------------------------------------------------
/lib/plugins/pre/reqIdHeaders.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var assert = require('assert-plus');
 4 | 
 5 | var DEFAULT_HEADERS = ['request-id', 'x-request-id'];
 6 | 
 7 | /**
 8 |  * This plugin pulls the value from an incoming request header and uses it
 9 |  * as the value of the request id. Subsequent calls to `req.id()`
10 |  * will return the header values.
11 |  *
12 |  * @public
13 |  * @function reqIdHeaders
14 |  * @param {Object}   opts - an options object
15 |  * @param {String[]} opts.headers - array of headers from where to pull existing
16 |  *                                request id headers. Lookup precedence
17 |  *                                is left to right (lowest index first)
18 |  * @returns {Function} Handler
19 |  */
20 | function createReqIdHeaders(opts) {
21 |     assert.object(opts, 'opts');
22 |     assert.arrayOfString(opts.headers, 'opts.headers');
23 | 
24 |     var headers = opts.headers.concat(DEFAULT_HEADERS);
25 | 
26 |     return function reqIdHeaders(req, res, next) {
27 |         for (var i = 0; i < headers.length; i++) {
28 |             var val = req.header(headers[i]);
29 | 
30 |             if (val) {
31 |                 req.id(val);
32 |                 break;
33 |             }
34 |         }
35 | 
36 |         return next();
37 |     };
38 | }
39 | 
40 | ///--- Exports
41 | 
42 | module.exports = createReqIdHeaders;
43 | 


--------------------------------------------------------------------------------
/lib/plugins/pre/strictQueryParams.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var BadRequestError = require('restify-errors').BadRequestError;
 4 | var assert = require('assert-plus');
 5 | 
 6 | ///--- API
 7 | 
 8 | /**
 9 |  * Prevents `req.urls` non-strict key-value query params
10 |  *
11 |  * The Request-URI is transmitted in the format specified in section 3.2.1.
12 |  * If the Request-URI is encoded using the "% HEX HEX" encoding [42],
13 |  * the origin server MUST decode the Request-URI
14 |  * in order to properly interpret the request.
15 |  * Servers SHOULD respond to invalid Request-URIs
16 |  * with an appropriate status code.
17 |  *
18 |  * part of Hypertext Transfer Protocol -- HTTP/1.1 | 5.1.2 Request-URI
19 |  * RFC 2616 Fielding, et al.
20 |  *
21 |  * @public
22 |  * @function strictQueryParams
23 |  * @param    {Object}   [options] - an options object
24 |  * @param    {String}   [options.message] - a custom error message
25 |  *                              default value:
26 |  *                              "Url query params does not meet strict format"
27 |  * @returns  {Function} Handler
28 |  */
29 | function strictQueryParams(options) {
30 |     var opts = options || {};
31 |     assert.optionalObject(opts, 'options');
32 |     assert.optionalString(opts.message, 'options.message');
33 | 
34 |     function _strictQueryParams(req, res, next) {
35 |         var keyValQParams = !/(\&(?!(\w+=\w+)))/.test(req.url);
36 | 
37 |         if (!keyValQParams) {
38 |             var msg = opts.message
39 |                 ? opts.message
40 |                 : 'Url query params does not meet strict format';
41 |             return next(new BadRequestError(msg));
42 |         }
43 | 
44 |         return next();
45 |     }
46 | 
47 |     return _strictQueryParams;
48 | }
49 | 
50 | ///--- Exports
51 | 
52 | module.exports = strictQueryParams;
53 | 


--------------------------------------------------------------------------------
/lib/plugins/pre/userAgent.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var assert = require('assert-plus');
 6 | 
 7 | ///--- API
 8 | 
 9 | /**
10 |  * This basically exists for `curl`. `curl` on `HEAD` requests usually
11 |  * just sits there and hangs, unless you explicitly set
12 |  * Connection:close. And in general, you probably want to set
13 |  * Connection: close to curl anyway.
14 |  *
15 |  * Also, because curl spits out an annoying message to stderr about
16 |  * remaining bytes if content-length is set, this plugin also drops
17 |  * the `content-length` header (some user agents handle it and want it,
18 |  * curl does not).
19 |  *
20 |  * To be slightly more generic, the options block takes a user
21 |  * agent regexp, however.
22 |  *
23 |  * @public
24 |  * @function userAgentConnection
25 |  * @param    {Object} [options] - an options object
26 |  * @param    {RegExp} [options.userAgentRegExp=/^curl.+/] - matching any
27 |  *                                                        user-agents applicable
28 |  * @returns  {Function} Handler
29 |  */
30 | function userAgentConnection(options) {
31 |     var opts = options || {};
32 |     assert.optionalObject(opts, 'options');
33 |     assert.optionalObject(opts.userAgentRegExp, 'options.userAgentRegExp');
34 | 
35 |     var re = opts.userAgentRegExp;
36 | 
37 |     if (!re) {
38 |         re = /^curl.+/;
39 |     }
40 | 
41 |     function handleUserAgent(req, res, next) {
42 |         var ua = req.headers['user-agent'];
43 | 
44 |         if (ua && re.test(ua)) {
45 |             res.setHeader('Connection', 'close');
46 | 
47 |             if (req.method === 'HEAD') {
48 |                 res.once(
49 |                     'header',
50 |                     res.removeHeader.bind(res, 'content-length')
51 |                 );
52 |             }
53 |         }
54 | 
55 |         next();
56 |     }
57 | 
58 |     return handleUserAgent;
59 | }
60 | 
61 | module.exports = userAgentConnection;
62 | 


--------------------------------------------------------------------------------
/lib/plugins/query.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var qs = require('qs');
 6 | var assert = require('assert-plus');
 7 | 
 8 | /**
 9 |  * Parses the HTTP query string (i.e., `/foo?id=bar&name=mark`).
10 |  * If you use this, the parsed content will always be available in `req.query`,
11 |  * additionally params are merged into `req.params`.
12 |  * You can disable by passing in `mapParams: false` in the options object.
13 |  *
14 |  * Many options correspond directly to option defined for the underlying
15 |  * [`qs.parse`](https://github.com/ljharb/qs).
16 |  *
17 |  * @public
18 |  * @function queryParser
19 |  * @param    {Object}   [options] - an options object
20 |  * @param    {Object}   [options.mapParams=true] - disable passing
21 |  * @param {Boolean} [options.mapParams=false] - Copies parsed query parameters
22 |  * into`req.params`.
23 |  * @param {Boolean} [options.overrideParams=false] - Only applies when if
24 |  * mapParams true.
25 |  * When true, will stomp on req.params field when existing value is found.
26 |  * @param {Boolean} [options.allowDots=false] - Transform `?foo.bar=baz` to a
27 |  * nested object: `{foo: {bar: 'baz'}}`.
28 |  * @param {Number} [options.arrayLimit=20] - Only transform `?a[$index]=b`
29 |  * to an array if `$index` is less than `arrayLimit`.
30 |  * @param {Number} [options.depth=5] - The depth limit for parsing
31 |  * nested objects, e.g. `?a[b][c][d][e][f][g][h][i]=j`.
32 |  * @param {Number} [options.parameterLimit=1000] - Maximum number of query
33 |  * params parsed. Additional params are silently dropped.
34 |  * @param {Boolean} [options.parseArrays=true] - Whether to parse
35 |  * `?a[]=b&a[1]=c` to an array, e.g. `{a: ['b', 'c']}`.
36 |  * @param {Boolean} [options.plainObjects=false] - Whether `req.query` is a
37 |  * "plain" object -- does not inherit from `Object`.
38 |  * This can be used to allow query params whose names collide with Object
39 |  * methods, e.g. `?hasOwnProperty=blah`.
40 |  * @param {Boolean} [options.strictNullHandling=false] - If true, `?a&b=`
41 |  * results in `{a: null, b: ''}`. Otherwise, `{a: '', b: ''}`.
42 |  * @returns  {Function} Handler
43 |  * @example
44 |  * server.use(restify.plugins.queryParser({ mapParams: false }));
45 |  */
46 | function queryParser(options) {
47 |     var opts = options || {};
48 |     assert.object(opts, 'opts');
49 | 
50 |     function parseQueryString(req, res, next) {
51 |         if (!req.getQuery()) {
52 |             req.query = {};
53 |             return next();
54 |         }
55 | 
56 |         req.query = qs.parse(req.getQuery(), opts);
57 | 
58 |         if (opts.mapParams === true) {
59 |             Object.keys(req.query).forEach(function forEach(k) {
60 |                 if (req.params[k] && !opts.overrideParams) {
61 |                     return;
62 |                 }
63 |                 req.params[k] = req.query[k];
64 |             });
65 |         }
66 | 
67 |         return next();
68 |     }
69 | 
70 |     return parseQueryString;
71 | }
72 | 
73 | module.exports = queryParser;
74 | 


--------------------------------------------------------------------------------
/lib/plugins/requestLogger.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | var assert = require('assert-plus');
 6 | 
 7 | var shallowCopy = require('./utils/shallowCopy');
 8 | 
 9 | ///--- API
10 | 
11 | /**
12 |  * Sets up a child [logger](https://github.com/pinojs/pino) logger with
13 |  * the current request id filled in, along with any other parameters you define.
14 |  *
15 |  * You can pass in no options to this, in which case only the request id will be
16 |  * appended, and no serializers appended (this is also the most performant); the
17 |  * logger created at server creation time will be used as the parent logger.
18 |  * This logger can be used normally, with [req.log](#request-api).
19 |  *
20 |  * This plugin does _not_ log each individual request. Use the Audit Logging
21 |  * plugin or a custom middleware for that use.
22 |  *
23 |  * @public
24 |  * @function requestLogger
25 |  * @param    {Object}   [options] - an options object
26 |  * @param    {Array}    [options.headers] - A list of headers to transfer from
27 |  *                                  the request to top level props on the log.
28 |  * @param    {Object}   [options.properties] - A set of key-values to pass to the child logger
29 |  * @param    {Object}   [options.serializers] - Override serializers to use in the child logger
30 |  * @param    {Object}   [options.log] - A logger to use as a fallback if req.log is missing
31 |  * @param    {String}   [options.requestIdFieldName] - The name of the request id property attached
32 |  *                                   to log lines. Defaults to "req_id".
33 |  * @returns  {Function} Handler
34 |  * @example
35 |  * server.use(restify.plugins.requestLogger({
36 |  *     properties: {
37 |  *         foo: 'bar'
38 |  *     },
39 |  *     serializers: {...}
40 |  * }));
41 |  */
42 | function requestLogger(options) {
43 |     assert.optionalObject(options);
44 |     var opts = options || {};
45 | 
46 |     var props;
47 | 
48 |     if (opts.properties) {
49 |         props = shallowCopy(opts.properties);
50 |     } else {
51 |         props = {};
52 |     }
53 | 
54 |     if (opts.serializers) {
55 |         props.serializers = opts.serializers;
56 |     }
57 | 
58 |     var headersToCopy = opts.headers || [];
59 |     const requestIdFieldName = opts.requestIdFieldName || 'req_id';
60 | 
61 |     return function logger(req, res, next) {
62 |         if (!req.log && !opts.log) {
63 |             next();
64 |             return;
65 |         }
66 | 
67 |         var log = req.log || opts.log;
68 | 
69 |         props[requestIdFieldName] = req.getId();
70 | 
71 |         headersToCopy.forEach(function forEach(k) {
72 |             if (req.headers[k]) {
73 |                 props[k] = req.headers[k];
74 |             }
75 |         });
76 |         const childOptions = {};
77 |         if (props.serializers) {
78 |             childOptions.serializers = props.serializers;
79 |         }
80 |         req.log = log.child(props, childOptions);
81 | 
82 |         if (props[requestIdFieldName]) {
83 |             delete props[requestIdFieldName];
84 |         }
85 | 
86 |         next();
87 |     };
88 | }
89 | 
90 | ///--- Exports
91 | 
92 | module.exports = requestLogger;
93 | 


--------------------------------------------------------------------------------
/lib/plugins/utils/hrTimeDurationInMs.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | var NS_PER_SEC = 1e9;
 4 | var MS_PER_NS = 1e6;
 5 | 
 6 | /**
 7 | * Get duration in milliseconds from two process.hrtime()
 8 | 
 9 | * @function hrTimeDurationInMs
10 | * @param {Array} startTime - [seconds, nanoseconds]
11 | * @param {Array} endTime - [seconds, nanoseconds]
12 | * @returns {Number|null} durationInMs
13 | */
14 | function hrTimeDurationInMs(startTime, endTime) {
15 |     if (!Array.isArray(startTime) || !Array.isArray(endTime)) {
16 |         return null;
17 |     }
18 | 
19 |     var secondDiff = endTime[0] - startTime[0];
20 |     var nanoSecondDiff = endTime[1] - startTime[1];
21 |     var diffInNanoSecond = secondDiff * NS_PER_SEC + nanoSecondDiff;
22 | 
23 |     return Math.round(diffInNanoSecond / MS_PER_NS);
24 | }
25 | 
26 | module.exports = hrTimeDurationInMs;
27 | 


--------------------------------------------------------------------------------
/lib/plugins/utils/httpDate.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | /**
 6 |  * Takes an instance of a date object, formats it UTC
 7 |  * e.g., Wed, 17 Jun 2015 01:30:26 GMT.
 8 |  *
 9 |  * @public
10 |  * @function httpDate
11 |  * @param    {Object} now - a date object
12 |  * @returns  {String}       formatted dated object
13 |  */
14 | module.exports = function httpDate(now) {
15 |     return now.toUTCString();
16 | };
17 | 


--------------------------------------------------------------------------------
/lib/plugins/utils/regex.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | 
3 | module.exports = {
4 |     jsonContentType: new RegExp('^application/[a-zA-Z.]+\\+json')
5 | };
6 | 


--------------------------------------------------------------------------------
/lib/plugins/utils/shallowCopy.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | /**
 6 |  * Return a shallow copy of the given object
 7 |  *
 8 |  * @public
 9 |  * @function  shallowCopy
10 |  * @param   {Object} obj - the object to copy
11 |  * @returns {Object}       the new copy of the object
12 |  */
13 | function shallowCopy(obj) {
14 |     if (!obj) {
15 |         return obj;
16 |     }
17 |     var copy = {};
18 |     Object.keys(obj).forEach(function forEach(k) {
19 |         copy[k] = obj[k];
20 |     });
21 |     return copy;
22 | }
23 | 
24 | ///--- Exports
25 | 
26 | module.exports = shallowCopy;
27 | 


--------------------------------------------------------------------------------
/lib/routerRegistryRadix.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | 
  3 | var assert = require('assert-plus');
  4 | var FindMyWay = require('find-my-way');
  5 | var Chain = require('./chain');
  6 | 
  7 | /**
  8 |  * Radix tree based router registry backed by `find-my-way`
  9 |  *
 10 |  * @class RouterRegistryRadix
 11 |  * @public
 12 |  * @param  {Object} options - an options object
 13 |  * @param {Object} [options.ignoreTrailingSlash] - ignore trailing slash on
 14 |  *  paths
 15 |  */
 16 | function RouterRegistryRadix(options) {
 17 |     this._findMyWay = new FindMyWay(options);
 18 |     this._routes = {};
 19 | }
 20 | 
 21 | /**
 22 |  * Adds a route.
 23 |  *
 24 |  * @public
 25 |  * @memberof Router
 26 |  * @instance
 27 |  * @function add
 28 |  * @param    {Object} route - an route object
 29 |  * @param    {String} route.name - name of the route
 30 |  * @param    {String} route.method - HTTP method
 31 |  * @param    {String} route.path - any String accepted by
 32 |  * [find-my-way](https://github.com/delvedor/find-my-way)
 33 |  * @param    {Chain} route.chain - Chain instance
 34 |  * @returns  {Boolean} true
 35 |  */
 36 | RouterRegistryRadix.prototype.add = function add(route) {
 37 |     assert.object(route, 'route');
 38 |     assert.string(route.method, 'route.method');
 39 |     assert.string(route.path, 'path');
 40 |     assert.ok(route.chain instanceof Chain, 'route.chain');
 41 | 
 42 |     this._findMyWay.on(
 43 |         route.method,
 44 |         route.path,
 45 |         function onRoute(req, res, next) {
 46 |             route.chain.run(req, res, next);
 47 |         },
 48 |         {
 49 |             route: route
 50 |         }
 51 |     );
 52 | 
 53 |     this._routes[route.name] = route;
 54 | 
 55 |     return route;
 56 | };
 57 | 
 58 | /**
 59 |  * Removes a route.
 60 |  *
 61 |  * @public
 62 |  * @memberof RouterRegistryRadix
 63 |  * @instance
 64 |  * @function remove
 65 |  * @param    {String} name - the route name
 66 |  * @returns  {Object|undefined} removed route if found
 67 |  */
 68 | RouterRegistryRadix.prototype.remove = function remove(name) {
 69 |     assert.string(name, 'name');
 70 | 
 71 |     // check for route
 72 |     var route = this._routes[name];
 73 |     if (!route) {
 74 |         return undefined;
 75 |     }
 76 | 
 77 |     // remove from registry
 78 |     this._findMyWay.off(route.method, route.path);
 79 |     delete this._routes[name];
 80 | 
 81 |     return route;
 82 | };
 83 | 
 84 | /**
 85 |  * Registry for route
 86 |  *
 87 |  * @public
 88 |  * @memberof RouterRegistryRadix
 89 |  * @instance
 90 |  * @function Registry
 91 |  * @param  {String} method - method
 92 |  * @param  {String} pathname - pathname
 93 |  * @returns {Chain|undefined} handler or undefined
 94 |  */
 95 | RouterRegistryRadix.prototype.lookup = function lookup(method, pathname) {
 96 |     assert.string(method, 'method');
 97 |     assert.string(pathname, 'pathname');
 98 | 
 99 |     var fmwRoute = this._findMyWay.find(method, pathname);
100 | 
101 |     // Not found
102 |     if (!fmwRoute) {
103 |         return undefined;
104 |     }
105 | 
106 |     // Call handler chain
107 |     return {
108 |         route: fmwRoute.store.route,
109 |         params: fmwRoute.params,
110 |         handler: fmwRoute.handler
111 |     };
112 | };
113 | 
114 | /**
115 |  * Get registry
116 |  *
117 |  * @public
118 |  * @memberof RouterRegistryRadix
119 |  * @instance
120 |  * @function toString
121 |  * @returns  {String} stringified RouterRegistryRadix
122 |  */
123 | RouterRegistryRadix.prototype.get = function get() {
124 |     return this._routes;
125 | };
126 | 
127 | /**
128 |  * toString() serialization.
129 |  *
130 |  * @public
131 |  * @memberof RouterRegistryRadix
132 |  * @instance
133 |  * @function toString
134 |  * @returns  {String} stringified RouterRegistryRadix
135 |  */
136 | RouterRegistryRadix.prototype.toString = function toString() {
137 |     return this._findMyWay.prettyPrint();
138 | };
139 | 
140 | module.exports = RouterRegistryRadix;
141 | 


--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | 
 5 | /**
 6 |  * Return a shallow copy of the given object;
 7 |  *
 8 |  * @public
 9 |  * @function  shallowCopy
10 |  * @param   {Object} obj - the object to copy
11 |  * @returns {Object}     the new copy of the object
12 |  */
13 | function shallowCopy(obj) {
14 |     if (!obj) {
15 |         return obj;
16 |     }
17 |     var copy = {};
18 |     Object.keys(obj).forEach(function forEach(k) {
19 |         copy[k] = obj[k];
20 |     });
21 |     return copy;
22 | }
23 | 
24 | /**
25 |  * Merges two query parameter objects. Merges to array
26 |  * if the same key is encountered.
27 |  *
28 |  * @public
29 |  * @function  mergeQs
30 |  * @param   {Object} obj1 - first qs object
31 |  * @param   {Object} obj2 - second qs object
32 |  * @returns {Object}        the merged object
33 |  */
34 | function mergeQs(obj1, obj2) {
35 |     var merged = shallowCopy(obj1) || {};
36 | 
37 |     // defend against null cause null is an object. yay js.
38 |     if (obj2 && typeof obj2 === 'object') {
39 |         Object.keys(obj2).forEach(function forEach(key) {
40 |             // if we already have this key and it isn't an array,
41 |             // make it one array of the same element.
42 |             if (merged.hasOwnProperty(key) && !(merged[key] instanceof Array)) {
43 |                 merged[key] = [merged[key]];
44 | 
45 |                 // push the new value down
46 |                 merged[key].push(obj2[key]);
47 |             } else {
48 |                 // otherwise just set it
49 |                 merged[key] = obj2[key];
50 |             }
51 |         });
52 |     }
53 | 
54 |     return merged;
55 | }
56 | 
57 | ///--- Exports
58 | 
59 | module.exports = {
60 |     shallowCopy: shallowCopy,
61 |     mergeQs: mergeQs
62 | };
63 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
  1 | {
  2 |   "author": "Mark Cavage <mcavage@gmail.com>",
  3 |   "contributors": [
  4 |     "Adam Argo",
  5 |     "Alex Liu",
  6 |     "Alexander Olsson",
  7 |     "Andrew Robinson",
  8 |     "Andrew Sliwinski",
  9 |     "Anro Robinson",
 10 |     "Armin Tamzarian",
 11 |     "Asa Ayers",
 12 |     "Bastiaan Marinus van de Weerd",
 13 |     "Ben Doerr",
 14 |     "Ben Hale",
 15 |     "Ben Howes",
 16 |     "Ben Hutchison",
 17 |     "Benjamine Coe",
 18 |     "Benjamin Urban",
 19 |     "Blake VanLandingham",
 20 |     "Brian Pin",
 21 |     "Bryan Donovan",
 22 |     "Bryce Kahle",
 23 |     "Christopher Cannell",
 24 |     "Clément Désiles",
 25 |     "Colin O'Brien",
 26 |     "Corbin Uselton",
 27 |     "Diego Torres",
 28 |     "Domenic Denicola",
 29 |     "Domikik Lessel",
 30 |     "Dominic Barnes",
 31 |     "Erik Kristensen",
 32 |     "Falco Nogatz",
 33 |     "Gergely Nemeth",
 34 |     "Guillaume Chauvet",
 35 |     "Ifiok Idiang",
 36 |     "Isaac Schlueter",
 37 |     "Jacob Quatier",
 38 |     "James O'Cull",
 39 |     "James Womack",
 40 |     "Jonathan Dahan",
 41 |     "Josh Clulow",
 42 |     "Jorge Serrano",
 43 |     "Jason Ghent",
 44 |     "Khaja Naquiuddin",
 45 |     "Lou Sacco",
 46 |     "Matt Smillie",
 47 |     "Mattijs Spierings",
 48 |     "Micah Ransdell",
 49 |     "Michal Moskal",
 50 |     "Michael Paulson",
 51 |     "Mike Williams",
 52 |     "Nathanael Anderson",
 53 |     "Patrick Mooney",
 54 |     "Paul Bouzakis",
 55 |     "Pedro Palazón",
 56 |     "Quentin Buathier",
 57 |     "Richardo Stuven",
 58 |     "Scott Turnquest",
 59 |     "Shaun Berryman",
 60 |     "Steve Mason",
 61 |     "Tim Kuijsten",
 62 |     "Trent Mick",
 63 |     "Tuure Kanuisto",
 64 |     "Will Prater",
 65 |     "Yunong Xiao",
 66 |     "Zachary Snow"
 67 |   ],
 68 |   "name": "restify",
 69 |   "homepage": "http://restify.com",
 70 |   "description": "REST framework",
 71 |   "keywords": [
 72 |     "REST",
 73 |     "framework",
 74 |     "express",
 75 |     "DTrace"
 76 |   ],
 77 |   "version": "11.2.0",
 78 |   "repository": {
 79 |     "type": "git",
 80 |     "url": "git://github.com/restify/node-restify.git"
 81 |   },
 82 |   "bugs": {
 83 |     "url": "https://github.com/restify/node-restify/issues"
 84 |   },
 85 |   "main": "lib/index.js",
 86 |   "directories": {
 87 |     "lib": "./lib"
 88 |   },
 89 |   "bin": {
 90 |     "report-latency": "./bin/report-latency"
 91 |   },
 92 |   "engines": {
 93 |     "node": ">=10.0.0"
 94 |   },
 95 |   "dependencies": {
 96 |     "assert-plus": "^1.0.0",
 97 |     "csv": "^6.2.2",
 98 |     "escape-regexp-component": "^1.0.2",
 99 |     "ewma": "^2.0.1",
100 |     "find-my-way": "^7.6.0",
101 |     "formidable": "^1.2.1",
102 |     "http-signature": "^1.3.6",
103 |     "lodash": "^4.17.11",
104 |     "lru-cache": "^7.14.1",
105 |     "mime": "^3.0.0",
106 |     "negotiator": "^0.6.2",
107 |     "once": "^1.4.0",
108 |     "pidusage": "^3.0.2",
109 |     "pino": "^8.7.0",
110 |     "qs": "^6.7.0",
111 |     "restify-errors": "^8.0.2",
112 |     "semver": "^7.3.8",
113 |     "send": "^0.18.0",
114 |     "spdy": "^4.0.0",
115 |     "uuid": "^9.0.0",
116 |     "vasync": "^2.2.0"
117 |   },
118 |   "optionalDependencies": {
119 |     "dtrace-provider": "~0.8"
120 |   },
121 |   "devDependencies": {
122 |     "autocannon": "^4.0.0",
123 |     "autocannon-compare": "^0.4.0",
124 |     "chai": "^4.2.0",
125 |     "coveralls": "^3.0.3",
126 |     "documentation": "^11.0.0",
127 |     "eslint": "^5.16.0",
128 |     "eslint-config-prettier": "^4.3.0",
129 |     "eslint-plugin-jsdoc": "^3.15.1",
130 |     "eslint-plugin-prettier": "^3.1.0",
131 |     "glob": "^7.1.4",
132 |     "inquirer": "^3.3.0",
133 |     "mkdirp": "^0.5.1",
134 |     "mocha": "^7.1.1",
135 |     "nodeunit": "^0.11.3",
136 |     "nyc": "^15.0.0",
137 |     "ora": "^1.3.0",
138 |     "pre-commit": "^1.2.2",
139 |     "prettier": "^1.17.1",
140 |     "proxyquire": "^1.8.0",
141 |     "restify-clients": "^2.6.6",
142 |     "rimraf": "^2.6.3",
143 |     "sinon": "^7.5.0",
144 |     "validator": "^7.2.0",
145 |     "watershed": "^0.4.0"
146 |   },
147 |   "license": "MIT",
148 |   "scripts": {
149 |     "test": "make prepush"
150 |   }
151 | }
152 | 


--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |     "rules": {
3 |         "handle-callback-err": [ 0 ]
4 |     }
5 | }
6 | 


--------------------------------------------------------------------------------
/test/chainComposer.test.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | /* eslint-disable func-names */
 3 | 
 4 | if (require.cache[__dirname + '/lib/helper.js']) {
 5 |     delete require.cache[__dirname + '/lib/helper.js'];
 6 | }
 7 | var helper = require('./lib/helper.js');
 8 | 
 9 | ///--- Globals
10 | 
11 | var test = helper.test;
12 | var composer = require('../lib/helpers/chainComposer');
13 | 
14 | test('chainComposer creates a valid chain for a handler array ', function(t) {
15 |     var counter = 0;
16 |     var handlers = [];
17 |     handlers.push(function(req, res, next) {
18 |         counter++;
19 |         next();
20 |     });
21 | 
22 |     handlers.push(function(req, res, next) {
23 |         counter++;
24 |         next();
25 |     });
26 | 
27 |     var chain = composer(handlers);
28 |     chain(
29 |         {
30 |             startHandlerTimer: function() {},
31 |             endHandlerTimer: function() {},
32 |             connectionState: function() {
33 |                 return '';
34 |             }
35 |         },
36 |         {},
37 |         function() {
38 |             t.equal(counter, 2);
39 |             t.done();
40 |         }
41 |     );
42 | });
43 | 
44 | test('chainComposer creates a valid chain for a single handler', function(t) {
45 |     var counter = 0;
46 |     var handlers = function(req, res, next) {
47 |         counter++;
48 |         next();
49 |     };
50 | 
51 |     var chain = composer(handlers);
52 |     chain(
53 |         {
54 |             startHandlerTimer: function() {},
55 |             endHandlerTimer: function() {},
56 |             connectionState: function() {
57 |                 return '';
58 |             }
59 |         },
60 |         {},
61 |         function() {
62 |             t.equal(counter, 1);
63 |             t.done();
64 |         }
65 |     );
66 | });
67 | 


--------------------------------------------------------------------------------
/test/formatter-optional.test.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | /* eslint-disable func-names */
 3 | 
 4 | var restifyClients = require('restify-clients');
 5 | 
 6 | var restify = require('../lib');
 7 | 
 8 | if (require.cache[__dirname + '/lib/helper.js']) {
 9 |     delete require.cache[__dirname + '/lib/helper.js'];
10 | }
11 | var helper = require('./lib/helper.js');
12 | 
13 | ///--- Globals
14 | 
15 | var after = helper.after;
16 | var before = helper.before;
17 | var test = helper.test;
18 | 
19 | var CLIENT;
20 | var LOCALHOST;
21 | var PORT = process.env.UNIT_TEST_PORT || 0;
22 | var SERVER;
23 | 
24 | ///--- Tests
25 | 
26 | before(function(callback) {
27 |     try {
28 |         SERVER = restify.createServer({
29 |             handleUncaughtExceptions: true,
30 |             log: helper.getLog('server'),
31 |             strictFormatters: false
32 |         });
33 |         SERVER.listen(PORT, '127.0.0.1', function() {
34 |             PORT = SERVER.address().port;
35 |             CLIENT = restifyClients.createJsonClient({
36 |                 url: 'http://127.0.0.1:' + PORT,
37 |                 dtrace: helper.dtrace,
38 |                 retry: false
39 |             });
40 |             LOCALHOST = 'http://' + '127.0.0.1:' + PORT;
41 |             callback();
42 |         });
43 |     } catch (e) {
44 |         console.error(e.stack);
45 |         process.exit(1);
46 |     }
47 | });
48 | 
49 | after(function(callback) {
50 |     try {
51 |         SERVER.close(callback);
52 |         CLIENT.close();
53 |     } catch (e) {
54 |         console.error(e.stack);
55 |         process.exit(1);
56 |     }
57 | });
58 | 
59 | test('send 200 on formatter missing and strictFormatters false', function(t) {
60 |     // When server is passed "strictFormatters: false" at creation time,
61 |     // res.send still sends a successful response even when a formatter is
62 |     // not set up for a specific content-type.
63 |     SERVER.get('/11', function handle(req, res, next) {
64 |         res.header('content-type', 'application/hal+json');
65 |         res.send(200, JSON.stringify({ hello: 'world' }));
66 |         return next();
67 |     });
68 | 
69 |     CLIENT.get(LOCALHOST + '/11', function(err, _, res) {
70 |         t.ifError(err);
71 |         t.equal(res.statusCode, 200);
72 |         t.equal(res.headers['content-type'], 'application/hal+json');
73 |         t.end();
74 |     });
75 | });
76 | 


--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 2 | 
 3 | 'use strict';
 4 | /* eslint-disable func-names */
 5 | 
 6 | var httpDate = require('../lib/http_date');
 7 | 
 8 | if (require.cache[__dirname + '/lib/helper.js']) {
 9 |     delete require.cache[__dirname + '/lib/helper.js'];
10 | }
11 | var helper = require('./lib/helper.js');
12 | 
13 | ///--- Globals
14 | 
15 | var test = helper.test;
16 | 
17 | ///--- Tests
18 | 
19 | test('httpDate', function(t) {
20 |     var d = httpDate();
21 |     var regex = /\w{3}, \d{1,2} \w{3} \d{4} \d{2}:\d{2}:\d{2} GMT/;
22 |     t.ok(regex.test(d));
23 |     t.end();
24 | });
25 | 


--------------------------------------------------------------------------------
/test/keys/http2-cert.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE-----
 2 | MIICHzCCAYgCCQCPPSUAa8QZojANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJS
 3 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTENMAsGA1UEBxMET21zazEhMB8GA1UEChMY
 4 | SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTExMDQwOTEwMDY0NVoXDTExMDUw
 5 | OTEwMDY0NVowVDELMAkGA1UEBhMCUlUxEzARBgNVBAgTClNvbWUtU3RhdGUxDTAL
 6 | BgNVBAcTBE9tc2sxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCB
 7 | nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1bn25sPkv46wl70BffxradlkRd/x
 8 | p5Xf8HDhPSfzNNctERYslXT2fX7Dmfd5w1XTVqqGqJ4izp5VewoVOHA8uavo3ovp
 9 | gNWasil5zADWaM1T0nnV0RsFbZWzOTmm1U3D48K8rW3F5kOZ6f4yRq9QT1gF/gN7
10 | 5Pt494YyYyJu/a8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBuRZisIViI2G/R+w79
11 | vk21TzC/cJ+O7tKsseDqotXYTH8SuimEH5IWcXNgnWhNzczwN8s2362NixyvCipV
12 | yd4wzMpPbjIhnWGM0hluWZiK2RxfcqimIBjDParTv6CMUIuwGQ257THKY8hXGg7j
13 | Uws6Lif3P9UbsuRiYPxMgg98wg==
14 | -----END CERTIFICATE-----
15 | 
16 | 


--------------------------------------------------------------------------------
/test/keys/http2-csr.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN CERTIFICATE REQUEST-----
 2 | MIIBkzCB/QIBADBUMQswCQYDVQQGEwJSVTETMBEGA1UECBMKU29tZS1TdGF0ZTEN
 3 | MAsGA1UEBxMET21zazEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
 4 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF
 5 | 3/Gnld/wcOE9J/M01y0RFiyVdPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+je
 6 | i+mA1ZqyKXnMANZozVPSedXRGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+
 7 | A3vk+3j3hjJjIm79rwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAiNWhz6EppIVa
 8 | FfUaB3sLeqfamb9tg9kBHtvqj/FJni0snqms0kPWaTySEPHZF0irIb7VVdq/sVCb
 9 | 3gseMVSyoDvPJ4lHC3PXqGQ7kM1mIPhDnR/4HDA3BhlGhTXSDIHgZnvI+HMBdsyC
10 | hC3dz5odyKqe4nmoofomALkBL9t4H8s=
11 | -----END CERTIFICATE REQUEST-----
12 | 
13 | 


--------------------------------------------------------------------------------
/test/keys/http2-key.pem:
--------------------------------------------------------------------------------
 1 | -----BEGIN RSA PRIVATE KEY-----
 2 | MIICXAIBAAKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF3/Gnld/wcOE9J/M01y0RFiyV
 3 | dPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+jei+mA1ZqyKXnMANZozVPSedXR
 4 | GwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+A3vk+3j3hjJjIm79rwIDAQAB
 5 | AoGAAv2QI9h32epQND9TxwSCKD//dC7W/cZOFNovfKCTeZjNK6EIzKqPTGA6smvR
 6 | C1enFl5adf+IcyWqAoe4lkqTvurIj+2EhtXdQ8DBlVuXKr3xvEFdYxXPautdTCF6
 7 | KbXEyS/s1TZCRFjYftvCrXxc3pK45AQX/wg7z1K+YB5pyIECQQD0OJvLoxLYoXAc
 8 | FZraIOZiDsEbGuSHqoCReFXH75EC3+XGYkH2bQ/nSIZ0h1buuwQ/ylKXOlTPT3Qt
 9 | Xm1OQEBvAkEA4AjWsIO/rRpOm/Q2aCrynWMpoUXTZSbL2yGf8pxp/+8r2br5ier0
10 | M1LeBb/OPY1+k39NWLXxQoo64xoSFYk2wQJAd2wDCwX4HkR7HNCXw1hZL9QFK6rv
11 | 20NN0VSlpboJD/3KT0MW/FiCcVduoCbaJK0Au+zEjDyy4hj5N4I4Mw6KMwJAXVAx
12 | I+psTsxzS4/njXG+BgIEl/C+gRYsuMQDnAi8OebDq/et8l0Tg8ETSu++FnM18neG
13 | ntmBeMacinUUbTXuwQJBAJp/onZdsMzeVulsGrqR1uS+Lpjc5Q1gt5ttt2cxj91D
14 | rio48C/ZvWuKNE8EYj2ALtghcVKRvgaWfOxt2GPguGg=
15 | -----END RSA PRIVATE KEY-----
16 | 
17 | 


--------------------------------------------------------------------------------
/test/lib/helper.js:
--------------------------------------------------------------------------------
 1 | // Copyright 2012 Mark Cavage.  All rights reserved.
 2 | //
 3 | // Just a simple wrapper over nodeunit's exports syntax. Also exposes
 4 | // a common logger for all tests.
 5 | //
 6 | 
 7 | 'use strict';
 8 | /* eslint-disable func-names */
 9 | 
10 | var domain = require('domain');
11 | 
12 | var pino = require('pino');
13 | var once = require('once');
14 | 
15 | ///--- Exports
16 | 
17 | module.exports = {
18 |     after: function after(teardown) {
19 |         module.parent.exports.tearDown = function _teardown(callback) {
20 |             var d = domain.create();
21 |             var self = this;
22 | 
23 |             d.once('error', function(err) {
24 |                 console.error('after: uncaught error\n', err.stack);
25 |                 process.exit(1);
26 |             });
27 | 
28 |             d.run(function() {
29 |                 teardown.call(self, once(callback));
30 |             });
31 |         };
32 |     },
33 | 
34 |     before: function before(setup) {
35 |         module.parent.exports.setUp = function _setup(callback) {
36 |             var d = domain.create();
37 |             var self = this;
38 | 
39 |             d.once('error', function(err) {
40 |                 console.error('before: uncaught error\n' + err.stack);
41 |                 process.exit(1);
42 |             });
43 | 
44 |             d.run(function() {
45 |                 setup.call(self, once(callback));
46 |             });
47 |         };
48 |     },
49 | 
50 |     test: function test(name, tester) {
51 |         module.parent.exports[name] = function _(t) {
52 |             var d = domain.create();
53 |             var self = this;
54 | 
55 |             d.once('error', function(err) {
56 |                 t.ifError(err);
57 |                 t.end();
58 |             });
59 | 
60 |             d.add(t);
61 |             d.run(function() {
62 |                 t.end = once(function() {
63 |                     t.done();
64 |                 });
65 |                 t.notOk = function notOk(ok, message) {
66 |                     return t.ok(!ok, message);
67 |                 };
68 | 
69 |                 tester.call(self, t);
70 |             });
71 |         };
72 |     },
73 | 
74 |     getLog: function(name, streams, level) {
75 |         return pino(
76 |             {
77 |                 level: process.env.LOG_LEVEL || level || 'fatal',
78 |                 name: name || process.argv[1],
79 |                 serializers: pino.stdSerializers
80 |             },
81 |             streams || process.stdout
82 |         );
83 |     },
84 | 
85 |     get dtrace() {
86 |         return true;
87 |     },
88 | 
89 |     sleep: function sleep(timeInMs) {
90 |         return new Promise(function sleepPromise(resolve) {
91 |             setTimeout(function timeout() {
92 |                 resolve();
93 |             }, timeInMs);
94 |         });
95 |     }
96 | };
97 | 


--------------------------------------------------------------------------------
/test/lib/server-withDisableUncaughtException.js:
--------------------------------------------------------------------------------
 1 | // A simple node process that will start a restify server with the
 2 | // uncaughtException handler disabled. Responds to a 'serverPortRequest' message
 3 | // and sends back the server's bound port number.
 4 | 
 5 | 'use strict';
 6 | /* eslint-disable func-names */
 7 | 
 8 | var restify = require('../../lib');
 9 | 
10 | function main() {
11 |     var port = process.env.UNIT_TEST_PORT || 0;
12 |     var server = restify.createServer({ handleUncaughtExceptions: false });
13 |     server.get('/', function(req, res, next) {
14 |         throw new Error('Catch me!');
15 |     });
16 |     server.listen(0, function() {
17 |         port = server.address().port;
18 |         console.log('port: ', port);
19 | 
20 |         process.on('message', function(msg) {
21 |             if (msg.task !== 'serverPortRequest') {
22 |                 process.send({ error: 'Unexpected message: ' + msg });
23 |                 return;
24 |             }
25 |             process.send({ task: 'serverPortResponse', port: port });
26 |         });
27 |     });
28 | }
29 | 
30 | if (require.main === module) {
31 |     main();
32 | }
33 | 


--------------------------------------------------------------------------------
/test/lib/streamRecorder.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | 
 3 | const stream = require('stream');
 4 | 
 5 | class StreamRecorder extends stream.Writable {
 6 |     constructor(options) {
 7 |         options = options || {};
 8 |         super(options);
 9 |         this.flushRecords();
10 |     }
11 | 
12 |     _write(chunk, encoding, callback) {
13 |         const record = JSON.parse(chunk.toString());
14 |         this.records.push(record);
15 |         callback();
16 |     }
17 | 
18 |     flushRecords() {
19 |         this.records = [];
20 |     }
21 | }
22 | 
23 | module.exports = StreamRecorder;
24 | 


--------------------------------------------------------------------------------
/test/plugins/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 |     env: {
3 |         mocha: true
4 |     }
5 | }
6 | 


--------------------------------------------------------------------------------
/test/plugins/accept.test.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | /* eslint-disable func-names */
 3 | 
 4 | // external requires
 5 | var assert = require('chai').assert;
 6 | var restify = require('../../lib/index.js');
 7 | var restifyClients = require('restify-clients');
 8 | 
 9 | // local files
10 | var helper = require('../lib/helper');
11 | 
12 | // local globals
13 | var SERVER;
14 | var CLIENT;
15 | var PORT;
16 | 
17 | describe('accept parser', function() {
18 |     before(function(done) {
19 |         SERVER = restify.createServer({
20 |             dtrace: helper.dtrace,
21 |             log: helper.getLog('server')
22 |         });
23 | 
24 |         SERVER.use(restify.plugins.acceptParser(SERVER.acceptable));
25 | 
26 |         SERVER.get('/', function respond(req, res, next) {
27 |             res.send();
28 |             next();
29 |         });
30 | 
31 |         SERVER.listen(0, '127.0.0.1', function() {
32 |             PORT = SERVER.address().port;
33 |             CLIENT = restifyClients.createJsonClient({
34 |                 url: 'http://127.0.0.1:' + PORT,
35 |                 dtrace: helper.dtrace,
36 |                 retry: false
37 |             });
38 | 
39 |             done();
40 |         });
41 |     });
42 | 
43 |     after(function(done) {
44 |         CLIENT.close();
45 |         SERVER.close(done);
46 |     });
47 | 
48 |     it('accept ok', function(done) {
49 |         CLIENT.get('/', function(err, _, res) {
50 |             assert.ifError(err);
51 |             assert.equal(res.statusCode, 200);
52 |             done();
53 |         });
54 |     });
55 | 
56 |     it('accept not ok (406)', function(done) {
57 |         var opts = {
58 |             path: '/',
59 |             headers: {
60 |                 accept: 'foo/bar'
61 |             }
62 |         };
63 | 
64 |         CLIENT.get(opts, function(err, _, res) {
65 |             assert.ok(err);
66 |             assert.equal(err.name, 'NotAcceptableError');
67 |             assert.equal(res.statusCode, 406);
68 |             done();
69 |         });
70 |     });
71 | 
72 |     it('GH-1619: should fire NotAcceptable event on server', function(done) {
73 |         var opts = {
74 |             path: '/',
75 |             headers: {
76 |                 accept: 'foo/bar'
77 |             }
78 |         };
79 |         var evtFired = false;
80 | 
81 |         SERVER.on('NotAcceptable', function(req, res, err, cb) {
82 |             evtFired = true;
83 |             return cb();
84 |         });
85 | 
86 |         CLIENT.get(opts, function(err, _, res) {
87 |             assert.ok(err);
88 |             assert.equal(err.name, 'NotAcceptableError');
89 |             assert.equal(res.statusCode, 406);
90 |             assert.isTrue(evtFired);
91 |             return done();
92 |         });
93 |     });
94 | });
95 | 


--------------------------------------------------------------------------------
/test/plugins/authorization.test.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | /* eslint-disable func-names */
 3 | 
 4 | // external requires
 5 | var assert = require('chai').assert;
 6 | var restify = require('../../lib/index.js');
 7 | var restifyClients = require('restify-clients');
 8 | 
 9 | // local files
10 | var helper = require('../lib/helper');
11 | 
12 | // local globals
13 | var SERVER;
14 | var CLIENT;
15 | var PORT;
16 | 
17 | describe('authorization parser', function() {
18 |     before(function(done) {
19 |         SERVER = restify.createServer({
20 |             dtrace: helper.dtrace,
21 |             log: helper.getLog('server')
22 |         });
23 | 
24 |         SERVER.use(restify.plugins.authorizationParser());
25 | 
26 |         SERVER.get('/', function respond(req, res, next) {
27 |             res.send();
28 |             next();
29 |         });
30 | 
31 |         SERVER.listen(0, '127.0.0.1', function() {
32 |             PORT = SERVER.address().port;
33 |             CLIENT = restifyClients.createJsonClient({
34 |                 url: 'http://127.0.0.1:' + PORT,
35 |                 dtrace: helper.dtrace,
36 |                 retry: false
37 |             });
38 | 
39 |             done();
40 |         });
41 |     });
42 | 
43 |     after(function(done) {
44 |         CLIENT.close();
45 |         SERVER.close(done);
46 |     });
47 | 
48 |     it('should accept basic authorization', function(done) {
49 |         var authz = 'Basic ' + new Buffer('user:secret').toString('base64');
50 |         var opts = {
51 |             path: '/',
52 |             headers: {
53 |                 authorization: authz
54 |             }
55 |         };
56 |         CLIENT.get(opts, function(err, _, res) {
57 |             assert.ifError(err);
58 |             assert.equal(res.statusCode, 200);
59 |             done();
60 |         });
61 |     });
62 | 
63 |     it('should reject basic authorization', function(done) {
64 |         var opts = {
65 |             path: '/',
66 |             headers: {
67 |                 authorization: 'Basic '
68 |             }
69 |         };
70 |         CLIENT.get(opts, function(err, _, res) {
71 |             assert.ok(err);
72 |             assert.equal(err.name, 'InvalidHeaderError');
73 |             assert.equal(res.statusCode, 400);
74 |             done();
75 |         });
76 |     });
77 | });
78 | 


--------------------------------------------------------------------------------
/test/plugins/context.test.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | /* eslint-disable func-names */
  3 | 
  4 | // external requires
  5 | var assert = require('chai').assert;
  6 | var restify = require('../../lib/index.js');
  7 | var restifyClients = require('restify-clients');
  8 | 
  9 | // local files
 10 | var helper = require('../lib/helper');
 11 | 
 12 | // local globals
 13 | var SERVER;
 14 | var CLIENT;
 15 | var PORT;
 16 | 
 17 | describe('accept parser', function() {
 18 |     before(function(done) {
 19 |         SERVER = restify.createServer({
 20 |             dtrace: helper.dtrace,
 21 |             log: helper.getLog('server')
 22 |         });
 23 | 
 24 |         SERVER.use(restify.plugins.pre.context());
 25 | 
 26 |         SERVER.listen(0, '127.0.0.1', function() {
 27 |             PORT = SERVER.address().port;
 28 |             CLIENT = restifyClients.createJsonClient({
 29 |                 url: 'http://127.0.0.1:' + PORT,
 30 |                 dtrace: helper.dtrace,
 31 |                 retry: false
 32 |             });
 33 | 
 34 |             done();
 35 |         });
 36 |     });
 37 | 
 38 |     after(function(done) {
 39 |         CLIENT.close();
 40 |         SERVER.close(done);
 41 |     });
 42 | 
 43 |     it('should use context', function(done) {
 44 |         SERVER.get('/', [
 45 |             function one(req, res, next) {
 46 |                 req.set('foo', {
 47 |                     a: 1
 48 |                 });
 49 |                 return next();
 50 |             },
 51 |             function two(req, res, next) {
 52 |                 assert.deepEqual(req.get('foo'), {
 53 |                     a: 1
 54 |                 });
 55 |                 req.get('foo').b = 2;
 56 |                 req.set('bar', [1]);
 57 |                 return next();
 58 |             },
 59 |             function three(req, res, next) {
 60 |                 assert.deepEqual(req.get('foo'), {
 61 |                     a: 1,
 62 |                     b: 2
 63 |                 });
 64 |                 assert.deepEqual(req.get('bar'), [1]);
 65 | 
 66 |                 assert.deepEqual(req.getAll(), {
 67 |                     foo: {
 68 |                         a: 1,
 69 |                         b: 2
 70 |                     },
 71 |                     bar: [1]
 72 |                 });
 73 | 
 74 |                 res.send();
 75 |                 return next();
 76 |             }
 77 |         ]);
 78 | 
 79 |         CLIENT.get('/', function(err, _, res) {
 80 |             assert.ifError(err);
 81 |             assert.equal(res.statusCode, 200);
 82 |             return done();
 83 |         });
 84 |     });
 85 | 
 86 |     it('should not share context', function(done) {
 87 |         SERVER.get('/1', function one(req, res, next) {
 88 |             // ensure we don't get context from previous request
 89 |             assert.equal(req.get('foo', null));
 90 |             res.end();
 91 |             return next();
 92 |         });
 93 | 
 94 |         CLIENT.get('/1', function(err, _, res) {
 95 |             assert.ifError(err);
 96 |             assert.equal(res.statusCode, 200);
 97 |             return done();
 98 |         });
 99 |     });
100 | });
101 | 


--------------------------------------------------------------------------------
/test/plugins/cpuUsageThrottle.test.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | /* eslint-disable func-names */
  3 | 
  4 | var assert = require('chai').assert;
  5 | var proxyquire = require('proxyquire');
  6 | var restify = require('../../lib/index.js');
  7 | var restifyClients = require('restify-clients');
  8 | var pidusage = require('pidusage');
  9 | 
 10 | // Allow tests to set the CPU usage returned by pidUsage
 11 | var CPU = 50;
 12 | 
 13 | var cpuUsageThrottle = proxyquire('../../lib/plugins/cpuUsageThrottle.js', {
 14 |     pidusage: function(pid, cb) {
 15 |         return cb(null, { cpu: CPU });
 16 |     }
 17 | });
 18 | 
 19 | var MR = Math.random;
 20 | describe('cpuUsageThrottle', function() {
 21 |     var plugin;
 22 | 
 23 |     before('Setup: stub math.random', function(done) {
 24 |         Math.random = function() {
 25 |             return 0;
 26 |         };
 27 |         done();
 28 |     });
 29 | 
 30 |     it('Unit: Should shed load', function(done) {
 31 |         var opts = { limit: 0, interval: 500 };
 32 |         plugin = cpuUsageThrottle(opts);
 33 |         function next(cont) {
 34 |             assert(cont instanceof Error, 'Should call next with error');
 35 |             assert.equal(cont.statusCode, 503, 'Defaults to 503 status');
 36 |             done();
 37 |         }
 38 |         plugin({}, {}, next);
 39 |     });
 40 | 
 41 |     it('Unit: Should let request through when not under load', function(done) {
 42 |         var opts = { interval: 500, limit: 0.9 };
 43 |         plugin = cpuUsageThrottle(opts);
 44 |         function next(cont) {
 45 |             assert.isUndefined(cont, 'Should call next');
 46 |             done();
 47 |         }
 48 |         plugin({}, {}, next);
 49 |     });
 50 | 
 51 |     it('Unit: Update should update state', function(done) {
 52 |         var opts = {
 53 |             max: 1,
 54 |             limit: 0.9,
 55 |             halfLife: 50,
 56 |             interval: 50
 57 |         };
 58 |         plugin = cpuUsageThrottle(opts);
 59 |         opts = {
 60 |             max: 0.5,
 61 |             limit: 0.1,
 62 |             halfLife: 1000,
 63 |             interval: 1000
 64 |         };
 65 |         plugin.update(opts);
 66 |         assert.equal(plugin.state.limit, opts.limit, 'opts.limit');
 67 |         assert.equal(plugin.state.max, opts.max, 'opts.max');
 68 |         assert.equal(plugin.state.halfLife, opts.halfLife, 'opts.halfLife');
 69 |         assert.equal(plugin.state.interval, opts.interval, 'opts.interval');
 70 |         done();
 71 |     });
 72 | 
 73 |     it('Unit: Should have proper name', function(done) {
 74 |         var opts = {
 75 |             max: 1,
 76 |             limit: 0.9,
 77 |             halfLife: 50,
 78 |             interval: 50
 79 |         };
 80 |         plugin = cpuUsageThrottle(opts);
 81 |         assert.equal(plugin.name, 'cpuUsageThrottle');
 82 |         done();
 83 |     });
 84 | 
 85 |     it('Unit: Should report proper lag', function(done) {
 86 |         var opts = { max: 1, limit: 0.9, halfLife: 50, interval: 50 };
 87 |         var dn = Date.now;
 88 |         var now = 0;
 89 |         // First timer will be 0, all future timers will be interval
 90 |         Date.now = function() {
 91 |             return (now++ > 0) * opts.interval;
 92 |         };
 93 |         plugin = cpuUsageThrottle(opts);
 94 |         Date.now = dn;
 95 |         assert.equal(plugin.state.lag, 0);
 96 |         done();
 97 |     });
 98 | 
 99 |     it('Integration: Should shed load', function(done) {
100 |         var server = restify.createServer();
101 |         var client = {
102 |             close: function() {}
103 |         };
104 |         var opts = { interval: 500, limit: 0 };
105 |         plugin = cpuUsageThrottle(opts);
106 |         server.pre(plugin);
107 |         server.get('/foo', function(req, res, next) {
108 |             res.send(200);
109 |             next();
110 |         });
111 |         server.listen(0, '127.0.0.1', function() {
112 |             client = restifyClients.createJsonClient({
113 |                 url: 'http://127.0.0.1:' + server.address().port,
114 |                 retry: false
115 |             });
116 |             client.get({ path: '/foo' }, function(e, _, res) {
117 |                 assert(e, 'Second request is shed');
118 |                 assert.equal(
119 |                     res.statusCode,
120 |                     503,
121 |                     'Default shed status code returned'
122 |                 );
123 |                 clearTimeout(plugin._timeout);
124 |                 // we should close the server else mocha wont exit
125 |                 server.close();
126 |                 done();
127 |             });
128 |         });
129 |     });
130 | 
131 |     it('Integration: pidusage should report CPU usage', function(done) {
132 |         assert.isFunction(pidusage, 'pidusage can be invoked');
133 |         pidusage(process.pid, function(e, stat) {
134 |             assert.ifError(e);
135 |             assert.isObject(stat);
136 |             assert.isNumber(stat.cpu);
137 |             pidusage.clear();
138 |             done();
139 |         });
140 |     });
141 | 
142 |     afterEach(function(done) {
143 |         if (plugin) {
144 |             plugin.close();
145 |         }
146 |         plugin = undefined;
147 |         done();
148 |     });
149 | 
150 |     after('Teardown: Reset Math.random', function(done) {
151 |         Math.random = MR;
152 |         done();
153 |     });
154 | });
155 | 


--------------------------------------------------------------------------------
/test/plugins/dedupeSlashes.test.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | /* eslint-disable func-names */
 3 | 
 4 | // external requires
 5 | var assert = require('chai').assert;
 6 | var restify = require('../../lib/index.js');
 7 | var restifyClients = require('restify-clients');
 8 | 
 9 | // local files
10 | var helper = require('../lib/helper');
11 | 
12 | // local globals
13 | var SERVER;
14 | var CLIENT;
15 | var PORT;
16 | 
17 | describe('dedupe forward slashes in URL', function() {
18 |     before(function(done) {
19 |         SERVER = restify.createServer({
20 |             dtrace: helper.dtrace,
21 |             log: helper.getLog('server')
22 |         });
23 | 
24 |         SERVER.pre(restify.plugins.pre.dedupeSlashes());
25 | 
26 |         SERVER.get('/foo/bar/', function respond(req, res, next) {
27 |             res.send(req.url);
28 |             next();
29 |         });
30 | 
31 |         SERVER.listen(0, '127.0.0.1', function() {
32 |             PORT = SERVER.address().port;
33 |             CLIENT = restifyClients.createJsonClient({
34 |                 url: 'http://127.0.0.1:' + PORT,
35 |                 dtrace: helper.dtrace,
36 |                 retry: false
37 |             });
38 | 
39 |             done();
40 |         });
41 |     });
42 | 
43 |     after(function(done) {
44 |         CLIENT.close();
45 |         SERVER.close(done);
46 |     });
47 | 
48 |     it('should not remove single slashes', function(done) {
49 |         CLIENT.get('/foo/bar/', function(err, _, res, data) {
50 |             assert.ifError(err);
51 |             assert.equal(res.statusCode, 200);
52 |             assert.equal(data, '/foo/bar/');
53 |             done();
54 |         });
55 |     });
56 | 
57 |     it('should remove duplicate slashes', function(done) {
58 |         CLIENT.get('//////foo///bar///////', function(err, _, res, data) {
59 |             assert.ifError(err);
60 |             assert.equal(res.statusCode, 200);
61 |             assert.equal(data, '/foo/bar/');
62 |             done();
63 |         });
64 |     });
65 | 
66 |     // eslint-disable-next-line
67 |     it('should remove duplicate slashes including trailing slashes', function(done) {
68 |         CLIENT.get('//foo//bar//', function(err, _, res, data) {
69 |             assert.ifError(err);
70 |             assert.equal(res.statusCode, 200);
71 |             assert.equal(data, '/foo/bar/');
72 |             done();
73 |         });
74 |     });
75 | });
76 | 


--------------------------------------------------------------------------------
/test/plugins/files/data-csv.txt:
--------------------------------------------------------------------------------
1 | field1,field2,field3
2 | 1,2,3
3 | 3,2,1
4 | "a","b","c"
5 | "\"c","b","a"


--------------------------------------------------------------------------------
/test/plugins/files/data-tsv.txt:
--------------------------------------------------------------------------------
1 | field1	field2	field3
2 | 1	2	3
3 | 3	2	1


--------------------------------------------------------------------------------
/test/plugins/files/object-csv.json:
--------------------------------------------------------------------------------
1 | [
2 |   { "field1": "1", "field2": "2", "field3": "3", "index": 0 },
3 |   { "field1": "3", "field2": "2", "field3": "1", "index": 1 },
4 |   { "field1": "a", "field2": "b", "field3": "c", "index": 2 },
5 |   { "field1": "\"c", "field2": "b", "field3": "a", "index": 3 }
6 | ]


--------------------------------------------------------------------------------
/test/plugins/files/object-tsv.json:
--------------------------------------------------------------------------------
1 | [
2 |   { "field1": "1", "field2": "2", "field3": "3", "index": 0 },
3 |   { "field1": "3", "field2": "2", "field3": "1", "index": 1 }
4 | ]


--------------------------------------------------------------------------------
/test/plugins/gzip.test.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | /* eslint-disable func-names */
  3 | 
  4 | // external requires
  5 | var assert = require('chai').assert;
  6 | var restify = require('../../lib/index.js');
  7 | var restifyClients = require('restify-clients');
  8 | 
  9 | // local files
 10 | var helper = require('../lib/helper');
 11 | 
 12 | // local globals
 13 | var SERVER;
 14 | var CLIENT;
 15 | var PORT;
 16 | 
 17 | describe('gzip parser', function() {
 18 |     beforeEach(function(done) {
 19 |         SERVER = restify.createServer({
 20 |             dtrace: helper.dtrace,
 21 |             log: helper.getLog('server')
 22 |         });
 23 | 
 24 |         SERVER.listen(0, '127.0.0.1', function() {
 25 |             PORT = SERVER.address().port;
 26 |             CLIENT = restifyClients.createJsonClient({
 27 |                 url: 'http://127.0.0.1:' + PORT,
 28 |                 dtrace: helper.dtrace,
 29 |                 retry: false
 30 |             });
 31 | 
 32 |             done();
 33 |         });
 34 |     });
 35 | 
 36 |     afterEach(function(done) {
 37 |         CLIENT.close();
 38 |         SERVER.close(done);
 39 |     });
 40 | 
 41 |     it('should gzip response', function(done) {
 42 |         SERVER.use(restify.plugins.gzipResponse());
 43 | 
 44 |         SERVER.get('/gzip/:id', function(req, res, next) {
 45 |             res.send({
 46 |                 hello: 'world'
 47 |             });
 48 |             next();
 49 |         });
 50 | 
 51 |         var opts = {
 52 |             path: '/gzip/foo',
 53 |             headers: {
 54 |                 'Accept-Encoding': 'gzip'
 55 |             }
 56 |         };
 57 |         CLIENT.get(opts, function(err, _, res, obj) {
 58 |             assert.ifError(err);
 59 |             assert.deepEqual({ hello: 'world' }, obj);
 60 |             done();
 61 |         });
 62 |     });
 63 | 
 64 |     it('gzip large response', function(done) {
 65 |         var testResponseSize = 65536 * 3;
 66 |         var TestStream = function() {
 67 |             this.readable = true;
 68 |             this.sentSize = 0;
 69 |             this.totalSize = testResponseSize;
 70 |             this.interval = null;
 71 |         };
 72 |         require('util').inherits(TestStream, require('stream'));
 73 |         TestStream.prototype.resume = function() {
 74 |             var self = this;
 75 | 
 76 |             if (!this.interval) {
 77 |                 this.interval = setInterval(function() {
 78 |                     var chunkSize = Math.min(
 79 |                         self.totalSize - self.sentSize,
 80 |                         65536
 81 |                     );
 82 | 
 83 |                     if (chunkSize > 0) {
 84 |                         var chunk = new Array(chunkSize + 1);
 85 |                         chunk = chunk.join('a');
 86 |                         self.emit('data', chunk);
 87 |                         self.sentSize += chunkSize;
 88 |                     } else {
 89 |                         self.emit('data', '"}');
 90 |                         self.emit('end');
 91 |                         self.pause();
 92 |                     }
 93 |                 }, 1);
 94 |             }
 95 |         };
 96 | 
 97 |         TestStream.prototype.pause = function() {
 98 |             clearInterval(this.interval);
 99 |             this.interval = null;
100 |         };
101 | 
102 |         var bodyStream = new TestStream();
103 | 
104 |         SERVER.use(restify.plugins.gzipResponse());
105 |         SERVER.get('/gzip/:id', function(req, res, next) {
106 |             bodyStream.resume();
107 |             res.write('{"foo":"');
108 |             bodyStream.pipe(res);
109 |             bodyStream.on('end', function() {
110 |                 next();
111 |             });
112 |         });
113 | 
114 |         var opts = {
115 |             path: '/gzip/foo',
116 |             headers: {
117 |                 'Accept-Encoding': 'gzip'
118 |             }
119 |         };
120 |         CLIENT.get(opts, function(err, _, res, obj) {
121 |             assert.ifError(err);
122 |             var expectedResponse = {
123 |                 foo: new Array(testResponseSize + 1).join('a')
124 |             };
125 |             assert.deepEqual(expectedResponse, obj);
126 |             done();
127 |         });
128 |     });
129 | 
130 |     it('gzip body json ok', function(done) {
131 |         SERVER.use(restify.plugins.gzipResponse());
132 |         SERVER.use(
133 |             restify.plugins.queryParser({
134 |                 mapParams: true
135 |             })
136 |         );
137 |         SERVER.use(
138 |             restify.plugins.bodyParser({
139 |                 mapParams: true
140 |             })
141 |         );
142 |         SERVER.post('/body/:id', function(req, res, next) {
143 |             assert.equal(req.params.id, 'foo');
144 |             assert.equal(req.params.name, 'markc');
145 |             assert.equal(req.params.phone, '(206) 555-1212');
146 |             res.send();
147 |             next();
148 |         });
149 | 
150 |         var obj = {
151 |             phone: '(206) 555-1212',
152 |             name: 'somethingelse'
153 |         };
154 |         CLIENT.gzip = {};
155 |         CLIENT.post('/body/foo?name=markc', obj, function(err, _, res) {
156 |             assert.ifError(err);
157 |             assert.ok(res);
158 | 
159 |             if (res) {
160 |                 assert.equal(res.statusCode, 200);
161 |             }
162 |             done();
163 |         });
164 |     });
165 | });
166 | 


--------------------------------------------------------------------------------
/test/plugins/inflightRequestThrottle.test.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | /* eslint-disable func-names */
  3 | 
  4 | var assert = require('chai').assert;
  5 | var restify = require('../../lib/index.js');
  6 | var restifyClients = require('restify-clients');
  7 | var inflightRequestThrottle = restify.plugins.inflightRequestThrottle;
  8 | 
  9 | function fakeServer(count) {
 10 |     return {
 11 |         inflightRequests: function() {
 12 |             return count;
 13 |         }
 14 |     };
 15 | }
 16 | 
 17 | describe('inlfightRequestThrottle', function() {
 18 |     it('Unit: Should shed load', function(done) {
 19 |         var logged = false;
 20 |         var opts = { server: fakeServer(10), limit: 1 };
 21 |         var plugin = inflightRequestThrottle(opts);
 22 |         function send(body) {
 23 |             assert(logged, 'Should have emitted a log');
 24 |             assert.equal(body.statusCode, 503, 'Defaults to 503 status');
 25 |             assert(body instanceof Error, 'Defaults to error body');
 26 |             done();
 27 |         }
 28 |         function next(err) {
 29 |             assert.equal(err.name, 'ServiceUnavailableError');
 30 |             done();
 31 |         }
 32 |         function trace() {
 33 |             logged = true;
 34 |         }
 35 |         var log = { trace: trace };
 36 |         var fakeReq = { log: log };
 37 |         plugin(fakeReq, { send: send }, next);
 38 |     });
 39 | 
 40 |     it('Unit: Should support custom response', function(done) {
 41 |         var server = fakeServer(10);
 42 |         var err = new Error('foo');
 43 |         var opts = { server: server, limit: 1, err: err };
 44 |         var plugin = inflightRequestThrottle(opts);
 45 |         function send(body) {
 46 |             assert.equal(body, err, 'Overrides body');
 47 |         }
 48 |         function next(nextErr) {
 49 |             assert.equal(err, nextErr);
 50 |             done();
 51 |         }
 52 |         var fakeReq = { log: { trace: function() {} } };
 53 |         plugin(fakeReq, { send: send }, next);
 54 |     });
 55 | 
 56 |     it('Unit: Should let request through when not under load', function(done) {
 57 |         var opts = { server: fakeServer(1), limit: 2 };
 58 |         var plugin = inflightRequestThrottle(opts);
 59 |         function send() {
 60 |             assert(false, 'Should not call send');
 61 |         }
 62 |         function next(cont) {
 63 |             assert.isUndefined(cont, 'Should call next');
 64 |             done();
 65 |         }
 66 |         var fakeReq = { log: { trace: function() {} } };
 67 |         plugin(fakeReq, { send: send }, next);
 68 |     });
 69 | 
 70 |     it('Integration: Should shed load', function(done) {
 71 |         var server = restify.createServer();
 72 |         var client = {
 73 |             close: function() {}
 74 |         };
 75 |         var isDone = false;
 76 |         var to;
 77 |         function finish() {
 78 |             if (isDone) {
 79 |                 return null;
 80 |             }
 81 |             clearTimeout(to);
 82 |             isDone = true;
 83 |             client.close();
 84 |             server.close();
 85 |             return done();
 86 |         }
 87 |         to = setTimeout(finish, 2000);
 88 |         var err = new Error('foo');
 89 |         err.statusCode = 555;
 90 |         var opts = { server: server, limit: 1, err: err };
 91 |         server.pre(inflightRequestThrottle(opts));
 92 |         var RES;
 93 |         server.get('/foo', function(req, res, next) {
 94 |             if (RES) {
 95 |                 res.send(999);
 96 |             } else {
 97 |                 RES = res;
 98 |             }
 99 |         });
100 |         server.listen(0, '127.0.0.1', function() {
101 |             client = restifyClients.createJsonClient({
102 |                 url: 'http://127.0.0.1:' + server.address().port,
103 |                 retry: false
104 |             });
105 |             client.get({ path: '/foo' }, function(e, _, res) {
106 |                 assert(
107 |                     e === null || e === undefined,
108 |                     'First request isnt shed'
109 |                 );
110 |                 assert.equal(res.statusCode, 200, '200 returned on success');
111 |                 finish();
112 |             });
113 |             client.get({ path: '/foo' }, function(e, _, res) {
114 |                 assert(e, 'Second request is shed');
115 |                 assert.equal(
116 |                     e.name,
117 |                     'InternalServerError',
118 |                     'Default err returned'
119 |                 );
120 |                 assert.equal(
121 |                     res.statusCode,
122 |                     555,
123 |                     'Default shed status code returned'
124 |                 );
125 | 
126 |                 if (RES) {
127 |                     RES.send(200);
128 |                 }
129 |             });
130 |         });
131 |     });
132 | });
133 | 


--------------------------------------------------------------------------------
/test/plugins/reqIdHeaders.test.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | /* eslint-disable func-names */
  3 | 
  4 | // external modules
  5 | var assert = require('chai').assert;
  6 | var restify = require('../../lib/index.js');
  7 | var restifyClients = require('restify-clients');
  8 | var validator = require('validator');
  9 | 
 10 | // internal files
 11 | var helper = require('../lib/helper');
 12 | 
 13 | describe('request id headers', function() {
 14 |     var SERVER;
 15 |     var CLIENT;
 16 |     var PORT;
 17 | 
 18 |     beforeEach(function(done) {
 19 |         SERVER = restify.createServer({
 20 |             dtrace: helper.dtrace,
 21 |             log: helper.getLog('server')
 22 |         });
 23 | 
 24 |         SERVER.pre(
 25 |             restify.plugins.pre.reqIdHeaders({
 26 |                 headers: ['x-req-id-a', 'x-req-id-b']
 27 |             })
 28 |         );
 29 | 
 30 |         SERVER.listen(0, '127.0.0.1', function() {
 31 |             PORT = SERVER.address().port;
 32 |             CLIENT = restifyClients.createJsonClient({
 33 |                 url: 'http://127.0.0.1:' + PORT,
 34 |                 dtrace: helper.dtrace,
 35 |                 retry: false
 36 |             });
 37 |             return done();
 38 |         });
 39 |     });
 40 | 
 41 |     afterEach(function(done) {
 42 |         CLIENT.close();
 43 |         SERVER.close(function() {
 44 |             CLIENT = null;
 45 |             SERVER = null;
 46 |             return done();
 47 |         });
 48 |     });
 49 | 
 50 |     it('GH-1086: should reuse request id when available', function(done) {
 51 |         SERVER.get('/1', function(req, res, next) {
 52 |             // the 12345 value is set when the client is created.
 53 |             assert.ok(req.headers.hasOwnProperty('x-req-id-a'));
 54 |             assert.equal(req.getId(), req.headers['x-req-id-a']);
 55 |             res.send('hello world');
 56 |             return next();
 57 |         });
 58 | 
 59 |         // create new client since we new specific headers
 60 |         CLIENT = restifyClients.createJsonClient({
 61 |             url: 'http://127.0.0.1:' + PORT,
 62 |             headers: {
 63 |                 'x-req-id-a': 12345
 64 |             }
 65 |         });
 66 | 
 67 |         CLIENT.get('/1', function(err, req, res, data) {
 68 |             assert.ifError(err);
 69 |             assert.equal(data, 'hello world');
 70 |             return done();
 71 |         });
 72 |     });
 73 | 
 74 |     it('GH-1086: should use second request id when available', function(done) {
 75 |         SERVER.get('/1', function(req, res, next) {
 76 |             assert.ok(req.headers.hasOwnProperty('x-req-id-b'));
 77 |             assert.equal(req.getId(), req.headers['x-req-id-b']);
 78 |             res.send('hello world');
 79 |             return next();
 80 |         });
 81 | 
 82 |         // create new client since we new specific headers
 83 |         CLIENT = restifyClients.createJsonClient({
 84 |             url: 'http://127.0.0.1:' + PORT,
 85 |             headers: {
 86 |                 'x-req-id-b': 678910
 87 |             }
 88 |         });
 89 | 
 90 |         CLIENT.get('/1', function(err, req, res, data) {
 91 |             assert.ifError(err);
 92 |             assert.equal(data, 'hello world');
 93 |             return done();
 94 |         });
 95 |     });
 96 | 
 97 |     // eslint-disable-next-line
 98 |     it('GH-1086: should use default uuid request id if none provided', function(done) {
 99 |         SERVER.get('/1', function(req, res, next) {
100 |             assert.ok(req.getId());
101 |             assert.ok(validator.isUUID(req.getId()));
102 |             res.send('hello world');
103 |             return next();
104 |         });
105 | 
106 |         // create new client since we new specific headers
107 |         CLIENT = restifyClients.createJsonClient({
108 |             url: 'http://127.0.0.1:' + PORT
109 |         });
110 | 
111 |         CLIENT.get('/1', function(err, req, res, data) {
112 |             assert.ifError(err);
113 |             assert.equal(data, 'hello world');
114 |             return done();
115 |         });
116 |     });
117 | 
118 |     it('GH-1086: empty request id should be ignored', function(done) {
119 |         SERVER.get('/1', function(req, res, next) {
120 |             assert.ok(req.headers.hasOwnProperty('x-req-id-b'));
121 |             assert.equal(req.getId(), req.headers['x-req-id-b']);
122 |             res.send('hello world');
123 |             return next();
124 |         });
125 | 
126 |         // create new client since we new specific headers
127 |         CLIENT = restifyClients.createJsonClient({
128 |             url: 'http://127.0.0.1:' + PORT,
129 |             headers: {
130 |                 'x-req-id-a': '',
131 |                 'x-req-id-b': 12345
132 |             }
133 |         });
134 | 
135 |         CLIENT.get('/1', function(err, req, res, data) {
136 |             assert.ifError(err);
137 |             assert.equal(data, 'hello world');
138 |             return done();
139 |         });
140 |     });
141 | });
142 | 


--------------------------------------------------------------------------------
/test/plugins/testStaticFiles/docs/doc.md:
--------------------------------------------------------------------------------
1 | #This is doc.md


--------------------------------------------------------------------------------
/test/plugins/testStaticFiles/docs/index.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <html lang="en">
 3 |     <head>
 4 |         <meta charset="UTF-8" />
 5 |         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6 |         <meta http-equiv="X-UA-Compatible" content="ie=edge" />
 7 |         <title>Document</title>
 8 |     </head>
 9 |     <body>
10 |         <h1>testStaticFiles/docs/index.html</h1>
11 |     </body>
12 | </html>
13 | 


--------------------------------------------------------------------------------
/test/plugins/testStaticFiles/file1.txt:
--------------------------------------------------------------------------------
1 | This is file1.txt


--------------------------------------------------------------------------------
/test/plugins/testStaticFiles/index.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <html lang="en">
 3 |     <head>
 4 |         <meta charset="UTF-8" />
 5 |         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 6 |         <meta http-equiv="X-UA-Compatible" content="ie=edge" />
 7 |         <title>Document</title>
 8 |     </head>
 9 |     <body>
10 |         <h1>testStaticFiles/index.html</h1>
11 |     </body>
12 | </html>
13 | 


--------------------------------------------------------------------------------
/test/plugins/testStaticFiles/special/$_$/bad (file).txt:
--------------------------------------------------------------------------------
1 | This is a very badly named file.


--------------------------------------------------------------------------------
/test/plugins/userAgent.test.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | /* eslint-disable func-names */
  3 | 
  4 | // core requires
  5 | var child_process = require('child_process');
  6 | var http = require('http');
  7 | 
  8 | // external requires
  9 | var assert = require('chai').assert;
 10 | var restify = require('../../lib/index.js');
 11 | 
 12 | // local files
 13 | var helper = require('../lib/helper');
 14 | 
 15 | // local globals
 16 | var SERVER;
 17 | var SERVER_PORT;
 18 | var SERVER_ADDRESS = '127.0.0.1';
 19 | var SERVER_ENDPOINT;
 20 | var TEST_ENDPOINT;
 21 | var TEST_RESPONSE_DATA = 'foobar';
 22 | var TEST_RESPONSE_DATA_LENGTH = TEST_RESPONSE_DATA.length;
 23 | var TEST_PATH = '/test/userAgent';
 24 | 
 25 | describe('userAgent pre-route handler', function() {
 26 |     beforeEach(function(done) {
 27 |         SERVER = restify.createServer({
 28 |             dtrace: helper.dtrace,
 29 |             log: helper.getLog('server')
 30 |         });
 31 | 
 32 |         // Enable the user agent pre-route handler, since this is the component
 33 |         // under test.
 34 |         SERVER.use(restify.plugins.pre.userAgentConnection());
 35 | 
 36 |         SERVER.head('/test/:name', function(req, res, next) {
 37 |             // Explicitly set Content-Length response header so that we can test
 38 |             // for its removal (or lack thereof) by the userAgentConnection
 39 |             // pre-route handler in tests below.
 40 |             res.setHeader('Content-Length', TEST_RESPONSE_DATA_LENGTH);
 41 |             res.send(200, TEST_RESPONSE_DATA);
 42 |             next();
 43 |         });
 44 | 
 45 |         SERVER.listen(0, SERVER_ADDRESS, function() {
 46 |             SERVER_PORT = SERVER.address().port;
 47 |             SERVER_ENDPOINT = SERVER_ADDRESS + ':' + SERVER_PORT;
 48 |             TEST_ENDPOINT = SERVER_ENDPOINT + TEST_PATH;
 49 |             done();
 50 |         });
 51 |     });
 52 | 
 53 |     afterEach(function(done) {
 54 |         SERVER.close(done);
 55 |     });
 56 | 
 57 |     // By default, the userAgentConnection pre-route handler must:
 58 |     //
 59 |     // 1. set the 'connection' header to 'close'
 60 |     //
 61 |     // 2. remove the content-length header from the response
 62 |     //
 63 |     // when a HEAD request is handled and the client's user agent is curl.
 64 |     it('sets proper headers for HEAD requests from curl', function(done) {
 65 |         var CURL_CMD = ['curl', '-sS', '-i', TEST_ENDPOINT, '-X', 'HEAD'].join(
 66 |             ' '
 67 |         );
 68 | 
 69 |         child_process.exec(CURL_CMD, function onExec(err, stdout, stderr) {
 70 |             assert.ifError(err);
 71 | 
 72 |             var lines = stdout.split(/\n/);
 73 | 
 74 |             var contentLengthHeaderNotPresent = lines.every(
 75 |                 function checkContentLengthNotPresent(line) {
 76 |                     return /Content-Length:.*/.test(line) === false;
 77 |                 }
 78 |             );
 79 |             var connectionCloseHeaderPresent = lines.some(
 80 |                 function checkConnectionClosePresent(line) {
 81 |                     return /Connection: close/.test(line);
 82 |                 }
 83 |             );
 84 | 
 85 |             assert.ok(contentLengthHeaderNotPresent);
 86 |             assert.ok(connectionCloseHeaderPresent);
 87 | 
 88 |             done();
 89 |         });
 90 |     });
 91 | 
 92 |     // When handling a HEAD request, and if the client's user agent is not curl,
 93 |     // the userAgentConnection should not remove the content-length header from
 94 |     // the response, and it should not replace the value of the 'connection'
 95 |     // header by 'close'.
 96 |     // eslint-disable-next-line
 97 |     it('sets proper headers for HEAD requests from non-curl clients', function(done) {
 98 |         var req = http.request(
 99 |             {
100 |                 hostname: SERVER_ADDRESS,
101 |                 port: SERVER_PORT,
102 |                 path: TEST_PATH,
103 |                 method: 'HEAD',
104 |                 headers: {
105 |                     'user-agent': 'foobar',
106 |                     connection: 'keep-alive'
107 |                 }
108 |             },
109 |             function onResponse(res) {
110 |                 var responseHeaders = res.headers;
111 | 
112 |                 assert.ok(responseHeaders.hasOwnProperty('content-length'));
113 |                 assert.equal(responseHeaders.connection, 'keep-alive');
114 | 
115 |                 // destroy the socket explicitly now since the request was
116 |                 // explicitly requesting to not destroy the socket by setting
117 |                 // its connection header to 'keep-alive'.
118 |                 req.abort();
119 | 
120 |                 done();
121 |             }
122 |         );
123 | 
124 |         req.on('error', function onReqError(err) {
125 |             assert.ifError(err);
126 |             done();
127 |         });
128 | 
129 |         req.end();
130 |     });
131 | });
132 | 


--------------------------------------------------------------------------------
/test/plugins/utilsHrTimeDurationInMs.test.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | /* eslint-disable func-names */
 3 | 
 4 | var assert = require('chai').assert;
 5 | var hrTimeDurationInMs = require('../../lib/plugins/utils/hrTimeDurationInMs');
 6 | 
 7 | describe('utils #hrTimeDurationInMs', function() {
 8 |     it('should return with duration', function() {
 9 |         var startTime = [0, 0];
10 |         var endTime = [1, 1e6];
11 | 
12 |         var duration = hrTimeDurationInMs(startTime, endTime);
13 | 
14 |         assert.equal(duration, 1001);
15 |     });
16 | });
17 | 


--------------------------------------------------------------------------------
/test/routerRegistryRadix.test.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | /* eslint-disable func-names */
  3 | 
  4 | var RouterRegistryRadix = require('../lib/routerRegistryRadix');
  5 | var Chain = require('../lib/chain');
  6 | 
  7 | if (require.cache[__dirname + '/lib/helper.js']) {
  8 |     delete require.cache[__dirname + '/lib/helper.js'];
  9 | }
 10 | var helper = require('./lib/helper.js');
 11 | 
 12 | ///--- Globals
 13 | 
 14 | var test = helper.test;
 15 | 
 16 | function getTestRoute(opts) {
 17 |     var chain = new Chain();
 18 |     var name = opts.method + '-' + opts.path;
 19 |     name = name.replace(/\W/g, '').toLowerCase();
 20 | 
 21 |     return {
 22 |         name: name,
 23 |         method: opts.method,
 24 |         path: opts.path,
 25 |         spec: opts,
 26 |         chain: chain
 27 |     };
 28 | }
 29 | 
 30 | ///--- Tests
 31 | 
 32 | test('adds a route', function(t) {
 33 |     var registry = new RouterRegistryRadix();
 34 |     registry.add(getTestRoute({ method: 'GET', path: '/' }));
 35 |     registry.add(getTestRoute({ method: 'POST', path: '/' }));
 36 |     registry.add(getTestRoute({ method: 'GET', path: '/ab' }));
 37 | 
 38 |     t.deepEqual(Object.keys(registry.get()), ['get', 'post', 'getab']);
 39 | 
 40 |     t.done();
 41 | });
 42 | 
 43 | test('removes a route', function(t) {
 44 |     var registry = new RouterRegistryRadix();
 45 | 
 46 |     // Mount
 47 |     registry.add(getTestRoute({ method: 'GET', path: '/a' }));
 48 |     registry.add(getTestRoute({ method: 'POST', path: '/b' }));
 49 |     t.deepEqual(Object.keys(registry.get()), ['geta', 'postb']);
 50 | 
 51 |     // Unmount
 52 |     var route = registry.remove('geta');
 53 |     t.ok(route);
 54 |     t.equal(route.name, 'geta');
 55 | 
 56 |     // Removes from registry
 57 |     t.deepEqual(Object.keys(registry.get()), ['postb']);
 58 | 
 59 |     t.end();
 60 | });
 61 | 
 62 | test('lookups a route', function(t) {
 63 |     var registry = new RouterRegistryRadix();
 64 |     var route = getTestRoute({ method: 'GET', path: '/a/:b' });
 65 |     registry.add(route);
 66 | 
 67 |     var result = registry.lookup('GET', '/a/b');
 68 | 
 69 |     t.deepEqual(result, {
 70 |         route: route,
 71 |         params: { b: 'b' },
 72 |         handler: result.handler
 73 |     });
 74 | 
 75 |     t.done();
 76 | });
 77 | 
 78 | test('get registered routes', function(t) {
 79 |     var registry = new RouterRegistryRadix();
 80 |     registry.add(getTestRoute({ method: 'GET', path: '/' }));
 81 |     registry.add(getTestRoute({ method: 'GET', path: '/a' }));
 82 |     registry.add(getTestRoute({ method: 'GET', path: '/a/b' }));
 83 |     registry.add(getTestRoute({ method: 'POST', path: '/' }));
 84 | 
 85 |     t.deepEqual(Object.keys(registry.get()), ['get', 'geta', 'getab', 'post']);
 86 |     t.end();
 87 | });
 88 | 
 89 | test('toString()', function(t) {
 90 |     var registry = new RouterRegistryRadix();
 91 |     registry.add(getTestRoute({ method: 'GET', path: '/' }));
 92 |     registry.add(getTestRoute({ method: 'GET', path: '/a' }));
 93 |     registry.add(getTestRoute({ method: 'GET', path: '/a/b' }));
 94 |     registry.add(getTestRoute({ method: 'POST', path: '/' }));
 95 | 
 96 |     t.deepEqual(
 97 |         registry.toString(),
 98 |         // prettier-ignore
 99 |         '└── / (GET, POST)\n' +
100 |         '    └── a (GET)\n' +
101 |         '        └── /b (GET)\n'
102 |     );
103 |     t.end();
104 | });
105 | 
106 | test('toString() with ignoreTrailingSlash', function(t) {
107 |     var registry = new RouterRegistryRadix({ ignoreTrailingSlash: true });
108 |     registry.add(getTestRoute({ method: 'GET', path: '/' }));
109 |     registry.add(getTestRoute({ method: 'GET', path: '/a' }));
110 |     registry.add(getTestRoute({ method: 'GET', path: '/a/b' }));
111 |     registry.add(getTestRoute({ method: 'POST', path: '/' }));
112 | 
113 |     t.deepEqual(
114 |         registry.toString(),
115 |         // prettier-ignore
116 |         '└── / (GET, POST)\n' +
117 |         '    └── a (GET)\n' +
118 |         '        └── /b (GET)\n'
119 |     );
120 |     t.end();
121 | });
122 | 


--------------------------------------------------------------------------------
/test/serverHttp2.test.js:
--------------------------------------------------------------------------------
  1 | 'use strict';
  2 | /* eslint-disable func-names */
  3 | 
  4 | var path = require('path');
  5 | var fs = require('fs');
  6 | var http2;
  7 | 
  8 | // http2 module is not available < v8.4.0 (only with flag <= 8.8.0)
  9 | try {
 10 |     http2 = require('http2');
 11 | } catch (err) {
 12 |     console.log('HTTP2 module is not available');
 13 |     console.log(
 14 |         'Node.js version >= v8.8.8 required, current: ' + process.versions.node
 15 |     );
 16 |     return;
 17 | }
 18 | 
 19 | var restify = require('../lib');
 20 | 
 21 | if (require.cache[__dirname + '/lib/helper.js']) {
 22 |     delete require.cache[__dirname + '/lib/helper.js'];
 23 | }
 24 | var helper = require('./lib/helper.js');
 25 | 
 26 | ///--- Globals
 27 | 
 28 | var after = helper.after;
 29 | var before = helper.before;
 30 | var test = helper.test;
 31 | 
 32 | var CERT = fs.readFileSync(path.join(__dirname, './keys/http2-cert.pem'));
 33 | var KEY = fs.readFileSync(path.join(__dirname, './keys/http2-key.pem'));
 34 | var CA = fs.readFileSync(path.join(__dirname, 'keys/http2-csr.pem'));
 35 | 
 36 | var PORT = process.env.UNIT_TEST_PORT || 0;
 37 | var CLIENT;
 38 | var SERVER;
 39 | 
 40 | ///--- Tests
 41 | 
 42 | before(function(cb) {
 43 |     try {
 44 |         SERVER = restify.createServer({
 45 |             dtrace: helper.dtrace,
 46 |             handleUncaughtExceptions: true,
 47 |             http2: {
 48 |                 cert: CERT,
 49 |                 key: KEY,
 50 |                 ca: CA
 51 |             },
 52 |             log: helper.getLog('server')
 53 |         });
 54 |         SERVER.listen(PORT, '127.0.0.1', function() {
 55 |             PORT = SERVER.address().port;
 56 |             CLIENT = http2.connect('https://127.0.0.1:' + PORT, {
 57 |                 rejectUnauthorized: false
 58 |             });
 59 | 
 60 |             cb();
 61 |         });
 62 |     } catch (e) {
 63 |         console.error(e.stack);
 64 |         process.exit(1);
 65 |     }
 66 | });
 67 | 
 68 | after(function(cb) {
 69 |     try {
 70 |         CLIENT.destroy();
 71 |         SERVER.close(function() {
 72 |             CLIENT = null;
 73 |             SERVER = null;
 74 |             cb();
 75 |         });
 76 |     } catch (e) {
 77 |         console.error(e.stack);
 78 |         process.exit(1);
 79 |     }
 80 | });
 81 | 
 82 | test('get (path only)', function(t) {
 83 |     SERVER.get('/foo/:id', function echoId(req, res, next) {
 84 |         t.ok(req.params);
 85 |         t.equal(req.params.id, 'bar');
 86 |         t.equal(req.isUpload(), false);
 87 |         res.json({ hello: 'world' });
 88 |         next();
 89 |     });
 90 | 
 91 |     var req = CLIENT.request({
 92 |         ':path': '/foo/bar',
 93 |         ':method': 'GET'
 94 |     });
 95 | 
 96 |     req.on('response', function(headers, flags) {
 97 |         var data = '';
 98 |         t.equal(headers[':status'], 200);
 99 | 
100 |         req.on('data', function(chunk) {
101 |             data += chunk;
102 |         });
103 |         req.on('end', function() {
104 |             t.deepEqual(JSON.parse(data), { hello: 'world' });
105 |             t.end();
106 |         });
107 |     });
108 |     req.on('error', function(err) {
109 |         t.ifError(err);
110 |     });
111 | });
112 | 


--------------------------------------------------------------------------------
/test/utils.test.js:
--------------------------------------------------------------------------------
 1 | 'use strict';
 2 | /* eslint-disable func-names */
 3 | 
 4 | var mergeQs = require('../lib/utils').mergeQs;
 5 | 
 6 | if (require.cache[__dirname + '/lib/helper.js']) {
 7 |     delete require.cache[__dirname + '/lib/helper.js'];
 8 | }
 9 | var helper = require('./lib/helper.js');
10 | 
11 | ///--- Globals
12 | 
13 | var test = helper.test;
14 | 
15 | test('merge qs', function(t) {
16 |     var qs1 = mergeQs(undefined, { a: 1 });
17 |     t.deepEqual(qs1, { a: 1 });
18 | 
19 |     var qs2 = mergeQs({ a: 1 }, null);
20 |     t.deepEqual(qs2, { a: 1 });
21 | 
22 |     var qs3 = mergeQs({ a: 1 }, { a: 2 });
23 |     t.deepEqual(qs3, { a: [1, 2] });
24 | 
25 |     var qs4 = mergeQs({ a: 1 }, { b: 2 });
26 |     t.deepEqual(qs4, { a: 1, b: 2 });
27 | 
28 |     var qs5 = mergeQs(null, null);
29 |     t.deepEqual(qs5, {});
30 | 
31 |     t.done();
32 | });
33 | 


--------------------------------------------------------------------------------
/tools/mk/Makefile.defs:
--------------------------------------------------------------------------------
 1 | # -*- mode: makefile -*-
 2 | #
 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved.
 4 | #
 5 | # Makefile.defs: common defines.
 6 | #
 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
 8 | # into other repos as-is without requiring any modifications. If you find
 9 | # yourself changing this file, you should instead update the original copy in
10 | # eng.git and then update your repo to use the new version.
11 | #
12 | # This makefile defines some useful defines. Include it at the top of
13 | # your Makefile.
14 | #
15 | # Definitions in this Makefile:
16 | #
17 | #	TOP 		The absolute path to the project directory. The top dir.
18 | #	BRANCH 		The current git branch.
19 | #	TIMESTAMP	The timestamp for the build. This can be set via
20 | #			the TIMESTAMP envvar (used by MG-based builds).
21 | #	STAMP		A build stamp to use in built package names.
22 | #
23 | 
24 | TOP := $(shell pwd)
25 | 
26 | #
27 | # Mountain Gorilla-spec'd versioning.
28 | # See "Package Versioning" in MG's README.md:
29 | # <https://mo.joyent.com/mountain-gorilla/blob/master/README.md#L139-200>
30 | #
31 | # Need GNU awk for multi-char arg to "-F".
32 | _AWK := $(shell (which gawk >/dev/null && echo gawk) \
33 | 	|| (which nawk >/dev/null && echo nawk) \
34 | 	|| echo awk)
35 | BRANCH := $(shell git log -n 1 --pretty=%d HEAD | $(_AWK) '{print $NF}' | sed -e 's/.$//' | cut -d/ -f2)
36 | ifeq ($(TIMESTAMP),)
37 | 	TIMESTAMP := $(shell date -u "+%Y%m%dT%H%M%SZ")
38 | endif
39 | _GITDESCRIBE := g$(shell git describe --all --long --dirty | $(_AWK) -F'-g' '{print $NF}')
40 | STAMP := $(BRANCH)-$(TIMESTAMP)-$(_GITDESCRIBE)
41 | 


--------------------------------------------------------------------------------
/tools/mk/Makefile.deps:
--------------------------------------------------------------------------------
 1 | # -*- mode: makefile -*-
 2 | #
 3 | # Copyright (c) 2012, Joyent, Inc. All rights reserved.
 4 | #
 5 | # Makefile.deps: Makefile for including common tools as dependencies
 6 | #
 7 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
 8 | # into other repos as-is without requiring any modifications. If you find
 9 | # yourself changing this file, you should instead update the original copy in
10 | # eng.git and then update your repo to use the new version.
11 | #
12 | # This file is separate from Makefile.targ so that teams can choose
13 | # independently whether to use the common targets in Makefile.targ and the
14 | # common tools here.
15 | #
16 | 
17 | 
18 | #
19 | # restdown
20 | #
21 | RESTDOWN_EXEC	?= deps/restdown/bin/restdown
22 | RESTDOWN	?= python $(RESTDOWN_EXEC)
23 | $(RESTDOWN_EXEC): | deps/restdown/.git
24 | 


--------------------------------------------------------------------------------