mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
444 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea3d605652 | ||
|
|
40435ec997 | ||
|
|
7137bf567d | ||
|
|
bd1672f0a4 | ||
|
|
9395db4c22 | ||
|
|
19a2eeb476 | ||
|
|
d7da22550d | ||
|
|
961dbff904 | ||
|
|
9e0fa7f1ca | ||
|
|
9e067ad2cb | ||
|
|
de5fb62b1a | ||
|
|
b208b24f83 | ||
|
|
78e55108e4 | ||
|
|
48817a798f | ||
|
|
a4bd4373b2 | ||
|
|
a50f1098d0 | ||
|
|
e2d725e016 | ||
|
|
e0066227f7 | ||
|
|
44881fabe3 | ||
|
|
1dbaae51dd | ||
|
|
56e90e3c72 | ||
|
|
713d2aed93 | ||
|
|
e0aa8bf74e | ||
|
|
85770a71fc | ||
|
|
daf66beda4 | ||
|
|
b2af101821 | ||
|
|
3eb16c233c | ||
|
|
582381bceb | ||
|
|
5e16f400f1 | ||
|
|
43dff4ceb3 | ||
|
|
04beebb2c0 | ||
|
|
1adee79e63 | ||
|
|
bd5951e603 | ||
|
|
deffce5704 | ||
|
|
48777dc377 | ||
|
|
9467a392e3 | ||
|
|
9f019c8c69 | ||
|
|
cf37240e73 | ||
|
|
60f87f8074 | ||
|
|
fde8f647d3 | ||
|
|
6da454c7fb | ||
|
|
5cf473dc5d | ||
|
|
65494692c2 | ||
|
|
bc2986fe59 | ||
|
|
58cfc9911b | ||
|
|
ad4456c491 | ||
|
|
1ba9a9ac23 | ||
|
|
ae0b630ac7 | ||
|
|
5ea2a8ff8e | ||
|
|
de41c0bfa4 | ||
|
|
a13938eed7 | ||
|
|
1b6e7004b7 | ||
|
|
2d1dade36a | ||
|
|
df4f2719db | ||
|
|
c087a45b9c | ||
|
|
347d4db3ca | ||
|
|
aabf7802a9 | ||
|
|
3763d73a1f | ||
|
|
8acaa9a3ea | ||
|
|
dbf092d3ea | ||
|
|
efd7032f71 | ||
|
|
2189ff14a9 | ||
|
|
245fa8942a | ||
|
|
1b6ad08095 | ||
|
|
a1fffda3f2 | ||
|
|
f44368f8be | ||
|
|
64dd446aa8 | ||
|
|
d43b074f0b | ||
|
|
05fd1e4441 | ||
|
|
85c96fd64e | ||
|
|
d32ed68b29 | ||
|
|
57d3dfd9f8 | ||
|
|
eece3850bc | ||
|
|
8eb95ae579 | ||
|
|
67168fe231 | ||
|
|
c0089d971b | ||
|
|
dc8acc8676 | ||
|
|
7027b37764 | ||
|
|
b4550fbe7a | ||
|
|
4012846d25 | ||
|
|
6d9b127989 | ||
|
|
504a51c040 | ||
|
|
7f96896f67 | ||
|
|
f59de6ae3d | ||
|
|
72475543bc | ||
|
|
146a13ede7 | ||
|
|
9722202df9 | ||
|
|
51f52901eb | ||
|
|
8b6dc6ceec | ||
|
|
081b811b10 | ||
|
|
1f71fae23b | ||
|
|
acc4a619d9 | ||
|
|
1b43166fca | ||
|
|
6022567c75 | ||
|
|
12ff56e1e4 | ||
|
|
668f545fd4 | ||
|
|
7bc5f1af96 | ||
|
|
8de1230d03 | ||
|
|
034165caeb | ||
|
|
a9f15aaefc | ||
|
|
f87abb3493 | ||
|
|
cd7d241a5d | ||
|
|
9f4dbae083 | ||
|
|
f2bbd10ae7 | ||
|
|
92c859dd05 | ||
|
|
485b6f86ac | ||
|
|
906164b204 | ||
|
|
c63424a0a1 | ||
|
|
bbed8021c6 | ||
|
|
fefd729037 | ||
|
|
1a99bb0519 | ||
|
|
4420a7bd94 | ||
|
|
1678613db1 | ||
|
|
f3a47e39a4 | ||
|
|
3e3d35777d | ||
|
|
eb326d7ecb | ||
|
|
7ef92f8a17 | ||
|
|
c48014fde8 | ||
|
|
034f261fea | ||
|
|
abd1de73c1 | ||
|
|
fb3946f454 | ||
|
|
4f291e7d55 | ||
|
|
94377f681d | ||
|
|
0437c513f2 | ||
|
|
1f70b76c45 | ||
|
|
0b39fa2f7c | ||
|
|
0d6c64fdfd | ||
|
|
c238aca438 | ||
|
|
6b506d801a | ||
|
|
dc48f27f60 | ||
|
|
8e14e06ebf | ||
|
|
572657ee4a | ||
|
|
cfae537d3b | ||
|
|
a3d635309c | ||
|
|
a2e323a012 | ||
|
|
e73913b583 | ||
|
|
ddc93aa0e2 | ||
|
|
6723b4419a | ||
|
|
d1d9631b97 | ||
|
|
1a17888073 | ||
|
|
f6dca56e72 | ||
|
|
6210e47cb6 | ||
|
|
1bcc9b1236 | ||
|
|
421ad5aaaa | ||
|
|
505f8482f2 | ||
|
|
bd47aeb88d | ||
|
|
305f982bd7 | ||
|
|
9375a9afa9 | ||
|
|
14cf9c5636 | ||
|
|
605983fd0e | ||
|
|
e06766c573 | ||
|
|
76eaa326ee | ||
|
|
c12cc88392 | ||
|
|
23c22ce3fe | ||
|
|
791cabf939 | ||
|
|
fc40702cb7 | ||
|
|
c762b16f62 | ||
|
|
5d642af8c3 | ||
|
|
7fcf1d7d5b | ||
|
|
7ee41cc23f | ||
|
|
8ddab697cc | ||
|
|
d038689658 | ||
|
|
c9531ac1f1 | ||
|
|
98224d2c4f | ||
|
|
2e1284beb6 | ||
|
|
999546dfde | ||
|
|
cc25a04d10 | ||
|
|
f90f9dde3f | ||
|
|
b69b7605b0 | ||
|
|
c6039af39d | ||
|
|
bdf604a77e | ||
|
|
76c56d1ab8 | ||
|
|
2cf830b29e | ||
|
|
e5502690d2 | ||
|
|
31dd549f35 | ||
|
|
e3dd191d54 | ||
|
|
fd48bfe8fe | ||
|
|
d7ae24228d | ||
|
|
992cd085fb | ||
|
|
20a25489de | ||
|
|
741a5aac9c | ||
|
|
3d56e7374d | ||
|
|
12bc16e72f | ||
|
|
8931b2311a | ||
|
|
bb84cf955f | ||
|
|
f3d99a4fdb | ||
|
|
dd2b897774 | ||
|
|
4bcbf67482 | ||
|
|
b5a280111f | ||
|
|
05136550c7 | ||
|
|
193bed2649 | ||
|
|
e7a02f6a25 | ||
|
|
6847405974 | ||
|
|
f627ca8d0b | ||
|
|
e83eab85e4 | ||
|
|
1589ce2153 | ||
|
|
547ea368c2 | ||
|
|
8eee818e0b | ||
|
|
7c8456fcde | ||
|
|
53ee474c44 | ||
|
|
bd118c47df | ||
|
|
f2cf28c2f3 | ||
|
|
e44f024dab | ||
|
|
2493239192 | ||
|
|
c73b1bee51 | ||
|
|
4f8167f23f | ||
|
|
c95a1077d2 | ||
|
|
7d93503914 | ||
|
|
2b2a1b28f3 | ||
|
|
4416fb2746 | ||
|
|
60f8e77d66 | ||
|
|
23f021a3e1 | ||
|
|
67116cc5e6 | ||
|
|
42b944295a | ||
|
|
7a3b5aea11 | ||
|
|
e5ec966b2f | ||
|
|
5699d64b99 | ||
|
|
747fccfb44 | ||
|
|
9665aa2153 | ||
|
|
2f37f4b28d | ||
|
|
1a59246746 | ||
|
|
963d795d24 | ||
|
|
2f96412636 | ||
|
|
5a4310e9da | ||
|
|
2f8ac6726f | ||
|
|
ef7ad681b2 | ||
|
|
11a77a3fff | ||
|
|
ee90042d0c | ||
|
|
97b2d70d8a | ||
|
|
a4fcd91084 | ||
|
|
a559ca2d58 | ||
|
|
c398a9903d | ||
|
|
1cea9cedec | ||
|
|
e33c5035bb | ||
|
|
9848645a8e | ||
|
|
cb59086305 | ||
|
|
ce087e559e | ||
|
|
93dd15cd61 | ||
|
|
659c0b154c | ||
|
|
09c80bf823 | ||
|
|
b53feaa1d8 | ||
|
|
d51d1ea57a | ||
|
|
fc95112145 | ||
|
|
de7ffca33f | ||
|
|
2ac2509854 | ||
|
|
def241f91f | ||
|
|
a48c55896b | ||
|
|
7bea53b92b | ||
|
|
3314f767e1 | ||
|
|
2870add230 | ||
|
|
1f906d4ffb | ||
|
|
3c0ff8133b | ||
|
|
f247a4182f | ||
|
|
71d56db947 | ||
|
|
3ec7cca550 | ||
|
|
6c7a367338 | ||
|
|
f73ff92430 | ||
|
|
2a455890b9 | ||
|
|
9302acc5e4 | ||
|
|
5b4d4b4ab1 | ||
|
|
7d6c1e5c5c | ||
|
|
3b3e1fc38a | ||
|
|
5915894af3 | ||
|
|
bb53b20d4c | ||
|
|
d414a2dc73 | ||
|
|
f5a240636d | ||
|
|
10502480d0 | ||
|
|
a8a8564459 | ||
|
|
0634e7e189 | ||
|
|
60e2008dee | ||
|
|
e66bb4f328 | ||
|
|
24d1c98c0a | ||
|
|
e71014f522 | ||
|
|
95ad276cad | ||
|
|
91731b4b9c | ||
|
|
9073bb4fbc | ||
|
|
4212efad74 | ||
|
|
7b86a0ec98 | ||
|
|
8ad7e8f5e1 | ||
|
|
7b7aaf0bf3 | ||
|
|
b5f98ab3b3 | ||
|
|
f7e94a30bc | ||
|
|
8da51e3acc | ||
|
|
3d2ecdd5fa | ||
|
|
fce3d14b5c | ||
|
|
4b70375d22 | ||
|
|
115dbe1a4d | ||
|
|
4729685912 | ||
|
|
14b849246e | ||
|
|
c8d61b6269 | ||
|
|
0fbf2078e1 | ||
|
|
77402110b9 | ||
|
|
5207b99e08 | ||
|
|
92b5fa8c53 | ||
|
|
62dfa792b2 | ||
|
|
29f51c67a6 | ||
|
|
a20a9a1a2e | ||
|
|
9ae08f932e | ||
|
|
e9c9f95ade | ||
|
|
4952127f5b | ||
|
|
18c30d46aa | ||
|
|
2efe7da0a0 | ||
|
|
1255bca352 | ||
|
|
d84bdd6e7d | ||
|
|
57252060b2 | ||
|
|
cd52263d23 | ||
|
|
13300f4833 | ||
|
|
62d5c38575 | ||
|
|
b149430114 | ||
|
|
057898bbe8 | ||
|
|
630c650c3e | ||
|
|
ee2e4f17bb | ||
|
|
7df8fa3be5 | ||
|
|
96b4a76382 | ||
|
|
519f655f17 | ||
|
|
f7455227e0 | ||
|
|
c90de122c8 | ||
|
|
cac867da84 | ||
|
|
d0e8ac795d | ||
|
|
6d43bacb46 | ||
|
|
ea6e4d3b6f | ||
|
|
8dd1b3a618 | ||
|
|
ab3e7b2465 | ||
|
|
bbcc1e1f52 | ||
|
|
2f8a69b710 | ||
|
|
f56463f8bf | ||
|
|
c894c84e4a | ||
|
|
4597e118ae | ||
|
|
8ddf158d89 | ||
|
|
d0a830bb42 | ||
|
|
07217205bb | ||
|
|
9e3727bb23 | ||
|
|
410f561b1b | ||
|
|
40f4ac3cb3 | ||
|
|
01e059ca74 | ||
|
|
1114ca1ab6 | ||
|
|
9d16bae682 | ||
|
|
011e5dc241 | ||
|
|
389ab1b19f | ||
|
|
279c8bbec3 | ||
|
|
6744f811b4 | ||
|
|
d7a6d709af | ||
|
|
ff44e0f9ae | ||
|
|
100e50f23d | ||
|
|
20c040db22 | ||
|
|
5d8a7a610f | ||
|
|
d34d60ce92 | ||
|
|
dee9fbbbda | ||
|
|
7e0afa8268 | ||
|
|
1e6d2654a2 | ||
|
|
14a58759c3 | ||
|
|
dbc61fc191 | ||
|
|
31cb541b88 | ||
|
|
7ee56bbe9c | ||
|
|
cd6df7699d | ||
|
|
b2311c7402 | ||
|
|
9ca0c66e23 | ||
|
|
ef71373fa3 | ||
|
|
5873d335bd | ||
|
|
2e0f5e7817 | ||
|
|
20aa12616a | ||
|
|
bb4703e199 | ||
|
|
51f960f297 | ||
|
|
adb6069bd0 | ||
|
|
8fe5fd89c6 | ||
|
|
fa546bff85 | ||
|
|
1507757f49 | ||
|
|
028abf5d70 | ||
|
|
3882ba4ba5 | ||
|
|
24fce9deb3 | ||
|
|
9adfc2d586 | ||
|
|
53e5991dd1 | ||
|
|
9a8cf77c91 | ||
|
|
e56141a7a5 | ||
|
|
ce1abd9f26 | ||
|
|
e03ed050ef | ||
|
|
117d0c9796 | ||
|
|
531f024e48 | ||
|
|
85755e32d9 | ||
|
|
b40e74d6b6 | ||
|
|
eaf3318dd3 | ||
|
|
e1057bd7fd | ||
|
|
f22937f3d1 | ||
|
|
69a4869db0 | ||
|
|
c2a6c8d338 | ||
|
|
2ce05047f8 | ||
|
|
829fa34581 | ||
|
|
3610fdce36 | ||
|
|
ca480d7043 | ||
|
|
63ab25579b | ||
|
|
13c07237fe | ||
|
|
f5d485235e | ||
|
|
72d35b6b4a | ||
|
|
c73d7650b5 | ||
|
|
86328767fe | ||
|
|
e497d068a1 | ||
|
|
926a71f5ac | ||
|
|
55f5a2dc8d | ||
|
|
fe435e497e | ||
|
|
fea768dbcc | ||
|
|
2ccb6cf350 | ||
|
|
0b62f74a7f | ||
|
|
45ebb6cdf4 | ||
|
|
96f47432a5 | ||
|
|
41f8435d32 | ||
|
|
b275613d3a | ||
|
|
82689d68cf | ||
|
|
0c567b3282 | ||
|
|
855176b633 | ||
|
|
b95f2ee820 | ||
|
|
ae92db98f3 | ||
|
|
0b25547ca0 | ||
|
|
548f2865e2 | ||
|
|
40f7a8eaa2 | ||
|
|
9434ad39e1 | ||
|
|
7d983c94c7 | ||
|
|
0b1cacff03 | ||
|
|
b809498cf8 | ||
|
|
f6f78e5f02 | ||
|
|
591e89ed18 | ||
|
|
1616079a2d | ||
|
|
4db51fbeba | ||
|
|
ec8daf0e7b | ||
|
|
5312a990b9 | ||
|
|
935f05bc84 | ||
|
|
fc4eb6dae0 | ||
|
|
08939683c7 | ||
|
|
55699bee8f | ||
|
|
dab9222942 | ||
|
|
4070dabe53 | ||
|
|
a365864229 | ||
|
|
ee3f2b073c | ||
|
|
34b6385dc3 | ||
|
|
11529a2ea0 | ||
|
|
b0f8809e3d | ||
|
|
ec1175daa3 | ||
|
|
3a1d9b8289 | ||
|
|
192be8fea3 | ||
|
|
1afcff955b | ||
|
|
224fe05697 | ||
|
|
2dd332b491 | ||
|
|
33e4193f45 | ||
|
|
c163d2f33d | ||
|
|
a715ba6be4 |
11
.editorconfig
Normal file
11
.editorconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.js,*.json,*.yml}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@@ -0,0 +1,2 @@
|
||||
coverage
|
||||
node_modules
|
||||
8
.eslintrc
Normal file
8
.eslintrc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"rules": {
|
||||
"eol-last": "error",
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"no-trailing-spaces": "error",
|
||||
"no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }]
|
||||
}
|
||||
}
|
||||
31
.travis.yml
31
.travis.yml
@@ -1,10 +1,33 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
- "0.12"
|
||||
- "1.8"
|
||||
- "2.5"
|
||||
- "3.3"
|
||||
- "4.8"
|
||||
- "5.12"
|
||||
- "6.11"
|
||||
- "7.10"
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "8"
|
||||
env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
allow_failures:
|
||||
- node_js: "0.11"
|
||||
fast_finish: true
|
||||
script: "npm run-script test-travis"
|
||||
# Allow the nightly installs to fail
|
||||
- env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly"
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
before_install:
|
||||
# Remove all non-test dependencies
|
||||
- "npm rm --save-dev connect-redis"
|
||||
|
||||
# Update Node.js modules
|
||||
- "test ! -d node_modules || npm prune"
|
||||
- "test ! -d node_modules || npm rebuild"
|
||||
script:
|
||||
- "npm run test-ci"
|
||||
- "npm run lint"
|
||||
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
|
||||
|
||||
50
Collaborator-Guide.md
Normal file
50
Collaborator-Guide.md
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
## Website Issues
|
||||
|
||||
Open issues for the expressjs.com website in https://github.com/expressjs/expressjs.com.
|
||||
|
||||
## PRs and Code contributions
|
||||
|
||||
* Tests must pass.
|
||||
* Follow the [JavaScript Standard Style](http://standardjs.com/) and `npm run lint`.
|
||||
* If you fix a bug, add a test.
|
||||
|
||||
## Branches
|
||||
|
||||
Use the `master` branch for bug fixes or minor work that is intended for the
|
||||
current release stream.
|
||||
|
||||
Use the correspondingly named branch, e.g. `5.0`, for anything intended for
|
||||
a future release of Express.
|
||||
|
||||
## Steps for contributing
|
||||
|
||||
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
|
||||
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.
|
||||
4. To run the test suite, first install the dependencies by running `npm install`,
|
||||
then run `npm test`.
|
||||
5. Ensure your code is linted by running `npm run lint` -- fix any issue you
|
||||
see listed.
|
||||
6. If the tests pass, you can commit your changes to your fork and then create
|
||||
a pull request from there. Make sure to reference your issue from the pull
|
||||
request comments by including the issue number e.g. `#123`.
|
||||
|
||||
## Issues which are questions
|
||||
|
||||
We will typically close any vague issues or questions that are specific to some
|
||||
app you are writing. Please double check the docs and other references before
|
||||
being trigger happy with posting a question issue.
|
||||
|
||||
Things that will help get your question issue looked at:
|
||||
|
||||
* Full and runnable JS code.
|
||||
* Clear description of the problem or unexpected behavior.
|
||||
* Clear description of the expected result.
|
||||
* Steps you have taken to debug it yourself.
|
||||
|
||||
If you post a question and do not outline the above items or make it easy for
|
||||
us to understand and reproduce your issue, it will be closed.
|
||||
@@ -1,25 +1,85 @@
|
||||
# Express.js Community Contributing Guide 1.0
|
||||
|
||||
## Website Issues
|
||||
The goal of this document is to create a contribution process that:
|
||||
|
||||
Issues for the expressjs.com website go here https://github.com/strongloop/expressjs.com
|
||||
* Encourages new contributions.
|
||||
* Encourages contributors to remain involved.
|
||||
* Avoids unnecessary processes and bureaucracy whenever possible.
|
||||
* Creates a transparent decision making process that makes it clear how
|
||||
contributors can be involved in decision making.
|
||||
|
||||
## PRs and Code contributions
|
||||
## Vocabulary
|
||||
|
||||
* Tests must pass.
|
||||
* Follow existing coding style.
|
||||
* If you fix a bug, add a test.
|
||||
* A **Contributor** is any individual creating or commenting on an issue or pull request.
|
||||
* 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.
|
||||
|
||||
# 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
|
||||
exception is security dislosures which should be sent privately.
|
||||
|
||||
Committers may direct you to another repository, ask for additional clarifications, and
|
||||
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
|
||||
|
||||
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
|
||||
pull requests.
|
||||
|
||||
No pull request can be merged without being reviewed.
|
||||
|
||||
For non-trivial contributions, pull requests should sit for at least 36 hours to ensure that
|
||||
contributors in other timezones have time to review. Consideration should also be given to
|
||||
weekends and other holiday periods to ensure active committers all have reasonable time to
|
||||
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
|
||||
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.
|
||||
|
||||
In the case of an objection being raised in a pull request by another committer, all involved
|
||||
committers should seek to arrive at a consensus by way of addressing concerns being expressed
|
||||
by discussion, compromise on the proposed change, or withdrawal of the proposed change.
|
||||
|
||||
If a contribution is controversial and committers cannot agree about how to get it to land
|
||||
or if it should land then it should be escalated to the TC. TC members should regularly
|
||||
discuss pending contributions in order to find a resolution. It is expected that only a
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Issues which are questions
|
||||
|
||||
We will typically close any vague issues or questions that are specific to some app you are writing. Please double check the docs and other references before being trigger happy with posting a question issue.
|
||||
|
||||
Things that will help get your question issue looked at:
|
||||
|
||||
* Full and runnable JS code.
|
||||
* Clear description of the problem or unexpected behavior.
|
||||
* Clear description of the expected result.
|
||||
* Steps you have taken to debug it yourself.
|
||||
|
||||
If you post a question and do not outline the above items or make it easy for us to understand and reproduce your issue, it will be closed.
|
||||
|
||||
|
||||
775
History.md
775
History.md
@@ -1,3 +1,543 @@
|
||||
4.15.5 / 2017-09-24
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.9
|
||||
* deps: finalhandler@~1.0.6
|
||||
- deps: debug@2.6.9
|
||||
- deps: parseurl@~1.3.2
|
||||
* deps: fresh@0.5.2
|
||||
- Fix handling of modified headers with invalid dates
|
||||
- perf: improve ETag match loop
|
||||
- perf: improve `If-None-Match` token parsing
|
||||
* deps: send@0.15.6
|
||||
- Fix handling of modified headers with invalid dates
|
||||
- deps: debug@2.6.9
|
||||
- deps: etag@~1.8.1
|
||||
- deps: fresh@0.5.2
|
||||
- perf: improve `If-Match` token parsing
|
||||
* deps: serve-static@1.12.6
|
||||
- deps: parseurl@~1.3.2
|
||||
- deps: send@0.15.6
|
||||
- perf: improve slash collapsing
|
||||
|
||||
4.15.4 / 2017-08-06
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.8
|
||||
* deps: depd@~1.1.1
|
||||
- Remove unnecessary `Buffer` loading
|
||||
* deps: finalhandler@~1.0.4
|
||||
- deps: debug@2.6.8
|
||||
* deps: proxy-addr@~1.1.5
|
||||
- Fix array argument being altered
|
||||
- deps: ipaddr.js@1.4.0
|
||||
* deps: qs@6.5.0
|
||||
* deps: send@0.15.4
|
||||
- deps: debug@2.6.8
|
||||
- deps: depd@~1.1.1
|
||||
- deps: http-errors@~1.6.2
|
||||
* deps: serve-static@1.12.4
|
||||
- deps: send@0.15.4
|
||||
|
||||
4.15.3 / 2017-05-16
|
||||
===================
|
||||
|
||||
* Fix error when `res.set` cannot add charset to `Content-Type`
|
||||
* deps: debug@2.6.7
|
||||
- Fix `DEBUG_MAX_ARRAY_LENGTH`
|
||||
- deps: ms@2.0.0
|
||||
* deps: finalhandler@~1.0.3
|
||||
- Fix missing `</html>` in HTML document
|
||||
- deps: debug@2.6.7
|
||||
* deps: proxy-addr@~1.1.4
|
||||
- deps: ipaddr.js@1.3.0
|
||||
* deps: send@0.15.3
|
||||
- deps: debug@2.6.7
|
||||
- deps: ms@2.0.0
|
||||
* deps: serve-static@1.12.3
|
||||
- deps: send@0.15.3
|
||||
* deps: type-is@~1.6.15
|
||||
- deps: mime-types@~2.1.15
|
||||
* deps: vary@~1.1.1
|
||||
- perf: hoist regular expression
|
||||
|
||||
4.15.2 / 2017-03-06
|
||||
===================
|
||||
|
||||
* deps: qs@6.4.0
|
||||
- Fix regression parsing keys starting with `[`
|
||||
|
||||
4.15.1 / 2017-03-05
|
||||
===================
|
||||
|
||||
* deps: send@0.15.1
|
||||
- Fix issue when `Date.parse` does not return `NaN` on invalid date
|
||||
- Fix strict violation in broken environments
|
||||
* deps: serve-static@1.12.1
|
||||
- Fix issue when `Date.parse` does not return `NaN` on invalid date
|
||||
- deps: send@0.15.1
|
||||
|
||||
4.15.0 / 2017-03-01
|
||||
===================
|
||||
|
||||
* Add debug message when loading view engine
|
||||
* Add `next("router")` to exit from router
|
||||
* Fix case where `router.use` skipped requests routes did not
|
||||
* Remove usage of `res._headers` private field
|
||||
- Improves compatibility with Node.js 8 nightly
|
||||
* Skip routing when `req.url` is not set
|
||||
* Use `%o` in path debug to tell types apart
|
||||
* Use `Object.create` to setup request & response prototypes
|
||||
* Use `setprototypeof` module to replace `__proto__` setting
|
||||
* Use `statuses` instead of `http` module for status messages
|
||||
* deps: debug@2.6.1
|
||||
- Allow colors in workers
|
||||
- Deprecated `DEBUG_FD` environment variable set to `3` or higher
|
||||
- Fix error when running under React Native
|
||||
- Use same color for same namespace
|
||||
- deps: ms@0.7.2
|
||||
* deps: etag@~1.8.0
|
||||
- Use SHA1 instead of MD5 for ETag hashing
|
||||
- Works with FIPS 140-2 OpenSSL configuration
|
||||
* deps: finalhandler@~1.0.0
|
||||
- Fix exception when `err` cannot be converted to a string
|
||||
- Fully URL-encode the pathname in the 404
|
||||
- Only include the pathname in the 404 message
|
||||
- Send complete HTML document
|
||||
- Set `Content-Security-Policy: default-src 'self'` header
|
||||
- deps: debug@2.6.1
|
||||
* deps: fresh@0.5.0
|
||||
- Fix false detection of `no-cache` request directive
|
||||
- Fix incorrect result when `If-None-Match` has both `*` and ETags
|
||||
- Fix weak `ETag` matching to match spec
|
||||
- perf: delay reading header values until needed
|
||||
- perf: enable strict mode
|
||||
- perf: hoist regular expressions
|
||||
- perf: remove duplicate conditional
|
||||
- perf: remove unnecessary boolean coercions
|
||||
- perf: skip checking modified time if ETag check failed
|
||||
- perf: skip parsing `If-None-Match` when no `ETag` header
|
||||
- perf: use `Date.parse` instead of `new Date`
|
||||
* deps: qs@6.3.1
|
||||
- Fix array parsing from skipping empty values
|
||||
- Fix compacting nested arrays
|
||||
* deps: send@0.15.0
|
||||
- Fix false detection of `no-cache` request directive
|
||||
- Fix incorrect result when `If-None-Match` has both `*` and ETags
|
||||
- Fix weak `ETag` matching to match spec
|
||||
- Remove usage of `res._headers` private field
|
||||
- Support `If-Match` and `If-Unmodified-Since` headers
|
||||
- Use `res.getHeaderNames()` when available
|
||||
- Use `res.headersSent` when available
|
||||
- deps: debug@2.6.1
|
||||
- deps: etag@~1.8.0
|
||||
- deps: fresh@0.5.0
|
||||
- deps: http-errors@~1.6.1
|
||||
* deps: serve-static@1.12.0
|
||||
- Fix false detection of `no-cache` request directive
|
||||
- Fix incorrect result when `If-None-Match` has both `*` and ETags
|
||||
- Fix weak `ETag` matching to match spec
|
||||
- Remove usage of `res._headers` private field
|
||||
- Send complete HTML document in redirect response
|
||||
- Set default CSP header in redirect response
|
||||
- Support `If-Match` and `If-Unmodified-Since` headers
|
||||
- Use `res.getHeaderNames()` when available
|
||||
- Use `res.headersSent` when available
|
||||
- deps: send@0.15.0
|
||||
* perf: add fast match path for `*` route
|
||||
* perf: improve `req.ips` performance
|
||||
|
||||
4.14.1 / 2017-01-28
|
||||
===================
|
||||
|
||||
* deps: content-disposition@0.5.2
|
||||
* deps: finalhandler@0.5.1
|
||||
- Fix exception when `err.headers` is not an object
|
||||
- deps: statuses@~1.3.1
|
||||
- perf: hoist regular expressions
|
||||
- perf: remove duplicate validation path
|
||||
* deps: proxy-addr@~1.1.3
|
||||
- deps: ipaddr.js@1.2.0
|
||||
* deps: send@0.14.2
|
||||
- deps: http-errors@~1.5.1
|
||||
- deps: ms@0.7.2
|
||||
- deps: statuses@~1.3.1
|
||||
* deps: serve-static@~1.11.2
|
||||
- deps: send@0.14.2
|
||||
* deps: type-is@~1.6.14
|
||||
- deps: mime-types@~2.1.13
|
||||
|
||||
4.14.0 / 2016-06-16
|
||||
===================
|
||||
|
||||
* Add `acceptRanges` option to `res.sendFile`/`res.sendfile`
|
||||
* Add `cacheControl` option to `res.sendFile`/`res.sendfile`
|
||||
* Add `options` argument to `req.range`
|
||||
- Includes the `combine` option
|
||||
* Encode URL in `res.location`/`res.redirect` if not already encoded
|
||||
* Fix some redirect handling in `res.sendFile`/`res.sendfile`
|
||||
* Fix Windows absolute path check using forward slashes
|
||||
* Improve error with invalid arguments to `req.get()`
|
||||
* Improve performance for `res.json`/`res.jsonp` in most cases
|
||||
* Improve `Range` header handling in `res.sendFile`/`res.sendfile`
|
||||
* deps: accepts@~1.3.3
|
||||
- Fix including type extensions in parameters in `Accept` parsing
|
||||
- Fix parsing `Accept` parameters with quoted equals
|
||||
- Fix parsing `Accept` parameters with quoted semicolons
|
||||
- Many performance improvments
|
||||
- deps: mime-types@~2.1.11
|
||||
- deps: negotiator@0.6.1
|
||||
* deps: content-type@~1.0.2
|
||||
- perf: enable strict mode
|
||||
* deps: cookie@0.3.1
|
||||
- Add `sameSite` option
|
||||
- Fix cookie `Max-Age` to never be a floating point number
|
||||
- Improve error message when `encode` is not a function
|
||||
- Improve error message when `expires` is not a `Date`
|
||||
- Throw better error for invalid argument to parse
|
||||
- Throw on invalid values provided to `serialize`
|
||||
- perf: enable strict mode
|
||||
- perf: hoist regular expression
|
||||
- perf: use for loop in parse
|
||||
- perf: use string concatination for serialization
|
||||
* deps: finalhandler@0.5.0
|
||||
- Change invalid or non-numeric status code to 500
|
||||
- Overwrite status message to match set status code
|
||||
- Prefer `err.statusCode` if `err.status` is invalid
|
||||
- Set response headers from `err.headers` object
|
||||
- Use `statuses` instead of `http` module for status messages
|
||||
* deps: proxy-addr@~1.1.2
|
||||
- Fix accepting various invalid netmasks
|
||||
- Fix IPv6-mapped IPv4 validation edge cases
|
||||
- IPv4 netmasks must be contingous
|
||||
- IPv6 addresses cannot be used as a netmask
|
||||
- deps: ipaddr.js@1.1.1
|
||||
* deps: qs@6.2.0
|
||||
- Add `decoder` option in `parse` function
|
||||
* deps: range-parser@~1.2.0
|
||||
- Add `combine` option to combine overlapping ranges
|
||||
- Fix incorrectly returning -1 when there is at least one valid range
|
||||
- perf: remove internal function
|
||||
* deps: send@0.14.1
|
||||
- Add `acceptRanges` option
|
||||
- Add `cacheControl` option
|
||||
- Attempt to combine multiple ranges into single range
|
||||
- Correctly inherit from `Stream` class
|
||||
- Fix `Content-Range` header in 416 responses when using `start`/`end` options
|
||||
- Fix `Content-Range` header missing from default 416 responses
|
||||
- Fix redirect error when `path` contains raw non-URL characters
|
||||
- Fix redirect when `path` starts with multiple forward slashes
|
||||
- Ignore non-byte `Range` headers
|
||||
- deps: http-errors@~1.5.0
|
||||
- deps: range-parser@~1.2.0
|
||||
- deps: statuses@~1.3.0
|
||||
- perf: remove argument reassignment
|
||||
* deps: serve-static@~1.11.1
|
||||
- Add `acceptRanges` option
|
||||
- Add `cacheControl` option
|
||||
- Attempt to combine multiple ranges into single range
|
||||
- Fix redirect error when `req.url` contains raw non-URL characters
|
||||
- Ignore non-byte `Range` headers
|
||||
- Use status code 301 for redirects
|
||||
- deps: send@0.14.1
|
||||
* deps: type-is@~1.6.13
|
||||
- Fix type error when given invalid type to match against
|
||||
- deps: mime-types@~2.1.11
|
||||
* deps: vary@~1.1.0
|
||||
- Only accept valid field names in the `field` argument
|
||||
* perf: use strict equality when possible
|
||||
|
||||
4.13.4 / 2016-01-21
|
||||
===================
|
||||
|
||||
* deps: content-disposition@0.5.1
|
||||
- perf: enable strict mode
|
||||
* deps: cookie@0.1.5
|
||||
- Throw on invalid values provided to `serialize`
|
||||
* deps: depd@~1.1.0
|
||||
- Support web browser loading
|
||||
- perf: enable strict mode
|
||||
* deps: escape-html@~1.0.3
|
||||
- perf: enable strict mode
|
||||
- perf: optimize string replacement
|
||||
- perf: use faster string coercion
|
||||
* deps: finalhandler@0.4.1
|
||||
- deps: escape-html@~1.0.3
|
||||
* deps: merge-descriptors@1.0.1
|
||||
- perf: enable strict mode
|
||||
* deps: methods@~1.1.2
|
||||
- perf: enable strict mode
|
||||
* deps: parseurl@~1.3.1
|
||||
- perf: enable strict mode
|
||||
* deps: proxy-addr@~1.0.10
|
||||
- deps: ipaddr.js@1.0.5
|
||||
- perf: enable strict mode
|
||||
* deps: range-parser@~1.0.3
|
||||
- perf: enable strict mode
|
||||
* deps: send@0.13.1
|
||||
- deps: depd@~1.1.0
|
||||
- deps: destroy@~1.0.4
|
||||
- deps: escape-html@~1.0.3
|
||||
- deps: range-parser@~1.0.3
|
||||
* deps: serve-static@~1.10.2
|
||||
- deps: escape-html@~1.0.3
|
||||
- deps: parseurl@~1.3.0
|
||||
- deps: send@0.13.1
|
||||
|
||||
4.13.3 / 2015-08-02
|
||||
===================
|
||||
|
||||
* Fix infinite loop condition using `mergeParams: true`
|
||||
* Fix inner numeric indices incorrectly altering parent `req.params`
|
||||
|
||||
4.13.2 / 2015-07-31
|
||||
===================
|
||||
|
||||
* deps: accepts@~1.2.12
|
||||
- deps: mime-types@~2.1.4
|
||||
* deps: array-flatten@1.1.1
|
||||
- perf: enable strict mode
|
||||
* deps: path-to-regexp@0.1.7
|
||||
- Fix regression with escaped round brackets and matching groups
|
||||
* deps: type-is@~1.6.6
|
||||
- deps: mime-types@~2.1.4
|
||||
|
||||
4.13.1 / 2015-07-05
|
||||
===================
|
||||
|
||||
* deps: accepts@~1.2.10
|
||||
- deps: mime-types@~2.1.2
|
||||
* deps: qs@4.0.0
|
||||
- Fix dropping parameters like `hasOwnProperty`
|
||||
- Fix various parsing edge cases
|
||||
* deps: type-is@~1.6.4
|
||||
- deps: mime-types@~2.1.2
|
||||
- perf: enable strict mode
|
||||
- perf: remove argument reassignment
|
||||
|
||||
4.13.0 / 2015-06-20
|
||||
===================
|
||||
|
||||
* Add settings to debug output
|
||||
* Fix `res.format` error when only `default` provided
|
||||
* Fix issue where `next('route')` in `app.param` would incorrectly skip values
|
||||
* Fix hiding platform issues with `decodeURIComponent`
|
||||
- Only `URIError`s are a 400
|
||||
* Fix using `*` before params in routes
|
||||
* Fix using capture groups before params in routes
|
||||
* Simplify `res.cookie` to call `res.append`
|
||||
* Use `array-flatten` module for flattening arrays
|
||||
* deps: accepts@~1.2.9
|
||||
- deps: mime-types@~2.1.1
|
||||
- perf: avoid argument reassignment & argument slice
|
||||
- perf: avoid negotiator recursive construction
|
||||
- perf: enable strict mode
|
||||
- perf: remove unnecessary bitwise operator
|
||||
* deps: cookie@0.1.3
|
||||
- perf: deduce the scope of try-catch deopt
|
||||
- perf: remove argument reassignments
|
||||
* deps: escape-html@1.0.2
|
||||
* deps: etag@~1.7.0
|
||||
- Always include entity length in ETags for hash length extensions
|
||||
- Generate non-Stats ETags using MD5 only (no longer CRC32)
|
||||
- Improve stat performance by removing hashing
|
||||
- Improve support for JXcore
|
||||
- Remove base64 padding in ETags to shorten
|
||||
- Support "fake" stats objects in environments without fs
|
||||
- Use MD5 instead of MD4 in weak ETags over 1KB
|
||||
* deps: finalhandler@0.4.0
|
||||
- Fix a false-positive when unpiping in Node.js 0.8
|
||||
- Support `statusCode` property on `Error` objects
|
||||
- Use `unpipe` module for unpiping requests
|
||||
- deps: escape-html@1.0.2
|
||||
- deps: on-finished@~2.3.0
|
||||
- perf: enable strict mode
|
||||
- perf: remove argument reassignment
|
||||
* deps: fresh@0.3.0
|
||||
- Add weak `ETag` matching support
|
||||
* deps: on-finished@~2.3.0
|
||||
- Add defined behavior for HTTP `CONNECT` requests
|
||||
- Add defined behavior for HTTP `Upgrade` requests
|
||||
- deps: ee-first@1.1.1
|
||||
* deps: path-to-regexp@0.1.6
|
||||
* deps: send@0.13.0
|
||||
- Allow Node.js HTTP server to set `Date` response header
|
||||
- Fix incorrectly removing `Content-Location` on 304 response
|
||||
- Improve the default redirect response headers
|
||||
- Send appropriate headers on default error response
|
||||
- Use `http-errors` for standard emitted errors
|
||||
- Use `statuses` instead of `http` module for status messages
|
||||
- deps: escape-html@1.0.2
|
||||
- deps: etag@~1.7.0
|
||||
- deps: fresh@0.3.0
|
||||
- deps: on-finished@~2.3.0
|
||||
- perf: enable strict mode
|
||||
- perf: remove unnecessary array allocations
|
||||
* deps: serve-static@~1.10.0
|
||||
- Add `fallthrough` option
|
||||
- Fix reading options from options prototype
|
||||
- Improve the default redirect response headers
|
||||
- Malformed URLs now `next()` instead of 400
|
||||
- deps: escape-html@1.0.2
|
||||
- deps: send@0.13.0
|
||||
- perf: enable strict mode
|
||||
- perf: remove argument reassignment
|
||||
* deps: type-is@~1.6.3
|
||||
- deps: mime-types@~2.1.1
|
||||
- perf: reduce try block size
|
||||
- perf: remove bitwise operations
|
||||
* perf: enable strict mode
|
||||
* perf: isolate `app.render` try block
|
||||
* perf: remove argument reassignments in application
|
||||
* perf: remove argument reassignments in request prototype
|
||||
* perf: remove argument reassignments in response prototype
|
||||
* perf: remove argument reassignments in routing
|
||||
* perf: remove argument reassignments in `View`
|
||||
* perf: skip attempting to decode zero length string
|
||||
* perf: use saved reference to `http.STATUS_CODES`
|
||||
|
||||
4.12.4 / 2015-05-17
|
||||
===================
|
||||
|
||||
* deps: accepts@~1.2.7
|
||||
- deps: mime-types@~2.0.11
|
||||
- deps: negotiator@0.5.3
|
||||
* deps: debug@~2.2.0
|
||||
- deps: ms@0.7.1
|
||||
* deps: depd@~1.0.1
|
||||
* deps: etag@~1.6.0
|
||||
- Improve support for JXcore
|
||||
- Support "fake" stats objects in environments without `fs`
|
||||
* deps: finalhandler@0.3.6
|
||||
- deps: debug@~2.2.0
|
||||
- deps: on-finished@~2.2.1
|
||||
* deps: on-finished@~2.2.1
|
||||
- Fix `isFinished(req)` when data buffered
|
||||
* deps: proxy-addr@~1.0.8
|
||||
- deps: ipaddr.js@1.0.1
|
||||
* deps: qs@2.4.2
|
||||
- Fix allowing parameters like `constructor`
|
||||
* deps: send@0.12.3
|
||||
- deps: debug@~2.2.0
|
||||
- deps: depd@~1.0.1
|
||||
- deps: etag@~1.6.0
|
||||
- deps: ms@0.7.1
|
||||
- deps: on-finished@~2.2.1
|
||||
* deps: serve-static@~1.9.3
|
||||
- deps: send@0.12.3
|
||||
* deps: type-is@~1.6.2
|
||||
- deps: mime-types@~2.0.11
|
||||
|
||||
4.12.3 / 2015-03-17
|
||||
===================
|
||||
|
||||
* deps: accepts@~1.2.5
|
||||
- deps: mime-types@~2.0.10
|
||||
* deps: debug@~2.1.3
|
||||
- Fix high intensity foreground color for bold
|
||||
- deps: ms@0.7.0
|
||||
* deps: finalhandler@0.3.4
|
||||
- deps: debug@~2.1.3
|
||||
* deps: proxy-addr@~1.0.7
|
||||
- deps: ipaddr.js@0.1.9
|
||||
* deps: qs@2.4.1
|
||||
- Fix error when parameter `hasOwnProperty` is present
|
||||
* deps: send@0.12.2
|
||||
- Throw errors early for invalid `extensions` or `index` options
|
||||
- deps: debug@~2.1.3
|
||||
* deps: serve-static@~1.9.2
|
||||
- deps: send@0.12.2
|
||||
* deps: type-is@~1.6.1
|
||||
- deps: mime-types@~2.0.10
|
||||
|
||||
4.12.2 / 2015-03-02
|
||||
===================
|
||||
|
||||
* Fix regression where `"Request aborted"` is logged using `res.sendFile`
|
||||
|
||||
4.12.1 / 2015-03-01
|
||||
===================
|
||||
|
||||
* Fix constructing application with non-configurable prototype properties
|
||||
* Fix `ECONNRESET` errors from `res.sendFile` usage
|
||||
* Fix `req.host` when using "trust proxy" hops count
|
||||
* Fix `req.protocol`/`req.secure` when using "trust proxy" hops count
|
||||
* Fix wrong `code` on aborted connections from `res.sendFile`
|
||||
* deps: merge-descriptors@1.0.0
|
||||
|
||||
4.12.0 / 2015-02-23
|
||||
===================
|
||||
|
||||
* Fix `"trust proxy"` setting to inherit when app is mounted
|
||||
* Generate `ETag`s for all request responses
|
||||
- No longer restricted to only responses for `GET` and `HEAD` requests
|
||||
* Use `content-type` to parse `Content-Type` headers
|
||||
* deps: accepts@~1.2.4
|
||||
- Fix preference sorting to be stable for long acceptable lists
|
||||
- deps: mime-types@~2.0.9
|
||||
- deps: negotiator@0.5.1
|
||||
* deps: cookie-signature@1.0.6
|
||||
* deps: send@0.12.1
|
||||
- Always read the stat size from the file
|
||||
- Fix mutating passed-in `options`
|
||||
- deps: mime@1.3.4
|
||||
* deps: serve-static@~1.9.1
|
||||
- deps: send@0.12.1
|
||||
* deps: type-is@~1.6.0
|
||||
- fix argument reassignment
|
||||
- fix false-positives in `hasBody` `Transfer-Encoding` check
|
||||
- support wildcard for both type and subtype (`*/*`)
|
||||
- deps: mime-types@~2.0.9
|
||||
|
||||
4.11.2 / 2015-02-01
|
||||
===================
|
||||
|
||||
* Fix `res.redirect` double-calling `res.end` for `HEAD` requests
|
||||
* deps: accepts@~1.2.3
|
||||
- deps: mime-types@~2.0.8
|
||||
* deps: proxy-addr@~1.0.6
|
||||
- deps: ipaddr.js@0.1.8
|
||||
* deps: type-is@~1.5.6
|
||||
- deps: mime-types@~2.0.8
|
||||
|
||||
4.11.1 / 2015-01-20
|
||||
===================
|
||||
|
||||
* deps: send@0.11.1
|
||||
- Fix root path disclosure
|
||||
* deps: serve-static@~1.8.1
|
||||
- Fix redirect loop in Node.js 0.11.14
|
||||
- Fix root path disclosure
|
||||
- deps: send@0.11.1
|
||||
|
||||
4.11.0 / 2015-01-13
|
||||
===================
|
||||
|
||||
* Add `res.append(field, val)` to append headers
|
||||
* Deprecate leading `:` in `name` for `app.param(name, fn)`
|
||||
* Deprecate `req.param()` -- use `req.params`, `req.body`, or `req.query` instead
|
||||
* Deprecate `app.param(fn)`
|
||||
* Fix `OPTIONS` responses to include the `HEAD` method properly
|
||||
* Fix `res.sendFile` not always detecting aborted connection
|
||||
* Match routes iteratively to prevent stack overflows
|
||||
* deps: accepts@~1.2.2
|
||||
- deps: mime-types@~2.0.7
|
||||
- deps: negotiator@0.5.0
|
||||
* deps: send@0.11.0
|
||||
- deps: debug@~2.1.1
|
||||
- deps: etag@~1.5.1
|
||||
- deps: ms@0.7.0
|
||||
- deps: on-finished@~2.2.0
|
||||
* deps: serve-static@~1.8.0
|
||||
- deps: send@0.11.0
|
||||
|
||||
4.10.8 / 2015-01-13
|
||||
===================
|
||||
|
||||
* Fix crash from error within `OPTIONS` response handler
|
||||
* deps: proxy-addr@~1.0.5
|
||||
- deps: ipaddr.js@0.1.6
|
||||
|
||||
4.10.7 / 2015-01-04
|
||||
===================
|
||||
|
||||
@@ -629,6 +1169,235 @@
|
||||
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
|
||||
- Router & Route - public API
|
||||
|
||||
3.21.2 / 2015-07-31
|
||||
===================
|
||||
|
||||
* deps: connect@2.30.2
|
||||
- deps: body-parser@~1.13.3
|
||||
- deps: compression@~1.5.2
|
||||
- deps: errorhandler@~1.4.2
|
||||
- deps: method-override@~2.3.5
|
||||
- deps: serve-index@~1.7.2
|
||||
- deps: type-is@~1.6.6
|
||||
- deps: vhost@~3.0.1
|
||||
* deps: vary@~1.0.1
|
||||
- Fix setting empty header from empty `field`
|
||||
- perf: enable strict mode
|
||||
- perf: remove argument reassignments
|
||||
|
||||
3.21.1 / 2015-07-05
|
||||
===================
|
||||
|
||||
* deps: basic-auth@~1.0.3
|
||||
* deps: connect@2.30.1
|
||||
- deps: body-parser@~1.13.2
|
||||
- deps: compression@~1.5.1
|
||||
- deps: errorhandler@~1.4.1
|
||||
- deps: morgan@~1.6.1
|
||||
- deps: pause@0.1.0
|
||||
- deps: qs@4.0.0
|
||||
- deps: serve-index@~1.7.1
|
||||
- deps: type-is@~1.6.4
|
||||
|
||||
3.21.0 / 2015-06-18
|
||||
===================
|
||||
|
||||
* deps: basic-auth@1.0.2
|
||||
- perf: enable strict mode
|
||||
- perf: hoist regular expression
|
||||
- perf: parse with regular expressions
|
||||
- perf: remove argument reassignment
|
||||
* deps: connect@2.30.0
|
||||
- deps: body-parser@~1.13.1
|
||||
- deps: bytes@2.1.0
|
||||
- deps: compression@~1.5.0
|
||||
- deps: cookie@0.1.3
|
||||
- deps: cookie-parser@~1.3.5
|
||||
- deps: csurf@~1.8.3
|
||||
- deps: errorhandler@~1.4.0
|
||||
- deps: express-session@~1.11.3
|
||||
- deps: finalhandler@0.4.0
|
||||
- deps: fresh@0.3.0
|
||||
- deps: morgan@~1.6.0
|
||||
- deps: serve-favicon@~2.3.0
|
||||
- deps: serve-index@~1.7.0
|
||||
- deps: serve-static@~1.10.0
|
||||
- deps: type-is@~1.6.3
|
||||
* deps: cookie@0.1.3
|
||||
- perf: deduce the scope of try-catch deopt
|
||||
- perf: remove argument reassignments
|
||||
* deps: escape-html@1.0.2
|
||||
* deps: etag@~1.7.0
|
||||
- Always include entity length in ETags for hash length extensions
|
||||
- Generate non-Stats ETags using MD5 only (no longer CRC32)
|
||||
- Improve stat performance by removing hashing
|
||||
- Improve support for JXcore
|
||||
- Remove base64 padding in ETags to shorten
|
||||
- Support "fake" stats objects in environments without fs
|
||||
- Use MD5 instead of MD4 in weak ETags over 1KB
|
||||
* deps: fresh@0.3.0
|
||||
- Add weak `ETag` matching support
|
||||
* deps: mkdirp@0.5.1
|
||||
- Work in global strict mode
|
||||
* deps: send@0.13.0
|
||||
- Allow Node.js HTTP server to set `Date` response header
|
||||
- Fix incorrectly removing `Content-Location` on 304 response
|
||||
- Improve the default redirect response headers
|
||||
- Send appropriate headers on default error response
|
||||
- Use `http-errors` for standard emitted errors
|
||||
- Use `statuses` instead of `http` module for status messages
|
||||
- deps: escape-html@1.0.2
|
||||
- deps: etag@~1.7.0
|
||||
- deps: fresh@0.3.0
|
||||
- deps: on-finished@~2.3.0
|
||||
- perf: enable strict mode
|
||||
- perf: remove unnecessary array allocations
|
||||
|
||||
3.20.3 / 2015-05-17
|
||||
===================
|
||||
|
||||
* deps: connect@2.29.2
|
||||
- deps: body-parser@~1.12.4
|
||||
- deps: compression@~1.4.4
|
||||
- deps: connect-timeout@~1.6.2
|
||||
- deps: debug@~2.2.0
|
||||
- deps: depd@~1.0.1
|
||||
- deps: errorhandler@~1.3.6
|
||||
- deps: finalhandler@0.3.6
|
||||
- deps: method-override@~2.3.3
|
||||
- deps: morgan@~1.5.3
|
||||
- deps: qs@2.4.2
|
||||
- deps: response-time@~2.3.1
|
||||
- deps: serve-favicon@~2.2.1
|
||||
- deps: serve-index@~1.6.4
|
||||
- deps: serve-static@~1.9.3
|
||||
- deps: type-is@~1.6.2
|
||||
* deps: debug@~2.2.0
|
||||
- deps: ms@0.7.1
|
||||
* deps: depd@~1.0.1
|
||||
* deps: proxy-addr@~1.0.8
|
||||
- deps: ipaddr.js@1.0.1
|
||||
* deps: send@0.12.3
|
||||
- deps: debug@~2.2.0
|
||||
- deps: depd@~1.0.1
|
||||
- deps: etag@~1.6.0
|
||||
- deps: ms@0.7.1
|
||||
- deps: on-finished@~2.2.1
|
||||
|
||||
3.20.2 / 2015-03-16
|
||||
===================
|
||||
|
||||
* deps: connect@2.29.1
|
||||
- deps: body-parser@~1.12.2
|
||||
- deps: compression@~1.4.3
|
||||
- deps: connect-timeout@~1.6.1
|
||||
- deps: debug@~2.1.3
|
||||
- deps: errorhandler@~1.3.5
|
||||
- deps: express-session@~1.10.4
|
||||
- deps: finalhandler@0.3.4
|
||||
- deps: method-override@~2.3.2
|
||||
- deps: morgan@~1.5.2
|
||||
- deps: qs@2.4.1
|
||||
- deps: serve-index@~1.6.3
|
||||
- deps: serve-static@~1.9.2
|
||||
- deps: type-is@~1.6.1
|
||||
* deps: debug@~2.1.3
|
||||
- Fix high intensity foreground color for bold
|
||||
- deps: ms@0.7.0
|
||||
* deps: merge-descriptors@1.0.0
|
||||
* deps: proxy-addr@~1.0.7
|
||||
- deps: ipaddr.js@0.1.9
|
||||
* deps: send@0.12.2
|
||||
- Throw errors early for invalid `extensions` or `index` options
|
||||
- deps: debug@~2.1.3
|
||||
|
||||
3.20.1 / 2015-02-28
|
||||
===================
|
||||
|
||||
* Fix `req.host` when using "trust proxy" hops count
|
||||
* Fix `req.protocol`/`req.secure` when using "trust proxy" hops count
|
||||
|
||||
3.20.0 / 2015-02-18
|
||||
===================
|
||||
|
||||
* Fix `"trust proxy"` setting to inherit when app is mounted
|
||||
* Generate `ETag`s for all request responses
|
||||
- No longer restricted to only responses for `GET` and `HEAD` requests
|
||||
* Use `content-type` to parse `Content-Type` headers
|
||||
* deps: connect@2.29.0
|
||||
- Use `content-type` to parse `Content-Type` headers
|
||||
- deps: body-parser@~1.12.0
|
||||
- deps: compression@~1.4.1
|
||||
- deps: connect-timeout@~1.6.0
|
||||
- deps: cookie-parser@~1.3.4
|
||||
- deps: cookie-signature@1.0.6
|
||||
- deps: csurf@~1.7.0
|
||||
- deps: errorhandler@~1.3.4
|
||||
- deps: express-session@~1.10.3
|
||||
- deps: http-errors@~1.3.1
|
||||
- deps: response-time@~2.3.0
|
||||
- deps: serve-index@~1.6.2
|
||||
- deps: serve-static@~1.9.1
|
||||
- deps: type-is@~1.6.0
|
||||
* deps: cookie-signature@1.0.6
|
||||
* deps: send@0.12.1
|
||||
- Always read the stat size from the file
|
||||
- Fix mutating passed-in `options`
|
||||
- deps: mime@1.3.4
|
||||
|
||||
3.19.2 / 2015-02-01
|
||||
===================
|
||||
|
||||
* deps: connect@2.28.3
|
||||
- deps: compression@~1.3.1
|
||||
- deps: csurf@~1.6.6
|
||||
- deps: errorhandler@~1.3.3
|
||||
- deps: express-session@~1.10.2
|
||||
- deps: serve-index@~1.6.1
|
||||
- deps: type-is@~1.5.6
|
||||
* deps: proxy-addr@~1.0.6
|
||||
- deps: ipaddr.js@0.1.8
|
||||
|
||||
3.19.1 / 2015-01-20
|
||||
===================
|
||||
|
||||
* deps: connect@2.28.2
|
||||
- deps: body-parser@~1.10.2
|
||||
- deps: serve-static@~1.8.1
|
||||
* deps: send@0.11.1
|
||||
- Fix root path disclosure
|
||||
|
||||
3.19.0 / 2015-01-09
|
||||
===================
|
||||
|
||||
* Fix `OPTIONS` responses to include the `HEAD` method property
|
||||
* Use `readline` for prompt in `express(1)`
|
||||
* deps: commander@2.6.0
|
||||
* deps: connect@2.28.1
|
||||
- deps: body-parser@~1.10.1
|
||||
- deps: compression@~1.3.0
|
||||
- deps: connect-timeout@~1.5.0
|
||||
- deps: csurf@~1.6.4
|
||||
- deps: debug@~2.1.1
|
||||
- deps: errorhandler@~1.3.2
|
||||
- deps: express-session@~1.10.1
|
||||
- deps: finalhandler@0.3.3
|
||||
- deps: method-override@~2.3.1
|
||||
- deps: morgan@~1.5.1
|
||||
- deps: serve-favicon@~2.2.0
|
||||
- deps: serve-index@~1.6.0
|
||||
- deps: serve-static@~1.8.0
|
||||
- deps: type-is@~1.5.5
|
||||
* deps: debug@~2.1.1
|
||||
* deps: methods@~1.1.1
|
||||
* deps: proxy-addr@~1.0.5
|
||||
- deps: ipaddr.js@0.1.6
|
||||
* deps: send@0.11.0
|
||||
- deps: debug@~2.1.1
|
||||
- deps: etag@~1.5.1
|
||||
- deps: ms@0.7.0
|
||||
- deps: on-finished@~2.2.0
|
||||
|
||||
3.18.6 / 2014-12-12
|
||||
===================
|
||||
|
||||
@@ -1333,7 +2102,7 @@
|
||||
* update commander
|
||||
* jsonp: check if callback is a function
|
||||
* router: wrap encodeURIComponent in a try/catch #1735 (@lxe)
|
||||
* res.format: now includes chraset @1747 (@sorribas)
|
||||
* res.format: now includes charset @1747 (@sorribas)
|
||||
* res.links: allow multiple calls @1746 (@sorribas)
|
||||
|
||||
3.4.0 / 2013-09-07
|
||||
@@ -1612,7 +2381,7 @@
|
||||
* Added another example to content-negotiation
|
||||
* Added `fresh` dep
|
||||
* Changed: `res.send()` always checks freshness
|
||||
* Fixed: expose connects mime module. Cloases #1165
|
||||
* Fixed: expose connects mime module. Closes #1165
|
||||
|
||||
3.0.0beta2 / 2012-06-06
|
||||
==================
|
||||
@@ -1694,7 +2463,7 @@
|
||||
* Added `req.ips`
|
||||
* Added `req.fresh`
|
||||
* Added `req.stale`
|
||||
* Added comma-delmited / array support for `req.accepts()`
|
||||
* Added comma-delimited / array support for `req.accepts()`
|
||||
* Added debug instrumentation
|
||||
* Added `res.set(obj)`
|
||||
* Added `res.set(field, value)`
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,8 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com>
|
||||
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
125
Readme-Guide.md
Normal file
125
Readme-Guide.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# README guidelines
|
||||
|
||||
Every module in the expressjs, pillarjs, and jshttp organizations should have
|
||||
a README file named `README.md`. The purpose of the README is to:
|
||||
|
||||
- Explain the purpose of the module and how to use it.
|
||||
- Act as a landing page (both on GitHub and npmjs.com) for the module to help
|
||||
people find it via search. Middleware module READMEs are also incorporated
|
||||
into https://expressjs.com/en/resources/middleware.html.
|
||||
- Encourage community contributions and participation.
|
||||
|
||||
Use the [README template](https://github.com/expressjs/express/wiki/README-template)
|
||||
to quickly create a new README file.
|
||||
|
||||
## Top-level items
|
||||
|
||||
**Badges** (optional): At the very top (with no subheading), include any
|
||||
applicable badges, such as npm version/downloads, build status, test coverage,
|
||||
and so on. Badges should resolve properly (not display a broken image).
|
||||
|
||||
Possible badges include:
|
||||
- npm version: `[![NPM Version][npm-image]][npm-url]`
|
||||
- npm downloads: `[![NPM Downloads][downloads-image]][downloads-url]`
|
||||
- Build status: `[![Build Status][travis-image]][travis-url]`
|
||||
- Test coverage: `[![Test Coverage][coveralls-image]][coveralls-url]`
|
||||
- Tips: `[![Gratipay][gratipay-image]][gratipay-url]`
|
||||
|
||||
**Summary**: Following badges, provide a one- or two-sentence description of
|
||||
what the module does. This should be the same as the npmjs.org blurb (which
|
||||
comes from the description property of `package.json`). Since npm doesn't
|
||||
handle markdown for the blurb, avoid using markdown in the summary sentence.
|
||||
|
||||
**TOC** (Optional): For longer READMEs, provide a table of contents that has
|
||||
a relative link to each section. A tool such as
|
||||
[doctoc](https://www.npmjs.com/package/doctoc) makes it very easy to generate
|
||||
a TOC.
|
||||
|
||||
## Overview
|
||||
|
||||
Optionally, include a section of one or two paragraphs with more high-level
|
||||
information on what the module does, what problems it solves, why one would
|
||||
use it and how. Don't just repeat what's in the summary.
|
||||
|
||||
## Installation
|
||||
|
||||
Required. This section is typically just:
|
||||
|
||||
```sh
|
||||
$ npm install module-name
|
||||
```
|
||||
|
||||
But include any other steps or requirements.
|
||||
|
||||
NOTE: Use the `sh` code block to make the shell command display properly on
|
||||
the website.
|
||||
|
||||
## Basic use
|
||||
|
||||
- Provide a general description of how to use the module with code sample.
|
||||
Include any important caveats or restrictions.
|
||||
- Explain the most common use cases.
|
||||
- Optional: a simple "hello world" type example (where applicable). This
|
||||
example is in addition to the more comprehensive [example section](#examples)
|
||||
later.
|
||||
|
||||
## API
|
||||
|
||||
Provide complete API documentation.
|
||||
|
||||
Formatting conventions: Each function is listed in a 3rd-level heading (`###`),
|
||||
like this:
|
||||
|
||||
```
|
||||
### Function_name(arg, options [, optional_arg] ... )
|
||||
```
|
||||
|
||||
**Options objects**
|
||||
|
||||
For arguments that are objects (for example, options object), describe the
|
||||
properties in a table, as follows. This matches the formatting used in the
|
||||
[Express API docs](https://expressjs.com/en/4x/api.html).
|
||||
|
||||
|Property | Description | Type | Default|
|
||||
|----------|-----------|------------|-------------|
|
||||
|Name of the property in `monospace`. | Brief description | String, Number, Boolean, etc. | If applicable.|
|
||||
|
||||
If all the properties are required (i.e. there are no defaults), then you
|
||||
can omit the default column.
|
||||
|
||||
Instead of very lengthy descriptions, link out to subsequent paragraphs for
|
||||
more detailed explanation of specific cases (e.g. "When this property is set
|
||||
to 'foobar', xyz happens; see <link to following section >.)
|
||||
|
||||
If there are options properties that are themselves options, use additional
|
||||
tables. See [`trust proxy` and `etag` properties](https://expressjs.com/en/4x/api.html#app.settings.table).
|
||||
|
||||
## Examples
|
||||
|
||||
Every README should have at least one example; ideally more. For code samples,
|
||||
be sure to use the `js` code block, for proper display in the website, e.g.:
|
||||
|
||||
```js
|
||||
var csurf = require('csurf')
|
||||
...
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
What tests are included.
|
||||
|
||||
How to run them.
|
||||
|
||||
The convention for running tests is `npm test`. All our projects should follow
|
||||
this convention.
|
||||
|
||||
## Contributors
|
||||
|
||||
Names of module "owners" (lead developers) and other developers who have
|
||||
contributed.
|
||||
|
||||
## License
|
||||
|
||||
Link to the license, with a short description of what it is, e.g. "MIT" or
|
||||
whatever. Ideally, avoid putting the license text directly in the README; link
|
||||
to it instead.
|
||||
44
Readme.md
44
Readme.md
@@ -4,7 +4,8 @@
|
||||
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Linux Build][travis-image]][travis-url]
|
||||
[![Windows Build][appveyor-image]][appveyor-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
```js
|
||||
@@ -36,15 +37,18 @@ $ npm install express
|
||||
|
||||
## Docs & Community
|
||||
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/strongloop/expressjs.com)]
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/expressjs/expressjs.com)]
|
||||
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
|
||||
* [Github Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||
* Visit the [Wiki](https://github.com/strongloop/express/wiki)
|
||||
* [GitHub Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||
* Visit the [Wiki](https://github.com/expressjs/express/wiki)
|
||||
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
* [한국어 문서](http://expressjs.kr) - [[website repo](https://github.com/Hanul/expressjs.kr)]
|
||||
* [Gitter](https://gitter.im/expressjs/express) for support and discussion
|
||||
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/strongloop/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/strongloop/express/wiki/New-features-in-4.x).
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/expressjs/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/expressjs/express/wiki/New-features-in-4.x).
|
||||
|
||||
### Security Issues
|
||||
|
||||
If you discover a security vulnerability in Express, please see [Security Policies and Procedures](Security.md).
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -86,10 +90,10 @@ $ npm start
|
||||
|
||||
## Examples
|
||||
|
||||
To view the examples, clone the Express repo and install the dependancies:
|
||||
To view the examples, clone the Express repo and install the dependencies:
|
||||
|
||||
```bash
|
||||
$ git clone git://github.com/strongloop/express.git --depth 1
|
||||
$ git clone git://github.com/expressjs/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
```
|
||||
@@ -102,7 +106,7 @@ $ node examples/content-negotiation
|
||||
|
||||
## Tests
|
||||
|
||||
To run the test suite, first install the dependancies, then run `npm test`:
|
||||
To run the test suite, first install the dependencies, then run `npm test`:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
@@ -115,21 +119,23 @@ The original author of Express is [TJ Holowaychuk](https://github.com/tj) [ [![Doug's Gratipay][gratipay-image-dougwilson]][gratipay-url-dougwilson]
|
||||
|
||||
[List of all contributors](https://github.com/strongloop/express/graphs/contributors)
|
||||
[List of all contributors](https://github.com/expressjs/express/graphs/contributors)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[npm-image]: https://img.shields.io/npm/v/express.svg?style=flat
|
||||
[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?style=flat
|
||||
[downloads-image]: https://img.shields.io/npm/dm/express.svg
|
||||
[downloads-url]: https://npmjs.org/package/express
|
||||
[travis-image]: https://img.shields.io/travis/strongloop/express.svg?style=flat
|
||||
[travis-url]: https://travis-ci.org/strongloop/express
|
||||
[coveralls-image]: https://img.shields.io/coveralls/strongloop/express.svg?style=flat
|
||||
[coveralls-url]: https://coveralls.io/r/strongloop/express?branch=master
|
||||
[gratipay-image-visionmedia]: https://img.shields.io/gratipay/visionmedia.svg?style=flat
|
||||
[travis-image]: https://img.shields.io/travis/expressjs/express/master.svg?label=linux
|
||||
[travis-url]: https://travis-ci.org/expressjs/express
|
||||
[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
|
||||
[coveralls-url]: https://coveralls.io/r/expressjs/express?branch=master
|
||||
[gratipay-image-visionmedia]: https://img.shields.io/gratipay/visionmedia.svg
|
||||
[gratipay-url-visionmedia]: https://gratipay.com/visionmedia/
|
||||
[gratipay-image-dougwilson]: https://img.shields.io/gratipay/dougwilson.svg?style=flat
|
||||
[gratipay-image-dougwilson]: https://img.shields.io/gratipay/dougwilson.svg
|
||||
[gratipay-url-dougwilson]: https://gratipay.com/dougwilson/
|
||||
|
||||
186
Release-Process.md
Normal file
186
Release-Process.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Express Release Process
|
||||
|
||||
This document contains the technical aspects of the Express release process. The
|
||||
intended audience is those who have been authorized by the Express Technical
|
||||
Committee (TC) to create, promote and sign official release builds for Express,
|
||||
as npm packages hosted on https://npmjs.com/package/express.
|
||||
|
||||
## Who can make releases?
|
||||
|
||||
Release authorization is given by the Express TC. Once authorized, an individual
|
||||
must have the following access permissions:
|
||||
|
||||
### 1. Github release access
|
||||
|
||||
The individual making the release will need to be a member of the
|
||||
expressjs/express team with Write permission level so they are able to tag the
|
||||
release commit and push changes to the expressjs/express repository
|
||||
(see Steps 4 and 5).
|
||||
|
||||
### 2. npmjs.com release access
|
||||
|
||||
The individual making the release will need to be made an owner on the
|
||||
`express` package on npmjs.com so they are able to publish the release
|
||||
(see Step 6).
|
||||
|
||||
## How to publish a release
|
||||
|
||||
Before publishing, the following preconditions should be met:
|
||||
|
||||
- A release proposal issue or tracking pull request (see "Proposal branch"
|
||||
below) will exist documenting:
|
||||
- the proposed changes
|
||||
- the type of release: patch, minor or major
|
||||
- the version number (according to semantic versioning - http://semver.org)
|
||||
- The proposed changes should be complete.
|
||||
|
||||
There are two main release flows: patch and non-patch.
|
||||
|
||||
The patch flow is for making **patch releases**. As per semantic versioning,
|
||||
patch releases are for simple changes, eg: typo fixes, patch dependency updates,
|
||||
and simple/low-risk bug fixes. Every other type of change is made via the
|
||||
non-patch flow.
|
||||
|
||||
### Branch terminology
|
||||
|
||||
"Master branch"
|
||||
|
||||
- There is a branch in git used for the current major version of Express, named
|
||||
`master`.
|
||||
- This branch contains the completed commits for the next patch release of the
|
||||
current major version.
|
||||
- Releases for the current major version are published from this branch.
|
||||
|
||||
"Version branch"
|
||||
|
||||
- For any given major version of Express (current, previous or next) there is
|
||||
a branch in git for that release named `<major-version>.x` (eg: `4.x`).
|
||||
- This branch points to the commit of the latest tag for the given major version.
|
||||
|
||||
"Release branch"
|
||||
|
||||
- For any given major version of Express, there is a branch used for publishing
|
||||
releases.
|
||||
- For the current major version of Express, the release branch is the
|
||||
"Master branch" named `master`.
|
||||
- For all other major versions of Express, the release branch is the
|
||||
"Version branch" named `<major-version>.x`.
|
||||
|
||||
"Proposal branch"
|
||||
|
||||
- A branch in git representing a proposed new release of Express. This can be a
|
||||
minor or major release, named `<major-version>.0` for a major release,
|
||||
`<major-version>.<minor-version>` for a minor release.
|
||||
- A tracking pull request should exist to document the proposed release,
|
||||
targeted at the appropriate release branch. Prior to opening the tracking
|
||||
pull request the content of the release may have be discussed in an issue.
|
||||
- This branch contains the commits accepted so far that implement the proposal
|
||||
in the tracking pull request.
|
||||
|
||||
### Patch flow
|
||||
|
||||
In the patch flow, simple changes are committed to the release branch which
|
||||
acts as an ever-present branch for the next patch release of the associated
|
||||
major version of Express.
|
||||
|
||||
The release branch is usually kept in a state where it is ready to release.
|
||||
Releases are made when sufficient time or change has been made to warrant it.
|
||||
This is usually proposed and decided using a github issue.
|
||||
|
||||
### Non-patch flow
|
||||
|
||||
In the non-patch flow, changes are committed to a temporary proposal branch
|
||||
created specifically for that release proposal. The branch is based on the
|
||||
most recent release of the major version of Express that the release targets.
|
||||
|
||||
Releases are made when all the changes on a proposal branch are complete and
|
||||
approved. This is done by merging the proposal branch into the release branch
|
||||
(using a fast-forward merge), tagging it with the new version number and
|
||||
publishing the release package to npmjs.com.
|
||||
|
||||
### Flow
|
||||
|
||||
Below is a detailed description of the steps to publish a release.
|
||||
|
||||
#### Step 1. Check the release is ready to publish
|
||||
|
||||
Check any relevant information to ensure the release is ready, eg: any
|
||||
milestone, label, issue or tracking pull request for the release. The release
|
||||
is ready when all proposed code, tests and documentation updates are complete
|
||||
(either merged, closed or re-targeted to another release).
|
||||
|
||||
#### Step 2. (Non-patch flow only) Merge the proposal branch into the release branch
|
||||
|
||||
In the patch flow: skip this step.
|
||||
|
||||
In the non-patch flow:
|
||||
```sh
|
||||
$ git checkout <release-branch>
|
||||
$ git merge --ff-only <proposal-branch>
|
||||
```
|
||||
|
||||
<release-branch> - see "Release branch" of "Branches" above.
|
||||
<proposal-branch> - see "Proposal branch" of "Non-patch flow" above.
|
||||
|
||||
**NOTE:** You may need to rebase the proposal branch to allow a fast-forward
|
||||
merge. Using a fast-forward merge keeps the history clean as it does
|
||||
not introduce merge commits.
|
||||
|
||||
### Step 3. Update the History.md and package.json to the new version number
|
||||
|
||||
The changes so far for the release should already be documented under the
|
||||
"unreleased" section at the top of the History.md file, as per the usual
|
||||
development practice. Change "unreleased" to the new release version / date.
|
||||
Example diff fragment:
|
||||
|
||||
```diff
|
||||
-unreleased
|
||||
-==========
|
||||
+4.13.3 / 2015-08-02
|
||||
+===================
|
||||
```
|
||||
|
||||
The version property in the package.json should already contain the version of
|
||||
the previous release. Change it to the new release version.
|
||||
|
||||
Commit these changes together under a single commit with the message set to
|
||||
the new release version (eg: `4.13.3`):
|
||||
|
||||
```sh
|
||||
$ git checkout <release-branch>
|
||||
<..edit files..>
|
||||
$ git add History.md package.json
|
||||
$ git commit -m '<version-number>'
|
||||
```
|
||||
|
||||
### Step 4. Identify and tag the release commit with the new release version
|
||||
|
||||
Create a lightweight tag (rather than an annotated tag) named after the new
|
||||
release version (eg: `4.13.3`).
|
||||
|
||||
```sh
|
||||
$ git tag <version-number>
|
||||
```
|
||||
|
||||
### Step 5. Push the release branch changes and tag to github
|
||||
|
||||
The branch and tag should be pushed directly to the main repository
|
||||
(https://github.com/expressjs/express).
|
||||
|
||||
```sh
|
||||
$ git push origin <release-branch>
|
||||
$ git push origin <version-number>
|
||||
```
|
||||
|
||||
### Step 6. Publish to npmjs.com
|
||||
|
||||
Ensure your local working copy is completely clean (no extra or changed files).
|
||||
You can use `git status` for this purpose.
|
||||
|
||||
```sh
|
||||
$ npm login <npm-username>
|
||||
$ npm publish
|
||||
```
|
||||
|
||||
**NOTE:** The version number to publish will be picked up automatically from
|
||||
package.json.
|
||||
43
Security.md
Normal file
43
Security.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Security Policies and Procedures
|
||||
|
||||
This document outlines security procedures and general policies for the Express
|
||||
project.
|
||||
|
||||
* [Reporting a Bug](#reporting-a-bug)
|
||||
* [Disclosure Policy](#disclosure-policy)
|
||||
* [Comments on this Policy](#comments-on-this-policy)
|
||||
|
||||
## Reporting a Bug
|
||||
|
||||
The Express team and community take all security bugs in Express seriously.
|
||||
Thank you for improving the security of Express. We appreciate your efforts and
|
||||
responsible disclosure and will make every effort to acknowledge your
|
||||
contributions.
|
||||
|
||||
Report security bugs by emailing the lead maintainer in the Readme.md file.
|
||||
|
||||
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
|
||||
endeavor to keep you informed of the progress towards a fix and full
|
||||
announcement, and may ask for additional information or guidance.
|
||||
|
||||
Report security bugs in third-party modules to the person or team maintaining
|
||||
the module. You can also report a vulnerability through the
|
||||
[Node Security Project](https://nodesecurity.io/report).
|
||||
|
||||
## Disclosure Policy
|
||||
|
||||
When the security team receives a security bug report, they will assign it to a
|
||||
primary handler. This person will coordinate the fix and release process,
|
||||
involving the following steps:
|
||||
|
||||
* Confirm the problem and determine the affected versions.
|
||||
* Audit code to find any potential similar problems.
|
||||
* Prepare fixes for all releases still under maintenance. These fixes will be
|
||||
released as fast as possible to npm.
|
||||
|
||||
## Comments on this Policy
|
||||
|
||||
If you have suggestions on how this process could be improved please submit a
|
||||
pull request.
|
||||
26
appveyor.yml
Normal file
26
appveyor.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "0.12"
|
||||
- nodejs_version: "1.8"
|
||||
- nodejs_version: "2.5"
|
||||
- nodejs_version: "3.3"
|
||||
- nodejs_version: "4.8"
|
||||
- nodejs_version: "5.12"
|
||||
- nodejs_version: "6.11"
|
||||
- nodejs_version: "7.10"
|
||||
cache:
|
||||
- node_modules
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- npm rm --save-dev connect-redis
|
||||
- if exist node_modules npm prune
|
||||
- if exist node_modules npm rebuild
|
||||
- npm install
|
||||
build: off
|
||||
test_script:
|
||||
- node --version
|
||||
- npm --version
|
||||
- npm run test-ci
|
||||
- npm run lint
|
||||
version: "{build}"
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
var http = require('http');
|
||||
var express = require('..');
|
||||
var app = express();
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var hash = require('./pass').hash;
|
||||
var bodyParser = require('body-parser');
|
||||
var hash = require('pbkdf2-password')()
|
||||
var path = require('path');
|
||||
var session = require('express-session');
|
||||
|
||||
var app = module.exports = express();
|
||||
@@ -12,7 +13,7 @@ var app = module.exports = express();
|
||||
// config
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// middleware
|
||||
|
||||
@@ -45,7 +46,7 @@ var users = {
|
||||
// when you create a user, generate a salt
|
||||
// and hash the password ('foobar' is the pass here)
|
||||
|
||||
hash('foobar', function(err, salt, hash){
|
||||
hash({ password: 'foobar' }, function (err, pass, salt, hash) {
|
||||
if (err) throw err;
|
||||
// store the salt & hash in the "db"
|
||||
users.tj.salt = salt;
|
||||
@@ -63,7 +64,7 @@ function authenticate(name, pass, fn) {
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the hash against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
hash(pass, user.salt, function(err, hash){
|
||||
hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) {
|
||||
if (err) return fn(err);
|
||||
if (hash == user.hash) return fn(null, user);
|
||||
fn(new Error('invalid password'));
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
// check out https://github.com/tj/node-pwd
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Bytesize.
|
||||
*/
|
||||
|
||||
var len = 128;
|
||||
|
||||
/**
|
||||
* Iterations. ~300ms
|
||||
*/
|
||||
|
||||
var iterations = 12000;
|
||||
|
||||
/**
|
||||
* Hashes a password with optional `salt`, otherwise
|
||||
* generate a salt for `pass` and invoke `fn(err, salt, hash)`.
|
||||
*
|
||||
* @param {String} password to hash
|
||||
* @param {String} optional salt
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.hash = function (pwd, salt, fn) {
|
||||
if (3 == arguments.length) {
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
|
||||
fn(err, hash.toString('base64'));
|
||||
});
|
||||
} else {
|
||||
fn = salt;
|
||||
crypto.randomBytes(len, function(err, salt){
|
||||
if (err) return fn(err);
|
||||
salt = salt.toString('base64');
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
|
||||
if (err) return fn(err);
|
||||
fn(null, salt, hash.toString('base64'));
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var app = express();
|
||||
|
||||
app.set('views', __dirname);
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
var pets = [];
|
||||
|
||||
var n = 1000;
|
||||
while (n--) {
|
||||
pets.push({ name: 'Tobi', age: 2, species: 'ferret' });
|
||||
pets.push({ name: 'Loki', age: 1, species: 'ferret' });
|
||||
pets.push({ name: 'Jane', age: 6, species: 'ferret' });
|
||||
}
|
||||
|
||||
app.use(logger('dev'));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('pets', { pets: pets });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
style.
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px "Helvetica Neue", Helvetica;
|
||||
}
|
||||
|
||||
table
|
||||
for pet in pets
|
||||
tr
|
||||
td= pet.name
|
||||
td= pet.age
|
||||
td= pet.species
|
||||
@@ -4,4 +4,4 @@ users.push({ name: 'Tobi' });
|
||||
users.push({ name: 'Loki' });
|
||||
users.push({ name: 'Jane' });
|
||||
|
||||
module.exports = users;
|
||||
module.exports = users;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var cookieSession = require('cookie-session');
|
||||
var express = require('../../');
|
||||
|
||||
var app = module.exports = express();
|
||||
@@ -14,9 +15,8 @@ app.use(count);
|
||||
|
||||
// custom middleware
|
||||
function count(req, res) {
|
||||
req.session.count = req.session.count || 0;
|
||||
var n = req.session.count++;
|
||||
res.send('viewed ' + n + ' times\n');
|
||||
req.session.count = (req.session.count || 0) + 1
|
||||
res.send('viewed ' + req.session.count + ' times\n')
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
한中日
|
||||
@@ -3,12 +3,12 @@
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
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/utf-8 한中日.txt">utf-8 한中日.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>');
|
||||
@@ -17,10 +17,9 @@ app.get('/', function(req, res){
|
||||
// /files/* is accessed via req.params[0]
|
||||
// but here we name it :file
|
||||
app.get('/files/:file(*)', function(req, res, next){
|
||||
var file = req.params.file;
|
||||
var path = __dirname + '/files/' + file;
|
||||
var filePath = path.join(__dirname, 'files', req.params.file);
|
||||
|
||||
res.download(path, function(err){
|
||||
res.download(filePath, function (err) {
|
||||
if (!err) return; // file sent
|
||||
if (err && err.status !== 404) return next(err); // non-404 error
|
||||
// file for download not found
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('path');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -21,7 +22,11 @@ app.engine('.html', require('ejs').__express);
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// Path to our public directory
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// Without this you would need to
|
||||
// supply the extension to res.render()
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> <%= title %> </title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 13px Helvetica, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
<title><%= title %></title>
|
||||
<link rel="stylesheet" href="/stylesheets/style.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var path = require('path');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
// general config
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// our custom "verbose errors" setting
|
||||
@@ -60,20 +61,17 @@ app.get('/500', function(req, res, next){
|
||||
app.use(function(req, res, next){
|
||||
res.status(404);
|
||||
|
||||
// respond with html page
|
||||
if (req.accepts('html')) {
|
||||
res.render('404', { url: req.url });
|
||||
return;
|
||||
}
|
||||
|
||||
// respond with json
|
||||
if (req.accepts('json')) {
|
||||
res.send({ error: 'Not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
// default to plain-text. send()
|
||||
res.type('txt').send('Not found');
|
||||
res.format({
|
||||
html: function () {
|
||||
res.render('404', { url: req.url })
|
||||
},
|
||||
json: function () {
|
||||
res.json({ error: 'Not found' })
|
||||
},
|
||||
default: function () {
|
||||
res.type('txt').send('Not found')
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// error-handling middleware, take the same form
|
||||
|
||||
@@ -31,6 +31,9 @@ app.get('/', function(req, res){
|
||||
|
||||
app.get('/next', function(req, res, next){
|
||||
// We can also pass exceptions to next()
|
||||
// The reason for process.nextTick() is to show that
|
||||
// next() can be called inside an async operation,
|
||||
// in real life it can be a DB read or HTTP request.
|
||||
process.nextTick(function(){
|
||||
next(new Error('oh no!'));
|
||||
});
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var app = express();
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
function User(name) {
|
||||
this.private = 'heyyyy';
|
||||
this.secret = 'something';
|
||||
this.name = name;
|
||||
this.id = 123;
|
||||
}
|
||||
|
||||
// You'll probably want to do
|
||||
// something like this so you
|
||||
// dont expose "secret" data.
|
||||
|
||||
User.prototype.toJSON = function(){
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name
|
||||
};
|
||||
};
|
||||
|
||||
app.use(logger('dev'));
|
||||
|
||||
// earlier on expose an object
|
||||
// that we can tack properties on.
|
||||
// all res.locals props are exposed
|
||||
// to the templates, so "expose" will
|
||||
// be present.
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.locals.expose = {};
|
||||
// you could alias this as req or res.expose
|
||||
// to make it shorter and less annoying
|
||||
next();
|
||||
});
|
||||
|
||||
// pretend we loaded a user
|
||||
|
||||
app.use(function(req, res, next){
|
||||
req.user = new User('Tobi');
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/user');
|
||||
});
|
||||
|
||||
app.get('/user', function(req, res){
|
||||
// we only want to expose the user
|
||||
// to the client for this route:
|
||||
res.locals.expose.user = req.user;
|
||||
res.render('page');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
html
|
||||
head
|
||||
title Express
|
||||
script.
|
||||
// call this whatever you like,
|
||||
// or dump them into individual
|
||||
// props like "var user ="
|
||||
var data = !{JSON.stringify(expose)}
|
||||
body
|
||||
h1 Expose client data
|
||||
p The following was exposed to the client:
|
||||
pre
|
||||
script.
|
||||
document.write(JSON.stringify(data, null, 2))
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
// Path to our public directory
|
||||
|
||||
var pub = __dirname + '/public';
|
||||
|
||||
// setup middleware
|
||||
|
||||
var app = express();
|
||||
app.use(express.static(pub));
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Set our default template engine to "jade"
|
||||
// which prevents the need for extensions
|
||||
// (although you can still mix and match)
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
function User(name, email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
new User('tj', 'tj@vision-media.ca')
|
||||
, new User('ciaran', 'ciaranj@gmail.com')
|
||||
, new User('aaron', 'aaron.heckmann+github@gmail.com')
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
// change this to a better error handler in your code
|
||||
// sending stacktrace to users in production is not good
|
||||
app.use(function(err, req, res, next) {
|
||||
res.send(err.stack);
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
head
|
||||
title Jade Example
|
||||
link(rel="stylesheet", href="/stylesheets/style.css")
|
||||
@@ -1,5 +0,0 @@
|
||||
doctype html
|
||||
html
|
||||
include header
|
||||
body
|
||||
block content
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1 Users
|
||||
#users
|
||||
for user in users
|
||||
include user
|
||||
@@ -1,3 +0,0 @@
|
||||
.user
|
||||
h2= user.name
|
||||
.email= user.email
|
||||
@@ -2,9 +2,11 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var escapeHtml = require('escape-html');
|
||||
var express = require('../..');
|
||||
var fs = require('fs');
|
||||
var md = require('marked').parse;
|
||||
var marked = require('marked');
|
||||
var path = require('path');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -13,19 +15,14 @@ var app = module.exports = express();
|
||||
app.engine('md', function(path, options, fn){
|
||||
fs.readFile(path, 'utf8', function(err, str){
|
||||
if (err) return fn(err);
|
||||
try {
|
||||
var html = md(str);
|
||||
html = html.replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return options[name] || '';
|
||||
});
|
||||
fn(null, html);
|
||||
} catch(err) {
|
||||
fn(err);
|
||||
}
|
||||
var html = marked.parse(str).replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return escapeHtml(options[name] || '');
|
||||
});
|
||||
fn(null, html);
|
||||
});
|
||||
});
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// make it the default so we dont need .md
|
||||
app.set('view engine', 'md');
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<body>
|
||||
<h1><%= pet.name %></h1>
|
||||
<form action="/pet/<%= pet.id %>?_method=put" method="post">
|
||||
<label>Name: <input type="text" name="user[name]" value="<%= pet.name %>"></label>
|
||||
<label>Name: <input type="text" name="pet[name]" value="<%= pet.name %>"></label>
|
||||
<input type="submit" value="Update">
|
||||
</form>
|
||||
</body>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'hbs';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
if (!id) return next();
|
||||
|
||||
25
examples/mvc/controllers/user/views/edit.hbs
Normal file
25
examples/mvc/controllers/user/views/edit.hbs
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Edit {{user.name}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{user.name}}</h1>
|
||||
<form action="/user/{{user.id}}?_method=put" method="post">
|
||||
<label for="user[name]">Name:
|
||||
<input type="text" name="user[name]" value="{{user.name}}">
|
||||
</label>
|
||||
|
||||
<input type="submit" name="submit" value="Update">
|
||||
</form>
|
||||
|
||||
<form action="/user/{{user.id}}/pet" method="post">
|
||||
<label for="pet[name]">Pet:
|
||||
<input type="text" name="pet[name]" placeholder="Pet Name">
|
||||
</label>
|
||||
|
||||
<input type="submit" name="submit" value="Add">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= user.name
|
||||
form(action='/user/#{user.id}?_method=put', method='post')
|
||||
label= 'Name: '
|
||||
input(type='text', name='user[name]', value='#{user.name}')
|
||||
input(type='submit', value='Update')
|
||||
|
||||
form(action='/user/#{user.id}/pet', method='post')
|
||||
label= 'Pet: '
|
||||
input(type='text', name='pet[name]', placeholder='Name')
|
||||
input(type='submit', value='Add')
|
||||
16
examples/mvc/controllers/user/views/list.hbs
Normal file
16
examples/mvc/controllers/user/views/list.hbs
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Users</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Users</h1>
|
||||
<p>Click a user below to view their pets.</p>
|
||||
<ul>
|
||||
{{#each users}}
|
||||
<li><a href="/user/{{id}}">{{name}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 Users
|
||||
p Click a user below to view their pets.
|
||||
ul
|
||||
each user in users
|
||||
li
|
||||
a(href='/user/#{user.id}')= user.name
|
||||
29
examples/mvc/controllers/user/views/show.hbs
Normal file
29
examples/mvc/controllers/user/views/show.hbs
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>{{user.name}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{user.name}} <a href="/user/{{user.id}}/edit">edit</a></h1>
|
||||
|
||||
{{#if hasMessages}}
|
||||
<ul>
|
||||
{{#each messages}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if user.pets.length}}
|
||||
<p>View {{user.name}}'s pets:</p>
|
||||
<ul>
|
||||
{{#each user.pets}}
|
||||
<li><a href="/pet/{{id}}">{{name}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<p>No pets!</p>
|
||||
{{/if}}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,17 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= user.name + ' '
|
||||
a(href='/user/#{user.id}/edit') edit
|
||||
|
||||
if (hasMessages)
|
||||
ul#messages
|
||||
each msg in messages
|
||||
li= msg
|
||||
|
||||
if (user.pets.length)
|
||||
p View #{user.name}'s pets:
|
||||
ul
|
||||
each pet in user.pets
|
||||
li
|
||||
a(href='/pet/#{pet.id}')= pet.name
|
||||
else
|
||||
p No pets!
|
||||
@@ -11,4 +11,4 @@ var users = exports.users = [];
|
||||
|
||||
users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 });
|
||||
users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 });
|
||||
users.push({ name: 'Nathan', pets: [], id: 2 });
|
||||
users.push({ name: 'Nathan', pets: [], id: 2 });
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var path = require('path');
|
||||
var session = require('express-session');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// settings
|
||||
|
||||
// set our default template engine to "jade"
|
||||
// which prevents the need for extensions
|
||||
app.set('view engine', 'jade');
|
||||
// set our default template engine to "ejs"
|
||||
// which prevents the need for using file extensions
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// set views for error and 404 pages
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
// define a custom res.message() method
|
||||
// which stores messages in the session
|
||||
@@ -34,7 +33,7 @@ app.response.message = function(msg){
|
||||
if (!module.parent) app.use(logger('dev'));
|
||||
|
||||
// serve static files
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// session support
|
||||
app.use(session({
|
||||
|
||||
@@ -4,22 +4,26 @@
|
||||
|
||||
var express = require('../../..');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(parent, options){
|
||||
var dir = path.join(__dirname, '..', 'controllers');
|
||||
var verbose = options.verbose;
|
||||
fs.readdirSync(__dirname + '/../controllers').forEach(function(name){
|
||||
fs.readdirSync(dir).forEach(function(name){
|
||||
var file = path.join(dir, name)
|
||||
if (!fs.statSync(file).isDirectory()) return;
|
||||
verbose && console.log('\n %s:', name);
|
||||
var obj = require('./../controllers/' + name);
|
||||
var obj = require(file);
|
||||
var name = obj.name || name;
|
||||
var prefix = obj.prefix || '';
|
||||
var app = express();
|
||||
var handler;
|
||||
var method;
|
||||
var path;
|
||||
var url;
|
||||
|
||||
// allow specifying the view engine
|
||||
if (obj.engine) app.set('view engine', obj.engine);
|
||||
app.set('views', __dirname + '/../controllers/' + name + '/views');
|
||||
app.set('views', path.join(__dirname, '..', 'controllers', name, 'views'));
|
||||
|
||||
// generate routes based
|
||||
// on the exported methods
|
||||
@@ -30,27 +34,27 @@ module.exports = function(parent, options){
|
||||
switch (key) {
|
||||
case 'show':
|
||||
method = 'get';
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
url = '/' + name + '/:' + name + '_id';
|
||||
break;
|
||||
case 'list':
|
||||
method = 'get';
|
||||
path = '/' + name + 's';
|
||||
url = '/' + name + 's';
|
||||
break;
|
||||
case 'edit':
|
||||
method = 'get';
|
||||
path = '/' + name + '/:' + name + '_id/edit';
|
||||
url = '/' + name + '/:' + name + '_id/edit';
|
||||
break;
|
||||
case 'update':
|
||||
method = 'put';
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
url = '/' + name + '/:' + name + '_id';
|
||||
break;
|
||||
case 'create':
|
||||
method = 'post';
|
||||
path = '/' + name;
|
||||
url = '/' + name;
|
||||
break;
|
||||
case 'index':
|
||||
method = 'get';
|
||||
path = '/';
|
||||
url = '/';
|
||||
break;
|
||||
default:
|
||||
/* istanbul ignore next */
|
||||
@@ -59,15 +63,15 @@ module.exports = function(parent, options){
|
||||
|
||||
// setup
|
||||
handler = obj[key];
|
||||
path = prefix + path;
|
||||
url = prefix + url;
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
app[method](path, obj.before, handler);
|
||||
verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), path, key);
|
||||
app[method](url, obj.before, handler);
|
||||
verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), url, key);
|
||||
} else {
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
app[method](url, handler);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), url, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
examples/mvc/views/404.ejs
Normal file
12
examples/mvc/views/404.ejs
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Not Found</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>404: Not Found</h1>
|
||||
<p>Sorry we can't find <%= url %></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 404: Not Found
|
||||
p Sorry we can't find #{url}
|
||||
12
examples/mvc/views/5xx.ejs
Normal file
12
examples/mvc/views/5xx.ejs
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Internal Server Error</title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>500: Internal Server Error</h1>
|
||||
<p>Looks like something blew up!</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 500: Internal Server Error
|
||||
p Looks like something blew up!
|
||||
@@ -1,4 +1,8 @@
|
||||
// first:
|
||||
|
||||
// install redis first:
|
||||
// https://redis.io/
|
||||
|
||||
// then:
|
||||
// $ npm install redis online
|
||||
// $ redis-server
|
||||
|
||||
|
||||
@@ -15,12 +15,20 @@ var users = [
|
||||
, { name: 'bandit' }
|
||||
];
|
||||
|
||||
// Create HTTP error
|
||||
|
||||
function createError(status, message) {
|
||||
var err = new Error(message);
|
||||
err.status = status;
|
||||
return err;
|
||||
}
|
||||
|
||||
// Convert :to and :from to integers
|
||||
|
||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
req.params[name] = parseInt(num, 10);
|
||||
if( isNaN(req.params[name]) ){
|
||||
next(new Error('failed to parseInt '+num));
|
||||
next(createError(400, 'failed to parseInt '+num));
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
@@ -32,7 +40,7 @@ app.param('user', function(req, res, next, id){
|
||||
if (req.user = users[id]) {
|
||||
next();
|
||||
} else {
|
||||
next(new Error('failed to find user'));
|
||||
next(createError(404, 'failed to find user'));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -60,7 +68,7 @@ app.get('/users/:from-:to', function(req, res, next){
|
||||
var from = req.params.from;
|
||||
var to = req.params.to;
|
||||
var names = users.map(function(user){ return user.name; });
|
||||
res.send('users ' + names.slice(from, to).join(', '));
|
||||
res.send('users ' + names.slice(from, to + 1).join(', '));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -75,7 +75,7 @@ app.resource('/users', User);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send([
|
||||
'<h1>Examples:</h1> <ul>'
|
||||
'<h1>Examples:</h1> <ul>'
|
||||
, '<li>GET /users</li>'
|
||||
, '<li>GET /users/1</li>'
|
||||
, '<li>GET /users/3</li>'
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var path = require('path');
|
||||
var app = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
@@ -16,18 +17,18 @@ module.exports = app;
|
||||
|
||||
// Config
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.use(express.logger('dev'));
|
||||
app.use(logger('dev'));
|
||||
}
|
||||
|
||||
app.use(methodOverride('_method'));
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// General
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
exports.index = function(req, res){
|
||||
res.render('index', { title: 'Route Separation Example' });
|
||||
};
|
||||
};
|
||||
|
||||
2
examples/route-separation/views/footer.ejs
Normal file
2
examples/route-separation/views/footer.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
8
examples/route-separation/views/header.ejs
Normal file
8
examples/route-separation/views/header.ejs
Normal file
@@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><%= title %></title>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
10
examples/route-separation/views/index.ejs
Normal file
10
examples/route-separation/views/index.ejs
Normal file
@@ -0,0 +1,10 @@
|
||||
<% include header %>
|
||||
|
||||
<h1><%= title %></h1>
|
||||
|
||||
<ul>
|
||||
<li>Visit the <a href="/users">users</a> page.</li>
|
||||
<li>Visit the <a href="/posts">posts</a> page.</li>
|
||||
</ul>
|
||||
|
||||
<% include footer %>
|
||||
@@ -1,6 +0,0 @@
|
||||
extends layout
|
||||
|
||||
block content
|
||||
ul
|
||||
li Visit the <a href="/users">users</a> page
|
||||
li Visit the <a href="/posts">posts</a> page
|
||||
@@ -1,6 +0,0 @@
|
||||
html
|
||||
head
|
||||
title= title
|
||||
link(href="/style.css", rel="stylesheet")
|
||||
body
|
||||
block content
|
||||
12
examples/route-separation/views/posts/index.ejs
Normal file
12
examples/route-separation/views/posts/index.ejs
Normal file
@@ -0,0 +1,12 @@
|
||||
<% include ../header %>
|
||||
|
||||
<h1>Posts</h1>
|
||||
|
||||
<dl id="posts">
|
||||
<% posts.forEach(function(post) { %>
|
||||
<dt><%= post.title %></dt>
|
||||
<dd><%= post.body %></dd>
|
||||
<% }) %>
|
||||
</dl>
|
||||
|
||||
<% include ../footer %>
|
||||
@@ -1,8 +0,0 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1 Posts
|
||||
dl#posts
|
||||
for post in posts
|
||||
dt= post.title
|
||||
dd= post.body
|
||||
23
examples/route-separation/views/users/edit.ejs
Normal file
23
examples/route-separation/views/users/edit.ejs
Normal file
@@ -0,0 +1,23 @@
|
||||
<% include ../header %>
|
||||
|
||||
<h1>Editing <%= user.name %></h1>
|
||||
|
||||
<div id="user">
|
||||
<form action="?_method=put", method="post">
|
||||
<p>
|
||||
Name:
|
||||
<input type="text" value="<%= user.name %>" name="user[name]" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Email:
|
||||
<input type="email" value="<%= user.email %>" name="user[email]" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="submit" value="Save" />
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<% include ../footer %>
|
||||
@@ -1,12 +0,0 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1 Editing #{user.name}
|
||||
#user
|
||||
form(action="?_method=put", method="post")
|
||||
p Name:
|
||||
input(type="text", value= user.name, name="user[name]")
|
||||
p Email:
|
||||
input(type="text", value= user.email, name="user[email]")
|
||||
p
|
||||
input(type="submit", value="Save")
|
||||
14
examples/route-separation/views/users/index.ejs
Normal file
14
examples/route-separation/views/users/index.ejs
Normal file
@@ -0,0 +1,14 @@
|
||||
<% include ../header %>
|
||||
|
||||
<h1><%= title %></h1>
|
||||
|
||||
<div id="users">
|
||||
<% users.forEach(function(user, index) { %>
|
||||
<li>
|
||||
<a href="/user/<%= index %>"><%= user.name %></a>
|
||||
<a href="/user/<%= index %>/edit">edit</a>
|
||||
</li>
|
||||
<% }) %>
|
||||
</div>
|
||||
|
||||
<% include ../footer %>
|
||||
@@ -1,9 +0,0 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1 Users
|
||||
#users
|
||||
for user, i in users
|
||||
li
|
||||
a(href="/user/#{i}")= user.name
|
||||
a.edit(href="/user/#{i}/edit") edit
|
||||
9
examples/route-separation/views/users/view.ejs
Normal file
9
examples/route-separation/views/users/view.ejs
Normal file
@@ -0,0 +1,9 @@
|
||||
<% include ../header %>
|
||||
|
||||
<h1><%= user.name %></h1>
|
||||
|
||||
<div id="user">
|
||||
<p>Email: <%= user.email %></p>
|
||||
</div>
|
||||
|
||||
<% include ../footer %>
|
||||
@@ -1,6 +0,0 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1= user.name
|
||||
#user
|
||||
p Email: #{user.email}
|
||||
@@ -1,4 +1,8 @@
|
||||
// first:
|
||||
|
||||
// install redis first:
|
||||
// https://redis.io/
|
||||
|
||||
// then:
|
||||
// $ npm install redis
|
||||
// $ redis-server
|
||||
|
||||
@@ -7,6 +11,7 @@
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var path = require('path');
|
||||
var redis = require('redis');
|
||||
|
||||
var db = redis.createClient();
|
||||
@@ -15,8 +20,7 @@ var db = redis.createClient();
|
||||
|
||||
var app = express();
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', __dirname);
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// populate search
|
||||
|
||||
@@ -26,14 +30,6 @@ db.sadd('ferret', 'jane');
|
||||
db.sadd('cat', 'manny');
|
||||
db.sadd('cat', 'luna');
|
||||
|
||||
/**
|
||||
* GET the search page.
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('search');
|
||||
});
|
||||
|
||||
/**
|
||||
* GET search for :query.
|
||||
*/
|
||||
@@ -54,7 +50,7 @@ app.get('/search/:query?', function(req, res){
|
||||
*/
|
||||
|
||||
app.get('/client.js', function(req, res){
|
||||
res.sendFile(__dirname + '/client.js');
|
||||
res.sendFile(path.join(__dirname, 'client.js'));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -10,4 +10,4 @@ search.addEventListener('keyup', function(){
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}, false);
|
||||
}, false);
|
||||
20
examples/search/public/index.html
Normal file
20
examples/search/public/index.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Search example</title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font: 14px "Helvetica Neue", Helvetica;
|
||||
padding: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Search</h2>
|
||||
<p>Try searching for "ferret" or "cat".</p>
|
||||
<input type="search" name="search" value="" />
|
||||
<pre />
|
||||
<script src="/client.js" charset="utf-8"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
doctype
|
||||
html
|
||||
head
|
||||
title Search example
|
||||
style.
|
||||
body {
|
||||
font: 14px "Helvetica Neue", Helvetica;
|
||||
padding: 50px;
|
||||
}
|
||||
body
|
||||
h2 Search
|
||||
p Try searching for "ferret" or "cat".
|
||||
input(type='search')
|
||||
pre
|
||||
script(src='client.js')
|
||||
@@ -1,4 +1,8 @@
|
||||
// first:
|
||||
|
||||
// install redis first:
|
||||
// https://redis.io/
|
||||
|
||||
// then:
|
||||
// $ npm install redis
|
||||
// $ redis-server
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var path = require('path');
|
||||
var app = express();
|
||||
|
||||
// log requests
|
||||
@@ -16,7 +17,7 @@ app.use(logger('dev'));
|
||||
// that you pass it. In this case "GET /js/app.js"
|
||||
// will look for "./public/js/app.js".
|
||||
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// if you wanted to "prefix" you may use
|
||||
// the mounting feature of Connect, for example
|
||||
@@ -24,13 +25,13 @@ app.use(express.static(__dirname + '/public'));
|
||||
// The mount-path "/static" is simply removed before
|
||||
// passing control to the express.static() middleware,
|
||||
// thus it serves the file correctly by ignoring "/static"
|
||||
app.use('/static', express.static(__dirname + '/public'));
|
||||
app.use('/static', express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// if for some reason you want to serve files from
|
||||
// several directories, you can use express.static()
|
||||
// multiple times! Here we're passing "./public/css",
|
||||
// this will allow "GET /style.css" instead of "GET /css/style.css":
|
||||
app.use(express.static(__dirname + '/public/css'));
|
||||
app.use(express.static(path.join(__dirname, 'public', 'css')));
|
||||
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
|
||||
@@ -1 +1 @@
|
||||
foo
|
||||
foo
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var path = require('path');
|
||||
var extname = path.extname;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var http = require('http');
|
||||
var GithubView = require('./github-view');
|
||||
var md = require('marked').parse;
|
||||
|
||||
@@ -23,7 +22,7 @@ app.engine('md', function(str, options, fn){
|
||||
});
|
||||
|
||||
// pointing to a particular github repo to load files from it
|
||||
app.set('views', 'strongloop/express');
|
||||
app.set('views', 'expressjs/express');
|
||||
|
||||
// register a new view constructor
|
||||
app.set('view', GithubView);
|
||||
@@ -36,7 +35,7 @@ app.get('/', function(req, res){
|
||||
});
|
||||
|
||||
app.get('/Readme.md', function(req, res){
|
||||
// rendering a view from https://github.com/strongloop/express/blob/master/Readme.md
|
||||
// rendering a view from https://github.com/expressjs/express/blob/master/Readme.md
|
||||
res.render('Readme.md');
|
||||
});
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var path = require('path');
|
||||
var User = require('./user');
|
||||
var app = express();
|
||||
|
||||
app.set('views', __dirname);
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// filter ferrets only
|
||||
|
||||
@@ -25,7 +26,7 @@ app.get('/', function(req, res, next){
|
||||
if (err) return next(err);
|
||||
User.all(function(err, users){
|
||||
if (err) return next(err);
|
||||
res.render('user', {
|
||||
res.render('index', {
|
||||
title: 'Users',
|
||||
count: count,
|
||||
users: users.filter(ferrets)
|
||||
@@ -59,7 +60,7 @@ function users(req, res, next) {
|
||||
}
|
||||
|
||||
app.get('/middleware', count, users, function(req, res, next){
|
||||
res.render('user', {
|
||||
res.render('index', {
|
||||
title: 'Users',
|
||||
count: req.count,
|
||||
users: req.users.filter(ferrets)
|
||||
@@ -101,7 +102,7 @@ app.get('/middleware-locals', count2, users2, function(req, res, next){
|
||||
// to pass to res.render(). If we have
|
||||
// several routes related to users this
|
||||
// can be a great productivity booster
|
||||
res.render('user', { title: 'Users' });
|
||||
res.render('index', { title: 'Users' });
|
||||
});
|
||||
|
||||
// keep in mind that middleware may be placed anywhere
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
title= title
|
||||
style.
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px Helvetica, Arial;
|
||||
}
|
||||
body
|
||||
h2= title
|
||||
block content
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
for user in users
|
||||
.user
|
||||
h3= user.name
|
||||
p #{user.name} is a #{user.age} year old #{user.species}.
|
||||
@@ -9,6 +9,9 @@ function User(name, age, species) {
|
||||
}
|
||||
|
||||
User.all = function(fn){
|
||||
// process.nextTick makes sure this function API
|
||||
// behaves in an asynchronous manner, like if it
|
||||
// was a real DB query to read all users.
|
||||
process.nextTick(function(){
|
||||
fn(null, users);
|
||||
});
|
||||
|
||||
19
examples/view-locals/views/index.ejs
Normal file
19
examples/view-locals/views/index.ejs
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title><%= title %></title>
|
||||
<style media="screen">
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px Helvetica, Arial;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2><%= title %></h2>
|
||||
<% users.forEach(function(user) { %>
|
||||
<li><strong><%= user.name %></strong> is a <% user.age %> year old <%= user.species %></li>
|
||||
<% }); %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -49,7 +49,7 @@ var apiKeys = ['foo', 'bar', 'baz'];
|
||||
// these two objects will serve as our faux database
|
||||
|
||||
var repos = [
|
||||
{ name: 'express', url: 'http://github.com/strongloop/express' }
|
||||
{ name: 'express', url: 'http://github.com/expressjs/express' }
|
||||
, { name: 'stylus', url: 'http://github.com/learnboost/stylus' }
|
||||
, { name: 'cluster', url: 'http://github.com/learnboost/cluster' }
|
||||
];
|
||||
@@ -61,7 +61,7 @@ var users = [
|
||||
];
|
||||
|
||||
var userRepos = {
|
||||
tobi: [repos[0], repos[1]]
|
||||
tobi: [repos[0], repos[1]]
|
||||
, loki: [repos[1]]
|
||||
, jane: [repos[2]]
|
||||
};
|
||||
|
||||
9
index.js
9
index.js
@@ -1,2 +1,11 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./lib/express');
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var finalhandler = require('finalhandler');
|
||||
var flatten = require('./utils').flatten;
|
||||
var Router = require('./router');
|
||||
var methods = require('methods');
|
||||
var middleware = require('./middleware/init');
|
||||
@@ -15,8 +25,10 @@ var compileETag = require('./utils').compileETag;
|
||||
var compileQueryParser = require('./utils').compileQueryParser;
|
||||
var compileTrust = require('./utils').compileTrust;
|
||||
var deprecate = require('depd')('express');
|
||||
var flatten = require('array-flatten');
|
||||
var merge = require('utils-merge');
|
||||
var resolve = require('path').resolve;
|
||||
var setPrototypeOf = require('setprototypeof')
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
@@ -25,6 +37,13 @@ var slice = Array.prototype.slice;
|
||||
|
||||
var app = exports = module.exports = {};
|
||||
|
||||
/**
|
||||
* Variable for trust proxy inheritance back-compat
|
||||
* @private
|
||||
*/
|
||||
|
||||
var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
|
||||
|
||||
/**
|
||||
* Initialize the server.
|
||||
*
|
||||
@@ -32,40 +51,54 @@ var app = exports = module.exports = {};
|
||||
* - setup default middleware
|
||||
* - setup route reflection methods
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
app.init = function(){
|
||||
app.init = function init() {
|
||||
this.cache = {};
|
||||
this.settings = {};
|
||||
this.engines = {};
|
||||
this.settings = {};
|
||||
|
||||
this.defaultConfiguration();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize application configuration.
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
app.defaultConfiguration = function(){
|
||||
app.defaultConfiguration = function defaultConfiguration() {
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
// default settings
|
||||
this.enable('x-powered-by');
|
||||
this.set('etag', 'weak');
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
this.set('env', env);
|
||||
this.set('query parser', 'extended');
|
||||
this.set('subdomain offset', 2);
|
||||
this.set('trust proxy', false);
|
||||
|
||||
// trust proxy inherit back-compat
|
||||
Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
|
||||
configurable: true,
|
||||
value: true
|
||||
});
|
||||
|
||||
debug('booting in %s mode', env);
|
||||
|
||||
// inherit protos
|
||||
this.on('mount', function(parent){
|
||||
this.request.__proto__ = parent.request;
|
||||
this.response.__proto__ = parent.response;
|
||||
this.engines.__proto__ = parent.engines;
|
||||
this.settings.__proto__ = parent.settings;
|
||||
this.on('mount', function onmount(parent) {
|
||||
// inherit trust proxy
|
||||
if (this.settings[trustProxyDefaultSymbol] === true
|
||||
&& typeof parent.settings['trust proxy fn'] === 'function') {
|
||||
delete this.settings['trust proxy'];
|
||||
delete this.settings['trust proxy fn'];
|
||||
}
|
||||
|
||||
// inherit protos
|
||||
setPrototypeOf(this.request, parent.request)
|
||||
setPrototypeOf(this.response, parent.response)
|
||||
setPrototypeOf(this.engines, parent.engines)
|
||||
setPrototypeOf(this.settings, parent.settings)
|
||||
});
|
||||
|
||||
// setup locals
|
||||
@@ -99,9 +132,9 @@ app.defaultConfiguration = function(){
|
||||
* We cannot add the base router in the defaultConfiguration because
|
||||
* it reads app settings which might be set after that has run.
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
app.lazyrouter = function() {
|
||||
app.lazyrouter = function lazyrouter() {
|
||||
if (!this._router) {
|
||||
this._router = new Router({
|
||||
caseSensitive: this.enabled('case sensitive routing'),
|
||||
@@ -116,17 +149,17 @@ app.lazyrouter = function() {
|
||||
/**
|
||||
* Dispatch a req, res pair into the application. Starts pipeline processing.
|
||||
*
|
||||
* If no _done_ callback is provided, then default error handlers will respond
|
||||
* If no callback is provided, then default error handlers will respond
|
||||
* in the event of an error bubbling through the stack.
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
app.handle = function(req, res, done) {
|
||||
app.handle = function handle(req, res, callback) {
|
||||
var router = this._router;
|
||||
|
||||
// final handler
|
||||
done = done || finalhandler(req, res, {
|
||||
var done = callback || finalhandler(req, res, {
|
||||
env: this.get('env'),
|
||||
onerror: logerror.bind(this)
|
||||
});
|
||||
@@ -148,7 +181,7 @@ app.handle = function(req, res, done) {
|
||||
* If the _fn_ parameter is an express app, then it will be
|
||||
* mounted at the _route_ specified.
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.use = function use(fn) {
|
||||
@@ -195,8 +228,8 @@ app.use = function use(fn) {
|
||||
router.use(path, function mounted_app(req, res, next) {
|
||||
var orig = req.app;
|
||||
fn.handle(req, res, function (err) {
|
||||
req.__proto__ = orig.request;
|
||||
res.__proto__ = orig.response;
|
||||
setPrototypeOf(req, orig.request)
|
||||
setPrototypeOf(res, orig.response)
|
||||
next(err);
|
||||
});
|
||||
});
|
||||
@@ -215,10 +248,10 @@ app.use = function use(fn) {
|
||||
* Routes are isolated middleware stacks for specific paths.
|
||||
* See the Route api docs for details.
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.route = function(path){
|
||||
app.route = function route(path) {
|
||||
this.lazyrouter();
|
||||
return this._router.route(path);
|
||||
};
|
||||
@@ -229,9 +262,9 @@ app.route = function(path){
|
||||
*
|
||||
* By default will `require()` the engine based on the
|
||||
* file extension. For example if you try to render
|
||||
* a "foo.jade" file Express will invoke the following internally:
|
||||
* a "foo.ejs" file Express will invoke the following internally:
|
||||
*
|
||||
* app.engine('jade', require('jade').__express);
|
||||
* app.engine('ejs', require('ejs').__express);
|
||||
*
|
||||
* For engines that do not provide `.__express` out of the box,
|
||||
* or if you wish to "map" a different extension to the template engine
|
||||
@@ -254,13 +287,22 @@ app.route = function(path){
|
||||
* @param {String} ext
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.engine = function(ext, fn){
|
||||
if ('function' != typeof fn) throw new Error('callback function required');
|
||||
if ('.' != ext[0]) ext = '.' + ext;
|
||||
this.engines[ext] = fn;
|
||||
app.engine = function engine(ext, fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error('callback function required');
|
||||
}
|
||||
|
||||
// get file extension
|
||||
var extension = ext[0] !== '.'
|
||||
? '.' + ext
|
||||
: ext;
|
||||
|
||||
// store engine
|
||||
this.engines[extension] = fn;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -273,20 +315,22 @@ app.engine = function(ext, fn){
|
||||
* @param {String|Array} name
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.param = function(name, fn){
|
||||
app.param = function param(name, fn) {
|
||||
this.lazyrouter();
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function(key) {
|
||||
this.param(key, fn);
|
||||
}, this);
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
this.param(name[i], fn);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
this._router.param(name, fn);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -302,31 +346,37 @@ app.param = function(name, fn){
|
||||
* @param {String} setting
|
||||
* @param {*} [val]
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.set = function(setting, val){
|
||||
app.set = function set(setting, val) {
|
||||
if (arguments.length === 1) {
|
||||
// app.get(setting)
|
||||
return this.settings[setting];
|
||||
}
|
||||
|
||||
debug('set "%s" to %o', setting, val);
|
||||
|
||||
// set value
|
||||
this.settings[setting] = val;
|
||||
|
||||
// trigger matched settings
|
||||
switch (setting) {
|
||||
case 'etag':
|
||||
debug('compile etag %s', val);
|
||||
this.set('etag fn', compileETag(val));
|
||||
break;
|
||||
case 'query parser':
|
||||
debug('compile query parser %s', val);
|
||||
this.set('query parser fn', compileQueryParser(val));
|
||||
break;
|
||||
case 'trust proxy':
|
||||
debug('compile trust proxy %s', val);
|
||||
this.set('trust proxy fn', compileTrust(val));
|
||||
|
||||
// trust proxy inherit back-compat
|
||||
Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
|
||||
configurable: true,
|
||||
value: false
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -344,10 +394,10 @@ app.set = function(setting, val){
|
||||
* return value would be "/blog/admin".
|
||||
*
|
||||
* @return {String}
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
app.path = function(){
|
||||
app.path = function path() {
|
||||
return this.parent
|
||||
? this.parent.path() + this.mountpath
|
||||
: '';
|
||||
@@ -365,11 +415,11 @@ app.path = function(){
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.enabled = function(setting){
|
||||
return !!this.set(setting);
|
||||
app.enabled = function enabled(setting) {
|
||||
return Boolean(this.set(setting));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -384,10 +434,10 @@ app.enabled = function(setting){
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.disabled = function(setting){
|
||||
app.disabled = function disabled(setting) {
|
||||
return !this.set(setting);
|
||||
};
|
||||
|
||||
@@ -396,10 +446,10 @@ app.disabled = function(setting){
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.enable = function(setting){
|
||||
app.enable = function enable(setting) {
|
||||
return this.set(setting, true);
|
||||
};
|
||||
|
||||
@@ -408,10 +458,10 @@ app.enable = function(setting){
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.disable = function(setting){
|
||||
app.disable = function disable(setting) {
|
||||
return this.set(setting, false);
|
||||
};
|
||||
|
||||
@@ -421,7 +471,10 @@ app.disable = function(setting){
|
||||
|
||||
methods.forEach(function(method){
|
||||
app[method] = function(path){
|
||||
if ('get' == method && 1 == arguments.length) return this.set(path);
|
||||
if (method === 'get' && arguments.length === 1) {
|
||||
// app.get(setting)
|
||||
return this.set(path);
|
||||
}
|
||||
|
||||
this.lazyrouter();
|
||||
|
||||
@@ -438,17 +491,18 @@ methods.forEach(function(method){
|
||||
* @param {String} path
|
||||
* @param {Function} ...
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.all = function(path){
|
||||
app.all = function all(path) {
|
||||
this.lazyrouter();
|
||||
|
||||
var route = this._router.route(path);
|
||||
var args = slice.call(arguments, 1);
|
||||
methods.forEach(function(method){
|
||||
route[method].apply(route, args);
|
||||
});
|
||||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
route[methods[i]].apply(route, args);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -469,44 +523,51 @@ app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');
|
||||
* })
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String|Function} options or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
* @param {Object|Function} options or fn
|
||||
* @param {Function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.render = function(name, options, fn){
|
||||
var opts = {};
|
||||
app.render = function render(name, options, callback) {
|
||||
var cache = this.cache;
|
||||
var done = callback;
|
||||
var engines = this.engines;
|
||||
var opts = options;
|
||||
var renderOptions = {};
|
||||
var view;
|
||||
|
||||
// support callback function as second arg
|
||||
if ('function' == typeof options) {
|
||||
fn = options, options = {};
|
||||
if (typeof options === 'function') {
|
||||
done = options;
|
||||
opts = {};
|
||||
}
|
||||
|
||||
// merge app.locals
|
||||
merge(opts, this.locals);
|
||||
merge(renderOptions, this.locals);
|
||||
|
||||
// merge options._locals
|
||||
if (options._locals) {
|
||||
merge(opts, options._locals);
|
||||
if (opts._locals) {
|
||||
merge(renderOptions, opts._locals);
|
||||
}
|
||||
|
||||
// merge options
|
||||
merge(opts, options);
|
||||
merge(renderOptions, opts);
|
||||
|
||||
// set .cache unless explicitly provided
|
||||
opts.cache = null == opts.cache
|
||||
? this.enabled('view cache')
|
||||
: opts.cache;
|
||||
if (renderOptions.cache == null) {
|
||||
renderOptions.cache = this.enabled('view cache');
|
||||
}
|
||||
|
||||
// primed cache
|
||||
if (opts.cache) view = cache[name];
|
||||
if (renderOptions.cache) {
|
||||
view = cache[name];
|
||||
}
|
||||
|
||||
// view
|
||||
if (!view) {
|
||||
view = new (this.get('view'))(name, {
|
||||
var View = this.get('view');
|
||||
|
||||
view = new View(name, {
|
||||
defaultEngine: this.get('view engine'),
|
||||
root: this.get('views'),
|
||||
engines: engines
|
||||
@@ -518,19 +579,17 @@ app.render = function(name, options, fn){
|
||||
: 'directory "' + view.root + '"'
|
||||
var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
|
||||
err.view = view;
|
||||
return fn(err);
|
||||
return done(err);
|
||||
}
|
||||
|
||||
// prime the cache
|
||||
if (opts.cache) cache[name] = view;
|
||||
if (renderOptions.cache) {
|
||||
cache[name] = view;
|
||||
}
|
||||
}
|
||||
|
||||
// render
|
||||
try {
|
||||
view.render(opts, fn);
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
tryRender(view, renderOptions, done);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -551,21 +610,35 @@ app.render = function(name, options, fn){
|
||||
* https.createServer({ ... }, app).listen(443);
|
||||
*
|
||||
* @return {http.Server}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
app.listen = function(){
|
||||
app.listen = function listen() {
|
||||
var server = http.createServer(this);
|
||||
return server.listen.apply(server, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* Log error using console.error.
|
||||
*
|
||||
* @param {Error} err
|
||||
* @api public
|
||||
*/
|
||||
* Log error using console.error.
|
||||
*
|
||||
* @param {Error} err
|
||||
* @private
|
||||
*/
|
||||
|
||||
function logerror(err){
|
||||
function logerror(err) {
|
||||
/* istanbul ignore next */
|
||||
if (this.get('env') !== 'test') console.error(err.stack || err.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try rendering a view.
|
||||
* @private
|
||||
*/
|
||||
|
||||
function tryRender(view, options, callback) {
|
||||
try {
|
||||
view.render(options, callback);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -28,11 +38,19 @@ function createApplication() {
|
||||
app.handle(req, res, next);
|
||||
};
|
||||
|
||||
mixin(app, proto);
|
||||
mixin(app, EventEmitter.prototype);
|
||||
mixin(app, EventEmitter.prototype, false);
|
||||
mixin(app, proto, false);
|
||||
|
||||
// expose the prototype that will get set on requests
|
||||
app.request = Object.create(req, {
|
||||
app: { configurable: true, enumerable: true, writable: true, value: app }
|
||||
})
|
||||
|
||||
// expose the prototype that will get set on responses
|
||||
app.response = Object.create(res, {
|
||||
app: { configurable: true, enumerable: true, writable: true, value: app }
|
||||
})
|
||||
|
||||
app.request = { __proto__: req, app: app };
|
||||
app.response = { __proto__: res, app: app };
|
||||
app.init();
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var setPrototypeOf = require('setprototypeof')
|
||||
|
||||
/**
|
||||
* Initialization middleware, exposing the
|
||||
* request and response to eachother, as well
|
||||
* request and response to each other, as well
|
||||
* as defaulting the X-Powered-By header field.
|
||||
*
|
||||
* @param {Function} app
|
||||
@@ -15,8 +32,8 @@ exports.init = function(app){
|
||||
res.req = req;
|
||||
req.next = next;
|
||||
|
||||
req.__proto__ = app.request;
|
||||
res.__proto__ = app.response;
|
||||
setPrototypeOf(req, app.request)
|
||||
setPrototypeOf(res, app.response)
|
||||
|
||||
res.locals = res.locals || Object.create(null);
|
||||
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var merge = require('utils-merge')
|
||||
var parseUrl = require('parseurl');
|
||||
var qs = require('qs');
|
||||
|
||||
@@ -12,17 +23,23 @@ var qs = require('qs');
|
||||
*/
|
||||
|
||||
module.exports = function query(options) {
|
||||
var opts = merge({}, options)
|
||||
var queryparse = qs.parse;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
queryparse = options;
|
||||
options = undefined;
|
||||
opts = undefined;
|
||||
}
|
||||
|
||||
if (opts !== undefined && opts.allowPrototypes === undefined) {
|
||||
// back-compat for qs module
|
||||
opts.allowPrototypes = true;
|
||||
}
|
||||
|
||||
return function query(req, res, next){
|
||||
if (!req.query) {
|
||||
var val = parseUrl(req).query;
|
||||
req.query = queryparse(val, options);
|
||||
req.query = queryparse(val, opts);
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
189
lib/request.js
189
lib/request.js
@@ -1,5 +1,16 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var accepts = require('accepts');
|
||||
@@ -14,11 +25,17 @@ var proxyaddr = require('proxy-addr');
|
||||
|
||||
/**
|
||||
* Request prototype.
|
||||
* @public
|
||||
*/
|
||||
|
||||
var req = exports = module.exports = {
|
||||
__proto__: http.IncomingMessage.prototype
|
||||
};
|
||||
var req = Object.create(http.IncomingMessage.prototype)
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = req
|
||||
|
||||
/**
|
||||
* Return request header.
|
||||
@@ -41,18 +58,28 @@ var req = exports = module.exports = {
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.get =
|
||||
req.header = function(name){
|
||||
switch (name = name.toLowerCase()) {
|
||||
req.header = function header(name) {
|
||||
if (!name) {
|
||||
throw new TypeError('name argument is required to req.get');
|
||||
}
|
||||
|
||||
if (typeof name !== 'string') {
|
||||
throw new TypeError('name must be a string to req.get');
|
||||
}
|
||||
|
||||
var lc = name.toLowerCase();
|
||||
|
||||
switch (lc) {
|
||||
case 'referer':
|
||||
case 'referrer':
|
||||
return this.headers.referrer
|
||||
|| this.headers.referer;
|
||||
default:
|
||||
return this.headers[name];
|
||||
return this.headers[lc];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -63,12 +90,12 @@ req.header = function(name){
|
||||
* the best match when true, otherwise `undefined`, in which
|
||||
* case you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* The `type` value may be a single mime type string
|
||||
* such as "application/json", the extension name
|
||||
* such as "json", a comma-delimted list such as "json, html, text/plain",
|
||||
* The `type` value may be a single MIME type string
|
||||
* such as "application/json", an extension name
|
||||
* such as "json", a comma-delimited list such as "json, html, text/plain",
|
||||
* an argument list such as `"json", "html", "text/plain"`,
|
||||
* or an array `["json", "html", "text/plain"]`. When a list
|
||||
* or array is given the _best_ match, if any is returned.
|
||||
* or array is given, the _best_ match, if any is returned.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -98,8 +125,8 @@ req.header = function(name){
|
||||
* // => "json"
|
||||
*
|
||||
* @param {String|Array} type(s)
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @return {String|Array|Boolean}
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.accepts = function(){
|
||||
@@ -111,8 +138,8 @@ req.accepts = function(){
|
||||
* Check if the given `encoding`s are accepted.
|
||||
*
|
||||
* @param {String} ...encoding
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @return {String|Array}
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.acceptsEncodings = function(){
|
||||
@@ -128,8 +155,8 @@ req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* @param {String} ...charset
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @return {String|Array}
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.acceptsCharsets = function(){
|
||||
@@ -145,8 +172,8 @@ req.acceptsCharset = deprecate.function(req.acceptsCharsets,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* @param {String} ...lang
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @return {String|Array}
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.acceptsLanguages = function(){
|
||||
@@ -158,29 +185,34 @@ req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
|
||||
'req.acceptsLanguage: Use acceptsLanguages instead');
|
||||
|
||||
/**
|
||||
* Parse Range header field,
|
||||
* capping to the given `size`.
|
||||
* Parse Range header field, capping to the given `size`.
|
||||
*
|
||||
* Unspecified ranges such as "0-" require
|
||||
* knowledge of your resource length. In
|
||||
* the case of a byte range this is of course
|
||||
* the total number of bytes. If the Range
|
||||
* header field is not given `null` is returned,
|
||||
* `-1` when unsatisfiable, `-2` when syntactically invalid.
|
||||
* Unspecified ranges such as "0-" require knowledge of your resource length. In
|
||||
* the case of a byte range this is of course the total number of bytes. If the
|
||||
* Range header field is not given `undefined` is returned, `-1` when unsatisfiable,
|
||||
* and `-2` when syntactically invalid.
|
||||
*
|
||||
* NOTE: remember that ranges are inclusive, so
|
||||
* for example "Range: users=0-3" should respond
|
||||
* with 4 users when available, not 3.
|
||||
* When ranges are returned, the array has a "type" property which is the type of
|
||||
* range that is required (most commonly, "bytes"). Each array element is an object
|
||||
* with a "start" and "end" property for the portion of the range.
|
||||
*
|
||||
* @param {Number} size
|
||||
* @return {Array}
|
||||
* @api public
|
||||
* The "combine" option can be set to `true` and overlapping & adjacent ranges
|
||||
* will be combined into a single range.
|
||||
*
|
||||
* NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"
|
||||
* should respond with 4 users when available, not 3.
|
||||
*
|
||||
* @param {number} size
|
||||
* @param {object} [options]
|
||||
* @param {boolean} [options.combine=false]
|
||||
* @return {number|array}
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.range = function(size){
|
||||
req.range = function range(size, options) {
|
||||
var range = this.get('Range');
|
||||
if (!range) return;
|
||||
return parseRange(size, range);
|
||||
return parseRange(size, range, options);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -197,16 +229,23 @@ req.range = function(size){
|
||||
* @param {String} name
|
||||
* @param {Mixed} [defaultValue]
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.param = function(name, defaultValue){
|
||||
req.param = function param(name, defaultValue) {
|
||||
var params = this.params || {};
|
||||
var body = this.body || {};
|
||||
var query = this.query || {};
|
||||
|
||||
var args = arguments.length === 1
|
||||
? 'name'
|
||||
: 'name, default';
|
||||
deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead');
|
||||
|
||||
if (null != params[name] && params.hasOwnProperty(name)) return params[name];
|
||||
if (null != body[name]) return body[name];
|
||||
if (null != query[name]) return query[name];
|
||||
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
@@ -231,14 +270,23 @@ req.param = function(name, defaultValue){
|
||||
* req.is('html');
|
||||
* // => false
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @param {String|Array} types...
|
||||
* @return {String|false|null}
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.is = function(types){
|
||||
if (!Array.isArray(types)) types = [].slice.call(arguments);
|
||||
return typeis(this, types);
|
||||
req.is = function is(types) {
|
||||
var arr = types;
|
||||
|
||||
// support flattened arguments
|
||||
if (!Array.isArray(types)) {
|
||||
arr = new Array(arguments.length);
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
arr[i] = arguments[i];
|
||||
}
|
||||
}
|
||||
|
||||
return typeis(this, arr);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -252,7 +300,7 @@ req.is = function(types){
|
||||
* supplies https for you this may be enabled.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'protocol', function protocol(){
|
||||
@@ -261,7 +309,7 @@ defineGetter(req, 'protocol', function protocol(){
|
||||
: 'http';
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
|
||||
if (!trust(this.connection.remoteAddress)) {
|
||||
if (!trust(this.connection.remoteAddress, 0)) {
|
||||
return proto;
|
||||
}
|
||||
|
||||
@@ -274,14 +322,14 @@ defineGetter(req, 'protocol', function protocol(){
|
||||
/**
|
||||
* Short-hand for:
|
||||
*
|
||||
* req.protocol == 'https'
|
||||
* req.protocol === 'https'
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'secure', function secure(){
|
||||
return 'https' == this.protocol;
|
||||
return this.protocol === 'https';
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -291,7 +339,7 @@ defineGetter(req, 'secure', function secure(){
|
||||
* "trust proxy" is set.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'ip', function ip(){
|
||||
@@ -308,13 +356,18 @@ defineGetter(req, 'ip', function ip(){
|
||||
* "proxy2" were trusted.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'ips', function ips() {
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var addrs = proxyaddr.all(this, trust);
|
||||
return addrs.slice(1).reverse();
|
||||
|
||||
// reverse the order (to farthest -> closest)
|
||||
// and remove socket address
|
||||
addrs.reverse().pop()
|
||||
|
||||
return addrs
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -329,7 +382,7 @@ defineGetter(req, 'ips', function ips() {
|
||||
* If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'subdomains', function subdomains() {
|
||||
@@ -349,7 +402,7 @@ defineGetter(req, 'subdomains', function subdomains() {
|
||||
* Short-hand for `url.parse(req.url).pathname`.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'path', function path() {
|
||||
@@ -364,14 +417,14 @@ defineGetter(req, 'path', function path() {
|
||||
* be trusted.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'hostname', function hostname(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var host = this.get('X-Forwarded-Host');
|
||||
|
||||
if (!host || !trust(this.connection.remoteAddress)) {
|
||||
if (!host || !trust(this.connection.remoteAddress, 0)) {
|
||||
host = this.get('Host');
|
||||
}
|
||||
|
||||
@@ -383,7 +436,7 @@ defineGetter(req, 'hostname', function hostname(){
|
||||
: 0;
|
||||
var index = host.indexOf(':', offset);
|
||||
|
||||
return ~index
|
||||
return index !== -1
|
||||
? host.substring(0, index)
|
||||
: host;
|
||||
});
|
||||
@@ -400,19 +453,23 @@ defineGetter(req, 'host', deprecate.function(function host(){
|
||||
* still match.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'fresh', function(){
|
||||
var method = this.method;
|
||||
var s = this.res.statusCode;
|
||||
var res = this.res
|
||||
var status = res.statusCode
|
||||
|
||||
// GET or HEAD for weak freshness validation only
|
||||
if ('GET' != method && 'HEAD' != method) return false;
|
||||
if ('GET' !== method && 'HEAD' !== method) return false;
|
||||
|
||||
// 2xx or 304 as per rfc2616 14.26
|
||||
if ((s >= 200 && s < 300) || 304 == s) {
|
||||
return fresh(this.headers, (this.res._headers || {}));
|
||||
if ((status >= 200 && status < 300) || 304 === status) {
|
||||
return fresh(this.headers, {
|
||||
'etag': res.get('ETag'),
|
||||
'last-modified': res.get('Last-Modified')
|
||||
})
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -424,7 +481,7 @@ defineGetter(req, 'fresh', function(){
|
||||
* resource has changed.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'stale', function stale(){
|
||||
@@ -435,12 +492,12 @@ defineGetter(req, 'stale', function stale(){
|
||||
* Check if the request was an _XMLHttpRequest_.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'xhr', function xhr(){
|
||||
var val = this.get('X-Requested-With') || '';
|
||||
return 'xmlhttprequest' == val.toLowerCase();
|
||||
return val.toLowerCase() === 'xmlhttprequest';
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -449,7 +506,7 @@ defineGetter(req, 'xhr', function xhr(){
|
||||
* @param {Object} obj
|
||||
* @param {String} name
|
||||
* @param {Function} getter
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
function defineGetter(obj, name, getter) {
|
||||
Object.defineProperty(obj, name, {
|
||||
@@ -457,4 +514,4 @@ function defineGetter(obj, name, getter) {
|
||||
enumerable: true,
|
||||
get: getter
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
382
lib/response.js
382
lib/response.js
@@ -1,20 +1,31 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var contentDisposition = require('content-disposition');
|
||||
var deprecate = require('depd')('express');
|
||||
var encodeUrl = require('encodeurl');
|
||||
var escapeHtml = require('escape-html');
|
||||
var http = require('http');
|
||||
var isAbsolute = require('./utils').isAbsolute;
|
||||
var onFinished = require('on-finished');
|
||||
var path = require('path');
|
||||
var statuses = require('statuses')
|
||||
var merge = require('utils-merge');
|
||||
var sign = require('cookie-signature').sign;
|
||||
var normalizeType = require('./utils').normalizeType;
|
||||
var normalizeTypes = require('./utils').normalizeTypes;
|
||||
var setCharset = require('./utils').setCharset;
|
||||
var statusCodes = http.STATUS_CODES;
|
||||
var cookie = require('cookie');
|
||||
var send = require('send');
|
||||
var extname = path.extname;
|
||||
@@ -24,21 +35,34 @@ var vary = require('vary');
|
||||
|
||||
/**
|
||||
* Response prototype.
|
||||
* @public
|
||||
*/
|
||||
|
||||
var res = module.exports = {
|
||||
__proto__: http.ServerResponse.prototype
|
||||
};
|
||||
var res = Object.create(http.ServerResponse.prototype)
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = res
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var charsetRegExp = /;\s*charset\s*=/;
|
||||
|
||||
/**
|
||||
* Set status `code`.
|
||||
*
|
||||
* @param {Number} code
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.status = function(code){
|
||||
res.status = function status(code) {
|
||||
this.statusCode = code;
|
||||
return this;
|
||||
};
|
||||
@@ -55,7 +79,7 @@ res.status = function(code){
|
||||
*
|
||||
* @param {Object} links
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.links = function(links){
|
||||
@@ -76,7 +100,7 @@ res.links = function(links){
|
||||
* res.send('<p>some html</p>');
|
||||
*
|
||||
* @param {string|number|boolean|object|Buffer} body
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.send = function send(body) {
|
||||
@@ -111,7 +135,7 @@ res.send = function send(body) {
|
||||
|
||||
deprecate('res.send(status): Use res.sendStatus(status) instead');
|
||||
this.statusCode = chunk;
|
||||
chunk = http.STATUS_CODES[chunk];
|
||||
chunk = statuses[chunk]
|
||||
}
|
||||
|
||||
switch (typeof chunk) {
|
||||
@@ -159,15 +183,12 @@ res.send = function send(body) {
|
||||
this.set('Content-Length', len);
|
||||
}
|
||||
|
||||
// method check
|
||||
var isHead = req.method === 'HEAD';
|
||||
|
||||
// ETag support
|
||||
if (len !== undefined && (isHead || req.method === 'GET')) {
|
||||
var etag = app.get('etag fn');
|
||||
if (etag && !this.get('ETag')) {
|
||||
etag = etag(chunk, encoding);
|
||||
etag && this.set('ETag', etag);
|
||||
// populate ETag
|
||||
var etag;
|
||||
var generateETag = len !== undefined && app.get('etag fn');
|
||||
if (typeof generateETag === 'function' && !this.get('ETag')) {
|
||||
if ((etag = generateETag(chunk, encoding))) {
|
||||
this.set('ETag', etag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,14 +196,14 @@ res.send = function send(body) {
|
||||
if (req.fresh) this.statusCode = 304;
|
||||
|
||||
// strip irrelevant headers
|
||||
if (204 == this.statusCode || 304 == this.statusCode) {
|
||||
if (204 === this.statusCode || 304 === this.statusCode) {
|
||||
this.removeHeader('Content-Type');
|
||||
this.removeHeader('Content-Length');
|
||||
this.removeHeader('Transfer-Encoding');
|
||||
chunk = '';
|
||||
}
|
||||
|
||||
if (isHead) {
|
||||
if (req.method === 'HEAD') {
|
||||
// skip body for HEAD
|
||||
this.end();
|
||||
} else {
|
||||
@@ -202,7 +223,7 @@ res.send = function send(body) {
|
||||
* res.json({ user: 'tj' });
|
||||
*
|
||||
* @param {string|number|boolean|object} obj
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.json = function json(obj) {
|
||||
@@ -225,7 +246,7 @@ res.json = function json(obj) {
|
||||
var app = this.app;
|
||||
var replacer = app.get('json replacer');
|
||||
var spaces = app.get('json spaces');
|
||||
var body = JSON.stringify(val, replacer, spaces);
|
||||
var body = stringify(val, replacer, spaces);
|
||||
|
||||
// content-type
|
||||
if (!this.get('Content-Type')) {
|
||||
@@ -244,7 +265,7 @@ res.json = function json(obj) {
|
||||
* res.jsonp({ user: 'tj' });
|
||||
*
|
||||
* @param {string|number|boolean|object} obj
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.jsonp = function jsonp(obj) {
|
||||
@@ -267,7 +288,7 @@ res.jsonp = function jsonp(obj) {
|
||||
var app = this.app;
|
||||
var replacer = app.get('json replacer');
|
||||
var spaces = app.get('json spaces');
|
||||
var body = JSON.stringify(val, replacer, spaces);
|
||||
var body = stringify(val, replacer, spaces);
|
||||
var callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
@@ -315,11 +336,11 @@ res.jsonp = function jsonp(obj) {
|
||||
* res.sendStatus(200);
|
||||
*
|
||||
* @param {number} statusCode
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.sendStatus = function sendStatus(statusCode) {
|
||||
var body = http.STATUS_CODES[statusCode] || String(statusCode);
|
||||
var body = statuses[statusCode] || String(statusCode)
|
||||
|
||||
this.statusCode = statusCode;
|
||||
this.type('txt');
|
||||
@@ -331,7 +352,7 @@ res.sendStatus = function sendStatus(statusCode) {
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
* Automatically sets the _Content-Type_ response header field.
|
||||
* The callback `fn(err)` is invoked when the transfer is complete
|
||||
* The callback `callback(err)` is invoked when the transfer is complete
|
||||
* or when an error occurs. Be sure to check `res.sentHeader`
|
||||
* if you wish to attempt responding, as the header and some data
|
||||
* may have already been transferred.
|
||||
@@ -365,13 +386,15 @@ res.sendStatus = function sendStatus(statusCode) {
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.sendFile = function sendFile(path, options, fn) {
|
||||
res.sendFile = function sendFile(path, options, callback) {
|
||||
var done = callback;
|
||||
var req = this.req;
|
||||
var res = this;
|
||||
var next = req.next;
|
||||
var opts = options || {};
|
||||
|
||||
if (!path) {
|
||||
throw new TypeError('path argument is required to res.sendFile');
|
||||
@@ -379,27 +402,25 @@ res.sendFile = function sendFile(path, options, fn) {
|
||||
|
||||
// support function as second arg
|
||||
if (typeof options === 'function') {
|
||||
fn = options;
|
||||
options = {};
|
||||
done = options;
|
||||
opts = {};
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.root && !isAbsolute(path)) {
|
||||
if (!opts.root && !isAbsolute(path)) {
|
||||
throw new TypeError('path must be absolute or specify root to res.sendFile');
|
||||
}
|
||||
|
||||
// create file stream
|
||||
var pathname = encodeURI(path);
|
||||
var file = send(req, pathname, options);
|
||||
var file = send(req, pathname, opts);
|
||||
|
||||
// transfer
|
||||
sendfile(res, file, options, function (err) {
|
||||
if (fn) return fn(err);
|
||||
sendfile(res, file, opts, function (err) {
|
||||
if (done) return done(err);
|
||||
if (err && err.code === 'EISDIR') return next();
|
||||
|
||||
// next() all but write errors
|
||||
if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') {
|
||||
if (err && err.code !== 'ECONNABORTED' && err.syscall !== 'write') {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
@@ -409,7 +430,7 @@ res.sendFile = function sendFile(path, options, fn) {
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
* Automatically sets the _Content-Type_ response header field.
|
||||
* The callback `fn(err)` is invoked when the transfer is complete
|
||||
* The callback `callback(err)` is invoked when the transfer is complete
|
||||
* or when an error occurs. Be sure to check `res.sentHeader`
|
||||
* if you wish to attempt responding, as the header and some data
|
||||
* may have already been transferred.
|
||||
@@ -443,28 +464,28 @@ res.sendFile = function sendFile(path, options, fn) {
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.sendfile = function(path, options, fn){
|
||||
res.sendfile = function (path, options, callback) {
|
||||
var done = callback;
|
||||
var req = this.req;
|
||||
var res = this;
|
||||
var next = req.next;
|
||||
var opts = options || {};
|
||||
|
||||
// support function as second arg
|
||||
if (typeof options === 'function') {
|
||||
fn = options;
|
||||
options = {};
|
||||
done = options;
|
||||
opts = {};
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
// create file stream
|
||||
var file = send(req, path, options);
|
||||
var file = send(req, path, opts);
|
||||
|
||||
// transfer
|
||||
sendfile(res, file, options, function (err) {
|
||||
if (fn) return fn(err);
|
||||
sendfile(res, file, opts, function (err) {
|
||||
if (done) return done(err);
|
||||
if (err && err.code === 'EISDIR') return next();
|
||||
|
||||
// next() all but write errors
|
||||
@@ -481,33 +502,34 @@ res.sendfile = deprecate.function(res.sendfile,
|
||||
* Transfer the file at the given `path` as an attachment.
|
||||
*
|
||||
* Optionally providing an alternate attachment `filename`,
|
||||
* and optional callback `fn(err)`. The callback is invoked
|
||||
* and optional callback `callback(err)`. The callback is invoked
|
||||
* when the data transfer is complete, or when an error has
|
||||
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
|
||||
*
|
||||
* This method uses `res.sendfile()`.
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.download = function download(path, filename, fn) {
|
||||
res.download = function download(path, filename, callback) {
|
||||
var done = callback;
|
||||
var name = filename;
|
||||
|
||||
// support function as second arg
|
||||
if (typeof filename === 'function') {
|
||||
fn = filename;
|
||||
filename = null;
|
||||
done = filename;
|
||||
name = null;
|
||||
}
|
||||
|
||||
filename = filename || path;
|
||||
|
||||
// set Content-Disposition when file is sent
|
||||
var headers = {
|
||||
'Content-Disposition': contentDisposition(filename)
|
||||
'Content-Disposition': contentDisposition(name || path)
|
||||
};
|
||||
|
||||
// Resolve the full path for sendFile
|
||||
var fullPath = resolve(path);
|
||||
|
||||
return this.sendFile(fullPath, { headers: headers }, fn);
|
||||
return this.sendFile(fullPath, { headers: headers }, done);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -524,14 +546,16 @@ res.download = function download(path, filename, fn) {
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.contentType =
|
||||
res.type = function(type){
|
||||
return this.set('Content-Type', ~type.indexOf('/')
|
||||
? type
|
||||
: mime.lookup(type));
|
||||
res.type = function contentType(type) {
|
||||
var ct = type.indexOf('/') === -1
|
||||
? mime.lookup(type)
|
||||
: type;
|
||||
|
||||
return this.set('Content-Type', ct);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -588,7 +612,7 @@ res.type = function(type){
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.format = function(obj){
|
||||
@@ -599,7 +623,9 @@ res.format = function(obj){
|
||||
if (fn) delete obj.default;
|
||||
var keys = Object.keys(obj);
|
||||
|
||||
var key = req.accepts(keys);
|
||||
var key = keys.length > 0
|
||||
? req.accepts(keys)
|
||||
: false;
|
||||
|
||||
this.vary("Accept");
|
||||
|
||||
@@ -610,7 +636,7 @@ res.format = function(obj){
|
||||
fn();
|
||||
} else {
|
||||
var err = new Error('Not Acceptable');
|
||||
err.status = 406;
|
||||
err.status = err.statusCode = 406;
|
||||
err.types = normalizeTypes(keys).map(function(o){ return o.value });
|
||||
next(err);
|
||||
}
|
||||
@@ -623,7 +649,7 @@ res.format = function(obj){
|
||||
*
|
||||
* @param {String} filename
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.attachment = function attachment(filename) {
|
||||
@@ -636,6 +662,35 @@ res.attachment = function attachment(filename) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Append additional header `field` with value `val`.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
|
||||
* res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
|
||||
* res.append('Warning', '199 Miscellaneous warning');
|
||||
*
|
||||
* @param {String} field
|
||||
* @param {String|Array} val
|
||||
* @return {ServerResponse} for chaining
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.append = function append(field, val) {
|
||||
var prev = this.get(field);
|
||||
var value = val;
|
||||
|
||||
if (prev) {
|
||||
// concat the new and prev vals
|
||||
value = Array.isArray(prev) ? prev.concat(val)
|
||||
: Array.isArray(val) ? [prev].concat(val)
|
||||
: [prev, val];
|
||||
}
|
||||
|
||||
return this.set(field, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set header `field` to `val`, or pass
|
||||
* an object of header fields.
|
||||
@@ -648,22 +703,31 @@ res.attachment = function attachment(filename) {
|
||||
*
|
||||
* Aliased as `res.header()`.
|
||||
*
|
||||
* @param {String|Object|Array} field
|
||||
* @param {String} val
|
||||
* @param {String|Object} field
|
||||
* @param {String|Array} val
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.set =
|
||||
res.header = function header(field, val) {
|
||||
if (arguments.length === 2) {
|
||||
if (Array.isArray(val)) val = val.map(String);
|
||||
else val = String(val);
|
||||
if ('content-type' == field.toLowerCase() && !/;\s*charset\s*=/.test(val)) {
|
||||
var charset = mime.charsets.lookup(val.split(';')[0]);
|
||||
if (charset) val += '; charset=' + charset.toLowerCase();
|
||||
var value = Array.isArray(val)
|
||||
? val.map(String)
|
||||
: String(val);
|
||||
|
||||
// add charset to content-type
|
||||
if (field.toLowerCase() === 'content-type') {
|
||||
if (Array.isArray(value)) {
|
||||
throw new TypeError('Content-Type cannot be set to an Array');
|
||||
}
|
||||
if (!charsetRegExp.test(value)) {
|
||||
var charset = mime.charsets.lookup(value.split(';')[0]);
|
||||
if (charset) value += '; charset=' + charset.toLowerCase();
|
||||
}
|
||||
}
|
||||
this.setHeader(field, val);
|
||||
|
||||
this.setHeader(field, value);
|
||||
} else {
|
||||
for (var key in field) {
|
||||
this.set(key, field[key]);
|
||||
@@ -677,7 +741,7 @@ res.header = function header(field, val) {
|
||||
*
|
||||
* @param {String} field
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.get = function(field){
|
||||
@@ -688,20 +752,19 @@ res.get = function(field){
|
||||
* Clear cookie `name`.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} options
|
||||
* @param {Object} [options]
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.clearCookie = function(name, options){
|
||||
var opts = { expires: new Date(1), path: '/' };
|
||||
return this.cookie(name, '', options
|
||||
? merge(opts, options)
|
||||
: opts);
|
||||
res.clearCookie = function clearCookie(name, options) {
|
||||
var opts = merge({ expires: new Date(1), path: '/' }, options);
|
||||
|
||||
return this.cookie(name, '', opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set cookie `name` to `val`, with the given `options`.
|
||||
* Set cookie `name` to `value`, with the given `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
@@ -718,41 +781,43 @@ res.clearCookie = function(name, options){
|
||||
* res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String|Object} val
|
||||
* @param {Options} options
|
||||
* @param {String|Object} value
|
||||
* @param {Object} [options]
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.cookie = function(name, val, options){
|
||||
options = merge({}, options);
|
||||
res.cookie = function (name, value, options) {
|
||||
var opts = merge({}, options);
|
||||
var secret = this.req.secret;
|
||||
var signed = options.signed;
|
||||
if (signed && !secret) throw new Error('cookieParser("secret") required for signed cookies');
|
||||
if ('number' == typeof val) val = val.toString();
|
||||
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
|
||||
if (signed) val = 's:' + sign(val, secret);
|
||||
if ('maxAge' in options) {
|
||||
options.expires = new Date(Date.now() + options.maxAge);
|
||||
options.maxAge /= 1000;
|
||||
}
|
||||
if (null == options.path) options.path = '/';
|
||||
var headerVal = cookie.serialize(name, String(val), options);
|
||||
var signed = opts.signed;
|
||||
|
||||
// supports multiple 'res.cookie' calls by getting previous value
|
||||
var prev = this.get('Set-Cookie');
|
||||
if (prev) {
|
||||
if (Array.isArray(prev)) {
|
||||
headerVal = prev.concat(headerVal);
|
||||
} else {
|
||||
headerVal = [prev, headerVal];
|
||||
}
|
||||
if (signed && !secret) {
|
||||
throw new Error('cookieParser("secret") required for signed cookies');
|
||||
}
|
||||
this.set('Set-Cookie', headerVal);
|
||||
|
||||
var val = typeof value === 'object'
|
||||
? 'j:' + JSON.stringify(value)
|
||||
: String(value);
|
||||
|
||||
if (signed) {
|
||||
val = 's:' + sign(val, secret);
|
||||
}
|
||||
|
||||
if ('maxAge' in opts) {
|
||||
opts.expires = new Date(Date.now() + opts.maxAge);
|
||||
opts.maxAge /= 1000;
|
||||
}
|
||||
|
||||
if (opts.path == null) {
|
||||
opts.path = '/';
|
||||
}
|
||||
|
||||
this.append('Set-Cookie', cookie.serialize(name, String(val), opts));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set the location header to `url`.
|
||||
*
|
||||
@@ -767,18 +832,19 @@ res.cookie = function(name, val, options){
|
||||
*
|
||||
* @param {String} url
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.location = function(url){
|
||||
var req = this.req;
|
||||
res.location = function location(url) {
|
||||
var loc = url;
|
||||
|
||||
// "back" is an alias for the referrer
|
||||
if ('back' == url) url = req.get('Referrer') || '/';
|
||||
if (url === 'back') {
|
||||
loc = this.req.get('Referrer') || '/';
|
||||
}
|
||||
|
||||
// Respond
|
||||
this.set('Location', url);
|
||||
return this;
|
||||
// set location
|
||||
return this.set('Location', encodeUrl(loc));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -796,7 +862,7 @@ res.location = function(url){
|
||||
* res.redirect(301, 'http://example.com');
|
||||
* res.redirect('../login'); // /blog/post/1 -> /blog/login
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.redirect = function redirect(url) {
|
||||
@@ -816,18 +882,17 @@ res.redirect = function redirect(url) {
|
||||
}
|
||||
|
||||
// Set location header
|
||||
this.location(address);
|
||||
address = this.get('Location');
|
||||
address = this.location(address).get('Location');
|
||||
|
||||
// Support text/{plain,html} by default
|
||||
this.format({
|
||||
text: function(){
|
||||
body = statusCodes[status] + '. Redirecting to ' + encodeURI(address);
|
||||
body = statuses[status] + '. Redirecting to ' + address
|
||||
},
|
||||
|
||||
html: function(){
|
||||
var u = escapeHtml(address);
|
||||
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
|
||||
body = '<p>' + statuses[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'
|
||||
},
|
||||
|
||||
default: function(){
|
||||
@@ -841,9 +906,9 @@ res.redirect = function redirect(url) {
|
||||
|
||||
if (this.req.method === 'HEAD') {
|
||||
this.end();
|
||||
} else {
|
||||
this.end(body);
|
||||
}
|
||||
|
||||
this.end(body);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -852,7 +917,7 @@ res.redirect = function redirect(url) {
|
||||
*
|
||||
* @param {Array|String} field
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.vary = function(field){
|
||||
@@ -877,37 +942,49 @@ res.vary = function(field){
|
||||
* - `cache` boolean hinting to the engine it should cache
|
||||
* - `filename` filename of the view being rendered
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
res.render = function(view, options, fn){
|
||||
options = options || {};
|
||||
var self = this;
|
||||
res.render = function render(view, options, callback) {
|
||||
var app = this.req.app;
|
||||
var done = callback;
|
||||
var opts = options || {};
|
||||
var req = this.req;
|
||||
var app = req.app;
|
||||
var self = this;
|
||||
|
||||
// support callback function as second arg
|
||||
if ('function' == typeof options) {
|
||||
fn = options, options = {};
|
||||
if (typeof options === 'function') {
|
||||
done = options;
|
||||
opts = {};
|
||||
}
|
||||
|
||||
// merge res.locals
|
||||
options._locals = self.locals;
|
||||
opts._locals = self.locals;
|
||||
|
||||
// default callback to respond
|
||||
fn = fn || function(err, str){
|
||||
done = done || function (err, str) {
|
||||
if (err) return req.next(err);
|
||||
self.send(str);
|
||||
};
|
||||
|
||||
// render
|
||||
app.render(view, options, fn);
|
||||
app.render(view, opts, done);
|
||||
};
|
||||
|
||||
// pipe the send file stream
|
||||
function sendfile(res, file, options, callback) {
|
||||
var done = false;
|
||||
var streaming = false;
|
||||
var streaming;
|
||||
|
||||
// request aborted
|
||||
function onaborted() {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
var err = new Error('Request aborted');
|
||||
err.code = 'ECONNABORTED';
|
||||
callback(err);
|
||||
}
|
||||
|
||||
// directory
|
||||
function ondirectory() {
|
||||
@@ -921,7 +998,6 @@ function sendfile(res, file, options, callback) {
|
||||
|
||||
// errors
|
||||
function onerror(err) {
|
||||
if (!err) return;
|
||||
if (done) return;
|
||||
done = true;
|
||||
callback(err);
|
||||
@@ -936,27 +1012,24 @@ function sendfile(res, file, options, callback) {
|
||||
|
||||
// file
|
||||
function onfile() {
|
||||
onFinished(res, onfinish);
|
||||
streaming = false;
|
||||
}
|
||||
|
||||
// finished
|
||||
function onfinish(err) {
|
||||
if (err && err.code === 'ECONNRESET') return onaborted();
|
||||
if (err) return onerror(err);
|
||||
if (done) return;
|
||||
|
||||
setImmediate(function () {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
if (!streaming) {
|
||||
callback();
|
||||
if (streaming !== false && !done) {
|
||||
onaborted();
|
||||
return;
|
||||
}
|
||||
|
||||
// response finished before end of file
|
||||
var err = new Error('Request aborted');
|
||||
err.code = 'ECONNABORT';
|
||||
callback(err);
|
||||
if (done) return;
|
||||
done = true;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -970,7 +1043,7 @@ function sendfile(res, file, options, callback) {
|
||||
file.on('error', onerror);
|
||||
file.on('file', onfile);
|
||||
file.on('stream', onstream);
|
||||
onFinished(res, onerror);
|
||||
onFinished(res, onfinish);
|
||||
|
||||
if (options.headers) {
|
||||
// set headers on successful transfer
|
||||
@@ -988,3 +1061,16 @@ function sendfile(res, file, options, callback) {
|
||||
// pipe
|
||||
file.pipe(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stringify JSON, like JSON.stringify, but v8 optimized.
|
||||
* @private
|
||||
*/
|
||||
|
||||
function stringify(value, replacer, spaces) {
|
||||
// v8 checks arguments.length for optimizing simple call
|
||||
// https://bugs.chromium.org/p/v8/issues/detail?id=4730
|
||||
return replacer || spaces
|
||||
? JSON.stringify(value, replacer, spaces)
|
||||
: JSON.stringify(value);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Route = require('./route');
|
||||
@@ -8,11 +18,14 @@ var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
var mixin = require('utils-merge');
|
||||
var debug = require('debug')('express:router');
|
||||
var deprecate = require('depd')('express');
|
||||
var flatten = require('array-flatten');
|
||||
var parseUrl = require('parseurl');
|
||||
var utils = require('../utils');
|
||||
var setPrototypeOf = require('setprototypeof')
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var objectRegExp = /^\[object (\S+)\]$/;
|
||||
@@ -24,24 +37,24 @@ var toString = Object.prototype.toString;
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Router} which is an callable function
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
var proto = module.exports = function(options) {
|
||||
options = options || {};
|
||||
var opts = options || {};
|
||||
|
||||
function router(req, res, next) {
|
||||
router.handle(req, res, next);
|
||||
}
|
||||
|
||||
// mixin Router class functions
|
||||
router.__proto__ = proto;
|
||||
setPrototypeOf(router, proto)
|
||||
|
||||
router.params = {};
|
||||
router._params = [];
|
||||
router.caseSensitive = options.caseSensitive;
|
||||
router.mergeParams = options.mergeParams;
|
||||
router.strict = options.strict;
|
||||
router.caseSensitive = opts.caseSensitive;
|
||||
router.mergeParams = opts.mergeParams;
|
||||
router.strict = opts.strict;
|
||||
router.stack = [];
|
||||
|
||||
return router;
|
||||
@@ -78,12 +91,13 @@ var proto = module.exports = function(options) {
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
proto.param = function(name, fn){
|
||||
proto.param = function param(name, fn) {
|
||||
// param logic
|
||||
if ('function' == typeof name) {
|
||||
if (typeof name === 'function') {
|
||||
deprecate('router.param(fn): Refactor to use path params');
|
||||
this._params.push(name);
|
||||
return;
|
||||
}
|
||||
@@ -94,6 +108,7 @@ proto.param = function(name, fn){
|
||||
var ret;
|
||||
|
||||
if (name[0] === ':') {
|
||||
deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead');
|
||||
name = name.substr(1);
|
||||
}
|
||||
|
||||
@@ -105,7 +120,7 @@ proto.param = function(name, fn){
|
||||
|
||||
// ensure we end up with a
|
||||
// middleware function
|
||||
if ('function' != typeof fn) {
|
||||
if ('function' !== typeof fn) {
|
||||
throw new Error('invalid param() call for ' + name + ', got ' + fn);
|
||||
}
|
||||
|
||||
@@ -115,20 +130,16 @@ proto.param = function(name, fn){
|
||||
|
||||
/**
|
||||
* Dispatch a req, res into the router.
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
proto.handle = function(req, res, done) {
|
||||
proto.handle = function handle(req, res, out) {
|
||||
var self = this;
|
||||
|
||||
debug('dispatching %s %s', req.method, req.url);
|
||||
|
||||
var search = 1 + req.url.indexOf('?');
|
||||
var pathlength = search ? search - 1 : req.url.length;
|
||||
var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://');
|
||||
var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
|
||||
var idx = 0;
|
||||
var protohost = getProtohost(req.url) || ''
|
||||
var removed = '';
|
||||
var slashAdded = false;
|
||||
var paramcalled = {};
|
||||
@@ -143,7 +154,7 @@ proto.handle = function(req, res, done) {
|
||||
// manage inter-router variables
|
||||
var parentParams = req.params;
|
||||
var parentUrl = req.baseUrl || '';
|
||||
done = restore(done, req, 'baseUrl', 'next', 'params');
|
||||
var done = restore(out, req, 'baseUrl', 'next', 'params');
|
||||
|
||||
// setup next layer
|
||||
req.next = next;
|
||||
@@ -152,9 +163,7 @@ proto.handle = function(req, res, done) {
|
||||
if (req.method === 'OPTIONS') {
|
||||
done = wrap(done, function(old, err) {
|
||||
if (err || options.length === 0) return old(err);
|
||||
|
||||
var body = options.join(',');
|
||||
return res.set('Allow', body).send(body);
|
||||
sendOptionsResponse(res, options, old);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -169,90 +178,127 @@ proto.handle = function(req, res, done) {
|
||||
? null
|
||||
: err;
|
||||
|
||||
var layer = stack[idx++];
|
||||
|
||||
// remove added slash
|
||||
if (slashAdded) {
|
||||
req.url = req.url.substr(1);
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
// restore altered req.url
|
||||
if (removed.length !== 0) {
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
removed = '';
|
||||
}
|
||||
|
||||
if (!layer) {
|
||||
// signal to exit router
|
||||
if (layerError === 'router') {
|
||||
setImmediate(done, null)
|
||||
return
|
||||
}
|
||||
|
||||
// no more matching layers
|
||||
if (idx >= stack.length) {
|
||||
setImmediate(done, layerError);
|
||||
return;
|
||||
}
|
||||
|
||||
self.match_layer(layer, req, res, function (err, path) {
|
||||
if (err || path === undefined) {
|
||||
// get pathname of request
|
||||
var path = getPathname(req);
|
||||
|
||||
if (path == null) {
|
||||
return done(layerError);
|
||||
}
|
||||
|
||||
// find next matching layer
|
||||
var layer;
|
||||
var match;
|
||||
var route;
|
||||
|
||||
while (match !== true && idx < stack.length) {
|
||||
layer = stack[idx++];
|
||||
match = matchLayer(layer, path);
|
||||
route = layer.route;
|
||||
|
||||
if (typeof match !== 'boolean') {
|
||||
// hold on to layerError
|
||||
layerError = layerError || match;
|
||||
}
|
||||
|
||||
if (match !== true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!route) {
|
||||
// process non-route handlers normally
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layerError) {
|
||||
// routes do not match with a pending error
|
||||
match = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
var method = req.method;
|
||||
var has_method = route._handles_method(method);
|
||||
|
||||
// build up automatic options response
|
||||
if (!has_method && method === 'OPTIONS') {
|
||||
appendMethods(options, route._options());
|
||||
}
|
||||
|
||||
// don't even bother matching route
|
||||
if (!has_method && method !== 'HEAD') {
|
||||
match = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// no match
|
||||
if (match !== true) {
|
||||
return done(layerError);
|
||||
}
|
||||
|
||||
// store route for dispatch on change
|
||||
if (route) {
|
||||
req.route = route;
|
||||
}
|
||||
|
||||
// Capture one-time layer values
|
||||
req.params = self.mergeParams
|
||||
? mergeParams(layer.params, parentParams)
|
||||
: layer.params;
|
||||
var layerPath = layer.path;
|
||||
|
||||
// this should be done for the layer
|
||||
self.process_params(layer, paramcalled, req, res, function (err) {
|
||||
if (err) {
|
||||
return next(layerError || err);
|
||||
}
|
||||
|
||||
// route object and not middleware
|
||||
var route = layer.route;
|
||||
|
||||
// if final route, then we support options
|
||||
if (route) {
|
||||
// we don't run any routes with error first
|
||||
if (layerError) {
|
||||
return next(layerError);
|
||||
}
|
||||
|
||||
var method = req.method;
|
||||
var has_method = route._handles_method(method);
|
||||
|
||||
// build up automatic options response
|
||||
if (!has_method && method === 'OPTIONS') {
|
||||
appendMethods(options, route._options());
|
||||
}
|
||||
|
||||
// don't even bother
|
||||
if (!has_method && method !== 'HEAD') {
|
||||
return next();
|
||||
}
|
||||
|
||||
// we can now dispatch to the route
|
||||
req.route = route;
|
||||
return layer.handle_request(req, res, next);
|
||||
}
|
||||
|
||||
// Capture one-time layer values
|
||||
req.params = self.mergeParams
|
||||
? mergeParams(layer.params, parentParams)
|
||||
: layer.params;
|
||||
var layerPath = layer.path;
|
||||
|
||||
// this should be done for the layer
|
||||
self.process_params(layer, paramcalled, req, res, function (err) {
|
||||
if (err) {
|
||||
return next(layerError || err);
|
||||
}
|
||||
|
||||
if (route) {
|
||||
return layer.handle_request(req, res, next);
|
||||
}
|
||||
|
||||
trim_prefix(layer, layerError, layerPath, path);
|
||||
});
|
||||
trim_prefix(layer, layerError, layerPath, path);
|
||||
});
|
||||
}
|
||||
|
||||
function trim_prefix(layer, layerError, layerPath, path) {
|
||||
var c = path[layerPath.length];
|
||||
if (c && '/' !== c && '.' !== c) return next(layerError);
|
||||
|
||||
// Trim off the part of the url that matches the route
|
||||
// middleware (.use stuff) needs to have the path stripped
|
||||
if (layerPath.length !== 0) {
|
||||
// Validate path breaks on a path separator
|
||||
var c = path[layerPath.length]
|
||||
if (c && c !== '/' && c !== '.') return next(layerError)
|
||||
|
||||
// Trim off the part of the url that matches the route
|
||||
// middleware (.use stuff) needs to have the path stripped
|
||||
debug('trim prefix (%s) from url %s', layerPath, req.url);
|
||||
removed = layerPath;
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
|
||||
// Ensure leading slash
|
||||
if (!fqdn && req.url[0] !== '/') {
|
||||
if (!protohost && req.url[0] !== '/') {
|
||||
req.url = '/' + req.url;
|
||||
slashAdded = true;
|
||||
}
|
||||
@@ -273,36 +319,12 @@ proto.handle = function(req, res, done) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Match request to a layer.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
proto.match_layer = function match_layer(layer, req, res, done) {
|
||||
var error = null;
|
||||
var path;
|
||||
|
||||
try {
|
||||
path = parseUrl(req).pathname;
|
||||
|
||||
if (!layer.match(path)) {
|
||||
path = undefined;
|
||||
}
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
done(error, path);
|
||||
};
|
||||
|
||||
/**
|
||||
* Process any parameters for the layer.
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
proto.process_params = function(layer, called, req, res, done) {
|
||||
proto.process_params = function process_params(layer, called, req, res, done) {
|
||||
var params = this.params;
|
||||
|
||||
// captured parameters from the layer, keys and values
|
||||
@@ -334,11 +356,6 @@ proto.process_params = function(layer, called, req, res, done) {
|
||||
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
|
||||
if (!key) {
|
||||
return done();
|
||||
}
|
||||
|
||||
name = key.name;
|
||||
paramVal = req.params[name];
|
||||
paramCallbacks = params[name];
|
||||
@@ -349,7 +366,8 @@ proto.process_params = function(layer, called, req, res, done) {
|
||||
}
|
||||
|
||||
// param previously called with same value or error occurred
|
||||
if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
|
||||
if (paramCalled && (paramCalled.match === paramVal
|
||||
|| (paramCalled.error && paramCalled.error !== 'route'))) {
|
||||
// restore value
|
||||
req.params[name] = paramCalled.value;
|
||||
|
||||
@@ -404,7 +422,7 @@ proto.process_params = function(layer, called, req, res, done) {
|
||||
* handlers can operate without any code changes regardless of the "prefix"
|
||||
* pathname.
|
||||
*
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
proto.use = function use(fn) {
|
||||
@@ -427,19 +445,21 @@ proto.use = function use(fn) {
|
||||
}
|
||||
}
|
||||
|
||||
var callbacks = utils.flatten(slice.call(arguments, offset));
|
||||
var callbacks = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (callbacks.length === 0) {
|
||||
throw new TypeError('Router.use() requires middleware functions');
|
||||
}
|
||||
|
||||
callbacks.forEach(function (fn) {
|
||||
for (var i = 0; i < callbacks.length; i++) {
|
||||
var fn = callbacks[i];
|
||||
|
||||
if (typeof fn !== 'function') {
|
||||
throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
|
||||
}
|
||||
|
||||
// add the middleware
|
||||
debug('use %s %s', path, fn.name || '<anonymous>');
|
||||
debug('use %o %s', path, fn.name || '<anonymous>')
|
||||
|
||||
var layer = new Layer(path, {
|
||||
sensitive: this.caseSensitive,
|
||||
@@ -450,7 +470,7 @@ proto.use = function use(fn) {
|
||||
layer.route = undefined;
|
||||
|
||||
this.stack.push(layer);
|
||||
}, this);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -465,10 +485,10 @@ proto.use = function use(fn) {
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Route}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
proto.route = function(path){
|
||||
proto.route = function route(path) {
|
||||
var route = new Route(path);
|
||||
|
||||
var layer = new Layer(path, {
|
||||
@@ -502,6 +522,32 @@ function appendMethods(list, addition) {
|
||||
}
|
||||
}
|
||||
|
||||
// get pathname of request
|
||||
function getPathname(req) {
|
||||
try {
|
||||
return parseUrl(req).pathname;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Get get protocol + host for a URL
|
||||
function getProtohost(url) {
|
||||
if (typeof url !== 'string' || url.length === 0 || url[0] === '/') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
var searchIndex = url.indexOf('?')
|
||||
var pathLength = searchIndex !== -1
|
||||
? searchIndex
|
||||
: url.length
|
||||
var fqdnIndex = url.substr(0, pathLength).indexOf('://')
|
||||
|
||||
return fqdnIndex !== -1
|
||||
? url.substr(0, url.indexOf('/', 3 + fqdnIndex))
|
||||
: undefined
|
||||
}
|
||||
|
||||
// get type for error message
|
||||
function gettype(obj) {
|
||||
var type = typeof obj;
|
||||
@@ -515,6 +561,22 @@ function gettype(obj) {
|
||||
.replace(objectRegExp, '$1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Match path to a layer.
|
||||
*
|
||||
* @param {Layer} layer
|
||||
* @param {string} path
|
||||
* @private
|
||||
*/
|
||||
|
||||
function matchLayer(layer, path) {
|
||||
try {
|
||||
return layer.match(path);
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// merge params with parent params
|
||||
function mergeParams(params, parent) {
|
||||
if (typeof parent !== 'object' || !parent) {
|
||||
@@ -533,9 +595,12 @@ function mergeParams(params, parent) {
|
||||
var o = 0;
|
||||
|
||||
// determine numeric gaps
|
||||
while (i === o || o in parent) {
|
||||
if (i in params) i++;
|
||||
if (o in parent) o++;
|
||||
while (i in params) {
|
||||
i++;
|
||||
}
|
||||
|
||||
while (o in parent) {
|
||||
o++;
|
||||
}
|
||||
|
||||
// offset numeric indices in params before merge
|
||||
@@ -548,7 +613,7 @@ function mergeParams(params, parent) {
|
||||
}
|
||||
}
|
||||
|
||||
return mixin(parent, params);
|
||||
return mixin(obj, params);
|
||||
}
|
||||
|
||||
// restore obj props after function
|
||||
@@ -561,7 +626,7 @@ function restore(fn, obj) {
|
||||
vals[i] = obj[props[i]];
|
||||
}
|
||||
|
||||
return function(err){
|
||||
return function () {
|
||||
// restore vals
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
obj[props[i]] = vals[i];
|
||||
@@ -571,6 +636,17 @@ function restore(fn, obj) {
|
||||
};
|
||||
}
|
||||
|
||||
// send an OPTIONS response
|
||||
function sendOptionsResponse(res, options, next) {
|
||||
try {
|
||||
var body = options.join(',');
|
||||
res.set('Allow', body);
|
||||
res.send(body);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
// wrap a function
|
||||
function wrap(old, fn) {
|
||||
return function proxy() {
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var pathRegexp = require('path-to-regexp');
|
||||
@@ -7,12 +18,14 @@ var debug = require('debug')('express:router:layer');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Expose `Layer`.
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = Layer;
|
||||
@@ -22,18 +35,18 @@ function Layer(path, options, fn) {
|
||||
return new Layer(path, options, fn);
|
||||
}
|
||||
|
||||
debug('new %s', path);
|
||||
options = options || {};
|
||||
debug('new %o', path)
|
||||
var opts = options || {};
|
||||
|
||||
this.handle = fn;
|
||||
this.name = fn.name || '<anonymous>';
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
this.regexp = pathRegexp(path, this.keys = [], options);
|
||||
this.regexp = pathRegexp(path, this.keys = [], opts);
|
||||
|
||||
if (path === '/' && options.end === false) {
|
||||
this.regexp.fast_slash = true;
|
||||
}
|
||||
// set fast path flags
|
||||
this.regexp.fast_star = path === '*'
|
||||
this.regexp.fast_slash = path === '/' && opts.end === false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,23 +108,28 @@ Layer.prototype.handle_request = function handle(req, res, next) {
|
||||
*/
|
||||
|
||||
Layer.prototype.match = function match(path) {
|
||||
if (path == null) {
|
||||
// no path, nothing matches
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
return false;
|
||||
var match
|
||||
|
||||
if (path != null) {
|
||||
// fast path non-ending match for / (any path matches)
|
||||
if (this.regexp.fast_slash) {
|
||||
this.params = {}
|
||||
this.path = ''
|
||||
return true
|
||||
}
|
||||
|
||||
// fast path for * (everything matched in a param)
|
||||
if (this.regexp.fast_star) {
|
||||
this.params = {'0': decode_param(path)}
|
||||
this.path = path
|
||||
return true
|
||||
}
|
||||
|
||||
// match the path
|
||||
match = this.regexp.exec(path)
|
||||
}
|
||||
|
||||
if (this.regexp.fast_slash) {
|
||||
// fast path non-ending match for / (everything matches)
|
||||
this.params = {};
|
||||
this.path = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
var m = this.regexp.exec(path);
|
||||
|
||||
if (!m) {
|
||||
if (!match) {
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
return false;
|
||||
@@ -119,21 +137,15 @@ Layer.prototype.match = function match(path) {
|
||||
|
||||
// store values
|
||||
this.params = {};
|
||||
this.path = m[0];
|
||||
this.path = match[0]
|
||||
|
||||
var keys = this.keys;
|
||||
var params = this.params;
|
||||
var prop;
|
||||
var n = 0;
|
||||
var key;
|
||||
var val;
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
key = keys[i - 1];
|
||||
prop = key
|
||||
? key.name
|
||||
: n++;
|
||||
val = decode_param(m[i]);
|
||||
for (var i = 1; i < match.length; i++) {
|
||||
var key = keys[i - 1];
|
||||
var prop = key.name;
|
||||
var val = decode_param(match[i])
|
||||
|
||||
if (val !== undefined || !(hasOwnProperty.call(params, prop))) {
|
||||
params[prop] = val;
|
||||
@@ -148,19 +160,22 @@ Layer.prototype.match = function match(path) {
|
||||
*
|
||||
* @param {string} val
|
||||
* @return {string}
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
function decode_param(val){
|
||||
if (typeof val !== 'string') {
|
||||
function decode_param(val) {
|
||||
if (typeof val !== 'string' || val.length === 0) {
|
||||
return val;
|
||||
}
|
||||
|
||||
try {
|
||||
return decodeURIComponent(val);
|
||||
} catch (e) {
|
||||
var err = new TypeError("Failed to decode param '" + val + "'");
|
||||
err.status = 400;
|
||||
} catch (err) {
|
||||
if (err instanceof URIError) {
|
||||
err.message = 'Failed to decode param \'' + val + '\'';
|
||||
err.status = err.statusCode = 400;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,34 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var debug = require('debug')('express:router:route');
|
||||
var flatten = require('array-flatten');
|
||||
var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Expose `Route`.
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var slice = Array.prototype.slice;
|
||||
var toString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = Route;
|
||||
@@ -17,20 +37,22 @@ module.exports = Route;
|
||||
* Initialize `Route` with the given `path`,
|
||||
*
|
||||
* @param {String} path
|
||||
* @api private
|
||||
* @public
|
||||
*/
|
||||
|
||||
function Route(path) {
|
||||
debug('new %s', path);
|
||||
this.path = path;
|
||||
this.stack = [];
|
||||
|
||||
debug('new %o', path)
|
||||
|
||||
// route handlers for various http methods
|
||||
this.methods = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @api private
|
||||
* Determine if the route handles a given method.
|
||||
* @private
|
||||
*/
|
||||
|
||||
Route.prototype._handles_method = function _handles_method(method) {
|
||||
@@ -38,33 +60,42 @@ Route.prototype._handles_method = function _handles_method(method) {
|
||||
return true;
|
||||
}
|
||||
|
||||
method = method.toLowerCase();
|
||||
var name = method.toLowerCase();
|
||||
|
||||
if (method === 'head' && !this.methods['head']) {
|
||||
method = 'get';
|
||||
if (name === 'head' && !this.methods['head']) {
|
||||
name = 'get';
|
||||
}
|
||||
|
||||
return Boolean(this.methods[method]);
|
||||
return Boolean(this.methods[name]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {Array} supported HTTP methods
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
Route.prototype._options = function(){
|
||||
return Object.keys(this.methods).map(function(method) {
|
||||
return method.toUpperCase();
|
||||
});
|
||||
Route.prototype._options = function _options() {
|
||||
var methods = Object.keys(this.methods);
|
||||
|
||||
// append automatic head
|
||||
if (this.methods.get && !this.methods.head) {
|
||||
methods.push('head');
|
||||
}
|
||||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
// make upper case
|
||||
methods[i] = methods[i].toUpperCase();
|
||||
}
|
||||
|
||||
return methods;
|
||||
};
|
||||
|
||||
/**
|
||||
* dispatch req, res into this route
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
|
||||
Route.prototype.dispatch = function(req, res, done){
|
||||
Route.prototype.dispatch = function dispatch(req, res, done) {
|
||||
var idx = 0;
|
||||
var stack = this.stack;
|
||||
if (stack.length === 0) {
|
||||
@@ -81,10 +112,16 @@ Route.prototype.dispatch = function(req, res, done){
|
||||
next();
|
||||
|
||||
function next(err) {
|
||||
// signal to exit route
|
||||
if (err && err === 'route') {
|
||||
return done();
|
||||
}
|
||||
|
||||
// signal to exit router
|
||||
if (err && err === 'router') {
|
||||
return done(err)
|
||||
}
|
||||
|
||||
var layer = stack[idx++];
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
@@ -130,44 +167,50 @@ Route.prototype.dispatch = function(req, res, done){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Route.prototype.all = function(){
|
||||
var callbacks = utils.flatten([].slice.call(arguments));
|
||||
callbacks.forEach(function(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
var type = {}.toString.call(fn);
|
||||
Route.prototype.all = function all() {
|
||||
var handles = flatten(slice.call(arguments));
|
||||
|
||||
for (var i = 0; i < handles.length; i++) {
|
||||
var handle = handles[i];
|
||||
|
||||
if (typeof handle !== 'function') {
|
||||
var type = toString.call(handle);
|
||||
var msg = 'Route.all() requires callback functions but got a ' + type;
|
||||
throw new Error(msg);
|
||||
throw new TypeError(msg);
|
||||
}
|
||||
|
||||
var layer = Layer('/', {}, fn);
|
||||
var layer = Layer('/', {}, handle);
|
||||
layer.method = undefined;
|
||||
|
||||
this.methods._all = true;
|
||||
this.stack.push(layer);
|
||||
}, this);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
methods.forEach(function(method){
|
||||
Route.prototype[method] = function(){
|
||||
var callbacks = utils.flatten([].slice.call(arguments));
|
||||
var handles = flatten(slice.call(arguments));
|
||||
|
||||
callbacks.forEach(function(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
var type = {}.toString.call(fn);
|
||||
for (var i = 0; i < handles.length; i++) {
|
||||
var handle = handles[i];
|
||||
|
||||
if (typeof handle !== 'function') {
|
||||
var type = toString.call(handle);
|
||||
var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
debug('%s %s', method, this.path);
|
||||
debug('%s %o', method, this.path)
|
||||
|
||||
var layer = Layer('/', {}, fn);
|
||||
var layer = Layer('/', {}, handle);
|
||||
layer.method = method;
|
||||
|
||||
this.methods[method] = true;
|
||||
this.stack.push(layer);
|
||||
}, this);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
63
lib/utils.js
63
lib/utils.js
@@ -1,16 +1,26 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var contentDisposition = require('content-disposition');
|
||||
var contentType = require('content-type');
|
||||
var deprecate = require('depd')('express');
|
||||
var flatten = require('array-flatten');
|
||||
var mime = require('send').mime;
|
||||
var basename = require('path').basename;
|
||||
var etag = require('etag');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
var qs = require('qs');
|
||||
var querystring = require('querystring');
|
||||
var typer = require('media-typer');
|
||||
|
||||
/**
|
||||
* Return strong ETag for `body`.
|
||||
@@ -55,9 +65,9 @@ exports.wetag = function wetag(body, encoding){
|
||||
*/
|
||||
|
||||
exports.isAbsolute = function(path){
|
||||
if ('/' == path[0]) return true;
|
||||
if (':' == path[1] && '\\' == path[2]) return true;
|
||||
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
|
||||
if ('/' === path[0]) return true;
|
||||
if (':' === path[1] && ('\\' === path[2] || '/' === path[2])) return true; // Windows device path
|
||||
if ('\\\\' === path.substring(0, 2)) return true; // Microsoft Azure absolute path
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -68,18 +78,8 @@ exports.isAbsolute = function(path){
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.flatten = function(arr, ret){
|
||||
ret = ret || [];
|
||||
var len = arr.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (Array.isArray(arr[i])) {
|
||||
exports.flatten(arr[i], ret);
|
||||
} else {
|
||||
ret.push(arr[i]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
exports.flatten = deprecate.function(flatten,
|
||||
'utils.flatten: use array-flatten npm module instead');
|
||||
|
||||
/**
|
||||
* Normalize the given `type`, for example "html" becomes "text/html".
|
||||
@@ -141,7 +141,7 @@ function acceptParams(str, index) {
|
||||
|
||||
for (var i = 1; i < parts.length; ++i) {
|
||||
var pms = parts[i].split(/ *= */);
|
||||
if ('q' == pms[0]) {
|
||||
if ('q' === pms[0]) {
|
||||
ret.quality = parseFloat(pms[1]);
|
||||
} else {
|
||||
ret.params[pms[0]] = pms[1];
|
||||
@@ -208,7 +208,7 @@ exports.compileQueryParser = function compileQueryParser(val) {
|
||||
fn = newObject;
|
||||
break;
|
||||
case 'extended':
|
||||
fn = qs.parse;
|
||||
fn = parseExtendedQueryString;
|
||||
break;
|
||||
case 'simple':
|
||||
fn = querystring.parse;
|
||||
@@ -258,21 +258,36 @@ exports.compileTrust = function(val) {
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.setCharset = function(type, charset){
|
||||
if (!type || !charset) return type;
|
||||
exports.setCharset = function setCharset(type, charset) {
|
||||
if (!type || !charset) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// parse type
|
||||
var parsed = typer.parse(type);
|
||||
var parsed = contentType.parse(type);
|
||||
|
||||
// set charset
|
||||
parsed.parameters.charset = charset;
|
||||
|
||||
// format type
|
||||
return typer.format(parsed);
|
||||
return contentType.format(parsed);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return new empty objet.
|
||||
* Parse an extended query string with qs.
|
||||
*
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function parseExtendedQueryString(str) {
|
||||
return qs.parse(str, {
|
||||
allowPrototypes: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return new empty object.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api private
|
||||
|
||||
86
lib/view.js
86
lib/view.js
@@ -1,11 +1,21 @@
|
||||
/*!
|
||||
* express
|
||||
* Copyright(c) 2009-2013 TJ Holowaychuk
|
||||
* Copyright(c) 2013 Roman Shtylman
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var debug = require('debug')('express:view');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
@@ -19,7 +29,8 @@ var join = path.join;
|
||||
var resolve = path.resolve;
|
||||
|
||||
/**
|
||||
* Expose `View`.
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = View;
|
||||
@@ -33,30 +44,53 @@ module.exports = View;
|
||||
* - `engines` template engine require() cache
|
||||
* - `root` root path for view lookup
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
* @param {string} name
|
||||
* @param {object} options
|
||||
* @public
|
||||
*/
|
||||
|
||||
function View(name, options) {
|
||||
options = options || {};
|
||||
var opts = options || {};
|
||||
|
||||
this.defaultEngine = opts.defaultEngine;
|
||||
this.ext = extname(name);
|
||||
this.name = name;
|
||||
this.root = options.root;
|
||||
var engines = options.engines;
|
||||
this.defaultEngine = options.defaultEngine;
|
||||
var ext = this.ext = extname(name);
|
||||
if (!ext && !this.defaultEngine) throw new Error('No default engine was specified and no extension was provided.');
|
||||
if (!ext) name += (ext = this.ext = ('.' != this.defaultEngine[0] ? '.' : '') + this.defaultEngine);
|
||||
this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express);
|
||||
this.path = this.lookup(name);
|
||||
this.root = opts.root;
|
||||
|
||||
if (!this.ext && !this.defaultEngine) {
|
||||
throw new Error('No default engine was specified and no extension was provided.');
|
||||
}
|
||||
|
||||
var fileName = name;
|
||||
|
||||
if (!this.ext) {
|
||||
// get extension from default engine name
|
||||
this.ext = this.defaultEngine[0] !== '.'
|
||||
? '.' + this.defaultEngine
|
||||
: this.defaultEngine;
|
||||
|
||||
fileName += this.ext;
|
||||
}
|
||||
|
||||
if (!opts.engines[this.ext]) {
|
||||
// load engine
|
||||
var mod = this.ext.substr(1)
|
||||
debug('require "%s"', mod)
|
||||
opts.engines[this.ext] = require(mod).__express
|
||||
}
|
||||
|
||||
// store loaded engine
|
||||
this.engine = opts.engines[this.ext];
|
||||
|
||||
// lookup path
|
||||
this.path = this.lookup(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup view by the given `name`
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {String}
|
||||
* @api private
|
||||
* @param {string} name
|
||||
* @private
|
||||
*/
|
||||
|
||||
View.prototype.lookup = function lookup(name) {
|
||||
@@ -81,16 +115,16 @@ View.prototype.lookup = function lookup(name) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Render with the given `options` and callback `fn(err, str)`.
|
||||
* Render with the given options.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Function} fn
|
||||
* @api private
|
||||
* @param {object} options
|
||||
* @param {function} callback
|
||||
* @private
|
||||
*/
|
||||
|
||||
View.prototype.render = function render(options, fn) {
|
||||
View.prototype.render = function render(options, callback) {
|
||||
debug('render "%s"', this.path);
|
||||
this.engine(this.path, options, fn);
|
||||
this.engine(this.path, options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -103,12 +137,10 @@ View.prototype.render = function render(options, fn) {
|
||||
|
||||
View.prototype.resolve = function resolve(dir, file) {
|
||||
var ext = this.ext;
|
||||
var path;
|
||||
var stat;
|
||||
|
||||
// <path>.<ext>
|
||||
path = join(dir, file);
|
||||
stat = tryStat(path);
|
||||
var path = join(dir, file);
|
||||
var stat = tryStat(path);
|
||||
|
||||
if (stat && stat.isFile()) {
|
||||
return path;
|
||||
|
||||
97
package.json
97
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Fast, unopinionated, minimalist web framework",
|
||||
"version": "4.10.7",
|
||||
"version": "4.15.5",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
|
||||
@@ -13,7 +13,7 @@
|
||||
"Young Jae Sim <hanul@hanul.me>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": "strongloop/express",
|
||||
"repository": "expressjs/express",
|
||||
"homepage": "http://expressjs.com/",
|
||||
"keywords": [
|
||||
"express",
|
||||
@@ -27,49 +27,55 @@
|
||||
"api"
|
||||
],
|
||||
"dependencies": {
|
||||
"accepts": "~1.1.4",
|
||||
"content-disposition": "0.5.0",
|
||||
"cookie-signature": "1.0.5",
|
||||
"debug": "~2.1.1",
|
||||
"depd": "~1.0.0",
|
||||
"escape-html": "1.0.1",
|
||||
"etag": "~1.5.1",
|
||||
"finalhandler": "0.3.3",
|
||||
"fresh": "0.2.4",
|
||||
"media-typer": "0.3.0",
|
||||
"methods": "1.1.1",
|
||||
"on-finished": "~2.2.0",
|
||||
"parseurl": "~1.3.0",
|
||||
"path-to-regexp": "0.1.3",
|
||||
"proxy-addr": "~1.0.4",
|
||||
"qs": "2.3.3",
|
||||
"range-parser": "~1.0.2",
|
||||
"send": "0.10.1",
|
||||
"serve-static": "~1.7.2",
|
||||
"type-is": "~1.5.5",
|
||||
"vary": "~1.0.0",
|
||||
"cookie": "0.1.2",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"utils-merge": "1.0.0"
|
||||
"accepts": "~1.3.3",
|
||||
"array-flatten": "1.1.1",
|
||||
"content-disposition": "0.5.2",
|
||||
"content-type": "~1.0.2",
|
||||
"cookie": "0.3.1",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~1.1.1",
|
||||
"encodeurl": "~1.0.1",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.0",
|
||||
"finalhandler": "~1.0.6",
|
||||
"fresh": "0.5.2",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "~2.3.0",
|
||||
"parseurl": "~1.3.1",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~1.1.5",
|
||||
"qs": "6.5.0",
|
||||
"range-parser": "~1.2.0",
|
||||
"send": "0.15.6",
|
||||
"serve-static": "1.12.6",
|
||||
"setprototypeof": "1.0.3",
|
||||
"statuses": "~1.3.1",
|
||||
"type-is": "~1.6.15",
|
||||
"utils-merge": "1.0.0",
|
||||
"vary": "~1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.1",
|
||||
"istanbul": "0.3.5",
|
||||
"mocha": "~2.0.0",
|
||||
"should": "~4.3.1",
|
||||
"supertest": "~0.15.0",
|
||||
"ejs": "~1.0.0",
|
||||
"marked": "0.3.2",
|
||||
"hjs": "~0.0.6",
|
||||
"body-parser": "~1.9.3",
|
||||
"connect-redis": "~2.1.0",
|
||||
"cookie-parser": "~1.3.3",
|
||||
"express-session": "~1.9.2",
|
||||
"jade": "~1.7.0",
|
||||
"method-override": "~2.3.0",
|
||||
"morgan": "~1.5.0",
|
||||
"multiparty": "~4.0.0",
|
||||
"vhost": "~3.0.0"
|
||||
"after": "0.8.2",
|
||||
"body-parser": "1.18.1",
|
||||
"cookie-parser": "~1.4.3",
|
||||
"cookie-session": "1.3.1",
|
||||
"ejs": "2.5.7",
|
||||
"eslint": "2.13.1",
|
||||
"express-session": "1.15.5",
|
||||
"hbs": "4.0.1",
|
||||
"istanbul": "0.4.5",
|
||||
"marked": "0.3.6",
|
||||
"method-override": "2.3.9",
|
||||
"mocha": "3.5.3",
|
||||
"morgan": "1.8.2",
|
||||
"multiparty": "4.1.3",
|
||||
"pbkdf2-password": "1.2.1",
|
||||
"should": "13.1.0",
|
||||
"supertest": "1.2.0",
|
||||
"connect-redis": "~2.4.1",
|
||||
"vhost": "~3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
@@ -82,9 +88,10 @@
|
||||
"lib/"
|
||||
],
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
|
||||
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
|
||||
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
|
||||
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/",
|
||||
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/"
|
||||
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,13 @@ var should = require('should');
|
||||
var express = require('../')
|
||||
, Route = express.Route
|
||||
, methods = require('methods')
|
||||
, assert = require('assert');
|
||||
|
||||
describe('Route', function(){
|
||||
it('should work without handlers', function(done) {
|
||||
var req = { method: 'GET', url: '/' }
|
||||
var route = new Route('/foo')
|
||||
route.dispatch(req, {}, done)
|
||||
})
|
||||
|
||||
describe('.all', function(){
|
||||
it('should add handler', function(done){
|
||||
@@ -20,7 +24,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.ok;
|
||||
should(req.called).be.ok()
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -79,7 +83,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.ok;
|
||||
should(req.called).be.ok()
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -99,7 +103,7 @@ describe('Route', function(){
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.true;
|
||||
should(req.called).be.true()
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -151,7 +155,7 @@ describe('Route', function(){
|
||||
});
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
should(err).be.ok;
|
||||
should(err).be.ok()
|
||||
should(err.message).equal('foobar');
|
||||
req.order.should.equal('a');
|
||||
done();
|
||||
@@ -177,7 +181,7 @@ describe('Route', function(){
|
||||
});
|
||||
|
||||
route.dispatch(req, {}, function (err) {
|
||||
should(err).be.ok;
|
||||
should(err).be.ok()
|
||||
should(err.message).equal('foobar');
|
||||
req.order.should.equal('a');
|
||||
done();
|
||||
@@ -217,7 +221,7 @@ describe('Route', function(){
|
||||
});
|
||||
|
||||
route.dispatch(req, {}, function(err){
|
||||
should(err).be.ok;
|
||||
should(err).be.ok()
|
||||
err.message.should.equal('boom!');
|
||||
done();
|
||||
});
|
||||
@@ -229,7 +233,7 @@ describe('Route', function(){
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
// this should not execute
|
||||
true.should.be.false;
|
||||
true.should.be.false()
|
||||
});
|
||||
|
||||
route.dispatch(req, {}, done);
|
||||
|
||||
@@ -47,12 +47,37 @@ describe('Router', function(){
|
||||
var router = new Router();
|
||||
|
||||
router.use(function (req, res) {
|
||||
false.should.be.true;
|
||||
false.should.be.true()
|
||||
});
|
||||
|
||||
router.handle({ url: '', method: 'GET' }, {}, done);
|
||||
});
|
||||
|
||||
it('should handle missing URL', function (done) {
|
||||
var router = new Router()
|
||||
|
||||
router.use(function (req, res) {
|
||||
throw new Error('should not be called')
|
||||
})
|
||||
|
||||
router.handle({ method: 'GET' }, {}, done)
|
||||
})
|
||||
|
||||
it('should not stack overflow with many registered routes', function(done){
|
||||
var handler = function(req, res){ res.end(new Error('wrong handler')) };
|
||||
var router = new Router();
|
||||
|
||||
for (var i = 0; i < 6000; i++) {
|
||||
router.get('/thing' + i, handler)
|
||||
}
|
||||
|
||||
router.get('/', function (req, res) {
|
||||
res.end();
|
||||
});
|
||||
|
||||
router.handle({ url: '/', method: 'GET' }, { end: done });
|
||||
});
|
||||
|
||||
describe('.handle', function(){
|
||||
it('should dispatch', function(done){
|
||||
var router = new Router();
|
||||
@@ -322,6 +347,24 @@ describe('Router', function(){
|
||||
assert.equal(count, methods.length);
|
||||
done();
|
||||
})
|
||||
|
||||
it('should be called for any URL when "*"', function (done) {
|
||||
var cb = after(4, done)
|
||||
var router = new Router()
|
||||
|
||||
function no () {
|
||||
throw new Error('should not be called')
|
||||
}
|
||||
|
||||
router.all('*', function (req, res) {
|
||||
res.end()
|
||||
})
|
||||
|
||||
router.handle({ url: '/', method: 'GET' }, { end: cb }, no)
|
||||
router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no)
|
||||
router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no)
|
||||
router.handle({ url: '*', method: 'GET' }, { end: cb }, no)
|
||||
})
|
||||
})
|
||||
|
||||
describe('.use', function() {
|
||||
@@ -338,6 +381,24 @@ describe('Router', function(){
|
||||
router.use.bind(router, '/', new Date()).should.throw(/requires middleware function.*Date/)
|
||||
})
|
||||
|
||||
it('should be called for any URL', function (done) {
|
||||
var cb = after(4, done)
|
||||
var router = new Router()
|
||||
|
||||
function no () {
|
||||
throw new Error('should not be called')
|
||||
}
|
||||
|
||||
router.use(function (req, res) {
|
||||
res.end()
|
||||
})
|
||||
|
||||
router.handle({ url: '/', method: 'GET' }, { end: cb }, no)
|
||||
router.handle({ url: '/foo', method: 'GET' }, { end: cb }, no)
|
||||
router.handle({ url: 'foo', method: 'GET' }, { end: cb }, no)
|
||||
router.handle({ url: '*', method: 'GET' }, { end: cb }, no)
|
||||
})
|
||||
|
||||
it('should accept array of middleware', function(done){
|
||||
var count = 0;
|
||||
var router = new Router();
|
||||
|
||||
38
test/acceptance/cookie-sessions.js
Normal file
38
test/acceptance/cookie-sessions.js
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
var app = require('../../examples/cookie-sessions')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('cookie-sessions', function () {
|
||||
describe('GET /', function () {
|
||||
it('should display no views', function (done) {
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'viewed 1 times\n', done)
|
||||
})
|
||||
|
||||
it('should set a session cookie', function (done) {
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('Set-Cookie', /express:sess=/)
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should display 1 view on revisit', function (done) {
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'viewed 1 times\n', function (err, res) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Cookie', getCookies(res))
|
||||
.expect(200, 'viewed 2 times\n', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function getCookies(res) {
|
||||
return res.headers['set-cookie'].map(function (val) {
|
||||
return val.split(';')[0]
|
||||
}).join('; ');
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user