Comparar commits

..

146 Commits

Autor SHA1 Mensagem Data
Roman Shtylman adc3092b49 bump cookie parser dependency to 1.0.1 2014-02-22 09:20:15 -05:00
Roman Shtylman e2651cfeec move support for multiple res.cookie calls to lib/response
Patch.js is simpler and follows upstream node.js closer as a result.
2014-02-17 23:50:22 -05:00
Roman Shtylman ca0f29413e remove support for node 0.8 2014-02-17 23:39:09 -05:00
Roman Shtylman 5bd65f1578 remove ServerResonse.headerSent monkey patch
node.js ServerResponse contains a headersSent field. Use that instead of
our patched misnamed version.
2014-02-17 23:36:56 -05:00
Roman Shtylman 6d9d757c95 remove last pieces of connect dependency
- copy over patch.js to shim ServerResponse
- bundle `static` middleware
2014-02-15 20:21:45 -05:00
Roman Shtylman 8264a1f0a4 use local copy of parseUrl 2014-02-04 10:15:46 -05:00
Roman Shtylman 07cdb7f8e3 move connect.query() into our repo 2014-02-04 10:10:56 -05:00
Roman Shtylman f41d09a3cf remove app.router and refactor middleware processing
This is an overhaul of middleware processing, Router and Route. Connect is no
longer used to process the middleware stack. This functionality has been
split into two parts: middleware stack and default error response.

The entry point for request processing is the `app.handle` method. It
sets up the default error response handle (to run in the event of no
other error handler) and then triggers the app router (instance of
Router) to handle the request.

The app router `handle` function contains the middleware dispatch layer
previously in the connect codebase. This layer handle the logic for
dispatching `.use` calls (stripping paths if needed). The app contains a
base router `app._router`. New routes can be created and `.use`d on this
router to organize routes into files.

Routers now have the following methods `.use`, `.all`, `.param` which
are all public.

Additionally, Routers have a `.route(path)` method which returns a new
instance of Route for the requested path. Route(s) are isolated
middleware stacks and contain methods for the HTTP verbs as well as an
`.all` method to act similar to middleware. These methods are chainable
to easily describe requirements for a route.

  var route = Router.route('/foo'); // or 'app.route('/foo')'

  route
  .all(auth)
  .get(function(...) {})
  .all(more_checks)
  .post(function(...) {})

Any Route and Router methods which accept handlers also accept error
(arity 4) handlers which will also behave as expected.

Finally, the `app.router` getter has been removed. Middleware and
handlers are run IN THE ORDER they are seen in the file. This means that
code which injected the `app.router` and then added error handlers (or
other middleware) will need to be updated to move those handlers after
any requests added on the app object. The examples have been updated
accordingly. This is the largest breaking change to codebases in this
commit.
2014-02-03 15:59:52 -05:00
Roman Shtylman 4bf9cfd477 update merge-descriptors 2014-01-29 20:01:10 -05:00
Roman Shtylman 08cbc442f5 update cookie-signature to 1.0.3
Fix for timing attack
2014-01-29 20:00:23 -05:00
Roman Shtylman a02dd201e6 update send to 0.2.0 2014-01-29 19:58:53 -05:00
TJ Holowaychuk a5f7dcee04 update node-fresh 2014-01-29 12:17:16 -08:00
Roman Shtylman 0ddd761904 update range parser to 1.0.0
- License

see #1912
2014-01-29 10:00:28 -05:00
Roman Shtylman 991c2a9d05 Merge pull request #1908 from visionmedia/locals-object
change res.locals to a plain js object.
2014-01-28 14:23:52 -08:00
Roman Shtylman 4983c38298 change res.locals to a plain js object.
Anyone who wants something fancier should use modules.

