Comparar commits

...

99 Commits

Autor SHA1 Mensagem Data
Roman Shtylman f8b954bcd9 make express.Router() return a Router function instance
Similar to how express() returns an express `app` instance which is also
a function, express.Router() returns the Router instance which is also a
function and can be easily used via another router or the app.

app.use(express.Router());
2014-02-26 20:22:11 -05:00
Roman Shtylman caa25b506d Merge pull request #1935 from visionmedia/router-params-middleware
Router: add parameter handling to middleware
2014-02-25 12:35:13 -05:00
Roman Shtylman 6911815171 Router: add parameter handling to middleware
Middleware (.use) can now specify parameter arguments to trigger
Router.param loading. This is handy if you want to `.use` additional
routers but need to load certain objects before the mounted middleware
runs.
2014-02-23 19:21:13 -05:00
Roman Shtylman 0719e5f402 implement app.route() 2014-02-23 11:31:43 -05:00
Roman Shtylman 07b731add0 bump cookie parser dependency to 1.0.1 2014-02-22 09:26:30 -05:00
Roman Shtylman d42d8f5b07 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-22 09:26:30 -05:00
Roman Shtylman 143e72dd85 remove support for node 0.8 2014-02-22 09:26:30 -05:00
Roman Shtylman 6835289564 remove ServerResonse.headerSent monkey patch
node.js ServerResponse contains a headersSent field. Use that instead of
our patched misnamed version.
2014-02-22 09:26:29 -05:00
Roman Shtylman 1396e0855d remove last pieces of connect dependency
- copy over patch.js to shim ServerResponse
- bundle `static` middleware
2014-02-22 09:26:29 -05:00
Roman Shtylman 6a7363e4ae use local copy of parseUrl 2014-02-22 09:26:29 -05:00
Roman Shtylman 9bc63d92a0 move connect.query() into our repo 2014-02-22 09:26:29 -05:00
TJ Holowaychuk 6b05f60bad update node-fresh 2014-02-19 15:29:39 -08:00
Jonathan Ong 25e6629bcc update history 2014-02-08 11:40:48 -08:00
Jonathan Ong 0796c1d2d2 test app.router: ignore connect method
so tests pass in 0.11. 0.11 client seems to throw errors more often, so
this is not an issue with express or node’s servers.
2014-02-08 11:39:26 -08:00
Jonathan Ong aac1d52c4f res.location: remove resolving relative urls
closes #1804

this is an unnecessary maintenance burden (see the number of removed
tests), especially when supporting mounting. browsers handle relative
locations, and so should all clients.

