Merge tag '4.17.2'

This commit is contained in:
Douglas Christopher Wilson
2021-12-16 23:01:28 -05:00
61 changed files with 847 additions and 227 deletions

View File

@@ -3,6 +3,6 @@ root: true
rules:
eol-last: error
eqeqeq: [error, allow-null]
indent: [error, 2, { SwitchCase: 1 }]
indent: [error, 2, { MemberExpression: "off", SwitchCase: 1 }]
no-trailing-spaces: error
no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }]

160
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,160 @@
name: ci
on:
- pull_request
- push
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
name:
- Node.js 0.10
- Node.js 0.12
- io.js 1.x
- io.js 2.x
- io.js 3.x
- Node.js 4.x
- Node.js 5.x
- Node.js 6.x
- Node.js 7.x
- Node.js 8.x
- Node.js 9.x
- Node.js 10.x
- Node.js 11.x
- Node.js 12.x
- Node.js 13.x
- Node.js 14.x
include:
- name: Node.js 0.10
node-version: "0.10"
npm-i: mocha@3.5.3 supertest@2.0.0
- name: Node.js 0.12
node-version: "0.12"
npm-i: mocha@3.5.3 supertest@2.0.0
- name: io.js 1.x
node-version: "1.8"
npm-i: mocha@3.5.3 supertest@2.0.0
- name: io.js 2.x
node-version: "2.5"
npm-i: mocha@3.5.3 supertest@2.0.0
- name: io.js 3.x
node-version: "3.3"
npm-i: mocha@3.5.3 supertest@2.0.0
- name: Node.js 4.x
node-version: "4.9"
npm-i: mocha@5.2.0 supertest@3.4.2
- name: Node.js 5.x
node-version: "5.12"
npm-i: mocha@5.2.0 supertest@3.4.2
- name: Node.js 6.x
node-version: "6.17"
npm-i: mocha@6.2.2
- name: Node.js 7.x
node-version: "7.10"
npm-i: mocha@6.2.2
- name: Node.js 8.x
node-version: "8.17"
npm-i: mocha@7.2.0
- name: Node.js 9.x
node-version: "9.11"
npm-i: mocha@7.2.0
- name: Node.js 10.x
node-version: "10.24"
npm-i: mocha@8.4.0
- name: Node.js 11.x
node-version: "11.15"
npm-i: mocha@8.4.0
- name: Node.js 12.x
node-version: "12.22"
- name: Node.js 13.x
node-version: "13.14"
- name: Node.js 14.x
node-version: "14.18"
steps:
- uses: actions/checkout@v2
- name: Install Node.js ${{ matrix.node-version }}
shell: bash -eo pipefail -l {0}
run: |
nvm install --default ${{ matrix.node-version }}
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
- name: Configure npm
run: |
npm config set loglevel error
npm config set shrinkwrap false
- name: Install npm module(s) ${{ matrix.npm-i }}
run: npm install --save-dev ${{ matrix.npm-i }}
if: matrix.npm-i != ''
- name: Remove non-test dependencies
run: npm rm --silent --save-dev connect-redis
- name: Setup Node.js version-specific dependencies
shell: bash
run: |
# eslint for linting
# - remove on Node.js < 10
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
grep -E '^eslint(-|$)' | \
sort -r | \
xargs -n1 npm rm --silent --save-dev
fi
- name: Install Node.js dependencies
run: npm install
- name: List environment
id: list_env
shell: bash
run: |
echo "node@$(node -v)"
echo "npm@$(npm -v)"
npm -s ls ||:
- name: Run tests
shell: bash
run: npm run test-ci
- name: Lint code
if: steps.list_env.outputs.eslint != ''
run: npm run lint
- name: Collect code coverage
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
flag-name: run-${{ matrix.test_number }}
parallel: true
coverage:
needs: test
runs-on: ubuntu-latest
steps:
- name: Upload code coverage
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true

View File

@@ -1,66 +0,0 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "1.8"
- "2.5"
- "3.3"
- "4.9"
- "5.12"
- "6.17"
- "7.10"
- "8.16"
- "9.11"
- "10.15"
- "11.15"
- "12.3"
matrix:
include:
- node_js: "13"
env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
allow_failures:
# Allow the nightly installs to fail
- env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
sudo: false
cache:
directories:
- node_modules
before_install:
# Configure npm
- |
# Skip updating shrinkwrap / lock
npm config set shrinkwrap false
# Remove all non-test dependencies
- |
# Remove example dependencies
npm rm --silent --save-dev connect-redis
# Setup Node.js version-specific dependencies
- |
# mocha for testing
# - use 3.x for Node.js < 6
if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 6 ]]; then
npm install --silent --save-dev mocha@3.5.3
fi
- |
# supertest for http calls
# - use 2.0.0 for Node.js < 4
if [[ "$(cut -d. -f1 <<< "$TRAVIS_NODE_VERSION")" -lt 4 ]]; then
npm install --silent --save-dev supertest@2.0.0
fi
# Update Node.js modules
- |
# Prune and rebuild node_modules
if [[ -d node_modules ]]; then
npm prune
npm rebuild
fi
script:
# Run test script
- npm run test-ci
# Run linting
- npm run lint
after_script:
- |
# Upload coverage to coveralls
npm install --save-dev coveralls@2.12.0
coveralls < ./coverage/lcov.info

92
Charter.md Normal file
View File