- fixes annoyance with not being able to set 'name' property on locals
2014-01-27 19:17:29 -05:00
Roman Shtylman 337ab24899 remove unused require 2014-01-24 19:31:32 -05:00
Roman Shtylman 63c6a9c5ad use escape-html module to escape html
Another util bites the dust.
2014-01-24 19:21:21 -05:00
Roman Shtylman 718e68ffae use utils-merge module to mixin object properties 2014-01-24 19:16:37 -05:00
Roman Shtylman f56a5f01c4 remove deprecated express.createServer() method
This has been warning about deprecation for a long time. Use `express()`
to instantiate an express app.
2014-01-19 14:05:12 -05:00
Roman Shtylman b77ffe0228 Merge pull request #1904 from popomore/master
delete semicolon
2014-01-19 09:18:29 -08:00
Haoliang Gao fd6439bb36 delete semicolon 2014-01-19 23:53:48 +08:00
Jonathan Ong 121f8d02f3 Merge pull request #1889 from vesln/send-null-undefined
update the tests to show a difference between `send(null)` and `send(und...
2014-01-14 09:17:04 -08:00
Roman Shtylman 5ddbb6965f Merge pull request #1868 from dpatti/smarter-router-auto-options
Automatic OPTIONS response breaks with multiple routers
2014-01-13 14:45:06 -08:00
Doug Patti a3b5f6d07f prevent incorrect automatic OPTIONS responses
The router has automatic handling of OPTIONS based on the registered
routes, but if you make an OPTIONS request for an endpoint that does
not exist, then it will still return a 200 with nothing allowed.
Instead, we can let the request move on down the middleware chain. This
has two benefits: first, if the route was not defined and no other
middleware handles it, it will return with a 404. Secondly, if multiple
routers are used and a later one has the route or a custom OPTIONS
defined, the first router will not respond incorrectly.
2014-01-13 17:40:42 -05:00
Roman Shtylman ac2cbef8be Merge pull request #1899 from visionmedia/remove-configure
Remove app.configure
2014-01-11 15:42:55 -08:00
Roman Shtylman dff22e9d09 update history file with configure changes 2014-01-11 10:54:13 -05:00
Roman Shtylman 7282b50ad0 remove app.configure() 2014-01-11 10:53:54 -05:00
Roman Shtylman 8c059469fd No 'json spaces' by default
Json rendering can be handled by user tools or overridden in their own
app to behave as desired. Minimizes the use of magic env settings.
2014-01-11 10:53:36 -05:00
Roman Shtylman 8c3f153dd4 remove use of app.configure for view cache setting 2014-01-11 10:52:38 -05:00
Jonathan Ong 185b526e60 Merge pull request #1892 from matheusazzi/patch-1
Update to valid Jade Doctype
2014-01-04 19:23:44 -08:00
Matheus Azzi 38996b30b1 Update layout.jade 2014-01-05 01:14:38 -02:00
TJ Holowaychuk 827dfed7c2 Merge pull request #1890 from oliversalzburg/patch-1
Value parameter of app.set() should be typed optional Object
2014-01-04 18:12:52 -08:00
Oliver Salzburg 28af21baeb Value parameter of app.set() is now typed optional mixed 2014-01-04 22:05:19 +01:00
Oliver Salzburg 951c70496b Value parameter of app.set() should be typed optional Object 2014-01-04 17:50:27 +01:00
Veselin Todorov a36eeb96f3 update the tests to show a difference between send(null) and send(undefiend) 2014-01-03 19:47:57 +02:00
Jonathan Ong 7018d3d0e6 history: req.params 2014-01-03 03:00:48 -08:00
Jonathan Ong 3f14b4de1f Merge pull request #1835 from visionmedia/change-req-params-to-object
change req.params to an object instead of an array
2014-01-03 03:00:13 -08:00
Jonathan Ong 26c0be4c4e improve history.md 2014-01-03 02:57:24 -08:00
Jonathan Ong cec0c06a70 refactor req.is and req.accepts* 2014-01-03 02:50:09 -08:00
Jonathan Ong 476f8deb07 remove binary 2014-01-03 02:33:00 -08:00
TJ Holowaychuk dc5932d177 Merge pull request #1877 from reqshark/master
update express jade layout generator
2013-12-23 10:34:47 -08:00
Bent Cardan cfd93b7529 update express jade layout generator
update doctype
2013-12-23 13:03:21 -05:00
Jonathan Ong 8b2208f394 Merge pull request #1876 from yosssi/dev
Updated the example file to use `doctype html` on  because  `doctype 5` was deprecated on Jade version 1.0.0.
2013-12-23 08:10:26 -08:00
yosssi 00a3b01f39 Changed doctype 5 to doctype html on the example file because the former was deprecated on Jade version 1.0.0. 2013-12-23 22:13:05 +09:00
TJ Holowaychuk 3baca251f0 use 8 threads for benchmarks 2013-12-22 08:57:04 -08:00
TJ Holowaychuk 72daae1d92 Merge pull request #1869 from yamatt/master
Error message now describes where the view was not able to be found.
2013-12-21 11:13:04 -08:00
Matt Copperwaite fcbe53ddb5 Added appropriate test for more descriptive render error 2013-12-21 17:34:59 +00:00
Jonathan Ong e9851672eb bench: remove --harmony-generators flag 2013-12-20 21:15:17 -08:00
TJ Holowaychuk 9a45f7bd3d add new benchmarks (to match koa) 2013-12-20 19:34:59 -08:00
Matt Copperwaite 85834fd146 Error message now describes where the view was not able to be found. Useful for debugging. 2013-12-20 11:39:31 +00:00
Roman Shtylman a0c1ac7b45 add license field to package.json
close #1862
2013-12-18 10:16:56 -05:00
Alex Kocharin 7b0dca0f9c throw 400 in case of malformed paths 2013-12-11 17:14:44 -08:00
Jonathan Ong 34c83d7d29 3.4.7 2013-12-10 23:57:39 -08:00
Jonathan Ong 7c6882234e bump connect, mocha, and should 2013-12-10 23:54:07 -08:00
Jonathan Ong 2e68ddbae9 expose connect.middleware using Object.getOwnPropertyDescriptor()
closes #1853. no tests, but it should be fine.
2013-12-10 23:52:48 -08:00
Jonathan Ong 7724fc6af7 3.4.6 2013-12-01 12:21:08 -08:00
Roman Shtylman 2939075f03 Merge pull request #1836 from fluxusfrequency/patch-1
Grammar and punctuation fixes [ci skip]
2013-11-28 08:18:42 -08:00
Ben Lewis 606f68de02 Grammar and punctuation fixes [ci skip] 2013-11-28 06:36:21 -07:00
TJ Holowaychuk c6c71abf4d change req.params to an object instead of an array 2013-11-27 19:46:39 -08:00
Jonathan Ong 863160ae49 3.4.5 2013-11-27 15:54:41 -08:00
TJ Holowaychuk edd39fb194 fix weird variable name in example 2013-11-26 23:39:35 -08:00
TJ Holowaychuk a71d264d45 fix weird variable name in example 2013-11-26 23:39:08 -08:00
TJ Holowaychuk 8a7a695836 ocd 2013-11-26 11:12:56 -08:00
TJ Holowaychuk de54af4061 Merge pull request #1829 from michaelficarra/patch-1
fixes #1826: res.redirect('toString') fails with 500
2013-11-26 11:12:13 -08:00
Michael Ficarra 2f2a652bc9 fixes #1826: res.redirect('toString') fails with 500
Removed the unused map and corrected the doc comment.
2013-11-26 13:11:15 -06:00
TJ Holowaychuk 1e638663de Merge pull request #1822 from yakubori/auth-buffer-call-removal
Removed Buffer call with 'binary' encoding option in auth example.
2013-11-21 12:32:39 -08:00
Rick Yakubowski 1684a8792a Removed Buffer call with 'binary' encoding option in auth example.
According to the Node.js documentation for Buffer objects regarding the
'binary' encoding option:

"This encoding method is deprecated and should be avoided in favor of
Buffer objects where possible. This encoding will be removed in future
versions of Node."

Simply calling toString() with a 'base64' argument on the hash seems to
accomplish the same thing; this makes the code compatible with current
documentation as well as being a bit easier to follow.
2013-11-21 14:01:56 -05:00
Roman Shtylman f47c0d9774 add Router.all() method
Similar to app.all() but specifically for attaching handlers to all
methods under a standalone router. This is useful for isolating routers
that require "middleware" like features for all routes managed by the
router.
2013-11-19 18:52:04 -05:00
Roman Shtylman 89e7264e53 pin marked devDep to protect out tests
marked has shown that it cannot be trusted with patch level changes!
2013-11-09 22:31:17 -05:00
Roman Shtylman cada9f61c8 pin devDependencies using ~
If tests are passing and everything works, don't let things change out
from under us as much. Really we should do hard pinning, but will be a
bit lenient for now.
2013-11-09 22:26:09 -05:00
Jonathan Ong 373fa55981 fix markdown example test
marked 0.2.10 adds ids to header elements now.
2013-11-09 19:08:25 -08:00
Jonathan Ong 2bc703cfc2 Merge pull request #1802 from kapouer/patch-1
Remove leading ./ when using res.location('./relative')
2013-11-02 15:09:30 -07:00
Jérémy Lal c9865b821d Test location with leading ./ and containing .. 2013-11-02 02:28:54 +01:00
Jérémy Lal 9c0de23645 Update tests expectancy of location headers 2013-11-02 02:28:49 +01:00
Jérémy Lal b7a38af41d Use url.resolve to compute location header of relative paths 2013-11-02 02:28:29 +01:00
Jérémy Lal 661914781e semicolons 2013-11-02 00:39:32 +01:00
Jonathan Ong 6e3f3887e9 pin deps using semver1
somebody is going to complain that they can't install stuff because
they haven't upgraded npm
2013-10-30 20:55:11 -07:00
Jonathan Ong a66d6bb034 pin dev deps to semver compatible versions 2013-10-30 20:51:10 -07:00
Jonathan Ong 2e197e2b98 be less picky with ENOENT errors in tests
closes #1580
2013-10-30 20:37:01 -07:00
Jonathan Ong 55d1a4f964 always send ETag when content-length > 0
closes #1780
2013-10-30 20:34:16 -07:00
Jonathan Ong 82a7d7a977 no semver2 so travis stops crying 2013-10-29 22:44:01 -07:00
Jonathan Ong dae54b456f 3.4.4 2013-10-29 10:33:32 -07:00
Jonathan Ong 1b7a044f33 bump connect 2013-10-29 10:30:26 -07:00
Jonathan Ong 18264403b1 bump supertest to 0.8.1 2013-10-28 15:24:48 -07:00
Jonathan Ong 04d43b7039 remove .gitmodules
it's empty
2013-10-28 14:38:46 -07:00
TJ Holowaychuk 2dfecfb661 update methods for SEARCH 2013-10-28 12:02:24 -07:00
Jonathan Ong 250f1f5f6e Merge pull request #1796 from malixsys/patch-1
2013-100-23 -> 2013-10-23
2013-10-25 10:59:48 -07:00
M Alix 3ac718763f 2013-100-23 -> 2013-10-23 2013-10-25 14:28:24 +02:00
Jonathan Ong 05e1555c0d Merge pull request #1795 from chirag04/master
replace bodyparser with json and urlencoded
2013-10-25 03:31:24 -07:00
chirag04 855d1e2bf5 replace bodyparser with json and urlencoded 2013-10-25 15:45:25 +05:30
Jonathan Ong f0bfb3b2b2 3.4.3 2013-10-23 11:19:48 -07:00
Jonathan Ong 4b4db0f7fb 3.4.2 2013-10-18 19:03:41 -07:00
Jonathan Ong 9bed2b80ee lint: remove unused stuff 2013-10-18 01:18:56 -07:00
Jonathan Ong bb157c0cbf replace old contributors info with github's contributors 2013-10-17 13:06:13 -07:00
Jonathan Ong 1ef05d4a28 downgrade commander. closes #1783 2013-10-17 12:57:39 -07:00
TJ Holowaychuk 0b88208022 Merge branch 'master' of github.com:visionmedia/express 2013-10-16 19:52:00 -07:00
TJ Holowaychuk c9d9ed3493 fix res.sendfile() callback
what the hell... I was just told readable streams have finish not end,
make up your mind node!
2013-10-17 02:51:01 +00:00
TJ Holowaychuk bd8b9f5781 Merge branch 'master' of github.com:visionmedia/express 2013-10-16 19:21:09 -07:00
TJ Holowaychuk 9cbcf23df0 docs 2013-10-16 19:17:49 -07:00
Jonathan Ong 36e42db05b mocha globals - readable-stream defines globals
isaac you bastard
2013-10-15 18:33:47 -07:00
Jonathan Ong 2bf6a1d813 3.4.1 2013-10-15 18:28:49 -07:00
Jonathan Ong e8373d3564 Merge pull request #1779 from visionmedia/jsonp-typeof-callback
check existence of jsonp callback
2013-10-15 18:22:01 -07:00
Jonathan Ong e218377a3d check existence of jsonp callback 2013-10-15 12:39:32 -07:00
Jonathan Ong 7d1aed4955 update commander. closes #1693
i hope this doesn't break anything
2013-10-14 21:22:19 -07:00
Jonathan Ong b4acbcf1fe use path.join for 'views' setting. closes #1427 2013-10-14 21:16:57 -07:00
Jonathan Ong 50cb62c5d2 fix tests for should.js 2013-10-14 18:35:46 -07:00
Jonathan Ong 4cf868bd74 Merge pull request #1776 from ykumar6/master
Add Runnable.com button
2013-10-14 18:33:34 -07:00
Yash Kumar baa5a7c3e9 Add Runnable.com button 2013-10-14 14:16:18 -07:00
Jonathan Ong ee228f7aea Merge pull request #1759 from muratgu/patch-1
fixes #1600
2013-09-19 12:43:42 -07:00
Jonathan Ong d5b11c7d1b Merge pull request #1760 from jseip1679/master
documentation language fix
2013-09-19 12:38:21 -07:00
Jake Seip ed7db34bab documentation language fix 2013-09-19 10:41:45 -07:00
muratgu 57e45e3af8 fixes #1600 2013-09-19 10:27:21 -07:00
Jonathan Ong 1dc46478cb README: add more links to expressjs.com 2013-09-17 00:35:23 -07:00
TJ Holowaychuk 113ed0927d fix test label typo 2013-09-16 23:34:16 +00:00
TJ Holowaychuk ab8be2d741 remove second signed cookie test
for now
2013-09-16 23:33:42 +00:00
TJ Holowaychuk 3b53b11fcd fix signed cookies test 2013-09-16 23:32:34 +00:00
TJ Holowaychuk 9fb661559b refactor signed cookie tests 2013-09-16 23:24:54 +00:00
Jonathan Ong 04882cf72c Merge pull request #1735 from lxe/malformed-capture-route
Wrapped encodeURIcomponent in try-catch to eliminate errors on malformed captures.
2013-09-16 15:32:57 -07:00
lxe 288176bbc9 Added safe encodeURIcomponent to eliminate errors on malformed captures. 2013-09-16 14:57:31 -04:00
Jonathan Ong 5638a4fc62 Merge pull request #1688 from menzoic/issue/menzoic-1
removed unnecessary require statement
2013-09-09 21:45:19 -07:00
TJ Holowaychuk 3b4ce91fa3 refactor res.format() with a little ocd 2013-09-08 09:30:59 -07:00
TJ Holowaychuk 1c87e5e9a8 Merge pull request #1747 from sorribas/master
res.format() now includes charset.
2013-09-08 09:30:23 -07:00
Eduardo Sorribas a887e6a881 Minor refactor of res.format 2013-09-08 02:34:52 -04:00
Eduardo Sorribas 69290cad6f res.format() now includes charset. Fixes #1744 2013-09-08 02:10:46 -04:00
Jonathan Ong b66c7da05f Merge pull request #1659 from dresende/patch-1
Fixes typo in index.js vhost example
2013-09-07 21:43:45 -07:00
Jonathan Ong 92ddf77453 Merge pull request #1729 from patelatharva/patch-1
Improved variable names and updated comments for better clarity of example
2013-09-07 21:43:06 -07:00
TJ Holowaychuk 8e2f538983 refactor res.links() 2013-09-07 15:26:12 -07:00
TJ Holowaychuk 2817d8caf2 Merge pull request #1746 from sorribas/master
Allow multiple call concatenation for res.links.
2013-09-07 15:24:50 -07:00
TJ Holowaychuk b7f08fb159 Release 3.4.0 2013-09-07 12:25:00 -07:00
TJ Holowaychuk 0c2768f5bd update connect 2013-09-07 12:24:24 -07:00
Eduardo Sorribas 09bede1a92 Fix the links test so it resets the header for each test. 2013-09-07 01:10:13 -04:00
Eduardo Sorribas 7059d3b71e Allow multiple call concatenation for res.links. Fixes #1683 2013-09-06 21:44:03 -04:00
cjihrig e5de08faa1 add res.vary(). Closes #1682 2013-09-02 09:10:14 -07:00
TJ Holowaychuk e43ff076fd Merge pull request #1740 from superic/master
Updated Util.isAbsolute(path) to return true for Azure absolute paths
2013-09-02 08:56:03 -07:00
TJ Holowaychuk 3ea7381dea Merge pull request #1711 from jonjenkins/master
Fixes from pull request #1643
2013-09-02 08:55:23 -07:00
TJ Holowaychuk f1c46f51e5 Release 3.3.8 2013-09-02 08:01:07 -07:00
TJ Holowaychuk 297fb4e0b0 update connect 2013-09-02 08:00:48 -07:00
Eric Willis 9e406dfee2 Updated Util.isAbsolute(path) to return true for Azure absolute paths
- Azure absolute paths look like \\ip_address\volume\guid\guid\site\wwwroot\...file.js.
  Changed Util.isAbsolute to return true for paths that start with two backslashes.
2013-08-31 16:16:49 -07:00
TJ Holowaychuk 30b7aa8a17 update connect 2013-08-28 10:03:42 -07:00
TJ Holowaychuk c1d16e0016 Release 3.3.7 2013-08-28 09:39:31 -07:00
TJ Holowaychuk 929ffb8d77 update connect 2013-08-28 09:37:45 -07:00
Atharva 058d7ec2ea Improved variable names and updated comments for better clarity of example 2013-08-24 16:44:44 +05:30
Jon Jenkins 19cb39869f Fixes from pull request #1643, array method correction 2013-08-04 12:46:50 -05:00
Jon Jenkins bdbdab7fcc Fixes from pull request #1643 2013-08-03 16:33:15 -05:00
Esco Obong 7c2ed1d2d6 removed unnecessary require statement 2013-07-15 02:13:32 -04:00
Diogo Resende 7a31a1d311 Fixes typo in index.js vhost example 2013-06-23 23:23:58 +02:00
78 arquivos alterados com 1808 adições e 2154 exclusões
Ver Arquivo
-1
Ver Arquivo
@@ -1,4 +1,3 @@
language: node_js
node_js:
- "0.8"
- "0.10"
+107 -19
Ver Arquivo
@@ -1,33 +1,121 @@
4.0.0 /
==================
3.3.6 / 2013-08-27
* remove:
- express(1) - moved to [express-generator](https://github.com/expressjs/generator)
- `req.accepted*` - use `req.accepts*()` instead
- `app.configure` - use logic in your own app code
- `express.createServer()` - it has been deprecated for a long time. Use `express()`
- `app.router` - is removed
- all bundled middleware except `static`
* change:
- `req.accepts*` -> `req.accepts*s` - i.e. `req.acceptsEncoding` -> `req.acceptsEncodings`
- `req.params` is now an object instead of an array
- `json spaces` no longer enabled by default in development
- `res.locals` is no longer a function. It is a plain js object. Treat it as such.
- `app.route` -> `app.mountpath` when mounting an express app in another express app
- `res.headerSent` -> `res.headersSent` to match node.js ServerResponse object
* refactor:
- `req.accepts*` with [accepts](https://github.com/expressjs/accepts)
- `req.is` with [type-is](https://github.com/expressjs/type-is)
* add:
- `app.router()` - returns the app Router instance
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
- Router & Route - public API
3.4.7 / 2013-12-10
==================
* update connect
3.4.6 / 2013-12-01
==================
* update connect (raw-body)
3.4.5 / 2013-11-27
==================
* update connect
* res.location: remove leading ./ #1802 @kapouer
* res.redirect: fix `res.redirect('toString') #1829 @michaelficarra
* res.send: always send ETag when content-length > 0
* router: add Router.all() method
3.4.4 / 2013-10-29
==================
* update connect
* update supertest
* update methods
* express(1): replace bodyParser() with urlencoded() and json() #1795 @chirag04
3.4.3 / 2013-10-23
==================
* update connect
3.4.2 / 2013-10-18
==================
* update connect
* downgrade commander
3.4.1 / 2013-10-15
==================
* update connect
* update commander
* jsonp: check if callback is a function
* router: wrap encodeURIComponent in a try/catch #1735 (@lxe)
* res.format: now includes chraset @1747 (@sorribas)
* res.links: allow multiple calls @1746 (@sorribas)
3.4.0 / 2013-09-07
==================
* add res.vary(). Closes #1682
* update connect
3.3.8 / 2013-09-02
==================
* update connect
3.3.7 / 2013-08-28
==================
* update connect
3.3.6 / 2013-08-27
==================
* Revert "remove charset from json responses. Closes #1631" (causes issues in some clients)
* add: req.accepts take an argument list
3.3.4 / 2013-07-08
3.3.4 / 2013-07-08
==================
* update send and connect
3.3.3 / 2013-07-04
3.3.3 / 2013-07-04
==================
* update connect
3.3.2 / 2013-07-03
3.3.2 / 2013-07-03
==================
* update connect
* update send
* remove .version export
3.3.1 / 2013-06-27
3.3.1 / 2013-06-27
==================
* update connect
3.3.0 / 2013-06-26
3.3.0 / 2013-06-26
==================
* update connect
@@ -36,12 +124,12 @@
* change: return actual booleans from req.accept* functions
* fix jsonp callback array throw
3.2.6 / 2013-06-02
3.2.6 / 2013-06-02
==================
* update connect
3.2.5 / 2013-05-21
3.2.5 / 2013-05-21
==================
* update connect
@@ -49,23 +137,23 @@
* add: throw a meaningful error when there is no default engine
* change generation of ETags with res.send() to GET requests only. Closes #1619
3.2.4 / 2013-05-09
3.2.4 / 2013-05-09
==================
* fix `req.subdomains` when no Host is present
* fix `req.host` when no Host is present, return undefined
3.2.3 / 2013-05-07
3.2.3 / 2013-05-07
==================
* update connect / qs
3.2.2 / 2013-05-03
3.2.2 / 2013-05-03
==================
* update qs
3.2.1 / 2013-04-29
3.2.1 / 2013-04-29
==================
* add app.VERB() paths array deprecation warning
@@ -73,27 +161,27 @@
* update qs and remove all ~ semver crap
* fix: accept number as value of Signed Cookie
3.2.0 / 2013-04-15
3.2.0 / 2013-04-15
==================
* add "view" constructor setting to override view behaviour
* add req.acceptsEncoding(name)
* add req.acceptedEncodings
* revert cookie signature change causing session race conditions
* fix sorting of Accept values of the same quality
* fix sorting of Accept values of the same quality
3.1.2 / 2013-04-12
3.1.2 / 2013-04-12
==================
* add support for custom Accept parameters
* update cookie-signature
3.1.1 / 2013-04-01
3.1.1 / 2013-04-01
==================
* add X-Forwarded-Host support to `req.host`
* fix relative redirects
* update mkdirp
* fix relative redirects
* update mkdirp
* update buffer-crc32
* remove legacy app.configure() method from app template.
+4 -3
Ver Arquivo
@@ -9,6 +9,7 @@ test: test-unit test-acceptance
test-unit:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--globals setImmediate,clearImmediate \
$(MOCHA_OPTS)
test-acceptance:
@@ -23,11 +24,11 @@ test-cov: lib-cov
lib-cov:
@jscoverage lib lib-cov
benchmark:
@./support/bench
bench:
@$(MAKE) -C benchmarks
clean:
rm -f coverage.html
rm -fr lib-cov
.PHONY: test test-unit test-acceptance benchmark clean
.PHONY: test test-unit test-acceptance bench clean
+17 -70
Ver Arquivo
@@ -1,6 +1,8 @@
![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)
[![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)](http://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). [![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
[![Build Status](https://secure.travis-ci.org/visionmedia/express.png)](http://travis-ci.org/visionmedia/express) [![Gittip](http://img.shields.io/gittip/visionmedia.png)](https://www.gittip.com/visionmedia/)
```js
var express = require('express');
@@ -48,18 +50,19 @@ app.listen(3000);
## Philosophy
The Express philosophy is to provide small, robust tooling for HTTP servers. Making
The Express philosophy is to provide small, robust tooling for HTTP servers, making
it a great solution for single page applications, web sites, hybrids, or public
HTTP APIs.
Built on Connect you can use _only_ what you need, and nothing more, applications
Built on Connect, you can use _only_ what you need, and nothing more. Applications
can be as big or as small as you like, even a single file. Express does
not force you to use any specific ORM or template engine. With support for over
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js)
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js),
you can quickly craft your perfect framework.
## More Information
* [Website and Documentation](http://expressjs.com/) stored at [visionmedia/expressjs.com](https://github.com/visionmedia/expressjs.com)
* Join #express on freenode
* [Google Group](http://groups.google.com/group/express-js) for discussion
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
@@ -69,89 +72,33 @@ app.listen(3000);
## Viewing Examples
Clone the Express repo, then install the dev dependencies to install all the example / test suite deps:
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
$ git clone git://github.com/visionmedia/express.git --depth 1
$ cd express
$ npm install
then run whichever tests you want:
Then run whichever tests you want:
$ node examples/content-negotiation
You can also view live examples here:
<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 invoke the following command within the repo, installing the development dependencies:
$ npm install
then run the tests:
Then run the tests:
$ make test
## Contributors
```
project: express
commits: 3559
active : 468 days
files : 237
authors:
1891 Tj Holowaychuk 53.1%
1285 visionmedia 36.1%
182 TJ Holowaychuk 5.1%
54 Aaron Heckmann 1.5%
34 csausdev 1.0%
26 ciaranj 0.7%
21 Robert Sköld 0.6%
6 Guillermo Rauch 0.2%
3 Dav Glass 0.1%
3 Nick Poulden 0.1%
2 Randy Merrill 0.1%
2 Benny Wong 0.1%
2 Hunter Loftis 0.1%
2 Jake Gordon 0.1%
2 Brian McKinney 0.1%
2 Roman Shtylman 0.1%
2 Ben Weaver 0.1%
2 Dave Hoover 0.1%
2 Eivind Fjeldstad 0.1%
2 Daniel Shaw 0.1%
1 Matt Colyer 0.0%
1 Pau Ramon 0.0%
1 Pero Pejovic 0.0%
1 Peter Rekdal Sunde 0.0%
1 Raynos 0.0%
1 Teng Siong Ong 0.0%
1 Viktor Kelemen 0.0%
1 ctide 0.0%
1 8bitDesigner 0.0%
1 isaacs 0.0%
1 mgutz 0.0%
1 pikeas 0.0%
1 shuwatto 0.0%
1 tstrimple 0.0%
1 ewoudj 0.0%
1 Adam Sanderson 0.0%
1 Andrii Kostenko 0.0%
1 Andy Hiew 0.0%
1 Arpad Borsos 0.0%
1 Ashwin Purohit 0.0%
1 Benjen 0.0%
1 Darren Torpey 0.0%
1 Greg Ritter 0.0%
1 Gregory Ritter 0.0%
1 James Herdman 0.0%
1 Jim Snodgrass 0.0%
1 Joe McCann 0.0%
1 Jonathan Dumaine 0.0%
1 Jonathan Palardy 0.0%
1 Jonathan Zacsh 0.0%
1 Justin Lilly 0.0%
1 Ken Sato 0.0%
1 Maciej Małecki 0.0%
1 Masahiro Hayashi 0.0%
```
https://github.com/visionmedia/express/graphs/contributors
## License
+13
Ver Arquivo
@@ -0,0 +1,13 @@
all:
@./run 1 middleware
@./run 5 middleware
@./run 10 middleware
@./run 15 middleware
@./run 20 middleware
@./run 30 middleware
@./run 50 middleware
@./run 100 middleware
@echo
.PHONY: all
+23
Ver Arquivo
@@ -0,0 +1,23 @@
var http = require('http');
var express = require('..');
var app = express();
// number of middleware
var n = parseInt(process.env.MW || '1', 10);
console.log(' %s middleware', n);
while (n--) {
app.use(function(req, res, next){
next();
});
}
var body = new Buffer('Hello World');
app.use(function(req, res, next){
res.send(body);
});
app.listen(3333);
Arquivo executável
+16
Ver Arquivo
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
echo
MW=$1 node $2 &
pid=$!
sleep 2
wrk 'http://localhost:3333/?foo[bar]=baz' \
-d 3 \
-c 50 \
-t 8 \
| grep 'Requests/sec' \
| awk '{ print " " $2 }'
kill $pid
-423
Ver Arquivo
@@ -1,423 +0,0 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var exec = require('child_process').exec
, program = require('commander')
, mkdirp = require('mkdirp')
, pkg = require('../package.json')
, version = pkg.version
, os = require('os')
, fs = require('fs');
// CLI
program
.version(version)
.usage('[options] [dir]')
.option('-s, --sessions', 'add session support')
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
.option('-H, --hogan', 'add hogan.js engine support')
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
.option('-f, --force', 'force on non-empty directory')
.parse(process.argv);
// Path
var path = program.args.shift() || '.';
// end-of-line code
var eol = os.EOL
// Template engine
program.template = 'jade';
if (program.ejs) program.template = 'ejs';
if (program.jshtml) program.template = 'jshtml';
if (program.hogan) program.template = 'hjs';
/**
* Routes index template.
*/
var index = [
''
, '/*'
, ' * GET home page.'
, ' */'
, ''
, 'exports.index = function(req, res){'
, ' res.render(\'index\', { title: \'Express\' });'
, '};'
].join(eol);
/**
* Routes users template.
*/
var users = [
''
, '/*'
, ' * GET users listing.'
, ' */'
, ''
, 'exports.list = function(req, res){'
, ' res.send("respond with a resource");'
, '};'
].join(eol);
/**
* Jade layout template.
*/
var jadeLayout = [
'doctype 5'
, 'html'
, ' head'
, ' title= title'
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
, ' body'
, ' block content'
].join(eol);
/**
* Jade index template.
*/
var jadeIndex = [
'extends layout'
, ''
, 'block content'
, ' h1= title'
, ' p Welcome to #{title}'
].join(eol);
/**
* EJS index template.
*/
var ejsIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title><%= title %></title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1><%= title %></h1>'
, ' <p>Welcome to <%= title %></p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML layout template.
*/
var jshtmlLayout = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title> @write(title) </title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' @write(body)'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML index template.
*/
var jshtmlIndex = [
'<h1>@write(title)</h1>'
, '<p>Welcome to @write(title)</p>'
].join(eol);
/**
* Hogan.js index template.
*/
var hoganIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title>{{ title }}</title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1>{{ title }}</h1>'
, ' <p>Welcome to {{ title }}</p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* Default css template.
*/
var css = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
/**
* Default less template.
*/
var less = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
/**
* Default stylus template.
*/
var stylus = [
'body'
, ' padding: 50px'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, 'a'
, ' color: #00B7FF'
].join(eol);
/**
* App template.
*/
var app = [
''
, '/**'
, ' * Module dependencies.'
, ' */'
, ''
, 'var express = require(\'express\');'
, 'var routes = require(\'./routes\');'
, 'var user = require(\'./routes/user\');'
, 'var http = require(\'http\');'
, 'var path = require(\'path\');'
, ''
, 'var app = express();'
, ''
, '// all environments'
, 'app.set(\'port\', process.env.PORT || 3000);'
, 'app.set(\'views\', __dirname + \'/views\');'
, 'app.set(\'view engine\', \':TEMPLATE\');'
, 'app.use(express.favicon());'
, 'app.use(express.logger(\'dev\'));'
, 'app.use(express.bodyParser());'
, 'app.use(express.methodOverride());{sess}'
, 'app.use(app.router);{css}'
, 'app.use(express.static(path.join(__dirname, \'public\')));'
, ''
, '// development only'
, 'if (\'development\' == app.get(\'env\')) {'
, ' app.use(express.errorHandler());'
, '}'
, ''
, 'app.get(\'/\', routes.index);'
, 'app.get(\'/users\', user.list);'
, ''
, 'http.createServer(app).listen(app.get(\'port\'), function(){'
, ' console.log(\'Express server listening on port \' + app.get(\'port\'));'
, '});'
, ''
].join(eol);
// Generate application
(function createApplication(path) {
emptyDirectory(path, function(empty){
if (empty || program.force) {
createApplicationAt(path);
} else {
program.confirm('destination is not empty, continue? ', function(ok){
if (ok) {
process.stdin.destroy();
createApplicationAt(path);
} else {
abort('aborting');
}
});
}
});
})(path);
/**
* Create application at the given directory `path`.
*
* @param {String} path
*/
function createApplicationAt(path) {
console.log();
process.on('exit', function(){
console.log();
console.log(' install dependencies:');
console.log(' $ cd %s && npm install', path);
console.log();
console.log(' run the app:');
console.log(' $ node app');
console.log();
});
mkdir(path, function(){
mkdir(path + '/public');
mkdir(path + '/public/javascripts');
mkdir(path + '/public/images');
mkdir(path + '/public/stylesheets', function(){
switch (program.css) {
case 'less':
write(path + '/public/stylesheets/style.less', less);
break;
case 'stylus':
write(path + '/public/stylesheets/style.styl', stylus);
break;
default:
write(path + '/public/stylesheets/style.css', css);
}
});
mkdir(path + '/routes', function(){
write(path + '/routes/index.js', index);
write(path + '/routes/user.js', users);
});
mkdir(path + '/views', function(){
switch (program.template) {
case 'ejs':
write(path + '/views/index.ejs', ejsIndex);
break;
case 'jade':
write(path + '/views/layout.jade', jadeLayout);
write(path + '/views/index.jade', jadeIndex);
break;
case 'jshtml':
write(path + '/views/layout.jshtml', jshtmlLayout);
write(path + '/views/index.jshtml', jshtmlIndex);
break;
case 'hjs':
write(path + '/views/index.hjs', hoganIndex);
break;
}
});
// CSS Engine support
switch (program.css) {
case 'less':
app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: __dirname + \'/public\' }));');
break;
case 'stylus':
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(__dirname + \'/public\'));');
break;
default:
app = app.replace('{css}', '');
}
// Session support
app = app.replace('{sess}', program.sessions
? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());'
: '');
// Template support
app = app.replace(':TEMPLATE', program.template);
// package.json
var pkg = {
name: 'application-name'
, version: '0.0.1'
, private: true
, scripts: { start: 'node app.js' }
, dependencies: {
express: version
}
}
if (program.template) pkg.dependencies[program.template] = '*';
// CSS Engine support
switch (program.css) {
case 'less':
pkg.dependencies['less-middleware'] = '*';
break;
default:
if (program.css) {
pkg.dependencies[program.css] = '*';
}
}
write(path + '/package.json', JSON.stringify(pkg, null, 2));
write(path + '/app.js', app);
});
}
/**
* Check if the given directory `path` is empty.
*
* @param {String} path
* @param {Function} fn
*/
function emptyDirectory(path, fn) {
fs.readdir(path, function(err, files){
if (err && 'ENOENT' != err.code) throw err;
fn(!files || !files.length);
});
}
/**
* echo str > path.
*
* @param {String} path
* @param {String} str
*/
function write(path, str) {
fs.writeFile(path, str);
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
}
/**
* Mkdir -p.
*
* @param {String} path
* @param {Function} fn
*/
function mkdir(path, fn) {
mkdirp(path, 0755, function(err){
if (err) throw err;
console.log(' \033[36mcreate\033[0m : ' + path);
fn && fn();
});
}
/**
* Exit with the given `str`.
*
* @param {String} str
*/
function abort(str) {
console.error(str);
process.exit(1);
}
+7 -4
Ver Arquivo
@@ -3,7 +3,10 @@
*/
var express = require('../..')
, hash = require('./pass').hash;
, hash = require('./pass').hash
, bodyParser = require('body-parser')
, cookieParser = require('cookie-parser')
, session = require('express-session')
var app = module.exports = express();
@@ -14,9 +17,9 @@ app.set('views', __dirname + '/views');
// middleware
app.use(express.bodyParser());
app.use(express.cookieParser('shhhh, very secret'));
app.use(express.session());
app.use(bodyParser());
app.use(cookieParser('shhhh, very secret'));
app.use(session());
// Session-persisted message middleware
+3 -3
Ver Arquivo
@@ -32,7 +32,7 @@ var iterations = 12000;
exports.hash = function (pwd, salt, fn) {
if (3 == arguments.length) {
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
fn(err, (new Buffer(hash, 'binary')).toString('base64'));
fn(err, hash.toString('base64'));
});
} else {
fn = salt;
@@ -41,8 +41,8 @@ exports.hash = function (pwd, salt, fn) {
salt = salt.toString('base64');
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
if (err) return fn(err);
fn(null, salt, (new Buffer(hash, 'binary')).toString('base64'));
fn(null, salt, hash.toString('base64'));
});
});
}
};
};
+3 -2
Ver Arquivo
@@ -1,5 +1,6 @@
var express = require('../..')
, logger = require('morgan')
, app = express();
app.set('views', __dirname);
@@ -14,11 +15,11 @@ while (n--) {
pets.push({ name: 'Jane', age: 6, species: 'ferret' });
}
app.use(express.logger('dev'));
app.use(logger('dev'));
app.get('/', function(req, res){
res.render('pets', { pets: pets });
});
app.listen(3000);
console.log('Express listening on port 3000');
console.log('Express listening on port 3000');
+7 -5
Ver Arquivo
@@ -1,8 +1,9 @@
var express = require('../../')
, app = module.exports = express()
, users = require('./db');
// so either you can deal with different types of formatting
// for expected response in index.js
app.get('/', function(req, res){
res.format({
html: function(){
@@ -24,10 +25,11 @@ app.get('/', function(req, res){
});
// or you could write a tiny middleware like
// this to abstract make things a bit more declarative:
// this to add a layer of abstraction
// and make things a bit more declarative:
function format(mod) {
var obj = require(mod);
function format(path) {
var obj = require(path);
return function(req, res){
res.format(obj);
}
@@ -38,4 +40,4 @@ app.get('/users', format('./users'));
if (!module.parent) {
app.listen(3000);
console.log('listening on port 3000');
}
}
+6 -4
Ver Arquivo
@@ -4,17 +4,19 @@
*/
var express = require('../../');
var favicon = require('static-favicon');
var cookie-parser = require('cookie-parser');
var app = module.exports = express();
// ignore GET /favicon.ico
app.use(express.favicon());
app.use(favicon());
// pass a secret to cookieParser() for signed cookies
app.use(express.cookieParser('manny is cool'));
app.use(cookieParser('manny is cool'));
// add req.session cookie support
app.use(express.cookieSession());
app.use(cookieSession());
// do something with the session
app.use(count);
@@ -29,4 +31,4 @@ function count(req, res) {
if (!module.parent) {
app.listen(3000);
console.log('Express server listening on port 3000');
}
}
+11 -7
Ver Arquivo
@@ -4,7 +4,11 @@
*/
var express = require('../../')
, app = module.exports = express();
, app = module.exports = express()
, favicon = require('static-favicon')
, logger = require('morgan')
, cookieParser = require('cookie-parser')
, bodyParser = require('body-parser')
// add favicon() before logger() so
@@ -12,20 +16,20 @@ var express = require('../../')
// logged, because this middleware
// reponds to /favicon.ico and does not
// call next()
app.use(express.favicon());
app.use(favicon());
// custom log format
if ('test' != process.env.NODE_ENV)
app.use(express.logger(':method :url'));
app.use(logger(':method :url'));
// parses request cookies, populating
// req.cookies and req.signedCookies
// when the secret is passed, used
// when the secret is passed, used
// for signing the cookies.
app.use(express.cookieParser('my secret here'));
app.use(cookieParser('my secret here'));
// parses json, x-www-form-urlencoded, and multipart/form-data
app.use(express.bodyParser());
app.use(bodyParser());
app.get('/', function(req, res){
if (req.cookies.remember) {
@@ -51,4 +55,4 @@ app.post('/', function(req, res){
if (!module.parent){
app.listen(3000);
console.log('Express started on port 3000');
}
}
+4 -2
Ver Arquivo
@@ -3,7 +3,9 @@
*/
var express = require('../..')
, logger = require('morgan')
, app = express()
, bodyParser = require('body-parser')
, api = express();
// app middleware
@@ -12,8 +14,8 @@ app.use(express.static(__dirname + '/public'));
// api middleware
api.use(express.logger('dev'));
api.use(express.bodyParser());
api.use(logger('dev'));
api.use(bodyParser());
/**
* CORS support.
+31 -35
Ver Arquivo
@@ -4,6 +4,8 @@
var express = require('../../')
, app = module.exports = express()
, logger = require('morgan')
, favicon = require('static-favicon')
, silent = 'test' == process.env.NODE_ENV;
// general config
@@ -21,18 +23,36 @@ if ('production' == app.settings.env) {
app.disable('verbose errors');
}
app.use(express.favicon());
app.use(favicon());
silent || app.use(express.logger('dev'));
silent || app.use(logger('dev'));
// "app.router" positions our routes
// above the middleware defined below,
// this means that Express will attempt
// to match & call routes _before_ continuing
// on, at which point we assume it's a 404 because
// no route has handled the request.
// Routes
app.use(app.router);
app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/404', function(req, res, next){
// trigger a 404 since no other middleware
// will match /404 after this one, and we're not
// responding here
next();
});
app.get('/403', function(req, res, next){
// trigger a 403 error
var err = new Error('not allowed!');
err.status = 403;
next(err);
});
app.get('/500', function(req, res, next){
// trigger a generic (500) error
next(new Error('keyboard cat!'));
});
// Error handlers
// Since this is the last non-error-handling
// middleware use()d, we assume 404, as nothing else
@@ -44,7 +64,7 @@ app.use(app.router);
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.render('404', { url: req.url });
@@ -81,32 +101,8 @@ app.use(function(err, req, res, next){
res.render('500', { error: err });
});
// Routes
app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/404', function(req, res, next){
// trigger a 404 since no other middleware
// will match /404 after this one, and we're not
// responding here
next();
});
app.get('/403', function(req, res, next){
// trigger a 403 error
var err = new Error('not allowed!');
err.status = 403;
next(err);
});
app.get('/500', function(req, res, next){
// trigger a generic (500) error
next(new Error('keyboard cat!'));
});
if (!module.parent) {
app.listen(3000);
silent || console.log('Express started on port 3000');
}
}
+8 -9
Ver Arquivo
@@ -4,17 +4,11 @@
*/
var express = require('../../')
, logger = require('morgan')
, app = module.exports = express()
, test = app.get('env') == 'test';
if (!test) app.use(express.logger('dev'));
app.use(app.router);
// the error handler is strategically
// placed *below* the app.router; if it
// were above it would not receive errors
// from app.get() etc
app.use(error);
if (!test) app.use(logger('dev'));
// error handling middleware have an arity of 4
// instead of the typical (req, res, next),
@@ -42,7 +36,12 @@ app.get('/next', function(req, res, next){
});
});
// the error handler is placed after routes
// if it were above it would not receive errors
// from app.get() etc
app.use(error);
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
}
+3 -2
Ver Arquivo
@@ -1,5 +1,6 @@
var express = require('../..')
, logger = require('morgan')
, app = express();
app.set('view engine', 'jade');
@@ -23,7 +24,7 @@ User.prototype.toJSON = function(){
}
};
app.use(express.logger('dev'));
app.use(logger('dev'));
// earlier on expose an object
// that we can tack properties on.
@@ -57,4 +58,4 @@ app.get('/user', function(req, res){
});
app.listen(3000);
console.log('app listening on port 3000');
console.log('app listening on port 3000');
+2 -2
Ver Arquivo
@@ -1,5 +1,5 @@
!!! 5
doctype html
html
include header
body
block content
block content
+8 -7
Ver Arquivo
@@ -1,4 +1,8 @@
var express = require('../..');
var logger = require('morgan');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var app = module.exports = express();
@@ -25,20 +29,17 @@ app.response.message = function(msg){
};
// log
if (!module.parent) app.use(express.logger('dev'));
if (!module.parent) app.use(logger('dev'));
// serve static files
app.use(express.static(__dirname + '/public'));
// session support
app.use(express.cookieParser('some secret here'));
app.use(express.session());
app.use(cookieParser('some secret here'));
app.use(session());
// parse request bodies (req.body)
app.use(express.bodyParser());
// support _method (PUT in forms etc)
app.use(express.methodOverride());
app.use(bodyParser());
// expose the "messages" local variable when views are rendered
app.use(function(req, res, next){
+6 -4
Ver Arquivo
@@ -5,6 +5,9 @@
var express = require('../..')
, app = express()
, logger = require('morgan')
, cookieParser = require('cookie-parser')
, bodyParser = require('body-parser')
, site = require('./site')
, post = require('./post')
, user = require('./user');
@@ -13,10 +16,9 @@ var express = require('../..')
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
app.use(express.logger('dev'));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(logger('dev'));
app.use(cookieParser());
app.use(bodyParser());
app.use(express.static(__dirname + '/public'));
// General
-2
Ver Arquivo
@@ -7,8 +7,6 @@ var express = require('../..');
var app = express();
app.use(express.logger('dev'));
// Required by session() middleware
// pass the secret for signed cookies
// (required by session())
+3 -2
Ver Arquivo
@@ -1,9 +1,10 @@
var express = require('../..');
var logger = require('morgan');
var app = express();
// log requests
app.use(express.logger('dev'));
app.use(logger('dev'));
// express on its own has no notion
// of a "file". The express.static()
@@ -41,4 +42,4 @@ console.log('listening on port 3000');
console.log('try:');
console.log(' GET /hello.txt');
console.log(' GET /js/app.js');
console.log(' GET /css/style.css');
console.log(' GET /css/style.css');
+3 -2
Ver Arquivo
@@ -3,6 +3,7 @@
*/
var express = require('../..');
var logger = require('morgan');
/*
edit /etc/hosts:
@@ -16,14 +17,14 @@ edit /etc/hosts:
var main = express();
main.use(express.logger('dev'));
main.use(logger('dev'));
main.get('/', function(req, res){
res.send('Hello from main app!')
});
main.get('/:sub', function(req, res){
res.send('requsted ' + req.params.sub);
res.send('requested ' + req.params.sub);
});
// Redirect app
+1 -1
Ver Arquivo
@@ -1,4 +1,4 @@
doctype 5
doctype html
html
head
title= title
+20 -25
Ver Arquivo
@@ -40,29 +40,6 @@ app.use('/api', function(req, res, next){
next();
});
// position our routes above the error handling middleware,
// and below our API middleware, since we want the API validation
// to take place BEFORE our routes
app.use(app.router);
// middleware with an arity of 4 are considered
// error handling middleware. When you next(err)
// it will be passed through the defined middleware
// in order, but ONLY those with an arity of 4, ignoring
// regular middleware.
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 });
});
// 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" });
});
// map of valid api keys, typically mapped to
// account info with some sort of database like redis.
// api keys do _not_ serve as authentication, merely to
@@ -104,12 +81,30 @@ app.get('/api/repos', function(req, res, next){
app.get('/api/user/:name/repos', function(req, res, next){
var name = req.params.name
, user = userRepos[name];
if (user) res.send(user);
else next();
});
// middleware with an arity of 4 are considered
// error handling middleware. When you next(err)
// it will be passed through the defined middleware
// in order, but ONLY those with an arity of 4, ignoring
// regular middleware.
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 });
});
// 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" });
});
if (!module.parent) {
app.listen(3000);
console.log('Express server listening on port 3000');
}
}
+148 -145
Ver Arquivo
@@ -2,17 +2,15 @@
* Module dependencies.
*/
var connect = require('connect')
var mixin = require('utils-merge')
, escapeHtml = require('escape-html')
, Router = require('./router')
, methods = require('methods')
, middleware = require('./middleware')
, middleware = require('./middleware/init')
, query = require('./middleware/query')
, debug = require('debug')('express:application')
, locals = require('./utils').locals
, View = require('./view')
, utils = connect.utils
, path = require('path')
, http = require('http')
, join = path.join;
, http = require('http');
/**
* Application prototype.
@@ -47,13 +45,11 @@ app.defaultConfiguration = function(){
// default settings
this.enable('x-powered-by');
this.enable('etag');
this.set('env', process.env.NODE_ENV || 'development');
var env = process.env.NODE_ENV || 'development';
this.set('env', env);
this.set('subdomain offset', 2);
debug('booting in %s mode', this.get('env'));
// implicit middleware
this.use(connect.query());
this.use(middleware.init(this));
debug('booting in %s mode', env);
// inherit protos
this.on('mount', function(parent){
@@ -63,18 +59,11 @@ app.defaultConfiguration = function(){
this.settings.__proto__ = parent.settings;
});
// router
this._router = new Router(this);
this.routes = this._router.map;
this.__defineGetter__('router', function(){
this._usedRouter = true;
this._router.caseSensitive = this.enabled('case sensitive routing');
this._router.strict = this.enabled('strict routing');
return this._router.middleware;
});
// setup locals
this.locals = locals(this);
this.locals = Object.create(null);
// top-most app is mounted at /
this.mountpath = '/';
// default locals
this.locals.settings = this.settings;
@@ -84,18 +73,94 @@ app.defaultConfiguration = function(){
this.set('views', process.cwd() + '/views');
this.set('jsonp callback name', 'callback');
this.configure('development', function(){
this.set('json spaces', 2);
});
this.configure('production', function(){
if (env === 'production') {
this.enable('view cache');
}
Object.defineProperty(this, 'router', {
get: function() {
throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
}
});
};
/**
* Proxy `connect#use()` to apply settings to
* mounted applications.
* lazily adds the base router if it has not yet been added.
*
* We cannot add the base router in the defaultConfiguration because
* it reads app settings which might be set after that has run.
*
* @api private
*/
app.lazyrouter = function() {
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(middleware.init(this));
}
};
/**
* Dispatch a req, res pair into the application. Starts pipeline processing.
*
* If no _done_ callback is provided, then default error handlers will respond
* in the event of an error bubbling through the stack.
*
* @api private
*/
app.handle = function(req, res, done) {
var env = this.get('env');
this._router.handle(req, res, function(err) {
if (done) {
return done(err);
}
// unhandled error
if (err) {
// default to 500
if (res.statusCode < 400) res.statusCode = 500;
debug('default %s', res.statusCode);
// respect err.status
if (err.status) res.statusCode = err.status;
// production gets a basic error message
var msg = 'production' == env
? http.STATUS_CODES[res.statusCode]
: err.stack || err.toString();
msg = escapeHtml(msg);
// log to stderr in a non-test env
if ('test' != env) console.error(err.stack || err.toString());
if (res.headersSent) return req.socket.destroy();
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', Buffer.byteLength(msg));
if ('HEAD' == req.method) return res.end();
res.end(msg);
return;
}
// 404
debug('default 404');
res.statusCode = 404;
res.setHeader('Content-Type', 'text/html');
if ('HEAD' == req.method) return res.end();
res.end('Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl) + '\n');
});
};
/**
* Proxy `Router#use()` to add middleware to the app router.
* See Router#use() documentation for details.
*
* If the _fn_ parameter is an express app, then it will be
* mounted at the _route_ specified.
*
* @param {String|Function|Server} route
* @param {Function|Server} fn
@@ -104,20 +169,21 @@ app.defaultConfiguration = function(){
*/
app.use = function(route, fn){
var app;
var mount_app;
// default route to '/'
if ('string' != typeof route) fn = route, route = '/';
// express app
if (fn.handle && fn.set) app = fn;
if (fn.handle && fn.set) mount_app = fn;
// restore .app property on req and res
if (app) {
app.route = route;
if (mount_app) {
debug('.use app under %s', route);
mount_app.mountpath = route;
fn = function(req, res, next) {
var orig = req.app;
app.handle(req, res, function(err){
mount_app.handle(req, res, function(err) {
req.__proto__ = orig.request;
res.__proto__ = orig.response;
next(err);
@@ -125,17 +191,31 @@ app.use = function(route, fn){
};
}
connect.proto.use.call(this, route, fn);
this.lazyrouter();
this._router.use(route, fn);
// mounted an app
if (app) {
app.parent = this;
app.emit('mount', this);
if (mount_app) {
mount_app.parent = this;
mount_app.emit('mount', this);
}
return this;
};
/**
* Proxy to the app `Router#route()`
* Returns a new `Route` instance for the _path_.
*
* Routes are isolated middleware stacks for specific paths.
* See the Route api docs for details.
*
* @api public
*/
app.route = function(path){
};
/**
* Register the given template engine callback `fn`
* as `ext`.
@@ -178,30 +258,10 @@ app.engine = function(ext, fn){
};
/**
* Map the given param placeholder `name`(s) to the given callback(s).
* Proxy to `Router#param()` with one added api feature. The _name_ parameter
* can be an array of names.
*
* Parameter mapping is used to provide pre-conditions to routes
* which use normalized placeholders. For example a _:user_id_ parameter
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the same signature as middleware, the only differencing
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
*
* app.param('user_id', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* next(err);
* } else if (user) {
* req.user = user;
* next();
* } else {
* next(new Error('failed to load user'));
* }
* });
* });
* See the Router#param() docs for more details.
*
* @param {String|Array} name
* @param {Function} fn
@@ -210,27 +270,17 @@ app.engine = function(ext, fn){
*/
app.param = function(name, fn){
var self = this
, fns = [].slice.call(arguments, 1);
var self = this;
self.lazyrouter();
// array
if (Array.isArray(name)) {
name.forEach(function(name){
fns.forEach(function(fn){
self.param(name, fn);
});
});
// param logic
} else if ('function' == typeof name) {
this._router.param(name);
// single
} else {
if (':' == name[0]) name = name.substr(1);
fns.forEach(function(fn){
self._router.param(name, fn);
name.forEach(function(key) {
self.param(key, fn);
});
return this;
}
self._router.param(name, fn);
return this;
};
@@ -244,7 +294,7 @@ app.param = function(name, fn){
* Mounted servers inherit their parent server's settings.
*
* @param {String} setting
* @param {String} val
* @param {*} [val]
* @return {Server} for chaining
* @api public
*/
@@ -274,7 +324,7 @@ app.set = function(setting, val){
app.path = function(){
return this.parent
? this.parent.path() + this.route
? this.parent.path() + this.mountpath
: '';
};
@@ -340,60 +390,6 @@ app.disable = function(setting){
return this.set(setting, false);
};
/**
* Configure callback for zero or more envs,
* when no `env` is specified that callback will
* be invoked for all environments. Any combination
* can be used multiple times, in any order desired.
*
* Examples:
*
* app.configure(function(){
* // executed for all envs
* });
*
* app.configure('stage', function(){
* // executed staging env
* });
*
* app.configure('stage', 'production', function(){
* // executed for stage and production
* });
*
* Note:
*
* These callbacks are invoked immediately, and
* are effectively sugar for the following:
*
* var env = process.env.NODE_ENV || 'development';
*
* switch (env) {
* case 'development':
* ...
* break;
* case 'stage':
* ...
* break;
* case 'production':
* ...
* break;
* }
*
* @param {String} env...
* @param {Function} fn
* @return {app} for chaining
* @api public
*/
app.configure = function(env, fn){
var envs = 'all'
, args = [].slice.call(arguments);
fn = args.pop();
if (args.length) envs = args;
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
return this;
};
/**
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
*/
@@ -402,16 +398,17 @@ methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
this.lazyrouter();
// deprecated
if (Array.isArray(path)) {
console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
}
// if no router attached yet, attach the router
if (!this._usedRouter) this.use(this.router);
// setup route
this._router[method].apply(this._router, arguments);
var route = this._router.route(path);
for (var i=1 ; i<arguments.length ; ++i) {
route[method](arguments[i]);
}
return this;
};
});
@@ -427,10 +424,16 @@ methods.forEach(function(method){
*/
app.all = function(path){
this.lazyrouter();
var route = this._router.route(path);
var args = arguments;
methods.forEach(function(method){
app[method].apply(this, args);
}, this);
for (var i=1 ; i<args.length ; ++i) {
route[method](args[i]);
}
});
return this;
};
@@ -467,13 +470,13 @@ app.render = function(name, options, fn){
}
// merge app.locals
utils.merge(opts, this.locals);
mixin(opts, this.locals);
// merge options._locals
if (options._locals) utils.merge(opts, options._locals);
if (options._locals) mixin(opts, options._locals);
// merge options
utils.merge(opts, options);
mixin(opts, options);
// set .cache unless explicitly provided
opts.cache = null == opts.cache
@@ -492,7 +495,7 @@ app.render = function(name, options, fn){
});
if (!view.path) {
var err = new Error('Failed to lookup view "' + name + '"');
var err = new Error('Failed to lookup view "' + name + '" in views directory "' + view.root + '"');
err.view = view;
return fn(err);
}
+20 -41
Ver Arquivo
@@ -2,13 +2,19 @@
* Module dependencies.
*/
var connect = require('connect')
, proto = require('./application')
var EventEmitter = require('events').EventEmitter;
var merge = require('merge-descriptors')
, mixin = require('utils-merge')
var proto = require('./application')
, Route = require('./router/route')
, Router = require('./router')
, req = require('./request')
, res = require('./response')
, utils = connect.utils;
// monkey patch ServerResponse methods
require('./patch')
/**
* Expose `createApplication()`.
@@ -16,12 +22,6 @@ var connect = require('connect')
exports = module.exports = createApplication;
/**
* Expose mime.
*/
exports.mime = connect.mime;
/**
* Create an express application.
*
@@ -30,41 +30,19 @@ exports.mime = connect.mime;
*/
function createApplication() {
var app = connect();
utils.merge(app, proto);
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, proto);
mixin(app, EventEmitter.prototype);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
/**
* Expose connect.middleware as express.*
* for example `express.logger` etc.
*/
for (var key in connect.middleware) {
Object.defineProperty(
exports
, key
, Object.getOwnPropertyDescriptor(connect.middleware, key));
}
/**
* Error on createServer().
*/
exports.createServer = function(){
console.warn('Warning: express.createServer() is deprecated, express');
console.warn('applications no longer inherit from http.Server,');
console.warn('please use:');
console.warn('');
console.warn(' var express = require("express");');
console.warn(' var app = express();');
console.warn('');
return createApplication();
};
/**
* Expose the prototypes.
*/
@@ -80,7 +58,8 @@ exports.response = res;
exports.Route = Route;
exports.Router = Router;
// Error handler title
exports.errorHandler.title = 'Express';
/**
* Expose middleware
*/
exports.static = require('./middleware/static');
+2 -8
Ver Arquivo
@@ -1,10 +1,3 @@
/**
* Module dependencies.
*/
var utils = require('./utils');
/**
* Initialization middleware, exposing the
* request and response to eachother, as well
@@ -25,8 +18,9 @@ exports.init = function(app){
req.__proto__ = app.request;
res.__proto__ = app.response;
res.locals = res.locals || utils.locals(res);
res.locals = res.locals || Object.create(null);
next();
}
};
+35
Ver Arquivo
@@ -0,0 +1,35 @@
var qs = require('qs');
var parseUrl = require('../utils').parseUrl;
/**
* 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){
return function query(req, res, next){
if (!req.query) {
req.query = ~req.url.indexOf('?')
? qs.parse(parseUrl(req).query, options)
: {};
}
next();
};
};
+87
Ver Arquivo
@@ -0,0 +1,87 @@
/*!
* Connect - static
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var send = require('send')
, utils = require('../utils')
, parse = utils.parseUrl
, url = require('url');
/**
* Static:
*
* Static file server with the given `root` path.
*
* Examples:
*
* var oneDay = 86400000;
*
* connect()
* .use(connect.static(__dirname + '/public'))
*
* connect()
* .use(connect.static(__dirname + '/public', { maxAge: oneDay }))
*
* Options:
*
* - `maxAge` Browser cache maxAge in milliseconds. defaults to 0
* - `hidden` Allow transfer of hidden files. defaults to false
* - `redirect` Redirect to trailing "/" when the pathname is a dir. defaults to true
* - `index` Default file name, defaults to 'index.html'
*
* @param {String} root
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function(root, options){
options = options || {};
// root required
if (!root) throw new Error('static() root path required');
// default redirect
var redirect = false !== options.redirect;
return function staticMiddleware(req, res, next) {
if ('GET' != req.method && 'HEAD' != req.method) return next();
var originalUrl = url.parse(req.originalUrl);
var path = parse(req).pathname;
if (path == '/' && originalUrl.pathname[originalUrl.pathname.length - 1] != '/') {
return directory();
}
function directory() {
if (!redirect) return next();
var target;
originalUrl.pathname += '/';
target = url.format(originalUrl);
res.statusCode = 303;
res.setHeader('Location', target);
res.end('Redirecting to ' + utils.escape(target));
}
function error(err) {
if (404 == err.status) return next();
next(err);
}
send(req, path)
.maxage(options.maxAge || 0)
.root(root)
.index(options.index || 'index.html')
.hidden(options.hidden)
.on('error', error)
.on('directory', directory)
.pipe(res);
};
};
+54
Ver Arquivo
@@ -0,0 +1,54 @@
/*!
* Connect
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var http = require('http');
var ServerResponse = http.ServerResponse;
// apply only once
if (ServerResponse.prototype._hasConnectPatch) {
return;
}
// original methods
var setHeader = ServerResponse.prototype.setHeader;
var writeHead = ServerResponse.prototype.writeHead;
/**
* Set header `field` to `val`, special-casing
* the `Set-Cookie` field for multiple support.
*
* @param {String} field
* @param {String} val
* @api public
*/
ServerResponse.prototype.setHeader = function(field, val){
var key = field.toLowerCase();
if ('content-type' == key && this.charset) {
val += '; charset=' + this.charset;
}
return setHeader.call(this, field, val);
};
ServerResponse.prototype.writeHead = function(statusCode, reasonPhrase, headers){
if (typeof reasonPhrase === 'object') headers = reasonPhrase;
if (typeof headers === 'object') {
Object.keys(headers).forEach(function(key){
this.setHeader(key, headers[key]);
}, this);
}
if (!this._emittedHeader) this.emit('header');
this._emittedHeader = true;
return writeHead.call(this, statusCode, reasonPhrase);
};
ServerResponse.prototype._hasConnectPatch = true;
+28 -124
Ver Arquivo
@@ -3,13 +3,13 @@
* Module dependencies.
*/
var accepts = require('accepts');
var typeis = require('type-is');
var http = require('http')
, utils = require('./utils')
, connect = require('connect')
, fresh = require('fresh')
, parseRange = require('range-parser')
, parse = connect.utils.parseUrl
, mime = connect.mime;
, parse = utils.parseUrl
/**
* Request prototype.
@@ -56,6 +56,8 @@ req.header = function(name){
};
/**
* To do: update docs.
*
* Check if the given `type(s)` is acceptable, returning
* the best match when true, otherwise `undefined`, in which
* case you should respond with 406 "Not Acceptable".
@@ -99,9 +101,9 @@ req.header = function(name){
* @api public
*/
req.accepts = function(type){
var args = arguments.length > 1 ? [].slice.apply(arguments) : type;
return utils.accepts(args, this.get('Accept'));
req.accepts = function(){
var accept = accepts(this);
return accept.types.apply(accept, arguments);
};
/**
@@ -112,11 +114,15 @@ req.accepts = function(type){
* @api public
*/
req.acceptsEncoding = function(encoding){
return !! ~this.acceptedEncodings.indexOf(encoding);
req.acceptsEncoding = // backwards compatibility
req.acceptsEncodings = function(){
var accept = accepts(this);
return accept.encodings.apply(accept, arguments);
};
/**
* To do: update docs.
*
* Check if the given `charset` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
@@ -125,14 +131,15 @@ req.acceptsEncoding = function(encoding){
* @api public
*/
req.acceptsCharset = function(charset){
var accepted = this.acceptedCharsets;
return accepted.length
? !! ~accepted.indexOf(charset)
: true;
req.acceptsCharset = // backwards compatibility
req.acceptsCharsets = function(){
var accept = accepts(this);
return accept.charsets.apply(accept, arguments);
};
/**
* To do: update docs.
*
* Check if the given `lang` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
@@ -141,11 +148,10 @@ req.acceptsCharset = function(charset){
* @api public
*/
req.acceptsLanguage = function(lang){
var accepted = this.acceptedLanguages;
return accepted.length
? !! ~accepted.indexOf(lang)
: true;
req.acceptsLanguage = // backwards compatibility
req.acceptsLanguages = function(lang){
var accept = accepts(this);
return accept.languages.apply(accept, arguments);
};
/**
@@ -174,98 +180,6 @@ req.range = function(size){
return parseRange(size, range);
};
/**
* Return an array of encodings.
*
* Examples:
*
* ['gzip', 'deflate']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedEncodings', function(){
var accept = this.get('Accept-Encoding');
return accept
? accept.trim().split(/ *, */)
: [];
});
/**
* Return an array of Accepted media types
* ordered from highest quality to lowest.
*
* Examples:
*
* [ { value: 'application/json',
* quality: 1,
* type: 'application',
* subtype: 'json' },
* { value: 'text/html',
* quality: 0.5,
* type: 'text',
* subtype: 'html' } ]
*
* @return {Array}
* @api public
*/
req.__defineGetter__('accepted', function(){
var accept = this.get('Accept');
return accept
? utils.parseAccept(accept)
: [];
});
/**
* Return an array of Accepted languages
* ordered from highest quality to lowest.
*
* Examples:
*
* Accept-Language: en;q=.5, en-us
* ['en-us', 'en']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedLanguages', function(){
var accept = this.get('Accept-Language');
return accept
? utils
.parseParams(accept)
.map(function(obj){
return obj.value;
})
: [];
});
/**
* Return an array of Accepted charsets
* ordered from highest quality to lowest.
*
* Examples:
*
* Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8
* ['unicode-1-1', 'iso-8859-5']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedCharsets', function(){
var accept = this.get('Accept-Charset');
return accept
? utils
.parseParams(accept)
.map(function(obj){
return obj.value;
})
: [];
});
/**
* Return the value of param `name` when present or `defaultValue`.
*
@@ -275,7 +189,7 @@ req.__defineGetter__('acceptedCharsets', function(){
*
* To utilize request bodies, `req.body`
* should be an object. This can be done by using
* the `connect.bodyParser()` middleware.
* the `bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} [defaultValue]
@@ -319,19 +233,9 @@ req.param = function(name, defaultValue){
* @api public
*/
req.is = function(type){
var ct = this.get('Content-Type');
if (!ct) return false;
ct = ct.split(';')[0];
if (!~type.indexOf('/')) type = mime.lookup(type);
if (~type.indexOf('*')) {
type = type.split('/');
ct = ct.split('/');
if ('*' == type[0] && type[1] == ct[1]) return true;
if ('*' == type[1] && type[0] == ct[0]) return true;
return false;
}
return !! ~ct.indexOf(type);
req.is = function(types){
if (!Array.isArray(types)) types = [].slice.call(arguments);
return typeis(this, types);
};
/**
+83 -34
Ver Arquivo
@@ -4,8 +4,8 @@
var http = require('http')
, path = require('path')
, connect = require('connect')
, utils = connect.utils
, mixin = require('utils-merge')
, escapeHtml = require('escape-html')
, sign = require('cookie-signature').sign
, normalizeType = require('./utils').normalizeType
, normalizeTypes = require('./utils').normalizeTypes
@@ -13,10 +13,10 @@ var http = require('http')
, statusCodes = http.STATUS_CODES
, cookie = require('cookie')
, send = require('send')
, mime = connect.mime
, resolve = require('url').resolve
, basename = path.basename
, extname = path.extname
, join = path.join;
, mime = send.mime
/**
* Response prototype.
@@ -55,7 +55,9 @@ res.status = function(code){
*/
res.links = function(links){
return this.set('Link', Object.keys(links).map(function(rel){
var link = this.get('Link') || '';
if (link) link += ', ';
return this.set('Link', link + Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
}).join(', '));
};
@@ -131,7 +133,7 @@ res.send = function(body){
// ETag support
// TODO: W/ support
if (app.settings.etag && len > 1024 && 'GET' == req.method) {
if (app.settings.etag && len && 'GET' == req.method) {
if (!this.get('ETag')) {
this.set('ETag', etag(body));
}
@@ -237,10 +239,10 @@ res.jsonp = function(obj){
// jsonp
if (callback) {
if (callback instanceof Array) callback = callback[0];
if (Array.isArray(callback)) callback = callback[0];
this.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = cb + ' && ' + cb + '(' + body + ');';
body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');';
}
return this.send(body);
@@ -309,23 +311,23 @@ res.sendfile = function(path, options, fn){
// clean up
cleanup();
if (!self.headerSent) self.removeHeader('Content-Disposition');
if (!self.headersSent) self.removeHeader('Content-Disposition');
// callback available
if (fn) return fn(err);
// list in limbo if there's no callback
if (self.headerSent) return;
if (self.headersSent) return;
// delegate
next(err);
}
// streaming
function stream() {
function stream(stream) {
if (done) return;
cleanup();
if (fn) self.on('finish', fn);
if (fn) stream.on('end', fn);
}
// cleanup
@@ -350,7 +352,7 @@ res.sendfile = function(path, options, fn){
* Optionally providing an alternate attachment `filename`,
* and optional callback `fn(err)`. The callback is invoked
* when the data transfer is complete, or when an error has
* ocurred. Be sure to check `res.headerSent` if you plan to respond.
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
*
* This method uses `res.sendfile()`.
*
@@ -463,10 +465,13 @@ res.format = function(obj){
var key = req.accepts(keys);
this.set('Vary', 'Accept');
this.vary("Accept");
if (key) {
this.set('Content-Type', normalizeType(key).value);
var type = normalizeType(key).value;
var charset = mime.charsets.lookup(type);
if (charset) type += '; charset=' + charset;
this.set('Content-Type', type);
obj[key](req, this, next);
} else if (fn) {
fn();
@@ -552,7 +557,7 @@ res.get = function(field){
res.clearCookie = function(name, options){
var opts = { expires: new Date(1), path: '/' };
return this.cookie(name, '', options
? utils.merge(opts, options)
? mixin(opts, options)
: opts);
};
@@ -580,10 +585,10 @@ res.clearCookie = function(name, options){
*/
res.cookie = function(name, val, options){
options = utils.merge({}, options);
options = mixin({}, options);
var secret = this.req.secret;
var signed = options.signed;
if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
if (signed && !secret) throw new Error('cookieParser("secret") required for signed cookies');
if ('number' == typeof val) val = val.toString();
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
if (signed) val = 's:' + sign(val, secret);
@@ -592,7 +597,18 @@ res.cookie = function(name, val, options){
options.maxAge /= 1000;
}
if (null == options.path) options.path = '/';
this.set('Set-Cookie', cookie.serialize(name, String(val), options));
var headerVal = cookie.serialize(name, String(val), options);
// supports multiple 'res.cookie' calls by getting previous value
var prev = this.get('Set-Cookie');
if (prev) {
if (Array.isArray(prev)) {
headerVal = prev.concat(headerVal);
} else {
headerVal = [prev, headerVal];
}
}
this.set('Set-Cookie', headerVal);
return this;
};
@@ -600,8 +616,7 @@ res.cookie = function(name, val, options){
/**
* Set the location header to `url`.
*
* The given `url` can also be the name of a mapped url, for
* example by default express supports "back" which redirects
* The given `url` can also be "back", which redirects
* to the _Referrer_ or _Referer_ headers or "/".
*
* Examples:
@@ -629,22 +644,19 @@ res.cookie = function(name, val, options){
res.location = function(url){
var app = this.app
, req = this.req;
, req = this.req
, path;
// setup redirect map
var map = { back: req.get('Referrer') || '/' };
// perform redirect
url = map[url] || url;
// "back" is an alias for the referrer
if ('back' == url) url = req.get('Referrer') || '/';
// relative
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
var path
// relative to path
if ('.' == url[0]) {
path = req.originalUrl.split('?')[0]
url = path + ('/' == path[path.length - 1] ? '' : '/') + url;
path = req.originalUrl.split('?')[0];
path = path + ('/' == path[path.length - 1] ? '' : '/');
url = resolve(path, url);
// relative to mount-point
} else if ('/' != url[0]) {
path = app.path();
@@ -679,8 +691,7 @@ res.location = function(url){
*/
res.redirect = function(url){
var app = this.app
, head = 'HEAD' == this.req.method
var head = 'HEAD' == this.req.method
, status = 302
, body;
@@ -705,7 +716,7 @@ res.redirect = function(url){
},
html: function(){
var u = utils.escape(url);
var u = escapeHtml(url);
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
},
@@ -720,6 +731,44 @@ res.redirect = function(url){
this.end(head ? null : body);
};
/**
* Add `field` to Vary. If already present in the Vary set, then
* this call is simply ignored.
*
* @param {Array|String} field
* @param {ServerResponse} for chaining
* @api public
*/
res.vary = function(field){
var self = this;
// nothing
if (!field) return this;
// array
if (Array.isArray(field)) {
field.forEach(function(field){
self.vary(field);
});
return;
}
var vary = this.get('Vary');
// append
if (vary) {
vary = vary.split(/ *, */);
if (!~vary.indexOf(field)) vary.push(field);
this.set('Vary', vary.join(', '));
return this;
}
// set
this.set('Vary', field);
return this;
};
/**
* Render `view` with the given `options` and optional callback `fn`.
* When a callback function is given a response will _not_ be made
+273 -213
Ver Arquivo
@@ -6,7 +6,7 @@ var Route = require('./route')
, utils = require('../utils')
, methods = require('methods')
, debug = require('debug')('express:router')
, parse = require('connect').utils.parseUrl;
, parseUrl = utils.parseUrl;
/**
* Expose `Router` constructor.
@@ -24,22 +24,47 @@ exports = module.exports = Router;
function Router(options) {
options = options || {};
var self = this;
this.map = {};
this.params = {};
this._params = [];
this.caseSensitive = options.caseSensitive;
this.strict = options.strict;
this.middleware = function router(req, res, next){
self._dispatch(req, res, next);
};
self.params = {};
self._params = [];
self.caseSensitive = options.caseSensitive;
self.strict = options.strict;
self.stack = [];
self.middleware = self.handle.bind(self);
}
/**
* Register a param callback `fn` for the given `name`.
* Map the given param placeholder `name`(s) to the given callback.
*
* @param {String|Function} name
* Parameter mapping is used to provide pre-conditions to routes
* which use normalized placeholders. For example a _:user_id_ parameter
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
*
* Just like in middleware, you must either respond to the request or call next
* to avoid stalling the request.
*
* app.param('user_id', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* return next(err);
* } else if (!user) {
* return next(new Error('failed to load user'));
* }
* req.user = user;
* next();
* });
* });
*
* @param {String} name
* @param {Function} fn
* @return {Router} for chaining
* @return {app} for chaining
* @api public
*/
@@ -55,6 +80,10 @@ Router.prototype.param = function(name, fn){
, len = params.length
, ret;
if (name[0] === ':') {
name = name.substr(1);
}
for (var i = 0; i < len; ++i) {
if (ret = params[i](name, fn)) {
fn = ret;
@@ -72,240 +101,271 @@ Router.prototype.param = function(name, fn){
};
/**
* Route dispatcher aka the route "middleware".
* Dispatch a req, res into the router.
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @param {Function} next
* @api private
*/
Router.prototype._dispatch = function(req, res, next){
var params = this.params
, self = this;
debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
// route dispatch
(function pass(i, err){
var paramCallbacks
, paramIndex = 0
, paramVal
, route
, keys
, key;
// match next route
function nextRoute(err) {
pass(req._route_index + 1, err);
}
// match route
req.route = route = self.matchRequest(req, i);
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
// no route
if (!route) return next(err);
debug('matched %s %s', route.method, route.path);
// we have a route
// start at param 0
req.params = route.params;
keys = route.keys;
i = 0;
// param callbacks
function param(err) {
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
try {
if ('route' == err) {
nextRoute();
} else if (err) {
i = 0;
callbacks(err);
} else if (paramCallbacks && undefined !== paramVal) {
paramCallback();
} else if (key) {
param();
} else {
i = 0;
callbacks();
}
} catch (err) {
param(err);
}
};
param(err);
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
if (err || !fn) return param(err);
fn(req, res, paramCallback, paramVal, key.name);
}
// invoke route callbacks
function callbacks(err) {
var fn = route.callbacks[i++];
try {
if ('route' == err) {
nextRoute();
} else if (err && fn) {
if (fn.length < 4) return callbacks(err);
fn(err, req, res, callbacks);
} else if (fn) {
if (fn.length < 4) return fn(req, res, callbacks);
callbacks();
} else {
nextRoute(err);
}
} catch (err) {
callbacks(err);
}
}
})(0);
};
/**
* Respond to __OPTIONS__ method.
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @api private
*/
Router.prototype._options = function(req, res){
var path = parse(req).pathname
, body = this._optionsFor(path).join(',');
res.set('Allow', body).send(body);
};
/**
* Return an array of HTTP verbs or "options" for `path`.
*
* @param {String} path
* @return {Array}
* @api private
*/
Router.prototype._optionsFor = function(path){
Router.prototype.handle = function(req, res, done) {
var self = this;
return methods.filter(function(method){
var routes = self.map[method];
if (!routes || 'options' == method) return;
for (var i = 0, len = routes.length; i < len; ++i) {
if (routes[i].match(path)) return true;
}
}).map(function(method){
return method.toUpperCase();
});
};
/**
* Attempt to match a route for `req`
* with optional starting index of `i`
* defaulting to 0.
*
* @param {IncomingMessage} req
* @param {Number} i
* @return {Route}
* @api private
*/
debug('dispatching %s %s', req.method, req.url);
Router.prototype.matchRequest = function(req, i, head){
var method = req.method.toLowerCase()
, url = parse(req)
, path = url.pathname
, routes = this.map
, i = i || 0
, route;
var method = req.method.toLowerCase();
// HEAD support
if (!head && 'head' == method) {
route = this.matchRequest(req, i, true);
if (route) return route;
method = 'get';
var search = 1 + req.url.indexOf('?');
var pathlength = search ? search - 1 : req.url.length;
var fqdn = 1 + req.url.substr(0, pathlength).indexOf('://');
var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
var idx = 0;
var removed = '';
var slashAdded = false;
// store options for OPTIONS request
// only used if OPTIONS request
var options = [];
// middleware and routes
var stack = this.stack;
// for options requests, respond with a default if nothing else responds
if (method === 'options') {
var old = done;
done = function(err) {
if (err || options.length === 0) return old(err);
var body = options.join(',');
return res.set('Allow', body).send(body);
};
}
// routes for this method
if (routes = routes[method]) {
(function next(err) {
if (err === 'route') {
err = undefined;
}
// matching routes
for (var len = routes.length; i < len; ++i) {
route = routes[i];
if (route.match(path)) {
req._route_index = i;
return route;
var layer = stack[idx++];
if (!layer) {
return done(err);
}
if (slashAdded) {
req.url = req.url.substr(1);
slashAdded = false;
}
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 = '/';
// route object and not middleware
var route = layer.route;
// handle route
if (route) {
// we don't run any routs with error first
if (err || !route.match(path)) {
return next(err);
}
req.params = route.params;
// we can now dispatch to the route
if (method === 'options' && !route.methods['options']) {
options.push.apply(options, route._options());
}
return self.process_params(route, req, res, function(err) {
if (err) {
return next(err);
}
route.dispatch(req, res, next);
});
}
// skip this layer if the path doesn't match.
if (0 != path.toLowerCase().indexOf(layer.path.toLowerCase())) return next(err);
var c = path[layer.path.length];
if (c && '/' != c && '.' != c) return next(err);
// Trim off the part of the url that matches the route
// middleware (.use stuff) needs to have the path stripped
debug('trim prefix (%s) from url %s', removed, req.url);
removed = layer.path;
req.url = protohost + req.url.substr(protohost.length + removed.length);
// Ensure leading slash
if (!fqdn && '/' != req.url[0]) {
req.url = '/' + req.url;
slashAdded = true;
}
debug('%s %s : %s', layer.handle.name || 'anonymous', layer.path, req.originalUrl);
var arity = layer.handle.length;
if (err) {
if (arity === 4) {
layer.handle(err, req, res, next);
} else {
next(err);
}
} else if (arity < 4) {
layer.handle(req, res, next);
} else {
next(err);
}
} catch (err) {
next(err);
}
})();
};
/**
* Process any parameters for the route.
*
* @api private
*/
Router.prototype.process_params = function(route, req, res, done) {
var self = this;
var params = this.params;
// captured parameters from the route, keys and values
var keys = route.keys || [];
var i = 0;
var paramIndex = 0;
var key;
var paramVal;
var paramCallbacks;
// process params in order
// param callbacks can be async
function param(err) {
if (err) {
return done(err);
}
if (i >= keys.length ) {
return done();
}
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
try {
if (paramCallbacks && undefined !== paramVal) {
return paramCallback();
} else if (key) {
return param();
}
} catch (err) {
done(err);
}
done();
};
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
if (err || !fn) return param(err);
fn(req, res, paramCallback, paramVal, key.name);
}
param();
};
/**
* Attempt to match a route for `method`
* and `url` with optional starting
* index of `i` defaulting to 0.
* Use the given middleware function, with optional path, defaulting to "/".
*
* @param {String} method
* @param {String} url
* @param {Number} i
* @return {Route}
* @api private
* Use (like `.all`) will run for any http METHOD, but it will not add
* handlers for those methods so OPTIONS requests will not consider `.use`
* functions even if they could respond.
*
* The other difference is that _route_ path is stripped and not visible
* to the handler function. The main effect of this feature is that mounted
* 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
*/
Router.prototype.match = function(method, url, i, head){
var req = { method: method, url: url };
return this.matchRequest(req, i, head);
Router.prototype.use = function(route, fn){
// default route to '/'
if ('string' != typeof route) {
fn = route;
route = '/';
}
// strip trailing slash
if ('/' == route[route.length - 1]) {
route = route.slice(0, -1);
}
// add the middleware
debug('use %s %s', route || '/', fn.name || 'anonymous');
this.stack.push({ path: route, handle: fn });
return this;
};
/**
* Route `method`, `path`, and one or more callbacks.
* Create a new Route for the given path.
*
* Each route contains a separate middleware stack and VERB handlers.
*
* See the Route api documentation for details on adding handlers
* and middleware to routes.
*
* @param {String} method
* @param {String} path
* @param {Function} callback...
* @return {Router} for chaining
* @api private
* @return {Route}
* @api public
*/
Router.prototype.route = function(method, path, callbacks){
var method = method.toLowerCase()
, callbacks = utils.flatten([].slice.call(arguments, 2));
// ensure path was given
if (!path) throw new Error('Router#' + method + '() requires a path');
// ensure all callbacks are functions
callbacks.forEach(function(fn, i){
if ('function' == typeof fn) return;
var type = {}.toString.call(fn);
var msg = '.' + method + '() requires callback functions but got a ' + type;
throw new Error(msg);
});
// create the route
debug('defined %s %s', method, path);
var route = new Route(method, path, callbacks, {
Router.prototype.route = function(path){
var route = new Route(path, {
sensitive: this.caseSensitive,
strict: this.strict
});
// add it
(this.map[method] = this.map[method] || []).push(route);
return this;
this.stack.push({ path: path, route: route });
return route;
};
/**
* Special-cased "all" method, applying the given route `path`,
* middleware, and callback to _every_ HTTP method.
*
* @param {String} path
* @param {Function} ...
* @return {app} for chaining
* @api public
*/
Router.prototype.all = function(path, fn) {
var route = this.route(path);
methods.forEach(function(method){
route[method](fn);
});
};
// create Router#VERB functions
methods.forEach(function(method){
Router.prototype[method] = function(path){
var args = [method].concat([].slice.call(arguments));
this.route.apply(this, args);
return this;
Router.prototype[method] = function(path, fn){
var self = this;
self.route(path)[method](fn);
return self;
};
});
+159 -12
Ver Arquivo
@@ -3,7 +3,9 @@
* Module dependencies.
*/
var utils = require('../utils');
var utils = require('../utils')
, debug = require('debug')('express:router:route')
, methods = require('methods')
/**
* Expose `Route`.
@@ -20,22 +22,25 @@ module.exports = Route;
* - `sensitive` enable case-sensitive routes
* - `strict` enable strict matching for trailing slashes
*
* @param {String} method
* @param {String} path
* @param {Array} callbacks
* @param {Object} options.
* @api private
*/
function Route(method, path, callbacks, options) {
function Route(path, options) {
debug('new %s', path);
options = options || {};
this.path = path;
this.method = method;
this.callbacks = callbacks;
this.params = {};
this.regexp = utils.pathRegexp(path
, this.keys = []
, options.sensitive
, options.strict);
this.stack = undefined;
// route handlers for various http methods
this.methods = {};
}
/**
@@ -49,24 +54,166 @@ function Route(method, path, callbacks, options) {
Route.prototype.match = function(path){
var keys = this.keys
, params = this.params = []
, m = this.regexp.exec(path);
, params = this.params = {}
, m = this.regexp.exec(path)
, n = 0;
if (!m) return false;
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
var val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
: m[i];
try {
var val = 'string' == typeof m[i]
? decodeURIComponent(m[i])
: m[i];
} catch(e) {
var err = new Error("Failed to decode param '" + m[i] + "'");
err.status = 400;
throw err;
}
if (key) {
params[key.name] = val;
} else {
params.push(val);
params[n++] = val;
}
}
return true;
};
/**
* @return {Array} supported HTTP methods
* @api private
*/
Route.prototype._options = function(){
return Object.keys(this.methods).map(function(method) {
return method.toUpperCase();
});
};
/**
* dispatch req, res into this route
*
* @api private
*/
Route.prototype.dispatch = function(req, res, done){
var self = this;
var method = req.method.toLowerCase();
if (method === 'head' && !this.methods['head']) {
method = 'get';
}
req.route = self;
// single middleware route case
if (typeof this.stack === 'function') {
this.stack(req, res, done);
return;
}
var stack = self.stack;
if (!stack) {
return done();
}
var idx = 0;
(function next_layer(err) {
if (err && err === 'route') {
return done();
}
var layer = stack[idx++];
if (!layer) {
return done(err);
}
if (layer.method && layer.method !== method) {
return next_layer(err);
}
var arity = layer.handle.length;
if (err) {
if (arity < 4) {
return next_layer(err);
}
return layer.handle(err, req, res, next_layer);
}
if (arity > 3) {
return next_layer();
}
layer.handle(req, res, next_layer);
})();
};
/**
* Add a handler for all HTTP verbs to this route.
*
* Behaves just like middleware and can respond or call `next`
* to continue processing.
*
* You can use multiple `.all` call to add multiple handlers.
*
* function check_something(req, res, next){
* next();
* };
*
* function validate_user(req, res, next){
* next();
* };
*
* route
* .all(validate_user)
* .all(check_something)
* .get(function(req, res, next){
* res.send('hello world');
* });
*
* @param {function} handler
* @return {Route} for chaining
* @api public
*/
Route.prototype.all = function(fn){
if (typeof fn !== 'function') {
var type = {}.toString.call(fn);
var msg = 'Route.use() requires callback functions but got a ' + type;
throw new Error(msg);
}
if (!this.stack) {
this.stack = fn;
}
else if (typeof this.stack === 'function') {
this.stack = [{ handle: this.stack }, { handle: fn }];
}
else {
this.stack.push({ handle: fn });
}
return this;
};
methods.forEach(function(method){
Route.prototype[method] = function(fn){
debug('%s %s', method, this.path);
if (!this.methods[method]) {
this.methods[method] = true;
}
if (!this.stack) {
this.stack = [];
}
this.stack.push({ method: method, handle: fn })
return this;
};
});
+28 -158
Ver Arquivo
@@ -3,8 +3,9 @@
* Module dependencies.
*/
var mime = require('connect').mime
, crc32 = require('buffer-crc32');
var mime = require('send').mime;
var crc32 = require('buffer-crc32');
var parse = require('url').parse;
/**
* toString ref.
@@ -24,25 +25,6 @@ exports.etag = function(body){
return '"' + crc32.signed(body) + '"';
};
/**
* Make `locals()` bound to the given `obj`.
*
* This is used for `app.locals` and `res.locals`.
*
* @param {Object} obj
* @return {Function}
* @api private
*/
exports.locals = function(obj){
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
};
return locals;
};
/**
* Check if `path` looks absolute.
*
@@ -54,6 +36,7 @@ exports.locals = function(obj){
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
};
/**
@@ -109,127 +92,6 @@ exports.normalizeTypes = function(types){
return ret;
};
/**
* Return the acceptable type in `types`, if any.
*
* @param {Array} types
* @param {String} str
* @return {String}
* @api private
*/
exports.acceptsArray = function(types, str){
// accept anything when Accept is not present
if (!str) return types[0];
// parse
var accepted = exports.parseAccept(str)
, normalized = exports.normalizeTypes(types)
, len = accepted.length;
for (var i = 0; i < len; ++i) {
for (var j = 0, jlen = types.length; j < jlen; ++j) {
if (exports.accept(normalized[j], accepted[i])) {
return types[j];
}
}
}
};
/**
* Check if `type(s)` are acceptable based on
* the given `str`.
*
* @param {String|Array} type(s)
* @param {String} str
* @return {Boolean|String}
* @api private
*/
exports.accepts = function(type, str){
if ('string' == typeof type) type = type.split(/ *, */);
return exports.acceptsArray(type, str);
};
/**
* Check if `type` array is acceptable for `other`.
*
* @param {Object} type
* @param {Object} other
* @return {Boolean}
* @api private
*/
exports.accept = function(type, other){
var t = type.value.split('/');
return (t[0] == other.type || '*' == other.type)
&& (t[1] == other.subtype || '*' == other.subtype)
&& paramsEqual(type.params, other.params);
};
/**
* Check if accept params are equal.
*
* @param {Object} a
* @param {Object} b
* @return {Boolean}
* @api private
*/
function paramsEqual(a, b){
return !Object.keys(a).some(function(k) {
return a[k] != b[k];
});
}
/**
* Parse accept `str`, returning
* an array objects containing
* `.type` and `.subtype` along
* with the values provided by
* `parseQuality()`.
*
* @param {Type} name
* @return {Type}
* @api private
*/
exports.parseAccept = function(str){
return exports
.parseParams(str)
.map(function(obj){
var parts = obj.value.split('/');
obj.type = parts[0];
obj.subtype = parts[1];
return obj;
});
};
/**
* Parse quality `str`, returning an
* array of objects with `.value`,
* `.quality` and optional `.params`
*
* @param {String} str
* @return {Array}
* @api private
*/
exports.parseParams = function(str){
return str
.split(/ *, */)
.map(acceptParams)
.filter(function(obj){
return obj.quality;
})
.sort(function(a, b){
if (a.quality === b.quality) {
return a.originalIndex - b.originalIndex;
} else {
return b.quality - a.quality;
}
});
};
/**
* Parse accept params `str` returning an
@@ -257,22 +119,6 @@ function acceptParams(str, index) {
return ret;
}
/**
* Escape special characters in the given string of html.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function(html) {
return String(html)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
};
/**
* Normalize the given path string,
* returning a regular expression.
@@ -311,3 +157,27 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
/**
* Parse the `req` url with memoization.
*
* @param {ServerRequest} req
* @return {Object}
* @api private
*/
exports.parseUrl = function(req){
var parsed = req._parsedUrl;
if (parsed && parsed.href == req.url) {
return parsed;
} else {
parsed = parse(req.url);
if (parsed.auth && !parsed.protocol && ~parsed.href.indexOf('//')) {
// This parses pathnames, and a strange pathname like //r@e should work
parsed = parse(req.url.replace(/@/g, '%40'));
}
return req._parsedUrl = parsed;
}
};
+30 -25
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "3.3.6",
"version": "3.4.7",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{
@@ -22,28 +22,36 @@
}
],
"dependencies": {
"connect": "2.8.5",
"commander": "1.2.0",
"range-parser": "0.0.4",
"mkdirp": "0.3.5",
"accepts": "1.0.0",
"type-is": "1.0.0",
"range-parser": "1.0.0",
"cookie": "0.1.0",
"buffer-crc32": "0.2.1",
"fresh": "0.2.0",
"methods": "0.0.1",
"send": "0.1.4",
"cookie-signature": "1.0.1",
"debug": "*"
"fresh": "0.2.1",
"methods": "0.1.0",
"send": "0.2.0",
"cookie-signature": "1.0.3",
"merge-descriptors": "0.0.2",
"utils-merge": "1.0.0",
"escape-html": "1.0.1",
"qs": "0.6.6",
"debug": ">= 0.7.3 < 1"
},
"devDependencies": {
"ejs": "*",
"mocha": "*",
"jade": "0.30.0",
"hjs": "*",
"stylus": "*",
"should": "*",
"connect-redis": "*",
"marked": "*",
"supertest": "0.6.0"
"ejs": "~0.8.4",
"mocha": "~1.15.1",
"jade": "~0.30.0",
"hjs": "~0.0.6",
"stylus": "~0.40.0",
"should": "~2.1.1",
"connect-redis": "~1.4.5",
"marked": "0.2.10",
"supertest": "~0.8.1",
"body-parser": "1.0.0",
"cookie-parser": "1.0.1",
"static-favicon": "1.0.0",
"express-session": "1.0.1",
"morgan": "1.0.0"
},
"keywords": [
"express",
@@ -57,15 +65,12 @@
"api"
],
"repository": "git://github.com/visionmedia/express",
"main": "index",
"bin": {
"express": "./bin/express"
},
"scripts": {
"prepublish": "npm prune",
"test": "make test"
},
"engines": {
"node": "*"
}
"node": ">= 0.8.0"
},
"license": "MIT"
}
-1
Ver Arquivo
@@ -9,7 +9,6 @@ var app = express()
, blog = express()
, admin = express();
// app.use(express.logger('dev'))
blog.use('/admin', admin);
app.use('/blog', blog);
app.set('views', __dirname + '/views');
-32
Ver Arquivo
@@ -1,32 +0,0 @@
#!/usr/bin/env bash
NODE_ENV=production node ./support/app &
pid=$!
bench() {
ab -n 5000 -c 50 -k -q http://127.0.0.1:8000$1 \
| grep "Requests per" \
| cut -d ' ' -f 7 \
| xargs echo "$2:"
}
bench_conditional() {
ab -n 5000 -c 50 -H "If-None-Match: $3" -k -q http://127.0.0.1:8000$1 \
| grep "Requests per" \
| cut -d ' ' -f 7 \
| xargs echo "$2:"
}
sleep .5
bench / "Hello World"
bench /blog "Mounted Hello World"
bench /blog/admin "Mounted 2 Hello World"
bench /middleware "Middleware"
bench /match "Router"
bench /render "Render"
bench /json "JSON tiny"
bench /json/15 "JSON small"
bench /json/50 "JSON medium"
bench /json/150 "JSON large"
kill -9 $pid
+153
Ver Arquivo
@@ -0,0 +1,153 @@
var express = require('../')
, Route = express.Route
, methods = require('methods')
, assert = require('assert');
describe('Route', function(){
describe('.match', function(){
it('should match', function(){
var route = new Route('/foo/bar');
assert(route.match('/foo/bar'));
assert(!route.match('/foo/baz'));
})
})
describe('.all', function(){
it('should add handler', function(done){
var route = new Route('/foo');
route.all(function(req, res, next) {
assert.equal(req.a, 1);
assert.equal(res.b, 2);
next();
});
route.dispatch({ a:1, method: 'GET' }, { b:2 }, done);
})
it('should handle VERBS', function(done) {
var route = new Route('/foo');
var count = 0;
route.all(function(req, res, next) {
count++;
});
methods.forEach(function testMethod(method) {
route.dispatch({ method: method }, {});
});
assert.equal(count, methods.length);
done();
})
it('should stack', function(done) {
var route = new Route('/foo');
var count = 0;
route.all(function(req, res, next) {
count++;
next();
});
route.all(function(req, res, next) {
count++;
next();
});
route.dispatch({ method: 'GET' }, {}, function(err) {
assert.ifError(err);
count++;
});
assert.equal(count, 3);
done();
})
})
describe('.VERB', function(){
it('should support .get', function(done){
var route = new Route('');
var count = 0;
route.get(function(req, res, next) {
count++;
})
route.dispatch({ method: 'GET' }, {});
assert(count);
done();
})
it('should limit to just .VERB', function(done){
var route = new Route('');
route.get(function(req, res, next) {
assert(false);
done();
})
route.post(function(req, res, next) {
assert(true);
})
route.dispatch({ method: 'post' }, {});
done();
})
it('should allow fallthrough', function(done){
var route = new Route('');
var order = '';
route.get(function(req, res, next) {
order += 'a';
next();
})
route.all(function(req, res, next) {
order += 'b';
next();
});
route.get(function(req, res, next) {
order += 'c';
})
route.dispatch({ method: 'get' }, {});
assert.equal(order, 'abc');
done();
})
})
describe('errors', function(){
it('should handle errors via arity 4 functions', function(done){
var route = new Route('');
var order = '';
route.all(function(req, res, next){
next(new Error('foobar'));
});
route.all(function(req, res, next){
order += '0';
next();
});
route.all(function(err, req, res, next){
order += 'a';
next(err);
});
route.all(function(err, req, res, next){
assert.equal(err.message, 'foobar');
assert.equal(order, 'a');
done();
});
route.dispatch({ method: 'get' }, {});
})
})
})
+62 -68
Ver Arquivo
@@ -1,103 +1,97 @@
var express = require('../')
, Router = express.Router
, request = require('./support/http')
, methods = require('methods')
, assert = require('assert');
describe('Router', function(){
var router, app;
beforeEach(function(){
router = new Router;
app = express();
})
describe('.match(method, url, i)', function(){
it('should match based on index', function(){
router.route('get', '/foo', function(){});
router.route('get', '/foob?', function(){});
router.route('get', '/bar', function(){});
var method = 'GET';
var url = '/foo?bar=baz';
var route = router.match(method, url, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
var route = router.match(method, url, 1);
route.path.should.equal('/foob?');
var route = router.match(method, url, 2);
assert(!route);
url = '/bar';
var route = router.match(method, url);
route.path.should.equal('/bar');
})
})
describe('.matchRequest(req, i)', function(){
it('should match based on index', function(){
router.route('get', '/foo', function(){});
router.route('get', '/foob?', function(){});
router.route('get', '/bar', function(){});
var req = { method: 'GET', url: '/foo?bar=baz' };
var route = router.matchRequest(req, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
var route = router.matchRequest(req, 1);
req._route_index.should.equal(1);
route.path.should.equal('/foob?');
var route = router.matchRequest(req, 2);
assert(!route);
req.url = '/bar';
var route = router.matchRequest(req);
route.path.should.equal('/bar');
})
})
describe('.middleware', function(){
it('should dispatch', function(done){
router.route('get', '/foo', function(req, res){
var router = new Router();
router.route('/foo').get(function(req, res){
res.send('foo');
});
app.use(router.middleware);
request(app)
.get('/foo')
.expect('foo', done);
var res = {
send: function(val) {
val.should.equal('foo');
done();
}
}
router.handle({ url: '/foo', method: 'GET' }, res);
})
})
describe('.multiple callbacks', function(){
it('should throw if a callback is null', function(){
assert.throws(function () {
router.route('get', '/foo', null, function(){});
var router = new Router();
router.route('/foo').all(null);
})
})
it('should throw if a callback is undefined', function(){
assert.throws(function () {
router.route('get', '/foo', undefined, function(){});
var router = new Router();
router.route('/foo').all(undefined);
})
})
it('should throw if a callback is not a function', function(){
assert.throws(function () {
router.route('get', '/foo', 'not a function', function(){});
var router = new Router();
router.route('/foo').all('not a function');
})
})
it('should not throw if all callbacks are functions', function(){
router.route('get', '/foo', function(){}, function(){});
var router = new Router();
router.route('/foo').all(function(){}).all(function(){});
})
})
describe('error', function(){
it('should skip non error middleware', function(done){
var router = new Router();
router.get('/foo', function(req, res, next){
next(new Error('foo'));
});
router.get('/bar', function(req, res, next){
next(new Error('bar'));
});
router.use(function(req, res, next){
assert(false);
});
router.use(function(err, req, res, next){
assert.equal(err.message, 'foo');
done();
});
router.handle({ url: '/foo', method: 'GET' }, {}, done);
});
})
describe('.all', function() {
it('should support using .all to capture all http verbs', function(done){
var router = new Router();
var count = 0;
router.all('/foo', function(){ count++; });
var url = '/foo?bar=baz';
methods.forEach(function testMethod(method) {
router.handle({ url: url, method: method }, {}, function() {});
});
assert.equal(count, methods.length);
done();
})
})
})
+1 -1
Ver Arquivo
@@ -7,7 +7,7 @@ describe('markdown', function(){
it('should respond with html', function(done){
request(app)
.get('/')
.expect(/<h1>Markdown Example<\/h1>/,done)
.expect(/<h1[^>]*>Markdown Example<\/h1>/,done)
})
})
+17 -5
Ver Arquivo
@@ -25,7 +25,7 @@ describe('app.parent', function(){
})
})
describe('app.route', function(){
describe('app.mountpath', function(){
it('should return the mounted path', function(){
var app = express()
, blog = express()
@@ -34,9 +34,21 @@ describe('app.route', function(){
app.use('/blog', blog);
blog.use('/admin', blogAdmin);
app.route.should.equal('/');
blog.route.should.equal('/blog');
blogAdmin.route.should.equal('/admin');
app.mountpath.should.equal('/');
blog.mountpath.should.equal('/blog');
blogAdmin.mountpath.should.equal('/admin');
})
})
describe('app.router', function(){
it('should throw with notice', function(done){
var app = express()
try {
app.router;
} catch(err) {
done();
}
})
})
@@ -71,4 +83,4 @@ describe('in production', function(){
app.enabled('view cache').should.be.true;
process.env.NODE_ENV = 'test';
})
})
})
+2 -2
Ver Arquivo
@@ -7,8 +7,8 @@ describe('app', function(){
it('should merge locals', function(){
var app = express();
Object.keys(app.locals).should.eql(['settings']);
app.locals({ user: 'tobi', age: 1 });
app.locals({ age: 2 });
app.locals.user = 'tobi';
app.locals.age = 2;
Object.keys(app.locals).should.eql(['settings', 'user', 'age']);
app.locals.user.should.equal('tobi');
app.locals.age.should.equal(2);
+25 -1
Ver Arquivo
@@ -15,6 +15,30 @@ describe('OPTIONS', function(){
.expect('GET,PUT')
.expect('Allow', 'GET,PUT', done);
})
it('should not respond if the path is not defined', function(done){
var app = express();
app.get('/users', function(req, res){});
request(app)
.options('/other')
.expect(404, done);
})
it('should forward requests down the middleware chain', function(done){
var app = express();
var router = new express.Router();
router.get('/users', function(req, res){});
app.use(router.middleware);
app.get('/other', function(req, res){});
request(app)
.options('/other')
.expect('GET')
.expect('Allow', 'GET', done);
})
})
describe('app.options()', function(){
@@ -34,4 +58,4 @@ describe('app.options()', function(){
.expect('GET')
.expect('Allow', 'GET', done);
})
})
})
+3 -3
Ver Arquivo
@@ -52,13 +52,13 @@ describe('app', function(){
app.get('/post/:id', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});
app.get('/user/:uid', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});
@@ -87,7 +87,7 @@ describe('app', function(){
app.get('/user/:id', function(req, res){
var id = req.params.id;
id.should.be.a('number');
id.should.be.a.Number;
res.send('' + id);
});
+1 -1
Ver Arquivo
@@ -59,7 +59,7 @@ describe('app', function(){
var app = express();
app.set('views', __dirname + '/fixtures');
app.render('rawr.jade', function(err){
err.message.should.equal('Failed to lookup view "rawr.jade"');
err.message.should.equal('Failed to lookup view "rawr.jade" in views directory "' + __dirname + '/fixtures"');
done();
});
})
+43 -35
Ver Arquivo
@@ -27,49 +27,57 @@ describe('app.router', function(){
});
})
it('should decode params', function(done){
var app = express();
describe('decode querystring', function(){
it('should decode correct params', function(done){
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/foo%2Fbar')
.expect('foo/bar', done);
})
request(app)
.get('/foo%2Fbar')
.expect('foo/bar', done);
})
it('should be .use()able', function(done){
var app = express();
it('should not accept params in malformed paths', function(done) {
var app = express();
var calls = [];
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
app.use(function(req, res, next){
calls.push('before');
next();
});
request(app)
.get('/%foobar')
.expect(400, done);
})
app.use(app.router);
it('should not decode spaces', function(done) {
var app = express();
app.use(function(req, res, next){
calls.push('after');
res.end();
});
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
app.get('/', function(req, res, next){
calls.push('GET /')
next();
});
request(app)
.get('/foo+bar')
.expect('foo+bar', done);
})
request(app)
.get('/')
.end(function(res){
calls.should.eql(['before', 'GET /', 'after'])
done();
it('should work with unicode', function(done) {
var app = express();
app.get('/:name', function(req, res, next){
res.send(req.params.name);
});
request(app)
.get('/%ce%b1')
.expect('\u03b1', done);
})
})
it('should be auto .use()d on the first app.VERB() call', function(done){
it('should be .use()able', function(done){
var app = express();
var calls = [];
@@ -114,8 +122,8 @@ describe('app.router', function(){
var app = express();
app.get(/^\/user\/([0-9]+)\/(view|edit)?$/, function(req, res){
var id = req.params.shift()
, op = req.params.shift();
var id = req.params[0]
, op = req.params[1];
res.end(op + 'ing user ' + id);
});
@@ -290,8 +298,8 @@ describe('app.router', function(){
var app = express();
app.get('/api/*.*', function(req, res){
var resource = req.params.shift()
, format = req.params.shift();
var resource = req.params[0]
, format = req.params[1];
res.end(resource + ' as ' + format);
});
-48
Ver Arquivo
@@ -1,48 +0,0 @@
var express = require('../')
, assert = require('assert')
, request = require('./support/http');
describe('app.routes', function(){
it('should be initialized', function(){
var app = express();
app.routes.should.eql({});
})
it('should be populated with routes', function(){
var app = express();
app.get('/', function(req, res){});
app.get('/user/:id', function(req, res){});
var get = app.routes.get;
get.should.have.length(2);
get[0].path.should.equal('/');
get[0].method.should.equal('get');
get[0].regexp.toString().should.equal('/^\\/\\/?$/i');
get[1].path.should.equal('/user/:id');
get[1].method.should.equal('get');
})
it('should be mutable', function(done){
var app = express();
app.get('/', function(req, res){});
app.get('/user/:id', function(req, res){});
var get = app.routes.get;
get.should.have.length(2);
get[0].path.should.equal('/');
get[0].method.should.equal('get');
get[0].regexp.toString().should.equal('/^\\/\\/?$/i');
get.splice(1);
request(app)
.get('/user/12')
.expect(404, done);
})
})
-96
Ver Arquivo
@@ -1,96 +0,0 @@
var express = require('../');
describe('config', function(){
describe('.configure()', function(){
describe('when no env is given', function(){
it('should always execute', function(){
var app = express();
var calls = [];
app.configure(function(){
calls.push('all');
});
app.configure('test', function(){
calls.push('test');
});
app.configure('test', function(){
calls.push('test 2');
});
calls.should.eql(['all', 'test', 'test 2'])
})
})
describe('when an env is given', function(){
it('should only execute the matching env', function(){
var app = express();
var calls = [];
app.set('env', 'development');
app.configure('development', function(){
calls.push('dev');
});
app.configure('test', function(){
calls.push('test');
});
calls.should.eql(['dev']);
})
})
describe('when several envs are given', function(){
it('should execute when matching one', function(){
var app = express();
var calls = [];
app.set('env', 'development');
app.configure('development', function(){
calls.push('dev');
});
app.configure('test', 'development', function(){
calls.push('dev 2');
});
app.configure('development', 'test', function(){
calls.push('dev 3');
});
app.configure('test', function(){
calls.push('dev 3');
});
calls.should.eql(['dev', 'dev 2', 'dev 3']);
})
})
it('should execute in order as defined', function(){
var app = express();
var calls = [];
app.configure(function(){
calls.push('all');
});
app.configure('test', function(){
calls.push('test');
});
app.configure(function(){
calls.push('all 2');
});
app.configure('test', function(){
calls.push('test 2');
});
calls.should.eql(['all', 'test', 'all 2', 'test 2'])
})
})
})
+4 -14
Ver Arquivo
@@ -4,30 +4,20 @@ var express = require('../')
, assert = require('assert');
describe('exports', function(){
it('should expose connect middleware', function(){
express.should.have.property('bodyParser');
express.should.have.property('session');
express.should.have.property('static');
})
it('should expose .mime', function(){
assert(express.mime == require('connect').mime, 'express.mime should be connect.mime');
})
it('should expose Router', function(){
express.Router.should.be.a('function');
express.Router.should.be.a.Function;
})
it('should expose the application prototype', function(){
express.application.set.should.be.a('function');
express.application.set.should.be.a.Function;
})
it('should expose the request prototype', function(){
express.request.accepts.should.be.a('function');
express.request.accepts.should.be.a.Function;
})
it('should expose the response prototype', function(){
express.response.send.should.be.a('function');
express.response.send.should.be.a.Function;
})
it('should permit modifying the .application prototype', function(){
+1
Ver Arquivo
@@ -0,0 +1 @@
p= name
-37
Ver Arquivo
@@ -1,37 +0,0 @@
var express = require('../')
, request = require('./support/http');
describe('req', function(){
describe('.accepted', function(){
it('should return an array of accepted media types', function(done){
var app = express();
app.use(function(req, res){
req.accepted[0].value.should.equal('application/json');
req.accepted[1].value.should.equal('text/html');
res.end();
});
request(app)
.get('/')
.set('Accept', 'text/html;q=.5, application/json')
.expect(200, done);
})
describe('when Accept is not present', function(){
it('should default to []', function(done){
var app = express();
app.use(function(req, res){
req.accepted.should.have.length(0);
res.end();
});
request(app)
.get('/')
.expect(200, done);
})
})
})
})
-37
Ver Arquivo
@@ -1,37 +0,0 @@
var express = require('../')
, request = require('./support/http');
describe('req', function(){
describe('.acceptedCharsets', function(){
it('should return an array of accepted charsets', function(done){
var app = express();
app.use(function(req, res){
req.acceptedCharsets[0].should.equal('unicode-1-1');
req.acceptedCharsets[1].should.equal('iso-8859-5');
res.end();
});
request(app)
.get('/')
.set('Accept-Charset', 'iso-8859-5;q=.2, unicode-1-1;q=0.8')
.expect(200, done);
})
describe('when Accept-Charset is not present', function(){
it('should default to []', function(done){
var app = express();
app.use(function(req, res){
req.acceptedCharsets.should.have.length(0);
res.end();
});
request(app)
.get('/')
.expect(200, done);
})
})
})
})
-36
Ver Arquivo
@@ -1,36 +0,0 @@
var express = require('../')
, request = require('./support/http');
describe('req', function(){
describe('.acceptedEncodings', function(){
it('should return an array of accepted encodings', function(done){
var app = express();
app.use(function(req, res){
req.acceptedEncodings.should.eql(['gzip', 'deflate']);
res.end();
});
request(app)
.get('/')
.set('Accept-Encoding', ' gzip, deflate')
.expect(200, done);
})
describe('when Accept-Encoding is not present', function(){
it('should default to []', function(done){
var app = express();
app.use(function(req, res){
req.acceptedEncodings.should.have.length(0);
res.end();
});
request(app)
.get('/')
.expect(200, done);
})
})
})
})
-37
Ver Arquivo
@@ -1,37 +0,0 @@
var express = require('../')
, request = require('./support/http');
describe('req', function(){
describe('.acceptedLanguages', function(){
it('should return an array of accepted languages', function(done){
var app = express();
app.use(function(req, res){
req.acceptedLanguages[0].should.equal('en-us');
req.acceptedLanguages[1].should.equal('en');
res.end();
});
request(app)
.get('/')
.set('Accept-Language', 'en;q=.5, en-us')
.expect(200, done);
})
describe('when Accept-Language is not present', function(){
it('should default to []', function(done){
var app = express();
app.use(function(req, res){
req.acceptedLanguages.should.have.length(0);
res.end();
});
request(app)
.get('/')
.expect(200, done);
})
})
})
})
+1 -14
Ver Arquivo
@@ -43,19 +43,6 @@ describe('req', function(){
})
})
it('should accept a comma-delimited list of types', function(done){
var app = express();
app.use(function(req, res, next){
res.end(req.accepts('json, html'));
});
request(app)
.get('/')
.set('Accept', 'text/html')
.expect('html', done);
})
it('should accept an argument list of type names', function(done){
var app = express();
@@ -69,7 +56,7 @@ describe('req', function(){
.expect('json', done);
})
describe('.accept(types)', function(){
describe('.accepts(types)', function(){
it('should return the first when Accept is not present', function(done){
var app = express();
+3 -3
Ver Arquivo
@@ -9,7 +9,7 @@ describe('req', function(){
var app = express();
app.use(function(req, res, next){
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
});
request(app)
@@ -23,7 +23,7 @@ describe('req', function(){
var app = express();
app.use(function(req, res, next){
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
});
request(app)
@@ -36,7 +36,7 @@ describe('req', function(){
var app = express();
app.use(function(req, res, next){
res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no');
res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no');
});
request(app)
+12 -9
Ver Arquivo
@@ -4,8 +4,11 @@ var express = require('../')
function req(ct) {
var req = {
headers: { 'content-type': ct }
, __proto__: express.request
headers: {
'content-type': ct,
'transfer-encoding': 'chunked'
},
__proto__: express.request
};
return req;
@@ -15,7 +18,7 @@ describe('req.is()', function(){
it('should ignore charset', function(){
req('application/json; charset=utf-8')
.is('json')
.should.be.true;
.should.equal('json');
})
describe('when content-type is not present', function(){
@@ -30,7 +33,7 @@ describe('req.is()', function(){
it('should lookup the mime type', function(){
req('application/json')
.is('json')
.should.be.true;
.should.equal('json');
req('text/html')
.is('json')
@@ -42,7 +45,7 @@ describe('req.is()', function(){
it('should match', function(){
req('application/json')
.is('application/json')
.should.be.true;
.should.equal('application/json');
req('image/jpeg')
.is('application/json')
@@ -54,7 +57,7 @@ describe('req.is()', function(){
it('should match', function(){
req('application/json')
.is('*/json')
.should.be.true;
.should.equal('application/json');
req('image/jpeg')
.is('*/json')
@@ -65,7 +68,7 @@ describe('req.is()', function(){
it('should match', function(){
req('text/html; charset=utf-8')
.is('*/html')
.should.be.true;
.should.equal('text/html');
req('text/plain; charset=utf-8')
.is('*/html')
@@ -78,7 +81,7 @@ describe('req.is()', function(){
it('should match', function(){
req('image/png')
.is('image/*')
.should.be.true;
.should.equal('image/png');
req('text/html')
.is('image/*')
@@ -89,7 +92,7 @@ describe('req.is()', function(){
it('should match', function(){
req('text/html; charset=utf-8')
.is('text/*')
.should.be.true;
.should.equal('text/html');
req('something/html; charset=utf-8')
.is('text/*')
+3 -2
Ver Arquivo
@@ -1,6 +1,7 @@
var express = require('../')
, request = require('./support/http');
, request = require('./support/http')
, bodyParser = require('body-parser')
describe('req', function(){
describe('.param(name, default)', function(){
@@ -33,7 +34,7 @@ describe('req', function(){
it('should check req.body', function(done){
var app = express();
app.use(express.bodyParser());
app.use(bodyParser());
app.use(function(req, res){
res.end(req.param('name'));
-2
Ver Arquivo
@@ -8,13 +8,11 @@ describe('req', function(){
var app = express();
app.get('/user/:id/:op?', function(req, res, next){
req.route.method.should.equal('get');
req.route.path.should.equal('/user/:id/:op?');
next();
});
app.get('/user/:id/edit', function(req, res){
req.route.method.should.equal('get');
req.route.path.should.equal('/user/:id/edit');
res.end();
});
+22 -42
Ver Arquivo
@@ -1,59 +1,39 @@
var express = require('../')
, request = require('./support/http');
, request = require('./support/http')
, cookieParser = require('cookie-parser')
describe('req', function(){
describe('.signedCookies', function(){
it('should return a signed JSON cookie', function(done){
var app = express()
, cookieHeader
, val;
var app = express();
/* So we use the same serialization for expected results. */
var replacer = app.get('json replacer')
, spaces = app.get('json spaces');
app.use(express.cookieParser('secret'));
app.use(cookieParser('secret'));
app.use(function(req, res){
res.send(req.signedCookies);
if ('/set' == req.path) {
res.cookie('obj', { foo: 'bar' }, { signed: true });
res.end();
} else {
res.send(req.signedCookies);
}
});
app.response.req = { secret: 'secret' };
app.response.cookie('obj', { foo: 'bar' }, { signed: true });
cookieHeader = app.response.get('set-cookie');
val = JSON.stringify({ obj: { foo: 'bar' } }, replacer, spaces);
request(app)
.get('/')
.set('Cookie', cookieHeader)
.expect(val, done);
})
.get('/set')
.end(function(err, res){
if (err) return done(err);
var cookie = res.header['set-cookie'];
it('should return a signed cookie', function(done){
var app = express()
, cookieHeader
, val;
/* So we use the same serialization for expected results. */
var replacer = app.get('json replacer')
, spaces = app.get('json spaces');
app.use(express.cookieParser('secret'));
app.use(function(req, res){
res.send(req.signedCookies);
request(app)
.get('/')
.set('Cookie', cookie)
.end(function(err, res){
if (err) return don(err);
res.body.should.eql({ obj: { foo: 'bar' } });
done();
});
});
app.response.req = { secret: 'secret' };
app.response.cookie('foo', 'bar', { signed: true });
cookieHeader = app.response.get('set-cookie');
val = JSON.stringify({ foo: 'bar' }, replacer, spaces);
request(app)
.get('/')
.set('Cookie', cookieHeader)
.expect(val, done);
})
})
})
+6 -6
Ver Arquivo
@@ -15,7 +15,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.example.com')
.expect('["ferrets","tobi"]', done);
.expect(["ferrets","tobi"], done);
})
})
@@ -30,7 +30,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'example.com')
.expect('[]', done);
.expect([], done);
})
})
@@ -45,7 +45,7 @@ describe('req', function(){
request(app)
.get('/')
.expect('[]', done);
.expect([], done);
})
})
@@ -62,7 +62,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.sub.example.com')
.expect('["com","example","sub","ferrets","tobi"]', done);
.expect(["com","example","sub","ferrets","tobi"], done);
})
})
@@ -78,7 +78,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'tobi.ferrets.sub.example.com')
.expect('["ferrets","tobi"]', done);
.expect(["ferrets","tobi"], done);
})
})
@@ -94,7 +94,7 @@ describe('req', function(){
request(app)
.get('/')
.set('Host', 'sub.example.com')
.expect('[]', done);
.expect([], done);
})
})
})
+8 -6
Ver Arquivo
@@ -1,8 +1,9 @@
var express = require('../')
, request = require('./support/http')
, utils = require('connect').utils
, cookie = require('cookie');
, mixin = require('utils-merge')
, cookie = require('cookie')
, cookieParser = require('cookie-parser')
describe('res', function(){
describe('.cookie(name, object)', function(){
@@ -46,13 +47,14 @@ describe('res', function(){
app.use(function(req, res){
res.cookie('name', 'tobi');
res.cookie('age', 1);
res.cookie('gender', '?');
res.end();
});
request(app)
.get('/')
.end(function(err, res){
var val = ['name=tobi; Path=/', 'age=1; Path=/'];
var val = ['name=tobi; Path=/', 'age=1; Path=/', 'gender=%3F; Path=/'];
res.headers['set-cookie'].should.eql(val);
done();
})
@@ -114,7 +116,7 @@ describe('res', function(){
var app = express();
var options = { maxAge: 1000 };
var optionsCopy = utils.merge({}, options);
var optionsCopy = mixin({}, options);
app.use(function(req, res){
res.cookie('name', 'tobi', options)
@@ -134,7 +136,7 @@ describe('res', function(){
it('should generate a signed JSON cookie', function(done){
var app = express();
app.use(express.cookieParser('foo bar baz'));
app.use(cookieParser('foo bar baz'));
app.use(function(req, res){
res.cookie('user', { name: 'tobi' }, { signed: true }).end();
@@ -155,7 +157,7 @@ describe('res', function(){
it('should set a signed cookie', function(done){
var app = express();
app.use(express.cookieParser('foo bar baz'));
app.use(cookieParser('foo bar baz'));
app.use(function(req, res){
res.cookie('name', 'tobi', { signed: true }).end();
+18 -1
Ver Arquivo
@@ -93,10 +93,27 @@ function test(app) {
request(app)
.get('/')
.set('Accept', 'text/html; q=.5, text/plain')
.expect('Content-Type', 'text/plain')
.expect('Content-Type', 'text/plain; charset=UTF-8')
.expect('hey', done);
})
it('should set the correct charset for the Content-Type', function() {
request(app)
.get('/')
.set('Accept', 'text/html')
.expect('Content-Type', 'text/html; charset=UTF-8');
request(app)
.get('/')
.set('Accept', 'text/plain')
.expect('Content-Type', 'text/plain; charset=UTF-8');
request(app)
.get('/')
.set('Accept', 'application/json')
.expect('Content-Type', 'application/json');
})
it('should Vary: Accept', function(done){
request(app)
.get('/')
+3 -10
Ver Arquivo
@@ -52,7 +52,7 @@ describe('res', function(){
})
})
})
describe('when given an object', function(){
it('should respond with json', function(done){
var app = express();
@@ -95,14 +95,7 @@ describe('res', function(){
})
describe('"json spaces" setting', function(){
it('should default to 2 in development', function(){
process.env.NODE_ENV = 'development';
var app = express();
app.get('json spaces').should.equal(2);
process.env.NODE_ENV = 'test';
})
it('should be undefined otherwise', function(){
it('should be undefined by default', function(){
var app = express();
assert(undefined === app.get('json spaces'));
})
@@ -125,7 +118,7 @@ describe('res', function(){
})
})
})
describe('.json(status, object)', function(){
it('should respond with json and set the .statusCode', function(done){
var app = express();
+25 -16
Ver Arquivo
@@ -16,11 +16,27 @@ describe('res', function(){
.get('/?callback=something')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('something && something({"count":1});');
res.text.should.equal('typeof something === \'function\' && something({"count":1});');
done();
})
})
it('should use first callback parameter with jsonp', function(done){
var app = express();
app.use(function(req, res){
res.jsonp({ count: 1 });
});
request(app)
.get('/?callback=something&callback=somethingelse')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('typeof something === \'function\' && something({"count":1});');
done();
})
})
it('should allow renaming callback', function(done){
var app = express();
@@ -34,10 +50,10 @@ describe('res', function(){
.get('/?clb=something')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('something && something({"count":1});');
res.text.should.equal('typeof something === \'function\' && something({"count":1});');
done();
})
})
})
it('should allow []', function(done){
var app = express();
@@ -50,7 +66,7 @@ describe('res', function(){
.get('/?callback=callbacks[123]')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('callbacks[123] && callbacks[123]({"count":1});');
res.text.should.equal('typeof callbacks[123] === \'function\' && callbacks[123]({"count":1});');
done();
})
})
@@ -66,7 +82,7 @@ describe('res', function(){
.get('/?callback=foo;bar()')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('foobar && foobar({});');
res.text.should.equal('typeof foobar === \'function\' && foobar({});');
done();
})
})
@@ -82,7 +98,7 @@ describe('res', function(){
.get('/?callback=foo')
.end(function(err, res){
res.headers.should.have.property('content-type', 'text/javascript; charset=utf-8');
res.text.should.equal('foo && foo({"str":"\\u2028 \\u2029 woot"});');
res.text.should.equal('typeof foo === \'function\' && foo({"str":"\\u2028 \\u2029 woot"});');
done();
});
});
@@ -122,7 +138,7 @@ describe('res', function(){
})
})
})
describe('when given an object', function(){
it('should respond with json', function(done){
var app = express();
@@ -165,14 +181,7 @@ describe('res', function(){
})
describe('"json spaces" setting', function(){
it('should default to 2 in development', function(){
process.env.NODE_ENV = 'development';
var app = express();
app.get('json spaces').should.equal(2);
process.env.NODE_ENV = 'test';
})
it('should be undefined otherwise', function(){
it('should be undefined by default', function(){
var app = express();
assert(undefined === app.get('json spaces'));
})
@@ -195,7 +204,7 @@ describe('res', function(){
})
})
})
describe('.json(status, object)', function(){
it('should respond with json and set the .statusCode', function(done){
var app = express();
+22
Ver Arquivo
@@ -3,6 +3,11 @@ var express = require('../')
, res = express.response;
describe('res', function(){
beforeEach(function() {
res.removeHeader('link');
});
describe('.links(obj)', function(){
it('should set Link header field', function(){
res.links({
@@ -15,5 +20,22 @@ describe('res', function(){
'<http://api.example.com/users?page=2>; rel="next", '
+ '<http://api.example.com/users?page=5>; rel="last"');
})
it('should set Link header field for multiple calls', function() {
res.links({
next: 'http://api.example.com/users?page=2',
last: 'http://api.example.com/users?page=5'
});
res.links({
prev: 'http://api.example.com/users?page=1',
});
res.get('link')
.should.equal(
'<http://api.example.com/users?page=2>; rel="next", '
+ '<http://api.example.com/users?page=5>; rel="last", '
+ '<http://api.example.com/users?page=1>; rel="prev"');
})
})
})
+2 -5
Ver Arquivo
@@ -3,15 +3,12 @@ var express = require('../')
, request = require('./support/http');
describe('res', function(){
describe('.locals(obj)', function(){
it('should merge locals', function(done){
describe('.locals', function(){
it('should be empty by default', function(done){
var app = express();
app.use(function(req, res){
Object.keys(res.locals).should.eql([]);
res.locals({ user: 'tobi', age: 1 });
res.locals.user.should.equal('tobi');
res.locals.age.should.equal(1);
res.end();
});
+19 -2
Ver Arquivo
@@ -64,7 +64,7 @@ describe('res', function(){
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/./edit');
res.headers.should.have.property('location', '/post/1/edit');
done();
})
})
@@ -81,7 +81,24 @@ describe('res', function(){
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/../new');
res.headers.should.have.property('location', '/post/new');
done();
})
})
})
describe('with leading ./ and containing ..', function(){
it('should construct path-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('./skip/../../new').end();
});
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/new');
done();
})
})
+5 -5
Ver Arquivo
@@ -210,17 +210,17 @@ describe('res', function(){
request(root)
.get('/depth1')
.end(function(err, res){
res.headers.should.have.property('location', '/depth1/./index');
res.headers.should.have.property('location', '/depth1/index');
request(root)
.get('/depth1/depth2')
.end(function(err, res){
res.headers.should.have.property('location', '/depth1/depth2/./index');
res.headers.should.have.property('location', '/depth1/depth2/index');
request(root)
.get('/depth1/depth2/depth3')
.end(function(err, res){
res.headers.should.have.property('location', '/depth1/depth2/depth3/./index');
res.headers.should.have.property('location', '/depth1/depth2/depth3/index');
done();
})
})
@@ -231,11 +231,11 @@ describe('res', function(){
request(root)
.get('/depth2')
.end(function(err, res){
res.headers.should.have.property('location', '/depth2/./index');
res.headers.should.have.property('location', '/depth2/index');
request(root)
.get('/depth3')
.end(function(err, res){
res.headers.should.have.property('location', '/depth3/./index');
res.headers.should.have.property('location', '/depth3/index');
done();
})
})
+15
Ver Arquivo
@@ -47,6 +47,21 @@ describe('res', function(){
.get('/')
.expect('<p>tobi</p>', done);
})
it('should expose app.locals with `name` property', function(done){
var app = express();
app.set('views', __dirname + '/fixtures');
app.locals.name = 'tobi';
app.use(function(req, res){
res.render('name.jade');
});
request(app)
.get('/')
.expect('<p>tobi</p>', done);
})
it('should support index.<engine>', function(done){
var app = express();
+20 -1
Ver Arquivo
@@ -14,6 +14,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Length', '0')
.expect('', done);
})
})
@@ -28,7 +29,10 @@ describe('res', function(){
request(app)
.get('/')
.expect('', done);
.expect('', function(req, res){
res.header.should.not.have.property('content-length');
done();
});
})
})
@@ -321,6 +325,21 @@ describe('res', function(){
describe('"etag" setting', function(){
describe('when enabled', function(){
it('should send ETag even when content-length < 1024', function(done){
var app = express();
app.use(function(req, res){
res.send('kajdslfkasdf');
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('etag');
done();
});
})
it('should send ETag ', function(done){
var app = express();
+4 -4
Ver Arquivo
@@ -42,7 +42,7 @@ describe('res', function(){
app.use(function(req, res){
res.sendfile('test/fixtures/nope.html', function(err){
++calls;
assert(!res.headerSent);
assert(!res.headersSent);
res.send(err.message);
});
});
@@ -51,7 +51,7 @@ describe('res', function(){
.get('/')
.end(function(err, res){
assert(1 == calls, 'called too many times');
res.text.should.equal("ENOENT, stat 'test/fixtures/nope.html'");
res.text.should.startWith("ENOENT, stat");
res.statusCode.should.equal(200);
done();
});
@@ -77,7 +77,7 @@ describe('res', function(){
app.use(function(req, res){
res.sendfile('test/fixtures/foo/../user.html', function(err){
assert(!res.headerSent);
assert(!res.headersSent);
++calls;
res.send(err.message);
});
@@ -95,7 +95,7 @@ describe('res', function(){
app.use(function(req, res){
res.sendfile('test/fixtures/user.html', function(err){
assert(!res.headerSent);
assert(!res.headersSent);
req.socket.listeners('error').should.have.length(1); // node's original handler
done();
});
+55
Ver Arquivo
@@ -0,0 +1,55 @@
var express = require('../')
, should = require('should');
function response() {
var res = Object.create(express.response);
res._headers = {};
return res;
}
describe('res.vary()', function(){
describe('with no arguments', function(){
it('should not set Vary', function(){
var res = response();
res.vary();
should.not.exist(res.get('Vary'));
})
})
describe('with an empty array', function(){
it('should not set Vary', function(){
var res = response();
res.vary([]);
should.not.exist(res.get('Vary'));
})
})
describe('with an array', function(){
it('should set the values', function(){
var res = response();
res.vary(['Accept', 'Accept-Language', 'Accept-Encoding']);
res.get('Vary').should.equal('Accept, Accept-Language, Accept-Encoding');
})
})
describe('with a string', function(){
it('should set the value', function(){
var res = response();
res.vary('Accept');
res.get('Vary').should.equal('Accept');
})
})
describe('when the value is present', function(){
it('should not add it again', function(){
var res = response();
res.vary('Accept');
res.vary('Accept-Encoding');
res.vary('Accept-Encoding');
res.vary('Accept-Encoding');
res.vary('Accept');
res.get('Vary').should.equal('Accept, Accept-Encoding');
})
})
})
-178
Ver Arquivo
@@ -41,181 +41,3 @@ describe('utils.flatten(arr)', function(){
.should.eql(['one', 'two', 'three', 'four', 'five']);
})
})
describe('utils.escape(html)', function(){
it('should escape html entities', function(){
utils.escape('<script>foo & "bar"')
.should.equal('&lt;script&gt;foo &amp; &quot;bar&quot;')
})
})
describe('utils.parseParams(str)', function(){
it('should default quality to 1', function(){
utils.parseParams('text/html')
.should.eql([{ value: 'text/html', quality: 1, params: {}, originalIndex: 0 }]);
})
it('should parse qvalues', function(){
utils.parseParams('text/html; q=0.5')
.should.eql([{ value: 'text/html', quality: 0.5, params: {}, originalIndex: 0 }]);
utils.parseParams('text/html; q=.2')
.should.eql([{ value: 'text/html', quality: 0.2, params: {}, originalIndex: 0 }]);
})
it('should parse accept parameters', function(){
utils.parseParams('application/json; ver=2.0')
.should.eql([{ value: 'application/json', quality: 1, params: {ver: "2.0"}, originalIndex: 0 }]);
utils.parseParams('text/html; q=0.5; level=2')
.should.eql([{ value: 'text/html', quality: 0.5, params: {level: "2"}, originalIndex: 0 }]);
utils.parseParams('text/html;q=.2;ver=beta')
.should.eql([{ value: 'text/html', quality: 0.2, params: {ver: "beta"}, originalIndex: 0 }]);
})
it('should work with messed up whitespace', function(){
utils.parseParams('text/html ; q = .2')
.should.eql([{ value: 'text/html', quality: 0.2, params: {}, originalIndex: 0 }]);
})
it('should work with multiples', function(){
var str = 'da, en;q=.5, en-gb;q=.8';
var arr = utils.parseParams(str);
arr[0].value.should.equal('da');
arr[1].value.should.equal('en-gb');
arr[2].value.should.equal('en');
})
it('should work with long lists', function(){
var str = 'en, nl, fr, de, ja, it, es, pt, pt-PT, da, fi, nb, sv, ko, zh-Hans, zh-Hant, ru, pl';
var arr = utils.parseParams(str).map(function(o){ return o.value });
arr.should.eql(str.split(', '));
})
it('should sort by quality', function(){
var str = 'text/plain;q=.2, application/json, text/html;q=0.5';
var arr = utils.parseParams(str);
arr[0].value.should.equal('application/json');
arr[1].value.should.equal('text/html');
arr[2].value.should.equal('text/plain');
})
it('should exclude those with a quality of 0', function(){
var str = 'text/plain;q=.2, application/json, text/html;q=0';
var arr = utils.parseParams(str);
arr.should.have.length(2);
})
})
describe('utils.parseAccept(str)', function(){
it('should provide .type', function(){
var arr = utils.parseAccept('text/html');
arr[0].type.should.equal('text');
})
it('should provide .subtype', function(){
var arr = utils.parseAccept('text/html');
arr[0].subtype.should.equal('html');
})
})
describe('utils.accepts(type, str)', function(){
describe('when a string is not given', function(){
it('should return the value', function(){
utils.accepts('text/html')
.should.equal('text/html');
})
})
describe('when a string is empty', function(){
it('should return the value', function(){
utils.accepts('text/html', '')
.should.equal('text/html');
})
})
describe('when */* is given', function(){
it('should return the value', function(){
utils.accepts('text/html', 'text/plain, */*')
.should.equal('text/html');
})
})
describe('when an array is given', function(){
it('should return the best match', function(){
utils.accepts(['html', 'json'], 'text/plain, application/json')
.should.equal('json');
utils.accepts(['html', 'application/json'], 'text/plain, application/json')
.should.equal('application/json');
utils.accepts(['text/html', 'application/json'], 'application/json;q=.5, text/html')
.should.equal('text/html');
})
})
describe('when a comma-delimited list is give', function(){
it('should behave like an array', function(){
utils.accepts('html, json', 'text/plain, application/json')
.should.equal('json');
utils.accepts('html, application/json', 'text/plain, application/json')
.should.equal('application/json');
utils.accepts('text/html, application/json', 'application/json;q=.5, text/html')
.should.equal('text/html');
})
})
describe('when accepting type/subtype', function(){
it('should return the value when present', function(){
utils.accepts('text/html', 'text/plain, text/html')
.should.equal('text/html');
})
it('should return undefined otherwise', function(){
assert(null == utils.accepts('text/html', 'text/plain, application/json'));
})
})
describe('when accepting */subtype', function(){
it('should return the value when present', function(){
utils.accepts('text/html', 'text/*')
.should.equal('text/html');
})
it('should return undefined otherwise', function(){
assert(null == utils.accepts('text/html', 'image/*'));
})
})
describe('when accepting type/*', function(){
it('should return the value when present', function(){
utils.accepts('text/html', '*/html')
.should.equal('text/html');
})
it('should return undefined otherwise', function(){
assert(null == utils.accepts('text/html', '*/json'));
})
})
describe('when an extension is given', function(){
it('should return the value when present', function(){
utils.accepts('html', 'text/html, application/json')
.should.equal('html');
})
it('should return undefined otherwise', function(){
assert(null == utils.accepts('html', 'text/plain, application/json'));
})
it('should support *', function(){
utils.accepts('html', 'text/*')
.should.equal('html');
utils.accepts('html', '*/html')
.should.equal('html');
})
})
})