mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
666 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ff5e96c88b | ||
|
|
d842647217 | ||
|
|
12626aed35 | ||
|
|
3387916efc | ||
|
|
f76403b717 | ||
|
|
2d6faf6da7 | ||
|
|
a7227a0964 | ||
|
|
6e9cfa2aea | ||
|
|
571ce16405 | ||
|
|
98585d1d0a | ||
|
|
b78bd3d1fd | ||
|
|
4aa2801054 | ||
|
|
4405b849a9 | ||
|
|
5d74a553d6 | ||
|
|
262b60537f | ||
|
|
e77e644224 | ||
|
|
ce89c00cd9 | ||
|
|
4d8093302f | ||
|
|
4370908674 | ||
|
|
f0b679d02d | ||
|
|
d5ad34b0e9 | ||
|
|
c24463d829 | ||
|
|
ebfa00a9c0 | ||
|
|
d23417e6e8 | ||
|
|
91824514ce | ||
|
|
656e214937 | ||
|
|
4b26bbde2d | ||
|
|
7fcc8b190d | ||
|
|
b326ae89df | ||
|
|
869ddd775c | ||
|
|
997fd74e8c | ||
|
|
e3e41a1118 | ||
|
|
6c8bcd5c4e | ||
|
|
95e63ec287 | ||
|
|
ebca5887cc | ||
|
|
8535d3a990 | ||
|
|
13184c4379 | ||
|
|
eaba4eeb70 | ||
|
|
ac56cf4606 | ||
|
|
6dea32cd18 | ||
|
|
5fab60bc6c | ||
|
|
881e1ba660 | ||
|
|
0e488c19df | ||
|
|
2262a18900 | ||
|
|
28c6952d1c | ||
|
|
01e3530a31 | ||
|
|
77c83d0c57 | ||
|
|
b4eaf89186 | ||
|
|
e4debea297 | ||
|
|
1c96e18d20 | ||
|
|
8bb013ec95 | ||
|
|
6c0031bfd8 | ||
|
|
ab6c189504 | ||
|
|
a12ae729bd | ||
|
|
d53a0cd91e | ||
|
|
eabd4564aa | ||
|
|
d40dc651f3 | ||
|
|
68290ee87a | ||
|
|
6614352563 | ||
|
|
0e5f2f84ea | ||
|
|
b1d0c19ca1 | ||
|
|
dfa7ee4732 | ||
|
|
e9539fc780 | ||
|
|
5f7a37ee51 | ||
|
|
ff3a368b2f | ||
|
|
ccc45a74f8 | ||
|
|
cd9d2ec6a9 | ||
|
|
ce7bbae007 | ||
|
|
6a5dd52deb | ||
|
|
6c2f7fb48d | ||
|
|
4dd970578a | ||
|
|
88dfd36eaa | ||
|
|
5759b3e9f5 | ||
|
|
c939a771c0 | ||
|
|
af1043844f | ||
|
|
dd763ec5b8 | ||
|
|
9c2c21aaaf | ||
|
|
366000184f | ||
|
|
4d1ee23f84 | ||
|
|
6f31218ecc | ||
|
|
6cd4859035 | ||
|
|
4f1cd4f73c | ||
|
|
f15bba7309 | ||
|
|
6f0302fb78 | ||
|
|
0e5613363f | ||
|
|
7a7f18c20b | ||
|
|
b766aad112 | ||
|
|
7488e27609 | ||
|
|
bc9d854763 | ||
|
|
2e20a85810 | ||
|
|
a706408208 | ||
|
|
6f91416020 | ||
|
|
2c5ed88c90 | ||
|
|
6d39d0f8a8 | ||
|
|
159ea67713 | ||
|
|
33959ed350 | ||
|
|
be478d348c | ||
|
|
b0e4e641f9 | ||
|
|
94f10c26cb | ||
|
|
efd2dfb8c8 | ||
|
|
3f2454e3df | ||
|
|
61da3c7d0e | ||
|
|
2c140961ab | ||
|
|
6aa4a450ed | ||
|
|
9f292d873e | ||
|
|
ef3e95ca73 | ||
|
|
f45bd632df | ||
|
|
cc18da5cdf | ||
|
|
5603f86edd | ||
|
|
daadf6033b | ||
|
|
590c919204 | ||
|
|
3bcba79e2d | ||
|
|
b8c8ecebb7 | ||
|
|
43e2cd79cb | ||
|
|
653270bb43 | ||
|
|
734bdf5ca1 | ||
|
|
341c1919d9 | ||
|
|
8e46af1b1d | ||
|
|
e4fc09423e | ||
|
|
0300b61fdd | ||
|
|
b09afad7b1 | ||
|
|
0e0b259556 | ||
|
|
bc38d896ea | ||
|
|
b2518fe135 | ||
|
|
63286e1192 | ||
|
|
c00f2f8596 | ||
|
|
91891e3aee | ||
|
|
728917164c | ||
|
|
bf1980f1b4 | ||
|
|
3c1a964362 | ||
|
|
947fb8b274 | ||
|
|
c5193536e5 | ||
|
|
d08fd64190 | ||
|
|
2470ae6c72 | ||
|
|
916a75cf19 | ||
|
|
daacb749c1 | ||
|
|
f29399c4e1 | ||
|
|
f6ac068ab0 | ||
|
|
7eb65eeca2 | ||
|
|
178fe15091 | ||
|
|
381f278d0a | ||
|
|
534fa181c6 | ||
|
|
80847d8c82 | ||
|
|
bb8abf1f90 | ||
|
|
cf41a8f254 | ||
|
|
1716e3b067 | ||
|
|
12f92a50dc | ||
|
|
51d33edb79 | ||
|
|
2a0c35a108 | ||
|
|
d4de82b853 | ||
|
|
e2102263ce | ||
|
|
d2b1a89b4a | ||
|
|
d9937c628a | ||
|
|
276db8c49a | ||
|
|
1768d94a1a | ||
|
|
6bc7574ab5 | ||
|
|
3dca534995 | ||
|
|
4b1b8e420f | ||
|
|
70767b19ac | ||
|
|
7d277c1c15 | ||
|
|
fa1fcd9fec | ||
|
|
2de6514b4b | ||
|
|
d07c06363f | ||
|
|
4e97533fd2 | ||
|
|
d7d6219a1e | ||
|
|
9b18461bbc | ||
|
|
b77aa38c98 | ||
|
|
cbb251377e | ||
|
|
d6ed469de3 | ||
|
|
49284c236b | ||
|
|
be18487f7d | ||
|
|
094ff11949 | ||
|
|
d2d0afff64 | ||
|
|
33bb8fc4b6 | ||
|
|
b97f6eb506 | ||
|
|
621d074bd8 | ||
|
|
d7e7b2e7d7 | ||
|
|
2d6b735b4f | ||
|
|
a3115882d4 | ||
|
|
3d188fe13e | ||
|
|
8327708ec2 | ||
|
|
c8640b3465 | ||
|
|
3ce5f9b493 | ||
|
|
46f0bfc65f | ||
|
|
0b49e7f1fd | ||
|
|
f6f47f428c | ||
|
|
20635d03fc | ||
|
|
4d032cda05 | ||
|
|
4127ba10b0 | ||
|
|
b6ae091bdf | ||
|
|
a206b4e273 | ||
|
|
2b2733c235 | ||
|
|
7fb7bcc0f7 | ||
|
|
0299bee8fa | ||
|
|
6a581c9961 | ||
|
|
4986b1cb4c | ||
|
|
3de4e4276d | ||
|
|
27f195374d | ||
|
|
ff0de5eb27 | ||
|
|
f4ddef1570 | ||
|
|
9eafaa23d8 | ||
|
|
0b12cc0cac | ||
|
|
fdd0ccabe8 | ||
|
|
8c36eab679 | ||
|
|
5c145b5490 | ||
|
|
d7bef52591 | ||
|
|
1576a95e87 | ||
|
|
7f92fe66e0 | ||
|
|
0cf02d4667 | ||
|
|
ef52b80d75 | ||
|
|
1ca01c0c47 | ||
|
|
fbceae2716 | ||
|
|
ad3ca25c58 | ||
|
|
666ffc62d8 | ||
|
|
6680132392 | ||
|
|
f13f4652da | ||
|
|
059c068c7b | ||
|
|
49947f1476 | ||
|
|
0dddd772c0 | ||
|
|
0f87c6f392 | ||
|
|
1643ae442c | ||
|
|
2594f3103b | ||
|
|
8473b3c338 | ||
|
|
59cb99e9be | ||
|
|
7119f2b16d | ||
|
|
a57efea173 | ||
|
|
4a4ca7347a | ||
|
|
570f60d36e | ||
|
|
d13e613584 | ||
|
|
9204e1f42a | ||
|
|
22ca953e96 | ||
|
|
7989c883fe | ||
|
|
e05a52078a | ||
|
|
ddac571fdf | ||
|
|
982d24b475 | ||
|
|
552b441f8a | ||
|
|
e8f8ea7e05 | ||
|
|
4f5b27dd81 | ||
|
|
cca88a7c47 | ||
|
|
ea427c1bb4 | ||
|
|
0bd6c311cf | ||
|
|
7f606ebf29 | ||
|
|
a3b5adcf4a | ||
|
|
1150ca7264 | ||
|
|
4aea02310a | ||
|
|
17cea29013 | ||
|
|
8449f23f0d | ||
|
|
2cb029f896 | ||
|
|
7e32fa1be6 | ||
|
|
1168d0bb8b | ||
|
|
7d0f1c3db9 | ||
|
|
19abf7684b | ||
|
|
c652cf7eed | ||
|
|
19fd6f85b0 | ||
|
|
b6c5b0511f | ||
|
|
0e42a37edd | ||
|
|
b24ed15878 | ||
|
|
12e070e39a | ||
|
|
b886eb52cf | ||
|
|
d8237b976b | ||
|
|
15590d75b2 | ||
|
|
e8b471ff4f | ||
|
|
767db01b79 | ||
|
|
df413a41f3 | ||
|
|
52775a52ad | ||
|
|
112bc92d78 | ||
|
|
d8df26680f | ||
|
|
1854a5d35f | ||
|
|
54d3ffa9a0 | ||
|
|
0ee4dd82b5 | ||
|
|
454c4b2350 | ||
|
|
696e150f0a | ||
|
|
819265c7ae | ||
|
|
baf8b14a71 | ||
|
|
06e7685d65 | ||
|
|
8858f20d93 | ||
|
|
65f67e2ec0 | ||
|
|
8e63521f68 | ||
|
|
9f4968aaa3 | ||
|
|
afea3c0ae8 | ||
|
|
edaabe66cf | ||
|
|
f724730e1a | ||
|
|
e49d0dc9e3 | ||
|
|
e7a3fbaf48 | ||
|
|
1a9a837c45 | ||
|
|
ab8d116f42 | ||
|
|
d0b6b3dfcf | ||
|
|
f34944c539 | ||
|
|
11c74d72eb | ||
|
|
035685918c | ||
|
|
88cffadcaa | ||
|
|
3b7ca43170 | ||
|
|
7cd86a01da | ||
|
|
dc054d190a | ||
|
|
9019424725 | ||
|
|
928952e7f0 | ||
|
|
a28b7a85cf | ||
|
|
3fc8dc54ee | ||
|
|
0d77305a1a | ||
|
|
323c185079 | ||
|
|
1d0da9036b | ||
|
|
683ba1cd75 | ||
|
|
e4ff5281c9 | ||
|
|
7414a1f463 | ||
|
|
fd3b40533b | ||
|
|
21bb2ef30e | ||
|
|
da4c639954 | ||
|
|
d046208ca2 | ||
|
|
04f383087f | ||
|
|
fd86ab8da2 | ||
|
|
e29fa25bb4 | ||
|
|
b43205ca98 | ||
|
|
112dbb2ab4 | ||
|
|
3e32721e24 | ||
|
|
8ba3f39b33 | ||
|
|
82bdbad5e0 | ||
|
|
d29cf4d5e3 | ||
|
|
1623936a25 | ||
|
|
fa6a40526a | ||
|
|
c6e6203020 | ||
|
|
997a558a73 | ||
|
|
a01326adac | ||
|
|
76e8bfa1dc | ||
|
|
8dc67af606 | ||
|
|
996d319263 | ||
|
|
1c3bd36be6 | ||
|
|
4ea6f21b02 | ||
|
|
916c53737d | ||
|
|
ec5b9f5c61 | ||
|
|
b2382a7336 | ||
|
|
f684a64df7 | ||
|
|
5d03d0eac8 | ||
|
|
544c6665f5 | ||
|
|
cf8005e63f | ||
|
|
25ef8425d2 | ||
|
|
577cc1d1a0 | ||
|
|
3c87a6aede | ||
|
|
7c1f90bf16 | ||
|
|
7bcf5f5085 | ||
|
|
4fe1073f10 | ||
|
|
968b00c3d7 | ||
|
|
ef497fdae4 | ||
|
|
bcdeee2df5 | ||
|
|
23a49ff61e | ||
|
|
b4b2efee0f |
38
.gitignore
vendored
38
.gitignore
vendored
@@ -1,16 +1,26 @@
|
||||
coverage/
|
||||
.DS_Store
|
||||
*.seed
|
||||
# OS X
|
||||
.DS_Store*
|
||||
Icon?
|
||||
._*
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Linux
|
||||
.directory
|
||||
*~
|
||||
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
*.log
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.swp
|
||||
*.swo
|
||||
*.gz
|
||||
|
||||
|
||||
# Coveralls
|
||||
coverage
|
||||
|
||||
# Benchmarking
|
||||
benchmarks/graphs
|
||||
testing
|
||||
node_modules/
|
||||
testing
|
||||
test.js
|
||||
.idea
|
||||
|
||||
11
.npmignore
11
.npmignore
@@ -1,11 +0,0 @@
|
||||
.git*
|
||||
benchmarks/
|
||||
coverage/
|
||||
docs/
|
||||
examples/
|
||||
support/
|
||||
test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
.travis.yml
|
||||
Contributing.md
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,10 +1,23 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
matrix:
|
||||
allow_failures:
|
||||
- node_js: "0.11"
|
||||
fast_finish: true
|
||||
script: "npm run-script test-travis"
|
||||
- "0.12"
|
||||
- "1.8"
|
||||
- "2.5"
|
||||
- "3.3"
|
||||
- "4.7"
|
||||
- "5.12"
|
||||
- "6.9"
|
||||
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-script test-ci"
|
||||
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
|
||||
|
||||
36
Collaborator-Guide.md
Normal file
36
Collaborator-Guide.md
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
## Website Issues
|
||||
|
||||
Open issues for the expressjs.com website in https://github.com/expressjs/expressjs.com.
|
||||
|
||||
## PRs and Code contributions
|
||||
|
||||
* Tests must pass.
|
||||
* Follow existing coding style.
|
||||
* 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
|
||||
|
||||
* [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.
|
||||
* Create your own [fork](https://github.com/expressjs/express) on github, then checkout your fork.
|
||||
* 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.
|
||||
* To run the test suite, first install the dependencies by running `npm install`, then run `npm test`.
|
||||
* 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.
|
||||
105
Contributing.md
105
Contributing.md
@@ -1,25 +1,92 @@
|
||||
# Node.js Community Contributing Guide 1.0
|
||||
|
||||
## Website Issues
|
||||
This document describes a very simple process suitable for most projects
|
||||
in the Node.js ecosystem. Projects are encouraged to adopt this whether they
|
||||
are hosted in the Node.js Foundation or not.
|
||||
|
||||
Issues for the expressjs.com website go here https://github.com/visionmedia/expressjs.com
|
||||
The goal of this document is to create a contribution process that:
|
||||
|
||||
## PRs and Code contributions
|
||||
* Encourages new contributions.
|
||||
* Encourages contributors to remain involved.
|
||||
* Avoids unnecessary processes and bureaucracy whenever possible.
|
||||
* Creates a transparent decision making process which makes it clear how
|
||||
contributors can be involved in decision making.
|
||||
|
||||
* Tests must pass.
|
||||
* Follow existing coding style.
|
||||
* If you fix a bug, add a test.
|
||||
This document is based on much prior art in the Node.js community, io.js,
|
||||
and the Node.js project.
|
||||
|
||||
## Vocabulary
|
||||
|
||||
* 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,
|
||||
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.
|
||||
|
||||
|
||||
1436
History.md
1436
History.md
File diff suppressed because it is too large
Load Diff
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
|
||||
|
||||
115
Readme.md
115
Readme.md
@@ -1,11 +1,12 @@
|
||||
[](https://expressjs.com/)
|
||||
[](http://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](https://nodejs.org).
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
|
||||
[](https://badge.fury.io/js/express)
|
||||
[](https://travis-ci.org/visionmedia/express)
|
||||
[](https://coveralls.io/r/visionmedia/express)
|
||||
[](https://www.gittip.com/visionmedia/)
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
[![Linux Build][travis-image]][travis-url]
|
||||
[![Windows Build][appveyor-image]][appveyor-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
```js
|
||||
var express = require('express')
|
||||
@@ -18,14 +19,38 @@ app.get('/', function (req, res) {
|
||||
app.listen(3000)
|
||||
```
|
||||
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/visionmedia/express/wiki/New-features-in-4.x).
|
||||
|
||||
### Installation
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install express
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
* Focus on high performance
|
||||
* Super-high test coverage
|
||||
* HTTP helpers (redirection, caching, etc)
|
||||
* View system supporting 14+ template engines
|
||||
* Content negotiation
|
||||
* Executable for generating applications quickly
|
||||
|
||||
## Docs & Community
|
||||
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/strongloop/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/expressjs/express/wiki)
|
||||
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
||||
* [Gitter](https://gitter.im/expressjs/express) for support and discussion
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
|
||||
**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
|
||||
|
||||
The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:
|
||||
@@ -54,16 +79,6 @@ $ npm install
|
||||
$ npm start
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
* HTTP helpers (redirection, caching, etc)
|
||||
* View system supporting 14+ template engines
|
||||
* Content negotiation
|
||||
* Focus on high performance
|
||||
* Executable for generating applications quickly
|
||||
* High test coverage
|
||||
|
||||
## Philosophy
|
||||
|
||||
The Express philosophy is to provide small, robust tooling for HTTP servers, making
|
||||
@@ -71,57 +86,57 @@ $ npm start
|
||||
HTTP APIs.
|
||||
|
||||
Express does not force you to use any specific ORM or template engine. With support for over
|
||||
14 template engines via [Consolidate.js](https://github.com/visionmedia/consolidate.js),
|
||||
14 template engines via [Consolidate.js](https://github.com/tj/consolidate.js),
|
||||
you can quickly craft your perfect framework.
|
||||
|
||||
## More Information
|
||||
## Examples
|
||||
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/visionmedia/expressjs.com)]
|
||||
* [Github Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
|
||||
* Visit the [Wiki](https://github.com/visionmedia/express/wiki)
|
||||
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
* Run express examples [online](https://runnable.com/express)
|
||||
|
||||
## Viewing Examples
|
||||
|
||||
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
|
||||
To view the examples, clone the Express repo and install the dependencies:
|
||||
|
||||
```bash
|
||||
$ git clone git://github.com/visionmedia/express.git --depth 1
|
||||
$ git clone git://github.com/expressjs/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Then run whichever example you want:
|
||||
|
||||
$ node examples/content-negotiation
|
||||
```bash
|
||||
$ node examples/content-negotiation
|
||||
```
|
||||
|
||||
You can also view live examples here:
|
||||
## Tests
|
||||
|
||||
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
|
||||
|
||||
## Running Tests
|
||||
|
||||
To run the test suite, first invoke the following command within the repo, installing the development dependencies:
|
||||
To run the test suite, first install the dependencies, then run `npm test`:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Then run the tests:
|
||||
|
||||
```bash
|
||||
$ npm test
|
||||
```
|
||||
|
||||
### Contributors
|
||||
## People
|
||||
|
||||
* Author: [TJ Holowaychuk](https://github.com/visionmedia)
|
||||
* Lead Maintainer: [Douglas Christopher Wilson](https://github.com/dougwilson)
|
||||
* [All Contributors](https://github.com/visionmedia/express/graphs/contributors)
|
||||
The original author of Express is [TJ Holowaychuk](https://github.com/tj) [![TJ's Gratipay][gratipay-image-visionmedia]][gratipay-url-visionmedia]
|
||||
|
||||
### License
|
||||
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson) [![Doug's Gratipay][gratipay-image-dougwilson]][gratipay-url-dougwilson]
|
||||
|
||||
[List of all contributors](https://github.com/expressjs/express/graphs/contributors)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[npm-image]: https://img.shields.io/npm/v/express.svg
|
||||
[npm-url]: https://npmjs.org/package/express
|
||||
[downloads-image]: https://img.shields.io/npm/dm/express.svg
|
||||
[downloads-url]: https://npmjs.org/package/express
|
||||
[travis-image]: https://img.shields.io/travis/expressjs/express/master.svg?label=linux
|
||||
[travis-url]: https://travis-ci.org/expressjs/express
|
||||
[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
|
||||
[gratipay-url-dougwilson]: https://gratipay.com/dougwilson/
|
||||
|
||||
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.
|
||||
24
appveyor.yml
Normal file
24
appveyor.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
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.7"
|
||||
- nodejs_version: "5.12"
|
||||
- nodejs_version: "6.9"
|
||||
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
|
||||
version: "{build}"
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
// check out https://github.com/visionmedia/node-pwd
|
||||
// check out https://github.com/tj/node-pwd
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
|
||||
@@ -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
|
||||
@@ -2,6 +2,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var cookieSession = require('cookie-session');
|
||||
var express = require('../../');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var app = express();
|
||||
var bodyParser = require('body-parser');
|
||||
var api = express();
|
||||
|
||||
// app middleware
|
||||
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// api middleware
|
||||
|
||||
api.use(logger('dev'));
|
||||
api.use(bodyParser.json());
|
||||
|
||||
/**
|
||||
* CORS support.
|
||||
*/
|
||||
|
||||
api.all('*', function(req, res, next){
|
||||
if (!req.get('Origin')) return next();
|
||||
// use "*" here to accept any origin
|
||||
res.set('Access-Control-Allow-Origin', 'http://localhost:3000');
|
||||
res.set('Access-Control-Allow-Methods', 'PUT');
|
||||
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
|
||||
// res.set('Access-Control-Allow-Max-Age', 3600);
|
||||
if ('OPTIONS' == req.method) return res.send(200);
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT an existing user.
|
||||
*/
|
||||
|
||||
api.put('/user/:id', function(req, res){
|
||||
console.log(req.body);
|
||||
res.send(204);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
api.listen(3001);
|
||||
|
||||
console.log('app listening on 3000');
|
||||
console.log('api listening on 3001');
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var req = new XMLHttpRequest;
|
||||
req.open('PUT', 'http://localhost:3001/user/1', false);
|
||||
req.setRequestHeader('Content-Type', 'application/json');
|
||||
req.send('{"name":"tobi","species":"ferret"}');
|
||||
console.log(req.responseText);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
한中日
|
||||
@@ -8,7 +8,6 @@ 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>');
|
||||
@@ -9,7 +9,7 @@ var silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
// general config
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// our custom "verbose errors" setting
|
||||
// which we can use in the templates
|
||||
@@ -25,7 +25,7 @@ silent || app.use(logger('dev'));
|
||||
// Routes
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
res.render('index.ejs');
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res, next){
|
||||
@@ -96,7 +96,6 @@ app.use(function(err, req, res, next){
|
||||
res.render('500', { error: err });
|
||||
});
|
||||
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
|
||||
3
examples/error-pages/views/404.ejs
Normal file
3
examples/error-pages/views/404.ejs
Normal file
@@ -0,0 +1,3 @@
|
||||
<% include error_header %>
|
||||
<h2>Cannot find <%= url %></h2>
|
||||
<% include footer %>
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
extends error
|
||||
|
||||
block content
|
||||
h2 Cannot find #{url}
|
||||
8
examples/error-pages/views/500.ejs
Normal file
8
examples/error-pages/views/500.ejs
Normal file
@@ -0,0 +1,8 @@
|
||||
<% include error_header %>
|
||||
<h2>Error: <%= error.message %></h2>
|
||||
<% if (settings['verbose errors']) { %>
|
||||
<pre><%= error.stack %></pre>
|
||||
<% } else { %>
|
||||
<p>An error occurred!</p>
|
||||
<% } %>
|
||||
<% include footer %>
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
// note that we extend a different
|
||||
// layout with jade for 4xx & 5xx
|
||||
// responses
|
||||
|
||||
extends error
|
||||
|
||||
block content
|
||||
h1 Error: #{error.message}
|
||||
if settings['verbose errors']
|
||||
pre= error.stack
|
||||
else
|
||||
p An error ocurred!
|
||||
@@ -1,6 +0,0 @@
|
||||
html
|
||||
head
|
||||
title Error
|
||||
body
|
||||
h1 An error occurred!
|
||||
block content
|
||||
8
examples/error-pages/views/error_header.ejs
Normal file
8
examples/error-pages/views/error_header.ejs
Normal file
@@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Error</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>An error occurred!</h1>
|
||||
2
examples/error-pages/views/footer.ejs
Normal file
2
examples/error-pages/views/footer.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
18
examples/error-pages/views/index.ejs
Normal file
18
examples/error-pages/views/index.ejs
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Pages Example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>My Site</h1>
|
||||
<h2>Pages Example</h2>
|
||||
|
||||
<ul>
|
||||
<li>visit <a href="/500">500</a></li>
|
||||
<li>visit <a href="/404">404</a></li>
|
||||
<li>visit <a href="/403">403</a></li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h2 Pages Example
|
||||
ul
|
||||
li
|
||||
| visit
|
||||
a(href="/500") 500
|
||||
li
|
||||
| visit
|
||||
a(href="/404") 404
|
||||
li
|
||||
| visit
|
||||
a(href='/403') 403
|
||||
@@ -1,6 +0,0 @@
|
||||
html
|
||||
head
|
||||
title Custom Pages Example
|
||||
body
|
||||
h1 My Site
|
||||
block content
|
||||
@@ -20,7 +20,8 @@ function error(err, req, res, next) {
|
||||
if (!test) console.error(err.stack);
|
||||
|
||||
// respond with 500 "Internal Server Error".
|
||||
res.send(500);
|
||||
res.status(500);
|
||||
res.send('Internal Server Error');
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
@@ -30,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!'));
|
||||
});
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var escapeHtml = require('escape-html');
|
||||
var express = require('../..');
|
||||
var fs = require('fs');
|
||||
var md = require('marked').parse;
|
||||
var marked = require('marked');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -13,15 +14,10 @@ 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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
13
examples/multi-router/controllers/api_v1.js
Normal file
13
examples/multi-router/controllers/api_v1.js
Normal file
@@ -0,0 +1,13 @@
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv1 = express.Router();
|
||||
|
||||
apiv1.get('/', function(req, res) {
|
||||
res.send('Hello from APIv1 root route.');
|
||||
});
|
||||
|
||||
apiv1.get('/users', function(req, res) {
|
||||
res.send('List of APIv1 users.');
|
||||
});
|
||||
|
||||
module.exports = apiv1;
|
||||
13
examples/multi-router/controllers/api_v2.js
Normal file
13
examples/multi-router/controllers/api_v2.js
Normal file
@@ -0,0 +1,13 @@
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv2 = express.Router();
|
||||
|
||||
apiv2.get('/', function(req, res) {
|
||||
res.send('Hello from APIv2 root route.');
|
||||
});
|
||||
|
||||
apiv2.get('/users', function(req, res) {
|
||||
res.send('List of APIv2 users.');
|
||||
});
|
||||
|
||||
module.exports = apiv2;
|
||||
16
examples/multi-router/index.js
Normal file
16
examples/multi-router/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
var express = require('../..');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
app.use('/api/v1', require('./controllers/api_v1'));
|
||||
app.use('/api/v2', require('./controllers/api_v2'));
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.send('Hello form root route.');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'ejs';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var pet = db.pets[req.params.pet_id];
|
||||
if (!pet) return next('route');
|
||||
|
||||
15
examples/mvc/controllers/pet/views/edit.ejs
Normal file
15
examples/mvc/controllers/pet/views/edit.ejs
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Edit <%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><%= pet.name %></h1>
|
||||
<form action="/pet/<%= pet.id %>?_method=put" method="post">
|
||||
<label>Name: <input type="text" name="pet[name]" value="<%= pet.name %>"></label>
|
||||
<input type="submit" value="Update">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= pet.name
|
||||
form(action='/pet/#{pet.id}?_method=put', method='post')
|
||||
label= 'Name: '
|
||||
input(type='text', name='pet[name]', value=pet.name)
|
||||
input(type='submit', value='Update')
|
||||
13
examples/mvc/controllers/pet/views/show.ejs
Normal file
13
examples/mvc/controllers/pet/views/show.ejs
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title><%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><%= pet.name %> <a href="/pet/<%= pet.id %>/edit">edit</a></h1>
|
||||
|
||||
<p>You are viewing <%= pet.name %></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
|
||||
h1= pet.name
|
||||
a(href='/pet/#{pet.id}/edit') edit
|
||||
|
||||
p You are viewing #{pet.name}
|
||||
@@ -8,6 +8,7 @@ var fs = require('fs');
|
||||
module.exports = function(parent, options){
|
||||
var verbose = options.verbose;
|
||||
fs.readdirSync(__dirname + '/../controllers').forEach(function(name){
|
||||
if (!fs.statSync(__dirname + '/../controllers/' + name).isDirectory()) return;
|
||||
verbose && console.log('\n %s:', name);
|
||||
var obj = require('./../controllers/' + name);
|
||||
var name = obj.name || name;
|
||||
@@ -66,7 +67,7 @@ module.exports = function(parent, options){
|
||||
app[method](path, obj.before, handler);
|
||||
verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), path, key);
|
||||
} else {
|
||||
app[method](path, obj[key]);
|
||||
app[method](path, handler);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,15 +7,23 @@ var app = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
var site = require('./site');
|
||||
var post = require('./post');
|
||||
var user = require('./user');
|
||||
|
||||
module.exports = app;
|
||||
|
||||
// Config
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', __dirname + '/views');
|
||||
app.use(logger('dev'));
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.use(logger('dev'));
|
||||
}
|
||||
|
||||
app.use(methodOverride('_method'));
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
@@ -27,7 +35,7 @@ app.get('/', site.index);
|
||||
|
||||
// User
|
||||
|
||||
app.all('/users', user.list);
|
||||
app.get('/users', user.list);
|
||||
app.all('/user/:id/:op?', user.load);
|
||||
app.get('/user/:id', user.view);
|
||||
app.get('/user/:id/view', user.view);
|
||||
|
||||
@@ -15,7 +15,9 @@ exports.load = function(req, res, next){
|
||||
if (req.user) {
|
||||
next();
|
||||
} else {
|
||||
next(new Error('cannot find user ' + id));
|
||||
var err = new Error('cannot find user ' + id);
|
||||
err.status = 404;
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -40,4 +42,4 @@ exports.update = function(req, res){
|
||||
req.user.name = user.name;
|
||||
req.user.email = user.email;
|
||||
res.redirect('back');
|
||||
};
|
||||
};
|
||||
|
||||
@@ -47,14 +47,14 @@ app.get('/search/:query?', function(req, res){
|
||||
});
|
||||
|
||||
/**
|
||||
* GET client javascript. Here we use sendfile()
|
||||
* GET client javascript. Here we use sendFile()
|
||||
* because serving __dirname with the static() middleware
|
||||
* would also mean serving our server "index.js" and the "search.jade"
|
||||
* template.
|
||||
*/
|
||||
|
||||
app.get('/client.js', function(req, res){
|
||||
res.sendfile(__dirname + '/client.js');
|
||||
res.sendFile(__dirname + '/client.js');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -34,13 +34,13 @@ function GithubView(name, options){
|
||||
GithubView.prototype.render = function(options, fn){
|
||||
var self = this;
|
||||
var opts = {
|
||||
host: 'rawgithub.com',
|
||||
port: 80,
|
||||
host: 'raw.githubusercontent.com',
|
||||
port: 443,
|
||||
path: this.path,
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
http.request(opts, function(res) {
|
||||
https.request(opts, function(res) {
|
||||
var buf = '';
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function(str){ buf += str });
|
||||
|
||||
@@ -23,7 +23,7 @@ app.engine('md', function(str, options, fn){
|
||||
});
|
||||
|
||||
// pointing to a particular github repo to load files from it
|
||||
app.set('views', 'visionmedia/express');
|
||||
app.set('views', 'expressjs/express');
|
||||
|
||||
// register a new view constructor
|
||||
app.set('view', GithubView);
|
||||
@@ -36,7 +36,7 @@ app.get('/', function(req, res){
|
||||
});
|
||||
|
||||
app.get('/Readme.md', function(req, res){
|
||||
// rendering a view from https://github.com/visionmedia/express/blob/master/Readme.md
|
||||
// rendering a view from https://github.com/expressjs/express/blob/master/Readme.md
|
||||
res.render('Readme.md');
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ function error(status, msg) {
|
||||
app.use('/api', function(req, res, next){
|
||||
var key = req.query['api-key'];
|
||||
|
||||
// key isnt present
|
||||
// key isn't present
|
||||
if (!key) return next(error(400, 'api key required'));
|
||||
|
||||
// key is invalid
|
||||
@@ -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/visionmedia/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' }
|
||||
];
|
||||
@@ -93,14 +93,16 @@ app.get('/api/user/:name/repos', function(req, res, next){
|
||||
app.use(function(err, req, res, next){
|
||||
// whatever you want here, feel free to populate
|
||||
// properties on `err` to treat it differently in here.
|
||||
res.send(err.status || 500, { error: err.message });
|
||||
res.status(err.status || 500);
|
||||
res.send({ error: err.message });
|
||||
});
|
||||
|
||||
// our custom JSON 404 middleware. Since it's placed last
|
||||
// it will be the last middleware called, if all others
|
||||
// invoke next() and do not respond.
|
||||
app.use(function(req, res){
|
||||
res.send(404, { error: "Lame, can't find that" });
|
||||
res.status(404);
|
||||
res.send({ error: "Lame, can't find that" });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
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 mixin = require('utils-merge');
|
||||
var Router = require('./router');
|
||||
var methods = require('methods');
|
||||
var middleware = require('./middleware/init');
|
||||
@@ -12,9 +22,13 @@ var debug = require('debug')('express:application');
|
||||
var View = require('./view');
|
||||
var http = require('http');
|
||||
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 slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
@@ -22,6 +36,13 @@ var resolve = require('path').resolve;
|
||||
|
||||
var app = exports = module.exports = {};
|
||||
|
||||
/**
|
||||
* Variable for trust proxy inheritance back-compat
|
||||
* @private
|
||||
*/
|
||||
|
||||
var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
|
||||
|
||||
/**
|
||||
* Initialize the server.
|
||||
*
|
||||
@@ -29,35 +50,50 @@ 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.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
|
||||
this.request.__proto__ = parent.request;
|
||||
this.response.__proto__ = parent.response;
|
||||
this.engines.__proto__ = parent.engines;
|
||||
@@ -95,16 +131,16 @@ 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'),
|
||||
strict: this.enabled('strict routing')
|
||||
});
|
||||
|
||||
this._router.use(query());
|
||||
this._router.use(query(this.get('query parser fn')));
|
||||
this._router.use(middleware.init(this));
|
||||
}
|
||||
};
|
||||
@@ -112,17 +148,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)
|
||||
});
|
||||
@@ -144,40 +180,62 @@ 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(route, fn){
|
||||
var mount_app;
|
||||
app.use = function use(fn) {
|
||||
var offset = 0;
|
||||
var path = '/';
|
||||
|
||||
// default route to '/'
|
||||
if ('string' != typeof route) fn = route, route = '/';
|
||||
// default path to '/'
|
||||
// disambiguate app.use([fn])
|
||||
if (typeof fn !== 'function') {
|
||||
var arg = fn;
|
||||
|
||||
// express app
|
||||
if (fn.handle && fn.set) mount_app = fn;
|
||||
while (Array.isArray(arg) && arg.length !== 0) {
|
||||
arg = arg[0];
|
||||
}
|
||||
|
||||
// restore .app property on req and res
|
||||
if (mount_app) {
|
||||
debug('.use app under %s', route);
|
||||
mount_app.mountpath = route;
|
||||
fn = function(req, res, next) {
|
||||
// first arg is the path
|
||||
if (typeof arg !== 'function') {
|
||||
offset = 1;
|
||||
path = fn;
|
||||
}
|
||||
}
|
||||
|
||||
var fns = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (fns.length === 0) {
|
||||
throw new TypeError('app.use() requires middleware functions');
|
||||
}
|
||||
|
||||
// setup router
|
||||
this.lazyrouter();
|
||||
var router = this._router;
|
||||
|
||||
fns.forEach(function (fn) {
|
||||
// non-express app
|
||||
if (!fn || !fn.handle || !fn.set) {
|
||||
return router.use(path, fn);
|
||||
}
|
||||
|
||||
debug('.use app under %s', path);
|
||||
fn.mountpath = path;
|
||||
fn.parent = this;
|
||||
|
||||
// restore .app property on req and res
|
||||
router.use(path, function mounted_app(req, res, next) {
|
||||
var orig = req.app;
|
||||
mount_app.handle(req, res, function(err) {
|
||||
fn.handle(req, res, function (err) {
|
||||
req.__proto__ = orig.request;
|
||||
res.__proto__ = orig.response;
|
||||
next(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
this.lazyrouter();
|
||||
this._router.use(route, fn);
|
||||
|
||||
// mounted an app
|
||||
if (mount_app) {
|
||||
mount_app.parent = this;
|
||||
mount_app.emit('mount', this);
|
||||
}
|
||||
// mounted an app
|
||||
fn.emit('mount', this);
|
||||
}, this);
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -189,10 +247,10 @@ app.use = function(route, 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);
|
||||
};
|
||||
@@ -220,7 +278,7 @@ app.route = function(path){
|
||||
* so if you're using ".ejs" extensions you dont need to do anything.
|
||||
*
|
||||
* Some template engines do not follow this convention, the
|
||||
* [Consolidate.js](https://github.com/visionmedia/consolidate.js)
|
||||
* [Consolidate.js](https://github.com/tj/consolidate.js)
|
||||
* library was created to map all of node's popular template
|
||||
* engines to follow this convention, thus allowing them to
|
||||
* work seamlessly within Express.
|
||||
@@ -228,13 +286,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;
|
||||
};
|
||||
|
||||
@@ -247,21 +314,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){
|
||||
var self = this;
|
||||
self.lazyrouter();
|
||||
app.param = function param(name, fn) {
|
||||
this.lazyrouter();
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function(key) {
|
||||
self.param(key, fn);
|
||||
});
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
this.param(name[i], fn);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
self._router.param(name, fn);
|
||||
this._router.param(name, fn);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -277,27 +345,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':
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -315,10 +393,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
|
||||
: '';
|
||||
@@ -336,11 +414,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));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -355,10 +433,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);
|
||||
};
|
||||
|
||||
@@ -367,10 +445,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);
|
||||
};
|
||||
|
||||
@@ -379,10 +457,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);
|
||||
};
|
||||
|
||||
@@ -392,12 +470,15 @@ 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();
|
||||
|
||||
var route = this._router.route(path);
|
||||
route[method].apply(route, [].slice.call(arguments, 1));
|
||||
route[method].apply(route, slice.call(arguments, 1));
|
||||
return this;
|
||||
};
|
||||
});
|
||||
@@ -409,17 +490,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);
|
||||
});
|
||||
var args = slice.call(arguments, 1);
|
||||
|
||||
for (var i = 0; i < methods.length; i++) {
|
||||
route[methods[i]].apply(route, args);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -440,63 +522,73 @@ 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
|
||||
mixin(opts, this.locals);
|
||||
merge(renderOptions, this.locals);
|
||||
|
||||
// merge options._locals
|
||||
if (options._locals) mixin(opts, options._locals);
|
||||
if (opts._locals) {
|
||||
merge(renderOptions, opts._locals);
|
||||
}
|
||||
|
||||
// merge options
|
||||
mixin(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
|
||||
});
|
||||
|
||||
if (!view.path) {
|
||||
var err = new Error('Failed to lookup view "' + name + '" in views directory "' + view.root + '"');
|
||||
var dirs = Array.isArray(view.root) && view.root.length > 1
|
||||
? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'
|
||||
: '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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -517,21 +609,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,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.
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var mixin = require('utils-merge');
|
||||
var mixin = require('merge-descriptors');
|
||||
var proto = require('./application');
|
||||
var Route = require('./router/route');
|
||||
var Router = require('./router');
|
||||
@@ -28,8 +38,8 @@ function createApplication() {
|
||||
app.handle(req, res, next);
|
||||
};
|
||||
|
||||
mixin(app, proto);
|
||||
mixin(app, EventEmitter.prototype);
|
||||
mixin(app, EventEmitter.prototype, false);
|
||||
mixin(app, proto, false);
|
||||
|
||||
app.request = { __proto__: req, app: app };
|
||||
app.response = { __proto__: res, app: app };
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@@ -1,37 +1,44 @@
|
||||
/*!
|
||||
* 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 qs = require('qs');
|
||||
var parseUrl = require('parseurl');
|
||||
var qs = require('qs');
|
||||
|
||||
/**
|
||||
* Query:
|
||||
*
|
||||
* Automatically parse the query-string when available,
|
||||
* populating the `req.query` object using
|
||||
* [qs](https://github.com/visionmedia/node-querystring).
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* .use(connect.query())
|
||||
* .use(function(req, res){
|
||||
* res.end(JSON.stringify(req.query));
|
||||
* });
|
||||
*
|
||||
* The `options` passed are provided to qs.parse function.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function query(options){
|
||||
module.exports = function query(options) {
|
||||
var opts = Object.create(options || null);
|
||||
var queryparse = qs.parse;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
queryparse = options;
|
||||
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) {
|
||||
req.query = ~req.url.indexOf('?')
|
||||
? qs.parse(parseUrl(req).query, options)
|
||||
: {};
|
||||
var val = parseUrl(req).query;
|
||||
req.query = queryparse(val, opts);
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
189
lib/request.js
189
lib/request.js
@@ -1,9 +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 accepts = require('accepts');
|
||||
var deprecate = require('depd')('express');
|
||||
var isIP = require('net').isIP;
|
||||
var typeis = require('type-is');
|
||||
var http = require('http');
|
||||
var fresh = require('fresh');
|
||||
@@ -40,18 +52,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];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -62,12 +84,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:
|
||||
*
|
||||
@@ -97,8 +119,8 @@ req.header = function(name){
|
||||
* // => "json"
|
||||
*
|
||||
* @param {String|Array} type(s)
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @return {String|Array|Boolean}
|
||||
* @public
|
||||
*/
|
||||
|
||||
req.accepts = function(){
|
||||
@@ -110,8 +132,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(){
|
||||
@@ -127,8 +149,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(){
|
||||
@@ -144,8 +166,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(){
|
||||
@@ -157,29 +179,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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -196,16 +223,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;
|
||||
};
|
||||
|
||||
@@ -230,54 +264,66 @@ 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the protocol string "http" or "https"
|
||||
* when requested with TLS. When the "trust proxy"
|
||||
* setting trusts the socket address, the
|
||||
* "X-Forwarded-Proto" header field will be trusted.
|
||||
* "X-Forwarded-Proto" header field will be trusted
|
||||
* and used if present.
|
||||
*
|
||||
* If you're running behind a reverse proxy that
|
||||
* supplies https for you this may be enabled.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'protocol', function protocol(){
|
||||
var proto = this.connection.encrypted
|
||||
? 'https'
|
||||
: 'http';
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
|
||||
if (!trust(this.connection.remoteAddress)) {
|
||||
return this.connection.encrypted
|
||||
? 'https'
|
||||
: 'http';
|
||||
if (!trust(this.connection.remoteAddress, 0)) {
|
||||
return proto;
|
||||
}
|
||||
|
||||
// Note: X-Forwarded-Proto is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
var proto = this.get('X-Forwarded-Proto') || 'http';
|
||||
proto = this.get('X-Forwarded-Proto') || proto;
|
||||
return proto.split(/\s*,\s*/)[0];
|
||||
});
|
||||
|
||||
/**
|
||||
* 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';
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -287,7 +333,7 @@ defineGetter(req, 'secure', function secure(){
|
||||
* "trust proxy" is set.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'ip', function ip(){
|
||||
@@ -304,7 +350,7 @@ defineGetter(req, 'ip', function ip(){
|
||||
* "proxy2" were trusted.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'ips', function ips() {
|
||||
@@ -325,22 +371,27 @@ defineGetter(req, 'ips', function ips() {
|
||||
* If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'subdomains', function subdomains() {
|
||||
var hostname = this.hostname;
|
||||
|
||||
if (!hostname) return [];
|
||||
|
||||
var offset = this.app.get('subdomain offset');
|
||||
return (this.hostname || '')
|
||||
.split('.')
|
||||
.reverse()
|
||||
.slice(offset);
|
||||
var subdomains = !isIP(hostname)
|
||||
? hostname.split('.').reverse()
|
||||
: [hostname];
|
||||
|
||||
return subdomains.slice(offset);
|
||||
});
|
||||
|
||||
/**
|
||||
* Short-hand for `url.parse(req.url).pathname`.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'path', function path() {
|
||||
@@ -355,14 +406,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');
|
||||
}
|
||||
|
||||
@@ -374,7 +425,7 @@ defineGetter(req, 'hostname', function hostname(){
|
||||
: 0;
|
||||
var index = host.indexOf(':', offset);
|
||||
|
||||
return ~index
|
||||
return index !== -1
|
||||
? host.substring(0, index)
|
||||
: host;
|
||||
});
|
||||
@@ -391,7 +442,7 @@ defineGetter(req, 'host', deprecate.function(function host(){
|
||||
* still match.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'fresh', function(){
|
||||
@@ -399,11 +450,11 @@ defineGetter(req, 'fresh', function(){
|
||||
var s = this.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 ((s >= 200 && s < 300) || 304 === s) {
|
||||
return fresh(this.headers, (this.res._headers || {}));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -415,7 +466,7 @@ defineGetter(req, 'fresh', function(){
|
||||
* resource has changed.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
defineGetter(req, 'stale', function stale(){
|
||||
@@ -426,12 +477,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';
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -440,7 +491,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, {
|
||||
|
||||
780
lib/response.js
780
lib/response.js
File diff suppressed because it is too large
Load Diff
@@ -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 Route = require('./route');
|
||||
@@ -7,19 +18,29 @@ 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');
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var objectRegExp = /^\[object (\S+)\]$/;
|
||||
var slice = Array.prototype.slice;
|
||||
var toString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
*
|
||||
* @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);
|
||||
@@ -30,9 +51,9 @@ var proto = module.exports = function(options) {
|
||||
|
||||
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;
|
||||
@@ -69,12 +90,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;
|
||||
}
|
||||
@@ -85,6 +107,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);
|
||||
}
|
||||
|
||||
@@ -96,7 +119,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);
|
||||
}
|
||||
|
||||
@@ -106,20 +129,17 @@ 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 method = req.method.toLowerCase();
|
||||
|
||||
var search = 1 + req.url.indexOf('?');
|
||||
var pathlength = search ? search - 1 : req.url.length;
|
||||
var fqdn = 1 + req.url.substr(0, pathlength).indexOf('://');
|
||||
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 removed = '';
|
||||
@@ -136,112 +156,142 @@ 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;
|
||||
|
||||
// for options requests, respond with a default if nothing else responds
|
||||
if (method === 'options') {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
// setup basic req values
|
||||
req.baseUrl = parentUrl;
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
|
||||
next();
|
||||
|
||||
function next(err) {
|
||||
if (err === 'route') {
|
||||
err = undefined;
|
||||
}
|
||||
|
||||
var layer = stack[idx++];
|
||||
var layerPath;
|
||||
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
}
|
||||
var layerError = err === 'route'
|
||||
? null
|
||||
: err;
|
||||
|
||||
// remove added slash
|
||||
if (slashAdded) {
|
||||
req.url = req.url.substr(1);
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
removed = '';
|
||||
|
||||
try {
|
||||
var path = parseUrl(req).pathname;
|
||||
if (undefined == path) path = '/';
|
||||
|
||||
if (!layer.match(path)) return next(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 (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var has_method = route._handles_method(method);
|
||||
|
||||
// build up automatic options response
|
||||
if (!has_method && method === 'options') {
|
||||
options.push.apply(options, route._options());
|
||||
}
|
||||
|
||||
// don't even bother
|
||||
if (!has_method && method !== 'head') {
|
||||
return next();
|
||||
}
|
||||
|
||||
// we can now dispatch to the route
|
||||
req.route = route;
|
||||
}
|
||||
|
||||
// Capture one-time layer values
|
||||
req.params = self.mergeParams
|
||||
? mergeParams(layer.params, parentParams)
|
||||
: layer.params;
|
||||
layerPath = layer.path;
|
||||
|
||||
// this should be done for the layer
|
||||
return self.process_params(layer, paramcalled, req, res, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (route) {
|
||||
return layer.handle(req, res, next);
|
||||
}
|
||||
|
||||
trim_prefix();
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
next(err);
|
||||
// restore altered req.url
|
||||
if (removed.length !== 0) {
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
removed = '';
|
||||
}
|
||||
|
||||
function trim_prefix() {
|
||||
var c = path[layerPath.length];
|
||||
if (c && '/' != c && '.' != c) return next(err);
|
||||
// no more matching layers
|
||||
if (idx >= stack.length) {
|
||||
setImmediate(done, layerError);
|
||||
return;
|
||||
}
|
||||
|
||||
// Trim off the part of the url that matches the route
|
||||
// middleware (.use stuff) needs to have the path stripped
|
||||
removed = layerPath;
|
||||
if (removed.length) {
|
||||
debug('trim prefix (%s) from url %s', layerPath, req.url);
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (route) {
|
||||
return layer.handle_request(req, res, next);
|
||||
}
|
||||
|
||||
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) {
|
||||
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] !== '/') {
|
||||
req.url = '/' + req.url;
|
||||
@@ -249,36 +299,27 @@ proto.handle = function(req, res, done) {
|
||||
}
|
||||
|
||||
// Setup base URL (no trailing slash)
|
||||
if (removed.length && removed.substr(-1) === '/') {
|
||||
req.baseUrl = parentUrl + removed.substring(0, removed.length - 1);
|
||||
} else {
|
||||
req.baseUrl = parentUrl + removed;
|
||||
}
|
||||
req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
|
||||
? removed.substring(0, removed.length - 1)
|
||||
: removed);
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.handle.name || 'anonymous', layerPath, req.originalUrl);
|
||||
var arity = layer.handle.length;
|
||||
try {
|
||||
if (err && arity === 4) {
|
||||
layer.handle(err, req, res, next);
|
||||
} else if (!err && arity < 4) {
|
||||
layer.handle(req, res, next);
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
|
||||
|
||||
if (layerError) {
|
||||
layer.handle_error(layerError, req, res, next);
|
||||
} else {
|
||||
layer.handle_request(req, res, next);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -325,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;
|
||||
|
||||
@@ -339,11 +381,7 @@ proto.process_params = function(layer, called, req, res, done) {
|
||||
value: paramVal
|
||||
};
|
||||
|
||||
try {
|
||||
return paramCallback();
|
||||
} catch (err) {
|
||||
return done(err);
|
||||
}
|
||||
paramCallback();
|
||||
}
|
||||
|
||||
// single param callbacks
|
||||
@@ -362,7 +400,11 @@ proto.process_params = function(layer, called, req, res, done) {
|
||||
|
||||
if (!fn) return param();
|
||||
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
try {
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
} catch (e) {
|
||||
paramCallback(e);
|
||||
}
|
||||
}
|
||||
|
||||
param();
|
||||
@@ -380,40 +422,56 @@ proto.process_params = function(layer, called, req, res, done) {
|
||||
* handlers can operate without any code changes regardless of the "prefix"
|
||||
* pathname.
|
||||
*
|
||||
* @param {String|Function} route
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
* @public
|
||||
*/
|
||||
|
||||
proto.use = function(route, fn){
|
||||
// default route to '/'
|
||||
if ('string' != typeof route) {
|
||||
fn = route;
|
||||
route = '/';
|
||||
}
|
||||
proto.use = function use(fn) {
|
||||
var offset = 0;
|
||||
var path = '/';
|
||||
|
||||
// default path to '/'
|
||||
// disambiguate router.use([fn])
|
||||
if (typeof fn !== 'function') {
|
||||
var type = {}.toString.call(fn);
|
||||
var msg = 'Router.use() requires callback functions but got a ' + type;
|
||||
throw new Error(msg);
|
||||
var arg = fn;
|
||||
|
||||
while (Array.isArray(arg) && arg.length !== 0) {
|
||||
arg = arg[0];
|
||||
}
|
||||
|
||||
// first arg is the path
|
||||
if (typeof arg !== 'function') {
|
||||
offset = 1;
|
||||
path = fn;
|
||||
}
|
||||
}
|
||||
|
||||
// strip trailing slash
|
||||
if ('/' == route[route.length - 1]) {
|
||||
route = route.slice(0, -1);
|
||||
var callbacks = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (callbacks.length === 0) {
|
||||
throw new TypeError('Router.use() requires middleware functions');
|
||||
}
|
||||
|
||||
var layer = new Layer(route, {
|
||||
sensitive: this.caseSensitive,
|
||||
strict: this.strict,
|
||||
end: false
|
||||
}, fn);
|
||||
for (var i = 0; i < callbacks.length; i++) {
|
||||
var fn = callbacks[i];
|
||||
|
||||
// add the middleware
|
||||
debug('use %s %s', route || '/', fn.name || 'anonymous');
|
||||
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>');
|
||||
|
||||
var layer = new Layer(path, {
|
||||
sensitive: this.caseSensitive,
|
||||
strict: false,
|
||||
end: false
|
||||
}, fn);
|
||||
|
||||
layer.route = undefined;
|
||||
|
||||
this.stack.push(layer);
|
||||
}
|
||||
|
||||
this.stack.push(layer);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -427,10 +485,10 @@ proto.use = function(route, 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, {
|
||||
@@ -449,11 +507,59 @@ proto.route = function(path){
|
||||
methods.concat('all').forEach(function(method){
|
||||
proto[method] = function(path){
|
||||
var route = this.route(path)
|
||||
route[method].apply(route, [].slice.call(arguments, 1));
|
||||
route[method].apply(route, slice.call(arguments, 1));
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
// append methods to a list of methods
|
||||
function appendMethods(list, addition) {
|
||||
for (var i = 0; i < addition.length; i++) {
|
||||
var method = addition[i];
|
||||
if (list.indexOf(method) === -1) {
|
||||
list.push(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get pathname of request
|
||||
function getPathname(req) {
|
||||
try {
|
||||
return parseUrl(req).pathname;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// get type for error message
|
||||
function gettype(obj) {
|
||||
var type = typeof obj;
|
||||
|
||||
if (type !== 'object') {
|
||||
return type;
|
||||
}
|
||||
|
||||
// inspect [[Class]] for objects
|
||||
return toString.call(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) {
|
||||
@@ -472,9 +578,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
|
||||
@@ -487,7 +596,7 @@ function mergeParams(params, parent) {
|
||||
}
|
||||
}
|
||||
|
||||
return mixin(parent, params);
|
||||
return mixin(obj, params);
|
||||
}
|
||||
|
||||
// restore obj props after function
|
||||
@@ -510,10 +619,27 @@ 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 () {
|
||||
var args = [old].concat(slice.call(arguments));
|
||||
return function proxy() {
|
||||
var args = new Array(arguments.length + 1);
|
||||
|
||||
args[0] = old;
|
||||
for (var i = 0, len = arguments.length; i < len; i++) {
|
||||
args[i + 1] = arguments[i];
|
||||
}
|
||||
|
||||
fn.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
/*!
|
||||
* 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');
|
||||
var debug = require('debug')('express:router:layer');
|
||||
|
||||
/**
|
||||
* Expose `Layer`.
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = Layer;
|
||||
@@ -17,11 +36,68 @@ function Layer(path, options, fn) {
|
||||
}
|
||||
|
||||
debug('new %s', path);
|
||||
options = options || {};
|
||||
this.regexp = pathRegexp(path, this.keys = [], options);
|
||||
var opts = options || {};
|
||||
|
||||
this.handle = fn;
|
||||
this.name = fn.name || '<anonymous>';
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
this.regexp = pathRegexp(path, this.keys = [], opts);
|
||||
|
||||
if (path === '/' && opts.end === false) {
|
||||
this.regexp.fast_slash = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the error for the layer.
|
||||
*
|
||||
* @param {Error} error
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {function} next
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Layer.prototype.handle_error = function handle_error(error, req, res, next) {
|
||||
var fn = this.handle;
|
||||
|
||||
if (fn.length !== 4) {
|
||||
// not a standard error handler
|
||||
return next(error);
|
||||
}
|
||||
|
||||
try {
|
||||
fn(error, req, res, next);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the request for the layer.
|
||||
*
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {function} next
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Layer.prototype.handle_request = function handle(req, res, next) {
|
||||
var fn = this.handle;
|
||||
|
||||
if (fn.length > 3) {
|
||||
// not a standard request handler
|
||||
return next();
|
||||
}
|
||||
|
||||
try {
|
||||
fn(req, res, next);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this route matches `path`, if so
|
||||
* populate `.params`.
|
||||
@@ -31,26 +107,43 @@ function Layer(path, options, fn) {
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Layer.prototype.match = function(path){
|
||||
var keys = this.keys;
|
||||
var params = this.params = {};
|
||||
Layer.prototype.match = function match(path) {
|
||||
if (path == null) {
|
||||
// no path, nothing matches
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
var n = 0;
|
||||
var key;
|
||||
var val;
|
||||
|
||||
if (!m) return false;
|
||||
if (!m) {
|
||||
this.params = undefined;
|
||||
this.path = undefined;
|
||||
return false;
|
||||
}
|
||||
|
||||
// store values
|
||||
this.params = {};
|
||||
this.path = m[0];
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
key = keys[i - 1];
|
||||
val = decode_param(m[i]);
|
||||
var keys = this.keys;
|
||||
var params = this.params;
|
||||
|
||||
if (key) {
|
||||
params[key.name] = val;
|
||||
} else {
|
||||
params[n++] = val;
|
||||
for (var i = 1; i < m.length; i++) {
|
||||
var key = keys[i - 1];
|
||||
var prop = key.name;
|
||||
var val = decode_param(m[i]);
|
||||
|
||||
if (val !== undefined || !(hasOwnProperty.call(params, prop))) {
|
||||
params[prop] = val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,19 +155,22 @@ Layer.prototype.match = function(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,13 +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;
|
||||
@@ -16,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 %s', 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) {
|
||||
@@ -37,33 +60,43 @@ 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) {
|
||||
return done();
|
||||
@@ -76,8 +109,9 @@ Route.prototype.dispatch = function(req, res, done){
|
||||
|
||||
req.route = this;
|
||||
|
||||
var idx = 0;
|
||||
(function next_layer(err) {
|
||||
next();
|
||||
|
||||
function next(err) {
|
||||
if (err && err === 'route') {
|
||||
return done();
|
||||
}
|
||||
@@ -88,33 +122,15 @@ Route.prototype.dispatch = function(req, res, done){
|
||||
}
|
||||
|
||||
if (layer.method && layer.method !== method) {
|
||||
return next_layer(err);
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var arity = layer.handle.length;
|
||||
if (err) {
|
||||
if (arity < 4) {
|
||||
return next_layer(err);
|
||||
}
|
||||
|
||||
try {
|
||||
layer.handle(err, req, res, next_layer);
|
||||
} catch (err) {
|
||||
next_layer(err);
|
||||
}
|
||||
return;
|
||||
layer.handle_error(err, req, res, next);
|
||||
} else {
|
||||
layer.handle_request(req, res, next);
|
||||
}
|
||||
|
||||
if (arity > 3) {
|
||||
return next_layer();
|
||||
}
|
||||
|
||||
try {
|
||||
layer.handle(req, res, next_layer);
|
||||
} catch (err) {
|
||||
next_layer(err);
|
||||
}
|
||||
})();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -145,43 +161,50 @@ Route.prototype.dispatch = function(req, res, done){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Route.prototype.all = function(){
|
||||
var self = this;
|
||||
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);
|
||||
}
|
||||
|
||||
self.methods._all = true;
|
||||
self.stack.push({ handle: fn });
|
||||
});
|
||||
var layer = Layer('/', {}, handle);
|
||||
layer.method = undefined;
|
||||
|
||||
return self;
|
||||
this.methods._all = true;
|
||||
this.stack.push(layer);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
methods.forEach(function(method){
|
||||
Route.prototype[method] = function(){
|
||||
var self = this;
|
||||
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, self.path);
|
||||
debug('%s %s', method, this.path);
|
||||
|
||||
if (!self.methods[method]) {
|
||||
self.methods[method] = true;
|
||||
}
|
||||
var layer = Layer('/', {}, handle);
|
||||
layer.method = method;
|
||||
|
||||
self.stack.push({ method: method, handle: fn });
|
||||
});
|
||||
return self;
|
||||
this.methods[method] = true;
|
||||
this.stack.push(layer);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
157
lib/utils.js
157
lib/utils.js
@@ -1,13 +1,27 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
/*!
|
||||
* 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 crc32 = require('buffer-crc32');
|
||||
var crypto = require('crypto');
|
||||
var basename = require('path').basename;
|
||||
var etag = require('etag');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
var typer = require('media-typer');
|
||||
var qs = require('qs');
|
||||
var querystring = require('querystring');
|
||||
|
||||
/**
|
||||
* Return strong ETag for `body`.
|
||||
@@ -18,17 +32,12 @@ var typer = require('media-typer');
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.etag = function etag(body, encoding){
|
||||
if (body.length === 0) {
|
||||
// fast-path empty body
|
||||
return '"1B2M2Y8AsgTpgAmY7PhCfg=="'
|
||||
}
|
||||
exports.etag = function (body, encoding) {
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
: body;
|
||||
|
||||
var hash = crypto
|
||||
.createHash('md5')
|
||||
.update(body, encoding)
|
||||
.digest('base64')
|
||||
return '"' + hash + '"'
|
||||
return etag(buf, {weak: false});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -41,16 +50,11 @@ exports.etag = function etag(body, encoding){
|
||||
*/
|
||||
|
||||
exports.wetag = function wetag(body, encoding){
|
||||
if (body.length === 0) {
|
||||
// fast-path empty body
|
||||
return 'W/"0-0"'
|
||||
}
|
||||
var buf = !Buffer.isBuffer(body)
|
||||
? new Buffer(body, encoding)
|
||||
: body;
|
||||
|
||||
var buf = Buffer.isBuffer(body)
|
||||
? body
|
||||
: new Buffer(body, encoding)
|
||||
var len = buf.length
|
||||
return 'W/"' + len.toString(16) + '-' + crc32.unsigned(buf) + '"'
|
||||
return etag(buf, {weak: true});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -62,9 +66,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
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -75,18 +79,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".
|
||||
@@ -129,18 +123,8 @@ exports.normalizeTypes = function(types){
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.contentDisposition = function(filename){
|
||||
var ret = 'attachment';
|
||||
if (filename) {
|
||||
filename = basename(filename);
|
||||
// if filename contains non-ascii characters, add a utf-8 version ala RFC 5987
|
||||
ret = /[^\040-\176]/.test(filename)
|
||||
? 'attachment; filename="' + encodeURI(filename) + '"; filename*=UTF-8\'\'' + encodeURI(filename)
|
||||
: 'attachment; filename="' + filename + '"';
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
exports.contentDisposition = deprecate.function(contentDisposition,
|
||||
'utils.contentDisposition: use content-disposition npm module instead');
|
||||
|
||||
/**
|
||||
* Parse accept params `str` returning an
|
||||
@@ -158,7 +142,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];
|
||||
@@ -202,6 +186,41 @@ exports.compileETag = function(val) {
|
||||
return fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile "query parser" value to function.
|
||||
*
|
||||
* @param {String|Function} val
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.compileQueryParser = function compileQueryParser(val) {
|
||||
var fn;
|
||||
|
||||
if (typeof val === 'function') {
|
||||
return val;
|
||||
}
|
||||
|
||||
switch (val) {
|
||||
case true:
|
||||
fn = querystring.parse;
|
||||
break;
|
||||
case false:
|
||||
fn = newObject;
|
||||
break;
|
||||
case 'extended':
|
||||
fn = parseExtendedQueryString;
|
||||
break;
|
||||
case 'simple':
|
||||
fn = querystring.parse;
|
||||
break;
|
||||
default:
|
||||
throw new TypeError('unknown value for query parser function: ' + val);
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile "proxy trust" value to function.
|
||||
*
|
||||
@@ -240,15 +259,41 @@ 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
function newObject() {
|
||||
return {};
|
||||
}
|
||||
|
||||
164
lib/view.js
164
lib/view.js
@@ -1,18 +1,37 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
/*!
|
||||
* 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.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var dirname = path.dirname;
|
||||
var basename = path.basename;
|
||||
var extname = path.extname;
|
||||
var exists = fs.existsSync || path.existsSync;
|
||||
var join = path.join;
|
||||
var resolve = path.resolve;
|
||||
|
||||
/**
|
||||
* Expose `View`.
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = View;
|
||||
@@ -26,52 +45,129 @@ 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
|
||||
opts.engines[this.ext] = require(this.ext.substr(1)).__express;
|
||||
}
|
||||
|
||||
// store loaded engine
|
||||
this.engine = opts.engines[this.ext];
|
||||
|
||||
// lookup path
|
||||
this.path = this.lookup(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup view by the given `path`
|
||||
* Lookup view by the given `name`
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {String}
|
||||
* @api private
|
||||
* @param {string} name
|
||||
* @private
|
||||
*/
|
||||
|
||||
View.prototype.lookup = function(path){
|
||||
var ext = this.ext;
|
||||
View.prototype.lookup = function lookup(name) {
|
||||
var path;
|
||||
var roots = [].concat(this.root);
|
||||
|
||||
// <path>.<engine>
|
||||
if (!utils.isAbsolute(path)) path = join(this.root, path);
|
||||
if (exists(path)) return path;
|
||||
debug('lookup "%s"', name);
|
||||
|
||||
// <path>/index.<engine>
|
||||
path = join(dirname(path), basename(path, ext), 'index' + ext);
|
||||
if (exists(path)) return path;
|
||||
for (var i = 0; i < roots.length && !path; i++) {
|
||||
var root = roots[i];
|
||||
|
||||
// resolve the path
|
||||
var loc = resolve(root, name);
|
||||
var dir = dirname(loc);
|
||||
var file = basename(loc);
|
||||
|
||||
// resolve the file
|
||||
path = this.resolve(dir, file);
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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(options, fn){
|
||||
this.engine(this.path, options, fn);
|
||||
View.prototype.render = function render(options, callback) {
|
||||
debug('render "%s"', this.path);
|
||||
this.engine(this.path, options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve the file within the given directory.
|
||||
*
|
||||
* @param {string} dir
|
||||
* @param {string} file
|
||||
* @private
|
||||
*/
|
||||
|
||||
View.prototype.resolve = function resolve(dir, file) {
|
||||
var ext = this.ext;
|
||||
|
||||
// <path>.<ext>
|
||||
var path = join(dir, file);
|
||||
var stat = tryStat(path);
|
||||
|
||||
if (stat && stat.isFile()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// <path>/index.<ext>
|
||||
path = join(dir, basename(file, ext), 'index' + ext);
|
||||
stat = tryStat(path);
|
||||
|
||||
if (stat && stat.isFile()) {
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a stat, maybe.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {fs.Stats}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function tryStat(path) {
|
||||
debug('stat "%s"', path);
|
||||
|
||||
try {
|
||||
return fs.statSync(path);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
103
package.json
103
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Fast, unopinionated, minimalist web framework",
|
||||
"version": "4.5.0",
|
||||
"version": "4.14.1",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
|
||||
@@ -9,8 +9,12 @@
|
||||
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||
"Guillermo Rauch <rauchg@gmail.com>",
|
||||
"Jonathan Ong <me@jongleberry.com>",
|
||||
"Roman Shtylman <shtylman+expressjs@gmail.com"
|
||||
"Roman Shtylman <shtylman+expressjs@gmail.com>",
|
||||
"Young Jae Sim <hanul@hanul.me>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": "expressjs/express",
|
||||
"homepage": "http://expressjs.com/",
|
||||
"keywords": [
|
||||
"express",
|
||||
"framework",
|
||||
@@ -22,58 +26,67 @@
|
||||
"app",
|
||||
"api"
|
||||
],
|
||||
"repository": "visionmedia/express",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.0.7",
|
||||
"buffer-crc32": "0.2.3",
|
||||
"debug": "1.0.2",
|
||||
"depd": "0.3.0",
|
||||
"escape-html": "1.0.1",
|
||||
"finalhandler": "0.0.2",
|
||||
"media-typer": "0.2.0",
|
||||
"methods": "1.0.1",
|
||||
"parseurl": "1.0.1",
|
||||
"proxy-addr": "1.0.1",
|
||||
"range-parser": "1.0.0",
|
||||
"send": "0.5.0",
|
||||
"serve-static": "~1.3.0",
|
||||
"type-is": "~1.3.2",
|
||||
"vary": "0.1.0",
|
||||
"cookie": "0.1.2",
|
||||
"fresh": "0.2.2",
|
||||
"cookie-signature": "1.0.4",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"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.2.0",
|
||||
"depd": "~1.1.0",
|
||||
"encodeurl": "~1.0.1",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.7.0",
|
||||
"finalhandler": "0.5.1",
|
||||
"fresh": "0.3.0",
|
||||
"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.3",
|
||||
"qs": "6.2.0",
|
||||
"range-parser": "~1.2.0",
|
||||
"send": "0.14.2",
|
||||
"serve-static": "~1.11.2",
|
||||
"type-is": "~1.6.14",
|
||||
"utils-merge": "1.0.0",
|
||||
"qs": "0.6.6",
|
||||
"path-to-regexp": "0.1.2"
|
||||
"vary": "~1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.1",
|
||||
"istanbul": "0.2.14",
|
||||
"mocha": "~1.20.1",
|
||||
"should": "~4.0.4",
|
||||
"supertest": "~0.13.0",
|
||||
"connect-redis": "~2.0.0",
|
||||
"ejs": "~1.0.0",
|
||||
"jade": "~1.3.1",
|
||||
"marked": "0.3.2",
|
||||
"hjs": "~0.0.6",
|
||||
"body-parser": "~1.4.3",
|
||||
"cookie-parser": "~1.3.1",
|
||||
"express-session": "~1.6.1",
|
||||
"method-override": "2.0.2",
|
||||
"multiparty": "~3.3.0",
|
||||
"morgan": "1.1.1",
|
||||
"vhost": "2.0.0"
|
||||
"after": "0.8.2",
|
||||
"body-parser": "1.16.0",
|
||||
"cookie-parser": "~1.4.3",
|
||||
"ejs": "2.5.5",
|
||||
"express-session": "1.15.0",
|
||||
"istanbul": "0.4.5",
|
||||
"marked": "0.3.6",
|
||||
"method-override": "~2.3.6",
|
||||
"mocha": "3.2.0",
|
||||
"morgan": "~1.7.0",
|
||||
"multiparty": "4.1.3",
|
||||
"should": "11.2.0",
|
||||
"supertest": "1.2.0",
|
||||
"connect-redis": "~2.4.1",
|
||||
"cookie-session": "~1.2.0",
|
||||
"jade": "~1.11.0",
|
||||
"vhost": "~3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"History.md",
|
||||
"Readme.md",
|
||||
"index.js",
|
||||
"lib/"
|
||||
],
|
||||
"scripts": {
|
||||
"prepublish": "npm prune",
|
||||
"test": "mocha --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
|
||||
"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-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/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../');
|
||||
|
||||
var app = express()
|
||||
, blog = express()
|
||||
, admin = express();
|
||||
|
||||
blog.use('/admin', admin);
|
||||
app.use('/blog', blog);
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
app.locals.self = true;
|
||||
|
||||
app.get('/render', function(req, res){
|
||||
res.render('hello');
|
||||
});
|
||||
|
||||
admin.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
blog.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
app.get('/json', function(req, res){
|
||||
res.send({ name: 'Tobi', role: 'admin' });
|
||||
});
|
||||
|
||||
app.get('/json/:n', function(req, res){
|
||||
var n = ~~req.params.n;
|
||||
var arr = [];
|
||||
var obj = { name: 'Tobi', role: 'admin' };
|
||||
while (n--) arr.push(obj);
|
||||
res.send(arr);
|
||||
});
|
||||
|
||||
function foo(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
app.get('/middleware', foo, foo, foo, foo, function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
var n = 100;
|
||||
while (n--) {
|
||||
app.get('/foo', foo, foo, function(req, res){
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/match', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
app.listen(8000);
|
||||
@@ -1 +0,0 @@
|
||||
p Hello
|
||||
139
test/Route.js
139
test/Route.js
@@ -1,4 +1,5 @@
|
||||
|
||||
var after = require('after');
|
||||
var should = require('should');
|
||||
var express = require('../')
|
||||
, Route = express.Route
|
||||
@@ -6,170 +7,190 @@ var express = require('../')
|
||||
, 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){
|
||||
var req = { method: 'GET', url: '/' };
|
||||
var route = new Route('/foo');
|
||||
|
||||
route.all(function(req, res, next) {
|
||||
assert.equal(req.a, 1);
|
||||
assert.equal(res.b, 2);
|
||||
req.called = true;
|
||||
next();
|
||||
});
|
||||
|
||||
route.dispatch({ a:1, method: 'GET' }, { b:2 }, done);
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.ok;
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should handle VERBS', function(done) {
|
||||
var route = new Route('/foo');
|
||||
|
||||
var count = 0;
|
||||
var route = new Route('/foo');
|
||||
var cb = after(methods.length, function (err) {
|
||||
if (err) return done(err);
|
||||
count.should.equal(methods.length);
|
||||
done();
|
||||
});
|
||||
|
||||
route.all(function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
|
||||
methods.forEach(function testMethod(method) {
|
||||
route.dispatch({ method: method }, {});
|
||||
var req = { method: method, url: '/' };
|
||||
route.dispatch(req, {}, cb);
|
||||
});
|
||||
|
||||
assert.equal(count, methods.length);
|
||||
done();
|
||||
})
|
||||
|
||||
it('should stack', function(done) {
|
||||
var req = { count: 0, method: 'GET', url: '/' };
|
||||
var route = new Route('/foo');
|
||||
|
||||
var count = 0;
|
||||
route.all(function(req, res, next) {
|
||||
count++;
|
||||
req.count++;
|
||||
next();
|
||||
});
|
||||
|
||||
route.all(function(req, res, next) {
|
||||
count++;
|
||||
req.count++;
|
||||
next();
|
||||
});
|
||||
|
||||
route.dispatch({ method: 'GET' }, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
count++;
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
req.count.should.equal(2);
|
||||
done();
|
||||
});
|
||||
|
||||
assert.equal(count, 3);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('.VERB', function(){
|
||||
it('should support .get', function(done){
|
||||
var req = { method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
var count = 0;
|
||||
route.get(function(req, res, next) {
|
||||
count++;
|
||||
req.called = true;
|
||||
next();
|
||||
})
|
||||
|
||||
route.dispatch({ method: 'GET' }, {});
|
||||
assert(count);
|
||||
done();
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.ok;
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should limit to just .VERB', function(done){
|
||||
var req = { method: 'POST', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
route.get(function(req, res, next) {
|
||||
assert(false);
|
||||
done();
|
||||
throw new Error('not me!');
|
||||
})
|
||||
|
||||
route.post(function(req, res, next) {
|
||||
assert(true);
|
||||
req.called = true;
|
||||
next();
|
||||
})
|
||||
|
||||
route.dispatch({ method: 'post' }, {});
|
||||
done();
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.called).be.true;
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('should allow fallthrough', function(done){
|
||||
var req = { order: '', method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
var order = '';
|
||||
route.get(function(req, res, next) {
|
||||
order += 'a';
|
||||
req.order += 'a';
|
||||
next();
|
||||
})
|
||||
|
||||
route.all(function(req, res, next) {
|
||||
order += 'b';
|
||||
req.order += 'b';
|
||||
next();
|
||||
});
|
||||
|
||||
route.get(function(req, res, next) {
|
||||
order += 'c';
|
||||
req.order += 'c';
|
||||
next();
|
||||
})
|
||||
|
||||
route.dispatch({ method: 'get' }, {});
|
||||
assert.equal(order, 'abc');
|
||||
done();
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
req.order.should.equal('abc');
|
||||
done();
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', function(){
|
||||
it('should handle errors via arity 4 functions', function(done){
|
||||
var req = { order: '', method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
var order = '';
|
||||
route.all(function(req, res, next){
|
||||
next(new Error('foobar'));
|
||||
});
|
||||
|
||||
route.all(function(req, res, next){
|
||||
order += '0';
|
||||
req.order += '0';
|
||||
next();
|
||||
});
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
order += 'a';
|
||||
req.order += 'a';
|
||||
next(err);
|
||||
});
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
assert.equal(err.message, 'foobar');
|
||||
assert.equal(order, 'a');
|
||||
route.dispatch(req, {}, function (err) {
|
||||
should(err).be.ok;
|
||||
should(err.message).equal('foobar');
|
||||
req.order.should.equal('a');
|
||||
done();
|
||||
});
|
||||
|
||||
route.dispatch({ method: 'get' }, {});
|
||||
})
|
||||
|
||||
it('should handle throw', function(done) {
|
||||
var req = { order: '', method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
var order = '';
|
||||
route.all(function(req, res, next){
|
||||
throw new Error('foobar');
|
||||
});
|
||||
|
||||
route.all(function(req, res, next){
|
||||
order += '0';
|
||||
req.order += '0';
|
||||
next();
|
||||
});
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
order += 'a';
|
||||
req.order += 'a';
|
||||
next(err);
|
||||
});
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
assert.equal(err.message, 'foobar');
|
||||
assert.equal(order, 'a');
|
||||
route.dispatch(req, {}, function (err) {
|
||||
should(err).be.ok;
|
||||
should(err.message).equal('foobar');
|
||||
req.order.should.equal('a');
|
||||
done();
|
||||
});
|
||||
|
||||
route.dispatch({ method: 'get' }, {});
|
||||
});
|
||||
|
||||
it('should handle throwing inside error handlers', function(done) {
|
||||
var req = { method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
route.get(function(req, res, next){
|
||||
@@ -181,21 +202,26 @@ describe('Route', function(){
|
||||
});
|
||||
|
||||
route.get(function(err, req, res, next){
|
||||
assert.equal(err.message, 'oops');
|
||||
done();
|
||||
req.message = err.message;
|
||||
next();
|
||||
});
|
||||
|
||||
route.dispatch({ url: '/', method: 'GET' }, {});
|
||||
route.dispatch(req, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
should(req.message).equal('oops');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle throw in .all', function(done) {
|
||||
var req = { method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
route.all(function(req, res, next){
|
||||
throw new Error('boom!');
|
||||
});
|
||||
|
||||
route.dispatch({ url: '/', method: 'GET' }, {}, function(err){
|
||||
route.dispatch(req, {}, function(err){
|
||||
should(err).be.ok;
|
||||
err.message.should.equal('boom!');
|
||||
done();
|
||||
@@ -203,6 +229,7 @@ describe('Route', function(){
|
||||
});
|
||||
|
||||
it('should handle single error handler', function(done) {
|
||||
var req = { method: 'GET', url: '/' };
|
||||
var route = new Route('');
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
@@ -210,7 +237,7 @@ describe('Route', function(){
|
||||
true.should.be.false;
|
||||
});
|
||||
|
||||
route.dispatch({ url: '/', method: 'GET' }, {}, done);
|
||||
route.dispatch(req, {}, done);
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
184
test/Router.js
184
test/Router.js
@@ -43,6 +43,31 @@ describe('Router', function(){
|
||||
router.handle({ url: '/test/route', method: 'GET' }, { end: done });
|
||||
});
|
||||
|
||||
it('should handle blank URL', function(done){
|
||||
var router = new Router();
|
||||
|
||||
router.use(function (req, res) {
|
||||
false.should.be.true;
|
||||
});
|
||||
|
||||
router.handle({ url: '', 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();
|
||||
@@ -174,6 +199,128 @@ describe('Router', function(){
|
||||
});
|
||||
})
|
||||
|
||||
describe('FQDN', function () {
|
||||
it('should not obscure FQDNs', function (done) {
|
||||
var request = { hit: 0, url: 'http://example.com/foo', method: 'GET' };
|
||||
var router = new Router();
|
||||
|
||||
router.use(function (req, res, next) {
|
||||
assert.equal(req.hit++, 0);
|
||||
assert.equal(req.url, 'http://example.com/foo');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle(request, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(request.hit, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore FQDN in search', function (done) {
|
||||
var request = { hit: 0, url: '/proxy?url=http://example.com/blog/post/1', method: 'GET' };
|
||||
var router = new Router();
|
||||
|
||||
router.use('/proxy', function (req, res, next) {
|
||||
assert.equal(req.hit++, 0);
|
||||
assert.equal(req.url, '/?url=http://example.com/blog/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle(request, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(request.hit, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore FQDN in path', function (done) {
|
||||
var request = { hit: 0, url: '/proxy/http://example.com/blog/post/1', method: 'GET' };
|
||||
var router = new Router();
|
||||
|
||||
router.use('/proxy', function (req, res, next) {
|
||||
assert.equal(req.hit++, 0);
|
||||
assert.equal(req.url, '/http://example.com/blog/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle(request, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(request.hit, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should adjust FQDN req.url', function (done) {
|
||||
var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };
|
||||
var router = new Router();
|
||||
|
||||
router.use('/blog', function (req, res, next) {
|
||||
assert.equal(req.hit++, 0);
|
||||
assert.equal(req.url, 'http://example.com/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle(request, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(request.hit, 1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should adjust FQDN req.url with multiple handlers', function (done) {
|
||||
var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };
|
||||
var router = new Router();
|
||||
|
||||
router.use(function (req, res, next) {
|
||||
assert.equal(req.hit++, 0);
|
||||
assert.equal(req.url, 'http://example.com/blog/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/blog', function (req, res, next) {
|
||||
assert.equal(req.hit++, 1);
|
||||
assert.equal(req.url, 'http://example.com/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle(request, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(request.hit, 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should adjust FQDN req.url with multiple routed handlers', function (done) {
|
||||
var request = { hit: 0, url: 'http://example.com/blog/post/1', method: 'GET' };
|
||||
var router = new Router();
|
||||
|
||||
router.use('/blog', function (req, res, next) {
|
||||
assert.equal(req.hit++, 0);
|
||||
assert.equal(req.url, 'http://example.com/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/blog', function (req, res, next) {
|
||||
assert.equal(req.hit++, 1);
|
||||
assert.equal(req.url, 'http://example.com/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.use(function (req, res, next) {
|
||||
assert.equal(req.hit++, 2);
|
||||
assert.equal(req.url, 'http://example.com/blog/post/1');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle(request, {}, function (err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(request.hit, 3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
describe('.all', function() {
|
||||
it('should support using .all to capture all http verbs', function(done){
|
||||
var router = new Router();
|
||||
@@ -192,6 +339,43 @@ describe('Router', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('.use', function() {
|
||||
it('should require arguments', function(){
|
||||
var router = new Router();
|
||||
router.use.bind(router).should.throw(/requires middleware function/)
|
||||
})
|
||||
|
||||
it('should not accept non-functions', function(){
|
||||
var router = new Router();
|
||||
router.use.bind(router, '/', 'hello').should.throw(/requires middleware function.*string/)
|
||||
router.use.bind(router, '/', 5).should.throw(/requires middleware function.*number/)
|
||||
router.use.bind(router, '/', null).should.throw(/requires middleware function.*Null/)
|
||||
router.use.bind(router, '/', new Date()).should.throw(/requires middleware function.*Date/)
|
||||
})
|
||||
|
||||
it('should accept array of middleware', function(done){
|
||||
var count = 0;
|
||||
var router = new Router();
|
||||
|
||||
function fn1(req, res, next){
|
||||
assert.equal(++count, 1);
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next){
|
||||
assert.equal(++count, 2);
|
||||
next();
|
||||
}
|
||||
|
||||
router.use([fn1, fn2], function(req, res){
|
||||
assert.equal(++count, 3);
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/foo', method: 'GET' }, {}, function(){});
|
||||
})
|
||||
})
|
||||
|
||||
describe('.param', function() {
|
||||
it('should call param function when routing VERBS', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var app = require('../../examples/auth/app')
|
||||
var app = require('../../examples/auth')
|
||||
var request = require('supertest')
|
||||
|
||||
function getCookie(res) {
|
||||
|
||||
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 0 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 0 times\n', function (err, res) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Cookie', getCookies(res))
|
||||
.expect(200, 'viewed 1 times\n', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function getCookies(res) {
|
||||
return res.headers['set-cookie'].map(function (val) {
|
||||
return val.split(';')[0]
|
||||
}).join('; ');
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
var app = require('../../examples/cookies/app')
|
||||
var app = require('../../examples/cookies')
|
||||
, request = require('supertest');
|
||||
var utils = require('../support/utils');
|
||||
|
||||
describe('cookies', function(){
|
||||
describe('GET /', function(){
|
||||
@@ -13,10 +14,8 @@ describe('cookies', function(){
|
||||
it('should respond with no cookies', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.not.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
.expect(utils.shouldNotHaveHeader('Set-Cookie'))
|
||||
.expect(200, done)
|
||||
})
|
||||
|
||||
it('should respond to cookie', function(done){
|
||||
@@ -57,20 +56,16 @@ describe('cookies', function(){
|
||||
.post('/')
|
||||
.type('urlencoded')
|
||||
.send({ remember: 1 })
|
||||
.expect(302, function(err, res){
|
||||
res.headers.should.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
.expect('Set-Cookie', /remember=1/)
|
||||
.expect(302, done)
|
||||
})
|
||||
|
||||
it('should no set cookie w/o reminder', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.send({})
|
||||
.expect(302, function(err, res){
|
||||
res.headers.should.not.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
.expect(utils.shouldNotHaveHeader('Set-Cookie'))
|
||||
.expect(302, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
var app = require('../../examples/downloads/app')
|
||||
var app = require('../../examples/downloads')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('downloads', function(){
|
||||
@@ -15,11 +15,8 @@ describe('downloads', function(){
|
||||
it('should have a download header', function(done){
|
||||
request(app)
|
||||
.get('/files/amazing.txt')
|
||||
.end(function(err, res){
|
||||
res.status.should.equal(200);
|
||||
res.headers.should.have.property('content-disposition', 'attachment; filename="amazing.txt"')
|
||||
done()
|
||||
})
|
||||
.expect('Content-Disposition', 'attachment; filename="amazing.txt"')
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
44
test/acceptance/multi-router.js
Normal file
44
test/acceptance/multi-router.js
Normal file
@@ -0,0 +1,44 @@
|
||||
var app = require('../../examples/multi-router')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('multi-router', function(){
|
||||
describe('GET /',function(){
|
||||
it('should respond with root handler', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'Hello form root route.', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/v1/',function(){
|
||||
it('should respond with APIv1 root handler', function(done){
|
||||
request(app)
|
||||
.get('/api/v1/')
|
||||
.expect(200, 'Hello from APIv1 root route.', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/v1/users',function(){
|
||||
it('should respond with users from APIv1', function(done){
|
||||
request(app)
|
||||
.get('/api/v1/users')
|
||||
.expect(200, 'List of APIv1 users.', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/v2/',function(){
|
||||
it('should respond with APIv2 root handler', function(done){
|
||||
request(app)
|
||||
.get('/api/v2/')
|
||||
.expect(200, 'Hello from APIv2 root route.', done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /api/v2/users',function(){
|
||||
it('should respond with users from APIv2', function(done){
|
||||
request(app)
|
||||
.get('/api/v2/users')
|
||||
.expect(200, 'List of APIv2 users.', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -35,7 +35,7 @@ describe('mvc', function(){
|
||||
.put('/pet/3')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ pet: { name: 'Boots' } })
|
||||
.end(function(err, res){
|
||||
.expect(302, function (err, res) {
|
||||
if (err) return done(err);
|
||||
request(app)
|
||||
.get('/pet/3/edit')
|
||||
@@ -105,7 +105,7 @@ describe('mvc', function(){
|
||||
.put('/user/1')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ user: { name: 'Tobo' }})
|
||||
.end(function(err, res){
|
||||
.expect(302, function (err, res) {
|
||||
if (err) return done(err);
|
||||
request(app)
|
||||
.get('/user/1/edit')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var app = require('../../examples/params/app')
|
||||
var app = require('../../examples/params')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('params', function(){
|
||||
@@ -21,8 +21,8 @@ describe('params', function(){
|
||||
describe('GET /user/9', function(){
|
||||
it('should fail to find user', function(done){
|
||||
request(app)
|
||||
.get('/user/9')
|
||||
.expect(/failed to find user/,done)
|
||||
.get('/user/9')
|
||||
.expect(404, /failed to find user/, done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -37,8 +37,8 @@ describe('params', function(){
|
||||
describe('GET /users/foo-bar', function(){
|
||||
it('should fail integer parsing', function(done){
|
||||
request(app)
|
||||
.get('/users/foo-bar')
|
||||
.expect(/failed to parseInt foo/,done)
|
||||
.get('/users/foo-bar')
|
||||
.expect(400, /failed to parseInt foo/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var app = require('../../examples/resource/app')
|
||||
var app = require('../../examples/resource')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('resource', function(){
|
||||
|
||||
97
test/acceptance/route-separation.js
Normal file
97
test/acceptance/route-separation.js
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
var app = require('../../examples/route-separation')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('route-separation', function () {
|
||||
describe('GET /', function () {
|
||||
it('should respond with index', function (done) {
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, /Route Separation Example/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users', function () {
|
||||
it('should list users', function (done) {
|
||||
request(app)
|
||||
.get('/users')
|
||||
.expect(/TJ/)
|
||||
.expect(/Tobi/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/:id', function () {
|
||||
it('should get a user', function (done) {
|
||||
request(app)
|
||||
.get('/user/0')
|
||||
.expect(200, /Viewing user TJ/, done)
|
||||
})
|
||||
|
||||
it('should 404 on missing user', function (done) {
|
||||
request(app)
|
||||
.get('/user/10')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/:id/view', function () {
|
||||
it('should get a user', function (done) {
|
||||
request(app)
|
||||
.get('/user/0/view')
|
||||
.expect(200, /Viewing user TJ/, done)
|
||||
})
|
||||
|
||||
it('should 404 on missing user', function (done) {
|
||||
request(app)
|
||||
.get('/user/10/view')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/:id/edit', function () {
|
||||
it('should get a user to edit', function (done) {
|
||||
request(app)
|
||||
.get('/user/0/edit')
|
||||
.expect(200, /Editing user TJ/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /user/:id/edit', function () {
|
||||
it('should edit a user', function (done) {
|
||||
request(app)
|
||||
.put('/user/0/edit')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ user: { name: 'TJ', email: 'tj-invalid@vision-media.ca' } })
|
||||
.expect(302, function (err) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/user/0')
|
||||
.expect(200, /tj-invalid@vision-media\.ca/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /user/:id/edit?_method=PUT', function () {
|
||||
it('should edit a user', function (done) {
|
||||
request(app)
|
||||
.post('/user/1/edit?_method=PUT')
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.send({ user: { name: 'Tobi', email: 'tobi-invalid@vision-media.ca' } })
|
||||
.expect(302, function (err) {
|
||||
if (err) return done(err)
|
||||
request(app)
|
||||
.get('/user/1')
|
||||
.expect(200, /tobi-invalid@vision-media\.ca/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /posts', function () {
|
||||
it('should get a list of posts', function (done) {
|
||||
request(app)
|
||||
.get('/posts')
|
||||
.expect(200, /Posts/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -53,7 +53,7 @@ describe('web-service', function(){
|
||||
.get('/api/repos?api-key=foo')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(/"name":"express"/)
|
||||
.expect(/"url":"http:\/\/github.com\/visionmedia\/express"/)
|
||||
.expect(/"url":"http:\/\/github.com\/expressjs\/express"/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('app.all()', function(){
|
||||
});
|
||||
})
|
||||
|
||||
it('should ', function(done){
|
||||
it('should run the callback for a method just once', function(done){
|
||||
var app = express()
|
||||
, n = 0;
|
||||
|
||||
|
||||
15
test/app.js
15
test/app.js
@@ -12,7 +12,7 @@ describe('app', function(){
|
||||
|
||||
it('should be callable', function(){
|
||||
var app = express();
|
||||
assert(typeof app, 'function');
|
||||
assert.equal(typeof app, 'function');
|
||||
})
|
||||
|
||||
it('should 404 without routes', function(done){
|
||||
@@ -39,16 +39,19 @@ describe('app.parent', function(){
|
||||
|
||||
describe('app.mountpath', function(){
|
||||
it('should return the mounted path', function(){
|
||||
var app = express()
|
||||
, blog = express()
|
||||
, blogAdmin = express();
|
||||
var admin = express();
|
||||
var app = express();
|
||||
var blog = express();
|
||||
var fallback = express();
|
||||
|
||||
app.use('/blog', blog);
|
||||
blog.use('/admin', blogAdmin);
|
||||
app.use(fallback);
|
||||
blog.use('/admin', admin);
|
||||
|
||||
admin.mountpath.should.equal('/admin');
|
||||
app.mountpath.should.equal('/');
|
||||
blog.mountpath.should.equal('/blog');
|
||||
blogAdmin.mountpath.should.equal('/admin');
|
||||
fallback.mountpath.should.equal('/');
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -12,8 +12,22 @@ describe('OPTIONS', function(){
|
||||
|
||||
request(app)
|
||||
.options('/users')
|
||||
.expect('GET,PUT')
|
||||
.expect('Allow', 'GET,PUT', done);
|
||||
.expect('Allow', 'GET,HEAD,PUT')
|
||||
.expect(200, 'GET,HEAD,PUT', done);
|
||||
})
|
||||
|
||||
it('should only include each method once', function(done){
|
||||
var app = express();
|
||||
|
||||
app.del('/', function(){});
|
||||
app.get('/users', function(req, res){});
|
||||
app.put('/users', function(req, res){});
|
||||
app.get('/users', function(req, res){});
|
||||
|
||||
request(app)
|
||||
.options('/users')
|
||||
.expect('Allow', 'GET,HEAD,PUT')
|
||||
.expect(200, 'GET,HEAD,PUT', done);
|
||||
})
|
||||
|
||||
it('should not be affected by app.all', function(done){
|
||||
@@ -30,8 +44,8 @@ describe('OPTIONS', function(){
|
||||
request(app)
|
||||
.options('/users')
|
||||
.expect('x-hit', '1')
|
||||
.expect('allow', 'GET,PUT')
|
||||
.expect(200, 'GET,PUT', done);
|
||||
.expect('Allow', 'GET,HEAD,PUT')
|
||||
.expect(200, 'GET,HEAD,PUT', done);
|
||||
})
|
||||
|
||||
it('should not respond if the path is not defined', function(done){
|
||||
@@ -54,8 +68,30 @@ describe('OPTIONS', function(){
|
||||
|
||||
request(app)
|
||||
.options('/other')
|
||||
.expect('GET')
|
||||
.expect('Allow', 'GET', done);
|
||||
.expect('Allow', 'GET,HEAD')
|
||||
.expect(200, 'GET,HEAD', done);
|
||||
})
|
||||
|
||||
describe('when error occurs in response handler', function () {
|
||||
it('should pass error to callback', function (done) {
|
||||
var app = express();
|
||||
var router = express.Router();
|
||||
|
||||
router.get('/users', function(req, res){});
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.writeHead(200);
|
||||
next();
|
||||
});
|
||||
app.use(router);
|
||||
app.use(function (err, req, res, next) {
|
||||
res.end('true');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.options('/users')
|
||||
.expect(200, 'true', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -237,6 +237,27 @@ describe('app', function(){
|
||||
.expect(500, done);
|
||||
})
|
||||
|
||||
it('should catch thrown secondary error', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('id', function(req, res, next, val){
|
||||
process.nextTick(next);
|
||||
});
|
||||
|
||||
app.param('id', function(req, res, next, id){
|
||||
throw new Error('err!');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
var id = req.params.id;
|
||||
res.send('' + id);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.expect(500, done);
|
||||
})
|
||||
|
||||
it('should defer to next route', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -282,5 +303,65 @@ describe('app', function(){
|
||||
.get('/user/new')
|
||||
.expect('get.new', done);
|
||||
})
|
||||
|
||||
it('should not call when values differ on error', function(done) {
|
||||
var app = express();
|
||||
var called = 0;
|
||||
var count = 0;
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
called++;
|
||||
if (user === 'foo') throw new Error('err!');
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/:user/bob', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(err, req, res, next) {
|
||||
res.status(500);
|
||||
res.send([count, called, err.message].join(' '));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bob')
|
||||
.expect(500, '0 1 err!', done)
|
||||
});
|
||||
|
||||
it('should call when values differ when using "next"', function(done) {
|
||||
var app = express();
|
||||
var called = 0;
|
||||
var count = 0;
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
called++;
|
||||
if (user === 'foo') return next('route');
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/:user/bob', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.use(function(req, res) {
|
||||
res.end([count, called, req.user].join(' '));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bob')
|
||||
.expect('1 2 bob', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
|
||||
var express = require('../');
|
||||
var express = require('..');
|
||||
var tmpl = require('./support/tmpl');
|
||||
|
||||
describe('app', function(){
|
||||
describe('.render(name, fn)', function(){
|
||||
it('should support absolute paths', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render(__dirname + '/fixtures/user.jade', function(err, str){
|
||||
app.render(__dirname + '/fixtures/user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
@@ -16,9 +17,9 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should support absolute paths with "view engine"', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render(__dirname + '/fixtures/user', function(err, str){
|
||||
@@ -29,12 +30,12 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should expose app.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render('user.jade', function(err, str){
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
@@ -42,12 +43,12 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should support index.<engine>', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
|
||||
app.render('blog/post', function(err, str){
|
||||
app.render('blog/post', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<h1>blog post</h1>');
|
||||
done();
|
||||
@@ -77,10 +78,11 @@ describe('app', function(){
|
||||
|
||||
describe('when the file does not exist', function(){
|
||||
it('should provide a helpful error', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.render('rawr.jade', function(err){
|
||||
err.message.should.equal('Failed to lookup view "rawr.jade" in views directory "' + __dirname + '/fixtures"');
|
||||
app.render('rawr.tmpl', function (err) {
|
||||
err.message.should.equal('Failed to lookup view "rawr.tmpl" in views directory "' + __dirname + '/fixtures"');
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -88,11 +90,11 @@ describe('app', function(){
|
||||
|
||||
describe('when an error occurs', function(){
|
||||
it('should invoke the callback', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.render('user.jade', function(err, str){
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
// nextTick to prevent cyclic
|
||||
process.nextTick(function(){
|
||||
err.message.should.match(/Cannot read property '[^']+' of undefined/);
|
||||
@@ -104,11 +106,11 @@ describe('app', function(){
|
||||
|
||||
describe('when an extension is given', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.render('email.jade', function(err, str){
|
||||
app.render('email.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>This is an email</p>');
|
||||
done();
|
||||
@@ -118,9 +120,9 @@ describe('app', function(){
|
||||
|
||||
describe('when "view engine" is given', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'tmpl');
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.render('email', function(err, str){
|
||||
@@ -131,6 +133,64 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "views" is given', function(){
|
||||
it('should lookup the file in the path', function(done){
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures/default_layout');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('when array of paths', function(){
|
||||
it('should lookup the file in the path', function(done){
|
||||
var app = createApp();
|
||||
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
|
||||
|
||||
app.set('views', views);
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render('user.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<span>tobi</span>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should lookup in later paths until found', function(done){
|
||||
var app = createApp();
|
||||
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
|
||||
|
||||
app.set('views', views);
|
||||
app.locals.name = 'tobi';
|
||||
|
||||
app.render('name.tmpl', function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should error if file does not exist', function(done){
|
||||
var app = createApp();
|
||||
var views = [__dirname + '/fixtures/local_layout', __dirname + '/fixtures/default_layout'];
|
||||
|
||||
app.set('views', views);
|
||||
app.locals.name = 'tobi';
|
||||
|
||||
app.render('pet.tmpl', function (err, str) {
|
||||
err.message.should.equal('Failed to lookup view "pet.tmpl" in views directories "' + __dirname + '/fixtures/local_layout" or "' + __dirname + '/fixtures/default_layout"');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when a "view" constructor is given', function(){
|
||||
it('should create an instance of it', function(done){
|
||||
var app = express();
|
||||
@@ -219,13 +279,13 @@ describe('app', function(){
|
||||
|
||||
describe('.render(name, options, fn)', function(){
|
||||
it('should render the template', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
var user = { name: 'tobi' };
|
||||
|
||||
app.render('user.jade', { user: user }, function(err, str){
|
||||
app.render('user.tmpl', { user: user }, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
@@ -233,12 +293,12 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should expose app.locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.render('user.jade', {}, function(err, str){
|
||||
app.render('user.tmpl', {}, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>tobi</p>');
|
||||
done();
|
||||
@@ -246,13 +306,13 @@ describe('app', function(){
|
||||
})
|
||||
|
||||
it('should give precedence to app.render() locals', function(done){
|
||||
var app = express();
|
||||
var app = createApp();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
var jane = { name: 'jane' };
|
||||
|
||||
app.render('user.jade', { user: jane }, function(err, str){
|
||||
app.render('user.tmpl', { user: jane }, function (err, str) {
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>jane</p>');
|
||||
done();
|
||||
@@ -292,3 +352,11 @@ describe('app', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function createApp() {
|
||||
var app = express();
|
||||
|
||||
app.engine('.tmpl', tmpl);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
var after = require('after');
|
||||
var express = require('../')
|
||||
, request = require('supertest')
|
||||
, assert = require('assert')
|
||||
@@ -10,7 +11,7 @@ describe('app.router', function(){
|
||||
var router = new express.Router();
|
||||
|
||||
function handler1(req, res, next){
|
||||
res.setHeader('x-user-id', req.params.id);
|
||||
res.setHeader('x-user-id', String(req.params.id));
|
||||
next()
|
||||
}
|
||||
|
||||
@@ -19,7 +20,7 @@ describe('app.router', function(){
|
||||
}
|
||||
|
||||
router.use(function(req, res, next){
|
||||
res.setHeader('x-router', req.params.id);
|
||||
res.setHeader('x-router', String(req.params.id));
|
||||
next();
|
||||
});
|
||||
|
||||
@@ -32,7 +33,7 @@ describe('app.router', function(){
|
||||
.expect(200, '1', done);
|
||||
})
|
||||
|
||||
describe('methods supported', function(){
|
||||
describe('methods', function(){
|
||||
methods.concat('del').forEach(function(method){
|
||||
if (method === 'connect') return;
|
||||
|
||||
@@ -58,6 +59,35 @@ describe('app.router', function(){
|
||||
app[method].bind(app, '/', 3).should.throw(/Number/);
|
||||
})
|
||||
});
|
||||
|
||||
it('should re-route when method is altered', function (done) {
|
||||
var app = express();
|
||||
var cb = after(3, done);
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
if (req.method !== 'POST') return next();
|
||||
req.method = 'DELETE';
|
||||
res.setHeader('X-Method-Altered', '1');
|
||||
next();
|
||||
});
|
||||
|
||||
app.delete('/', function (req, res) {
|
||||
res.end('deleted everything');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, 'Cannot GET /\n', cb);
|
||||
|
||||
request(app)
|
||||
.delete('/')
|
||||
.expect(200, 'deleted everything', cb);
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.expect('X-Method-Altered', '1')
|
||||
.expect(200, 'deleted everything', cb);
|
||||
});
|
||||
})
|
||||
|
||||
describe('decode querystring', function(){
|
||||
@@ -290,6 +320,22 @@ describe('app.router', function(){
|
||||
.expect(200, '[["0","10"],["1","tj"],["2","profile"]]', done);
|
||||
})
|
||||
|
||||
it('should merge numeric indices req.params when parent has same number', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/name:(\\w+)', function(req, res){
|
||||
var keys = Object.keys(req.params).sort();
|
||||
res.send(keys.map(function(k){ return [k, req.params[k]] }));
|
||||
});
|
||||
|
||||
app.use('/user/id:(\\d+)', router);
|
||||
|
||||
request(app)
|
||||
.get('/user/id:10/name:tj')
|
||||
.expect(200, '[["0","10"],["1","tj"]]', done);
|
||||
})
|
||||
|
||||
it('should ignore invalid incoming req.params', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router({ mergeParams: true });
|
||||
@@ -308,6 +354,26 @@ describe('app.router', function(){
|
||||
.get('/user/tj')
|
||||
.expect(200, '[["name","tj"]]', done);
|
||||
})
|
||||
|
||||
it('should restore req.params', function(done){
|
||||
var app = express();
|
||||
var router = new express.Router({ mergeParams: true });
|
||||
|
||||
router.get('/user:(\\w+)/*', function (req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
app.use('/user/id:(\\d+)', function (req, res, next) {
|
||||
router(req, res, function (err) {
|
||||
var keys = Object.keys(req.params).sort();
|
||||
res.send(keys.map(function(k){ return [k, req.params[k]] }));
|
||||
});
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/id:42/user:tj/profile')
|
||||
.expect(200, '[["0","42"]]', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('trailing slashes', function(){
|
||||
@@ -338,6 +404,46 @@ describe('app.router', function(){
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
it('should pass-though middleware', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('strict routing');
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.setHeader('x-middleware', 'true');
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user/', function(req, res){
|
||||
res.end('tj');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/')
|
||||
.expect('x-middleware', 'true')
|
||||
.expect(200, 'tj', done);
|
||||
})
|
||||
|
||||
it('should pass-though mounted middleware', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('strict routing');
|
||||
|
||||
app.use('/user/', function (req, res, next) {
|
||||
res.setHeader('x-middleware', 'true');
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user/test/', function(req, res){
|
||||
res.end('tj');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/test/')
|
||||
.expect('x-middleware', 'true')
|
||||
.expect(200, 'tj', done);
|
||||
})
|
||||
|
||||
it('should match no slashes', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -352,6 +458,48 @@ describe('app.router', function(){
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
it('should match middleware when omitting the trailing slash', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('strict routing');
|
||||
|
||||
app.use('/user/', function(req, res){
|
||||
res.end('tj');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user')
|
||||
.expect(200, 'tj', done);
|
||||
})
|
||||
|
||||
it('should match middleware', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('strict routing');
|
||||
|
||||
app.use('/user', function(req, res){
|
||||
res.end('tj');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user')
|
||||
.expect(200, 'tj', done);
|
||||
})
|
||||
|
||||
it('should match middleware when adding the trailing slash', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('strict routing');
|
||||
|
||||
app.use('/user', function(req, res){
|
||||
res.end('tj');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/')
|
||||
.expect(200, 'tj', done);
|
||||
})
|
||||
|
||||
it('should fail when omitting the trailing slash', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -518,7 +666,7 @@ describe('app.router', function(){
|
||||
.expect('', done);
|
||||
})
|
||||
|
||||
it('should require a preceeding /', function(done){
|
||||
it('should require a preceding /', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/file/*', function(req, res){
|
||||
@@ -529,6 +677,30 @@ describe('app.router', function(){
|
||||
.get('/file')
|
||||
.expect(404, done);
|
||||
})
|
||||
|
||||
it('should keep correct parameter indexes', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/*/user/:id', function (req, res) {
|
||||
res.send(req.params);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/1/user/2')
|
||||
.expect(200, '{"0":"1","id":"2"}', done);
|
||||
})
|
||||
|
||||
it('should work within arrays', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get(['/user/:id', '/foo/*', '/:bar'], function (req, res) {
|
||||
res.send(req.params.bar);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/test')
|
||||
.expect(200, 'test', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe(':name', function(){
|
||||
@@ -567,6 +739,52 @@ describe('app.router', function(){
|
||||
.get('/user/tj/edit')
|
||||
.expect('editing tj', done);
|
||||
})
|
||||
|
||||
it('should work following a partial capture group', function(done){
|
||||
var app = express();
|
||||
var cb = after(2, done);
|
||||
|
||||
app.get('/user(s)?/:user/:op', function(req, res){
|
||||
res.end(req.params.op + 'ing ' + req.params.user + (req.params[0] ? ' (old)' : ''));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/tj/edit')
|
||||
.expect('editing tj', cb);
|
||||
|
||||
request(app)
|
||||
.get('/users/tj/edit')
|
||||
.expect('editing tj (old)', cb);
|
||||
})
|
||||
|
||||
it('should work inside literal parenthesis', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/:user\\(:op\\)', function(req, res){
|
||||
res.end(req.params.op + 'ing ' + req.params.user);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/tj(edit)')
|
||||
.expect('editing tj', done);
|
||||
})
|
||||
|
||||
it('should work in array of paths', function(done){
|
||||
var app = express();
|
||||
var cb = after(2, done);
|
||||
|
||||
app.get(['/user/:user/poke', '/user/:user/pokes'], function(req, res){
|
||||
res.end('poking ' + req.params.user);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/tj/poke')
|
||||
.expect('poking tj', cb);
|
||||
|
||||
request(app)
|
||||
.get('/user/tj/pokes')
|
||||
.expect('poking tj', cb);
|
||||
})
|
||||
})
|
||||
|
||||
describe(':name?', function(){
|
||||
@@ -724,6 +942,32 @@ describe('app.router', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should call handler in same route, if exists', function(done){
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
next(new Error('boom!'));
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.send('foo here');
|
||||
}
|
||||
|
||||
function fn3(err, req, res, next) {
|
||||
res.send('route go ' + err.message);
|
||||
}
|
||||
|
||||
app.get('/foo', fn1, fn2, fn3);
|
||||
|
||||
app.use(function (err, req, res, next) {
|
||||
res.end('error!');
|
||||
})
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('route go boom!', done)
|
||||
})
|
||||
})
|
||||
|
||||
it('should allow rewriting of the url', function(done){
|
||||
|
||||
@@ -3,6 +3,22 @@ var express = require('../')
|
||||
|
||||
describe('app', function(){
|
||||
describe('.VERB()', function(){
|
||||
it('should not get invoked without error handler on error', function(done) {
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
next(new Error('boom!'))
|
||||
});
|
||||
|
||||
app.get('/bar', function(req, res){
|
||||
res.send('hello, world!');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/bar')
|
||||
.expect(500, /Error: boom!/, done);
|
||||
});
|
||||
|
||||
it('should only call an error handling routing callback when an error is propagated', function(done){
|
||||
var app = express();
|
||||
|
||||
|
||||
454
test/app.use.js
454
test/app.use.js
@@ -1,6 +1,7 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
var after = require('after');
|
||||
var express = require('..');
|
||||
var request = require('supertest');
|
||||
|
||||
describe('app', function(){
|
||||
it('should emit "mount" when mounted', function(done){
|
||||
@@ -15,11 +16,6 @@ describe('app', function(){
|
||||
app.use(blog);
|
||||
})
|
||||
|
||||
it('should reject numbers', function(){
|
||||
var app = express();
|
||||
app.use.bind(app, 3).should.throw(/Number/);
|
||||
})
|
||||
|
||||
describe('.use(app)', function(){
|
||||
it('should mount the app', function(done){
|
||||
var blog = express()
|
||||
@@ -83,5 +79,449 @@ describe('app', function(){
|
||||
.get('/post/once-upon-a-time')
|
||||
.expect('success', done);
|
||||
})
|
||||
|
||||
it('should support mounted app anywhere', function(done){
|
||||
var cb = after(3, done);
|
||||
var blog = express()
|
||||
, other = express()
|
||||
, app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
blog.get('/', function(req, res){
|
||||
res.end('success');
|
||||
});
|
||||
|
||||
blog.once('mount', function (parent) {
|
||||
parent.should.equal(app);
|
||||
cb();
|
||||
});
|
||||
other.once('mount', function (parent) {
|
||||
parent.should.equal(app);
|
||||
cb();
|
||||
});
|
||||
|
||||
app.use('/post/:article', fn1, other, fn2, blog);
|
||||
|
||||
request(app)
|
||||
.get('/post/once-upon-a-time')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('success', cb);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.use(middleware)', function(){
|
||||
it('should accept multiple arguments', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
app.use(fn1, fn2, function fn3(req, res) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should invoke middleware for all requests', function (done) {
|
||||
var app = express();
|
||||
var cb = after(3, done);
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.send('saw ' + req.method + ' ' + req.url);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'saw GET /', cb);
|
||||
|
||||
request(app)
|
||||
.options('/')
|
||||
.expect(200, 'saw OPTIONS /', cb);
|
||||
|
||||
request(app)
|
||||
.post('/foo')
|
||||
.expect(200, 'saw POST /foo', cb);
|
||||
})
|
||||
|
||||
it('should accept array of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use([fn1, fn2, fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should accept multiple arrays of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use([fn1, fn2], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should accept nested arrays of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use([[fn1], fn2], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.use(path, middleware)', function(){
|
||||
it('should reject missing functions', function () {
|
||||
var app = express();
|
||||
app.use.bind(app, '/').should.throw(/requires middleware function/);
|
||||
})
|
||||
|
||||
it('should reject non-functions as middleware', function () {
|
||||
var app = express();
|
||||
app.use.bind(app, '/', 'hi').should.throw(/requires middleware function.*string/);
|
||||
app.use.bind(app, '/', 5).should.throw(/requires middleware function.*number/);
|
||||
app.use.bind(app, '/', null).should.throw(/requires middleware function.*Null/);
|
||||
app.use.bind(app, '/', new Date()).should.throw(/requires middleware function.*Date/);
|
||||
})
|
||||
|
||||
it('should strip path from req.url', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use('/foo', function (req, res) {
|
||||
res.send('saw ' + req.method + ' ' + req.url);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bar')
|
||||
.expect(200, 'saw GET /bar', done);
|
||||
})
|
||||
|
||||
it('should accept multiple arguments', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
app.use('/foo', fn1, fn2, function fn3(req, res) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should invoke middleware for all requests starting with path', function (done) {
|
||||
var app = express();
|
||||
var cb = after(3, done);
|
||||
|
||||
app.use('/foo', function (req, res) {
|
||||
res.send('saw ' + req.method + ' ' + req.url);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, cb);
|
||||
|
||||
request(app)
|
||||
.post('/foo')
|
||||
.expect(200, 'saw POST /', cb);
|
||||
|
||||
request(app)
|
||||
.post('/foo/bar')
|
||||
.expect(200, 'saw POST /bar', cb);
|
||||
})
|
||||
|
||||
it('should work if path has trailing slash', function (done) {
|
||||
var app = express();
|
||||
var cb = after(3, done);
|
||||
|
||||
app.use('/foo/', function (req, res) {
|
||||
res.send('saw ' + req.method + ' ' + req.url);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, cb);
|
||||
|
||||
request(app)
|
||||
.post('/foo')
|
||||
.expect(200, 'saw POST /', cb);
|
||||
|
||||
request(app)
|
||||
.post('/foo/bar')
|
||||
.expect(200, 'saw POST /bar', cb);
|
||||
})
|
||||
|
||||
it('should accept array of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use('/foo', [fn1, fn2, fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should accept multiple arrays of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use('/foo', [fn1, fn2], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should accept nested arrays of middleware', function (done) {
|
||||
var app = express();
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.end();
|
||||
}
|
||||
|
||||
app.use('/foo', [fn1, [fn2]], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
it('should support array of paths', function (done) {
|
||||
var app = express();
|
||||
var cb = after(3, done);
|
||||
|
||||
app.use(['/foo/', '/bar'], function (req, res) {
|
||||
res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, cb);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect(200, 'saw GET / through /foo', cb);
|
||||
|
||||
request(app)
|
||||
.get('/bar')
|
||||
.expect(200, 'saw GET / through /bar', cb);
|
||||
})
|
||||
|
||||
it('should support array of paths with middleware array', function (done) {
|
||||
var app = express();
|
||||
var cb = after(2, done);
|
||||
|
||||
function fn1(req, res, next) {
|
||||
res.setHeader('x-fn-1', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn2(req, res, next) {
|
||||
res.setHeader('x-fn-2', 'hit');
|
||||
next();
|
||||
}
|
||||
|
||||
function fn3(req, res, next) {
|
||||
res.setHeader('x-fn-3', 'hit');
|
||||
res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);
|
||||
}
|
||||
|
||||
app.use(['/foo/', '/bar'], [[fn1], fn2], [fn3]);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, 'saw GET / through /foo', cb);
|
||||
|
||||
request(app)
|
||||
.get('/bar')
|
||||
.expect('x-fn-1', 'hit')
|
||||
.expect('x-fn-2', 'hit')
|
||||
.expect('x-fn-3', 'hit')
|
||||
.expect(200, 'saw GET / through /bar', cb);
|
||||
})
|
||||
|
||||
it('should support regexp path', function (done) {
|
||||
var app = express();
|
||||
var cb = after(4, done);
|
||||
|
||||
app.use(/^\/[a-z]oo/, function (req, res) {
|
||||
res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(404, cb);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect(200, 'saw GET / through /foo', cb);
|
||||
|
||||
request(app)
|
||||
.get('/zoo/bear')
|
||||
.expect(200, 'saw GET /bear through /zoo/bear', cb);
|
||||
|
||||
request(app)
|
||||
.get('/get/zoo')
|
||||
.expect(404, cb);
|
||||
})
|
||||
|
||||
it('should support empty string path', function (done) {
|
||||
var app = express();
|
||||
|
||||
app.use('', function (req, res) {
|
||||
res.send('saw ' + req.method + ' ' + req.url + ' through ' + req.originalUrl);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, 'saw GET / through /', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
103
test/config.js
103
test/config.js
@@ -1,30 +1,36 @@
|
||||
|
||||
var express = require('../')
|
||||
, assert = require('assert');
|
||||
var assert = require('assert');
|
||||
var express = require('..');
|
||||
|
||||
describe('config', function(){
|
||||
describe('.set()', function(){
|
||||
it('should set a value', function(){
|
||||
describe('config', function () {
|
||||
describe('.set()', function () {
|
||||
it('should set a value', function () {
|
||||
var app = express();
|
||||
app.set('foo', 'bar').should.equal(app);
|
||||
app.set('foo', 'bar');
|
||||
assert.equal(app.get('foo'), 'bar');
|
||||
})
|
||||
|
||||
it('should return the app when undefined', function(){
|
||||
it('should return the app', function () {
|
||||
var app = express();
|
||||
app.set('foo', undefined).should.equal(app);
|
||||
assert.equal(app.set('foo', 'bar'), app);
|
||||
})
|
||||
|
||||
it('should return the app when undefined', function () {
|
||||
var app = express();
|
||||
assert.equal(app.set('foo', undefined), app);
|
||||
})
|
||||
|
||||
describe('"etag"', function(){
|
||||
it('should throw on bad value', function(){
|
||||
var app = express()
|
||||
app.set.bind(app, 'etag', 42).should.throw(/unknown value/)
|
||||
var app = express();
|
||||
assert.throws(app.set.bind(app, 'etag', 42), /unknown value/);
|
||||
})
|
||||
|
||||
it('should set "etag fn"', function(){
|
||||
var app = express()
|
||||
var fn = function(){}
|
||||
app.set('etag', fn)
|
||||
app.get('etag fn').should.equal(fn)
|
||||
assert.equal(app.get('etag fn'), fn)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -33,7 +39,7 @@ describe('config', function(){
|
||||
var app = express()
|
||||
var fn = function(){}
|
||||
app.set('trust proxy', fn)
|
||||
app.get('trust proxy fn').should.equal(fn)
|
||||
assert.equal(app.get('trust proxy fn'), fn)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -41,34 +47,73 @@ describe('config', function(){
|
||||
describe('.get()', function(){
|
||||
it('should return undefined when unset', function(){
|
||||
var app = express();
|
||||
assert(undefined === app.get('foo'));
|
||||
assert.strictEqual(app.get('foo'), undefined);
|
||||
})
|
||||
|
||||
it('should otherwise return the value', function(){
|
||||
var app = express();
|
||||
app.set('foo', 'bar');
|
||||
app.get('foo').should.equal('bar');
|
||||
assert.equal(app.get('foo'), 'bar');
|
||||
})
|
||||
|
||||
describe('when mounted', function(){
|
||||
it('should default to the parent app', function(){
|
||||
var app = express()
|
||||
, blog = express();
|
||||
var app = express();
|
||||
var blog = express();
|
||||
|
||||
app.set('title', 'Express');
|
||||
app.use(blog);
|
||||
blog.get('title').should.equal('Express');
|
||||
assert.equal(blog.get('title'), 'Express');
|
||||
})
|
||||
|
||||
|
||||
it('should given precedence to the child', function(){
|
||||
var app = express()
|
||||
, blog = express();
|
||||
var app = express();
|
||||
var blog = express();
|
||||
|
||||
app.use(blog);
|
||||
app.set('title', 'Express');
|
||||
blog.set('title', 'Some Blog');
|
||||
|
||||
blog.get('title').should.equal('Some Blog');
|
||||
assert.equal(blog.get('title'), 'Some Blog');
|
||||
})
|
||||
|
||||
it('should inherit "trust proxy" setting', function () {
|
||||
var app = express();
|
||||
var blog = express();
|
||||
|
||||
function fn() { return false }
|
||||
|
||||
app.set('trust proxy', fn);
|
||||
assert.equal(app.get('trust proxy'), fn);
|
||||
assert.equal(app.get('trust proxy fn'), fn);
|
||||
|
||||
app.use(blog);
|
||||
|
||||
assert.equal(blog.get('trust proxy'), fn);
|
||||
assert.equal(blog.get('trust proxy fn'), fn);
|
||||
})
|
||||
|
||||
it('should prefer child "trust proxy" setting', function () {
|
||||
var app = express();
|
||||
var blog = express();
|
||||
|
||||
function fn1() { return false }
|
||||
function fn2() { return true }
|
||||
|
||||
app.set('trust proxy', fn1);
|
||||
assert.equal(app.get('trust proxy'), fn1);
|
||||
assert.equal(app.get('trust proxy fn'), fn1);
|
||||
|
||||
blog.set('trust proxy', fn2);
|
||||
assert.equal(blog.get('trust proxy'), fn2);
|
||||
assert.equal(blog.get('trust proxy fn'), fn2);
|
||||
|
||||
app.use(blog);
|
||||
|
||||
assert.equal(app.get('trust proxy'), fn1);
|
||||
assert.equal(app.get('trust proxy fn'), fn1);
|
||||
assert.equal(blog.get('trust proxy'), fn2);
|
||||
assert.equal(blog.get('trust proxy fn'), fn2);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -76,42 +121,42 @@ describe('config', function(){
|
||||
describe('.enable()', function(){
|
||||
it('should set the value to true', function(){
|
||||
var app = express();
|
||||
app.enable('tobi').should.equal(app);
|
||||
app.get('tobi').should.be.true;
|
||||
assert.equal(app.enable('tobi'), app);
|
||||
assert.strictEqual(app.get('tobi'), true);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.disable()', function(){
|
||||
it('should set the value to false', function(){
|
||||
var app = express();
|
||||
app.disable('tobi').should.equal(app);
|
||||
app.get('tobi').should.be.false;
|
||||
assert.equal(app.disable('tobi'), app);
|
||||
assert.strictEqual(app.get('tobi'), false);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.enabled()', function(){
|
||||
it('should default to false', function(){
|
||||
var app = express();
|
||||
app.enabled('foo').should.be.false;
|
||||
assert.strictEqual(app.enabled('foo'), false);
|
||||
})
|
||||
|
||||
it('should return true when set', function(){
|
||||
var app = express();
|
||||
app.set('foo', 'bar');
|
||||
app.enabled('foo').should.be.true;
|
||||
assert.strictEqual(app.enabled('foo'), true);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.disabled()', function(){
|
||||
it('should default to true', function(){
|
||||
var app = express();
|
||||
app.disabled('foo').should.be.true;
|
||||
assert.strictEqual(app.disabled('foo'), true);
|
||||
})
|
||||
|
||||
it('should return false when set', function(){
|
||||
var app = express();
|
||||
app.set('foo', 'bar');
|
||||
app.disabled('foo').should.be.false;
|
||||
assert.strictEqual(app.disabled('foo'), false);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
1
test/fixtures/% of dogs.txt
vendored
Normal file
1
test/fixtures/% of dogs.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
20%
|
||||
1
test/fixtures/blog/index.html
vendored
Normal file
1
test/fixtures/blog/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<b>index</b>
|
||||
1
test/fixtures/blog/post/index.jade
vendored
1
test/fixtures/blog/post/index.jade
vendored
@@ -1 +0,0 @@
|
||||
h1 blog post
|
||||
1
test/fixtures/blog/post/index.tmpl
vendored
Normal file
1
test/fixtures/blog/post/index.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<h1>blog post</h1>
|
||||
1
test/fixtures/default_layout/name.tmpl
vendored
Normal file
1
test/fixtures/default_layout/name.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>$name</p>
|
||||
1
test/fixtures/default_layout/user.tmpl
vendored
Normal file
1
test/fixtures/default_layout/user.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>$user.name</p>
|
||||
1
test/fixtures/email.jade
vendored
1
test/fixtures/email.jade
vendored
@@ -1 +0,0 @@
|
||||
p This is an email
|
||||
1
test/fixtures/email.tmpl
vendored
Normal file
1
test/fixtures/email.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>This is an email</p>
|
||||
1
test/fixtures/local_layout/user.tmpl
vendored
Normal file
1
test/fixtures/local_layout/user.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<span>$user.name</span>
|
||||
1
test/fixtures/name.jade
vendored
1
test/fixtures/name.jade
vendored
@@ -1 +0,0 @@
|
||||
p= name
|
||||
1
test/fixtures/name.tmpl
vendored
Normal file
1
test/fixtures/name.tmpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>$name</p>
|
||||
1
test/fixtures/pet.jade
vendored
1
test/fixtures/pet.jade
vendored
@@ -1 +0,0 @@
|
||||
p #{first} #{last} is a #{species}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user