@@ -0,0 +1,92 @@
# Express Charter
## Section 0: Guiding Principles
The Express project is part of the OpenJS Foundation which operates
transparently, openly, collaboratively, and ethically.
Project proposals, timelines, and status must not merely be open, but
also easily visible to outsiders.
## Section 1: Scope
Express is a http web server framework with a simple and expressive API
which is highly aligned with Node.js core. We aim to be the best in
class for writing performant, spec compliant, and powerful web servers
in Node.js. As one of the oldest and most popular web frameworks in
the ecosystem, we have an important place for new users and experts
alike.
### 1.1: In-scope
Express is made of many modules spread between three GitHub Orgs:
- [expressjs](http://github.com/expressjs/): Top level middleware and
libraries
- [pillarjs](http://github.com/pillarjs/): Components which make up
Express but can also be used for other web frameworks
- [jshttp](http://github.com/jshttp/): Low level http libraries
### 1.2: Out-of-Scope
Section Intentionally Left Blank
## Section 2: Relationship with OpenJS Foundation CPC.
Technical leadership for the projects within the OpenJS Foundation is
delegated to the projects through their project charters by the OpenJS
Cross Project Council (CPC). In the case of the Express project, it is
delegated to the Express Technical Committee ("TC").
This Technical Committee is in charge of both the day-to-day operations
of the project, as well as its technical management. This charter can
be amended by the TC requiring at least two approvals and a minimum two
week comment period for other TC members or CPC members to object. Any
changes the CPC wishes to propose will be considered a priority but
will follow the same process.
### 2.1 Other Formal Project Relationships
Section Intentionally Left Blank
## Section 3: Express Governing Body
The Express project is managed by the Technical Committee ("TC").
Members can be added to the TC at any time. Any committer can nominate
another committer to the TC and the TC uses its standard consensus
seeking process to evaluate whether or not to add this new member.
Members who do not participate consistently at the level of a majority
of the other members are expected to resign.
## Section 4: Roles & Responsibilities
The Express TC manages all aspects of both the technical and community
parts of the project. Members of the TC should attend the regular
meetings when possible, and be available for discussion of time
sensitive or important issues.
### Section 4.1 Project Operations & Management
Section Intentionally Left Blank
### Section 4.2: Decision-making, Voting, and/or Elections
The Express TC uses a "consensus seeking" process for issues that are
escalated to the TC. The group tries to find a resolution that has no
open objections among TC members. If a consensus cannot be reached
that has no objections then a majority wins vote is called. It is also
expected that the majority of decisions made by the TC are via a
consensus seeking process and that voting is only used as a last-resort.
Resolution may involve returning the issue to committers with
suggestions on how to move forward towards a consensus. It is not
expected that a meeting of the TC will resolve all issues on its
agenda during that meeting and may prefer to continue the discussion
happening among the committers.
### Section 4.3: Other Project Roles
Section Intentionally Left Blank
## Section 5: Definitions
Section Intentionally Left Blank

139
Code-Of-Conduct.md Normal file
View File

@@ -0,0 +1,139 @@
# Contributor Covenant Code of Conduct
As a member of the Open JS Foundation, Express has adopted the
[Contributor Covenant 2.0][cc-20-doc].
If an issue arises and you cannot resolve it directly with the parties
involved, you can report it to the Express project TC through the following
email: express-coc@lists.openjsf.org
In addition, the OpenJS Foundation maintains a Code of Conduct Panel (CoCP).
This is a foundation-wide team established to manage escalation when a reporter
believes that a report to a member project or the CPC has not been properly
handled. In order to escalate to the CoCP send an email to
coc-escalation@lists.openjsf.org.
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances
of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for
moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail
address, posting via an official social media account, or acting as an
appointed representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
express-coc@lists.openjsf.org. All complaints will be reviewed and
investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited
interaction with those enforcing the Code of Conduct, is allowed during this
period. Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
project community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant, version 2.0](cc-20-doc).
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
[cc-20-doc]: https://www.contributor-covenant.org/version/2/0/code_of_conduct/
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -1,3 +1,4 @@
# Express Collaborator Guide
## Website Issues
@@ -21,7 +22,7 @@ a future release of Express.
1. [Create an issue](https://github.com/expressjs/express/issues/new) for the
bug you want to fix or the feature that you want to add.
2. Create your own [fork](https://github.com/expressjs/express) on github, then
2. Create your own [fork](https://github.com/expressjs/express) on GitHub, then
checkout your fork.
3. Write your code in your local copy. It's good practice to create a branch for
each new issue you work on, although not compulsory.

View File

@@ -14,8 +14,9 @@ contributors can be involved in decision making.
* A **Committer** is a subset of contributors who have been given write access to the repository.
* A **TC (Technical Committee)** is a group of committers representing the required technical
expertise to resolve rare disputes.
* A **Triager** is a subset of contributors who have been given triage access to the repository.
# Logging Issues
## Logging Issues
Log an issue for any question or problem you might have. When in doubt, log an issue, and
any additional policies about what to include will be provided in the responses. The only
@@ -27,7 +28,7 @@ add appropriate metadata before the issue is addressed.
Please be courteous and respectful. Every participant is expected to follow the
project's Code of Conduct.
# Contributions
## Contributions
Any change to resources in this repository must be through pull requests. This applies to all changes
to documentation, code, binary files, etc. Even long term committers and TC members must use
@@ -41,7 +42,7 @@ weekends and other holiday periods to ensure active committers all have reasonab
become involved in the discussion and review process if they wish.
The default for each contribution is that it is accepted once no committer has an objection.
During review committers may also request that a specific contributor who is most versed in a
During a review, committers may also request that a specific contributor who is most versed in a
particular area gives a "LGTM" before the PR can be merged. There is no additional "sign off"
process for contributions to land. Once all issues brought by committers are addressed it can
be landed by any committer.
@@ -56,7 +57,36 @@ discuss pending contributions in order to find a resolution. It is expected that
small minority of issues be brought to the TC for resolution and that discussion and
compromise among committers be the default resolution mechanism.
# Becoming a Committer
## Becoming a Triager
Anyone can become a triager! Read more about the process of being a triager in
[the triage process document](Triager-Guide.md).
[Open an issue in `expressjs/express` repo](https://github.com/expressjs/express/issues/new)
to request the triage role. State that you have read and agree to the
[Code of Conduct](Code-Of-Conduct.md) and details of the role.
Here is an example issue content you can copy and paste:
```
Title: Request triager role for <your GitHub username>
I have read and understood the project's Code of Conduct.
I also have read and understood the process and best practices around Express triaging.
I request for a triager role for the following GitHub organizations:
jshttp
pillarjs
express
```
Once you have opened your issue, a member of the TC will add you to the `triage` team in
the organizations requested. They will then close the issue.
Happy triaging!
## Becoming a Committer
All contributors who land a non-trivial contribution should be on-boarded in a timely manner,
and added as a committer, and be given write access to the repository.
@@ -64,7 +94,7 @@ and added as a committer, and be given write access to the repository.
Committers are expected to follow this policy and continue to send pull requests, go through
proper review, and have other committers merge their pull requests.
# TC Process
## TC Process
The TC uses a "consensus seeking" process for issues that are escalated to the TC.
The group tries to find a resolution that has no open objections among TC members.
@@ -81,5 +111,3 @@ Members can be added to the TC at any time. Any committer can nominate another c
to the TC and the TC uses its standard consensus seeking process to evaluate whether or
not to add this new member. Members who do not participate consistently at the level of
a majority of the other members are expected to resign.

View File

@@ -1,3 +1,8 @@
5.x
===
This incorporates all changes after 4.17.1 up to 4.17.2.
5.0.0-alpha.8 / 2020-03-25
==========================
@@ -119,6 +124,39 @@ This is the first Express 5.0 alpha release, based off 4.10.1.
* add:
- `app.router` is a reference to the base router
4.17.2 / 2021-12-16
===================
* Fix handling of `undefined` in `res.jsonp`
* Fix handling of `undefined` when `"json escape"` is enabled
* Fix incorrect middleware execution with unanchored `RegExp`s
* Fix `res.jsonp(obj, status)` deprecation message
* Fix typo in `res.is` JSDoc
* deps: body-parser@1.19.1
- deps: bytes@3.1.1
- deps: http-errors@1.8.1
- deps: qs@6.9.6
- deps: raw-body@2.4.2
- deps: safe-buffer@5.2.1
- deps: type-is@~1.6.18
* deps: content-disposition@0.5.4
- deps: safe-buffer@5.2.1
* deps: cookie@0.4.1
- Fix `maxAge` option to reject invalid values
* deps: proxy-addr@~2.0.7
- Use `req.socket` over deprecated `req.connection`
- deps: forwarded@0.2.0
- deps: ipaddr.js@1.9.1
* deps: qs@6.9.6
* deps: safe-buffer@5.2.1
* deps: send@0.17.2
- deps: http-errors@1.8.1
- deps: ms@2.1.3
- pref: ignore empty http tokens
* deps: serve-static@1.14.2
- deps: send@0.17.2
* deps: setprototypeof@1.2.0
4.17.1 / 2019-05-25
===================

View File

@@ -4,7 +4,7 @@
[![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url]
[![Linux Build][travis-image]][travis-url]
[![Linux Build][ci-image]][ci-url]
[![Windows Build][appveyor-image]][appveyor-url]
[![Test Coverage][coveralls-image]][coveralls-url]
@@ -27,6 +27,9 @@ This is a [Node.js](https://nodejs.org/en/) module available through the
Before installing, [download and install Node.js](https://nodejs.org/en/download/).
Node.js 0.10 or higher is required.
If this is a brand new project, make sure to create a `package.json` first with
the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
@@ -95,7 +98,7 @@ $ npm start
## Philosophy
The Express philosophy is to provide small, robust tooling for HTTP servers, making
it a great solution for single page applications, web sites, hybrids, or public
it a great solution for single page applications, websites, hybrids, or public
HTTP APIs.
Express does not force you to use any specific ORM or template engine. With support for over
@@ -143,12 +146,12 @@ The current lead maintainer is [Douglas Christopher Wilson](https://github.com/d
[MIT](LICENSE)
[ci-image]: https://img.shields.io/github/workflow/status/expressjs/express/ci/master.svg?label=linux
[ci-url]: https://github.com/expressjs/express/actions?query=workflow%3Aci
[npm-image]: https://img.shields.io/npm/v/express.svg
[npm-url]: https://npmjs.org/package/express
[downloads-image]: https://img.shields.io/npm/dm/express.svg
[downloads-url]: https://npmjs.org/package/express
[travis-image]: https://img.shields.io/travis/expressjs/express/master.svg?label=linux
[travis-url]: https://travis-ci.org/expressjs/express
[downloads-url]: https://npmcharts.com/compare/express?minimal=true
[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/express/master.svg?label=windows
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/express
[coveralls-image]: https://img.shields.io/coveralls/expressjs/express/master.svg

View File

@@ -16,6 +16,10 @@ contributions.
Report security bugs by emailing the lead maintainer in the Readme.md file.
To ensure the timely response to your report, please ensure that the entirety
of the report is contained within the email body and not solely behind a web
link or an attachment.
The lead maintainer will acknowledge your email within 48 hours, and will send a
more detailed response within 48 hours indicating the next steps in handling
your report. After the initial reply to your report, the security team will

63
Triager-Guide.md Normal file
View File

@@ -0,0 +1,63 @@
# Express Triager Guide
## Issue Triage Process
When a new issue or pull request is opened the issue will be labeled with `needs triage`.
If a triage team member is available they can help make sure all the required information
is provided. Depending on the issue or PR there are several next labels they can add for further
classification:
* `needs triage`: This can be kept if the triager is unsure which next steps to take
* `awaiting more info`: If more info has been requested from the author, apply this label.
* `question`: User questions that do not appear to be bugs or enhancements.
* `discuss`: Topics for discussion. Might end in an `enhancement` or `question` label.
* `bug`: Issues that present a reasonable conviction there is a reproducible bug.
* `enhancement`: Issues that are found to be a reasonable candidate feature additions.
In all cases, issues may be closed by maintainers if they don't receive a timely response when
further information is sought, or when additional questions are asked.
## Approaches and Best Practices for getting into triage contributions
Review the organization's [StatusBoard](https://expressjs.github.io/statusboard/),
pay special attention to these columns: stars, watchers, open issues, and contributors.
This gives you a general idea about the criticality and health of the repository.
Pick a few projects based on that criteria, your interests, and skills (existing or aspiring).
Review the project's contribution guideline if present. In a nutshell,
commit to the community's standards and values. Review the
documentation, for most of the projects it is just the README.md, and
make sure you understand the key APIs, semantics, configurations, and use cases.
It might be helpful to write your own test apps to re-affirm your
understanding of the key functions. This may identify some gaps in
documentation, record those as they might be good PR's to open.
Skim through the issue backlog; identify low hanging issues and mostly new ones.
From those, attempt to recreate issues based on the OP description and
ask questions if required. No question is a bad question!
## Removal of Triage Role
There are a few cases where members can be removed as triagers:
- Breaking the CoC or project contributor guidelines
- Abuse or misuse of the role as deemed by the TC
- Lack of participation for more than 6 months
If any of these happen we will discuss as a part of the triage portion of the regular TC meetings.
If you have questions feel free to reach out to any of the TC members.
## Other Helpful Hints:
- Everyone is welcome to attend the [Express Technical Committee Meetings](https://github.com/expressjs/discussions#expressjs-tc-meetings), and as a triager, it might help to get a better idea of what's happening with the project.
- When exploring the module's functionality there are a few helpful steps:
- Turn on `DEBUG=*` (see https://www.npmjs.com/package/debug) to get detailed log information
- It is also a good idea to do live debugging to follow the control flow, try using `node --inspect`
- It is a good idea to make at least one pass of reading through the entire source
- When reviewing the list of open issues there are some common types and suggested actions:
- New/unattended issues or simple questions: A good place to start
- Hard bugs & ongoing discussions: always feel free to chime in and help
- Issues that imply gaps in the documentation: open PRs with changes or help the user to do so
- For recurring issues, it is helpful to create functional examples to demonstrate (publish as gists or a repo)
- Review and identify the maintainers. If necessary, at-mention one or more of them if you are unsure what to do
- Make sure all your interactions are professional, welcoming, and respectful to the parties involved.

View File

@@ -9,11 +9,13 @@ environment:
- nodejs_version: "5.12"
- nodejs_version: "6.17"
- nodejs_version: "7.10"
- nodejs_version: "8.16"
- nodejs_version: "8.17"
- nodejs_version: "9.11"
- nodejs_version: "10.15"
- nodejs_version: "10.24"
- nodejs_version: "11.15"
- nodejs_version: "12.3"
- nodejs_version: "12.22"
- nodejs_version: "13.14"
- nodejs_version: "14.18"
cache:
- node_modules
install:
@@ -23,24 +25,43 @@ install:
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) }
# Configure npm
- ps: |
# Skip updating shrinkwrap / lock
npm config set loglevel error
npm config set shrinkwrap false
# Remove all non-test dependencies
- ps: |
# Remove example dependencies
npm rm --silent --save-dev connect-redis
# Remove lint dependencies
cmd.exe /c "node -pe `"Object.keys(require('./package').devDependencies).join('\n')`"" | `
sls "^eslint(-|$)" | `
%{ npm rm --silent --save-dev $_ }
# Setup Node.js version-specific dependencies
- ps: |
# mocha for testing
# - use 3.x for Node.js < 6
if ($env:nodejs_version.split(".")[0] -lt 6) {
# - use 3.x for Node.js < 4
# - use 5.x for Node.js < 6
# - use 6.x for Node.js < 8
# - use 7.x for Node.js < 10
# - use 8.x for Node.js < 12
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev mocha@3.5.3
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev mocha@5.2.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
npm install --silent --save-dev mocha@6.2.2
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
npm install --silent --save-dev mocha@7.2.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 12) {
npm install --silent --save-dev mocha@8.4.0
}
- ps: |
# supertest for http calls
# - use 2.0.0 for Node.js < 4
if ($env:nodejs_version.split(".")[0] -lt 4) {
# - use 3.4.2 for Node.js < 6
if ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev supertest@2.0.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev supertest@3.4.2
}
# Update Node.js modules
- ps: |
@@ -59,6 +80,4 @@ test_script:
npm --version
# Run test script
- npm run test-ci
# Run linting
- npm run lint
version: "{build}"

30
examples/README.md Normal file
View File

@@ -0,0 +1,30 @@
# Express examples
This page contains list of examples using Express.
- [auth](./auth) - Authentication with login and password
- [content-negotiation](./content-negotiation) - HTTP content negotiation
- [cookie-sessions](./cookie-sessions) - Working with cookie-based sessions
- [cookies](./cookies) - Working with cookies
- [downloads](./downloads) - Transferring files to client
- [ejs](./ejs) - Working with Embedded JavaScript templating (ejs)
- [error-pages](./error-pages) - Creating error pages
- [error](./error) - Working with error middleware
- [hello-world](./hello-world) - Simple request handler
- [markdown](./markdown) - Markdown as template engine
- [multi-router](./multi-router) - Working with multiple Express routers
- [multipart](./multipart) - Accepting multipart-encoded forms
- [mvc](./mvc) - MVC-style controllers
- [online](./online) - Tracking online user activity with `online` and `redis` packages
- [params](./params) - Working with route parameters
- [resource](./resource) - Multiple HTTP operations on the same resource
- [route-map](./route-map) - Organizing routes using a map
- [route-middleware](./route-middleware) - Working with route middleware
- [route-separation](./route-separation) - Organizing routes per each resource
- [search](./search) - Search API
- [session](./session) - User sessions
- [static-files](./static-files) - Serving static files
- [vhost](./vhost) - Working with virtual hosts
- [view-constructor](./view-constructor) - Rendering views dynamically
- [view-locals](./view-locals) - Saving data in request object between middleware calls
- [web-service](./web-service) - Simple API service

View File

@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><%= title %></title>
<style>
body {

View File

@@ -1,6 +1,5 @@
<% var title = 'Authentication Example' %>
<% include head %>
<%- include('head', { title: 'Authentication Example' }) -%>
<h1>Login</h1>
<%- message %>
@@ -19,4 +18,4 @@ Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj"
</p>
</form>
<% include foot %>
<%- include('foot') -%>

View File

@@ -0,0 +1,3 @@
* milk
* eggs
* bread

View File

@@ -7,11 +7,12 @@ var path = require('path');
var app = module.exports = express();
app.get('/', function(req, res){
res.send('<ul>'
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
+ '<li>Download <a href="/files/CCTV大赛上海分赛区.txt">CCTV大赛上海分赛区.txt</a>.</li>'
+ '</ul>');
res.send('<ul>' +
'<li>Download <a href="/files/notes/groceries.txt">notes/groceries.txt</a>.</li>' +
'<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>' +
'<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>' +
'<li>Download <a href="/files/CCTV大赛上海分赛区.txt">CCTV大赛上海分赛区.txt</a>.</li>' +
'</ul>')
});
// /files/* is accessed via req.params[0]

View File

@@ -1,4 +1,4 @@
body {
padding: 50px 80px;
font: 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif;
font: 14px "Helvetica Neue", "Lucida Grande", Arial, sans-serif;
}

View File

@@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><%= title %></title>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>

View File

@@ -1,4 +1,4 @@
<% include header.html %>
<%- include('header.html') -%>
<h1>Users</h1>
<ul id="users">
@@ -7,4 +7,4 @@
<% }) %>
</ul>
<% include footer.html %>
<%- include('footer.html') -%>

View File

@@ -1,3 +1,3 @@
<% include error_header %>
<%- include('error_header') -%>
<h2>Cannot find <%= url %></h2>
<% include footer %>
<%- include('footer') -%>

View File

@@ -1,8 +1,8 @@
<% include error_header %>
<%- include('error_header') -%>
<h2>Error: <%= error.message %></h2>
<% if (settings['verbose errors']) { %>
<pre><%= error.stack %></pre>
<% } else { %>
<p>An error occurred!</p>
<% } %>
<% include footer %>
<%- include('footer') -%>

View File

@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Error</title>
</head>

View File

@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Custom Pages Example</title>
</head>

View File

@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/style.css">
<title>Edit <%= pet.name %></title>
</head>

View File

@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/style.css">
<title><%= pet.name %></title>
</head>

View File

@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/style.css">
<title>Edit {{user.name}}</title>
</head>

View File

@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/style.css">
<title>Users</title>
</head>

View File

@@ -1,6 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="/style.css">
<title>{{user.name}}</title>
</head>

View File

@@ -2,6 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Not Found</title>
<link rel="stylesheet" href="/style.css">
</head>

View File

@@ -2,6 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Internal Server Error</title>
<link rel="stylesheet" href="/style.css">
</head>

View File

@@ -8,7 +8,7 @@ var app = module.exports = express();
// Faux database
var users = [
{ name: 'tj' }
{ name: 'tj' }
, { name: 'tobi' }
, { name: 'loki' }
, { name: 'jane' }

View File

@@ -26,7 +26,7 @@ app.resource = function(path, obj) {
// Fake records
var users = [
{ name: 'tj' }
{ name: 'tj' }
, { name: 'ciaran' }
, { name: 'aaron' }
, { name: 'guillermo' }

View File

@@ -2,6 +2,7 @@
* Module dependencies.
*/
var escapeHtml = require('escape-html')
var express = require('../../lib/express');
var verbose = process.env.NODE_ENV !== 'test'
@@ -31,7 +32,7 @@ var users = {
},
get: function(req, res){
res.send('user ' + req.params.uid);
res.send('user ' + escapeHtml(req.params.uid))
},
delete: function(req, res){
@@ -41,11 +42,11 @@ var users = {
var pets = {
list: function(req, res){
res.send('user ' + req.params.uid + '\'s pets');
res.send('user ' + escapeHtml(req.params.uid) + '\'s pets')
},
delete: function(req, res){
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
res.send('delete ' + escapeHtml(req.params.uid) + '\'s pet ' + escapeHtml(req.params.pid))
}
};

View File

@@ -15,7 +15,7 @@ var app = express();
// Dummy users
var users = [
{ id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' }
{ id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' }
, { id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' }
, { id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' }
];

View File

@@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><%= title %></title>
<link rel="stylesheet" href="/style.css">
</head>

View File

@@ -1,4 +1,4 @@
<% include header %>
<%- include('header') -%>
<h1><%= title %></h1>
@@ -7,4 +7,4 @@
<li>Visit the <a href="/posts">posts</a> page.</li>
</ul>
<% include footer %>
<%- include('footer') -%>

View File

@@ -1,4 +1,4 @@
<% include ../header %>
<%- include('../header') -%>
<h1>Posts</h1>
@@ -9,4 +9,4 @@
<% }) %>
</dl>
<% include ../footer %>
<%- include('../footer') -%>

View File

@@ -1,4 +1,4 @@
<% include ../header %>
<%- include('../header') -%>
<h1>Editing <%= user.name %></h1>
@@ -20,4 +20,4 @@
</form>
</div>
<% include ../footer %>
<%- include('../footer') -%>

View File

@@ -1,4 +1,4 @@
<% include ../header %>
<%- include('../header') -%>
<h1><%= title %></h1>
@@ -11,4 +11,4 @@
<% }) %>
</div>
<% include ../footer %>
<%- include('../footer') -%>

View File

@@ -1,4 +1,4 @@
<% include ../header %>
<%- include('../header') -%>
<h1><%= user.name %></h1>
@@ -6,4 +6,4 @@
<p>Email: <%= user.email %></p>
</div>
<% include ../footer %>
<%- include('../footer') -%>

View File

@@ -2,6 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Search example</title>
<style type="text/css">
body {

View File

@@ -2,6 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><%= title %></title>
<style media="screen">
body {

View File

@@ -8,7 +8,7 @@ var app = module.exports = express();
// create an error with .status. we
// can then use the property in our
// custom error handler (Connect repects this prop as well)
// custom error handler (Connect respects this prop as well)
function error(status, msg) {
var err = new Error(msg);
@@ -55,7 +55,7 @@ var repos = [
];
var users = [
{ name: 'tobi' }
{ name: 'tobi' }
, { name: 'loki' }
, { name: 'jane' }
];
@@ -105,7 +105,7 @@ app.use(function(err, req, res, next){
// invoke next() and do not respond.
app.use(function(req, res){
res.status(404);
res.send({ error: "Lame, can't find that" });
res.send({ error: "Sorry, can't find that" })
});
/* istanbul ignore next */

View File

@@ -272,7 +272,7 @@ app.route = function route(path) {
* In this case EJS provides a `.renderFile()` method with
* the same signature that Express expects: `(path, options, callback)`,
* though note that it aliases this method as `ejs.__express` internally
* so if you're using ".ejs" extensions you dont need to do anything.
* so if you're using ".ejs" extensions you don't need to do anything.
*
* Some template engines do not follow this convention, the
* [Consolidate.js](https://github.com/tj/consolidate.js)

View File

@@ -230,7 +230,7 @@ defineGetter(req, 'query', function query(){
/**
* Check if the incoming request contains the "Content-Type"
* header field, and it contains the give mime `type`.
* header field, and it contains the given mime `type`.
*
* Examples:
*

View File

@@ -266,10 +266,15 @@ res.jsonp = function jsonp(obj) {
// restrict callback charset
callback = callback.replace(/[^\[\]\w$.]/g, '');
// replace chars not allowed in JavaScript that are in JSON
body = body
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
if (body === undefined) {
// empty argument
body = ''
} else if (typeof body === 'string') {
// replace chars not allowed in JavaScript that are in JSON
body = body
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029')
}
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
// the typeof check is just to reduce client error noise
@@ -308,7 +313,7 @@ res.sendStatus = function sendStatus(statusCode) {
*
* Automatically sets the _Content-Type_ response header field.
* The callback `callback(err)` is invoked when the transfer is complete
* or when an error occurs. Be sure to check `res.sentHeader`
* or when an error occurs. Be sure to check `res.headersSent`
* if you wish to attempt responding, as the header and some data
* may have already been transferred.
*
@@ -495,7 +500,7 @@ res.type = function contentType(type) {
* res.send('<p>hey</p>');
* },
*
* 'appliation/json': function(){
* 'application/json': function () {
* res.send({ message: 'hey' });
* }
* });
@@ -598,7 +603,7 @@ res.append = function append(field, val) {
// concat the new and prev vals
value = Array.isArray(prev) ? prev.concat(val)
: Array.isArray(val) ? [prev].concat(val)
: [prev, val];
: [prev, val]
}
return this.set(field, value);
@@ -983,7 +988,7 @@ function stringify (value, replacer, spaces, escape) {
? JSON.stringify(value, replacer, spaces)
: JSON.stringify(value);
if (escape) {
if (escape && typeof json === 'string') {
json = json.replace(/[<>&]/g, function (c) {
switch (c.charCodeAt(0)) {
case 0x3c:

View File

@@ -117,6 +117,7 @@ exports.compileETag = function(val) {
switch (val) {
case true:
case 'weak':
fn = exports.wetag;
break;
case false:
@@ -124,9 +125,6 @@ exports.compileETag = function(val) {
case 'strong':
fn = exports.etag;
break;
case 'weak':
fn = exports.wetag;
break;
default:
throw new TypeError('unknown value for etag function: ' + val);
}
@@ -151,6 +149,7 @@ exports.compileQueryParser = function compileQueryParser(val) {
switch (val) {
case true:
case 'simple':
fn = querystring.parse;
break;
case false:
@@ -158,9 +157,6 @@ exports.compileQueryParser = function compileQueryParser(val) {
case 'extended':
fn = parseExtendedQueryString;
break;
case 'simple':
fn = querystring.parse;
break;
default:
throw new TypeError('unknown value for query parser function: ' + val);
}

View File

@@ -20,6 +20,7 @@
"framework",
"sinatra",
"web",
"http",
"rest",
"restful",
"router",
@@ -29,10 +30,10 @@
"dependencies": {
"accepts": "~1.3.7",
"array-flatten": "2.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"body-parser": "1.19.1",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie": "0.4.1",
"cookie-signature": "1.0.6",
"debug": "3.1.0",
"depd": "~1.1.2",
@@ -46,14 +47,14 @@
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-is-absolute": "1.0.1",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"proxy-addr": "~2.0.7",
"qs": "6.9.6",
"range-parser": "~1.2.1",
"router": "2.0.0-alpha.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"safe-buffer": "5.2.1",
"send": "0.17.2",
"serve-static": "1.14.2",
"setprototypeof": "1.2.0",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
@@ -61,22 +62,22 @@
},
"devDependencies": {
"after": "0.8.2",
"connect-redis": "3.4.1",
"cookie-parser": "~1.4.4",
"cookie-session": "1.3.3",
"ejs": "2.6.1",
"eslint": "2.13.1",
"express-session": "1.16.1",
"hbs": "4.0.4",
"connect-redis": "3.4.2",
"cookie-parser": "1.4.6",
"cookie-session": "2.0.0",
"ejs": "3.1.6",
"eslint": "7.32.0",
"express-session": "1.17.2",
"hbs": "4.2.0",
"istanbul": "0.4.5",
"marked": "0.6.2",
"marked": "0.7.0",
"method-override": "3.0.0",
"mocha": "5.2.0",
"morgan": "1.9.1",
"multiparty": "4.2.1",
"mocha": "9.1.3",
"morgan": "1.10.0",
"multiparty": "4.2.2",
"pbkdf2-password": "1.2.1",
"should": "13.2.3",
"supertest": "3.3.0",
"supertest": "6.1.6",
"vhost": "~3.0.2"
},
"engines": {

View File

@@ -32,7 +32,7 @@ describe('Router', function(){
var another = new Router();
another.get('/:bar', function(req, res){
req.params.bar.should.equal('route');
assert.strictEqual(req.params.bar, 'route')
res.end();
});
router.use('/:foo', another);
@@ -44,7 +44,7 @@ describe('Router', function(){
var router = new Router();
router.use(function (req, res) {
false.should.be.true()
throw new Error('should not be called')
});
router.handle({ url: '', method: 'GET' }, {}, done);
@@ -85,7 +85,7 @@ describe('Router', function(){
var res = {
send: function(val) {
val.should.equal('foo');
assert.strictEqual(val, 'foo')
done();
}
}

View File

@@ -13,7 +13,7 @@ describe('cookie-sessions', function () {
it('should set a session cookie', function (done) {
request(app)
.get('/')
.expect('Set-Cookie', /express:sess=/)
.expect('Set-Cookie', /session=/)
.expect(200, done)
})

View File

@@ -11,6 +11,15 @@ describe('downloads', function(){
})
})
describe('GET /files/notes/groceries.txt', function () {
it('should have a download header', function (done) {
request(app)
.get('/files/notes/groceries.txt')
.expect('Content-Disposition', 'attachment; filename="groceries.txt"')
.expect(200, done)
})
})
describe('GET /files/amazing.txt', function(){
it('should have a download header', function(done){
request(app)

View File

@@ -97,9 +97,9 @@ describe('web-service', function(){
describe('when requesting an invalid route', function(){
it('should respond with 404 json', function(done){
request(app)
.get('/api/something?api-key=bar')
.expect('Content-Type', /json/)
.expect(404, '{"error":"Lame, can\'t find that"}', done)
.get('/api/something?api-key=bar')
.expect('Content-Type', /json/)
.expect(404, '{"error":"Sorry, can\'t find that"}', done)
})
})
})

View File

@@ -26,7 +26,7 @@ describe('HEAD', function(){
});
request(app)
.get('/tobi')
.head('/tobi')
.expect(200, function(err, res){
if (err) return done(err);
var headers = res.headers;

View File

@@ -5,10 +5,6 @@ describe('app.listen()', function(){
it('should wrap with an HTTP server', function(done){
var app = express();
app.get('/tobi', function(req, res){
res.end('got tobi!');
});
var server = app.listen(9999, function(){
server.close();
done();

View File

@@ -144,7 +144,7 @@ describe('app', function(){
});
app.param('user', function(req, res, next, user) {
next(new Error('invalid invokation'));
next(new Error('invalid invocation'))
});
app.post('/:user', function(req, res, next) {

View File

@@ -188,6 +188,35 @@ describe('app.router', function(){
.get('/user/10/edit')
.expect('editing user 10', done);
})
it.skip('should ensure regexp matches path prefix', function (done) {
var app = express()
var p = []
app.use(/\/api.*/, function (req, res, next) {
p.push('a')
next()
})
app.use(/api/, function (req, res, next) {
p.push('b')
next()
})
app.use(/\/test/, function (req, res, next) {
p.push('c')
next()
})
app.use(function (req, res) {
res.end()
})
request(app)
.get('/test/api/1234')
.expect(200, function (err) {
if (err) return done(err)
assert.deepEqual(p, ['c'])
done()
})
})
})
describe('case sensitivity', function(){

View File

@@ -429,7 +429,7 @@ describe('express.static()', function () {
describe('lastModified', function () {
describe('when false', function () {
it('should not include Last-Modifed', function (done) {
it('should not include Last-Modified', function (done) {
request(createApp(fixtures, { 'lastModified': false }))
.get('/nums.txt')
.expect(utils.shouldNotHaveHeader('Last-Modified'))
@@ -438,7 +438,7 @@ describe('express.static()', function () {
})
describe('when true', function () {
it('should include Last-Modifed', function (done) {
it('should include Last-Modified', function (done) {
request(createApp(fixtures, { 'lastModified': true }))
.get('/nums.txt')
.expect('Last-Modified', /^\w{3}, \d+ \w+ \d+ \d+:\d+:\d+ \w+$/)

View File

@@ -115,6 +115,19 @@ describe('res', function(){
.get('/')
.expect(200, optionsCopy, done)
})
it('should throw an error with invalid maxAge', function (done) {
var app = express()
app.use(function (req, res) {
res.cookie('name', 'tobi', { maxAge: 'foobar' })
res.end()
})
request(app)
.get('/')
.expect(500, /option maxAge is invalid/, done)
})
})
describe('signed', function(){

View File

@@ -122,6 +122,21 @@ describe('res', function(){
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"\\u0026":"\\u003cscript\\u003e"}', done)
})
it('should not break undefined escape', function (done) {
var app = express()
app.enable('json escape')
app.use(function (req, res) {
res.json(undefined)
})
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '', done)
})
})
describe('"json replacer" setting', function(){

View File

@@ -156,89 +156,93 @@ describe('res', function(){
.expect(200, /cb\(\{"hello":"world"\}\);$/, done);
})
describe('when given primitives', function(){
it('should respond with json', function(done){
var app = express();
describe('when given undefined', function () {
it('should invoke callback with no arguments', function (done) {
var app = express()
app.use(function(req, res){
res.jsonp(null);
});
app.use(function (req, res) {
res.jsonp(undefined)
})
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, 'null', done)
.get('/?callback=cb')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\(\)/, done)
})
})
describe('when given an array', function(){
it('should respond with json', function(done){
var app = express();
describe('when given null', function () {
it('should invoke callback with null', function (done) {
var app = express()
app.use(function(req, res){
res.jsonp(['foo', 'bar', 'baz']);
});
app.use(function (req, res) {
res.jsonp(null)
})
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '["foo","bar","baz"]', done)
.get('/?callback=cb')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\(null\)/, done)
})
})
describe('when given an object', function(){
it('should respond with json', function(done){
var app = express();
describe('when given a string', function () {
it('should invoke callback with a string', function (done) {
var app = express()
app.use(function(req, res){
res.jsonp({ name: 'tobi' });
});
app.use(function (req, res) {
res.jsonp('tobi')
})
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"name":"tobi"}', done)
.get('/?callback=cb')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\("tobi"\)/, done)
})
})
describe('when given primitives', function(){
it('should respond with json for null', function(done){
var app = express();
describe('when given a number', function () {
it('should invoke callback with a number', function (done) {
var app = express()
app.use(function(req, res){
res.jsonp(null);
});
app.use(function (req, res) {
res.jsonp(42)
})
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, 'null', done)
.get('/?callback=cb')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\(42\)/, done)
})
})
it('should respond with json for Number', function(done){
var app = express();
describe('when given an array', function () {
it('should invoke callback with an array', function (done) {
var app = express()
app.use(function(req, res){
res.jsonp(300);
});
app.use(function (req, res) {
res.jsonp(['foo', 'bar', 'baz'])
})
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '300', done)
.get('/?callback=cb')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\(\["foo","bar","baz"\]\)/, done)
})
})
it('should respond with json for String', function(done){
var app = express();
describe('when given an object', function () {
it('should invoke callback with an object', function (done) {
var app = express()
app.use(function(req, res){
res.jsonp('str');
});
app.use(function (req, res) {
res.jsonp({ name: 'tobi' })
})
request(app)
.get('/')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '"str"', done)
.get('/?callback=cb')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\(\{"name":"tobi"\}\)/, done)
})
})
@@ -262,6 +266,21 @@ describe('res', function(){
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /foo\({"\\u0026":"\\u2028\\u003cscript\\u003e\\u2029"}\)/, done)
})
it('should not break undefined escape', function (done) {
var app = express()
app.enable('json escape')
app.use(function (req, res) {
res.jsonp(undefined)
})
request(app)
.get('/?callback=cb')
.expect('Content-Type', 'text/javascript; charset=utf-8')
.expect(200, /cb\(\)/, done)
})
})
describe('"json replacer" setting', function(){