a regression could be absolute locations on a mounted app, but 1. we
can fix that later when someone complains and 2) code-smell
2014-02-08 11:37:43 -08: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
76 arquivos alterados com 1669 adições e 2309 exclusões
Ver Arquivo
-1
Ver Arquivo
@@ -1,4 +1,3 @@
language: node_js
node_js:
- "0.8"
- "0.10"
+60
Ver Arquivo
@@ -1,10 +1,70 @@
4.0.0 /
==================
* remove:
- express(1) - moved to [express-generator](https://github.com/expressjs/generator)
- `express.createServer()` - it has been deprecated for a long time. Use `express()`
- `app.configure` - use logic in your own app code
- `app.router` - is removed
- `req.accepted*` - use `req.accepts*()` instead
- `res.location` - relative URL resolution is removed
- all bundled middleware except `static`
* change:
- `app.route` -> `app.mountpath` when mounting an express app in another express app
- `json spaces` no longer enabled by default in development
- `req.accepts*` -> `req.accepts*s` - i.e. `req.acceptsEncoding` -> `req.acceptsEncodings`
- `req.params` is now an object instead of an array
- `res.locals` is no longer a function. It is a plain js object. Treat it as such.
- `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
+3 -3
Ver Arquivo
@@ -24,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
+8 -8
Ver Arquivo
@@ -50,14 +50,14 @@ 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
@@ -72,27 +72,27 @@ 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
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
+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
-422
Ver Arquivo
@@ -1,422 +0,0 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var 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\', path.join(__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: path.join(__dirname, \'public\') }));');
break;
case 'stylus':
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__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');
+3 -3
Ver Arquivo
@@ -28,10 +28,10 @@ app.get('/', function(req, res){
// this to add a layer of abstraction
// and make things a bit more declarative:
function format(requestHandlerName) {
var requestHandler = require(requestHandlerName);
function format(path) {
var obj = require(path);
return function(req, res){
res.format(requestHandler);
res.format(obj);
}
}
+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');
+2 -1
Ver Arquivo
@@ -3,6 +3,7 @@
*/
var express = require('../..');
var logger = require('morgan');
/*
edit /etc/hosts:
@@ -16,7 +17,7 @@ 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!')
+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');
}
}
+149 -142
Ver Arquivo
@@ -2,14 +2,14 @@
* 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
, http = require('http');
/**
@@ -45,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){
@@ -61,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;
@@ -82,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
@@ -102,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);
@@ -123,17 +191,33 @@ 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){
this.lazyrouter();
return this._router.route(path);
};
/**
* Register the given template engine callback `fn`
* as `ext`.
@@ -176,30 +260,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 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.
*
* 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
@@ -208,27 +272,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;
};
@@ -242,7 +296,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
*/
@@ -272,7 +326,7 @@ app.set = function(setting, val){
app.path = function(){
return this.parent
? this.parent.path() + this.route
? this.parent.path() + this.mountpath
: '';
};
@@ -338,60 +392,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(...)`.
*/
@@ -400,16 +400,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;
};
});
@@ -425,10 +426,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;
};
@@ -465,13 +472,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
@@ -490,7 +497,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);
};
/**
+30 -51
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,9 +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;
, extname = path.extname
, mime = send.mime
/**
* Response prototype.
@@ -132,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));
}
@@ -310,13 +311,13 @@ 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);
@@ -351,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()`.
*
@@ -556,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);
};
@@ -584,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);
@@ -596,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;
};
@@ -604,57 +616,24 @@ 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:
*
* res.location('/foo/bar').;
* res.location('http://example.com');
* res.location('../login'); // /blog/post/1 -> /blog/login
*
* Mounting:
*
* When an application is mounted and `res.location()`
* is given a path that does _not_ lead with "/" it becomes
* relative to the mount-point. For example if the application
* is mounted at "/blog", the following would become "/blog/login".
*
* res.location('login');
*
* While the leading slash would result in a location of "/login":
*
* res.location('/login');
* res.location('../login');
*
* @param {String} url
* @api public
*/
res.location = function(url){
var app = this.app
, req = this.req;
var req = this.req;
// setup redirect map
var map = { back: req.get('Referrer') || '/' };
// perform redirect
url = map[url] || url;
// 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;
// relative to mount-point
} else if ('/' != url[0]) {
path = app.path();
url = path + '/' + url;
}
}
// "back" is an alias for the referrer
if ('back' == url) url = req.get('Referrer') || '/';
// Respond
this.set('Location', url);
@@ -708,7 +687,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>';
},
+325 -236
Ver Arquivo
@@ -3,47 +3,74 @@
*/
var Route = require('./route')
, Layer = require('./layer')
, utils = require('../utils')
, methods = require('methods')
, debug = require('debug')('express:router')
, parse = require('connect').utils.parseUrl;
/**
* Expose `Router` constructor.
*/
exports = module.exports = Router;
, parseUrl = utils.parseUrl;
/**
* Initialize a new `Router` with the given `options`.
*
* @param {Object} options
* @api private
*/
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);
};
}
/**
* Register a param callback `fn` for the given `name`.
*
* @param {String|Function} name
* @param {Function} fn
* @return {Router} for chaining
* @return {Router} which is an callable function
* @api public
*/
Router.prototype.param = function(name, fn){
var proto = module.exports = function(options) {
options = options || {};
function router(req, res, next) {
router.handle(req, res, next);
};
// mixin Router class functions
router.__proto__ = proto;
router.params = {};
router._params = [];
router.caseSensitive = options.caseSensitive;
router.strict = options.strict;
router.stack = [];
return router;
};
/**
* Map the given param placeholder `name`(s) to the given callback.
*
* 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 {app} for chaining
* @api public
*/
proto.param = function(name, fn){
// param logic
if ('function' == typeof name) {
this._params.push(name);
@@ -55,6 +82,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 +103,298 @@ 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){
proto.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 = self.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 = '/';
if (!layer.match(path)) return next(err);
// route object and not middleware
var route = layer.route;
// if final route, then we support options
if (route) {
// we don't run any routs with error first
if (err) {
return next(err);
}
req.route = route;
// we can now dispatch to the route
if (method === 'options' && !route.methods['options']) {
options.push.apply(options, route._options());
}
}
req.params = layer.params;
// this should be done for the layer
return self.process_params(layer, req, res, function(err) {
if (err) {
return next(err);
}
if (route) {
return layer.handle(req, res, next);
}
trim_prefix();
});
return next(err);
function trim_prefix() {
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
*/
proto.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 || [];
// fast track
if (keys.length === 0) {
return done();
}
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);
};
proto.use = function(route, fn){
/**
* Route `method`, `path`, and one or more callbacks.
*
* @param {String} method
* @param {String} path
* @param {Function} callback...
* @return {Router} for chaining
* @api private
*/
// default route to '/'
if ('string' != typeof route) {
fn = route;
route = '/';
}
Router.prototype.route = function(method, path, callbacks){
var method = method.toLowerCase()
, callbacks = utils.flatten([].slice.call(arguments, 2));
// strip trailing slash
if ('/' == route[route.length - 1]) {
route = route.slice(0, -1);
}
// ensure path was given
if (!path) throw new Error('Router#' + method + '() requires a path');
// ensure all callbacks are functions
callbacks.forEach(function(fn){
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, {
var layer = Layer(route, {
sensitive: this.caseSensitive,
strict: this.strict
});
strict: this.strict,
end: false
}, fn);
// add it
(this.map[method] = this.map[method] || []).push(route);
// add the middleware
debug('use %s %s', route || '/', fn.name || 'anonymous');
this.stack.push(layer);
return this;
};
/**
* 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} path
* @return {Route}
* @api public
*/
proto.route = function(path){
var route = new Route(path);
var layer = Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
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
*/
proto.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;
proto[method] = function(path, fn){
var self = this;
self.route(path)[method](fn);
return self;
};
});
+61
Ver Arquivo
@@ -0,0 +1,61 @@
var utils = require('../utils')
, debug = require('debug')('express:router:layer')
function Layer(path, options, fn) {
if (!(this instanceof Layer)) {
return new Layer(path, options, fn);
}
debug('new %s', path);
options = options || {};
this.path = path;
this.params = {};
this.regexp = utils.pathRegexp(path
, this.keys = []
, options.sensitive
, options.strict
, options.end);
this.handle = fn;
}
/**
* Check if this route matches `path`, if so
* populate `.params`.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
Layer.prototype.match = function(path){
var keys = this.keys
, 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];
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[n++] = val;
}
}
return true;
};
module.exports = Layer;
+133 -41
Ver Arquivo
@@ -3,7 +3,8 @@
* Module dependencies.
*/
var utils = require('../utils');
var debug = require('debug')('express:router:route')
, methods = require('methods')
/**
* Expose `Route`.
@@ -12,61 +13,152 @@ var utils = require('../utils');
module.exports = Route;
/**
* Initialize `Route` with the given HTTP `method`, `path`,
* and an array of `callbacks` and `options`.
* Initialize `Route` with the given `path`,
*
* Options:
*
* - `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) {
options = options || {};
function Route(path) {
debug('new %s', path);
this.path = path;
this.method = method;
this.callbacks = callbacks;
this.regexp = utils.pathRegexp(path
, this.keys = []
, options.sensitive
, options.strict);
this.stack = undefined;
// route handlers for various http methods
this.methods = {};
}
/**
* Check if this route matches `path`, if so
* populate `.params`.
*
* @param {String} path
* @return {Boolean}
* @return {Array} supported HTTP methods
* @api private
*/
Route.prototype.match = function(path){
var keys = this.keys
, params = this.params = []
, m = this.regexp.exec(path);
Route.prototype._options = function(){
return Object.keys(this.methods).map(function(method) {
return method.toUpperCase();
});
};
if (!m) return false;
/**
* dispatch req, res into this route
*
* @api private
*/
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
Route.prototype.dispatch = function(req, res, done){
var self = this;
var method = req.method.toLowerCase();
var val = 'string' == typeof m[i]
? utils.decode(m[i])
: m[i];
if (key) {
params[key.name] = val;
} else {
params.push(val);
}
if (method === 'head' && !this.methods['head']) {
method = 'get';
}
return true;
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;
};
});
+23 -172
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(){
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
};
return locals;
};
/**
* Check if `path` looks absolute.
*
@@ -110,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
@@ -258,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.
@@ -287,11 +132,12 @@ exports.escape = function(html) {
* @param {Array} keys
* @param {Boolean} sensitive
* @param {Boolean} strict
* @param {Boolean} end (whether to append $ to regex)
* @return {RegExp}
* @api private
*/
exports.pathRegexp = function(path, keys, sensitive, strict) {
exports.pathRegexp = function(path, keys, sensitive, strict, end) {
if (toString.call(path) == '[object RegExp]') return path;
if (Array.isArray(path)) path = '(' + path.join('|') + ')';
path = path
@@ -310,24 +156,29 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
return new RegExp('^' + path + ((end) ? '$' : ''), sensitive ? '' : 'i');
}
/**
* Decodes a URI component. Returns
* the original string if the component
* is malformed.
* Parse the `req` url with memoization.
*
* @param {String} str
* @return {String}
* @param {ServerRequest} req
* @return {Object}
* @api private
*/
exports.decode = function(str) {
try {
return decodeURIComponent(str);
} catch (e) {
return str;
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.4.2",
"version": "3.4.7",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{
@@ -22,28 +22,36 @@
}
],
"dependencies": {
"connect": "2.9.2",
"commander": "1.3.2",
"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.2",
"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": "2",
"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
+144
Ver Arquivo
@@ -0,0 +1,144 @@
var express = require('../')
, Route = express.Route
, methods = require('methods')
, assert = require('assert');
describe('Route', function(){
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' }, {});
})
})
})
+116 -63
Ver Arquivo
@@ -1,103 +1,156 @@
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();
})
it('should return a function with router methods', function() {
var router = Router();
assert(typeof router == 'function');
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 router = new Router();
assert(typeof router == 'function');
var method = 'GET';
var url = '/foo?bar=baz';
assert(typeof router.get == 'function');
assert(typeof router.handle == 'function');
assert(typeof router.use == 'function');
});
var route = router.match(method, url, 0);
route.constructor.name.should.equal('Route');
route.method.should.equal('get');
route.path.should.equal('/foo');
it('should support .use of other routers', function(done) {
var router = Router();
var another = Router();
var route = router.match(method, url, 1);
route.path.should.equal('/foob?');
another.get('/bar', function(req, res) {
res.done();
});
router.use('/foo', another);
var route = router.match(method, url, 2);
assert(!route);
router.handle({ url: '/foo/bar', method: 'GET' }, { done: done });
});
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(){
describe('.handle', 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();
})
})
describe('.param', function() {
it('should call param function when routing VERBS', function(done) {
var router = new Router();
router.param('id', function(req, res, next, id) {
assert.equal(id, '123');
next();
});
router.get('/foo/:id/bar', function(req, res, next) {
assert.equal(req.params.id, '123');
next();
});
router.handle({ url: '/foo/123/bar', method: 'get' }, {}, done);
});
it('should call param function when routing middleware', function(done) {
var router = new Router();
router.param('id', function(req, res, next, id) {
assert.equal(id, '123');
next();
});
router.use('/foo/:id/bar', function(req, res, next) {
assert.equal(req.params.id, '123');
assert.equal(req.url, '/baz');
next();
});
router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done);
});
});
})
+3 -3
Ver Arquivo
@@ -18,7 +18,7 @@ describe('auth', function(){
it('should redirect to /login', function(done){
request(app)
.get('/')
.end(redirects(/\/login$/, done))
.end(redirects(/login$/, done))
})
})
@@ -26,7 +26,7 @@ describe('auth', function(){
it('should redirect to /login', function(done){
request(app)
.get('/restricted')
.end(redirects(/\/login$/,done))
.end(redirects(/login$/,done))
})
})
@@ -36,7 +36,7 @@ describe('auth', function(){
.post('/login')
.type('urlencoded')
.send('username=not-tj&password=foobar')
.end(redirects(/\/login$/, done))
.end(redirects(/login$/, 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);
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);
})
})
})
+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();
});
})
+20
Ver Arquivo
@@ -0,0 +1,20 @@
var express = require('../')
, request = require('./support/http')
describe('app.route', function(){
it('should return a new route', function(done){
var app = express();
app.route('/foo')
.get(function(req, res) {
res.send('get');
})
.post(function(req, res) {
res.send('post');
});
request(app)
.post('/foo')
.expect('post', done);
});
});
+43 -45
Ver Arquivo
@@ -7,6 +7,8 @@ var express = require('../')
describe('app.router', function(){
describe('methods supported', function(){
methods.forEach(function(method){
if (method === 'connect') return;
it('should include ' + method.toUpperCase(), function(done){
if (method == 'delete') method = 'del';
var app = express();
@@ -27,61 +29,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 accept params in malformed paths', function(done) {
var app = express();
it('should not accept params in malformed paths', 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('/%foobar')
.expect('%foobar', done);
})
request(app)
.get('/%foobar')
.expect(400, done);
})
it('should be .use()able', function(done){
var app = express();
it('should not decode spaces', 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('/foo+bar')
.expect('foo+bar', done);
})
app.use(app.router);
it('should work with unicode', 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('/')
.end(function(res){
calls.should.eql(['before', 'GET /', 'after'])
done();
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 = [];
@@ -126,8 +124,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);
});
@@ -302,8 +300,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'])
})
})
})
-10
Ver Arquivo
@@ -4,16 +4,6 @@ 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;
})
+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);
})
})
})
})
-13
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();
+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();
});
+3 -2
Ver Arquivo
@@ -1,13 +1,14 @@
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();
app.use(express.cookieParser('secret'));
app.use(cookieParser('secret'));
app.use(function(req, res){
if ('/set' == req.path) {
+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();
+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();
+1 -8
Ver Arquivo
@@ -181,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'));
})
+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();
});
-149
Ver Arquivo
@@ -18,154 +18,5 @@ describe('res', function(){
done();
})
})
describe('with leading //', function(){
it('should pass through scheme-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('//cuteoverload.com').end();
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('location', '//cuteoverload.com');
done();
})
})
})
describe('with leading /', function(){
it('should construct scheme-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('/login').end();
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('location', '/login');
done();
})
})
})
describe('with leading ./', function(){
it('should construct path-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('./edit').end();
});
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/./edit');
done();
})
})
})
describe('with leading ../', function(){
it('should construct path-relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('../new').end();
});
request(app)
.get('/post/1')
.end(function(err, res){
res.headers.should.have.property('location', '/post/1/../new');
done();
})
})
})
describe('without leading /', function(){
it('should construct mount-point relative urls', function(done){
var app = express();
app.use(function(req, res){
res.location('login').end();
});
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('location', '/login');
done();
})
})
})
describe('when mounted', function(){
describe('deeply', function(){
it('should respect the mount-point', function(done){
var app = express()
, blog = express()
, admin = express();
admin.use(function(req, res){
res.location('login').end();
});
app.use('/blog', blog);
blog.use('/admin', admin);
request(app)
.get('/blog/admin')
.end(function(err, res){
res.headers.should.have.property('location', '/blog/admin/login');
done();
})
})
})
describe('omitting leading /', function(){
it('should respect the mount-point', function(done){
var app = express()
, admin = express();
admin.use(function(req, res){
res.location('admin/login').end();
});
app.use('/blog', admin);
request(app)
.get('/blog')
.end(function(err, res){
res.headers.should.have.property('location', '/blog/admin/login');
done();
})
})
})
describe('providing leading /', function(){
it('should ignore mount-point', function(done){
var app = express()
, admin = express();
admin.use(function(req, res){
res.location('/admin/login').end();
});
app.use('/blog', admin);
request(app)
.get('/blog')
.end(function(err, res){
res.headers.should.have.property('location', '/admin/login');
done();
})
})
})
})
})
})
+1 -73
Ver Arquivo
@@ -105,7 +105,7 @@ describe('res', function(){
.set('Host', 'http://example.com')
.set('Accept', 'text/html')
.end(function(err, res){
res.text.should.equal('<p>Moved Temporarily. Redirecting to <a href="/&lt;lame&gt;">/&lt;lame&gt;</a></p>');
res.text.should.equal('<p>Moved Temporarily. Redirecting to <a href="&lt;lame&gt;">&lt;lame&gt;</a></p>');
done();
})
})
@@ -169,76 +169,4 @@ describe('res', function(){
})
})
})
describe('responses redirected to relative paths', function(){
function create(depth, parent) {
var app = express();
if (parent) {
parent.use('/depth' + depth, app);
}
app.get('/', function(req, res){
res.redirect('./index');
});
app.get('/index', function(req, res){
res.json({ depth: depth, content: 'index' });
});
return app;
}
var root = create(0);
var depth1 = create(1, root);
var depth2 = create(2, depth1);
var depth3 = create(3, depth2);
root.use('/depth2', depth2);
root.use('/depth3', depth3);
it('should not contain redundant leading slashes in the location header', function(done){
request(root)
.get('/')
.end(function(err, res){
res.headers.location.search(/^\/{2}/).should.equal(-1);
done();
})
})
it('should preserve context when redirecting nested applications at any depth', function(done){
request(root)
.get('/depth1')
.end(function(err, res){
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');
request(root)
.get('/depth1/depth2/depth3')
.end(function(err, res){
res.headers.should.have.property('location', '/depth1/depth2/depth3/./index');
done();
})
})
});
})
it('should redirect correctly for nested applications that have been remounted', function(done){
request(root)
.get('/depth2')
.end(function(err, res){
res.headers.should.have.property('location', '/depth2/./index');
request(root)
.get('/depth3')
.end(function(err, res){
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();
});
-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');
})
})
})