├── test ├── fixtures │ ├── .name │ ├── name.txt │ ├── % of dogs.txt │ ├── name.tmpl │ ├── blog │ │ ├── index.html │ │ └── post │ │ │ └── index.tmpl │ ├── user.html │ ├── user.tmpl │ ├── email.tmpl │ ├── default_layout │ │ ├── name.tmpl │ │ └── user.tmpl │ └── local_layout │ │ └── user.tmpl ├── mocha.opts ├── support │ ├── env.js │ ├── utils.js │ └── tmpl.js ├── app.del.js ├── app.listen.js ├── regression.js ├── req.path.js ├── res.status.js ├── res.get.js ├── acceptance │ ├── ejs.js │ ├── markdown.js │ ├── error.js │ ├── downloads.js │ ├── cookie-sessions.js │ ├── route-map.js │ ├── params.js │ ├── multi-router.js │ ├── vhost.js │ ├── content-negotiation.js │ ├── cookies.js │ ├── resource.js │ ├── error-pages.js │ ├── route-separation.js │ ├── auth.js │ ├── web-service.js │ └── mvc.js ├── app.request.js ├── req.route.js ├── res.sendStatus.js ├── app.all.js ├── app.locals.js ├── res.locals.js ├── req.signedCookies.js ├── req.acceptsEncoding.js ├── req.acceptsEncodings.js ├── res.clearCookie.js ├── app.response.js ├── middleware.basic.js ├── res.type.js ├── res.location.js ├── req.stale.js ├── req.fresh.js ├── app.route.js ├── req.acceptsCharset.js ├── req.acceptsCharsets.js ├── res.links.js ├── req.acceptsLanguage.js ├── req.acceptsLanguages.js ├── req.param.js ├── req.get.js ├── app.head.js ├── app.routes.error.js ├── req.xhr.js ├── exports.js ├── req.ips.js ├── res.attachment.js ├── res.vary.js ├── req.baseUrl.js ├── app.engine.js ├── req.ip.js ├── req.range.js ├── req.secure.js ├── utils.js ├── app.js ├── req.protocol.js ├── res.append.js ├── res.set.js ├── app.options.js ├── req.accepts.js ├── res.download.js ├── req.query.js └── req.hostname.js ├── examples ├── static-files │ ├── public │ │ ├── hello.txt │ │ ├── js │ │ │ └── app.js │ │ └── css │ │ │ └── style.css │ └── index.js ├── auth │ └── views │ │ ├── foot.ejs │ │ ├── head.ejs │ │ └── login.ejs ├── ejs │ ├── views │ │ ├── footer.html │ │ ├── header.html │ │ └── users.html │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ └── index.js ├── downloads │ ├── files │ │ ├── amazing.txt │ │ └── CCTV大赛上海分赛区.txt │ └── index.js ├── error-pages │ ├── views │ │ ├── footer.ejs │ │ ├── 404.ejs │ │ ├── error_header.ejs │ │ ├── 500.ejs │ │ └── index.ejs │ └── index.js ├── route-separation │ ├── views │ │ ├── footer.ejs │ │ ├── header.ejs │ │ ├── users │ │ │ ├── view.ejs │ │ │ ├── index.ejs │ │ │ └── edit.ejs │ │ ├── index.ejs │ │ └── posts │ │ │ └── index.ejs │ ├── site.js │ ├── post.js │ ├── public │ │ └── style.css │ ├── user.js │ └── index.js ├── markdown │ ├── views │ │ └── index.md │ └── index.js ├── mvc │ ├── controllers │ │ ├── main │ │ │ └── index.js │ │ ├── pet │ │ │ ├── views │ │ │ │ ├── show.ejs │ │ │ │ └── edit.ejs │ │ │ └── index.js │ │ ├── user │ │ │ ├── views │ │ │ │ ├── list.hbs │ │ │ │ ├── show.hbs │ │ │ │ └── edit.hbs │ │ │ └── index.js │ │ └── user-pet │ │ │ └── index.js │ ├── public │ │ └── style.css │ ├── views │ │ ├── 404.ejs │ │ └── 5xx.ejs │ ├── db.js │ ├── lib │ │ └── boot.js │ └── index.js ├── content-negotiation │ ├── db.js │ ├── users.js │ └── index.js ├── hello-world │ └── index.js ├── multi-router │ ├── controllers │ │ ├── api_v1.js │ │ └── api_v2.js │ └── index.js ├── search │ ├── public │ │ ├── client.js │ │ └── index.html │ └── index.js ├── view-locals │ ├── views │ │ └── index.ejs │ ├── user.js │ └── index.js ├── cookie-sessions │ └── index.js ├── session │ ├── index.js │ └── redis.js ├── online │ └── index.js ├── vhost │ └── index.js ├── view-constructor │ ├── github-view.js │ └── index.js ├── cookies │ └── index.js ├── error │ └── index.js ├── route-map │ └── index.js ├── multipart │ └── index.js ├── params │ └── index.js ├── resource │ └── index.js ├── route-middleware │ └── index.js └── web-service │ └── index.js ├── .editorconfig ├── benchmarks ├── Makefile ├── run └── middleware.js ├── index.js ├── .gitignore ├── appveyor.yml ├── .travis.yml ├── lib ├── middleware │ ├── init.js │ └── query.js └── express.js ├── LICENSE ├── Security.md ├── Collaborator-Guide.md └── package.json /test/fixtures/.name: -------------------------------------------------------------------------------- 1 | tobi -------------------------------------------------------------------------------- /test/fixtures/name.txt: -------------------------------------------------------------------------------- 1 | tobi -------------------------------------------------------------------------------- /test/fixtures/% of dogs.txt: -------------------------------------------------------------------------------- 1 | 20% -------------------------------------------------------------------------------- /test/fixtures/name.tmpl: -------------------------------------------------------------------------------- 1 |

$name

-------------------------------------------------------------------------------- /examples/static-files/public/hello.txt: -------------------------------------------------------------------------------- 1 | hey -------------------------------------------------------------------------------- /examples/static-files/public/js/app.js: -------------------------------------------------------------------------------- 1 | foo -------------------------------------------------------------------------------- /test/fixtures/blog/index.html: -------------------------------------------------------------------------------- 1 | index -------------------------------------------------------------------------------- /test/fixtures/user.html: -------------------------------------------------------------------------------- 1 |

{{user.name}}

-------------------------------------------------------------------------------- /test/fixtures/user.tmpl: -------------------------------------------------------------------------------- 1 |

$user.name

-------------------------------------------------------------------------------- /test/fixtures/email.tmpl: -------------------------------------------------------------------------------- 1 |

This is an email

-------------------------------------------------------------------------------- /test/fixtures/blog/post/index.tmpl: -------------------------------------------------------------------------------- 1 |

blog post

-------------------------------------------------------------------------------- /test/fixtures/default_layout/name.tmpl: -------------------------------------------------------------------------------- 1 |

$name

-------------------------------------------------------------------------------- /examples/auth/views/foot.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/ejs/views/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /test/fixtures/default_layout/user.tmpl: -------------------------------------------------------------------------------- 1 |

$user.name

-------------------------------------------------------------------------------- /examples/downloads/files/amazing.txt: -------------------------------------------------------------------------------- 1 | what an amazing download -------------------------------------------------------------------------------- /examples/error-pages/views/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/static-files/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | 3 | } -------------------------------------------------------------------------------- /test/fixtures/local_layout/user.tmpl: -------------------------------------------------------------------------------- 1 | $user.name -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require should 2 | --slow 20 3 | --growl 4 | -------------------------------------------------------------------------------- /examples/route-separation/views/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/downloads/files/CCTV大赛上海分赛区.txt: -------------------------------------------------------------------------------- 1 | Only for test. 2 | The file name is faked. -------------------------------------------------------------------------------- /examples/markdown/views/index.md: -------------------------------------------------------------------------------- 1 | 2 | # {title} 3 | 4 | Just an example view rendered with _markdown_. -------------------------------------------------------------------------------- /test/support/env.js: -------------------------------------------------------------------------------- 1 | 2 | process.env.NODE_ENV = 'test'; 3 | process.env.NO_DEPRECATION = 'express'; 4 | -------------------------------------------------------------------------------- /examples/mvc/controllers/main/index.js: -------------------------------------------------------------------------------- 1 | exports.index = function(req, res){ 2 | res.redirect('/users'); 3 | }; -------------------------------------------------------------------------------- /examples/error-pages/views/404.ejs: -------------------------------------------------------------------------------- 1 | <% include error_header %> 2 |

Cannot find <%= url %>

3 | <% include footer %> 4 | -------------------------------------------------------------------------------- /examples/route-separation/site.js: -------------------------------------------------------------------------------- 1 | exports.index = function(req, res){ 2 | res.render('index', { title: 'Route Separation Example' }); 3 | }; -------------------------------------------------------------------------------- /examples/ejs/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px 80px; 3 | font: 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif; 4 | } 5 | -------------------------------------------------------------------------------- /examples/error-pages/views/error_header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Error 5 | 6 | 7 | 8 |

An error occurred!

9 | -------------------------------------------------------------------------------- /examples/content-negotiation/db.js: -------------------------------------------------------------------------------- 1 | var users = []; 2 | 3 | users.push({ name: 'Tobi' }); 4 | users.push({ name: 'Loki' }); 5 | users.push({ name: 'Jane' }); 6 | 7 | module.exports = users; -------------------------------------------------------------------------------- /examples/ejs/views/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= title %> 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/route-separation/views/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= title %> 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/route-separation/views/users/view.ejs: -------------------------------------------------------------------------------- 1 | <% include ../header %> 2 | 3 |

<%= user.name %>

4 | 5 |
6 |

Email: <%= user.email %>

7 |
8 | 9 | <% include ../footer %> 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [{*.js,*.json,*.yml}] 10 | indent_size = 2 11 | indent_style = space 12 | -------------------------------------------------------------------------------- /examples/ejs/views/users.html: -------------------------------------------------------------------------------- 1 | <% include header.html %> 2 | 3 |

Users

4 | 9 | 10 | <% include footer.html %> 11 | -------------------------------------------------------------------------------- /examples/error-pages/views/500.ejs: -------------------------------------------------------------------------------- 1 | <% include error_header %> 2 |

Error: <%= error.message %>

3 | <% if (settings['verbose errors']) { %> 4 |
<%= error.stack %>
5 | <% } else { %> 6 |

An error occurred!

7 | <% } %> 8 | <% include footer %> 9 | -------------------------------------------------------------------------------- /examples/route-separation/views/index.ejs: -------------------------------------------------------------------------------- 1 | <% include header %> 2 | 3 |

<%= title %>

4 | 5 | 9 | 10 | <% include footer %> 11 | -------------------------------------------------------------------------------- /benchmarks/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | @./run 1 middleware 4 | @./run 5 middleware 5 | @./run 10 middleware 6 | @./run 15 middleware 7 | @./run 20 middleware 8 | @./run 30 middleware 9 | @./run 50 middleware 10 | @./run 100 middleware 11 | @echo 12 | 13 | .PHONY: all 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express 3 | * Copyright(c) 2009-2013 TJ Holowaychuk 4 | * Copyright(c) 2013 Roman Shtylman 5 | * Copyright(c) 2014-2015 Douglas Christopher Wilson 6 | * MIT Licensed 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = require('./lib/express'); 12 | -------------------------------------------------------------------------------- /benchmarks/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo 4 | MW=$1 node $2 & 5 | pid=$! 6 | 7 | sleep 2 8 | 9 | wrk 'http://localhost:3333/?foo[bar]=baz' \ 10 | -d 3 \ 11 | -c 50 \ 12 | -t 8 \ 13 | | grep 'Requests/sec' \ 14 | | awk '{ print " " $2 }' 15 | 16 | kill $pid 17 | -------------------------------------------------------------------------------- /examples/mvc/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 16px "Helvetica Neue", Helvetica, Arial; 4 | } 5 | a { 6 | color: #107aff; 7 | text-decoration: none; 8 | } 9 | a:hover { 10 | text-decoration: underline; 11 | } 12 | h1 a { 13 | font-size: 16px; 14 | } 15 | -------------------------------------------------------------------------------- /examples/route-separation/views/posts/index.ejs: -------------------------------------------------------------------------------- 1 | <% include ../header %> 2 | 3 |

Posts

4 | 5 |
6 | <% posts.forEach(function(post) { %> 7 |
<%= post.title %>
8 |
<%= post.body %>
9 | <% }) %> 10 |
11 | 12 | <% include ../footer %> 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store* 3 | Icon? 4 | ._* 5 | 6 | # Windows 7 | Thumbs.db 8 | ehthumbs.db 9 | Desktop.ini 10 | 11 | # Linux 12 | .directory 13 | *~ 14 | 15 | 16 | # npm 17 | node_modules 18 | *.log 19 | *.gz 20 | 21 | 22 | # Coveralls 23 | coverage 24 | 25 | # Benchmarking 26 | benchmarks/graphs 27 | -------------------------------------------------------------------------------- /examples/mvc/views/404.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Not Found 6 | 7 | 8 | 9 |

404: Not Found

10 |

Sorry we can't find <%= url %>

11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/hello-world/index.js: -------------------------------------------------------------------------------- 1 | var express = require('../../'); 2 | 3 | var app = express(); 4 | 5 | app.get('/', function(req, res){ 6 | res.send('Hello World'); 7 | }); 8 | 9 | /* istanbul ignore next */ 10 | if (!module.parent) { 11 | app.listen(3000); 12 | console.log('Express started on port 3000'); 13 | } 14 | -------------------------------------------------------------------------------- /examples/mvc/controllers/pet/views/show.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= pet.name %> 6 | 7 | 8 | 9 |

<%= pet.name %> edit

10 | 11 |

You are viewing <%= pet.name %>

12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/mvc/views/5xx.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Internal Server Error 6 | 7 | 8 | 9 |

500: Internal Server Error

10 |

Looks like something blew up!

11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/multi-router/controllers/api_v1.js: -------------------------------------------------------------------------------- 1 | var express = require('../../..'); 2 | 3 | var apiv1 = express.Router(); 4 | 5 | apiv1.get('/', function(req, res) { 6 | res.send('Hello from APIv1 root route.'); 7 | }); 8 | 9 | apiv1.get('/users', function(req, res) { 10 | res.send('List of APIv1 users.'); 11 | }); 12 | 13 | module.exports = apiv1; 14 | -------------------------------------------------------------------------------- /examples/multi-router/controllers/api_v2.js: -------------------------------------------------------------------------------- 1 | var express = require('../../..'); 2 | 3 | var apiv2 = express.Router(); 4 | 5 | apiv2.get('/', function(req, res) { 6 | res.send('Hello from APIv2 root route.'); 7 | }); 8 | 9 | apiv2.get('/users', function(req, res) { 10 | res.send('List of APIv2 users.'); 11 | }); 12 | 13 | module.exports = apiv2; 14 | -------------------------------------------------------------------------------- /examples/route-separation/post.js: -------------------------------------------------------------------------------- 1 | // Fake posts database 2 | 3 | var posts = [ 4 | { title: 'Foo', body: 'some foo bar' }, 5 | { title: 'Foo bar', body: 'more foo bar' }, 6 | { title: 'Foo bar baz', body: 'more foo bar baz' } 7 | ]; 8 | 9 | exports.list = function(req, res){ 10 | res.render('posts', { title: 'Posts', posts: posts }); 11 | }; 12 | -------------------------------------------------------------------------------- /examples/error-pages/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom Pages Example 5 | 6 | 7 | 8 |

My Site

9 |

Pages Example

10 | 11 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/route-separation/views/users/index.ejs: -------------------------------------------------------------------------------- 1 | <% include ../header %> 2 | 3 |

<%= title %>

4 | 5 |
6 | <% users.forEach(function(user, index) { %> 7 |
  • 8 | <%= user.name %> 9 | edit 10 |
  • 11 | <% }) %> 12 |
    13 | 14 | <% include ../footer %> 15 | -------------------------------------------------------------------------------- /examples/auth/views/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/list.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Users 6 | 7 | 8 |

    Users

    9 |

    Click a user below to view their pets.

    10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/app.del.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('app.del()', function(){ 6 | it('should alias app.delete()', function(done){ 7 | var app = express(); 8 | 9 | app.del('/tobi', function(req, res){ 10 | res.end('deleted tobi!'); 11 | }); 12 | 13 | request(app) 14 | .del('/tobi') 15 | .expect('deleted tobi!', done); 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /examples/route-separation/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Helvetica Neue", Arial, sans-serif; 4 | } 5 | a { 6 | color: #00AEFF; 7 | text-decoration: none; 8 | } 9 | a.edit { 10 | color: #000; 11 | opacity: .3; 12 | } 13 | a.edit::before { 14 | content: ' ['; 15 | } 16 | a.edit::after { 17 | content: ']'; 18 | } 19 | dt { 20 | font-weight: bold; 21 | } 22 | dd { 23 | margin: 15px; 24 | } -------------------------------------------------------------------------------- /examples/search/public/client.js: -------------------------------------------------------------------------------- 1 | var search = document.querySelector('[type=search]'); 2 | var code = document.querySelector('pre'); 3 | 4 | search.addEventListener('keyup', function(){ 5 | var xhr = new XMLHttpRequest; 6 | xhr.open('GET', '/search/' + search.value, true); 7 | xhr.onreadystatechange = function(){ 8 | if (4 == xhr.readyState) { 9 | code.textContent = xhr.responseText; 10 | } 11 | }; 12 | xhr.send(); 13 | }, false); -------------------------------------------------------------------------------- /examples/mvc/controllers/pet/views/edit.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Edit <%= pet.name %> 6 | 7 | 8 | 9 |

    <%= pet.name %>

    10 |
    11 | 12 | 13 |
    14 | 15 | 16 | -------------------------------------------------------------------------------- /test/app.listen.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('app.listen()', function(){ 6 | it('should wrap with an HTTP server', function(done){ 7 | var app = express(); 8 | 9 | app.del('/tobi', function(req, res){ 10 | res.end('deleted tobi!'); 11 | }); 12 | 13 | var server = app.listen(9999, function(){ 14 | server.close(); 15 | done(); 16 | }); 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /test/regression.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('throw after .end()', function(){ 6 | it('should fail gracefully', function(done){ 7 | var app = express(); 8 | 9 | app.get('/', function(req, res){ 10 | res.end('yay'); 11 | throw new Error('boom'); 12 | }); 13 | 14 | request(app) 15 | .get('/') 16 | .expect('yay') 17 | .expect(200, done); 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /examples/multi-router/index.js: -------------------------------------------------------------------------------- 1 | var express = require('../..'); 2 | 3 | var app = module.exports = express(); 4 | 5 | app.use('/api/v1', require('./controllers/api_v1')); 6 | app.use('/api/v2', require('./controllers/api_v2')); 7 | 8 | app.get('/', function(req, res) { 9 | res.send('Hello form root route.'); 10 | }); 11 | 12 | /* istanbul ignore next */ 13 | if (!module.parent) { 14 | app.listen(3000); 15 | console.log('Express started on port 3000'); 16 | } 17 | -------------------------------------------------------------------------------- /examples/mvc/db.js: -------------------------------------------------------------------------------- 1 | // faux database 2 | 3 | var pets = exports.pets = []; 4 | 5 | pets.push({ name: 'Tobi', id: 0 }); 6 | pets.push({ name: 'Loki', id: 1 }); 7 | pets.push({ name: 'Jane', id: 2 }); 8 | pets.push({ name: 'Raul', id: 3 }); 9 | 10 | var users = exports.users = []; 11 | 12 | users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 }); 13 | users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 }); 14 | users.push({ name: 'Nathan', pets: [], id: 2 }); -------------------------------------------------------------------------------- /examples/content-negotiation/users.js: -------------------------------------------------------------------------------- 1 | 2 | var users = require('./db'); 3 | 4 | exports.html = function(req, res){ 5 | res.send(''); 8 | }; 9 | 10 | exports.text = function(req, res){ 11 | res.send(users.map(function(user){ 12 | return ' - ' + user.name + '\n'; 13 | }).join('')); 14 | }; 15 | 16 | exports.json = function(req, res){ 17 | res.json(users); 18 | }; 19 | -------------------------------------------------------------------------------- /benchmarks/middleware.js: -------------------------------------------------------------------------------- 1 | 2 | var http = require('http'); 3 | var express = require('..'); 4 | var app = express(); 5 | 6 | // number of middleware 7 | 8 | var n = parseInt(process.env.MW || '1', 10); 9 | console.log(' %s middleware', n); 10 | 11 | while (n--) { 12 | app.use(function(req, res, next){ 13 | next(); 14 | }); 15 | } 16 | 17 | var body = new Buffer('Hello World'); 18 | 19 | app.use(function(req, res, next){ 20 | res.send(body); 21 | }); 22 | 23 | app.listen(3333); 24 | -------------------------------------------------------------------------------- /test/req.path.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.path', function(){ 7 | it('should return the parsed pathname', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | res.end(req.path); 12 | }); 13 | 14 | request(app) 15 | .get('/login?redirect=/post/1/comments') 16 | .expect('/login', done); 17 | }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/res.status.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.status(code)', function(){ 7 | it('should set the response .statusCode', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | res.status(201).end('Created'); 12 | }); 13 | 14 | request(app) 15 | .get('/') 16 | .expect('Created') 17 | .expect(201, done); 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /examples/search/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Search example 6 | 12 | 13 | 14 |

    Search

    15 |

    Try searching for "ferret" or "cat".

    16 | 17 |
    18 |   
    19 | 
    20 | 
    21 | 
    
    
    --------------------------------------------------------------------------------
    /examples/view-locals/views/index.ejs:
    --------------------------------------------------------------------------------
     1 | 
     2 | 
     3 |   
     4 |     
     5 |     <%= title %>
     6 |     
    12 |   
    13 |   
    14 |     

    <%= title %>

    15 | <% users.forEach(function(user) { %> 16 |
  • <%= user.name %> is a <% user.age %> year old <%= user.species %>
  • 17 | <% }); %> 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/res.get.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('..'); 3 | var request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.get(field)', function(){ 7 | it('should get the response header field', function (done) { 8 | var app = express(); 9 | 10 | app.use(function (req, res) { 11 | res.setHeader('Content-Type', 'text/x-foo'); 12 | res.send(res.get('Content-Type')); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect(200, 'text/x-foo', done); 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /examples/route-separation/views/users/edit.ejs: -------------------------------------------------------------------------------- 1 | <% include ../header %> 2 | 3 |

    Editing <%= user.name %>

    4 | 5 |
    6 |
    7 |

    8 | Name: 9 | 10 |

    11 | 12 |

    13 | Email: 14 | 15 |

    16 | 17 |

    18 | 19 |

    20 |
    21 |
    22 | 23 | <% include ../footer %> 24 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user-pet/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var db = require('../../db'); 6 | 7 | exports.name = 'pet'; 8 | exports.prefix = '/user/:user_id'; 9 | 10 | exports.create = function(req, res, next){ 11 | var id = req.params.user_id; 12 | var user = db.users[id]; 13 | var body = req.body; 14 | if (!user) return next('route'); 15 | var pet = { name: body.pet.name }; 16 | pet.id = db.pets.push(pet) - 1; 17 | user.pets.push(pet); 18 | res.message('Added pet ' + body.pet.name); 19 | res.redirect('/user/' + id); 20 | }; 21 | -------------------------------------------------------------------------------- /examples/auth/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | <% var title = 'Authentication Example' %> 3 | <% include head %> 4 | 5 |

    Login

    6 | <%- message %> 7 | Try accessing /restricted, then authenticate with "tj" and "foobar". 8 |
    9 |

    10 | 11 | 12 |

    13 |

    14 | 15 | 16 |

    17 |

    18 | 19 |

    20 |
    21 | 22 | <% include foot %> 23 | -------------------------------------------------------------------------------- /test/acceptance/ejs.js: -------------------------------------------------------------------------------- 1 | 2 | var request = require('supertest') 3 | , app = require('../../examples/ejs'); 4 | 5 | describe('ejs', function(){ 6 | describe('GET /', function(){ 7 | it('should respond with html', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect('Content-Type', 'text/html; charset=utf-8') 11 | .expect(/
  • tobi <tobi@learnboost\.com><\/li>/) 12 | .expect(/
  • loki <loki@learnboost\.com><\/li>/) 13 | .expect(/
  • jane <jane@learnboost\.com><\/li>/) 14 | .expect(200, done) 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /test/acceptance/markdown.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/markdown') 3 | var request = require('supertest') 4 | 5 | describe('markdown', function(){ 6 | describe('GET /', function(){ 7 | it('should respond with html', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect(/]*>Markdown Example<\/h1>/,done) 11 | }) 12 | }) 13 | 14 | describe('GET /fail',function(){ 15 | it('should respond with an error', function(done){ 16 | request(app) 17 | .get('/fail') 18 | .expect(500,done) 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/support/utils.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | * @private 5 | */ 6 | var assert = require('assert'); 7 | 8 | /** 9 | * Module exports. 10 | * @public 11 | */ 12 | exports.shouldNotHaveHeader = shouldNotHaveHeader; 13 | 14 | /** 15 | * Assert that a supertest response does not have a header. 16 | * 17 | * @param {string} header Header name to check 18 | * @returns {function} 19 | */ 20 | function shouldNotHaveHeader(header) { 21 | return function (res) { 22 | assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header); 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /test/app.request.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('app', function(){ 6 | describe('.request', function(){ 7 | it('should extend the request prototype', function(done){ 8 | var app = express(); 9 | 10 | app.request.querystring = function(){ 11 | return require('url').parse(this.url).query; 12 | }; 13 | 14 | app.use(function(req, res){ 15 | res.end(req.querystring()); 16 | }); 17 | 18 | request(app) 19 | .get('/foo?name=tobi') 20 | .expect('name=tobi', done); 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/show.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{user.name}} 6 | 7 | 8 |

    {{user.name}} edit

    9 | 10 | {{#if hasMessages}} 11 |
      12 | {{#each messages}} 13 |
    • {{this}}
    • 14 | {{/each}} 15 |
    16 | {{/if}} 17 | 18 | {{#if user.pets.length}} 19 |

    View {{user.name}}'s pets:

    20 |
      21 | {{#each user.pets}} 22 |
    • {{name}}
    • 23 | {{/each}} 24 |
    25 | {{else}} 26 |

    No pets!

    27 | {{/if}} 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/req.route.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.route', function(){ 7 | it('should be the executed Route', function(done){ 8 | var app = express(); 9 | 10 | app.get('/user/:id/:op?', function(req, res, next){ 11 | req.route.path.should.equal('/user/:id/:op?'); 12 | next(); 13 | }); 14 | 15 | app.get('/user/:id/edit', function(req, res){ 16 | req.route.path.should.equal('/user/:id/edit'); 17 | res.end(); 18 | }); 19 | 20 | request(app) 21 | .get('/user/12/edit') 22 | .expect(200, done); 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: "0.10" 4 | - nodejs_version: "0.12" 5 | - nodejs_version: "1.8" 6 | - nodejs_version: "2.5" 7 | - nodejs_version: "3.3" 8 | - nodejs_version: "4.8" 9 | - nodejs_version: "5.12" 10 | - nodejs_version: "6.10" 11 | - nodejs_version: "7.6" 12 | cache: 13 | - node_modules 14 | install: 15 | - ps: Install-Product node $env:nodejs_version 16 | - npm rm --save-dev connect-redis 17 | - if exist node_modules npm prune 18 | - if exist node_modules npm rebuild 19 | - npm install 20 | build: off 21 | test_script: 22 | - node --version 23 | - npm --version 24 | - npm run test-ci 25 | version: "{build}" 26 | -------------------------------------------------------------------------------- /examples/cookie-sessions/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var cookieSession = require('cookie-session'); 6 | var express = require('../../'); 7 | 8 | var app = module.exports = express(); 9 | 10 | // add req.session cookie support 11 | app.use(cookieSession({ secret: 'manny is cool' })); 12 | 13 | // do something with the session 14 | app.use(count); 15 | 16 | // custom middleware 17 | function count(req, res) { 18 | req.session.count = req.session.count || 0; 19 | var n = req.session.count++; 20 | res.send('viewed ' + n + ' times\n'); 21 | } 22 | 23 | /* istanbul ignore next */ 24 | if (!module.parent) { 25 | app.listen(3000); 26 | console.log('Express started on port 3000'); 27 | } 28 | -------------------------------------------------------------------------------- /examples/mvc/controllers/pet/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var db = require('../../db'); 6 | 7 | exports.engine = 'ejs'; 8 | 9 | exports.before = function(req, res, next){ 10 | var pet = db.pets[req.params.pet_id]; 11 | if (!pet) return next('route'); 12 | req.pet = pet; 13 | next(); 14 | }; 15 | 16 | exports.show = function(req, res, next){ 17 | res.render('show', { pet: req.pet }); 18 | }; 19 | 20 | exports.edit = function(req, res, next){ 21 | res.render('edit', { pet: req.pet }); 22 | }; 23 | 24 | exports.update = function(req, res, next){ 25 | var body = req.body; 26 | req.pet.name = body.pet.name; 27 | res.message('Information updated!'); 28 | res.redirect('/pet/' + req.pet.id); 29 | }; 30 | -------------------------------------------------------------------------------- /test/acceptance/error.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/error') 3 | , request = require('supertest'); 4 | 5 | describe('error', function(){ 6 | describe('GET /', function(){ 7 | it('should respond with 500', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect(500,done) 11 | }) 12 | }) 13 | 14 | describe('GET /next', function(){ 15 | it('should respond with 500', function(done){ 16 | request(app) 17 | .get('/next') 18 | .expect(500,done) 19 | }) 20 | }) 21 | 22 | describe('GET /missing', function(){ 23 | it('should respond with 404', function(done){ 24 | request(app) 25 | .get('/missing') 26 | .expect(404,done) 27 | }) 28 | }) 29 | }) -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/edit.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Edit {{user.name}} 6 | 7 | 8 |

    {{user.name}}

    9 |
    10 | 13 | 14 | 15 |
    16 | 17 |
    18 | 21 | 22 | 23 |
    24 | 25 | 26 | -------------------------------------------------------------------------------- /test/res.sendStatus.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert') 3 | var express = require('..') 4 | var request = require('supertest') 5 | 6 | describe('res', function () { 7 | describe('.sendStatus(statusCode)', function () { 8 | it('should send the status code and message as body', function (done) { 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.sendStatus(201); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect(201, 'Created', done); 18 | }) 19 | 20 | it('should work with unknown code', function (done) { 21 | var app = express(); 22 | 23 | app.use(function(req, res){ 24 | res.sendStatus(599); 25 | }); 26 | 27 | request(app) 28 | .get('/') 29 | .expect(599, '599', done); 30 | }) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/support/tmpl.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | var variableRegExp = /\$([0-9a-zA-Z\.]+)/g; 4 | 5 | module.exports = function renderFile(fileName, options, callback) { 6 | function onReadFile(err, str) { 7 | if (err) { 8 | callback(err); 9 | return; 10 | } 11 | 12 | try { 13 | str = str.replace(variableRegExp, generateVariableLookup(options)); 14 | } catch (e) { 15 | err = e; 16 | } 17 | 18 | callback(err, str); 19 | } 20 | 21 | fs.readFile(fileName, 'utf8', onReadFile); 22 | }; 23 | 24 | function generateVariableLookup(data) { 25 | return function variableLookup(str, path) { 26 | var parts = path.split('.'); 27 | var value = data; 28 | 29 | for (var i = 0; i < parts.length; i++) { 30 | value = value[parts[i]]; 31 | } 32 | 33 | return value; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.12" 5 | - "1.8" 6 | - "2.5" 7 | - "3.3" 8 | - "4.8" 9 | - "5.12" 10 | - "6.10" 11 | - "7.6" 12 | matrix: 13 | include: 14 | - node_js: "8.0" 15 | env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" 16 | allow_failures: 17 | # Allow the nightly installs to fail 18 | - env: "NVM_NODEJS_ORG_MIRROR=https://nodejs.org/download/nightly" 19 | sudo: false 20 | cache: 21 | directories: 22 | - node_modules 23 | before_install: 24 | # Remove all non-test dependencies 25 | - "npm rm --save-dev connect-redis" 26 | 27 | # Update Node.js modules 28 | - "test ! -d node_modules || npm prune" 29 | - "test ! -d node_modules || npm rebuild" 30 | script: "npm run-script test-ci" 31 | after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls" 32 | -------------------------------------------------------------------------------- /test/app.all.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('app.all()', function(){ 6 | it('should add a router per method', function(done){ 7 | var app = express(); 8 | 9 | app.all('/tobi', function(req, res){ 10 | res.end(req.method); 11 | }); 12 | 13 | request(app) 14 | .put('/tobi') 15 | .expect('PUT', function(){ 16 | request(app) 17 | .get('/tobi') 18 | .expect('GET', done); 19 | }); 20 | }) 21 | 22 | it('should run the callback for a method just once', function(done){ 23 | var app = express() 24 | , n = 0; 25 | 26 | app.all('/*', function(req, res, next){ 27 | if (n++) return done(new Error('DELETE called several times')); 28 | next(); 29 | }); 30 | 31 | request(app) 32 | .del('/tobi') 33 | .expect(404, done); 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /examples/view-locals/user.js: -------------------------------------------------------------------------------- 1 | module.exports = User; 2 | 3 | // faux model 4 | 5 | function User(name, age, species) { 6 | this.name = name; 7 | this.age = age; 8 | this.species = species; 9 | } 10 | 11 | User.all = function(fn){ 12 | // process.nextTick makes sure this function API 13 | // behaves in an asynchronous manner, like if it 14 | // was a real DB query to read all users. 15 | process.nextTick(function(){ 16 | fn(null, users); 17 | }); 18 | }; 19 | 20 | User.count = function(fn){ 21 | process.nextTick(function(){ 22 | fn(null, users.length); 23 | }); 24 | }; 25 | 26 | // faux database 27 | 28 | var users = []; 29 | 30 | users.push(new User('Tobi', 2, 'ferret')); 31 | users.push(new User('Loki', 1, 'ferret')); 32 | users.push(new User('Jane', 6, 'ferret')); 33 | users.push(new User('Luna', 1, 'cat')); 34 | users.push(new User('Manny', 1, 'cat')); 35 | -------------------------------------------------------------------------------- /test/acceptance/downloads.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/downloads') 3 | , request = require('supertest'); 4 | 5 | describe('downloads', function(){ 6 | describe('GET /', function(){ 7 | it('should have a link to amazing.txt', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect(/href="\/files\/amazing.txt"/, done) 11 | }) 12 | }) 13 | 14 | describe('GET /files/amazing.txt', function(){ 15 | it('should have a download header', function(done){ 16 | request(app) 17 | .get('/files/amazing.txt') 18 | .expect('Content-Disposition', 'attachment; filename="amazing.txt"') 19 | .expect(200, done) 20 | }) 21 | }) 22 | 23 | describe('GET /files/missing.txt', function(){ 24 | it('should respond with 404', function(done){ 25 | request(app) 26 | .get('/files/missing.txt') 27 | .expect(404, done) 28 | }) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/app.locals.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('app', function(){ 6 | describe('.locals(obj)', function(){ 7 | it('should merge locals', function(){ 8 | var app = express(); 9 | Object.keys(app.locals).should.eql(['settings']); 10 | app.locals.user = 'tobi'; 11 | app.locals.age = 2; 12 | Object.keys(app.locals).should.eql(['settings', 'user', 'age']); 13 | app.locals.user.should.equal('tobi'); 14 | app.locals.age.should.equal(2); 15 | }) 16 | }) 17 | 18 | describe('.locals.settings', function(){ 19 | it('should expose app settings', function(){ 20 | var app = express(); 21 | app.set('title', 'House of Manny'); 22 | var obj = app.locals.settings; 23 | obj.should.have.property('env', 'test'); 24 | obj.should.have.property('title', 'House of Manny'); 25 | }) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /examples/session/index.js: -------------------------------------------------------------------------------- 1 | // first: 2 | // $ npm install redis 3 | // $ redis-server 4 | 5 | var express = require('../..'); 6 | var session = require('express-session'); 7 | 8 | var app = express(); 9 | 10 | // Populates req.session 11 | app.use(session({ 12 | resave: false, // don't save session if unmodified 13 | saveUninitialized: false, // don't create session until something stored 14 | secret: 'keyboard cat' 15 | })); 16 | 17 | app.get('/', function(req, res){ 18 | var body = ''; 19 | if (req.session.views) { 20 | ++req.session.views; 21 | } else { 22 | req.session.views = 1; 23 | body += '

    First time visiting? view this page in several browsers :)

    '; 24 | } 25 | res.send(body + '

    viewed ' + req.session.views + ' times.

    '); 26 | }); 27 | 28 | /* istanbul ignore next */ 29 | if (!module.parent) { 30 | app.listen(3000); 31 | console.log('Express started on port 3000'); 32 | } 33 | -------------------------------------------------------------------------------- /test/res.locals.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.locals', function(){ 7 | it('should be empty by default', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | Object.keys(res.locals).should.eql([]); 12 | res.end(); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect(200, done); 18 | }) 19 | }) 20 | 21 | it('should work when mounted', function(done){ 22 | var app = express(); 23 | var blog = express(); 24 | 25 | app.use(blog); 26 | 27 | blog.use(function(req, res, next){ 28 | res.locals.foo = 'bar'; 29 | next(); 30 | }); 31 | 32 | app.use(function(req, res){ 33 | res.locals.foo.should.equal('bar'); 34 | res.end(); 35 | }); 36 | 37 | request(app) 38 | .get('/') 39 | .expect(200, done); 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /test/req.signedCookies.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest') 4 | , cookieParser = require('cookie-parser') 5 | 6 | describe('req', function(){ 7 | describe('.signedCookies', function(){ 8 | it('should return a signed JSON cookie', function(done){ 9 | var app = express(); 10 | 11 | app.use(cookieParser('secret')); 12 | 13 | app.use(function(req, res){ 14 | if ('/set' == req.path) { 15 | res.cookie('obj', { foo: 'bar' }, { signed: true }); 16 | res.end(); 17 | } else { 18 | res.send(req.signedCookies); 19 | } 20 | }); 21 | 22 | request(app) 23 | .get('/set') 24 | .end(function(err, res){ 25 | if (err) return done(err); 26 | var cookie = res.header['set-cookie']; 27 | 28 | request(app) 29 | .get('/') 30 | .set('Cookie', cookie) 31 | .expect(200, { obj: { foo: 'bar' } }, done) 32 | }); 33 | }) 34 | }) 35 | }) 36 | 37 | -------------------------------------------------------------------------------- /test/req.acceptsEncoding.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.acceptsEncoding', function(){ 7 | it('should be true if encoding accepted', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | req.acceptsEncoding('gzip').should.be.ok; 12 | req.acceptsEncoding('deflate').should.be.ok; 13 | res.end(); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .set('Accept-Encoding', ' gzip, deflate') 19 | .expect(200, done); 20 | }) 21 | 22 | it('should be false if encoding not accepted', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | req.acceptsEncoding('bogus').should.not.be.ok; 27 | res.end(); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('Accept-Encoding', ' gzip, deflate') 33 | .expect(200, done); 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /test/req.acceptsEncodings.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.acceptsEncodingss', function(){ 7 | it('should be true if encoding accepted', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | req.acceptsEncodings('gzip').should.be.ok; 12 | req.acceptsEncodings('deflate').should.be.ok; 13 | res.end(); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .set('Accept-Encoding', ' gzip, deflate') 19 | .expect(200, done); 20 | }) 21 | 22 | it('should be false if encoding not accepted', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | req.acceptsEncodings('bogus').should.not.be.ok; 27 | res.end(); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('Accept-Encoding', ' gzip, deflate') 33 | .expect(200, done); 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /test/res.clearCookie.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.clearCookie(name)', function(){ 7 | it('should set a cookie passed expiry', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | res.clearCookie('sid').end(); 12 | }); 13 | 14 | request(app) 15 | .get('/') 16 | .expect('Set-Cookie', 'sid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT') 17 | .expect(200, done) 18 | }) 19 | }) 20 | 21 | describe('.clearCookie(name, options)', function(){ 22 | it('should set the given params', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | res.clearCookie('sid', { path: '/admin' }).end(); 27 | }); 28 | 29 | request(app) 30 | .get('/') 31 | .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') 32 | .expect(200, done) 33 | }) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var db = require('../../db'); 6 | 7 | exports.engine = 'hbs'; 8 | 9 | exports.before = function(req, res, next){ 10 | var id = req.params.user_id; 11 | if (!id) return next(); 12 | // pretend to query a database... 13 | process.nextTick(function(){ 14 | req.user = db.users[id]; 15 | // cant find that user 16 | if (!req.user) return next('route'); 17 | // found it, move on to the routes 18 | next(); 19 | }); 20 | }; 21 | 22 | exports.list = function(req, res, next){ 23 | res.render('list', { users: db.users }); 24 | }; 25 | 26 | exports.edit = function(req, res, next){ 27 | res.render('edit', { user: req.user }); 28 | }; 29 | 30 | exports.show = function(req, res, next){ 31 | res.render('show', { user: req.user }); 32 | }; 33 | 34 | exports.update = function(req, res, next){ 35 | var body = req.body; 36 | req.user.name = body.user.name; 37 | res.message('Information updated!'); 38 | res.redirect('/user/' + req.user.id); 39 | }; 40 | -------------------------------------------------------------------------------- /lib/middleware/init.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express 3 | * Copyright(c) 2009-2013 TJ Holowaychuk 4 | * Copyright(c) 2013 Roman Shtylman 5 | * Copyright(c) 2014-2015 Douglas Christopher Wilson 6 | * MIT Licensed 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Module dependencies. 13 | * @private 14 | */ 15 | 16 | var setPrototypeOf = require('setprototypeof') 17 | 18 | /** 19 | * Initialization middleware, exposing the 20 | * request and response to each other, as well 21 | * as defaulting the X-Powered-By header field. 22 | * 23 | * @param {Function} app 24 | * @return {Function} 25 | * @api private 26 | */ 27 | 28 | exports.init = function(app){ 29 | return function expressInit(req, res, next){ 30 | if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express'); 31 | req.res = res; 32 | res.req = req; 33 | req.next = next; 34 | 35 | setPrototypeOf(req, app.request) 36 | setPrototypeOf(res, app.response) 37 | 38 | res.locals = res.locals || Object.create(null); 39 | 40 | next(); 41 | }; 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /lib/middleware/query.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express 3 | * Copyright(c) 2009-2013 TJ Holowaychuk 4 | * Copyright(c) 2013 Roman Shtylman 5 | * Copyright(c) 2014-2015 Douglas Christopher Wilson 6 | * MIT Licensed 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Module dependencies. 13 | */ 14 | 15 | var parseUrl = require('parseurl'); 16 | var qs = require('qs'); 17 | 18 | /** 19 | * @param {Object} options 20 | * @return {Function} 21 | * @api public 22 | */ 23 | 24 | module.exports = function query(options) { 25 | var opts = Object.create(options || null); 26 | var queryparse = qs.parse; 27 | 28 | if (typeof options === 'function') { 29 | queryparse = options; 30 | opts = undefined; 31 | } 32 | 33 | if (opts !== undefined && opts.allowPrototypes === undefined) { 34 | // back-compat for qs module 35 | opts.allowPrototypes = true; 36 | } 37 | 38 | return function query(req, res, next){ 39 | if (!req.query) { 40 | var val = parseUrl(req).query; 41 | req.query = queryparse(val, opts); 42 | } 43 | 44 | next(); 45 | }; 46 | }; 47 | -------------------------------------------------------------------------------- /test/app.response.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('app', function(){ 6 | describe('.response', function(){ 7 | it('should extend the response prototype', function(done){ 8 | var app = express(); 9 | 10 | app.response.shout = function(str){ 11 | this.send(str.toUpperCase()); 12 | }; 13 | 14 | app.use(function(req, res){ 15 | res.shout('hey'); 16 | }); 17 | 18 | request(app) 19 | .get('/') 20 | .expect('HEY', done); 21 | }) 22 | 23 | it('should not be influenced by other app protos', function(done){ 24 | var app = express() 25 | , app2 = express(); 26 | 27 | app.response.shout = function(str){ 28 | this.send(str.toUpperCase()); 29 | }; 30 | 31 | app2.response.shout = function(str){ 32 | this.send(str); 33 | }; 34 | 35 | app.use(function(req, res){ 36 | res.shout('hey'); 37 | }); 38 | 39 | request(app) 40 | .get('/') 41 | .expect('HEY', done); 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /examples/session/redis.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../..'); 6 | var logger = require('morgan'); 7 | var session = require('express-session'); 8 | 9 | // pass the express to the connect redis module 10 | // allowing it to inherit from session.Store 11 | var RedisStore = require('connect-redis')(session); 12 | 13 | var app = express(); 14 | 15 | app.use(logger('dev')); 16 | 17 | // Populates req.session 18 | app.use(session({ 19 | resave: false, // don't save session if unmodified 20 | saveUninitialized: false, // don't create session until something stored 21 | secret: 'keyboard cat', 22 | store: new RedisStore 23 | })); 24 | 25 | app.get('/', function(req, res){ 26 | var body = ''; 27 | if (req.session.views) { 28 | ++req.session.views; 29 | } else { 30 | req.session.views = 1; 31 | body += '

    First time visiting? view this page in several browsers :)

    '; 32 | } 33 | res.send(body + '

    viewed ' + req.session.views + ' times.

    '); 34 | }); 35 | 36 | app.listen(3000); 37 | console.log('Express app started on port 3000'); 38 | -------------------------------------------------------------------------------- /test/middleware.basic.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../'); 3 | var request = require('supertest'); 4 | 5 | describe('middleware', function(){ 6 | describe('.next()', function(){ 7 | it('should behave like connect', function(done){ 8 | var app = express() 9 | , calls = []; 10 | 11 | app.use(function(req, res, next){ 12 | calls.push('one'); 13 | next(); 14 | }); 15 | 16 | app.use(function(req, res, next){ 17 | calls.push('two'); 18 | next(); 19 | }); 20 | 21 | app.use(function(req, res){ 22 | var buf = ''; 23 | res.setHeader('Content-Type', 'application/json'); 24 | req.setEncoding('utf8'); 25 | req.on('data', function(chunk){ buf += chunk }); 26 | req.on('end', function(){ 27 | res.end(buf); 28 | }); 29 | }); 30 | 31 | request(app.listen()) 32 | .get('/') 33 | .set('Content-Type', 'application/json') 34 | .send('{"foo":"bar"}') 35 | .expect('Content-Type', 'application/json') 36 | .expect(200, '{"foo":"bar"}', done) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /test/acceptance/cookie-sessions.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/cookie-sessions') 3 | var request = require('supertest') 4 | 5 | describe('cookie-sessions', function () { 6 | describe('GET /', function () { 7 | it('should display no views', function (done) { 8 | request(app) 9 | .get('/') 10 | .expect(200, 'viewed 0 times\n', done) 11 | }) 12 | 13 | it('should set a session cookie', function (done) { 14 | request(app) 15 | .get('/') 16 | .expect('Set-Cookie', /express:sess=/) 17 | .expect(200, done) 18 | }) 19 | 20 | it('should display 1 view on revisit', function (done) { 21 | request(app) 22 | .get('/') 23 | .expect(200, 'viewed 0 times\n', function (err, res) { 24 | if (err) return done(err) 25 | request(app) 26 | .get('/') 27 | .set('Cookie', getCookies(res)) 28 | .expect(200, 'viewed 1 times\n', done) 29 | }) 30 | }) 31 | }) 32 | }) 33 | 34 | function getCookies(res) { 35 | return res.headers['set-cookie'].map(function (val) { 36 | return val.split(';')[0] 37 | }).join('; '); 38 | } 39 | -------------------------------------------------------------------------------- /examples/downloads/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | var path = require('path'); 7 | var app = module.exports = express(); 8 | 9 | app.get('/', function(req, res){ 10 | res.send(''); 15 | }); 16 | 17 | // /files/* is accessed via req.params[0] 18 | // but here we name it :file 19 | app.get('/files/:file(*)', function(req, res, next){ 20 | var filePath = path.join(__dirname, 'files', req.params.file); 21 | 22 | res.download(filePath, function (err) { 23 | if (!err) return; // file sent 24 | if (err && err.status !== 404) return next(err); // non-404 error 25 | // file for download not found 26 | res.statusCode = 404; 27 | res.send('Cant find that file, sorry!'); 28 | }); 29 | }); 30 | 31 | /* istanbul ignore next */ 32 | if (!module.parent) { 33 | app.listen(3000); 34 | console.log('Express started on port 3000'); 35 | } 36 | -------------------------------------------------------------------------------- /examples/route-separation/user.js: -------------------------------------------------------------------------------- 1 | // Fake user database 2 | 3 | var users = [ 4 | { name: 'TJ', email: 'tj@vision-media.ca' }, 5 | { name: 'Tobi', email: 'tobi@vision-media.ca' } 6 | ]; 7 | 8 | exports.list = function(req, res){ 9 | res.render('users', { title: 'Users', users: users }); 10 | }; 11 | 12 | exports.load = function(req, res, next){ 13 | var id = req.params.id; 14 | req.user = users[id]; 15 | if (req.user) { 16 | next(); 17 | } else { 18 | var err = new Error('cannot find user ' + id); 19 | err.status = 404; 20 | next(err); 21 | } 22 | }; 23 | 24 | exports.view = function(req, res){ 25 | res.render('users/view', { 26 | title: 'Viewing user ' + req.user.name, 27 | user: req.user 28 | }); 29 | }; 30 | 31 | exports.edit = function(req, res){ 32 | res.render('users/edit', { 33 | title: 'Editing user ' + req.user.name, 34 | user: req.user 35 | }); 36 | }; 37 | 38 | exports.update = function(req, res){ 39 | // Normally you would handle all kinds of 40 | // validation and save back to the db 41 | var user = req.body.user; 42 | req.user.name = user.name; 43 | req.user.email = user.email; 44 | res.redirect('back'); 45 | }; 46 | -------------------------------------------------------------------------------- /examples/markdown/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var escapeHtml = require('escape-html'); 6 | var express = require('../..'); 7 | var fs = require('fs'); 8 | var marked = require('marked'); 9 | var path = require('path'); 10 | 11 | var app = module.exports = express(); 12 | 13 | // register .md as an engine in express view system 14 | 15 | app.engine('md', function(path, options, fn){ 16 | fs.readFile(path, 'utf8', function(err, str){ 17 | if (err) return fn(err); 18 | var html = marked.parse(str).replace(/\{([^}]+)\}/g, function(_, name){ 19 | return escapeHtml(options[name] || ''); 20 | }); 21 | fn(null, html); 22 | }); 23 | }); 24 | 25 | app.set('views', path.join(__dirname, 'views')); 26 | 27 | // make it the default so we dont need .md 28 | app.set('view engine', 'md'); 29 | 30 | app.get('/', function(req, res){ 31 | res.render('index', { title: 'Markdown Example' }); 32 | }); 33 | 34 | app.get('/fail', function(req, res){ 35 | res.render('missing', { title: 'Markdown Example' }); 36 | }); 37 | 38 | /* istanbul ignore next */ 39 | if (!module.parent) { 40 | app.listen(3000); 41 | console.log('Express started on port 3000'); 42 | } 43 | -------------------------------------------------------------------------------- /examples/content-negotiation/index.js: -------------------------------------------------------------------------------- 1 | var express = require('../../'); 2 | var app = module.exports = express(); 3 | var users = require('./db'); 4 | 5 | // so either you can deal with different types of formatting 6 | // for expected response in index.js 7 | app.get('/', function(req, res){ 8 | res.format({ 9 | html: function(){ 10 | res.send('
      ' + users.map(function(user){ 11 | return '
    • ' + user.name + '
    • '; 12 | }).join('') + '
    '); 13 | }, 14 | 15 | text: function(){ 16 | res.send(users.map(function(user){ 17 | return ' - ' + user.name + '\n'; 18 | }).join('')); 19 | }, 20 | 21 | json: function(){ 22 | res.json(users); 23 | } 24 | }); 25 | }); 26 | 27 | // or you could write a tiny middleware like 28 | // this to add a layer of abstraction 29 | // and make things a bit more declarative: 30 | 31 | function format(path) { 32 | var obj = require(path); 33 | return function(req, res){ 34 | res.format(obj); 35 | }; 36 | } 37 | 38 | app.get('/users', format('./users')); 39 | 40 | /* istanbul ignore next */ 41 | if (!module.parent) { 42 | app.listen(3000); 43 | console.log('Express started on port 3000'); 44 | } 45 | -------------------------------------------------------------------------------- /examples/online/index.js: -------------------------------------------------------------------------------- 1 | // first: 2 | // $ npm install redis online 3 | // $ redis-server 4 | 5 | /** 6 | * Module dependencies. 7 | */ 8 | 9 | var express = require('../..'); 10 | var online = require('online'); 11 | var redis = require('redis'); 12 | var db = redis.createClient(); 13 | 14 | // online 15 | 16 | online = online(db); 17 | 18 | // app 19 | 20 | var app = express(); 21 | 22 | // activity tracking, in this case using 23 | // the UA string, you would use req.user.id etc 24 | 25 | app.use(function(req, res, next){ 26 | // fire-and-forget 27 | online.add(req.headers['user-agent']); 28 | next(); 29 | }); 30 | 31 | /** 32 | * List helper. 33 | */ 34 | 35 | function list(ids) { 36 | return '
      ' + ids.map(function(id){ 37 | return '
    • ' + id + '
    • '; 38 | }).join('') + '
    '; 39 | } 40 | 41 | /** 42 | * GET users online. 43 | */ 44 | 45 | app.get('/', function(req, res, next){ 46 | online.last(5, function(err, ids){ 47 | if (err) return next(err); 48 | res.send('

    Users online: ' + ids.length + '

    ' + list(ids)); 49 | }); 50 | }); 51 | 52 | /* istanbul ignore next */ 53 | if (!module.parent) { 54 | app.listen(3000); 55 | console.log('Express started on port 3000'); 56 | } 57 | -------------------------------------------------------------------------------- /test/acceptance/route-map.js: -------------------------------------------------------------------------------- 1 | 2 | var request = require('supertest') 3 | , app = require('../../examples/route-map'); 4 | 5 | describe('route-map', function(){ 6 | describe('GET /users', function(){ 7 | it('should respond with users', function(done){ 8 | request(app) 9 | .get('/users') 10 | .expect('user list', done); 11 | }) 12 | }) 13 | 14 | describe('DELETE /users', function(){ 15 | it('should delete users', function(done){ 16 | request(app) 17 | .del('/users') 18 | .expect('delete users', done); 19 | }) 20 | }) 21 | 22 | describe('GET /users/:id', function(){ 23 | it('should get a user', function(done){ 24 | request(app) 25 | .get('/users/12') 26 | .expect('user 12', done); 27 | }) 28 | }) 29 | 30 | describe('GET /users/:id/pets', function(){ 31 | it('should get a users pets', function(done){ 32 | request(app) 33 | .get('/users/12/pets') 34 | .expect('user 12\'s pets', done); 35 | }) 36 | }) 37 | 38 | describe('GET /users/:id/pets/:pid', function(){ 39 | it('should get a users pet', function(done){ 40 | request(app) 41 | .del('/users/12/pets/2') 42 | .expect('delete 12\'s pet 2', done); 43 | }) 44 | }) 45 | }) -------------------------------------------------------------------------------- /examples/vhost/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../..'); 6 | var logger = require('morgan'); 7 | var vhost = require('vhost'); 8 | 9 | /* 10 | edit /etc/hosts: 11 | 12 | 127.0.0.1 foo.example.com 13 | 127.0.0.1 bar.example.com 14 | 127.0.0.1 example.com 15 | */ 16 | 17 | // Main server app 18 | 19 | var main = express(); 20 | 21 | if (!module.parent) main.use(logger('dev')); 22 | 23 | main.get('/', function(req, res){ 24 | res.send('Hello from main app!'); 25 | }); 26 | 27 | main.get('/:sub', function(req, res){ 28 | res.send('requested ' + req.params.sub); 29 | }); 30 | 31 | // Redirect app 32 | 33 | var redirect = express(); 34 | 35 | redirect.use(function(req, res){ 36 | if (!module.parent) console.log(req.vhost); 37 | res.redirect('http://example.com:3000/' + req.vhost[0]); 38 | }); 39 | 40 | // Vhost app 41 | 42 | var app = module.exports = express(); 43 | 44 | app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app 45 | app.use(vhost('example.com', main)); // Serves top level domain via Main server app 46 | 47 | /* istanbul ignore next */ 48 | if (!module.parent) { 49 | app.listen(3000); 50 | console.log('Express started on port 3000'); 51 | } 52 | -------------------------------------------------------------------------------- /test/acceptance/params.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/params') 2 | var request = require('supertest') 3 | 4 | describe('params', function(){ 5 | describe('GET /', function(){ 6 | it('should respond with instructions', function(done){ 7 | request(app) 8 | .get('/') 9 | .expect(/Visit/,done) 10 | }) 11 | }) 12 | 13 | describe('GET /user/0', function(){ 14 | it('should respond with a user', function(done){ 15 | request(app) 16 | .get('/user/0') 17 | .expect(/user tj/,done) 18 | }) 19 | }) 20 | 21 | describe('GET /user/9', function(){ 22 | it('should fail to find user', function(done){ 23 | request(app) 24 | .get('/user/9') 25 | .expect(404, /failed to find user/, done) 26 | }) 27 | }) 28 | 29 | describe('GET /users/0-2', function(){ 30 | it('should respond with three users', function(done){ 31 | request(app) 32 | .get('/users/0-2') 33 | .expect(/users tj, tobi/,done) 34 | }) 35 | }) 36 | 37 | describe('GET /users/foo-bar', function(){ 38 | it('should fail integer parsing', function(done){ 39 | request(app) 40 | .get('/users/foo-bar') 41 | .expect(400, /failed to parseInt foo/, done) 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /test/res.type.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.type(str)', function(){ 7 | it('should set the Content-Type based on a filename', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | res.type('foo.js').end('var name = "tj";'); 12 | }); 13 | 14 | request(app) 15 | .get('/') 16 | .expect('Content-Type', 'application/javascript', done); 17 | }) 18 | 19 | it('should default to application/octet-stream', function(done){ 20 | var app = express(); 21 | 22 | app.use(function(req, res){ 23 | res.type('rawr').end('var name = "tj";'); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .expect('Content-Type', 'application/octet-stream', done); 29 | }) 30 | 31 | it('should set the Content-Type with type/subtype', function(done){ 32 | var app = express(); 33 | 34 | app.use(function(req, res){ 35 | res.type('application/vnd.amazon.ebook') 36 | .end('var name = "tj";'); 37 | }); 38 | 39 | request(app) 40 | .get('/') 41 | .expect('Content-Type', 'application/vnd.amazon.ebook', done); 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /test/res.location.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.location(url)', function(){ 7 | it('should set the header', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | res.location('http://google.com').end(); 12 | }); 13 | 14 | request(app) 15 | .get('/') 16 | .expect('Location', 'http://google.com') 17 | .expect(200, done) 18 | }) 19 | 20 | it('should encode "url"', function (done) { 21 | var app = express() 22 | 23 | app.use(function (req, res) { 24 | res.location('https://google.com?q=\u2603 §10').end() 25 | }) 26 | 27 | request(app) 28 | .get('/') 29 | .expect('Location', 'https://google.com?q=%E2%98%83%20%C2%A710') 30 | .expect(200, done) 31 | }) 32 | 33 | it('should not touch already-encoded sequences in "url"', function (done) { 34 | var app = express() 35 | 36 | app.use(function (req, res) { 37 | res.location('https://google.com?q=%A710').end() 38 | }) 39 | 40 | request(app) 41 | .get('/') 42 | .expect('Location', 'https://google.com?q=%A710') 43 | .expect(200, done) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /examples/view-constructor/github-view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var http = require('http'); 6 | var path = require('path'); 7 | var extname = path.extname; 8 | 9 | /** 10 | * Expose `GithubView`. 11 | */ 12 | 13 | module.exports = GithubView; 14 | 15 | /** 16 | * Custom view that fetches and renders 17 | * remove github templates. You could 18 | * render templates from a database etc. 19 | */ 20 | 21 | function GithubView(name, options){ 22 | this.name = name; 23 | options = options || {}; 24 | this.engine = options.engines[extname(name)]; 25 | // "root" is the app.set('views') setting, however 26 | // in your own implementation you could ignore this 27 | this.path = '/' + options.root + '/master/' + name; 28 | } 29 | 30 | /** 31 | * Render the view. 32 | */ 33 | 34 | GithubView.prototype.render = function(options, fn){ 35 | var self = this; 36 | var opts = { 37 | host: 'raw.githubusercontent.com', 38 | port: 443, 39 | path: this.path, 40 | method: 'GET' 41 | }; 42 | 43 | https.request(opts, function(res) { 44 | var buf = ''; 45 | res.setEncoding('utf8'); 46 | res.on('data', function(str){ buf += str }); 47 | res.on('end', function(){ 48 | self.engine(buf, options, fn); 49 | }); 50 | }).end(); 51 | }; 52 | -------------------------------------------------------------------------------- /test/req.stale.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.stale', function(){ 7 | it('should return false when the resource is not modified', function(done){ 8 | var app = express(); 9 | var etag = '"12345"'; 10 | 11 | app.use(function(req, res){ 12 | res.set('ETag', etag); 13 | res.send(req.stale); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .set('If-None-Match', etag) 19 | .expect(304, done); 20 | }) 21 | 22 | it('should return true when the resource is modified', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | res.set('ETag', '"123"'); 27 | res.send(req.stale); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('If-None-Match', '"12345"') 33 | .expect(200, 'true', done); 34 | }) 35 | 36 | it('should return true without response headers', function(done){ 37 | var app = express(); 38 | 39 | app.disable('x-powered-by') 40 | app.use(function(req, res){ 41 | res.send(req.stale); 42 | }); 43 | 44 | request(app) 45 | .get('/') 46 | .expect(200, 'true', done); 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/req.fresh.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.fresh', function(){ 7 | it('should return true when the resource is not modified', function(done){ 8 | var app = express(); 9 | var etag = '"12345"'; 10 | 11 | app.use(function(req, res){ 12 | res.set('ETag', etag); 13 | res.send(req.fresh); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .set('If-None-Match', etag) 19 | .expect(304, done); 20 | }) 21 | 22 | it('should return false when the resource is modified', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | res.set('ETag', '"123"'); 27 | res.send(req.fresh); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('If-None-Match', '"12345"') 33 | .expect(200, 'false', done); 34 | }) 35 | 36 | it('should return false without response headers', function(done){ 37 | var app = express(); 38 | 39 | app.disable('x-powered-by') 40 | app.use(function(req, res){ 41 | res.send(req.fresh); 42 | }); 43 | 44 | request(app) 45 | .get('/') 46 | .expect(200, 'false', done); 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2009-2014 TJ Holowaychuk 4 | Copyright (c) 2013-2014 Roman Shtylman 5 | Copyright (c) 2014-2015 Douglas Christopher Wilson 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | 'Software'), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /examples/search/index.js: -------------------------------------------------------------------------------- 1 | // first: 2 | // $ npm install redis 3 | // $ redis-server 4 | 5 | /** 6 | * Module dependencies. 7 | */ 8 | 9 | var express = require('../..'); 10 | var path = require('path'); 11 | var redis = require('redis'); 12 | 13 | var db = redis.createClient(); 14 | 15 | // npm install redis 16 | 17 | var app = express(); 18 | 19 | app.use(express.static(path.join(__dirname, 'public'))); 20 | 21 | // populate search 22 | 23 | db.sadd('ferret', 'tobi'); 24 | db.sadd('ferret', 'loki'); 25 | db.sadd('ferret', 'jane'); 26 | db.sadd('cat', 'manny'); 27 | db.sadd('cat', 'luna'); 28 | 29 | /** 30 | * GET search for :query. 31 | */ 32 | 33 | app.get('/search/:query?', function(req, res){ 34 | var query = req.params.query; 35 | db.smembers(query, function(err, vals){ 36 | if (err) return res.send(500); 37 | res.send(vals); 38 | }); 39 | }); 40 | 41 | /** 42 | * GET client javascript. Here we use sendFile() 43 | * because serving __dirname with the static() middleware 44 | * would also mean serving our server "index.js" and the "search.jade" 45 | * template. 46 | */ 47 | 48 | app.get('/client.js', function(req, res){ 49 | res.sendFile(path.join(__dirname, 'client.js')); 50 | }); 51 | 52 | /* istanbul ignore next */ 53 | if (!module.parent) { 54 | app.listen(3000); 55 | console.log('Express started on port 3000'); 56 | } 57 | -------------------------------------------------------------------------------- /test/acceptance/multi-router.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/multi-router') 2 | var request = require('supertest') 3 | 4 | describe('multi-router', function(){ 5 | describe('GET /',function(){ 6 | it('should respond with root handler', function(done){ 7 | request(app) 8 | .get('/') 9 | .expect(200, 'Hello form root route.', done) 10 | }) 11 | }) 12 | 13 | describe('GET /api/v1/',function(){ 14 | it('should respond with APIv1 root handler', function(done){ 15 | request(app) 16 | .get('/api/v1/') 17 | .expect(200, 'Hello from APIv1 root route.', done) 18 | }) 19 | }) 20 | 21 | describe('GET /api/v1/users',function(){ 22 | it('should respond with users from APIv1', function(done){ 23 | request(app) 24 | .get('/api/v1/users') 25 | .expect(200, 'List of APIv1 users.', done) 26 | }) 27 | }) 28 | 29 | describe('GET /api/v2/',function(){ 30 | it('should respond with APIv2 root handler', function(done){ 31 | request(app) 32 | .get('/api/v2/') 33 | .expect(200, 'Hello from APIv2 root route.', done) 34 | }) 35 | }) 36 | 37 | describe('GET /api/v2/users',function(){ 38 | it('should respond with users from APIv2', function(done){ 39 | request(app) 40 | .get('/api/v2/users') 41 | .expect(200, 'List of APIv2 users.', done) 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /test/acceptance/vhost.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/vhost') 2 | var request = require('supertest') 3 | 4 | describe('vhost', function(){ 5 | describe('example.com', function(){ 6 | describe('GET /', function(){ 7 | it('should say hello', function(done){ 8 | request(app) 9 | .get('/') 10 | .set('Host', 'example.com') 11 | .expect(200, /hello/i, done) 12 | }) 13 | }) 14 | 15 | describe('GET /foo', function(){ 16 | it('should say foo', function(done){ 17 | request(app) 18 | .get('/foo') 19 | .set('Host', 'example.com') 20 | .expect(200, 'requested foo', done) 21 | }) 22 | }) 23 | }) 24 | 25 | describe('foo.example.com', function(){ 26 | describe('GET /', function(){ 27 | it('should redirect to /foo', function(done){ 28 | request(app) 29 | .get('/') 30 | .set('Host', 'foo.example.com') 31 | .expect(302, /Redirecting to http:\/\/example.com:3000\/foo/, done) 32 | }) 33 | }) 34 | }) 35 | 36 | describe('bar.example.com', function(){ 37 | describe('GET /', function(){ 38 | it('should redirect to /bar', function(done){ 39 | request(app) 40 | .get('/') 41 | .set('Host', 'bar.example.com') 42 | .expect(302, /Redirecting to http:\/\/example.com:3000\/bar/, done) 43 | }) 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /examples/view-constructor/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | var http = require('http'); 7 | var GithubView = require('./github-view'); 8 | var md = require('marked').parse; 9 | 10 | var app = module.exports = express(); 11 | 12 | // register .md as an engine in express view system 13 | app.engine('md', function(str, options, fn){ 14 | try { 15 | var html = md(str); 16 | html = html.replace(/\{([^}]+)\}/g, function(_, name){ 17 | return options[name] || ''; 18 | }); 19 | fn(null, html); 20 | } catch(err) { 21 | fn(err); 22 | } 23 | }); 24 | 25 | // pointing to a particular github repo to load files from it 26 | app.set('views', 'expressjs/express'); 27 | 28 | // register a new view constructor 29 | app.set('view', GithubView); 30 | 31 | app.get('/', function(req, res){ 32 | // rendering a view relative to the repo. 33 | // app.locals, res.locals, and locals passed 34 | // work like they normally would 35 | res.render('examples/markdown/views/index.md', { title: 'Example' }); 36 | }); 37 | 38 | app.get('/Readme.md', function(req, res){ 39 | // rendering a view from https://github.com/expressjs/express/blob/master/Readme.md 40 | res.render('Readme.md'); 41 | }); 42 | 43 | /* istanbul ignore next */ 44 | if (!module.parent) { 45 | app.listen(3000); 46 | console.log('Express started on port 3000'); 47 | } 48 | -------------------------------------------------------------------------------- /examples/route-separation/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../..'); 6 | var path = require('path'); 7 | var app = express(); 8 | var logger = require('morgan'); 9 | var cookieParser = require('cookie-parser'); 10 | var bodyParser = require('body-parser'); 11 | var methodOverride = require('method-override'); 12 | var site = require('./site'); 13 | var post = require('./post'); 14 | var user = require('./user'); 15 | 16 | module.exports = app; 17 | 18 | // Config 19 | 20 | app.set('view engine', 'ejs'); 21 | app.set('views', path.join(__dirname, 'views')); 22 | 23 | /* istanbul ignore next */ 24 | if (!module.parent) { 25 | app.use(logger('dev')); 26 | } 27 | 28 | app.use(methodOverride('_method')); 29 | app.use(cookieParser()); 30 | app.use(bodyParser.urlencoded({ extended: true })); 31 | app.use(express.static(path.join(__dirname, 'public'))); 32 | 33 | // General 34 | 35 | app.get('/', site.index); 36 | 37 | // User 38 | 39 | app.get('/users', user.list); 40 | app.all('/user/:id/:op?', user.load); 41 | app.get('/user/:id', user.view); 42 | app.get('/user/:id/view', user.view); 43 | app.get('/user/:id/edit', user.edit); 44 | app.put('/user/:id/edit', user.update); 45 | 46 | // Posts 47 | 48 | app.get('/posts', post.list); 49 | 50 | /* istanbul ignore next */ 51 | if (!module.parent) { 52 | app.listen(3000); 53 | console.log('Express started on port 3000'); 54 | } 55 | -------------------------------------------------------------------------------- /test/app.route.js: -------------------------------------------------------------------------------- 1 | var express = require('../'); 2 | var request = require('supertest'); 3 | 4 | describe('app.route', function(){ 5 | it('should return a new route', function(done){ 6 | var app = express(); 7 | 8 | app.route('/foo') 9 | .get(function(req, res) { 10 | res.send('get'); 11 | }) 12 | .post(function(req, res) { 13 | res.send('post'); 14 | }); 15 | 16 | request(app) 17 | .post('/foo') 18 | .expect('post', done); 19 | }); 20 | 21 | it('should all .VERB after .all', function(done){ 22 | var app = express(); 23 | 24 | app.route('/foo') 25 | .all(function(req, res, next) { 26 | next(); 27 | }) 28 | .get(function(req, res) { 29 | res.send('get'); 30 | }) 31 | .post(function(req, res) { 32 | res.send('post'); 33 | }); 34 | 35 | request(app) 36 | .post('/foo') 37 | .expect('post', done); 38 | }); 39 | 40 | it('should support dynamic routes', function(done){ 41 | var app = express(); 42 | 43 | app.route('/:foo') 44 | .get(function(req, res) { 45 | res.send(req.params.foo); 46 | }); 47 | 48 | request(app) 49 | .get('/test') 50 | .expect('test', done); 51 | }); 52 | 53 | it('should not error on empty routes', function(done){ 54 | var app = express(); 55 | 56 | app.route('/:foo'); 57 | 58 | request(app) 59 | .get('/test') 60 | .expect(404, done); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /test/req.acceptsCharset.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.acceptsCharset(type)', function(){ 7 | describe('when Accept-Charset is not present', function(){ 8 | it('should return true', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res, next){ 12 | res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('yes', done); 18 | }) 19 | }) 20 | 21 | describe('when Accept-Charset is not present', function(){ 22 | it('should return true when present', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res, next){ 26 | res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); 27 | }); 28 | 29 | request(app) 30 | .get('/') 31 | .set('Accept-Charset', 'foo, bar, utf-8') 32 | .expect('yes', done); 33 | }) 34 | 35 | it('should return false otherwise', function(done){ 36 | var app = express(); 37 | 38 | app.use(function(req, res, next){ 39 | res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); 40 | }); 41 | 42 | request(app) 43 | .get('/') 44 | .set('Accept-Charset', 'foo, bar') 45 | .expect('no', done); 46 | }) 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/req.acceptsCharsets.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.acceptsCharsets(type)', function(){ 7 | describe('when Accept-Charset is not present', function(){ 8 | it('should return true', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res, next){ 12 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('yes', done); 18 | }) 19 | }) 20 | 21 | describe('when Accept-Charset is not present', function(){ 22 | it('should return true when present', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res, next){ 26 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 27 | }); 28 | 29 | request(app) 30 | .get('/') 31 | .set('Accept-Charset', 'foo, bar, utf-8') 32 | .expect('yes', done); 33 | }) 34 | 35 | it('should return false otherwise', function(done){ 36 | var app = express(); 37 | 38 | app.use(function(req, res, next){ 39 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 40 | }); 41 | 42 | request(app) 43 | .get('/') 44 | .set('Accept-Charset', 'foo, bar') 45 | .expect('no', done); 46 | }) 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/res.links.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('..'); 3 | var request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.links(obj)', function(){ 7 | it('should set Link header field', function (done) { 8 | var app = express(); 9 | 10 | app.use(function (req, res) { 11 | res.links({ 12 | next: 'http://api.example.com/users?page=2', 13 | last: 'http://api.example.com/users?page=5' 14 | }); 15 | res.end(); 16 | }); 17 | 18 | request(app) 19 | .get('/') 20 | .expect('Link', '; rel="next", ; rel="last"') 21 | .expect(200, done); 22 | }) 23 | 24 | it('should set Link header field for multiple calls', function (done) { 25 | var app = express(); 26 | 27 | app.use(function (req, res) { 28 | res.links({ 29 | next: 'http://api.example.com/users?page=2', 30 | last: 'http://api.example.com/users?page=5' 31 | }); 32 | 33 | res.links({ 34 | prev: 'http://api.example.com/users?page=1' 35 | }); 36 | 37 | res.end(); 38 | }); 39 | 40 | request(app) 41 | .get('/') 42 | .expect('Link', '; rel="next", ; rel="last", ; rel="prev"') 43 | .expect(200, done); 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /examples/cookies/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | var app = module.exports = express(); 7 | var logger = require('morgan'); 8 | var cookieParser = require('cookie-parser'); 9 | var bodyParser = require('body-parser'); 10 | 11 | // custom log format 12 | if ('test' != process.env.NODE_ENV) app.use(logger(':method :url')); 13 | 14 | // parses request cookies, populating 15 | // req.cookies and req.signedCookies 16 | // when the secret is passed, used 17 | // for signing the cookies. 18 | app.use(cookieParser('my secret here')); 19 | 20 | // parses x-www-form-urlencoded 21 | app.use(bodyParser.urlencoded({ extended: false })); 22 | 23 | app.get('/', function(req, res){ 24 | if (req.cookies.remember) { 25 | res.send('Remembered :). Click to forget!.'); 26 | } else { 27 | res.send('

    Check to ' 29 | + '.

    '); 30 | } 31 | }); 32 | 33 | app.get('/forget', function(req, res){ 34 | res.clearCookie('remember'); 35 | res.redirect('back'); 36 | }); 37 | 38 | app.post('/', function(req, res){ 39 | var minute = 60000; 40 | if (req.body.remember) res.cookie('remember', 1, { maxAge: minute }); 41 | res.redirect('back'); 42 | }); 43 | 44 | /* istanbul ignore next */ 45 | if (!module.parent) { 46 | app.listen(3000); 47 | console.log('Express started on port 3000'); 48 | } 49 | -------------------------------------------------------------------------------- /examples/static-files/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../..'); 6 | var logger = require('morgan'); 7 | var path = require('path'); 8 | var app = express(); 9 | 10 | // log requests 11 | app.use(logger('dev')); 12 | 13 | // express on its own has no notion 14 | // of a "file". The express.static() 15 | // middleware checks for a file matching 16 | // the `req.path` within the directory 17 | // that you pass it. In this case "GET /js/app.js" 18 | // will look for "./public/js/app.js". 19 | 20 | app.use(express.static(path.join(__dirname, 'public'))); 21 | 22 | // if you wanted to "prefix" you may use 23 | // the mounting feature of Connect, for example 24 | // "GET /static/js/app.js" instead of "GET /js/app.js". 25 | // The mount-path "/static" is simply removed before 26 | // passing control to the express.static() middleware, 27 | // thus it serves the file correctly by ignoring "/static" 28 | app.use('/static', express.static(path.join(__dirname, 'public'))); 29 | 30 | // if for some reason you want to serve files from 31 | // several directories, you can use express.static() 32 | // multiple times! Here we're passing "./public/css", 33 | // this will allow "GET /style.css" instead of "GET /css/style.css": 34 | app.use(express.static(path.join(__dirname, 'public', 'css'))); 35 | 36 | app.listen(3000); 37 | console.log('listening on port 3000'); 38 | console.log('try:'); 39 | console.log(' GET /hello.txt'); 40 | console.log(' GET /js/app.js'); 41 | console.log(' GET /css/style.css'); 42 | -------------------------------------------------------------------------------- /test/req.acceptsLanguage.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.acceptsLanguage', function(){ 7 | it('should be true if language accepted', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | req.acceptsLanguage('en-us').should.be.ok; 12 | req.acceptsLanguage('en').should.be.ok; 13 | res.end(); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .set('Accept-Language', 'en;q=.5, en-us') 19 | .expect(200, done); 20 | }) 21 | 22 | it('should be false if language not accepted', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | req.acceptsLanguage('es').should.not.be.ok; 27 | res.end(); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('Accept-Language', 'en;q=.5, en-us') 33 | .expect(200, done); 34 | }) 35 | 36 | describe('when Accept-Language is not present', function(){ 37 | it('should always return true', function(done){ 38 | var app = express(); 39 | 40 | app.use(function(req, res){ 41 | req.acceptsLanguage('en').should.be.ok; 42 | req.acceptsLanguage('es').should.be.ok; 43 | req.acceptsLanguage('jp').should.be.ok; 44 | res.end(); 45 | }); 46 | 47 | request(app) 48 | .get('/') 49 | .expect(200, done); 50 | }) 51 | }) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /test/req.acceptsLanguages.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.acceptsLanguages', function(){ 7 | it('should be true if language accepted', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | req.acceptsLanguages('en-us').should.be.ok; 12 | req.acceptsLanguages('en').should.be.ok; 13 | res.end(); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .set('Accept-Language', 'en;q=.5, en-us') 19 | .expect(200, done); 20 | }) 21 | 22 | it('should be false if language not accepted', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | req.acceptsLanguages('es').should.not.be.ok; 27 | res.end(); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('Accept-Language', 'en;q=.5, en-us') 33 | .expect(200, done); 34 | }) 35 | 36 | describe('when Accept-Language is not present', function(){ 37 | it('should always return true', function(done){ 38 | var app = express(); 39 | 40 | app.use(function(req, res){ 41 | req.acceptsLanguages('en').should.be.ok; 42 | req.acceptsLanguages('es').should.be.ok; 43 | req.acceptsLanguages('jp').should.be.ok; 44 | res.end(); 45 | }); 46 | 47 | request(app) 48 | .get('/') 49 | .expect(200, done); 50 | }) 51 | }) 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /examples/ejs/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | var path = require('path'); 7 | 8 | var app = module.exports = express(); 9 | 10 | // Register ejs as .html. If we did 11 | // not call this, we would need to 12 | // name our views foo.ejs instead 13 | // of foo.html. The __express method 14 | // is simply a function that engines 15 | // use to hook into the Express view 16 | // system by default, so if we want 17 | // to change "foo.ejs" to "foo.html" 18 | // we simply pass _any_ function, in this 19 | // case `ejs.__express`. 20 | 21 | app.engine('.html', require('ejs').__express); 22 | 23 | // Optional since express defaults to CWD/views 24 | 25 | app.set('views', path.join(__dirname, 'views')); 26 | 27 | // Path to our public directory 28 | 29 | app.use(express.static(path.join(__dirname + 'public'))); 30 | 31 | // Without this you would need to 32 | // supply the extension to res.render() 33 | // ex: res.render('users.html'). 34 | app.set('view engine', 'html'); 35 | 36 | // Dummy users 37 | var users = [ 38 | { name: 'tobi', email: 'tobi@learnboost.com' }, 39 | { name: 'loki', email: 'loki@learnboost.com' }, 40 | { name: 'jane', email: 'jane@learnboost.com' } 41 | ]; 42 | 43 | app.get('/', function(req, res){ 44 | res.render('users', { 45 | users: users, 46 | title: "EJS example", 47 | header: "Some users" 48 | }); 49 | }); 50 | 51 | /* istanbul ignore next */ 52 | if (!module.parent) { 53 | app.listen(3000); 54 | console.log('Express started on port 3000'); 55 | } 56 | -------------------------------------------------------------------------------- /examples/error/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | var logger = require('morgan'); 7 | var app = module.exports = express(); 8 | var test = app.get('env') == 'test'; 9 | 10 | if (!test) app.use(logger('dev')); 11 | 12 | // error handling middleware have an arity of 4 13 | // instead of the typical (req, res, next), 14 | // otherwise they behave exactly like regular 15 | // middleware, you may have several of them, 16 | // in different orders etc. 17 | 18 | function error(err, req, res, next) { 19 | // log it 20 | if (!test) console.error(err.stack); 21 | 22 | // respond with 500 "Internal Server Error". 23 | res.status(500); 24 | res.send('Internal Server Error'); 25 | } 26 | 27 | app.get('/', function(req, res){ 28 | // Caught and passed down to the errorHandler middleware 29 | throw new Error('something broke!'); 30 | }); 31 | 32 | app.get('/next', function(req, res, next){ 33 | // We can also pass exceptions to next() 34 | // The reason for process.nextTick() is to show that 35 | // next() can be called inside an async operation, 36 | // in real life it can be a DB read or HTTP request. 37 | process.nextTick(function(){ 38 | next(new Error('oh no!')); 39 | }); 40 | }); 41 | 42 | // the error handler is placed after routes 43 | // if it were above it would not receive errors 44 | // from app.get() etc 45 | app.use(error); 46 | 47 | /* istanbul ignore next */ 48 | if (!module.parent) { 49 | app.listen(3000); 50 | console.log('Express started on port 3000'); 51 | } 52 | -------------------------------------------------------------------------------- /test/req.param.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest') 4 | , bodyParser = require('body-parser') 5 | 6 | describe('req', function(){ 7 | describe('.param(name, default)', function(){ 8 | it('should use the default value unless defined', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.end(req.param('name', 'tj')); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('tj', done); 18 | }) 19 | }) 20 | 21 | describe('.param(name)', function(){ 22 | it('should check req.query', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | res.end(req.param('name')); 27 | }); 28 | 29 | request(app) 30 | .get('/?name=tj') 31 | .expect('tj', done); 32 | }) 33 | 34 | it('should check req.body', function(done){ 35 | var app = express(); 36 | 37 | app.use(bodyParser.json()); 38 | 39 | app.use(function(req, res){ 40 | res.end(req.param('name')); 41 | }); 42 | 43 | request(app) 44 | .post('/') 45 | .send({ name: 'tj' }) 46 | .expect('tj', done); 47 | }) 48 | 49 | it('should check req.params', function(done){ 50 | var app = express(); 51 | 52 | app.get('/user/:name', function(req, res){ 53 | res.end(req.param('filter') + req.param('name')); 54 | }); 55 | 56 | request(app) 57 | .get('/user/tj') 58 | .expect('undefinedtj', done); 59 | }) 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /test/acceptance/content-negotiation.js: -------------------------------------------------------------------------------- 1 | 2 | var request = require('supertest') 3 | , app = require('../../examples/content-negotiation'); 4 | 5 | describe('content-negotiation', function(){ 6 | describe('GET /', function(){ 7 | it('should default to text/html', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect(200, '
    • Tobi
    • Loki
    • Jane
    ', done) 11 | }) 12 | 13 | it('should accept to text/plain', function(done){ 14 | request(app) 15 | .get('/') 16 | .set('Accept', 'text/plain') 17 | .expect(200, ' - Tobi\n - Loki\n - Jane\n', done) 18 | }) 19 | 20 | it('should accept to application/json', function(done){ 21 | request(app) 22 | .get('/') 23 | .set('Accept', 'application/json') 24 | .expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done) 25 | }) 26 | }) 27 | 28 | describe('GET /users', function(){ 29 | it('should default to text/html', function(done){ 30 | request(app) 31 | .get('/users') 32 | .expect(200, '
    • Tobi
    • Loki
    • Jane
    ', done) 33 | }) 34 | 35 | it('should accept to text/plain', function(done){ 36 | request(app) 37 | .get('/users') 38 | .set('Accept', 'text/plain') 39 | .expect(200, ' - Tobi\n - Loki\n - Jane\n', done) 40 | }) 41 | 42 | it('should accept to application/json', function(done){ 43 | request(app) 44 | .get('/users') 45 | .set('Accept', 'application/json') 46 | .expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done) 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /examples/route-map/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../lib/express'); 6 | 7 | var verbose = process.env.NODE_ENV != 'test'; 8 | 9 | var app = module.exports = express(); 10 | 11 | app.map = function(a, route){ 12 | route = route || ''; 13 | for (var key in a) { 14 | switch (typeof a[key]) { 15 | // { '/path': { ... }} 16 | case 'object': 17 | app.map(a[key], route + key); 18 | break; 19 | // get: function(){ ... } 20 | case 'function': 21 | if (verbose) console.log('%s %s', key, route); 22 | app[key](route, a[key]); 23 | break; 24 | } 25 | } 26 | }; 27 | 28 | var users = { 29 | list: function(req, res){ 30 | res.send('user list'); 31 | }, 32 | 33 | get: function(req, res){ 34 | res.send('user ' + req.params.uid); 35 | }, 36 | 37 | delete: function(req, res){ 38 | res.send('delete users'); 39 | } 40 | }; 41 | 42 | var pets = { 43 | list: function(req, res){ 44 | res.send('user ' + req.params.uid + '\'s pets'); 45 | }, 46 | 47 | delete: function(req, res){ 48 | res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid); 49 | } 50 | }; 51 | 52 | app.map({ 53 | '/users': { 54 | get: users.list, 55 | delete: users.delete, 56 | '/:uid': { 57 | get: users.get, 58 | '/pets': { 59 | get: pets.list, 60 | '/:pid': { 61 | delete: pets.delete 62 | } 63 | } 64 | } 65 | } 66 | }); 67 | 68 | /* istanbul ignore next */ 69 | if (!module.parent) { 70 | app.listen(3000); 71 | console.log('Express started on port 3000'); 72 | } 73 | -------------------------------------------------------------------------------- /test/req.get.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest') 4 | , assert = require('assert'); 5 | 6 | describe('req', function(){ 7 | describe('.get(field)', function(){ 8 | it('should return the header field value', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | assert(req.get('Something-Else') === undefined); 13 | res.end(req.get('Content-Type')); 14 | }); 15 | 16 | request(app) 17 | .post('/') 18 | .set('Content-Type', 'application/json') 19 | .expect('application/json', done); 20 | }) 21 | 22 | it('should special-case Referer', function(done){ 23 | var app = express(); 24 | 25 | app.use(function(req, res){ 26 | res.end(req.get('Referer')); 27 | }); 28 | 29 | request(app) 30 | .post('/') 31 | .set('Referrer', 'http://foobar.com') 32 | .expect('http://foobar.com', done); 33 | }) 34 | 35 | it('should throw missing header name', function (done) { 36 | var app = express() 37 | 38 | app.use(function (req, res) { 39 | res.end(req.get()) 40 | }) 41 | 42 | request(app) 43 | .get('/') 44 | .expect(500, /TypeError: name argument is required to req.get/, done) 45 | }) 46 | 47 | it('should throw for non-string header name', function (done) { 48 | var app = express() 49 | 50 | app.use(function (req, res) { 51 | res.end(req.get(42)) 52 | }) 53 | 54 | request(app) 55 | .get('/') 56 | .expect(500, /TypeError: name must be a string to req.get/, done) 57 | }) 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /examples/multipart/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../..'); 6 | var multiparty = require('multiparty'); 7 | var format = require('util').format; 8 | 9 | var app = module.exports = express(); 10 | 11 | app.get('/', function(req, res){ 12 | res.send('
    ' 13 | + '

    Title:

    ' 14 | + '

    Image:

    ' 15 | + '

    ' 16 | + '
    '); 17 | }); 18 | 19 | app.post('/', function(req, res, next){ 20 | // create a form to begin parsing 21 | var form = new multiparty.Form(); 22 | var image; 23 | var title; 24 | 25 | form.on('error', next); 26 | form.on('close', function(){ 27 | res.send(format('\nuploaded %s (%d Kb) as %s' 28 | , image.filename 29 | , image.size / 1024 | 0 30 | , title)); 31 | }); 32 | 33 | // listen on field event for title 34 | form.on('field', function(name, val){ 35 | if (name !== 'title') return; 36 | title = val; 37 | }); 38 | 39 | // listen on part event for image file 40 | form.on('part', function(part){ 41 | if (!part.filename) return; 42 | if (part.name !== 'image') return part.resume(); 43 | image = {}; 44 | image.filename = part.filename; 45 | image.size = 0; 46 | part.on('data', function(buf){ 47 | image.size += buf.length; 48 | }); 49 | }); 50 | 51 | 52 | // parse the form 53 | form.parse(req); 54 | }); 55 | 56 | /* istanbul ignore next */ 57 | if (!module.parent) { 58 | app.listen(4000); 59 | console.log('Express started on port 4000'); 60 | } 61 | -------------------------------------------------------------------------------- /test/app.head.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../'); 3 | var request = require('supertest'); 4 | var assert = require('assert'); 5 | 6 | describe('HEAD', function(){ 7 | it('should default to GET', function(done){ 8 | var app = express(); 9 | 10 | app.get('/tobi', function(req, res){ 11 | // send() detects HEAD 12 | res.send('tobi'); 13 | }); 14 | 15 | request(app) 16 | .head('/tobi') 17 | .expect(200, done); 18 | }) 19 | 20 | it('should output the same headers as GET requests', function(done){ 21 | var app = express(); 22 | 23 | app.get('/tobi', function(req, res){ 24 | // send() detects HEAD 25 | res.send('tobi'); 26 | }); 27 | 28 | request(app) 29 | .get('/tobi') 30 | .expect(200, function(err, res){ 31 | if (err) return done(err); 32 | var headers = res.headers; 33 | request(app) 34 | .get('/tobi') 35 | .expect(200, function(err, res){ 36 | if (err) return done(err); 37 | delete headers.date; 38 | delete res.headers.date; 39 | assert.deepEqual(res.headers, headers); 40 | done(); 41 | }); 42 | }); 43 | }) 44 | }) 45 | 46 | describe('app.head()', function(){ 47 | it('should override', function(done){ 48 | var app = express() 49 | , called; 50 | 51 | app.head('/tobi', function(req, res){ 52 | called = true; 53 | res.end(''); 54 | }); 55 | 56 | app.get('/tobi', function(req, res){ 57 | assert(0, 'should not call GET'); 58 | res.send('tobi'); 59 | }); 60 | 61 | request(app) 62 | .head('/tobi') 63 | .expect(200, function(){ 64 | assert(called); 65 | done(); 66 | }); 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /test/app.routes.error.js: -------------------------------------------------------------------------------- 1 | var express = require('../') 2 | , request = require('supertest'); 3 | 4 | describe('app', function(){ 5 | describe('.VERB()', function(){ 6 | it('should not get invoked without error handler on error', function(done) { 7 | var app = express(); 8 | 9 | app.use(function(req, res, next){ 10 | next(new Error('boom!')) 11 | }); 12 | 13 | app.get('/bar', function(req, res){ 14 | res.send('hello, world!'); 15 | }); 16 | 17 | request(app) 18 | .post('/bar') 19 | .expect(500, /Error: boom!/, done); 20 | }); 21 | 22 | it('should only call an error handling routing callback when an error is propagated', function(done){ 23 | var app = express(); 24 | 25 | var a = false; 26 | var b = false; 27 | var c = false; 28 | var d = false; 29 | 30 | app.get('/', function(req, res, next){ 31 | next(new Error('fabricated error')); 32 | }, function(req, res, next) { 33 | a = true; 34 | next(); 35 | }, function(err, req, res, next){ 36 | b = true; 37 | err.message.should.equal('fabricated error'); 38 | next(err); 39 | }, function(err, req, res, next){ 40 | c = true; 41 | err.message.should.equal('fabricated error'); 42 | next(); 43 | }, function(err, req, res, next){ 44 | d = true; 45 | next(); 46 | }, function(req, res){ 47 | a.should.be.false; 48 | b.should.be.true; 49 | c.should.be.true; 50 | d.should.be.false; 51 | res.send(204); 52 | }); 53 | 54 | request(app) 55 | .get('/') 56 | .expect(204, done); 57 | }) 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /examples/params/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | var app = module.exports = express(); 7 | 8 | // Faux database 9 | 10 | var users = [ 11 | { name: 'tj' } 12 | , { name: 'tobi' } 13 | , { name: 'loki' } 14 | , { name: 'jane' } 15 | , { name: 'bandit' } 16 | ]; 17 | 18 | // Create HTTP error 19 | 20 | function createError(status, message) { 21 | var err = new Error(message); 22 | err.status = status; 23 | return err; 24 | } 25 | 26 | // Convert :to and :from to integers 27 | 28 | app.param(['to', 'from'], function(req, res, next, num, name){ 29 | req.params[name] = parseInt(num, 10); 30 | if( isNaN(req.params[name]) ){ 31 | next(createError(400, 'failed to parseInt '+num)); 32 | } else { 33 | next(); 34 | } 35 | }); 36 | 37 | // Load user by id 38 | 39 | app.param('user', function(req, res, next, id){ 40 | if (req.user = users[id]) { 41 | next(); 42 | } else { 43 | next(createError(404, 'failed to find user')); 44 | } 45 | }); 46 | 47 | /** 48 | * GET index. 49 | */ 50 | 51 | app.get('/', function(req, res){ 52 | res.send('Visit /user/0 or /users/0-2'); 53 | }); 54 | 55 | /** 56 | * GET :user. 57 | */ 58 | 59 | app.get('/user/:user', function(req, res, next){ 60 | res.send('user ' + req.user.name); 61 | }); 62 | 63 | /** 64 | * GET users :from - :to. 65 | */ 66 | 67 | app.get('/users/:from-:to', function(req, res, next){ 68 | var from = req.params.from; 69 | var to = req.params.to; 70 | var names = users.map(function(user){ return user.name; }); 71 | res.send('users ' + names.slice(from, to).join(', ')); 72 | }); 73 | 74 | /* istanbul ignore next */ 75 | if (!module.parent) { 76 | app.listen(3000); 77 | console.log('Express started on port 3000'); 78 | } 79 | -------------------------------------------------------------------------------- /test/req.xhr.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.xhr', function(){ 7 | it('should return true when X-Requested-With is xmlhttprequest', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | req.xhr.should.be.true; 12 | res.end(); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .set('X-Requested-With', 'xmlhttprequest') 18 | .expect(200) 19 | .end(function(err, res){ 20 | done(err); 21 | }) 22 | }) 23 | 24 | it('should case-insensitive', function(done){ 25 | var app = express(); 26 | 27 | app.use(function(req, res){ 28 | req.xhr.should.be.true; 29 | res.end(); 30 | }); 31 | 32 | request(app) 33 | .get('/') 34 | .set('X-Requested-With', 'XMLHttpRequest') 35 | .expect(200) 36 | .end(function(err, res){ 37 | done(err); 38 | }) 39 | }) 40 | 41 | it('should return false otherwise', function(done){ 42 | var app = express(); 43 | 44 | app.use(function(req, res){ 45 | req.xhr.should.be.false; 46 | res.end(); 47 | }); 48 | 49 | request(app) 50 | .get('/') 51 | .set('X-Requested-With', 'blahblah') 52 | .expect(200) 53 | .end(function(err, res){ 54 | done(err); 55 | }) 56 | }) 57 | 58 | it('should return false when not present', function(done){ 59 | var app = express(); 60 | 61 | app.use(function(req, res){ 62 | req.xhr.should.be.false; 63 | res.end(); 64 | }); 65 | 66 | request(app) 67 | .get('/') 68 | .expect(200) 69 | .end(function(err, res){ 70 | done(err); 71 | }) 72 | }) 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /test/exports.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../'); 3 | var request = require('supertest'); 4 | var should = require('should'); 5 | 6 | describe('exports', function(){ 7 | it('should expose Router', function(){ 8 | express.Router.should.be.a.Function; 9 | }) 10 | 11 | it('should expose the application prototype', function(){ 12 | express.application.set.should.be.a.Function; 13 | }) 14 | 15 | it('should expose the request prototype', function(){ 16 | express.request.accepts.should.be.a.Function; 17 | }) 18 | 19 | it('should expose the response prototype', function(){ 20 | express.response.send.should.be.a.Function; 21 | }) 22 | 23 | it('should permit modifying the .application prototype', function(){ 24 | express.application.foo = function(){ return 'bar'; }; 25 | express().foo().should.equal('bar'); 26 | }) 27 | 28 | it('should permit modifying the .request prototype', function(done){ 29 | express.request.foo = function(){ return 'bar'; }; 30 | var app = express(); 31 | 32 | app.use(function(req, res, next){ 33 | res.end(req.foo()); 34 | }); 35 | 36 | request(app) 37 | .get('/') 38 | .expect('bar', done); 39 | }) 40 | 41 | it('should permit modifying the .response prototype', function(done){ 42 | express.response.foo = function(){ this.send('bar'); }; 43 | var app = express(); 44 | 45 | app.use(function(req, res, next){ 46 | res.foo(); 47 | }); 48 | 49 | request(app) 50 | .get('/') 51 | .expect('bar', done); 52 | }) 53 | 54 | it('should throw on old middlewares', function(){ 55 | var error; 56 | try { express.bodyParser; } catch (e) { error = e; } 57 | should(error).have.property('message'); 58 | error.message.should.containEql('middleware'); 59 | error.message.should.containEql('bodyParser'); 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /Security.md: -------------------------------------------------------------------------------- 1 | # Security Policies and Procedures 2 | 3 | This document outlines security procedures and general policies for the Express 4 | project. 5 | 6 | * [Reporting a Bug](#reporting-a-bug) 7 | * [Disclosure Policy](#disclosure-policy) 8 | * [Comments on this Policy](#comments-on-this-policy) 9 | 10 | ## Reporting a Bug 11 | 12 | The Express team and community take all security bugs in Express seriously. 13 | Thank you for improving the security of Express. We appreciate your efforts and 14 | responsible disclosure and will make every effort to acknowledge your 15 | contributions. 16 | 17 | Report security bugs by emailing the lead maintainer in the Readme.md file. 18 | 19 | The lead maintainer will acknowledge your email within 48 hours, and will send a 20 | more detailed response within 48 hours indicating the next steps in handling 21 | your report. After the initial reply to your report, the security team will 22 | endeavor to keep you informed of the progress towards a fix and full 23 | announcement, and may ask for additional information or guidance. 24 | 25 | Report security bugs in third-party modules to the person or team maintaining 26 | the module. You can also report a vulnerability through the 27 | [Node Security Project](https://nodesecurity.io/report). 28 | 29 | ## Disclosure Policy 30 | 31 | When the security team receives a security bug report, they will assign it to a 32 | primary handler. This person will coordinate the fix and release process, 33 | involving the following steps: 34 | 35 | * Confirm the problem and determine the affected versions. 36 | * Audit code to find any potential similar problems. 37 | * Prepare fixes for all releases still under maintenance. These fixes will be 38 | released as fast as possible to npm. 39 | 40 | ## Comments on this Policy 41 | 42 | If you have suggestions on how this process could be improved please submit a 43 | pull request. 44 | -------------------------------------------------------------------------------- /Collaborator-Guide.md: -------------------------------------------------------------------------------- 1 | 2 | ## Website Issues 3 | 4 | Open issues for the expressjs.com website in https://github.com/expressjs/expressjs.com. 5 | 6 | ## PRs and Code contributions 7 | 8 | * Tests must pass. 9 | * Follow the [JavaScript Standard Style](http://standardjs.com/). 10 | * If you fix a bug, add a test. 11 | 12 | ## Branches 13 | 14 | Use the `master` branch for bug fixes or minor work that is intended for the 15 | current release stream. 16 | 17 | Use the correspondingly named branch, e.g. `5.0`, for anything intended for 18 | a future release of Express. 19 | 20 | ## Steps for contributing 21 | 22 | 1. [Create an issue](https://github.com/expressjs/express/issues/new) for the 23 | bug you want to fix or the feature that you want to add. 24 | 2. Create your own [fork](https://github.com/expressjs/express) on github, then 25 | checkout your fork. 26 | 3. Write your code in your local copy. It's good practice to create a branch for 27 | each new issue you work on, although not compulsory. 28 | 4. To run the test suite, first install the dependencies by running `npm install`, 29 | then run `npm test`. 30 | 5. If the tests pass, you can commit your changes to your fork and then create 31 | a pull request from there. Make sure to reference your issue from the pull 32 | request comments by including the issue number e.g. `#123`. 33 | 34 | ## Issues which are questions 35 | 36 | We will typically close any vague issues or questions that are specific to some 37 | app you are writing. Please double check the docs and other references before 38 | being trigger happy with posting a question issue. 39 | 40 | Things that will help get your question issue looked at: 41 | 42 | * Full and runnable JS code. 43 | * Clear description of the problem or unexpected behavior. 44 | * Clear description of the expected result. 45 | * Steps you have taken to debug it yourself. 46 | 47 | If you post a question and do not outline the above items or make it easy for 48 | us to understand and reproduce your issue, it will be closed. 49 | -------------------------------------------------------------------------------- /test/req.ips.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.ips', function(){ 7 | describe('when X-Forwarded-For is present', function(){ 8 | describe('when "trust proxy" is enabled', function(){ 9 | it('should return an array of the specified addresses', function(done){ 10 | var app = express(); 11 | 12 | app.enable('trust proxy'); 13 | 14 | app.use(function(req, res, next){ 15 | res.send(req.ips); 16 | }); 17 | 18 | request(app) 19 | .get('/') 20 | .set('X-Forwarded-For', 'client, p1, p2') 21 | .expect('["client","p1","p2"]', done); 22 | }) 23 | 24 | it('should stop at first untrusted', function(done){ 25 | var app = express(); 26 | 27 | app.set('trust proxy', 2); 28 | 29 | app.use(function(req, res, next){ 30 | res.send(req.ips); 31 | }); 32 | 33 | request(app) 34 | .get('/') 35 | .set('X-Forwarded-For', 'client, p1, p2') 36 | .expect('["p1","p2"]', done); 37 | }) 38 | }) 39 | 40 | describe('when "trust proxy" is disabled', function(){ 41 | it('should return an empty array', function(done){ 42 | var app = express(); 43 | 44 | app.use(function(req, res, next){ 45 | res.send(req.ips); 46 | }); 47 | 48 | request(app) 49 | .get('/') 50 | .set('X-Forwarded-For', 'client, p1, p2') 51 | .expect('[]', done); 52 | }) 53 | }) 54 | }) 55 | 56 | describe('when X-Forwarded-For is not present', function(){ 57 | it('should return []', function(done){ 58 | var app = express(); 59 | 60 | app.use(function(req, res, next){ 61 | res.send(req.ips); 62 | }); 63 | 64 | request(app) 65 | .get('/') 66 | .expect('[]', done); 67 | }) 68 | }) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /test/acceptance/cookies.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/cookies') 3 | , request = require('supertest'); 4 | var utils = require('../support/utils'); 5 | 6 | describe('cookies', function(){ 7 | describe('GET /', function(){ 8 | it('should have a form', function(done){ 9 | request(app) 10 | .get('/') 11 | .expect(/
    Examples:<\/h1>/,done) 10 | }) 11 | }) 12 | 13 | describe('GET /users', function(){ 14 | it('should respond with all users', function(done){ 15 | request(app) 16 | .get('/users') 17 | .expect(/^\[{"name":"tj"},{"name":"ciaran"},{"name":"aaron"},{"name":"guillermo"},{"name":"simon"},{"name":"tobi"}\]/,done) 18 | }) 19 | }) 20 | 21 | describe('GET /users/1', function(){ 22 | it('should respond with user 1', function(done){ 23 | request(app) 24 | .get('/users/1') 25 | .expect(/^{"name":"ciaran"}/,done) 26 | }) 27 | }) 28 | 29 | describe('GET /users/9', function(){ 30 | it('should respond with error', function(done){ 31 | request(app) 32 | .get('/users/9') 33 | .expect('{"error":"Cannot find user"}', done) 34 | }) 35 | }) 36 | 37 | describe('GET /users/1..3', function(){ 38 | it('should respond with users 1 through 3', function(done){ 39 | request(app) 40 | .get('/users/1..3') 41 | .expect(/^
    • ciaran<\/li>\n
    • aaron<\/li>\n
    • guillermo<\/li><\/ul>/,done) 42 | }) 43 | }) 44 | 45 | describe('DELETE /users/1', function(){ 46 | it('should delete user 1', function(done){ 47 | request(app) 48 | .del('/users/1') 49 | .expect(/^destroyed/,done) 50 | }) 51 | }) 52 | 53 | describe('DELETE /users/9', function(){ 54 | it('should fail', function(done){ 55 | request(app) 56 | .del('/users/9') 57 | .expect('Cannot find user', done) 58 | }) 59 | }) 60 | 61 | describe('GET /users/1..3.json', function(){ 62 | it('should respond with users 2 and 3 as json', function(done){ 63 | request(app) 64 | .get('/users/1..3.json') 65 | .expect(/^\[null,{"name":"aaron"},{"name":"guillermo"}\]/,done) 66 | }) 67 | }) 68 | }) 69 | -------------------------------------------------------------------------------- /test/res.attachment.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.attachment()', function(){ 7 | it('should Content-Disposition to attachment', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | res.attachment().send('foo'); 12 | }); 13 | 14 | request(app) 15 | .get('/') 16 | .expect('Content-Disposition', 'attachment', done); 17 | }) 18 | }) 19 | 20 | describe('.attachment(filename)', function(){ 21 | it('should add the filename param', function(done){ 22 | var app = express(); 23 | 24 | app.use(function(req, res){ 25 | res.attachment('/path/to/image.png'); 26 | res.send('foo'); 27 | }); 28 | 29 | request(app) 30 | .get('/') 31 | .expect('Content-Disposition', 'attachment; filename="image.png"', done); 32 | }) 33 | 34 | it('should set the Content-Type', function(done){ 35 | var app = express(); 36 | 37 | app.use(function(req, res){ 38 | res.attachment('/path/to/image.png'); 39 | res.send(new Buffer(4)); 40 | }); 41 | 42 | request(app) 43 | .get('/') 44 | .expect('Content-Type', 'image/png', done); 45 | }) 46 | }) 47 | 48 | describe('.attachment(utf8filename)', function(){ 49 | it('should add the filename and filename* params', function(done){ 50 | var app = express(); 51 | 52 | app.use(function(req, res){ 53 | res.attachment('/locales/日本語.txt'); 54 | res.send('japanese'); 55 | }); 56 | 57 | request(app) 58 | .get('/') 59 | .expect('Content-Disposition', 'attachment; filename="???.txt"; filename*=UTF-8\'\'%E6%97%A5%E6%9C%AC%E8%AA%9E.txt') 60 | .expect(200, done); 61 | }) 62 | 63 | it('should set the Content-Type', function(done){ 64 | var app = express(); 65 | 66 | app.use(function(req, res){ 67 | res.attachment('/locales/日本語.txt'); 68 | res.send('japanese'); 69 | }); 70 | 71 | request(app) 72 | .get('/') 73 | .expect('Content-Type', 'text/plain; charset=utf-8', done); 74 | }) 75 | }) 76 | }) 77 | -------------------------------------------------------------------------------- /test/res.vary.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert'); 3 | var express = require('..'); 4 | var request = require('supertest'); 5 | var utils = require('./support/utils'); 6 | 7 | describe('res.vary()', function(){ 8 | describe('with no arguments', function(){ 9 | it('should not set Vary', function (done) { 10 | var app = express(); 11 | 12 | app.use(function (req, res) { 13 | res.vary(); 14 | res.end(); 15 | }); 16 | 17 | request(app) 18 | .get('/') 19 | .expect(utils.shouldNotHaveHeader('Vary')) 20 | .expect(200, done); 21 | }) 22 | }) 23 | 24 | describe('with an empty array', function(){ 25 | it('should not set Vary', function (done) { 26 | var app = express(); 27 | 28 | app.use(function (req, res) { 29 | res.vary([]); 30 | res.end(); 31 | }); 32 | 33 | request(app) 34 | .get('/') 35 | .expect(utils.shouldNotHaveHeader('Vary')) 36 | .expect(200, done); 37 | }) 38 | }) 39 | 40 | describe('with an array', function(){ 41 | it('should set the values', function (done) { 42 | var app = express(); 43 | 44 | app.use(function (req, res) { 45 | res.vary(['Accept', 'Accept-Language', 'Accept-Encoding']); 46 | res.end(); 47 | }); 48 | 49 | request(app) 50 | .get('/') 51 | .expect('Vary', 'Accept, Accept-Language, Accept-Encoding') 52 | .expect(200, done); 53 | }) 54 | }) 55 | 56 | describe('with a string', function(){ 57 | it('should set the value', function (done) { 58 | var app = express(); 59 | 60 | app.use(function (req, res) { 61 | res.vary('Accept'); 62 | res.end(); 63 | }); 64 | 65 | request(app) 66 | .get('/') 67 | .expect('Vary', 'Accept') 68 | .expect(200, done); 69 | }) 70 | }) 71 | 72 | describe('when the value is present', function(){ 73 | it('should not add it again', function (done) { 74 | var app = express(); 75 | 76 | app.use(function (req, res) { 77 | res.vary('Accept'); 78 | res.vary('Accept-Encoding'); 79 | res.vary('Accept-Encoding'); 80 | res.vary('Accept-Encoding'); 81 | res.vary('Accept'); 82 | res.end(); 83 | }); 84 | 85 | request(app) 86 | .get('/') 87 | .expect('Vary', 'Accept, Accept-Encoding') 88 | .expect(200, done); 89 | }) 90 | }) 91 | }) 92 | -------------------------------------------------------------------------------- /test/req.baseUrl.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('..') 3 | var request = require('supertest') 4 | 5 | describe('req', function(){ 6 | describe('.baseUrl', function(){ 7 | it('should be empty for top-level route', function(done){ 8 | var app = express() 9 | 10 | app.get('/:a', function(req, res){ 11 | res.end(req.baseUrl) 12 | }) 13 | 14 | request(app) 15 | .get('/foo') 16 | .expect(200, '', done) 17 | }) 18 | 19 | it('should contain lower path', function(done){ 20 | var app = express() 21 | var sub = express.Router() 22 | 23 | sub.get('/:b', function(req, res){ 24 | res.end(req.baseUrl) 25 | }) 26 | app.use('/:a', sub) 27 | 28 | request(app) 29 | .get('/foo/bar') 30 | .expect(200, '/foo', done); 31 | }) 32 | 33 | it('should contain full lower path', function(done){ 34 | var app = express() 35 | var sub1 = express.Router() 36 | var sub2 = express.Router() 37 | var sub3 = express.Router() 38 | 39 | sub3.get('/:d', function(req, res){ 40 | res.end(req.baseUrl) 41 | }) 42 | sub2.use('/:c', sub3) 43 | sub1.use('/:b', sub2) 44 | app.use('/:a', sub1) 45 | 46 | request(app) 47 | .get('/foo/bar/baz/zed') 48 | .expect(200, '/foo/bar/baz', done); 49 | }) 50 | 51 | it('should travel through routers correctly', function(done){ 52 | var urls = [] 53 | var app = express() 54 | var sub1 = express.Router() 55 | var sub2 = express.Router() 56 | var sub3 = express.Router() 57 | 58 | sub3.get('/:d', function(req, res, next){ 59 | urls.push('0@' + req.baseUrl) 60 | next() 61 | }) 62 | sub2.use('/:c', sub3) 63 | sub1.use('/', function(req, res, next){ 64 | urls.push('1@' + req.baseUrl) 65 | next() 66 | }) 67 | sub1.use('/bar', sub2) 68 | sub1.use('/bar', function(req, res, next){ 69 | urls.push('2@' + req.baseUrl) 70 | next() 71 | }) 72 | app.use(function(req, res, next){ 73 | urls.push('3@' + req.baseUrl) 74 | next() 75 | }) 76 | app.use('/:a', sub1) 77 | app.use(function(req, res, next){ 78 | urls.push('4@' + req.baseUrl) 79 | res.end(urls.join(',')) 80 | }) 81 | 82 | request(app) 83 | .get('/foo/bar/baz/zed') 84 | .expect(200, '3@,1@/foo,0@/foo/bar/baz,2@/foo/bar,4@', done); 85 | }) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /test/app.engine.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , fs = require('fs'); 4 | var path = require('path') 5 | 6 | function render(path, options, fn) { 7 | fs.readFile(path, 'utf8', function(err, str){ 8 | if (err) return fn(err); 9 | str = str.replace('{{user.name}}', options.user.name); 10 | fn(null, str); 11 | }); 12 | } 13 | 14 | describe('app', function(){ 15 | describe('.engine(ext, fn)', function(){ 16 | it('should map a template engine', function(done){ 17 | var app = express(); 18 | 19 | app.set('views', path.join(__dirname, 'fixtures')) 20 | app.engine('.html', render); 21 | app.locals.user = { name: 'tobi' }; 22 | 23 | app.render('user.html', function(err, str){ 24 | if (err) return done(err); 25 | str.should.equal('

      tobi

      '); 26 | done(); 27 | }) 28 | }) 29 | 30 | it('should throw when the callback is missing', function(){ 31 | var app = express(); 32 | (function(){ 33 | app.engine('.html', null); 34 | }).should.throw('callback function required'); 35 | }) 36 | 37 | it('should work without leading "."', function(done){ 38 | var app = express(); 39 | 40 | app.set('views', path.join(__dirname, 'fixtures')) 41 | app.engine('html', render); 42 | app.locals.user = { name: 'tobi' }; 43 | 44 | app.render('user.html', function(err, str){ 45 | if (err) return done(err); 46 | str.should.equal('

      tobi

      '); 47 | done(); 48 | }) 49 | }) 50 | 51 | it('should work "view engine" setting', function(done){ 52 | var app = express(); 53 | 54 | app.set('views', path.join(__dirname, 'fixtures')) 55 | app.engine('html', render); 56 | app.set('view engine', 'html'); 57 | app.locals.user = { name: 'tobi' }; 58 | 59 | app.render('user', function(err, str){ 60 | if (err) return done(err); 61 | str.should.equal('

      tobi

      '); 62 | done(); 63 | }) 64 | }) 65 | 66 | it('should work "view engine" with leading "."', function(done){ 67 | var app = express(); 68 | 69 | app.set('views', path.join(__dirname, 'fixtures')) 70 | app.engine('.html', render); 71 | app.set('view engine', '.html'); 72 | app.locals.user = { name: 'tobi' }; 73 | 74 | app.render('user', function(err, str){ 75 | if (err) return done(err); 76 | str.should.equal('

      tobi

      '); 77 | done(); 78 | }) 79 | }) 80 | }) 81 | }) 82 | -------------------------------------------------------------------------------- /examples/mvc/lib/boot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../..'); 6 | var fs = require('fs'); 7 | var path = require('path'); 8 | 9 | module.exports = function(parent, options){ 10 | var dir = path.join(__dirname, '..', 'controllers'); 11 | var verbose = options.verbose; 12 | fs.readdirSync(dir).forEach(function(name){ 13 | var file = path.join(dir, name) 14 | if (!fs.statSync(file).isDirectory()) return; 15 | verbose && console.log('\n %s:', name); 16 | var obj = require(file); 17 | var name = obj.name || name; 18 | var prefix = obj.prefix || ''; 19 | var app = express(); 20 | var handler; 21 | var method; 22 | var url; 23 | 24 | // allow specifying the view engine 25 | if (obj.engine) app.set('view engine', obj.engine); 26 | app.set('views', path.join(__dirname, '..', 'controllers', name, 'views')); 27 | 28 | // generate routes based 29 | // on the exported methods 30 | for (var key in obj) { 31 | // "reserved" exports 32 | if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue; 33 | // route exports 34 | switch (key) { 35 | case 'show': 36 | method = 'get'; 37 | url = '/' + name + '/:' + name + '_id'; 38 | break; 39 | case 'list': 40 | method = 'get'; 41 | url = '/' + name + 's'; 42 | break; 43 | case 'edit': 44 | method = 'get'; 45 | url = '/' + name + '/:' + name + '_id/edit'; 46 | break; 47 | case 'update': 48 | method = 'put'; 49 | url = '/' + name + '/:' + name + '_id'; 50 | break; 51 | case 'create': 52 | method = 'post'; 53 | url = '/' + name; 54 | break; 55 | case 'index': 56 | method = 'get'; 57 | url = '/'; 58 | break; 59 | default: 60 | /* istanbul ignore next */ 61 | throw new Error('unrecognized route: ' + name + '.' + key); 62 | } 63 | 64 | // setup 65 | handler = obj[key]; 66 | url = prefix + url; 67 | 68 | // before middleware support 69 | if (obj.before) { 70 | app[method](url, obj.before, handler); 71 | verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), url, key); 72 | } else { 73 | app[method](url, handler); 74 | verbose && console.log(' %s %s -> %s', method.toUpperCase(), url, key); 75 | } 76 | } 77 | 78 | // mount the app 79 | parent.use(app); 80 | }); 81 | }; 82 | -------------------------------------------------------------------------------- /examples/resource/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | 7 | var app = module.exports = express(); 8 | 9 | // Ad-hoc example resource method 10 | 11 | app.resource = function(path, obj) { 12 | this.get(path, obj.index); 13 | this.get(path + '/:a..:b.:format?', function(req, res){ 14 | var a = parseInt(req.params.a, 10); 15 | var b = parseInt(req.params.b, 10); 16 | var format = req.params.format; 17 | obj.range(req, res, a, b, format); 18 | }); 19 | this.get(path + '/:id', obj.show); 20 | this.delete(path + '/:id', function(req, res){ 21 | var id = parseInt(req.params.id, 10); 22 | obj.destroy(req, res, id); 23 | }); 24 | }; 25 | 26 | // Fake records 27 | 28 | var users = [ 29 | { name: 'tj' } 30 | , { name: 'ciaran' } 31 | , { name: 'aaron' } 32 | , { name: 'guillermo' } 33 | , { name: 'simon' } 34 | , { name: 'tobi' } 35 | ]; 36 | 37 | // Fake controller. 38 | 39 | var User = { 40 | index: function(req, res){ 41 | res.send(users); 42 | }, 43 | show: function(req, res){ 44 | res.send(users[req.params.id] || { error: 'Cannot find user' }); 45 | }, 46 | destroy: function(req, res, id){ 47 | var destroyed = id in users; 48 | delete users[id]; 49 | res.send(destroyed ? 'destroyed' : 'Cannot find user'); 50 | }, 51 | range: function(req, res, a, b, format){ 52 | var range = users.slice(a, b + 1); 53 | switch (format) { 54 | case 'json': 55 | res.send(range); 56 | break; 57 | case 'html': 58 | default: 59 | var html = '
        ' + range.map(function(user){ 60 | return '
      • ' + user.name + '
      • '; 61 | }).join('\n') + '
      '; 62 | res.send(html); 63 | break; 64 | } 65 | } 66 | }; 67 | 68 | // curl http://localhost:3000/users -- responds with all users 69 | // curl http://localhost:3000/users/1 -- responds with user 1 70 | // curl http://localhost:3000/users/4 -- responds with error 71 | // curl http://localhost:3000/users/1..3 -- responds with several users 72 | // curl -X DELETE http://localhost:3000/users/1 -- deletes the user 73 | 74 | app.resource('/users', User); 75 | 76 | app.get('/', function(req, res){ 77 | res.send([ 78 | '

      Examples:

        ' 79 | , '
      • GET /users
      • ' 80 | , '
      • GET /users/1
      • ' 81 | , '
      • GET /users/3
      • ' 82 | , '
      • GET /users/1..3
      • ' 83 | , '
      • GET /users/1..3.json
      • ' 84 | , '
      • DELETE /users/4
      • ' 85 | , '
      ' 86 | ].join('\n')); 87 | }); 88 | 89 | /* istanbul ignore next */ 90 | if (!module.parent) { 91 | app.listen(3000); 92 | console.log('Express started on port 3000'); 93 | } 94 | -------------------------------------------------------------------------------- /examples/mvc/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../..'); 6 | var logger = require('morgan'); 7 | var path = require('path'); 8 | var session = require('express-session'); 9 | var bodyParser = require('body-parser'); 10 | var methodOverride = require('method-override'); 11 | 12 | var app = module.exports = express(); 13 | 14 | // set our default template engine to "ejs" 15 | // which prevents the need for using file extensions 16 | app.set('view engine', 'ejs'); 17 | 18 | // set views for error and 404 pages 19 | app.set('views', path.join(__dirname, 'views')); 20 | 21 | // define a custom res.message() method 22 | // which stores messages in the session 23 | app.response.message = function(msg){ 24 | // reference `req.session` via the `this.req` reference 25 | var sess = this.req.session; 26 | // simply add the msg to an array for later 27 | sess.messages = sess.messages || []; 28 | sess.messages.push(msg); 29 | return this; 30 | }; 31 | 32 | // log 33 | if (!module.parent) app.use(logger('dev')); 34 | 35 | // serve static files 36 | app.use(express.static(path.join(__dirname, 'public'))); 37 | 38 | // session support 39 | app.use(session({ 40 | resave: false, // don't save session if unmodified 41 | saveUninitialized: false, // don't create session until something stored 42 | secret: 'some secret here' 43 | })); 44 | 45 | // parse request bodies (req.body) 46 | app.use(bodyParser.urlencoded({ extended: true })); 47 | 48 | // allow overriding methods in query (?_method=put) 49 | app.use(methodOverride('_method')); 50 | 51 | // expose the "messages" local variable when views are rendered 52 | app.use(function(req, res, next){ 53 | var msgs = req.session.messages || []; 54 | 55 | // expose "messages" local variable 56 | res.locals.messages = msgs; 57 | 58 | // expose "hasMessages" 59 | res.locals.hasMessages = !! msgs.length; 60 | 61 | /* This is equivalent: 62 | res.locals({ 63 | messages: msgs, 64 | hasMessages: !! msgs.length 65 | }); 66 | */ 67 | 68 | next(); 69 | // empty or "flush" the messages so they 70 | // don't build up 71 | req.session.messages = []; 72 | }); 73 | 74 | // load controllers 75 | require('./lib/boot')(app, { verbose: !module.parent }); 76 | 77 | app.use(function(err, req, res, next){ 78 | // log it 79 | if (!module.parent) console.error(err.stack); 80 | 81 | // error page 82 | res.status(500).render('5xx'); 83 | }); 84 | 85 | // assume 404 since no middleware responded 86 | app.use(function(req, res, next){ 87 | res.status(404).render('404', { url: req.originalUrl }); 88 | }); 89 | 90 | /* istanbul ignore next */ 91 | if (!module.parent) { 92 | app.listen(3000); 93 | console.log('Express started on port 3000'); 94 | } 95 | -------------------------------------------------------------------------------- /examples/route-middleware/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../lib/express'); 6 | 7 | var app = express(); 8 | 9 | // Example requests: 10 | // curl http://localhost:3000/user/0 11 | // curl http://localhost:3000/user/0/edit 12 | // curl http://localhost:3000/user/1 13 | // curl http://localhost:3000/user/1/edit (unauthorized since this is not you) 14 | // curl -X DELETE http://localhost:3000/user/0 (unauthorized since you are not an admin) 15 | 16 | // Dummy users 17 | var users = [ 18 | { id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' } 19 | , { id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' } 20 | , { id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' } 21 | ]; 22 | 23 | function loadUser(req, res, next) { 24 | // You would fetch your user from the db 25 | var user = users[req.params.id]; 26 | if (user) { 27 | req.user = user; 28 | next(); 29 | } else { 30 | next(new Error('Failed to load user ' + req.params.id)); 31 | } 32 | } 33 | 34 | function andRestrictToSelf(req, res, next) { 35 | // If our authenticated user is the user we are viewing 36 | // then everything is fine :) 37 | if (req.authenticatedUser.id == req.user.id) { 38 | next(); 39 | } else { 40 | // You may want to implement specific exceptions 41 | // such as UnauthorizedError or similar so that you 42 | // can handle these can be special-cased in an error handler 43 | // (view ./examples/pages for this) 44 | next(new Error('Unauthorized')); 45 | } 46 | } 47 | 48 | function andRestrictTo(role) { 49 | return function(req, res, next) { 50 | if (req.authenticatedUser.role == role) { 51 | next(); 52 | } else { 53 | next(new Error('Unauthorized')); 54 | } 55 | } 56 | } 57 | 58 | // Middleware for faux authentication 59 | // you would of course implement something real, 60 | // but this illustrates how an authenticated user 61 | // may interact with middleware 62 | 63 | app.use(function(req, res, next){ 64 | req.authenticatedUser = users[0]; 65 | next(); 66 | }); 67 | 68 | app.get('/', function(req, res){ 69 | res.redirect('/user/0'); 70 | }); 71 | 72 | app.get('/user/:id', loadUser, function(req, res){ 73 | res.send('Viewing user ' + req.user.name); 74 | }); 75 | 76 | app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){ 77 | res.send('Editing user ' + req.user.name); 78 | }); 79 | 80 | app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){ 81 | res.send('Deleted user ' + req.user.name); 82 | }); 83 | 84 | /* istanbul ignore next */ 85 | if (!module.parent) { 86 | app.listen(3000); 87 | console.log('Express started on port 3000'); 88 | } 89 | -------------------------------------------------------------------------------- /lib/express.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * express 3 | * Copyright(c) 2009-2013 TJ Holowaychuk 4 | * Copyright(c) 2013 Roman Shtylman 5 | * Copyright(c) 2014-2015 Douglas Christopher Wilson 6 | * MIT Licensed 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Module dependencies. 13 | */ 14 | 15 | var EventEmitter = require('events').EventEmitter; 16 | var mixin = require('merge-descriptors'); 17 | var proto = require('./application'); 18 | var Route = require('./router/route'); 19 | var Router = require('./router'); 20 | var req = require('./request'); 21 | var res = require('./response'); 22 | 23 | /** 24 | * Expose `createApplication()`. 25 | */ 26 | 27 | exports = module.exports = createApplication; 28 | 29 | /** 30 | * Create an express application. 31 | * 32 | * @return {Function} 33 | * @api public 34 | */ 35 | 36 | function createApplication() { 37 | var app = function(req, res, next) { 38 | app.handle(req, res, next); 39 | }; 40 | 41 | mixin(app, EventEmitter.prototype, false); 42 | mixin(app, proto, false); 43 | 44 | // expose the prototype that will get set on requests 45 | app.request = Object.create(req, { 46 | app: { configurable: true, enumerable: true, writable: true, value: app } 47 | }) 48 | 49 | // expose the prototype that will get set on responses 50 | app.response = Object.create(res, { 51 | app: { configurable: true, enumerable: true, writable: true, value: app } 52 | }) 53 | 54 | app.init(); 55 | return app; 56 | } 57 | 58 | /** 59 | * Expose the prototypes. 60 | */ 61 | 62 | exports.application = proto; 63 | exports.request = req; 64 | exports.response = res; 65 | 66 | /** 67 | * Expose constructors. 68 | */ 69 | 70 | exports.Route = Route; 71 | exports.Router = Router; 72 | 73 | /** 74 | * Expose middleware 75 | */ 76 | 77 | exports.query = require('./middleware/query'); 78 | exports.static = require('serve-static'); 79 | 80 | /** 81 | * Replace removed middleware with an appropriate error message. 82 | */ 83 | 84 | [ 85 | 'json', 86 | 'urlencoded', 87 | 'bodyParser', 88 | 'compress', 89 | 'cookieSession', 90 | 'session', 91 | 'logger', 92 | 'cookieParser', 93 | 'favicon', 94 | 'responseTime', 95 | 'errorHandler', 96 | 'timeout', 97 | 'methodOverride', 98 | 'vhost', 99 | 'csrf', 100 | 'directory', 101 | 'limit', 102 | 'multipart', 103 | 'staticCache', 104 | ].forEach(function (name) { 105 | Object.defineProperty(exports, name, { 106 | get: function () { 107 | throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.'); 108 | }, 109 | configurable: true 110 | }); 111 | }); 112 | -------------------------------------------------------------------------------- /test/req.ip.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.ip', function(){ 7 | describe('when X-Forwarded-For is present', function(){ 8 | describe('when "trust proxy" is enabled', function(){ 9 | it('should return the client addr', function(done){ 10 | var app = express(); 11 | 12 | app.enable('trust proxy'); 13 | 14 | app.use(function(req, res, next){ 15 | res.send(req.ip); 16 | }); 17 | 18 | request(app) 19 | .get('/') 20 | .set('X-Forwarded-For', 'client, p1, p2') 21 | .expect('client', done); 22 | }) 23 | 24 | it('should return the addr after trusted proxy', function(done){ 25 | var app = express(); 26 | 27 | app.set('trust proxy', 2); 28 | 29 | app.use(function(req, res, next){ 30 | res.send(req.ip); 31 | }); 32 | 33 | request(app) 34 | .get('/') 35 | .set('X-Forwarded-For', 'client, p1, p2') 36 | .expect('p1', done); 37 | }) 38 | 39 | it('should return the addr after trusted proxy, from sub app', function (done) { 40 | var app = express(); 41 | var sub = express(); 42 | 43 | app.set('trust proxy', 2); 44 | app.use(sub); 45 | 46 | sub.use(function (req, res, next) { 47 | res.send(req.ip); 48 | }); 49 | 50 | request(app) 51 | .get('/') 52 | .set('X-Forwarded-For', 'client, p1, p2') 53 | .expect(200, 'p1', done); 54 | }) 55 | }) 56 | 57 | describe('when "trust proxy" is disabled', function(){ 58 | it('should return the remote address', function(done){ 59 | var app = express(); 60 | 61 | app.use(function(req, res, next){ 62 | res.send(req.ip); 63 | }); 64 | 65 | var test = request(app).get('/') 66 | test.set('X-Forwarded-For', 'client, p1, p2') 67 | test.expect(200, getExpectedClientAddress(test._server), done); 68 | }) 69 | }) 70 | }) 71 | 72 | describe('when X-Forwarded-For is not present', function(){ 73 | it('should return the remote address', function(done){ 74 | var app = express(); 75 | 76 | app.enable('trust proxy'); 77 | 78 | app.use(function(req, res, next){ 79 | res.send(req.ip); 80 | }); 81 | 82 | var test = request(app).get('/') 83 | test.expect(200, getExpectedClientAddress(test._server), done) 84 | }) 85 | }) 86 | }) 87 | }) 88 | 89 | /** 90 | * Get the local client address depending on AF_NET of server 91 | */ 92 | 93 | function getExpectedClientAddress(server) { 94 | return server.address().address === '::' 95 | ? '::ffff:127.0.0.1' 96 | : '127.0.0.1'; 97 | } 98 | -------------------------------------------------------------------------------- /test/req.range.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert'); 3 | var express = require('..'); 4 | var request = require('supertest') 5 | 6 | describe('req', function(){ 7 | describe('.range(size)', function(){ 8 | it('should return parsed ranges', function (done) { 9 | var app = express() 10 | 11 | app.use(function (req, res) { 12 | res.json(req.range(120)) 13 | }) 14 | 15 | request(app) 16 | .get('/') 17 | .set('Range', 'bytes=0-50,51-100') 18 | .expect(200, '[{"start":0,"end":50},{"start":51,"end":100}]', done) 19 | }) 20 | 21 | it('should cap to the given size', function (done) { 22 | var app = express() 23 | 24 | app.use(function (req, res) { 25 | res.json(req.range(75)) 26 | }) 27 | 28 | request(app) 29 | .get('/') 30 | .set('Range', 'bytes=0-100') 31 | .expect(200, '[{"start":0,"end":74}]', done) 32 | }) 33 | 34 | it('should cap to the given size when open-ended', function (done) { 35 | var app = express() 36 | 37 | app.use(function (req, res) { 38 | res.json(req.range(75)) 39 | }) 40 | 41 | request(app) 42 | .get('/') 43 | .set('Range', 'bytes=0-') 44 | .expect(200, '[{"start":0,"end":74}]', done) 45 | }) 46 | 47 | it('should have a .type', function (done) { 48 | var app = express() 49 | 50 | app.use(function (req, res) { 51 | res.json(req.range(120).type) 52 | }) 53 | 54 | request(app) 55 | .get('/') 56 | .set('Range', 'bytes=0-100') 57 | .expect(200, '"bytes"', done) 58 | }) 59 | 60 | it('should accept any type', function (done) { 61 | var app = express() 62 | 63 | app.use(function (req, res) { 64 | res.json(req.range(120).type) 65 | }) 66 | 67 | request(app) 68 | .get('/') 69 | .set('Range', 'users=0-2') 70 | .expect(200, '"users"', done) 71 | }) 72 | 73 | it('should return undefined if no range', function (done) { 74 | var app = express() 75 | 76 | app.use(function (req, res) { 77 | res.send(String(req.range(120))) 78 | }) 79 | 80 | request(app) 81 | .get('/') 82 | .expect(200, 'undefined', done) 83 | }) 84 | }) 85 | 86 | describe('.range(size, options)', function(){ 87 | describe('with "combine: true" option', function(){ 88 | it('should return combined ranges', function (done) { 89 | var app = express() 90 | 91 | app.use(function (req, res) { 92 | res.json(req.range(120, { 93 | combine: true 94 | })) 95 | }) 96 | 97 | request(app) 98 | .get('/') 99 | .set('Range', 'bytes=0-50,51-100') 100 | .expect(200, '[{"start":0,"end":100}]', done) 101 | }) 102 | }) 103 | }) 104 | }) 105 | -------------------------------------------------------------------------------- /test/acceptance/error-pages.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/error-pages') 3 | , request = require('supertest'); 4 | 5 | describe('error-pages', function(){ 6 | describe('GET /', function(){ 7 | it('should respond with page list', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect(/Pages Example/, done) 11 | }) 12 | }) 13 | 14 | describe('Accept: text/html',function(){ 15 | describe('GET /403', function(){ 16 | it('should respond with 403', function(done){ 17 | request(app) 18 | .get('/403') 19 | .expect(403, done) 20 | }) 21 | }) 22 | 23 | describe('GET /404', function(){ 24 | it('should respond with 404', function(done){ 25 | request(app) 26 | .get('/404') 27 | .expect(404, done) 28 | }) 29 | }) 30 | 31 | describe('GET /500', function(){ 32 | it('should respond with 500', function(done){ 33 | request(app) 34 | .get('/500') 35 | .expect(500, done) 36 | }) 37 | }) 38 | }) 39 | 40 | describe('Accept: application/json',function(){ 41 | describe('GET /403', function(){ 42 | it('should respond with 403', function(done){ 43 | request(app) 44 | .get('/403') 45 | .set('Accept','application/json') 46 | .expect(403, done) 47 | }) 48 | }) 49 | 50 | describe('GET /404', function(){ 51 | it('should respond with 404', function(done){ 52 | request(app) 53 | .get('/404') 54 | .set('Accept','application/json') 55 | .end(function(err, res){ 56 | res.body.should.eql({ error: 'Not found' }); 57 | done() 58 | }) 59 | }) 60 | }) 61 | 62 | describe('GET /500', function(){ 63 | it('should respond with 500', function(done){ 64 | request(app) 65 | .get('/500') 66 | .set('Accept', 'application/json') 67 | .expect(500, done) 68 | }) 69 | }) 70 | }) 71 | 72 | 73 | describe('Accept: text/plain',function(){ 74 | describe('GET /403', function(){ 75 | it('should respond with 403', function(done){ 76 | request(app) 77 | .get('/403') 78 | .set('Accept','text/plain') 79 | .expect(403, done) 80 | }) 81 | }) 82 | 83 | describe('GET /404', function(){ 84 | it('should respond with 404', function(done){ 85 | request(app) 86 | .get('/404') 87 | .set('Accept', 'text/plain') 88 | .expect(404) 89 | .expect('Not found', done); 90 | }) 91 | }) 92 | 93 | describe('GET /500', function(){ 94 | it('should respond with 500', function(done){ 95 | request(app) 96 | .get('/500') 97 | .set('Accept','text/plain') 98 | .expect(500, done) 99 | }) 100 | }) 101 | }) 102 | }) -------------------------------------------------------------------------------- /test/acceptance/route-separation.js: -------------------------------------------------------------------------------- 1 | 2 | var app = require('../../examples/route-separation') 3 | var request = require('supertest') 4 | 5 | describe('route-separation', function () { 6 | describe('GET /', function () { 7 | it('should respond with index', function (done) { 8 | request(app) 9 | .get('/') 10 | .expect(200, /Route Separation Example/, done) 11 | }) 12 | }) 13 | 14 | describe('GET /users', function () { 15 | it('should list users', function (done) { 16 | request(app) 17 | .get('/users') 18 | .expect(/TJ/) 19 | .expect(/Tobi/) 20 | .expect(200, done) 21 | }) 22 | }) 23 | 24 | describe('GET /user/:id', function () { 25 | it('should get a user', function (done) { 26 | request(app) 27 | .get('/user/0') 28 | .expect(200, /Viewing user TJ/, done) 29 | }) 30 | 31 | it('should 404 on missing user', function (done) { 32 | request(app) 33 | .get('/user/10') 34 | .expect(404, done) 35 | }) 36 | }) 37 | 38 | describe('GET /user/:id/view', function () { 39 | it('should get a user', function (done) { 40 | request(app) 41 | .get('/user/0/view') 42 | .expect(200, /Viewing user TJ/, done) 43 | }) 44 | 45 | it('should 404 on missing user', function (done) { 46 | request(app) 47 | .get('/user/10/view') 48 | .expect(404, done) 49 | }) 50 | }) 51 | 52 | describe('GET /user/:id/edit', function () { 53 | it('should get a user to edit', function (done) { 54 | request(app) 55 | .get('/user/0/edit') 56 | .expect(200, /Editing user TJ/, done) 57 | }) 58 | }) 59 | 60 | describe('PUT /user/:id/edit', function () { 61 | it('should edit a user', function (done) { 62 | request(app) 63 | .put('/user/0/edit') 64 | .set('Content-Type', 'application/x-www-form-urlencoded') 65 | .send({ user: { name: 'TJ', email: 'tj-invalid@vision-media.ca' } }) 66 | .expect(302, function (err) { 67 | if (err) return done(err) 68 | request(app) 69 | .get('/user/0') 70 | .expect(200, /tj-invalid@vision-media\.ca/, done) 71 | }) 72 | }) 73 | }) 74 | 75 | describe('POST /user/:id/edit?_method=PUT', function () { 76 | it('should edit a user', function (done) { 77 | request(app) 78 | .post('/user/1/edit?_method=PUT') 79 | .set('Content-Type', 'application/x-www-form-urlencoded') 80 | .send({ user: { name: 'Tobi', email: 'tobi-invalid@vision-media.ca' } }) 81 | .expect(302, function (err) { 82 | if (err) return done(err) 83 | request(app) 84 | .get('/user/1') 85 | .expect(200, /tobi-invalid@vision-media\.ca/, done) 86 | }) 87 | }) 88 | }) 89 | 90 | describe('GET /posts', function () { 91 | it('should get a list of posts', function (done) { 92 | request(app) 93 | .get('/posts') 94 | .expect(200, /Posts/, done) 95 | }) 96 | }) 97 | }) 98 | -------------------------------------------------------------------------------- /test/req.secure.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.secure', function(){ 7 | describe('when X-Forwarded-Proto is missing', function(){ 8 | it('should return false when http', function(done){ 9 | var app = express(); 10 | 11 | app.get('/', function(req, res){ 12 | res.send(req.secure ? 'yes' : 'no'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('no', done) 18 | }) 19 | }) 20 | }) 21 | 22 | describe('.secure', function(){ 23 | describe('when X-Forwarded-Proto is present', function(){ 24 | it('should return false when http', function(done){ 25 | var app = express(); 26 | 27 | app.get('/', function(req, res){ 28 | res.send(req.secure ? 'yes' : 'no'); 29 | }); 30 | 31 | request(app) 32 | .get('/') 33 | .set('X-Forwarded-Proto', 'https') 34 | .expect('no', done) 35 | }) 36 | 37 | it('should return true when "trust proxy" is enabled', function(done){ 38 | var app = express(); 39 | 40 | app.enable('trust proxy'); 41 | 42 | app.get('/', function(req, res){ 43 | res.send(req.secure ? 'yes' : 'no'); 44 | }); 45 | 46 | request(app) 47 | .get('/') 48 | .set('X-Forwarded-Proto', 'https') 49 | .expect('yes', done) 50 | }) 51 | 52 | it('should return false when initial proxy is http', function(done){ 53 | var app = express(); 54 | 55 | app.enable('trust proxy'); 56 | 57 | app.get('/', function(req, res){ 58 | res.send(req.secure ? 'yes' : 'no'); 59 | }); 60 | 61 | request(app) 62 | .get('/') 63 | .set('X-Forwarded-Proto', 'http, https') 64 | .expect('no', done) 65 | }) 66 | 67 | it('should return true when initial proxy is https', function(done){ 68 | var app = express(); 69 | 70 | app.enable('trust proxy'); 71 | 72 | app.get('/', function(req, res){ 73 | res.send(req.secure ? 'yes' : 'no'); 74 | }); 75 | 76 | request(app) 77 | .get('/') 78 | .set('X-Forwarded-Proto', 'https, http') 79 | .expect('yes', done) 80 | }) 81 | 82 | describe('when "trust proxy" trusting hop count', function () { 83 | it('should respect X-Forwarded-Proto', function (done) { 84 | var app = express(); 85 | 86 | app.set('trust proxy', 1); 87 | 88 | app.get('/', function (req, res) { 89 | res.send(req.secure ? 'yes' : 'no'); 90 | }); 91 | 92 | request(app) 93 | .get('/') 94 | .set('X-Forwarded-Proto', 'https') 95 | .expect('yes', done) 96 | }) 97 | }) 98 | }) 99 | }) 100 | }) 101 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express", 3 | "description": "Fast, unopinionated, minimalist web framework", 4 | "version": "4.15.2", 5 | "author": "TJ Holowaychuk ", 6 | "contributors": [ 7 | "Aaron Heckmann ", 8 | "Ciaran Jessup ", 9 | "Douglas Christopher Wilson ", 10 | "Guillermo Rauch ", 11 | "Jonathan Ong ", 12 | "Roman Shtylman ", 13 | "Young Jae Sim " 14 | ], 15 | "license": "MIT", 16 | "repository": "expressjs/express", 17 | "homepage": "http://expressjs.com/", 18 | "keywords": [ 19 | "express", 20 | "framework", 21 | "sinatra", 22 | "web", 23 | "rest", 24 | "restful", 25 | "router", 26 | "app", 27 | "api" 28 | ], 29 | "dependencies": { 30 | "accepts": "~1.3.3", 31 | "array-flatten": "1.1.1", 32 | "content-disposition": "0.5.2", 33 | "content-type": "~1.0.2", 34 | "cookie": "0.3.1", 35 | "cookie-signature": "1.0.6", 36 | "debug": "2.6.3", 37 | "depd": "~1.1.0", 38 | "encodeurl": "~1.0.1", 39 | "escape-html": "~1.0.3", 40 | "etag": "~1.8.0", 41 | "finalhandler": "~1.0.1", 42 | "fresh": "0.5.0", 43 | "merge-descriptors": "1.0.1", 44 | "methods": "~1.1.2", 45 | "on-finished": "~2.3.0", 46 | "parseurl": "~1.3.1", 47 | "path-to-regexp": "0.1.7", 48 | "proxy-addr": "~1.1.4", 49 | "qs": "6.4.0", 50 | "range-parser": "~1.2.0", 51 | "send": "0.15.2", 52 | "serve-static": "1.12.2", 53 | "setprototypeof": "1.0.3", 54 | "statuses": "~1.3.1", 55 | "type-is": "~1.6.15", 56 | "utils-merge": "1.0.0", 57 | "vary": "~1.1.1" 58 | }, 59 | "devDependencies": { 60 | "after": "0.8.2", 61 | "body-parser": "1.17.1", 62 | "cookie-parser": "~1.4.3", 63 | "ejs": "2.5.6", 64 | "express-session": "1.15.1", 65 | "hbs": "4.0.1", 66 | "istanbul": "0.4.5", 67 | "marked": "0.3.6", 68 | "method-override": "2.3.7", 69 | "mocha": "3.2.0", 70 | "morgan": "1.8.1", 71 | "multiparty": "4.1.3", 72 | "pbkdf2-password": "1.2.1", 73 | "should": "11.2.1", 74 | "supertest": "1.2.0", 75 | "connect-redis": "~2.4.1", 76 | "cookie-session": "~1.2.0", 77 | "vhost": "~3.0.2" 78 | }, 79 | "engines": { 80 | "node": ">= 0.10.0" 81 | }, 82 | "files": [ 83 | "LICENSE", 84 | "History.md", 85 | "Readme.md", 86 | "index.js", 87 | "lib/" 88 | ], 89 | "scripts": { 90 | "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/", 91 | "test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/", 92 | "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/", 93 | "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /test/acceptance/auth.js: -------------------------------------------------------------------------------- 1 | var app = require('../../examples/auth') 2 | var request = require('supertest') 3 | 4 | function getCookie(res) { 5 | return res.headers['set-cookie'][0].split(';')[0]; 6 | } 7 | 8 | describe('auth', function(){ 9 | describe('GET /',function(){ 10 | it('should redirect to /login', function(done){ 11 | request(app) 12 | .get('/') 13 | .expect('Location', '/login') 14 | .expect(302, done) 15 | }) 16 | }) 17 | 18 | describe('GET /login',function(){ 19 | it('should render login form', function(done){ 20 | request(app) 21 | .get('/login') 22 | .expect(200, /') 17 | next() 18 | }) 19 | 20 | app.use(function (req, res) { 21 | res.append('Link', '') 22 | res.end() 23 | }) 24 | 25 | request(app) 26 | .get('/') 27 | .expect('Link', ', ', done) 28 | }) 29 | 30 | it('should accept array of values', function (done) { 31 | var app = express() 32 | 33 | app.use(function (req, res, next) { 34 | res.append('Set-Cookie', ['foo=bar', 'fizz=buzz']) 35 | res.end() 36 | }) 37 | 38 | request(app) 39 | .get('/') 40 | .expect(function (res) { 41 | should(res.headers['set-cookie']).eql(['foo=bar', 'fizz=buzz']) 42 | }) 43 | .expect(200, done) 44 | }) 45 | 46 | it('should get reset by res.set(field, val)', function (done) { 47 | var app = express() 48 | 49 | app.use(function (req, res, next) { 50 | res.append('Link', '') 51 | res.append('Link', '') 52 | next() 53 | }) 54 | 55 | app.use(function (req, res) { 56 | res.set('Link', '') 57 | res.end() 58 | }); 59 | 60 | request(app) 61 | .get('/') 62 | .expect('Link', '', done) 63 | }) 64 | 65 | it('should work with res.set(field, val) first', function (done) { 66 | var app = express() 67 | 68 | app.use(function (req, res, next) { 69 | res.set('Link', '') 70 | next() 71 | }) 72 | 73 | app.use(function(req, res){ 74 | res.append('Link', '') 75 | res.end() 76 | }) 77 | 78 | request(app) 79 | .get('/') 80 | .expect('Link', ', ', done) 81 | }) 82 | 83 | it('should work with cookies', function (done) { 84 | var app = express() 85 | 86 | app.use(function (req, res, next) { 87 | res.cookie('foo', 'bar') 88 | next() 89 | }) 90 | 91 | app.use(function (req, res) { 92 | res.append('Set-Cookie', 'bar=baz') 93 | res.end() 94 | }) 95 | 96 | request(app) 97 | .get('/') 98 | .expect(function (res) { 99 | should(res.headers['set-cookie']).eql(['foo=bar; Path=/', 'bar=baz']) 100 | }) 101 | .expect(200, done) 102 | }) 103 | }) 104 | }) 105 | -------------------------------------------------------------------------------- /examples/error-pages/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | var path = require('path'); 7 | var app = module.exports = express(); 8 | var logger = require('morgan'); 9 | var silent = 'test' == process.env.NODE_ENV; 10 | 11 | // general config 12 | app.set('views', path.join(__dirname, 'views')); 13 | app.set('view engine', 'ejs'); 14 | 15 | // our custom "verbose errors" setting 16 | // which we can use in the templates 17 | // via settings['verbose errors'] 18 | app.enable('verbose errors'); 19 | 20 | // disable them in production 21 | // use $ NODE_ENV=production node examples/error-pages 22 | if ('production' == app.settings.env) app.disable('verbose errors'); 23 | 24 | silent || app.use(logger('dev')); 25 | 26 | // Routes 27 | 28 | app.get('/', function(req, res){ 29 | res.render('index.ejs'); 30 | }); 31 | 32 | app.get('/404', function(req, res, next){ 33 | // trigger a 404 since no other middleware 34 | // will match /404 after this one, and we're not 35 | // responding here 36 | next(); 37 | }); 38 | 39 | app.get('/403', function(req, res, next){ 40 | // trigger a 403 error 41 | var err = new Error('not allowed!'); 42 | err.status = 403; 43 | next(err); 44 | }); 45 | 46 | app.get('/500', function(req, res, next){ 47 | // trigger a generic (500) error 48 | next(new Error('keyboard cat!')); 49 | }); 50 | 51 | // Error handlers 52 | 53 | // Since this is the last non-error-handling 54 | // middleware use()d, we assume 404, as nothing else 55 | // responded. 56 | 57 | // $ curl http://localhost:3000/notfound 58 | // $ curl http://localhost:3000/notfound -H "Accept: application/json" 59 | // $ curl http://localhost:3000/notfound -H "Accept: text/plain" 60 | 61 | app.use(function(req, res, next){ 62 | res.status(404); 63 | 64 | res.format({ 65 | html: function () { 66 | res.render('404', { url: req.url }) 67 | }, 68 | json: function () { 69 | res.json({ error: 'Not found' }) 70 | }, 71 | default: function () { 72 | res.type('txt').send('Not found') 73 | } 74 | }) 75 | }); 76 | 77 | // error-handling middleware, take the same form 78 | // as regular middleware, however they require an 79 | // arity of 4, aka the signature (err, req, res, next). 80 | // when connect has an error, it will invoke ONLY error-handling 81 | // middleware. 82 | 83 | // If we were to next() here any remaining non-error-handling 84 | // middleware would then be executed, or if we next(err) to 85 | // continue passing the error, only error-handling middleware 86 | // would remain being executed, however here 87 | // we simply respond with an error page. 88 | 89 | app.use(function(err, req, res, next){ 90 | // we may use properties of the error object 91 | // here and next(err) appropriately, or if 92 | // we possibly recovered from the error, simply next(). 93 | res.status(err.status || 500); 94 | res.render('500', { error: err }); 95 | }); 96 | 97 | /* istanbul ignore next */ 98 | if (!module.parent) { 99 | app.listen(3000); 100 | console.log('Express started on port 3000'); 101 | } 102 | -------------------------------------------------------------------------------- /test/res.set.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('..'); 3 | var request = require('supertest'); 4 | 5 | describe('res', function(){ 6 | describe('.set(field, value)', function(){ 7 | it('should set the response header field', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res){ 11 | res.set('Content-Type', 'text/x-foo; charset=utf-8').end(); 12 | }); 13 | 14 | request(app) 15 | .get('/') 16 | .expect('Content-Type', 'text/x-foo; charset=utf-8') 17 | .end(done); 18 | }) 19 | 20 | it('should coerce to a string', function (done) { 21 | var app = express(); 22 | 23 | app.use(function (req, res) { 24 | res.set('X-Number', 123); 25 | res.end(typeof res.get('X-Number')); 26 | }); 27 | 28 | request(app) 29 | .get('/') 30 | .expect('X-Number', '123') 31 | .expect(200, 'string', done); 32 | }) 33 | }) 34 | 35 | describe('.set(field, values)', function(){ 36 | it('should set multiple response header fields', function(done){ 37 | var app = express(); 38 | 39 | app.use(function(req, res){ 40 | res.set('Set-Cookie', ["type=ninja", "language=javascript"]); 41 | res.send(res.get('Set-Cookie')); 42 | }); 43 | 44 | request(app) 45 | .get('/') 46 | .expect('["type=ninja","language=javascript"]', done); 47 | }) 48 | 49 | it('should coerce to an array of strings', function (done) { 50 | var app = express(); 51 | 52 | app.use(function (req, res) { 53 | res.set('X-Numbers', [123, 456]); 54 | res.end(JSON.stringify(res.get('X-Numbers'))); 55 | }); 56 | 57 | request(app) 58 | .get('/') 59 | .expect('X-Numbers', '123, 456') 60 | .expect(200, '["123","456"]', done); 61 | }) 62 | 63 | it('should not set a charset of one is already set', function (done) { 64 | var app = express(); 65 | 66 | app.use(function (req, res) { 67 | res.set('Content-Type', 'text/html; charset=lol'); 68 | res.end(); 69 | }); 70 | 71 | request(app) 72 | .get('/') 73 | .expect('Content-Type', 'text/html; charset=lol') 74 | .expect(200, done); 75 | }) 76 | }) 77 | 78 | describe('.set(object)', function(){ 79 | it('should set multiple fields', function(done){ 80 | var app = express(); 81 | 82 | app.use(function(req, res){ 83 | res.set({ 84 | 'X-Foo': 'bar', 85 | 'X-Bar': 'baz' 86 | }).end(); 87 | }); 88 | 89 | request(app) 90 | .get('/') 91 | .expect('X-Foo', 'bar') 92 | .expect('X-Bar', 'baz') 93 | .end(done); 94 | }) 95 | 96 | it('should coerce to a string', function (done) { 97 | var app = express(); 98 | 99 | app.use(function (req, res) { 100 | res.set({ 'X-Number': 123 }); 101 | res.end(typeof res.get('X-Number')); 102 | }); 103 | 104 | request(app) 105 | .get('/') 106 | .expect('X-Number', '123') 107 | .expect(200, 'string', done); 108 | }) 109 | }) 110 | }) 111 | -------------------------------------------------------------------------------- /test/app.options.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('OPTIONS', function(){ 6 | it('should default to the routes defined', function(done){ 7 | var app = express(); 8 | 9 | app.del('/', function(){}); 10 | app.get('/users', function(req, res){}); 11 | app.put('/users', function(req, res){}); 12 | 13 | request(app) 14 | .options('/users') 15 | .expect('Allow', 'GET,HEAD,PUT') 16 | .expect(200, 'GET,HEAD,PUT', done); 17 | }) 18 | 19 | it('should only include each method once', function(done){ 20 | var app = express(); 21 | 22 | app.del('/', function(){}); 23 | app.get('/users', function(req, res){}); 24 | app.put('/users', function(req, res){}); 25 | app.get('/users', function(req, res){}); 26 | 27 | request(app) 28 | .options('/users') 29 | .expect('Allow', 'GET,HEAD,PUT') 30 | .expect(200, 'GET,HEAD,PUT', done); 31 | }) 32 | 33 | it('should not be affected by app.all', function(done){ 34 | var app = express(); 35 | 36 | app.get('/', function(){}); 37 | app.get('/users', function(req, res){}); 38 | app.put('/users', function(req, res){}); 39 | app.all('/users', function(req, res, next){ 40 | res.setHeader('x-hit', '1'); 41 | next(); 42 | }); 43 | 44 | request(app) 45 | .options('/users') 46 | .expect('x-hit', '1') 47 | .expect('Allow', 'GET,HEAD,PUT') 48 | .expect(200, 'GET,HEAD,PUT', done); 49 | }) 50 | 51 | it('should not respond if the path is not defined', function(done){ 52 | var app = express(); 53 | 54 | app.get('/users', function(req, res){}); 55 | 56 | request(app) 57 | .options('/other') 58 | .expect(404, done); 59 | }) 60 | 61 | it('should forward requests down the middleware chain', function(done){ 62 | var app = express(); 63 | var router = new express.Router(); 64 | 65 | router.get('/users', function(req, res){}); 66 | app.use(router); 67 | app.get('/other', function(req, res){}); 68 | 69 | request(app) 70 | .options('/other') 71 | .expect('Allow', 'GET,HEAD') 72 | .expect(200, 'GET,HEAD', done); 73 | }) 74 | 75 | describe('when error occurs in response handler', function () { 76 | it('should pass error to callback', function (done) { 77 | var app = express(); 78 | var router = express.Router(); 79 | 80 | router.get('/users', function(req, res){}); 81 | 82 | app.use(function (req, res, next) { 83 | res.writeHead(200); 84 | next(); 85 | }); 86 | app.use(router); 87 | app.use(function (err, req, res, next) { 88 | res.end('true'); 89 | }); 90 | 91 | request(app) 92 | .options('/users') 93 | .expect(200, 'true', done) 94 | }) 95 | }) 96 | }) 97 | 98 | describe('app.options()', function(){ 99 | it('should override the default behavior', function(done){ 100 | var app = express(); 101 | 102 | app.options('/users', function(req, res){ 103 | res.set('Allow', 'GET'); 104 | res.send('GET'); 105 | }); 106 | 107 | app.get('/users', function(req, res){}); 108 | app.put('/users', function(req, res){}); 109 | 110 | request(app) 111 | .options('/users') 112 | .expect('GET') 113 | .expect('Allow', 'GET', done); 114 | }) 115 | }) 116 | -------------------------------------------------------------------------------- /examples/web-service/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../../'); 6 | 7 | var app = module.exports = express(); 8 | 9 | // create an error with .status. we 10 | // can then use the property in our 11 | // custom error handler (Connect repects this prop as well) 12 | 13 | function error(status, msg) { 14 | var err = new Error(msg); 15 | err.status = status; 16 | return err; 17 | } 18 | 19 | // if we wanted to supply more than JSON, we could 20 | // use something similar to the content-negotiation 21 | // example. 22 | 23 | // here we validate the API key, 24 | // by mounting this middleware to /api 25 | // meaning only paths prefixed with "/api" 26 | // will cause this middleware to be invoked 27 | 28 | app.use('/api', function(req, res, next){ 29 | var key = req.query['api-key']; 30 | 31 | // key isn't present 32 | if (!key) return next(error(400, 'api key required')); 33 | 34 | // key is invalid 35 | if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key')); 36 | 37 | // all good, store req.key for route access 38 | req.key = key; 39 | next(); 40 | }); 41 | 42 | // map of valid api keys, typically mapped to 43 | // account info with some sort of database like redis. 44 | // api keys do _not_ serve as authentication, merely to 45 | // track API usage or help prevent malicious behavior etc. 46 | 47 | var apiKeys = ['foo', 'bar', 'baz']; 48 | 49 | // these two objects will serve as our faux database 50 | 51 | var repos = [ 52 | { name: 'express', url: 'http://github.com/expressjs/express' } 53 | , { name: 'stylus', url: 'http://github.com/learnboost/stylus' } 54 | , { name: 'cluster', url: 'http://github.com/learnboost/cluster' } 55 | ]; 56 | 57 | var users = [ 58 | { name: 'tobi' } 59 | , { name: 'loki' } 60 | , { name: 'jane' } 61 | ]; 62 | 63 | var userRepos = { 64 | tobi: [repos[0], repos[1]] 65 | , loki: [repos[1]] 66 | , jane: [repos[2]] 67 | }; 68 | 69 | // we now can assume the api key is valid, 70 | // and simply expose the data 71 | 72 | app.get('/api/users', function(req, res, next){ 73 | res.send(users); 74 | }); 75 | 76 | app.get('/api/repos', function(req, res, next){ 77 | res.send(repos); 78 | }); 79 | 80 | app.get('/api/user/:name/repos', function(req, res, next){ 81 | var name = req.params.name; 82 | var user = userRepos[name]; 83 | 84 | if (user) res.send(user); 85 | else next(); 86 | }); 87 | 88 | // middleware with an arity of 4 are considered 89 | // error handling middleware. When you next(err) 90 | // it will be passed through the defined middleware 91 | // in order, but ONLY those with an arity of 4, ignoring 92 | // regular middleware. 93 | app.use(function(err, req, res, next){ 94 | // whatever you want here, feel free to populate 95 | // properties on `err` to treat it differently in here. 96 | res.status(err.status || 500); 97 | res.send({ error: err.message }); 98 | }); 99 | 100 | // our custom JSON 404 middleware. Since it's placed last 101 | // it will be the last middleware called, if all others 102 | // invoke next() and do not respond. 103 | app.use(function(req, res){ 104 | res.status(404); 105 | res.send({ error: "Lame, can't find that" }); 106 | }); 107 | 108 | /* istanbul ignore next */ 109 | if (!module.parent) { 110 | app.listen(3000); 111 | console.log('Express started on port 3000'); 112 | } 113 | -------------------------------------------------------------------------------- /test/req.accepts.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.accepts(type)', function(){ 7 | it('should return true when Accept is not present', function(done){ 8 | var app = express(); 9 | 10 | app.use(function(req, res, next){ 11 | res.end(req.accepts('json') ? 'yes' : 'no'); 12 | }); 13 | 14 | request(app) 15 | .get('/') 16 | .expect('yes', done); 17 | }) 18 | 19 | it('should return true when present', function(done){ 20 | var app = express(); 21 | 22 | app.use(function(req, res, next){ 23 | res.end(req.accepts('json') ? 'yes' : 'no'); 24 | }); 25 | 26 | request(app) 27 | .get('/') 28 | .set('Accept', 'application/json') 29 | .expect('yes', done); 30 | }) 31 | 32 | it('should return false otherwise', function(done){ 33 | var app = express(); 34 | 35 | app.use(function(req, res, next){ 36 | res.end(req.accepts('json') ? 'yes' : 'no'); 37 | }); 38 | 39 | request(app) 40 | .get('/') 41 | .set('Accept', 'text/html') 42 | .expect('no', done); 43 | }) 44 | }) 45 | 46 | it('should accept an argument list of type names', function(done){ 47 | var app = express(); 48 | 49 | app.use(function(req, res, next){ 50 | res.end(req.accepts('json', 'html')); 51 | }); 52 | 53 | request(app) 54 | .get('/') 55 | .set('Accept', 'application/json') 56 | .expect('json', done); 57 | }) 58 | 59 | describe('.accepts(types)', function(){ 60 | it('should return the first when Accept is not present', function(done){ 61 | var app = express(); 62 | 63 | app.use(function(req, res, next){ 64 | res.end(req.accepts(['json', 'html'])); 65 | }); 66 | 67 | request(app) 68 | .get('/') 69 | .expect('json', done); 70 | }) 71 | 72 | it('should return the first acceptable type', function(done){ 73 | var app = express(); 74 | 75 | app.use(function(req, res, next){ 76 | res.end(req.accepts(['json', 'html'])); 77 | }); 78 | 79 | request(app) 80 | .get('/') 81 | .set('Accept', 'text/html') 82 | .expect('html', done); 83 | }) 84 | 85 | it('should return false when no match is made', function(done){ 86 | var app = express(); 87 | 88 | app.use(function(req, res, next){ 89 | res.end(req.accepts(['text/html', 'application/json']) ? 'yup' : 'nope'); 90 | }); 91 | 92 | request(app) 93 | .get('/') 94 | .set('Accept', 'foo/bar, bar/baz') 95 | .expect('nope', done); 96 | }) 97 | 98 | it('should take quality into account', function(done){ 99 | var app = express(); 100 | 101 | app.use(function(req, res, next){ 102 | res.end(req.accepts(['text/html', 'application/json'])); 103 | }); 104 | 105 | request(app) 106 | .get('/') 107 | .set('Accept', '*/html; q=.5, application/json') 108 | .expect('application/json', done); 109 | }) 110 | 111 | it('should return the first acceptable type with canonical mime types', function(done){ 112 | var app = express(); 113 | 114 | app.use(function(req, res, next){ 115 | res.end(req.accepts(['application/json', 'text/html'])); 116 | }); 117 | 118 | request(app) 119 | .get('/') 120 | .set('Accept', '*/html') 121 | .expect('text/html', done); 122 | }) 123 | }) 124 | }) 125 | -------------------------------------------------------------------------------- /test/acceptance/web-service.js: -------------------------------------------------------------------------------- 1 | 2 | var request = require('supertest') 3 | , app = require('../../examples/web-service'); 4 | 5 | describe('web-service', function(){ 6 | describe('GET /api/users', function(){ 7 | describe('without an api key', function(){ 8 | it('should respond with 400 bad request', function(done){ 9 | request(app) 10 | .get('/api/users') 11 | .expect(400, done); 12 | }) 13 | }) 14 | 15 | describe('with an invalid api key', function(){ 16 | it('should respond with 401 unauthorized', function(done){ 17 | request(app) 18 | .get('/api/users?api-key=rawr') 19 | .expect(401, done); 20 | }) 21 | }) 22 | 23 | describe('with a valid api key', function(){ 24 | it('should respond users json', function(done){ 25 | request(app) 26 | .get('/api/users?api-key=foo') 27 | .expect('Content-Type', 'application/json; charset=utf-8') 28 | .expect(200, '[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]', done) 29 | }) 30 | }) 31 | }) 32 | 33 | describe('GET /api/repos', function(){ 34 | describe('without an api key', function(){ 35 | it('should respond with 400 bad request', function(done){ 36 | request(app) 37 | .get('/api/repos') 38 | .expect(400, done); 39 | }) 40 | }) 41 | 42 | describe('with an invalid api key', function(){ 43 | it('should respond with 401 unauthorized', function(done){ 44 | request(app) 45 | .get('/api/repos?api-key=rawr') 46 | .expect(401, done); 47 | }) 48 | }) 49 | 50 | describe('with a valid api key', function(){ 51 | it('should respond repos json', function(done){ 52 | request(app) 53 | .get('/api/repos?api-key=foo') 54 | .expect('Content-Type', 'application/json; charset=utf-8') 55 | .expect(/"name":"express"/) 56 | .expect(/"url":"http:\/\/github.com\/expressjs\/express"/) 57 | .expect(200, done) 58 | }) 59 | }) 60 | }) 61 | 62 | describe('GET /api/user/:name/repos', function(){ 63 | describe('without an api key', function(){ 64 | it('should respond with 400 bad request', function(done){ 65 | request(app) 66 | .get('/api/user/loki/repos') 67 | .expect(400, done); 68 | }) 69 | }) 70 | 71 | describe('with an invalid api key', function(){ 72 | it('should respond with 401 unauthorized', function(done){ 73 | request(app) 74 | .get('/api/user/loki/repos?api-key=rawr') 75 | .expect(401, done); 76 | }) 77 | }) 78 | 79 | describe('with a valid api key', function(){ 80 | it('should respond user repos json', function(done){ 81 | request(app) 82 | .get('/api/user/loki/repos?api-key=foo') 83 | .expect('Content-Type', 'application/json; charset=utf-8') 84 | .expect(/"name":"stylus"/) 85 | .expect(/"url":"http:\/\/github.com\/learnboost\/stylus"/) 86 | .expect(200, done) 87 | }) 88 | 89 | it('should 404 with unknown user', function(done){ 90 | request(app) 91 | .get('/api/user/bob/repos?api-key=foo') 92 | .expect(404, done) 93 | }) 94 | }) 95 | }) 96 | 97 | describe('when requesting an invalid route', function(){ 98 | it('should respond with 404 json', function(done){ 99 | request(app) 100 | .get('/api/something?api-key=bar') 101 | .expect('Content-Type', /json/) 102 | .expect(404, '{"error":"Lame, can\'t find that"}', done) 103 | }) 104 | }) 105 | }) 106 | -------------------------------------------------------------------------------- /test/res.download.js: -------------------------------------------------------------------------------- 1 | 2 | var after = require('after'); 3 | var assert = require('assert'); 4 | var express = require('..'); 5 | var request = require('supertest'); 6 | 7 | describe('res', function(){ 8 | describe('.download(path)', function(){ 9 | it('should transfer as an attachment', function(done){ 10 | var app = express(); 11 | 12 | app.use(function(req, res){ 13 | res.download('test/fixtures/user.html'); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect('Content-Type', 'text/html; charset=UTF-8') 19 | .expect('Content-Disposition', 'attachment; filename="user.html"') 20 | .expect(200, '

      {{user.name}}

      ', done) 21 | }) 22 | }) 23 | 24 | describe('.download(path, filename)', function(){ 25 | it('should provide an alternate filename', function(done){ 26 | var app = express(); 27 | 28 | app.use(function(req, res){ 29 | res.download('test/fixtures/user.html', 'document'); 30 | }); 31 | 32 | request(app) 33 | .get('/') 34 | .expect('Content-Type', 'text/html; charset=UTF-8') 35 | .expect('Content-Disposition', 'attachment; filename="document"') 36 | .expect(200, done) 37 | }) 38 | }) 39 | 40 | describe('.download(path, fn)', function(){ 41 | it('should invoke the callback', function(done){ 42 | var app = express(); 43 | var cb = after(2, done); 44 | 45 | app.use(function(req, res){ 46 | res.download('test/fixtures/user.html', cb); 47 | }); 48 | 49 | request(app) 50 | .get('/') 51 | .expect('Content-Type', 'text/html; charset=UTF-8') 52 | .expect('Content-Disposition', 'attachment; filename="user.html"') 53 | .expect(200, cb); 54 | }) 55 | }) 56 | 57 | describe('.download(path, filename, fn)', function(){ 58 | it('should invoke the callback', function(done){ 59 | var app = express(); 60 | var cb = after(2, done); 61 | 62 | app.use(function(req, res){ 63 | res.download('test/fixtures/user.html', 'document', done); 64 | }); 65 | 66 | request(app) 67 | .get('/') 68 | .expect('Content-Type', 'text/html; charset=UTF-8') 69 | .expect('Content-Disposition', 'attachment; filename="document"') 70 | .expect(200, cb); 71 | }) 72 | }) 73 | 74 | describe('on failure', function(){ 75 | it('should invoke the callback', function(done){ 76 | var app = express(); 77 | 78 | app.use(function (req, res, next) { 79 | res.download('test/fixtures/foobar.html', function(err){ 80 | if (!err) return next(new Error('expected error')); 81 | res.send('got ' + err.status + ' ' + err.code); 82 | }); 83 | }); 84 | 85 | request(app) 86 | .get('/') 87 | .expect(200, 'got 404 ENOENT', done); 88 | }) 89 | 90 | it('should remove Content-Disposition', function(done){ 91 | var app = express() 92 | , calls = 0; 93 | 94 | app.use(function (req, res, next) { 95 | res.download('test/fixtures/foobar.html', function(err){ 96 | if (!err) return next(new Error('expected error')); 97 | res.end('failed'); 98 | }); 99 | }); 100 | 101 | request(app) 102 | .get('/') 103 | .expect(shouldNotHaveHeader('Content-Disposition')) 104 | .expect(200, 'failed', done); 105 | }) 106 | }) 107 | }) 108 | 109 | function shouldNotHaveHeader(header) { 110 | return function (res) { 111 | assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header); 112 | }; 113 | } 114 | -------------------------------------------------------------------------------- /test/req.query.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest'); 4 | 5 | describe('req', function(){ 6 | describe('.query', function(){ 7 | it('should default to {}', function(done){ 8 | var app = createApp(); 9 | 10 | request(app) 11 | .get('/') 12 | .expect(200, '{}', done); 13 | }); 14 | 15 | it('should default to parse complex keys', function (done) { 16 | var app = createApp(); 17 | 18 | request(app) 19 | .get('/?user[name]=tj') 20 | .expect(200, '{"user":{"name":"tj"}}', done); 21 | }); 22 | 23 | describe('when "query parser" is extended', function () { 24 | it('should parse complex keys', function (done) { 25 | var app = createApp('extended'); 26 | 27 | request(app) 28 | .get('/?user[name]=tj') 29 | .expect(200, '{"user":{"name":"tj"}}', done); 30 | }); 31 | 32 | it('should parse parameters with dots', function (done) { 33 | var app = createApp('extended'); 34 | 35 | request(app) 36 | .get('/?user.name=tj') 37 | .expect(200, '{"user.name":"tj"}', done); 38 | }); 39 | }); 40 | 41 | describe('when "query parser" is simple', function () { 42 | it('should not parse complex keys', function (done) { 43 | var app = createApp('simple'); 44 | 45 | request(app) 46 | .get('/?user%5Bname%5D=tj') 47 | .expect(200, '{"user[name]":"tj"}', done); 48 | }); 49 | }); 50 | 51 | describe('when "query parser" is a function', function () { 52 | it('should parse using function', function (done) { 53 | var app = createApp(function (str) { 54 | return {'length': (str || '').length}; 55 | }); 56 | 57 | request(app) 58 | .get('/?user%5Bname%5D=tj') 59 | .expect(200, '{"length":17}', done); 60 | }); 61 | }); 62 | 63 | describe('when "query parser" disabled', function () { 64 | it('should not parse query', function (done) { 65 | var app = createApp(false); 66 | 67 | request(app) 68 | .get('/?user%5Bname%5D=tj') 69 | .expect(200, '{}', done); 70 | }); 71 | }); 72 | 73 | describe('when "query parser" disabled', function () { 74 | it('should not parse complex keys', function (done) { 75 | var app = createApp(true); 76 | 77 | request(app) 78 | .get('/?user%5Bname%5D=tj') 79 | .expect(200, '{"user[name]":"tj"}', done); 80 | }); 81 | }); 82 | 83 | describe('when "query parser fn" is missing', function () { 84 | it('should act like "extended"', function (done) { 85 | var app = express(); 86 | 87 | delete app.settings['query parser']; 88 | delete app.settings['query parser fn']; 89 | 90 | app.use(function (req, res) { 91 | res.send(req.query); 92 | }); 93 | 94 | request(app) 95 | .get('/?user[name]=tj&user.name=tj') 96 | .expect(200, '{"user":{"name":"tj"},"user.name":"tj"}', done); 97 | }); 98 | }); 99 | 100 | describe('when "query parser" an unknown value', function () { 101 | it('should throw', function () { 102 | createApp.bind(null, 'bogus').should.throw(/unknown value.*query parser/); 103 | }); 104 | }); 105 | }) 106 | }) 107 | 108 | function createApp(setting) { 109 | var app = express(); 110 | 111 | if (setting !== undefined) { 112 | app.set('query parser', setting); 113 | } 114 | 115 | app.use(function (req, res) { 116 | res.send(req.query); 117 | }); 118 | 119 | return app; 120 | } 121 | -------------------------------------------------------------------------------- /test/req.hostname.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('../') 3 | , request = require('supertest') 4 | , assert = require('assert'); 5 | 6 | describe('req', function(){ 7 | describe('.hostname', function(){ 8 | it('should return the Host when present', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.end(req.hostname); 13 | }); 14 | 15 | request(app) 16 | .post('/') 17 | .set('Host', 'example.com') 18 | .expect('example.com', done); 19 | }) 20 | 21 | it('should strip port number', function(done){ 22 | var app = express(); 23 | 24 | app.use(function(req, res){ 25 | res.end(req.hostname); 26 | }); 27 | 28 | request(app) 29 | .post('/') 30 | .set('Host', 'example.com:3000') 31 | .expect('example.com', done); 32 | }) 33 | 34 | it('should return undefined otherwise', function(done){ 35 | var app = express(); 36 | 37 | app.use(function(req, res){ 38 | req.headers.host = null; 39 | res.end(String(req.hostname)); 40 | }); 41 | 42 | request(app) 43 | .post('/') 44 | .expect('undefined', done); 45 | }) 46 | 47 | it('should work with IPv6 Host', function(done){ 48 | var app = express(); 49 | 50 | app.use(function(req, res){ 51 | res.end(req.hostname); 52 | }); 53 | 54 | request(app) 55 | .post('/') 56 | .set('Host', '[::1]') 57 | .expect('[::1]', done); 58 | }) 59 | 60 | it('should work with IPv6 Host and port', function(done){ 61 | var app = express(); 62 | 63 | app.use(function(req, res){ 64 | res.end(req.hostname); 65 | }); 66 | 67 | request(app) 68 | .post('/') 69 | .set('Host', '[::1]:3000') 70 | .expect('[::1]', done); 71 | }) 72 | 73 | describe('when "trust proxy" is enabled', function(){ 74 | it('should respect X-Forwarded-Host', function(done){ 75 | var app = express(); 76 | 77 | app.enable('trust proxy'); 78 | 79 | app.use(function(req, res){ 80 | res.end(req.hostname); 81 | }); 82 | 83 | request(app) 84 | .get('/') 85 | .set('Host', 'localhost') 86 | .set('X-Forwarded-Host', 'example.com:3000') 87 | .expect('example.com', done); 88 | }) 89 | 90 | it('should ignore X-Forwarded-Host if socket addr not trusted', function(done){ 91 | var app = express(); 92 | 93 | app.set('trust proxy', '10.0.0.1'); 94 | 95 | app.use(function(req, res){ 96 | res.end(req.hostname); 97 | }); 98 | 99 | request(app) 100 | .get('/') 101 | .set('Host', 'localhost') 102 | .set('X-Forwarded-Host', 'example.com') 103 | .expect('localhost', done); 104 | }) 105 | 106 | it('should default to Host', function(done){ 107 | var app = express(); 108 | 109 | app.enable('trust proxy'); 110 | 111 | app.use(function(req, res){ 112 | res.end(req.hostname); 113 | }); 114 | 115 | request(app) 116 | .get('/') 117 | .set('Host', 'example.com') 118 | .expect('example.com', done); 119 | }) 120 | }) 121 | 122 | describe('when "trust proxy" is disabled', function(){ 123 | it('should ignore X-Forwarded-Host', function(done){ 124 | var app = express(); 125 | 126 | app.use(function(req, res){ 127 | res.end(req.hostname); 128 | }); 129 | 130 | request(app) 131 | .get('/') 132 | .set('Host', 'localhost') 133 | .set('X-Forwarded-Host', 'evil') 134 | .expect('localhost', done); 135 | }) 136 | }) 137 | }) 138 | }) 139 | -------------------------------------------------------------------------------- /examples/view-locals/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var express = require('../..'); 6 | var path = require('path'); 7 | var User = require('./user'); 8 | var app = express(); 9 | 10 | app.set('views', path.join(__dirname, 'views')); 11 | app.set('view engine', 'ejs'); 12 | 13 | // filter ferrets only 14 | 15 | function ferrets(user) { 16 | return user.species == 'ferret'; 17 | } 18 | 19 | // naive nesting approach, 20 | // delegating errors to next(err) 21 | // in order to expose the "count" 22 | // and "users" locals 23 | 24 | app.get('/', function(req, res, next){ 25 | User.count(function(err, count){ 26 | if (err) return next(err); 27 | User.all(function(err, users){ 28 | if (err) return next(err); 29 | res.render('index', { 30 | title: 'Users', 31 | count: count, 32 | users: users.filter(ferrets) 33 | }); 34 | }) 35 | }) 36 | }); 37 | 38 | 39 | 40 | 41 | // this approach is cleaner, 42 | // less nesting and we have 43 | // the variables available 44 | // on the request object 45 | 46 | function count(req, res, next) { 47 | User.count(function(err, count){ 48 | if (err) return next(err); 49 | req.count = count; 50 | next(); 51 | }) 52 | } 53 | 54 | function users(req, res, next) { 55 | User.all(function(err, users){ 56 | if (err) return next(err); 57 | req.users = users; 58 | next(); 59 | }) 60 | } 61 | 62 | app.get('/middleware', count, users, function(req, res, next){ 63 | res.render('index', { 64 | title: 'Users', 65 | count: req.count, 66 | users: req.users.filter(ferrets) 67 | }); 68 | }); 69 | 70 | 71 | 72 | 73 | // this approach is much like the last 74 | // however we're explicitly exposing 75 | // the locals within each middleware 76 | // 77 | // note that this may not always work 78 | // well, for example here we filter 79 | // the users in the middleware, which 80 | // may not be ideal for our application. 81 | // so in that sense the previous example 82 | // is more flexible with `req.users`. 83 | 84 | function count2(req, res, next) { 85 | User.count(function(err, count){ 86 | if (err) return next(err); 87 | res.locals.count = count; 88 | next(); 89 | }) 90 | } 91 | 92 | function users2(req, res, next) { 93 | User.all(function(err, users){ 94 | if (err) return next(err); 95 | res.locals.users = users.filter(ferrets); 96 | next(); 97 | }) 98 | } 99 | 100 | app.get('/middleware-locals', count2, users2, function(req, res, next){ 101 | // you can see now how we have much less 102 | // to pass to res.render(). If we have 103 | // several routes related to users this 104 | // can be a great productivity booster 105 | res.render('index', { title: 'Users' }); 106 | }); 107 | 108 | // keep in mind that middleware may be placed anywhere 109 | // and in various combinations, so if you have locals 110 | // that you wish to make available to all subsequent 111 | // middleware/routes you can do something like this: 112 | 113 | /* 114 | 115 | app.use(function(req, res, next){ 116 | res.locals.user = req.user; 117 | res.locals.sess = req.session; 118 | next(); 119 | }); 120 | 121 | */ 122 | 123 | // or suppose you have some /admin 124 | // "global" local variables: 125 | 126 | /* 127 | 128 | app.use('/api', function(req, res, next){ 129 | res.locals.user = req.user; 130 | res.locals.sess = req.session; 131 | next(); 132 | }); 133 | 134 | */ 135 | 136 | // the following is effectively the same, 137 | // but uses a route instead: 138 | 139 | /* 140 | 141 | app.all('/api/*', function(req, res, next){ 142 | res.locals.user = req.user; 143 | res.locals.sess = req.session; 144 | next(); 145 | }); 146 | 147 | */ 148 | 149 | /* istanbul ignore next */ 150 | if (!module.parent) { 151 | app.listen(3000); 152 | console.log('Express started on port 3000'); 153 | } 154 | -------------------------------------------------------------------------------- /test/acceptance/mvc.js: -------------------------------------------------------------------------------- 1 | 2 | var request = require('supertest') 3 | , app = require('../../examples/mvc'); 4 | 5 | describe('mvc', function(){ 6 | describe('GET /', function(){ 7 | it('should redirect to /users', function(done){ 8 | request(app) 9 | .get('/') 10 | .expect('Location', '/users') 11 | .expect(302, done) 12 | }) 13 | }) 14 | 15 | describe('GET /pet/0', function(){ 16 | it('should get pet', function(done){ 17 | request(app) 18 | .get('/pet/0') 19 | .expect(200, /Tobi/, done) 20 | }) 21 | }) 22 | 23 | describe('GET /pet/0/edit', function(){ 24 | it('should get pet edit page', function(done){ 25 | request(app) 26 | .get('/pet/0/edit') 27 | .expect(/Users<\/h1>/) 52 | .expect(/>TJGuillermoNathanTJ edit/, done) 65 | }) 66 | 67 | it('should display the users pets', function(done){ 68 | request(app) 69 | .get('/user/0') 70 | .expect(/\/pet\/0">Tobi/) 71 | .expect(/\/pet\/1">Loki/) 72 | .expect(/\/pet\/2">Jane/) 73 | .expect(200, done) 74 | }) 75 | }) 76 | 77 | describe('when not present', function(){ 78 | it('should 404', function(done){ 79 | request(app) 80 | .get('/user/123') 81 | .expect(404, done); 82 | }) 83 | }) 84 | }) 85 | 86 | describe('GET /user/:id/edit', function(){ 87 | it('should display the edit form', function(done){ 88 | request(app) 89 | .get('/user/1/edit') 90 | .expect(/Guillermo/) 91 | .expect(200, /