├── test ├── fixtures │ ├── .name │ ├── empty.txt │ ├── broken.send │ ├── name.txt │ ├── % of dogs.txt │ ├── nums.txt │ ├── snow ☃ │ │ └── .gitkeep │ ├── todo.txt │ ├── name.tmpl │ ├── pets │ │ └── names.txt │ ├── users │ │ ├── tobi.txt │ │ └── index.html │ ├── blog │ │ ├── index.html │ │ └── post │ │ │ └── index.tmpl │ ├── todo.html │ ├── user.html │ ├── user.tmpl │ ├── email.tmpl │ ├── default_layout │ │ ├── name.tmpl │ │ └── user.tmpl │ └── local_layout │ │ └── user.tmpl ├── support │ ├── env.js │ ├── tmpl.js │ └── utils.js ├── app.listen.js ├── app.del.js ├── regression.js ├── req.path.js ├── res.get.js ├── acceptance │ ├── ejs.js │ ├── markdown.js │ ├── hello-world.js │ ├── error.js │ ├── cookie-sessions.js │ ├── route-map.js │ ├── params.js │ ├── multi-router.js │ ├── vhost.js │ ├── downloads.js │ ├── content-negotiation.js │ ├── cookies.js │ ├── resource.js │ ├── error-pages.js │ ├── route-separation.js │ ├── auth.js │ └── web-service.js ├── req.route.js ├── res.sendStatus.js ├── app.locals.js ├── res.locals.js ├── app.all.js ├── req.signedCookies.js ├── res.clearCookie.js ├── req.acceptsEncoding.js ├── req.acceptsEncodings.js ├── req.xhr.js ├── middleware.basic.js ├── req.fresh.js ├── req.stale.js ├── res.type.js ├── req.acceptsCharset.js ├── req.acceptsCharsets.js ├── app.route.js ├── res.links.js ├── req.param.js ├── req.get.js ├── req.acceptsLanguage.js ├── req.acceptsLanguages.js ├── app.head.js ├── app.routes.error.js ├── req.ips.js ├── res.attachment.js ├── res.vary.js ├── req.baseUrl.js ├── app.engine.js ├── req.range.js ├── exports.js ├── req.secure.js ├── res.location.js ├── req.protocol.js ├── app.options.js ├── res.append.js ├── req.ip.js ├── app.js ├── res.set.js ├── req.accepts.js ├── utils.js ├── app.response.js └── app.request.js ├── .eslintignore ├── 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 │ │ ├── users.html │ │ └── header.html │ ├── public │ │ └── stylesheets │ │ │ └── style.css │ └── index.js ├── downloads │ ├── files │ │ ├── amazing.txt │ │ ├── notes │ │ │ └── groceries.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 │ │ ├── users │ │ │ ├── view.ejs │ │ │ ├── index.ejs │ │ │ └── edit.ejs │ │ ├── index.ejs │ │ ├── header.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 ├── cookie-sessions │ └── index.js ├── session │ ├── index.js │ └── redis.js ├── vhost │ └── index.js ├── online │ └── index.js ├── view-constructor │ ├── github-view.js │ └── index.js ├── cookies │ └── index.js ├── error │ └── index.js ├── params │ └── index.js ├── multipart │ └── index.js ├── route-map │ └── index.js ├── README.md ├── resource │ └── index.js ├── route-middleware │ └── index.js └── web-service │ └── index.js ├── .editorconfig ├── .eslintrc.yml ├── index.js ├── .gitignore ├── benchmarks ├── run ├── middleware.js └── Makefile ├── lib ├── middleware │ ├── init.js │ └── query.js └── express.js ├── LICENSE ├── Security.md ├── Collaborator-Guide.md └── package.json /test/fixtures/.name: -------------------------------------------------------------------------------- 1 | tobi -------------------------------------------------------------------------------- /test/fixtures/empty.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/broken.send: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/name.txt: -------------------------------------------------------------------------------- 1 | tobi -------------------------------------------------------------------------------- /test/fixtures/% of dogs.txt: -------------------------------------------------------------------------------- 1 | 20% -------------------------------------------------------------------------------- /test/fixtures/nums.txt: -------------------------------------------------------------------------------- 1 | 123456789 -------------------------------------------------------------------------------- /test/fixtures/snow ☃/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/todo.txt: -------------------------------------------------------------------------------- 1 | - groceries -------------------------------------------------------------------------------- /test/fixtures/name.tmpl: -------------------------------------------------------------------------------- 1 |

$name

-------------------------------------------------------------------------------- /test/fixtures/pets/names.txt: -------------------------------------------------------------------------------- 1 | tobi,loki -------------------------------------------------------------------------------- /test/fixtures/users/tobi.txt: -------------------------------------------------------------------------------- 1 | ferret -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | -------------------------------------------------------------------------------- /examples/static-files/public/hello.txt: -------------------------------------------------------------------------------- 1 | hey -------------------------------------------------------------------------------- /test/fixtures/blog/index.html: -------------------------------------------------------------------------------- 1 | index -------------------------------------------------------------------------------- /test/fixtures/todo.html: -------------------------------------------------------------------------------- 1 |
  • groceries
  • -------------------------------------------------------------------------------- /test/fixtures/user.html: -------------------------------------------------------------------------------- 1 |

    {{user.name}}

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

    $user.name

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

    This is an email

    -------------------------------------------------------------------------------- /examples/static-files/public/js/app.js: -------------------------------------------------------------------------------- 1 | // foo 2 | -------------------------------------------------------------------------------- /test/fixtures/blog/post/index.tmpl: -------------------------------------------------------------------------------- 1 |

    blog post

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

    $name

    -------------------------------------------------------------------------------- /test/fixtures/users/index.html: -------------------------------------------------------------------------------- 1 |

    tobi, loki, jane

    -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /examples/route-separation/views/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /examples/downloads/files/notes/groceries.txt: -------------------------------------------------------------------------------- 1 | * milk 2 | * eggs 3 | * bread 4 | -------------------------------------------------------------------------------- /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 = 'body-parser,express'; 4 | -------------------------------------------------------------------------------- /examples/error-pages/views/404.ejs: -------------------------------------------------------------------------------- 1 | <%- include('error_header') -%> 2 |

    Cannot find <%= url %>

    3 | <%- include('footer') -%> 4 | -------------------------------------------------------------------------------- /examples/mvc/controllers/main/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | exports.index = function(req, res){ 4 | res.redirect('/users'); 5 | }; 6 | -------------------------------------------------------------------------------- /examples/ejs/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px 80px; 3 | font: 14px "Helvetica Neue", "Lucida Grande", Arial, sans-serif; 4 | } 5 | -------------------------------------------------------------------------------- /examples/route-separation/site.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | exports.index = function(req, res){ 4 | res.render('index', { title: 'Route Separation Example' }); 5 | }; 6 | -------------------------------------------------------------------------------- /examples/content-negotiation/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var users = []; 4 | 5 | users.push({ name: 'Tobi' }); 6 | users.push({ name: 'Loki' }); 7 | users.push({ name: 'Jane' }); 8 | 9 | module.exports = users; 10 | -------------------------------------------------------------------------------- /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/error-pages/views/error_header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Error 7 | 8 | 9 | 10 |

    An error occurred!

    11 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | 3 | rules: 4 | eol-last: error 5 | eqeqeq: [error, allow-null] 6 | indent: [error, 2, { MemberExpression: "off", SwitchCase: 1 }] 7 | no-trailing-spaces: error 8 | no-unused-vars: [error, { vars: all, args: none, ignoreRestSiblings: true }] 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # npm 2 | node_modules 3 | package-lock.json 4 | *.log 5 | *.gz 6 | 7 | # Coveralls 8 | .nyc_output 9 | coverage 10 | 11 | # Benchmarking 12 | benchmarks/graphs 13 | 14 | # ignore additional files using core.excludesFile 15 | # https://git-scm.com/docs/gitignore 16 | -------------------------------------------------------------------------------- /examples/mvc/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 16px "Helvetica Neue", Helvetica, Arial, sans-serif; 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/ejs/views/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/route-separation/views/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/app.listen.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | 5 | describe('app.listen()', function(){ 6 | it('should wrap with an HTTP server', function(done){ 7 | var app = express(); 8 | 9 | var server = app.listen(0, function () { 10 | server.close(done) 11 | }); 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /benchmarks/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo 4 | MW=$1 node $2 & 5 | pid=$! 6 | 7 | echo " $3 connections" 8 | 9 | sleep 2 10 | 11 | wrk 'http://localhost:3333/?foo[bar]=baz' \ 12 | -d 3 \ 13 | -c $3 \ 14 | -t 8 \ 15 | | grep 'Requests/sec\|Latency' \ 16 | | awk '{ print " " $2 }' 17 | 18 | kill $pid 19 | -------------------------------------------------------------------------------- /examples/hello-world/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../../'); 4 | 5 | var app = module.exports = express() 6 | 7 | app.get('/', function(req, res){ 8 | res.send('Hello World'); 9 | }); 10 | 11 | /* istanbul ignore next */ 12 | if (!module.parent) { 13 | app.listen(3000); 14 | console.log('Express started on port 3000'); 15 | } 16 | -------------------------------------------------------------------------------- /examples/multi-router/controllers/api_v1.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../../..'); 4 | 5 | var apiv1 = express.Router(); 6 | 7 | apiv1.get('/', function(req, res) { 8 | res.send('Hello from APIv1 root route.'); 9 | }); 10 | 11 | apiv1.get('/users', function(req, res) { 12 | res.send('List of APIv1 users.'); 13 | }); 14 | 15 | module.exports = apiv1; 16 | -------------------------------------------------------------------------------- /examples/multi-router/controllers/api_v2.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../../..'); 4 | 5 | var apiv2 = express.Router(); 6 | 7 | apiv2.get('/', function(req, res) { 8 | res.send('Hello from APIv2 root route.'); 9 | }); 10 | 11 | apiv2.get('/users', function(req, res) { 12 | res.send('List of APIv2 users.'); 13 | }); 14 | 15 | module.exports = apiv2; 16 | -------------------------------------------------------------------------------- /examples/route-separation/post.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Fake posts database 4 | 5 | var posts = [ 6 | { title: 'Foo', body: 'some foo bar' }, 7 | { title: 'Foo bar', body: 'more foo bar' }, 8 | { title: 'Foo bar baz', body: 'more foo bar baz' } 9 | ]; 10 | 11 | exports.list = function(req, res){ 12 | res.render('posts', { title: 'Posts', posts: posts }); 13 | }; 14 | -------------------------------------------------------------------------------- /examples/mvc/views/404.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Not Found 7 | 8 | 9 | 10 |

    404: Not Found

    11 |

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

    12 | 13 | 14 | -------------------------------------------------------------------------------- /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/mvc/views/5xx.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Internal Server Error 7 | 8 | 9 | 10 |

    500: Internal Server Error

    11 |

    Looks like something blew up!

    12 | 13 | 14 | -------------------------------------------------------------------------------- /benchmarks/middleware.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('..'); 3 | var app = express(); 4 | 5 | // number of middleware 6 | 7 | var n = parseInt(process.env.MW || '1', 10); 8 | console.log(' %s middleware', n); 9 | 10 | while (n--) { 11 | app.use(function(req, res, next){ 12 | next(); 13 | }); 14 | } 15 | 16 | app.use(function(req, res){ 17 | res.send('Hello World') 18 | }); 19 | 20 | app.listen(3333); 21 | -------------------------------------------------------------------------------- /benchmarks/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | @./run 1 middleware 50 4 | @./run 5 middleware 50 5 | @./run 10 middleware 50 6 | @./run 15 middleware 50 7 | @./run 20 middleware 50 8 | @./run 30 middleware 50 9 | @./run 50 middleware 50 10 | @./run 100 middleware 50 11 | @./run 10 middleware 100 12 | @./run 10 middleware 250 13 | @./run 10 middleware 500 14 | @./run 10 middleware 1000 15 | @echo 16 | 17 | .PHONY: all 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/mvc/controllers/pet/views/show.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= pet.name %> 8 | 9 | 10 | 11 |

    <%= pet.name %> edit

    12 | 13 |

    You are viewing <%= pet.name %>

    14 | 15 | 16 | -------------------------------------------------------------------------------- /test/app.del.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('app.del()', function(){ 7 | it('should alias app.delete()', function(done){ 8 | var app = express(); 9 | 10 | app.del('/tobi', function(req, res){ 11 | res.end('deleted tobi!'); 12 | }); 13 | 14 | request(app) 15 | .del('/tobi') 16 | .expect('deleted tobi!', done); 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /examples/error-pages/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Custom Pages Example 7 | 8 | 9 | 10 |

    My Site

    11 |

    Pages Example

    12 | 13 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/search/public/client.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var search = document.querySelector('[type=search]'); 4 | var code = document.querySelector('pre'); 5 | 6 | search.addEventListener('keyup', function(){ 7 | var xhr = new XMLHttpRequest; 8 | xhr.open('GET', '/search/' + search.value, true); 9 | xhr.onreadystatechange = function(){ 10 | if (xhr.readyState === 4) { 11 | code.textContent = xhr.responseText; 12 | } 13 | }; 14 | xhr.send(); 15 | }, false); 16 | -------------------------------------------------------------------------------- /test/regression.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('throw after .end()', function(){ 7 | it('should fail gracefully', function(done){ 8 | var app = express(); 9 | 10 | app.get('/', function(req, res){ 11 | res.end('yay'); 12 | throw new Error('boom'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('yay') 18 | .expect(200, done); 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /examples/multi-router/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../..'); 4 | 5 | var app = module.exports = express(); 6 | 7 | app.use('/api/v1', require('./controllers/api_v1')); 8 | app.use('/api/v2', require('./controllers/api_v2')); 9 | 10 | app.get('/', function(req, res) { 11 | res.send('Hello from root route.') 12 | }); 13 | 14 | /* istanbul ignore next */ 15 | if (!module.parent) { 16 | app.listen(3000); 17 | console.log('Express started on port 3000'); 18 | } 19 | -------------------------------------------------------------------------------- /examples/content-negotiation/users.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var users = require('./db'); 4 | 5 | exports.html = function(req, res){ 6 | res.send(''); 9 | }; 10 | 11 | exports.text = function(req, res){ 12 | res.send(users.map(function(user){ 13 | return ' - ' + user.name + '\n'; 14 | }).join('')); 15 | }; 16 | 17 | exports.json = function(req, res){ 18 | res.json(users); 19 | }; 20 | -------------------------------------------------------------------------------- /examples/auth/views/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= title %> 7 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/list.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Users 8 | 9 | 10 |

    Users

    11 |

    Click a user below to view their pets.

    12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/mvc/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // faux database 4 | 5 | var pets = exports.pets = []; 6 | 7 | pets.push({ name: 'Tobi', id: 0 }); 8 | pets.push({ name: 'Loki', id: 1 }); 9 | pets.push({ name: 'Jane', id: 2 }); 10 | pets.push({ name: 'Raul', id: 3 }); 11 | 12 | var users = exports.users = []; 13 | 14 | users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 }); 15 | users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 }); 16 | users.push({ name: 'Nathan', pets: [], id: 2 }); 17 | -------------------------------------------------------------------------------- /test/req.path.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.path', function(){ 8 | it('should return the parsed pathname', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.end(req.path); 13 | }); 14 | 15 | request(app) 16 | .get('/login?redirect=/post/1/comments') 17 | .expect('/login', done); 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /examples/mvc/controllers/pet/views/edit.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Edit <%= pet.name %> 8 | 9 | 10 | 11 |

    <%= pet.name %>

    12 |
    13 | 14 | 15 |
    16 | 17 | 18 | -------------------------------------------------------------------------------- /test/res.get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..'); 4 | var request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.get(field)', function(){ 8 | it('should get the response header field', function (done) { 9 | var app = express(); 10 | 11 | app.use(function (req, res) { 12 | res.setHeader('Content-Type', 'text/x-foo'); 13 | res.send(res.get('Content-Type')); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect(200, 'text/x-foo', done); 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user-pet/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var db = require('../../db'); 8 | 9 | exports.name = 'pet'; 10 | exports.prefix = '/user/:user_id'; 11 | 12 | exports.create = function(req, res, next){ 13 | var id = req.params.user_id; 14 | var user = db.users[id]; 15 | var body = req.body; 16 | if (!user) return next('route'); 17 | var pet = { name: body.pet.name }; 18 | pet.id = db.pets.push(pet) - 1; 19 | user.pets.push(pet); 20 | res.message('Added pet ' + body.pet.name); 21 | res.redirect('/user/' + id); 22 | }; 23 | -------------------------------------------------------------------------------- /examples/search/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Search example 7 | 13 | 14 | 15 |

    Search

    16 |

    Try searching for "ferret" or "cat".

    17 | 18 |
    
    19 |   
    20 | 
    21 | 
    22 | 
    
    
    --------------------------------------------------------------------------------
    /test/acceptance/hello-world.js:
    --------------------------------------------------------------------------------
     1 | 
     2 | var app = require('../../examples/hello-world')
     3 | var request = require('supertest')
     4 | 
     5 | describe('hello-world', function () {
     6 |   describe('GET /', function () {
     7 |     it('should respond with hello world', function (done) {
     8 |       request(app)
     9 |         .get('/')
    10 |         .expect(200, 'Hello World', done)
    11 |     })
    12 |   })
    13 | 
    14 |   describe('GET /missing', function () {
    15 |     it('should respond with 404', function (done) {
    16 |       request(app)
    17 |         .get('/missing')
    18 |         .expect(404, done)
    19 |     })
    20 |   })
    21 | })
    22 | 
    
    
    --------------------------------------------------------------------------------
    /examples/view-locals/views/index.ejs:
    --------------------------------------------------------------------------------
     1 | 
     2 | 
     3 |   
     4 |     
     5 |     
     6 |     <%= title %>
     7 |     
    13 |   
    14 |   
    15 |     

    <%= title %>

    16 | <% users.forEach(function(user) { %> 17 |
  • <%= user.name %> is a <% user.age %> year old <%= user.species %>
  • 18 | <% }); %> 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/auth/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | <%- include('head', { title: 'Authentication Example' }) -%> 3 | 4 |

    Login

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

    9 | 10 | 11 |

    12 |

    13 | 14 | 15 |

    16 |

    17 | 18 |

    19 |
    20 | 21 | <%- include('foot') -%> 22 | -------------------------------------------------------------------------------- /examples/cookie-sessions/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var cookieSession = require('cookie-session'); 8 | var express = require('../../'); 9 | 10 | var app = module.exports = express(); 11 | 12 | // add req.session cookie support 13 | app.use(cookieSession({ secret: 'manny is cool' })); 14 | 15 | // do something with the session 16 | app.use(count); 17 | 18 | // custom middleware 19 | function count(req, res) { 20 | req.session.count = (req.session.count || 0) + 1 21 | res.send('viewed ' + req.session.count + ' times\n') 22 | } 23 | 24 | /* istanbul ignore next */ 25 | if (!module.parent) { 26 | app.listen(3000); 27 | console.log('Express started on port 3000'); 28 | } 29 | -------------------------------------------------------------------------------- /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 | }) 30 | -------------------------------------------------------------------------------- /examples/mvc/controllers/pet/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var db = require('../../db'); 8 | 9 | exports.engine = 'ejs'; 10 | 11 | exports.before = function(req, res, next){ 12 | var pet = db.pets[req.params.pet_id]; 13 | if (!pet) return next('route'); 14 | req.pet = pet; 15 | next(); 16 | }; 17 | 18 | exports.show = function(req, res, next){ 19 | res.render('show', { pet: req.pet }); 20 | }; 21 | 22 | exports.edit = function(req, res, next){ 23 | res.render('edit', { pet: req.pet }); 24 | }; 25 | 26 | exports.update = function(req, res, next){ 27 | var body = req.body; 28 | req.pet.name = body.pet.name; 29 | res.message('Information updated!'); 30 | res.redirect('/pet/' + req.pet.id); 31 | }; 32 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/show.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{user.name}} 8 | 9 | 10 |

    {{user.name}} edit

    11 | 12 | {{#if hasMessages}} 13 | 18 | {{/if}} 19 | 20 | {{#if user.pets.length}} 21 |

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

    22 | 27 | {{else}} 28 |

    No pets!

    29 | {{/if}} 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/req.route.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.route', function(){ 8 | it('should be the executed Route', function(done){ 9 | var app = express(); 10 | 11 | app.get('/user/:id/:op?', function(req, res, next){ 12 | res.header('path-1', req.route.path) 13 | next(); 14 | }); 15 | 16 | app.get('/user/:id/edit', function(req, res){ 17 | res.header('path-2', req.route.path) 18 | res.end(); 19 | }); 20 | 21 | request(app) 22 | .get('/user/12/edit') 23 | .expect('path-1', '/user/:id/:op?') 24 | .expect('path-2', '/user/:id/edit') 25 | .expect(200, done) 26 | }) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/res.sendStatus.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 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/app.locals.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var express = require('../') 5 | 6 | describe('app', function(){ 7 | describe('.locals', function () { 8 | it('should default object', function () { 9 | var app = express() 10 | assert.ok(app.locals) 11 | assert.strictEqual(typeof app.locals, 'object') 12 | }) 13 | 14 | describe('.settings', function () { 15 | it('should contain app settings ', function () { 16 | var app = express() 17 | app.set('title', 'Express') 18 | assert.ok(app.locals.settings) 19 | assert.strictEqual(typeof app.locals.settings, 'object') 20 | assert.strictEqual(app.locals.settings, app.settings) 21 | assert.strictEqual(app.locals.settings.title, 'Express') 22 | }) 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/views/edit.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Edit {{user.name}} 8 | 9 | 10 |

    {{user.name}}

    11 |
    12 | 15 | 16 | 17 |
    18 | 19 |
    20 | 23 | 24 | 25 |
    26 | 27 | 28 | -------------------------------------------------------------------------------- /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 | err.name = 'RenderError' 17 | } 18 | 19 | callback(err, str); 20 | } 21 | 22 | fs.readFile(fileName, 'utf8', onReadFile); 23 | }; 24 | 25 | function generateVariableLookup(data) { 26 | return function variableLookup(str, path) { 27 | var parts = path.split('.'); 28 | var value = data; 29 | 30 | for (var i = 0; i < parts.length; i++) { 31 | value = value[parts[i]]; 32 | } 33 | 34 | return value; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /examples/view-locals/user.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = User; 4 | 5 | // faux model 6 | 7 | function User(name, age, species) { 8 | this.name = name; 9 | this.age = age; 10 | this.species = species; 11 | } 12 | 13 | User.all = function(fn){ 14 | // process.nextTick makes sure this function API 15 | // behaves in an asynchronous manner, like if it 16 | // was a real DB query to read all users. 17 | process.nextTick(function(){ 18 | fn(null, users); 19 | }); 20 | }; 21 | 22 | User.count = function(fn){ 23 | process.nextTick(function(){ 24 | fn(null, users.length); 25 | }); 26 | }; 27 | 28 | // faux database 29 | 30 | var users = []; 31 | 32 | users.push(new User('Tobi', 2, 'ferret')); 33 | users.push(new User('Loki', 1, 'ferret')); 34 | users.push(new User('Jane', 6, 'ferret')); 35 | users.push(new User('Luna', 1, 'cat')); 36 | users.push(new User('Manny', 1, 'cat')); 37 | -------------------------------------------------------------------------------- /test/res.locals.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.locals', function(){ 8 | it('should be empty by default', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.json(res.locals) 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.json(res.locals) 34 | }); 35 | 36 | request(app) 37 | .get('/') 38 | .expect(200, { foo: 'bar' }, done) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /test/app.all.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var after = require('after') 4 | var express = require('../') 5 | , request = require('supertest'); 6 | 7 | describe('app.all()', function(){ 8 | it('should add a router per method', function(done){ 9 | var app = express(); 10 | var cb = after(2, done) 11 | 12 | app.all('/tobi', function(req, res){ 13 | res.end(req.method); 14 | }); 15 | 16 | request(app) 17 | .put('/tobi') 18 | .expect(200, 'PUT', cb) 19 | 20 | request(app) 21 | .get('/tobi') 22 | .expect(200, 'GET', cb) 23 | }) 24 | 25 | it('should run the callback for a method just once', function(done){ 26 | var app = express() 27 | , n = 0; 28 | 29 | app.all('/*', function(req, res, next){ 30 | if (n++) return done(new Error('DELETE called several times')); 31 | next(); 32 | }); 33 | 34 | request(app) 35 | .del('/tobi') 36 | .expect(404, done); 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /examples/session/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // install redis first: 4 | // https://redis.io/ 5 | 6 | // then: 7 | // $ npm install redis 8 | // $ redis-server 9 | 10 | var express = require('../..'); 11 | var session = require('express-session'); 12 | 13 | var app = express(); 14 | 15 | // Populates req.session 16 | app.use(session({ 17 | resave: false, // don't save session if unmodified 18 | saveUninitialized: false, // don't create session until something stored 19 | secret: 'keyboard cat' 20 | })); 21 | 22 | app.get('/', function(req, res){ 23 | var body = ''; 24 | if (req.session.views) { 25 | ++req.session.views; 26 | } else { 27 | req.session.views = 1; 28 | body += '

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

    '; 29 | } 30 | res.send(body + '

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

    '); 31 | }); 32 | 33 | /* istanbul ignore next */ 34 | if (!module.parent) { 35 | app.listen(3000); 36 | console.log('Express started on port 3000'); 37 | } 38 | -------------------------------------------------------------------------------- /test/req.signedCookies.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest') 5 | , cookieParser = require('cookie-parser') 6 | 7 | describe('req', function(){ 8 | describe('.signedCookies', function(){ 9 | it('should return a signed JSON cookie', function(done){ 10 | var app = express(); 11 | 12 | app.use(cookieParser('secret')); 13 | 14 | app.use(function(req, res){ 15 | if (req.path === '/set') { 16 | res.cookie('obj', { foo: 'bar' }, { signed: true }); 17 | res.end(); 18 | } else { 19 | res.send(req.signedCookies); 20 | } 21 | }); 22 | 23 | request(app) 24 | .get('/set') 25 | .end(function(err, res){ 26 | if (err) return done(err); 27 | var cookie = res.header['set-cookie']; 28 | 29 | request(app) 30 | .get('/') 31 | .set('Cookie', cookie) 32 | .expect(200, { obj: { foo: 'bar' } }, done) 33 | }); 34 | }) 35 | }) 36 | }) 37 | 38 | -------------------------------------------------------------------------------- /test/res.clearCookie.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.clearCookie(name)', function(){ 8 | it('should set a cookie passed expiry', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.clearCookie('sid').end(); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('Set-Cookie', 'sid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT') 18 | .expect(200, done) 19 | }) 20 | }) 21 | 22 | describe('.clearCookie(name, options)', function(){ 23 | it('should set the given params', function(done){ 24 | var app = express(); 25 | 26 | app.use(function(req, res){ 27 | res.clearCookie('sid', { path: '/admin' }).end(); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .expect('Set-Cookie', 'sid=; Path=/admin; Expires=Thu, 01 Jan 1970 00:00:00 GMT') 33 | .expect(200, done) 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /examples/mvc/controllers/user/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var db = require('../../db'); 8 | 9 | exports.engine = 'hbs'; 10 | 11 | exports.before = function(req, res, next){ 12 | var id = req.params.user_id; 13 | if (!id) return next(); 14 | // pretend to query a database... 15 | process.nextTick(function(){ 16 | req.user = db.users[id]; 17 | // cant find that user 18 | if (!req.user) return next('route'); 19 | // found it, move on to the routes 20 | next(); 21 | }); 22 | }; 23 | 24 | exports.list = function(req, res, next){ 25 | res.render('list', { users: db.users }); 26 | }; 27 | 28 | exports.edit = function(req, res, next){ 29 | res.render('edit', { user: req.user }); 30 | }; 31 | 32 | exports.show = function(req, res, next){ 33 | res.render('show', { user: req.user }); 34 | }; 35 | 36 | exports.update = function(req, res, next){ 37 | var body = req.body; 38 | req.user.name = body.user.name; 39 | res.message('Information updated!'); 40 | res.redirect('/user/' + req.user.id); 41 | }; 42 | -------------------------------------------------------------------------------- /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 merge = require('utils-merge') 16 | var parseUrl = require('parseurl'); 17 | var qs = require('qs'); 18 | 19 | /** 20 | * @param {Object} options 21 | * @return {Function} 22 | * @api public 23 | */ 24 | 25 | module.exports = function query(options) { 26 | var opts = merge({}, options) 27 | var queryparse = qs.parse; 28 | 29 | if (typeof options === 'function') { 30 | queryparse = options; 31 | opts = undefined; 32 | } 33 | 34 | if (opts !== undefined && opts.allowPrototypes === undefined) { 35 | // back-compat for qs module 36 | opts.allowPrototypes = true; 37 | } 38 | 39 | return function query(req, res, next){ 40 | if (!req.query) { 41 | var val = parseUrl(req).query; 42 | req.query = queryparse(val, opts); 43 | } 44 | 45 | next(); 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /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 1 times\n', done) 11 | }) 12 | 13 | it('should set a session cookie', function (done) { 14 | request(app) 15 | .get('/') 16 | .expect('Set-Cookie', /session=/) 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 1 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 2 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 | -------------------------------------------------------------------------------- /test/req.acceptsEncoding.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.acceptsEncoding', function(){ 8 | it('should return encoding if accepted', function (done) { 9 | var app = express(); 10 | 11 | app.get('/', function (req, res) { 12 | res.send({ 13 | gzip: req.acceptsEncoding('gzip'), 14 | deflate: req.acceptsEncoding('deflate') 15 | }) 16 | }) 17 | 18 | request(app) 19 | .get('/') 20 | .set('Accept-Encoding', ' gzip, deflate') 21 | .expect(200, { gzip: 'gzip', deflate: 'deflate' }, done) 22 | }) 23 | 24 | it('should be false if encoding not accepted', function(done){ 25 | var app = express(); 26 | 27 | app.get('/', function (req, res) { 28 | res.send({ 29 | bogus: req.acceptsEncoding('bogus') 30 | }) 31 | }) 32 | 33 | request(app) 34 | .get('/') 35 | .set('Accept-Encoding', ' gzip, deflate') 36 | .expect(200, { bogus: false }, done) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /examples/session/redis.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var logger = require('morgan'); 9 | var session = require('express-session'); 10 | 11 | // pass the express to the connect redis module 12 | // allowing it to inherit from session.Store 13 | var RedisStore = require('connect-redis')(session); 14 | 15 | var app = express(); 16 | 17 | app.use(logger('dev')); 18 | 19 | // Populates req.session 20 | app.use(session({ 21 | resave: false, // don't save session if unmodified 22 | saveUninitialized: false, // don't create session until something stored 23 | secret: 'keyboard cat', 24 | store: new RedisStore 25 | })); 26 | 27 | app.get('/', function(req, res){ 28 | var body = ''; 29 | if (req.session.views) { 30 | ++req.session.views; 31 | } else { 32 | req.session.views = 1; 33 | body += '

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

    '; 34 | } 35 | res.send(body + '

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

    '); 36 | }); 37 | 38 | app.listen(3000); 39 | console.log('Express app started on port 3000'); 40 | -------------------------------------------------------------------------------- /test/req.acceptsEncodings.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.acceptsEncodings', function () { 8 | it('should return encoding if accepted', function (done) { 9 | var app = express(); 10 | 11 | app.get('/', function (req, res) { 12 | res.send({ 13 | gzip: req.acceptsEncodings('gzip'), 14 | deflate: req.acceptsEncodings('deflate') 15 | }) 16 | }) 17 | 18 | request(app) 19 | .get('/') 20 | .set('Accept-Encoding', ' gzip, deflate') 21 | .expect(200, { gzip: 'gzip', deflate: 'deflate' }, done) 22 | }) 23 | 24 | it('should be false if encoding not accepted', function(done){ 25 | var app = express(); 26 | 27 | app.get('/', function (req, res) { 28 | res.send({ 29 | bogus: req.acceptsEncodings('bogus') 30 | }) 31 | }) 32 | 33 | request(app) 34 | .get('/') 35 | .set('Accept-Encoding', ' gzip, deflate') 36 | .expect(200, { bogus: false }, done) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /examples/route-separation/user.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Fake user database 4 | 5 | var users = [ 6 | { name: 'TJ', email: 'tj@vision-media.ca' }, 7 | { name: 'Tobi', email: 'tobi@vision-media.ca' } 8 | ]; 9 | 10 | exports.list = function(req, res){ 11 | res.render('users', { title: 'Users', users: users }); 12 | }; 13 | 14 | exports.load = function(req, res, next){ 15 | var id = req.params.id; 16 | req.user = users[id]; 17 | if (req.user) { 18 | next(); 19 | } else { 20 | var err = new Error('cannot find user ' + id); 21 | err.status = 404; 22 | next(err); 23 | } 24 | }; 25 | 26 | exports.view = function(req, res){ 27 | res.render('users/view', { 28 | title: 'Viewing user ' + req.user.name, 29 | user: req.user 30 | }); 31 | }; 32 | 33 | exports.edit = function(req, res){ 34 | res.render('users/edit', { 35 | title: 'Editing user ' + req.user.name, 36 | user: req.user 37 | }); 38 | }; 39 | 40 | exports.update = function(req, res){ 41 | // Normally you would handle all kinds of 42 | // validation and save back to the db 43 | var user = req.body.user; 44 | req.user.name = user.name; 45 | req.user.email = user.email; 46 | res.redirect('back'); 47 | }; 48 | -------------------------------------------------------------------------------- /test/req.xhr.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.xhr', function(){ 8 | before(function () { 9 | this.app = express() 10 | this.app.get('/', function (req, res) { 11 | res.send(req.xhr) 12 | }) 13 | }) 14 | 15 | it('should return true when X-Requested-With is xmlhttprequest', function(done){ 16 | request(this.app) 17 | .get('/') 18 | .set('X-Requested-With', 'xmlhttprequest') 19 | .expect(200, 'true', done) 20 | }) 21 | 22 | it('should case-insensitive', function(done){ 23 | request(this.app) 24 | .get('/') 25 | .set('X-Requested-With', 'XMLHttpRequest') 26 | .expect(200, 'true', done) 27 | }) 28 | 29 | it('should return false otherwise', function(done){ 30 | request(this.app) 31 | .get('/') 32 | .set('X-Requested-With', 'blahblah') 33 | .expect(200, 'false', done) 34 | }) 35 | 36 | it('should return false when not present', function(done){ 37 | request(this.app) 38 | .get('/') 39 | .expect(200, 'false', done) 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /examples/markdown/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var escapeHtml = require('escape-html'); 8 | var express = require('../..'); 9 | var fs = require('fs'); 10 | var marked = require('marked'); 11 | var path = require('path'); 12 | 13 | var app = module.exports = express(); 14 | 15 | // register .md as an engine in express view system 16 | 17 | app.engine('md', function(path, options, fn){ 18 | fs.readFile(path, 'utf8', function(err, str){ 19 | if (err) return fn(err); 20 | var html = marked.parse(str).replace(/\{([^}]+)\}/g, function(_, name){ 21 | return escapeHtml(options[name] || ''); 22 | }); 23 | fn(null, html); 24 | }); 25 | }); 26 | 27 | app.set('views', path.join(__dirname, 'views')); 28 | 29 | // make it the default, so we don't need .md 30 | app.set('view engine', 'md'); 31 | 32 | app.get('/', function(req, res){ 33 | res.render('index', { title: 'Markdown Example' }); 34 | }); 35 | 36 | app.get('/fail', function(req, res){ 37 | res.render('missing', { title: 'Markdown Example' }); 38 | }); 39 | 40 | /* istanbul ignore next */ 41 | if (!module.parent) { 42 | app.listen(3000); 43 | console.log('Express started on port 3000'); 44 | } 45 | -------------------------------------------------------------------------------- /examples/content-negotiation/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../../'); 4 | var app = module.exports = express(); 5 | var users = require('./db'); 6 | 7 | // so either you can deal with different types of formatting 8 | // for expected response in index.js 9 | app.get('/', function(req, res){ 10 | res.format({ 11 | html: function(){ 12 | res.send(''); 15 | }, 16 | 17 | text: function(){ 18 | res.send(users.map(function(user){ 19 | return ' - ' + user.name + '\n'; 20 | }).join('')); 21 | }, 22 | 23 | json: function(){ 24 | res.json(users); 25 | } 26 | }); 27 | }); 28 | 29 | // or you could write a tiny middleware like 30 | // this to add a layer of abstraction 31 | // and make things a bit more declarative: 32 | 33 | function format(path) { 34 | var obj = require(path); 35 | return function(req, res){ 36 | res.format(obj); 37 | }; 38 | } 39 | 40 | app.get('/users', format('./users')); 41 | 42 | /* istanbul ignore next */ 43 | if (!module.parent) { 44 | app.listen(3000); 45 | console.log('Express started on port 3000'); 46 | } 47 | -------------------------------------------------------------------------------- /test/middleware.basic.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var express = require('../'); 5 | var request = require('supertest'); 6 | 7 | describe('middleware', function(){ 8 | describe('.next()', function(){ 9 | it('should behave like connect', function(done){ 10 | var app = express() 11 | , calls = []; 12 | 13 | app.use(function(req, res, next){ 14 | calls.push('one'); 15 | next(); 16 | }); 17 | 18 | app.use(function(req, res, next){ 19 | calls.push('two'); 20 | next(); 21 | }); 22 | 23 | app.use(function(req, res){ 24 | var buf = ''; 25 | res.setHeader('Content-Type', 'application/json'); 26 | req.setEncoding('utf8'); 27 | req.on('data', function(chunk){ buf += chunk }); 28 | req.on('end', function(){ 29 | res.end(buf); 30 | }); 31 | }); 32 | 33 | request(app) 34 | .get('/') 35 | .set('Content-Type', 'application/json') 36 | .send('{"foo":"bar"}') 37 | .expect('Content-Type', 'application/json') 38 | .expect(function () { assert.deepEqual(calls, ['one', 'two']) }) 39 | .expect(200, '{"foo":"bar"}', done) 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /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 | }) 46 | -------------------------------------------------------------------------------- /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, loki/, 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 | -------------------------------------------------------------------------------- /examples/vhost/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var logger = require('morgan'); 9 | var vhost = require('vhost'); 10 | 11 | /* 12 | edit /etc/hosts: 13 | 14 | 127.0.0.1 foo.example.com 15 | 127.0.0.1 bar.example.com 16 | 127.0.0.1 example.com 17 | */ 18 | 19 | // Main server app 20 | 21 | var main = express(); 22 | 23 | if (!module.parent) main.use(logger('dev')); 24 | 25 | main.get('/', function(req, res){ 26 | res.send('Hello from main app!'); 27 | }); 28 | 29 | main.get('/:sub', function(req, res){ 30 | res.send('requested ' + req.params.sub); 31 | }); 32 | 33 | // Redirect app 34 | 35 | var redirect = express(); 36 | 37 | redirect.use(function(req, res){ 38 | if (!module.parent) console.log(req.vhost); 39 | res.redirect('http://example.com:3000/' + req.vhost[0]); 40 | }); 41 | 42 | // Vhost app 43 | 44 | var app = module.exports = express(); 45 | 46 | app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app 47 | app.use(vhost('example.com', main)); // Serves top level domain via Main server app 48 | 49 | /* istanbul ignore next */ 50 | if (!module.parent) { 51 | app.listen(3000); 52 | console.log('Express started on port 3000'); 53 | } 54 | -------------------------------------------------------------------------------- /examples/online/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // install redis first: 4 | // https://redis.io/ 5 | 6 | // then: 7 | // $ npm install redis online 8 | // $ redis-server 9 | 10 | /** 11 | * Module dependencies. 12 | */ 13 | 14 | var express = require('../..'); 15 | var online = require('online'); 16 | var redis = require('redis'); 17 | var db = redis.createClient(); 18 | 19 | // online 20 | 21 | online = online(db); 22 | 23 | // app 24 | 25 | var app = express(); 26 | 27 | // activity tracking, in this case using 28 | // the UA string, you would use req.user.id etc 29 | 30 | app.use(function(req, res, next){ 31 | // fire-and-forget 32 | online.add(req.headers['user-agent']); 33 | next(); 34 | }); 35 | 36 | /** 37 | * List helper. 38 | */ 39 | 40 | function list(ids) { 41 | return ''; 44 | } 45 | 46 | /** 47 | * GET users online. 48 | */ 49 | 50 | app.get('/', function(req, res, next){ 51 | online.last(5, function(err, ids){ 52 | if (err) return next(err); 53 | res.send('

    Users online: ' + ids.length + '

    ' + list(ids)); 54 | }); 55 | }); 56 | 57 | /* istanbul ignore next */ 58 | if (!module.parent) { 59 | app.listen(3000); 60 | console.log('Express started on port 3000'); 61 | } 62 | -------------------------------------------------------------------------------- /examples/view-constructor/github-view.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var https = require('https'); 8 | var path = require('path'); 9 | var extname = path.extname; 10 | 11 | /** 12 | * Expose `GithubView`. 13 | */ 14 | 15 | module.exports = GithubView; 16 | 17 | /** 18 | * Custom view that fetches and renders 19 | * remove github templates. You could 20 | * render templates from a database etc. 21 | */ 22 | 23 | function GithubView(name, options){ 24 | this.name = name; 25 | options = options || {}; 26 | this.engine = options.engines[extname(name)]; 27 | // "root" is the app.set('views') setting, however 28 | // in your own implementation you could ignore this 29 | this.path = '/' + options.root + '/master/' + name; 30 | } 31 | 32 | /** 33 | * Render the view. 34 | */ 35 | 36 | GithubView.prototype.render = function(options, fn){ 37 | var self = this; 38 | var opts = { 39 | host: 'raw.githubusercontent.com', 40 | port: 443, 41 | path: this.path, 42 | method: 'GET' 43 | }; 44 | 45 | https.request(opts, function(res) { 46 | var buf = ''; 47 | res.setEncoding('utf8'); 48 | res.on('data', function(str){ buf += str }); 49 | res.on('end', function(){ 50 | self.engine(buf, options, fn); 51 | }); 52 | }).end(); 53 | }; 54 | -------------------------------------------------------------------------------- /test/req.fresh.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.fresh', function(){ 8 | it('should return true when the resource is not modified', function(done){ 9 | var app = express(); 10 | var etag = '"12345"'; 11 | 12 | app.use(function(req, res){ 13 | res.set('ETag', etag); 14 | res.send(req.fresh); 15 | }); 16 | 17 | request(app) 18 | .get('/') 19 | .set('If-None-Match', etag) 20 | .expect(304, done); 21 | }) 22 | 23 | it('should return false when the resource is modified', function(done){ 24 | var app = express(); 25 | 26 | app.use(function(req, res){ 27 | res.set('ETag', '"123"'); 28 | res.send(req.fresh); 29 | }); 30 | 31 | request(app) 32 | .get('/') 33 | .set('If-None-Match', '"12345"') 34 | .expect(200, 'false', done); 35 | }) 36 | 37 | it('should return false without response headers', function(done){ 38 | var app = express(); 39 | 40 | app.disable('x-powered-by') 41 | app.use(function(req, res){ 42 | res.send(req.fresh); 43 | }); 44 | 45 | request(app) 46 | .get('/') 47 | .expect(200, 'false', done); 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/req.stale.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.stale', function(){ 8 | it('should return false when the resource is not modified', function(done){ 9 | var app = express(); 10 | var etag = '"12345"'; 11 | 12 | app.use(function(req, res){ 13 | res.set('ETag', etag); 14 | res.send(req.stale); 15 | }); 16 | 17 | request(app) 18 | .get('/') 19 | .set('If-None-Match', etag) 20 | .expect(304, done); 21 | }) 22 | 23 | it('should return true when the resource is modified', function(done){ 24 | var app = express(); 25 | 26 | app.use(function(req, res){ 27 | res.set('ETag', '"123"'); 28 | res.send(req.stale); 29 | }); 30 | 31 | request(app) 32 | .get('/') 33 | .set('If-None-Match', '"12345"') 34 | .expect(200, 'true', done); 35 | }) 36 | 37 | it('should return true without response headers', function(done){ 38 | var app = express(); 39 | 40 | app.disable('x-powered-by') 41 | app.use(function(req, res){ 42 | res.send(req.stale); 43 | }); 44 | 45 | request(app) 46 | .get('/') 47 | .expect(200, 'true', done); 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/res.type.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.type(str)', function(){ 8 | it('should set the Content-Type based on a filename', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.type('foo.js').end('var name = "tj";'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('Content-Type', 'application/javascript; charset=utf-8') 18 | .end(done) 19 | }) 20 | 21 | it('should default to application/octet-stream', function(done){ 22 | var app = express(); 23 | 24 | app.use(function(req, res){ 25 | res.type('rawr').end('var name = "tj";'); 26 | }); 27 | 28 | request(app) 29 | .get('/') 30 | .expect('Content-Type', 'application/octet-stream', done); 31 | }) 32 | 33 | it('should set the Content-Type with type/subtype', function(done){ 34 | var app = express(); 35 | 36 | app.use(function(req, res){ 37 | res.type('application/vnd.amazon.ebook') 38 | .end('var name = "tj";'); 39 | }); 40 | 41 | request(app) 42 | .get('/') 43 | .expect('Content-Type', 'application/vnd.amazon.ebook', done); 44 | }) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /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/downloads/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var path = require('path'); 9 | 10 | var app = module.exports = express(); 11 | 12 | // path to where the files are stored on disk 13 | var FILES_DIR = path.join(__dirname, 'files') 14 | 15 | app.get('/', function(req, res){ 16 | res.send('') 22 | }); 23 | 24 | // /files/* is accessed via req.params[0] 25 | // but here we name it :file 26 | app.get('/files/:file(*)', function(req, res, next){ 27 | res.download(req.params.file, { root: FILES_DIR }, function (err) { 28 | if (!err) return; // file sent 29 | if (err.status !== 404) return next(err); // non-404 error 30 | // file for download not found 31 | res.statusCode = 404; 32 | res.send('Cant find that file, sorry!'); 33 | }); 34 | }); 35 | 36 | /* istanbul ignore next */ 37 | if (!module.parent) { 38 | app.listen(3000); 39 | console.log('Express started on port 3000'); 40 | } 41 | -------------------------------------------------------------------------------- /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 from 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 | -------------------------------------------------------------------------------- /examples/route-separation/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var path = require('path'); 9 | var app = express(); 10 | var logger = require('morgan'); 11 | var cookieParser = require('cookie-parser'); 12 | var methodOverride = require('method-override'); 13 | var site = require('./site'); 14 | var post = require('./post'); 15 | var user = require('./user'); 16 | 17 | module.exports = app; 18 | 19 | // Config 20 | 21 | app.set('view engine', 'ejs'); 22 | app.set('views', path.join(__dirname, 'views')); 23 | 24 | /* istanbul ignore next */ 25 | if (!module.parent) { 26 | app.use(logger('dev')); 27 | } 28 | 29 | app.use(methodOverride('_method')); 30 | app.use(cookieParser()); 31 | app.use(express.urlencoded({ extended: true })) 32 | app.use(express.static(path.join(__dirname, 'public'))); 33 | 34 | // General 35 | 36 | app.get('/', site.index); 37 | 38 | // User 39 | 40 | app.get('/users', user.list); 41 | app.all('/user/:id/:op?', user.load); 42 | app.get('/user/:id', user.view); 43 | app.get('/user/:id/view', user.view); 44 | app.get('/user/:id/edit', user.edit); 45 | app.put('/user/:id/edit', user.update); 46 | 47 | // Posts 48 | 49 | app.get('/posts', post.list); 50 | 51 | /* istanbul ignore next */ 52 | if (!module.parent) { 53 | app.listen(3000); 54 | console.log('Express started on port 3000'); 55 | } 56 | -------------------------------------------------------------------------------- /examples/view-constructor/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var GithubView = require('./github-view'); 9 | var md = require('marked').parse; 10 | 11 | var app = module.exports = express(); 12 | 13 | // register .md as an engine in express view system 14 | app.engine('md', function(str, options, fn){ 15 | try { 16 | var html = md(str); 17 | html = html.replace(/\{([^}]+)\}/g, function(_, name){ 18 | return options[name] || ''; 19 | }); 20 | fn(null, html); 21 | } catch(err) { 22 | fn(err); 23 | } 24 | }); 25 | 26 | // pointing to a particular github repo to load files from it 27 | app.set('views', 'expressjs/express'); 28 | 29 | // register a new view constructor 30 | app.set('view', GithubView); 31 | 32 | app.get('/', function(req, res){ 33 | // rendering a view relative to the repo. 34 | // app.locals, res.locals, and locals passed 35 | // work like they normally would 36 | res.render('examples/markdown/views/index.md', { title: 'Example' }); 37 | }); 38 | 39 | app.get('/Readme.md', function(req, res){ 40 | // rendering a view from https://github.com/expressjs/express/blob/master/Readme.md 41 | res.render('Readme.md'); 42 | }); 43 | 44 | /* istanbul ignore next */ 45 | if (!module.parent) { 46 | app.listen(3000); 47 | console.log('Express started on port 3000'); 48 | } 49 | -------------------------------------------------------------------------------- /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/search/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // install redis first: 4 | // https://redis.io/ 5 | 6 | // then: 7 | // $ npm install redis 8 | // $ redis-server 9 | 10 | /** 11 | * Module dependencies. 12 | */ 13 | 14 | var express = require('../..'); 15 | var path = require('path'); 16 | var redis = require('redis'); 17 | 18 | var db = redis.createClient(); 19 | 20 | // npm install redis 21 | 22 | var app = express(); 23 | 24 | app.use(express.static(path.join(__dirname, 'public'))); 25 | 26 | // populate search 27 | 28 | db.sadd('ferret', 'tobi'); 29 | db.sadd('ferret', 'loki'); 30 | db.sadd('ferret', 'jane'); 31 | db.sadd('cat', 'manny'); 32 | db.sadd('cat', 'luna'); 33 | 34 | /** 35 | * GET search for :query. 36 | */ 37 | 38 | app.get('/search/:query?', function(req, res){ 39 | var query = req.params.query; 40 | db.smembers(query, function(err, vals){ 41 | if (err) return res.send(500); 42 | res.send(vals); 43 | }); 44 | }); 45 | 46 | /** 47 | * GET client javascript. Here we use sendFile() 48 | * because serving __dirname with the static() middleware 49 | * would also mean serving our server "index.js" and the "search.jade" 50 | * template. 51 | */ 52 | 53 | app.get('/client.js', function(req, res){ 54 | res.sendFile(path.join(__dirname, 'client.js')); 55 | }); 56 | 57 | /* istanbul ignore next */ 58 | if (!module.parent) { 59 | app.listen(3000); 60 | console.log('Express started on port 3000'); 61 | } 62 | -------------------------------------------------------------------------------- /test/req.acceptsCharset.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.acceptsCharset(type)', function(){ 8 | describe('when Accept-Charset is not present', function(){ 9 | it('should return true', function(done){ 10 | var app = express(); 11 | 12 | app.use(function(req, res, next){ 13 | res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect('yes', done); 19 | }) 20 | }) 21 | 22 | describe('when Accept-Charset is present', function () { 23 | it('should return true', function (done) { 24 | var app = express(); 25 | 26 | app.use(function(req, res, next){ 27 | res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('Accept-Charset', 'foo, bar, utf-8') 33 | .expect('yes', done); 34 | }) 35 | 36 | it('should return false otherwise', function(done){ 37 | var app = express(); 38 | 39 | app.use(function(req, res, next){ 40 | res.end(req.acceptsCharset('utf-8') ? 'yes' : 'no'); 41 | }); 42 | 43 | request(app) 44 | .get('/') 45 | .set('Accept-Charset', 'foo, bar') 46 | .expect('no', done); 47 | }) 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/req.acceptsCharsets.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.acceptsCharsets(type)', function(){ 8 | describe('when Accept-Charset is not present', function(){ 9 | it('should return true', function(done){ 10 | var app = express(); 11 | 12 | app.use(function(req, res, next){ 13 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect('yes', done); 19 | }) 20 | }) 21 | 22 | describe('when Accept-Charset is present', function () { 23 | it('should return true', function (done) { 24 | var app = express(); 25 | 26 | app.use(function(req, res, next){ 27 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('Accept-Charset', 'foo, bar, utf-8') 33 | .expect('yes', done); 34 | }) 35 | 36 | it('should return false otherwise', function(done){ 37 | var app = express(); 38 | 39 | app.use(function(req, res, next){ 40 | res.end(req.acceptsCharsets('utf-8') ? 'yes' : 'no'); 41 | }); 42 | 43 | request(app) 44 | .get('/') 45 | .set('Accept-Charset', 'foo, bar') 46 | .expect('no', done); 47 | }) 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /test/app.route.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../'); 4 | var request = require('supertest'); 5 | 6 | describe('app.route', function(){ 7 | it('should return a new route', function(done){ 8 | var app = express(); 9 | 10 | app.route('/foo') 11 | .get(function(req, res) { 12 | res.send('get'); 13 | }) 14 | .post(function(req, res) { 15 | res.send('post'); 16 | }); 17 | 18 | request(app) 19 | .post('/foo') 20 | .expect('post', done); 21 | }); 22 | 23 | it('should all .VERB after .all', function(done){ 24 | var app = express(); 25 | 26 | app.route('/foo') 27 | .all(function(req, res, next) { 28 | next(); 29 | }) 30 | .get(function(req, res) { 31 | res.send('get'); 32 | }) 33 | .post(function(req, res) { 34 | res.send('post'); 35 | }); 36 | 37 | request(app) 38 | .post('/foo') 39 | .expect('post', done); 40 | }); 41 | 42 | it('should support dynamic routes', function(done){ 43 | var app = express(); 44 | 45 | app.route('/:foo') 46 | .get(function(req, res) { 47 | res.send(req.params.foo); 48 | }); 49 | 50 | request(app) 51 | .get('/test') 52 | .expect('test', done); 53 | }); 54 | 55 | it('should not error on empty routes', function(done){ 56 | var app = express(); 57 | 58 | app.route('/:foo'); 59 | 60 | request(app) 61 | .get('/test') 62 | .expect(404, done); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /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/notes/groceries.txt', function () { 15 | it('should have a download header', function (done) { 16 | request(app) 17 | .get('/files/notes/groceries.txt') 18 | .expect('Content-Disposition', 'attachment; filename="groceries.txt"') 19 | .expect(200, done) 20 | }) 21 | }) 22 | 23 | describe('GET /files/amazing.txt', function(){ 24 | it('should have a download header', function(done){ 25 | request(app) 26 | .get('/files/amazing.txt') 27 | .expect('Content-Disposition', 'attachment; filename="amazing.txt"') 28 | .expect(200, done) 29 | }) 30 | }) 31 | 32 | describe('GET /files/missing.txt', function(){ 33 | it('should respond with 404', function(done){ 34 | request(app) 35 | .get('/files/missing.txt') 36 | .expect(404, done) 37 | }) 38 | }) 39 | 40 | describe('GET /files/../index.js', function () { 41 | it('should respond with 403', function (done) { 42 | request(app) 43 | .get('/files/../index.js') 44 | .expect(403, done) 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /examples/cookies/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var app = module.exports = express(); 9 | var logger = require('morgan'); 10 | var cookieParser = require('cookie-parser'); 11 | 12 | // custom log format 13 | if (process.env.NODE_ENV !== 'test') app.use(logger(':method :url')) 14 | 15 | // parses request cookies, populating 16 | // req.cookies and req.signedCookies 17 | // when the secret is passed, used 18 | // for signing the cookies. 19 | app.use(cookieParser('my secret here')); 20 | 21 | // parses x-www-form-urlencoded 22 | app.use(express.urlencoded({ extended: false })) 23 | 24 | app.get('/', function(req, res){ 25 | if (req.cookies.remember) { 26 | res.send('Remembered :). Click to forget!.'); 27 | } else { 28 | res.send('

    Check to ' 30 | + '.

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

    Title:

    ' 16 | + '

    Image:

    ' 17 | + '

    ' 18 | + '
    '); 19 | }); 20 | 21 | app.post('/', function(req, res, next){ 22 | // create a form to begin parsing 23 | var form = new multiparty.Form(); 24 | var image; 25 | var title; 26 | 27 | form.on('error', next); 28 | form.on('close', function(){ 29 | res.send(format('\nuploaded %s (%d Kb) as %s' 30 | , image.filename 31 | , image.size / 1024 | 0 32 | , title)); 33 | }); 34 | 35 | // listen on field event for title 36 | form.on('field', function(name, val){ 37 | if (name !== 'title') return; 38 | title = val; 39 | }); 40 | 41 | // listen on part event for image file 42 | form.on('part', function(part){ 43 | if (!part.filename) return; 44 | if (part.name !== 'image') return part.resume(); 45 | image = {}; 46 | image.filename = part.filename; 47 | image.size = 0; 48 | part.on('data', function(buf){ 49 | image.size += buf.length; 50 | }); 51 | }); 52 | 53 | 54 | // parse the form 55 | form.parse(req); 56 | }); 57 | 58 | /* istanbul ignore next */ 59 | if (!module.parent) { 60 | app.listen(4000); 61 | console.log('Express started on port 4000'); 62 | } 63 | -------------------------------------------------------------------------------- /test/app.head.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../'); 4 | var request = require('supertest'); 5 | var assert = require('assert'); 6 | 7 | describe('HEAD', function(){ 8 | it('should default to GET', function(done){ 9 | var app = express(); 10 | 11 | app.get('/tobi', function(req, res){ 12 | // send() detects HEAD 13 | res.send('tobi'); 14 | }); 15 | 16 | request(app) 17 | .head('/tobi') 18 | .expect(200, done); 19 | }) 20 | 21 | it('should output the same headers as GET requests', function(done){ 22 | var app = express(); 23 | 24 | app.get('/tobi', function(req, res){ 25 | // send() detects HEAD 26 | res.send('tobi'); 27 | }); 28 | 29 | request(app) 30 | .head('/tobi') 31 | .expect(200, function(err, res){ 32 | if (err) return done(err); 33 | var headers = res.headers; 34 | request(app) 35 | .get('/tobi') 36 | .expect(200, function(err, res){ 37 | if (err) return done(err); 38 | delete headers.date; 39 | delete res.headers.date; 40 | assert.deepEqual(res.headers, headers); 41 | done(); 42 | }); 43 | }); 44 | }) 45 | }) 46 | 47 | describe('app.head()', function(){ 48 | it('should override', function(done){ 49 | var app = express() 50 | 51 | app.head('/tobi', function(req, res){ 52 | res.header('x-method', 'head') 53 | res.end() 54 | }); 55 | 56 | app.get('/tobi', function(req, res){ 57 | res.header('x-method', 'get') 58 | res.send('tobi'); 59 | }); 60 | 61 | request(app) 62 | .head('/tobi') 63 | .expect('x-method', 'head') 64 | .expect(200, done) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /examples/route-map/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var escapeHtml = require('escape-html') 8 | var express = require('../../lib/express'); 9 | 10 | var verbose = process.env.NODE_ENV !== 'test' 11 | 12 | var app = module.exports = express(); 13 | 14 | app.map = function(a, route){ 15 | route = route || ''; 16 | for (var key in a) { 17 | switch (typeof a[key]) { 18 | // { '/path': { ... }} 19 | case 'object': 20 | app.map(a[key], route + key); 21 | break; 22 | // get: function(){ ... } 23 | case 'function': 24 | if (verbose) console.log('%s %s', key, route); 25 | app[key](route, a[key]); 26 | break; 27 | } 28 | } 29 | }; 30 | 31 | var users = { 32 | list: function(req, res){ 33 | res.send('user list'); 34 | }, 35 | 36 | get: function(req, res){ 37 | res.send('user ' + escapeHtml(req.params.uid)) 38 | }, 39 | 40 | delete: function(req, res){ 41 | res.send('delete users'); 42 | } 43 | }; 44 | 45 | var pets = { 46 | list: function(req, res){ 47 | res.send('user ' + escapeHtml(req.params.uid) + '\'s pets') 48 | }, 49 | 50 | delete: function(req, res){ 51 | res.send('delete ' + escapeHtml(req.params.uid) + '\'s pet ' + escapeHtml(req.params.pid)) 52 | } 53 | }; 54 | 55 | app.map({ 56 | '/users': { 57 | get: users.list, 58 | delete: users.delete, 59 | '/:uid': { 60 | get: users.get, 61 | '/pets': { 62 | get: pets.list, 63 | '/:pid': { 64 | delete: pets.delete 65 | } 66 | } 67 | } 68 | } 69 | }); 70 | 71 | /* istanbul ignore next */ 72 | if (!module.parent) { 73 | app.listen(3000); 74 | console.log('Express started on port 3000'); 75 | } 76 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Express examples 2 | 3 | This page contains list of examples using Express. 4 | 5 | - [auth](./auth) - Authentication with login and password 6 | - [content-negotiation](./content-negotiation) - HTTP content negotiation 7 | - [cookie-sessions](./cookie-sessions) - Working with cookie-based sessions 8 | - [cookies](./cookies) - Working with cookies 9 | - [downloads](./downloads) - Transferring files to client 10 | - [ejs](./ejs) - Working with Embedded JavaScript templating (ejs) 11 | - [error-pages](./error-pages) - Creating error pages 12 | - [error](./error) - Working with error middleware 13 | - [hello-world](./hello-world) - Simple request handler 14 | - [markdown](./markdown) - Markdown as template engine 15 | - [multi-router](./multi-router) - Working with multiple Express routers 16 | - [multipart](./multipart) - Accepting multipart-encoded forms 17 | - [mvc](./mvc) - MVC-style controllers 18 | - [online](./online) - Tracking online user activity with `online` and `redis` packages 19 | - [params](./params) - Working with route parameters 20 | - [resource](./resource) - Multiple HTTP operations on the same resource 21 | - [route-map](./route-map) - Organizing routes using a map 22 | - [route-middleware](./route-middleware) - Working with route middleware 23 | - [route-separation](./route-separation) - Organizing routes per each resource 24 | - [search](./search) - Search API 25 | - [session](./session) - User sessions 26 | - [static-files](./static-files) - Serving static files 27 | - [vhost](./vhost) - Working with virtual hosts 28 | - [view-constructor](./view-constructor) - Rendering views dynamically 29 | - [view-locals](./view-locals) - Saving data in request object between middleware calls 30 | - [web-service](./web-service) - Simple API service 31 | -------------------------------------------------------------------------------- /test/app.routes.error.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var express = require('../') 5 | , request = require('supertest'); 6 | 7 | describe('app', function(){ 8 | describe('.VERB()', function(){ 9 | it('should not get invoked without error handler on error', function(done) { 10 | var app = express(); 11 | 12 | app.use(function(req, res, next){ 13 | next(new Error('boom!')) 14 | }); 15 | 16 | app.get('/bar', function(req, res){ 17 | res.send('hello, world!'); 18 | }); 19 | 20 | request(app) 21 | .post('/bar') 22 | .expect(500, /Error: boom!/, done); 23 | }); 24 | 25 | it('should only call an error handling routing callback when an error is propagated', function(done){ 26 | var app = express(); 27 | 28 | var a = false; 29 | var b = false; 30 | var c = false; 31 | var d = false; 32 | 33 | app.get('/', function(req, res, next){ 34 | next(new Error('fabricated error')); 35 | }, function(req, res, next) { 36 | a = true; 37 | next(); 38 | }, function(err, req, res, next){ 39 | b = true; 40 | assert.strictEqual(err.message, 'fabricated error') 41 | next(err); 42 | }, function(err, req, res, next){ 43 | c = true; 44 | assert.strictEqual(err.message, 'fabricated error') 45 | next(); 46 | }, function(err, req, res, next){ 47 | d = true; 48 | next(); 49 | }, function(req, res){ 50 | assert.ok(!a) 51 | assert.ok(b) 52 | assert.ok(c) 53 | assert.ok(!d) 54 | res.send(204); 55 | }); 56 | 57 | request(app) 58 | .get('/') 59 | .expect(204, done); 60 | }) 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /test/support/utils.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | * @private 5 | */ 6 | 7 | var assert = require('assert'); 8 | var Buffer = require('safe-buffer').Buffer 9 | 10 | /** 11 | * Module exports. 12 | * @public 13 | */ 14 | 15 | exports.shouldHaveBody = shouldHaveBody 16 | exports.shouldHaveHeader = shouldHaveHeader 17 | exports.shouldNotHaveBody = shouldNotHaveBody 18 | exports.shouldNotHaveHeader = shouldNotHaveHeader; 19 | 20 | /** 21 | * Assert that a supertest response has a specific body. 22 | * 23 | * @param {Buffer} buf 24 | * @returns {function} 25 | */ 26 | 27 | function shouldHaveBody (buf) { 28 | return function (res) { 29 | var body = !Buffer.isBuffer(res.body) 30 | ? Buffer.from(res.text) 31 | : res.body 32 | assert.ok(body, 'response has body') 33 | assert.strictEqual(body.toString('hex'), buf.toString('hex')) 34 | } 35 | } 36 | 37 | /** 38 | * Assert that a supertest response does have a header. 39 | * 40 | * @param {string} header Header name to check 41 | * @returns {function} 42 | */ 43 | 44 | function shouldHaveHeader (header) { 45 | return function (res) { 46 | assert.ok((header.toLowerCase() in res.headers), 'should have header ' + header) 47 | } 48 | } 49 | 50 | /** 51 | * Assert that a supertest response does not have a body. 52 | * 53 | * @returns {function} 54 | */ 55 | 56 | function shouldNotHaveBody () { 57 | return function (res) { 58 | assert.ok(res.text === '' || res.text === undefined) 59 | } 60 | } 61 | 62 | /** 63 | * Assert that a supertest response does not have a header. 64 | * 65 | * @param {string} header Header name to check 66 | * @returns {function} 67 | */ 68 | function shouldNotHaveHeader(header) { 69 | return function (res) { 70 | assert.ok(!(header.toLowerCase() in res.headers), 'should not have header ' + header); 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /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 | To ensure the timely response to your report, please ensure that the entirety 20 | of the report is contained within the email body and not solely behind a web 21 | link or an attachment. 22 | 23 | The lead maintainer will acknowledge your email within 48 hours, and will send a 24 | more detailed response within 48 hours indicating the next steps in handling 25 | your report. After the initial reply to your report, the security team will 26 | endeavor to keep you informed of the progress towards a fix and full 27 | announcement, and may ask for additional information or guidance. 28 | 29 | Report security bugs in third-party modules to the person or team maintaining 30 | the module. 31 | 32 | ## Disclosure Policy 33 | 34 | When the security team receives a security bug report, they will assign it to a 35 | primary handler. This person will coordinate the fix and release process, 36 | involving the following steps: 37 | 38 | * Confirm the problem and determine the affected versions. 39 | * Audit code to find any potential similar problems. 40 | * Prepare fixes for all releases still under maintenance. These fixes will be 41 | released as fast as possible to npm. 42 | 43 | ## Comments on this Policy 44 | 45 | If you have suggestions on how this process could be improved please submit a 46 | pull request. 47 | -------------------------------------------------------------------------------- /test/req.ips.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.ips', function(){ 8 | describe('when X-Forwarded-For is present', function(){ 9 | describe('when "trust proxy" is enabled', function(){ 10 | it('should return an array of the specified addresses', function(done){ 11 | var app = express(); 12 | 13 | app.enable('trust proxy'); 14 | 15 | app.use(function(req, res, next){ 16 | res.send(req.ips); 17 | }); 18 | 19 | request(app) 20 | .get('/') 21 | .set('X-Forwarded-For', 'client, p1, p2') 22 | .expect('["client","p1","p2"]', done); 23 | }) 24 | 25 | it('should stop at first untrusted', function(done){ 26 | var app = express(); 27 | 28 | app.set('trust proxy', 2); 29 | 30 | app.use(function(req, res, next){ 31 | res.send(req.ips); 32 | }); 33 | 34 | request(app) 35 | .get('/') 36 | .set('X-Forwarded-For', 'client, p1, p2') 37 | .expect('["p1","p2"]', done); 38 | }) 39 | }) 40 | 41 | describe('when "trust proxy" is disabled', function(){ 42 | it('should return an empty array', function(done){ 43 | var app = express(); 44 | 45 | app.use(function(req, res, next){ 46 | res.send(req.ips); 47 | }); 48 | 49 | request(app) 50 | .get('/') 51 | .set('X-Forwarded-For', 'client, p1, p2') 52 | .expect('[]', done); 53 | }) 54 | }) 55 | }) 56 | 57 | describe('when X-Forwarded-For is not present', function(){ 58 | it('should return []', function(done){ 59 | var app = express(); 60 | 61 | app.use(function(req, res, next){ 62 | res.send(req.ips); 63 | }); 64 | 65 | request(app) 66 | .get('/') 67 | .expect('[]', done); 68 | }) 69 | }) 70 | }) 71 | }) 72 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Collaborator-Guide.md: -------------------------------------------------------------------------------- 1 | # Express Collaborator Guide 2 | 3 | ## Website Issues 4 | 5 | Open issues for the expressjs.com website in https://github.com/expressjs/expressjs.com. 6 | 7 | ## PRs and Code contributions 8 | 9 | * Tests must pass. 10 | * Follow the [JavaScript Standard Style](http://standardjs.com/) and `npm run lint`. 11 | * If you fix a bug, add a test. 12 | 13 | ## Branches 14 | 15 | Use the `master` branch for bug fixes or minor work that is intended for the 16 | current release stream. 17 | 18 | Use the correspondingly named branch, e.g. `5.0`, for anything intended for 19 | a future release of Express. 20 | 21 | ## Steps for contributing 22 | 23 | 1. [Create an issue](https://github.com/expressjs/express/issues/new) for the 24 | bug you want to fix or the feature that you want to add. 25 | 2. Create your own [fork](https://github.com/expressjs/express) on GitHub, then 26 | checkout your fork. 27 | 3. Write your code in your local copy. It's good practice to create a branch for 28 | each new issue you work on, although not compulsory. 29 | 4. To run the test suite, first install the dependencies by running `npm install`, 30 | then run `npm test`. 31 | 5. Ensure your code is linted by running `npm run lint` -- fix any issue you 32 | see listed. 33 | 6. If the tests pass, you can commit your changes to your fork and then create 34 | a pull request from there. Make sure to reference your issue from the pull 35 | request comments by including the issue number e.g. `#123`. 36 | 37 | ## Issues which are questions 38 | 39 | We will typically close any vague issues or questions that are specific to some 40 | app you are writing. Please double check the docs and other references before 41 | being trigger happy with posting a question issue. 42 | 43 | Things that will help get your question issue looked at: 44 | 45 | * Full and runnable JS code. 46 | * Clear description of the problem or unexpected behavior. 47 | * Clear description of the expected result. 48 | * Steps you have taken to debug it yourself. 49 | 50 | If you post a question and do not outline the above items or make it easy for 51 | us to understand and reproduce your issue, it will be closed. 52 | -------------------------------------------------------------------------------- /test/res.attachment.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var Buffer = require('safe-buffer').Buffer 4 | var express = require('../') 5 | , request = require('supertest'); 6 | 7 | describe('res', function(){ 8 | describe('.attachment()', function(){ 9 | it('should Content-Disposition to attachment', function(done){ 10 | var app = express(); 11 | 12 | app.use(function(req, res){ 13 | res.attachment().send('foo'); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect('Content-Disposition', 'attachment', done); 19 | }) 20 | }) 21 | 22 | describe('.attachment(filename)', function(){ 23 | it('should add the filename param', function(done){ 24 | var app = express(); 25 | 26 | app.use(function(req, res){ 27 | res.attachment('/path/to/image.png'); 28 | res.send('foo'); 29 | }); 30 | 31 | request(app) 32 | .get('/') 33 | .expect('Content-Disposition', 'attachment; filename="image.png"', done); 34 | }) 35 | 36 | it('should set the Content-Type', function(done){ 37 | var app = express(); 38 | 39 | app.use(function(req, res){ 40 | res.attachment('/path/to/image.png'); 41 | res.send(Buffer.alloc(4, '.')) 42 | }); 43 | 44 | request(app) 45 | .get('/') 46 | .expect('Content-Type', 'image/png', done); 47 | }) 48 | }) 49 | 50 | describe('.attachment(utf8filename)', function(){ 51 | it('should add the filename and filename* params', function(done){ 52 | var app = express(); 53 | 54 | app.use(function(req, res){ 55 | res.attachment('/locales/日本語.txt'); 56 | res.send('japanese'); 57 | }); 58 | 59 | request(app) 60 | .get('/') 61 | .expect('Content-Disposition', 'attachment; filename="???.txt"; filename*=UTF-8\'\'%E6%97%A5%E6%9C%AC%E8%AA%9E.txt') 62 | .expect(200, done); 63 | }) 64 | 65 | it('should set the Content-Type', function(done){ 66 | var app = express(); 67 | 68 | app.use(function(req, res){ 69 | res.attachment('/locales/日本語.txt'); 70 | res.send('japanese'); 71 | }); 72 | 73 | request(app) 74 | .get('/') 75 | .expect('Content-Type', 'text/plain; charset=utf-8', done); 76 | }) 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /test/res.vary.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 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 | 'use strict' 2 | 3 | var express = require('..') 4 | var request = require('supertest') 5 | 6 | describe('req', function(){ 7 | describe('.baseUrl', function(){ 8 | it('should be empty for top-level route', function(done){ 9 | var app = express() 10 | 11 | app.get('/:a', function(req, res){ 12 | res.end(req.baseUrl) 13 | }) 14 | 15 | request(app) 16 | .get('/foo') 17 | .expect(200, '', done) 18 | }) 19 | 20 | it('should contain lower path', function(done){ 21 | var app = express() 22 | var sub = express.Router() 23 | 24 | sub.get('/:b', function(req, res){ 25 | res.end(req.baseUrl) 26 | }) 27 | app.use('/:a', sub) 28 | 29 | request(app) 30 | .get('/foo/bar') 31 | .expect(200, '/foo', done); 32 | }) 33 | 34 | it('should contain full lower path', function(done){ 35 | var app = express() 36 | var sub1 = express.Router() 37 | var sub2 = express.Router() 38 | var sub3 = express.Router() 39 | 40 | sub3.get('/:d', function(req, res){ 41 | res.end(req.baseUrl) 42 | }) 43 | sub2.use('/:c', sub3) 44 | sub1.use('/:b', sub2) 45 | app.use('/:a', sub1) 46 | 47 | request(app) 48 | .get('/foo/bar/baz/zed') 49 | .expect(200, '/foo/bar/baz', done); 50 | }) 51 | 52 | it('should travel through routers correctly', function(done){ 53 | var urls = [] 54 | var app = express() 55 | var sub1 = express.Router() 56 | var sub2 = express.Router() 57 | var sub3 = express.Router() 58 | 59 | sub3.get('/:d', function(req, res, next){ 60 | urls.push('0@' + req.baseUrl) 61 | next() 62 | }) 63 | sub2.use('/:c', sub3) 64 | sub1.use('/', function(req, res, next){ 65 | urls.push('1@' + req.baseUrl) 66 | next() 67 | }) 68 | sub1.use('/bar', sub2) 69 | sub1.use('/bar', function(req, res, next){ 70 | urls.push('2@' + req.baseUrl) 71 | next() 72 | }) 73 | app.use(function(req, res, next){ 74 | urls.push('3@' + req.baseUrl) 75 | next() 76 | }) 77 | app.use('/:a', sub1) 78 | app.use(function(req, res, next){ 79 | urls.push('4@' + req.baseUrl) 80 | res.end(urls.join(',')) 81 | }) 82 | 83 | request(app) 84 | .get('/foo/bar/baz/zed') 85 | .expect(200, '3@,1@/foo,0@/foo/bar/baz,2@/foo/bar,4@', done); 86 | }) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /test/app.engine.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var express = require('../') 5 | , fs = require('fs'); 6 | var path = require('path') 7 | 8 | function render(path, options, fn) { 9 | fs.readFile(path, 'utf8', function(err, str){ 10 | if (err) return fn(err); 11 | str = str.replace('{{user.name}}', options.user.name); 12 | fn(null, str); 13 | }); 14 | } 15 | 16 | describe('app', function(){ 17 | describe('.engine(ext, fn)', function(){ 18 | it('should map a template engine', function(done){ 19 | var app = express(); 20 | 21 | app.set('views', path.join(__dirname, 'fixtures')) 22 | app.engine('.html', render); 23 | app.locals.user = { name: 'tobi' }; 24 | 25 | app.render('user.html', function(err, str){ 26 | if (err) return done(err); 27 | assert.strictEqual(str, '

      tobi

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

      tobi

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

      tobi

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

      tobi

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

      Examples:

        ' 81 | , '
      • GET /users
      • ' 82 | , '
      • GET /users/1
      • ' 83 | , '
      • GET /users/3
      • ' 84 | , '
      • GET /users/1..3
      • ' 85 | , '
      • GET /users/1..3.json
      • ' 86 | , '
      • DELETE /users/4
      • ' 87 | , '
      ' 88 | ].join('\n')); 89 | }); 90 | 91 | /* istanbul ignore next */ 92 | if (!module.parent) { 93 | app.listen(3000); 94 | console.log('Express started on port 3000'); 95 | } 96 | -------------------------------------------------------------------------------- /examples/mvc/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../..'); 8 | var logger = require('morgan'); 9 | var path = require('path'); 10 | var session = require('express-session'); 11 | var methodOverride = require('method-override'); 12 | 13 | var app = module.exports = express(); 14 | 15 | // set our default template engine to "ejs" 16 | // which prevents the need for using file extensions 17 | app.set('view engine', 'ejs'); 18 | 19 | // set views for error and 404 pages 20 | app.set('views', path.join(__dirname, 'views')); 21 | 22 | // define a custom res.message() method 23 | // which stores messages in the session 24 | app.response.message = function(msg){ 25 | // reference `req.session` via the `this.req` reference 26 | var sess = this.req.session; 27 | // simply add the msg to an array for later 28 | sess.messages = sess.messages || []; 29 | sess.messages.push(msg); 30 | return this; 31 | }; 32 | 33 | // log 34 | if (!module.parent) app.use(logger('dev')); 35 | 36 | // serve static files 37 | app.use(express.static(path.join(__dirname, 'public'))); 38 | 39 | // session support 40 | app.use(session({ 41 | resave: false, // don't save session if unmodified 42 | saveUninitialized: false, // don't create session until something stored 43 | secret: 'some secret here' 44 | })); 45 | 46 | // parse request bodies (req.body) 47 | app.use(express.urlencoded({ extended: true })) 48 | 49 | // allow overriding methods in query (?_method=put) 50 | app.use(methodOverride('_method')); 51 | 52 | // expose the "messages" local variable when views are rendered 53 | app.use(function(req, res, next){ 54 | var msgs = req.session.messages || []; 55 | 56 | // expose "messages" local variable 57 | res.locals.messages = msgs; 58 | 59 | // expose "hasMessages" 60 | res.locals.hasMessages = !! msgs.length; 61 | 62 | /* This is equivalent: 63 | res.locals({ 64 | messages: msgs, 65 | hasMessages: !! msgs.length 66 | }); 67 | */ 68 | 69 | next(); 70 | // empty or "flush" the messages so they 71 | // don't build up 72 | req.session.messages = []; 73 | }); 74 | 75 | // load controllers 76 | require('./lib/boot')(app, { verbose: !module.parent }); 77 | 78 | app.use(function(err, req, res, next){ 79 | // log it 80 | if (!module.parent) console.error(err.stack); 81 | 82 | // error page 83 | res.status(500).render('5xx'); 84 | }); 85 | 86 | // assume 404 since no middleware responded 87 | app.use(function(req, res, next){ 88 | res.status(404).render('404', { url: req.originalUrl }); 89 | }); 90 | 91 | /* istanbul ignore next */ 92 | if (!module.parent) { 93 | app.listen(3000); 94 | console.log('Express started on port 3000'); 95 | } 96 | -------------------------------------------------------------------------------- /examples/route-middleware/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../lib/express'); 8 | 9 | var app = express(); 10 | 11 | // Example requests: 12 | // curl http://localhost:3000/user/0 13 | // curl http://localhost:3000/user/0/edit 14 | // curl http://localhost:3000/user/1 15 | // curl http://localhost:3000/user/1/edit (unauthorized since this is not you) 16 | // curl -X DELETE http://localhost:3000/user/0 (unauthorized since you are not an admin) 17 | 18 | // Dummy users 19 | var users = [ 20 | { id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' } 21 | , { id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' } 22 | , { id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' } 23 | ]; 24 | 25 | function loadUser(req, res, next) { 26 | // You would fetch your user from the db 27 | var user = users[req.params.id]; 28 | if (user) { 29 | req.user = user; 30 | next(); 31 | } else { 32 | next(new Error('Failed to load user ' + req.params.id)); 33 | } 34 | } 35 | 36 | function andRestrictToSelf(req, res, next) { 37 | // If our authenticated user is the user we are viewing 38 | // then everything is fine :) 39 | if (req.authenticatedUser.id === req.user.id) { 40 | next(); 41 | } else { 42 | // You may want to implement specific exceptions 43 | // such as UnauthorizedError or similar so that you 44 | // can handle these can be special-cased in an error handler 45 | // (view ./examples/pages for this) 46 | next(new Error('Unauthorized')); 47 | } 48 | } 49 | 50 | function andRestrictTo(role) { 51 | return function(req, res, next) { 52 | if (req.authenticatedUser.role === role) { 53 | next(); 54 | } else { 55 | next(new Error('Unauthorized')); 56 | } 57 | } 58 | } 59 | 60 | // Middleware for faux authentication 61 | // you would of course implement something real, 62 | // but this illustrates how an authenticated user 63 | // may interact with middleware 64 | 65 | app.use(function(req, res, next){ 66 | req.authenticatedUser = users[0]; 67 | next(); 68 | }); 69 | 70 | app.get('/', function(req, res){ 71 | res.redirect('/user/0'); 72 | }); 73 | 74 | app.get('/user/:id', loadUser, function(req, res){ 75 | res.send('Viewing user ' + req.user.name); 76 | }); 77 | 78 | app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){ 79 | res.send('Editing user ' + req.user.name); 80 | }); 81 | 82 | app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){ 83 | res.send('Deleted user ' + req.user.name); 84 | }); 85 | 86 | /* istanbul ignore next */ 87 | if (!module.parent) { 88 | app.listen(3000); 89 | console.log('Express started on port 3000'); 90 | } 91 | -------------------------------------------------------------------------------- /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 | .expect(404, { error: 'Not found' }, done) 56 | }) 57 | }) 58 | 59 | describe('GET /500', function(){ 60 | it('should respond with 500', function(done){ 61 | request(app) 62 | .get('/500') 63 | .set('Accept', 'application/json') 64 | .expect(500, done) 65 | }) 66 | }) 67 | }) 68 | 69 | 70 | describe('Accept: text/plain',function(){ 71 | describe('GET /403', function(){ 72 | it('should respond with 403', function(done){ 73 | request(app) 74 | .get('/403') 75 | .set('Accept','text/plain') 76 | .expect(403, done) 77 | }) 78 | }) 79 | 80 | describe('GET /404', function(){ 81 | it('should respond with 404', function(done){ 82 | request(app) 83 | .get('/404') 84 | .set('Accept', 'text/plain') 85 | .expect(404) 86 | .expect('Not found', done); 87 | }) 88 | }) 89 | 90 | describe('GET /500', function(){ 91 | it('should respond with 500', function(done){ 92 | request(app) 93 | .get('/500') 94 | .set('Accept','text/plain') 95 | .expect(500, done) 96 | }) 97 | }) 98 | }) 99 | }) 100 | -------------------------------------------------------------------------------- /test/req.range.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 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/exports.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var express = require('../'); 5 | var request = require('supertest'); 6 | 7 | describe('exports', function(){ 8 | it('should expose Router', function(){ 9 | assert.strictEqual(typeof express.Router, 'function') 10 | }) 11 | 12 | it('should expose json middleware', function () { 13 | assert.equal(typeof express.json, 'function') 14 | assert.equal(express.json.length, 1) 15 | }) 16 | 17 | it('should expose raw middleware', function () { 18 | assert.equal(typeof express.raw, 'function') 19 | assert.equal(express.raw.length, 1) 20 | }) 21 | 22 | it('should expose static middleware', function () { 23 | assert.equal(typeof express.static, 'function') 24 | assert.equal(express.static.length, 2) 25 | }) 26 | 27 | it('should expose text middleware', function () { 28 | assert.equal(typeof express.text, 'function') 29 | assert.equal(express.text.length, 1) 30 | }) 31 | 32 | it('should expose urlencoded middleware', function () { 33 | assert.equal(typeof express.urlencoded, 'function') 34 | assert.equal(express.urlencoded.length, 1) 35 | }) 36 | 37 | it('should expose the application prototype', function(){ 38 | assert.strictEqual(typeof express.application, 'object') 39 | assert.strictEqual(typeof express.application.set, 'function') 40 | }) 41 | 42 | it('should expose the request prototype', function(){ 43 | assert.strictEqual(typeof express.request, 'object') 44 | assert.strictEqual(typeof express.request.accepts, 'function') 45 | }) 46 | 47 | it('should expose the response prototype', function(){ 48 | assert.strictEqual(typeof express.response, 'object') 49 | assert.strictEqual(typeof express.response.send, 'function') 50 | }) 51 | 52 | it('should permit modifying the .application prototype', function(){ 53 | express.application.foo = function(){ return 'bar'; }; 54 | assert.strictEqual(express().foo(), 'bar') 55 | }) 56 | 57 | it('should permit modifying the .request prototype', function(done){ 58 | express.request.foo = function(){ return 'bar'; }; 59 | var app = express(); 60 | 61 | app.use(function(req, res, next){ 62 | res.end(req.foo()); 63 | }); 64 | 65 | request(app) 66 | .get('/') 67 | .expect('bar', done); 68 | }) 69 | 70 | it('should permit modifying the .response prototype', function(done){ 71 | express.response.foo = function(){ this.send('bar'); }; 72 | var app = express(); 73 | 74 | app.use(function(req, res, next){ 75 | res.foo(); 76 | }); 77 | 78 | request(app) 79 | .get('/') 80 | .expect('bar', done); 81 | }) 82 | 83 | it('should throw on old middlewares', function(){ 84 | assert.throws(function () { express.bodyParser() }, /Error:.*middleware.*bodyParser/) 85 | assert.throws(function () { express.limit() }, /Error:.*middleware.*limit/) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /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 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.secure', function(){ 8 | describe('when X-Forwarded-Proto is missing', function(){ 9 | it('should return false when http', function(done){ 10 | var app = express(); 11 | 12 | app.get('/', function(req, res){ 13 | res.send(req.secure ? 'yes' : 'no'); 14 | }); 15 | 16 | request(app) 17 | .get('/') 18 | .expect('no', done) 19 | }) 20 | }) 21 | }) 22 | 23 | describe('.secure', function(){ 24 | describe('when X-Forwarded-Proto is present', function(){ 25 | it('should return false when http', function(done){ 26 | var app = express(); 27 | 28 | app.get('/', function(req, res){ 29 | res.send(req.secure ? 'yes' : 'no'); 30 | }); 31 | 32 | request(app) 33 | .get('/') 34 | .set('X-Forwarded-Proto', 'https') 35 | .expect('no', done) 36 | }) 37 | 38 | it('should return true when "trust proxy" is enabled', function(done){ 39 | var app = express(); 40 | 41 | app.enable('trust proxy'); 42 | 43 | app.get('/', function(req, res){ 44 | res.send(req.secure ? 'yes' : 'no'); 45 | }); 46 | 47 | request(app) 48 | .get('/') 49 | .set('X-Forwarded-Proto', 'https') 50 | .expect('yes', done) 51 | }) 52 | 53 | it('should return false when initial proxy is http', function(done){ 54 | var app = express(); 55 | 56 | app.enable('trust proxy'); 57 | 58 | app.get('/', function(req, res){ 59 | res.send(req.secure ? 'yes' : 'no'); 60 | }); 61 | 62 | request(app) 63 | .get('/') 64 | .set('X-Forwarded-Proto', 'http, https') 65 | .expect('no', done) 66 | }) 67 | 68 | it('should return true when initial proxy is https', function(done){ 69 | var app = express(); 70 | 71 | app.enable('trust proxy'); 72 | 73 | app.get('/', function(req, res){ 74 | res.send(req.secure ? 'yes' : 'no'); 75 | }); 76 | 77 | request(app) 78 | .get('/') 79 | .set('X-Forwarded-Proto', 'https, http') 80 | .expect('yes', done) 81 | }) 82 | 83 | describe('when "trust proxy" trusting hop count', function () { 84 | it('should respect X-Forwarded-Proto', function (done) { 85 | var app = express(); 86 | 87 | app.set('trust proxy', 1); 88 | 89 | app.get('/', function (req, res) { 90 | res.send(req.secure ? 'yes' : 'no'); 91 | }); 92 | 93 | request(app) 94 | .get('/') 95 | .set('X-Forwarded-Proto', 'https') 96 | .expect('yes', done) 97 | }) 98 | }) 99 | }) 100 | }) 101 | }) 102 | -------------------------------------------------------------------------------- /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 bodyParser = require('body-parser') 16 | var EventEmitter = require('events').EventEmitter; 17 | var mixin = require('merge-descriptors'); 18 | var proto = require('./application'); 19 | var Route = require('./router/route'); 20 | var Router = require('./router'); 21 | var req = require('./request'); 22 | var res = require('./response'); 23 | 24 | /** 25 | * Expose `createApplication()`. 26 | */ 27 | 28 | exports = module.exports = createApplication; 29 | 30 | /** 31 | * Create an express application. 32 | * 33 | * @return {Function} 34 | * @api public 35 | */ 36 | 37 | function createApplication() { 38 | var app = function(req, res, next) { 39 | app.handle(req, res, next); 40 | }; 41 | 42 | mixin(app, EventEmitter.prototype, false); 43 | mixin(app, proto, false); 44 | 45 | // expose the prototype that will get set on requests 46 | app.request = Object.create(req, { 47 | app: { configurable: true, enumerable: true, writable: true, value: app } 48 | }) 49 | 50 | // expose the prototype that will get set on responses 51 | app.response = Object.create(res, { 52 | app: { configurable: true, enumerable: true, writable: true, value: app } 53 | }) 54 | 55 | app.init(); 56 | return app; 57 | } 58 | 59 | /** 60 | * Expose the prototypes. 61 | */ 62 | 63 | exports.application = proto; 64 | exports.request = req; 65 | exports.response = res; 66 | 67 | /** 68 | * Expose constructors. 69 | */ 70 | 71 | exports.Route = Route; 72 | exports.Router = Router; 73 | 74 | /** 75 | * Expose middleware 76 | */ 77 | 78 | exports.json = bodyParser.json 79 | exports.query = require('./middleware/query'); 80 | exports.raw = bodyParser.raw 81 | exports.static = require('serve-static'); 82 | exports.text = bodyParser.text 83 | exports.urlencoded = bodyParser.urlencoded 84 | 85 | /** 86 | * Replace removed middleware with an appropriate error message. 87 | */ 88 | 89 | var removedMiddlewares = [ 90 | 'bodyParser', 91 | 'compress', 92 | 'cookieSession', 93 | 'session', 94 | 'logger', 95 | 'cookieParser', 96 | 'favicon', 97 | 'responseTime', 98 | 'errorHandler', 99 | 'timeout', 100 | 'methodOverride', 101 | 'vhost', 102 | 'csrf', 103 | 'directory', 104 | 'limit', 105 | 'multipart', 106 | 'staticCache' 107 | ] 108 | 109 | removedMiddlewares.forEach(function (name) { 110 | Object.defineProperty(exports, name, { 111 | get: function () { 112 | 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.'); 113 | }, 114 | configurable: true 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /test/res.location.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.location(url)', function(){ 8 | it('should set the header', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.location('http://google.com').end(); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('Location', 'http://google.com') 18 | .expect(200, done) 19 | }) 20 | 21 | it('should encode "url"', function (done) { 22 | var app = express() 23 | 24 | app.use(function (req, res) { 25 | res.location('https://google.com?q=\u2603 §10').end() 26 | }) 27 | 28 | request(app) 29 | .get('/') 30 | .expect('Location', 'https://google.com?q=%E2%98%83%20%C2%A710') 31 | .expect(200, done) 32 | }) 33 | 34 | it('should not touch already-encoded sequences in "url"', function (done) { 35 | var app = express() 36 | 37 | app.use(function (req, res) { 38 | res.location('https://google.com?q=%A710').end() 39 | }) 40 | 41 | request(app) 42 | .get('/') 43 | .expect('Location', 'https://google.com?q=%A710') 44 | .expect(200, done) 45 | }) 46 | 47 | describe('when url is "back"', function () { 48 | it('should set location from "Referer" header', function (done) { 49 | var app = express() 50 | 51 | app.use(function (req, res) { 52 | res.location('back').end() 53 | }) 54 | 55 | request(app) 56 | .get('/') 57 | .set('Referer', '/some/page.html') 58 | .expect('Location', '/some/page.html') 59 | .expect(200, done) 60 | }) 61 | 62 | it('should set location from "Referrer" header', function (done) { 63 | var app = express() 64 | 65 | app.use(function (req, res) { 66 | res.location('back').end() 67 | }) 68 | 69 | request(app) 70 | .get('/') 71 | .set('Referrer', '/some/page.html') 72 | .expect('Location', '/some/page.html') 73 | .expect(200, done) 74 | }) 75 | 76 | it('should prefer "Referrer" header', function (done) { 77 | var app = express() 78 | 79 | app.use(function (req, res) { 80 | res.location('back').end() 81 | }) 82 | 83 | request(app) 84 | .get('/') 85 | .set('Referer', '/some/page1.html') 86 | .set('Referrer', '/some/page2.html') 87 | .expect('Location', '/some/page2.html') 88 | .expect(200, done) 89 | }) 90 | 91 | it('should set the header to "/" without referrer', function (done) { 92 | var app = express() 93 | 94 | app.use(function (req, res) { 95 | res.location('back').end() 96 | }) 97 | 98 | request(app) 99 | .get('/') 100 | .expect('Location', '/') 101 | .expect(200, done) 102 | }) 103 | }) 104 | }) 105 | }) 106 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express", 3 | "description": "Fast, unopinionated, minimalist web framework", 4 | "version": "4.18.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 | "http", 24 | "rest", 25 | "restful", 26 | "router", 27 | "app", 28 | "api" 29 | ], 30 | "dependencies": { 31 | "accepts": "~1.3.8", 32 | "array-flatten": "1.1.1", 33 | "body-parser": "1.20.2", 34 | "content-disposition": "0.5.4", 35 | "content-type": "~1.0.4", 36 | "cookie": "0.5.0", 37 | "cookie-signature": "1.0.6", 38 | "debug": "2.6.9", 39 | "depd": "2.0.0", 40 | "encodeurl": "~1.0.2", 41 | "escape-html": "~1.0.3", 42 | "etag": "~1.8.1", 43 | "finalhandler": "1.2.0", 44 | "fresh": "0.5.2", 45 | "http-errors": "2.0.0", 46 | "merge-descriptors": "1.0.1", 47 | "methods": "~1.1.2", 48 | "on-finished": "2.4.1", 49 | "parseurl": "~1.3.3", 50 | "path-to-regexp": "0.1.7", 51 | "proxy-addr": "~2.0.7", 52 | "qs": "6.11.0", 53 | "range-parser": "~1.2.1", 54 | "safe-buffer": "5.2.1", 55 | "send": "0.18.0", 56 | "serve-static": "1.15.0", 57 | "setprototypeof": "1.2.0", 58 | "statuses": "2.0.1", 59 | "type-is": "~1.6.18", 60 | "utils-merge": "1.0.1", 61 | "vary": "~1.1.2" 62 | }, 63 | "devDependencies": { 64 | "after": "0.8.2", 65 | "connect-redis": "3.4.2", 66 | "cookie-parser": "1.4.6", 67 | "cookie-session": "2.0.0", 68 | "ejs": "3.1.9", 69 | "eslint": "8.36.0", 70 | "express-session": "1.17.2", 71 | "hbs": "4.2.0", 72 | "marked": "0.7.0", 73 | "method-override": "3.0.0", 74 | "mocha": "10.2.0", 75 | "morgan": "1.10.0", 76 | "multiparty": "4.2.3", 77 | "nyc": "15.1.0", 78 | "pbkdf2-password": "1.2.1", 79 | "supertest": "6.3.0", 80 | "vhost": "~3.0.2" 81 | }, 82 | "engines": { 83 | "node": ">= 0.10.0" 84 | }, 85 | "files": [ 86 | "LICENSE", 87 | "History.md", 88 | "Readme.md", 89 | "index.js", 90 | "lib/" 91 | ], 92 | "scripts": { 93 | "lint": "eslint .", 94 | "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/", 95 | "test-ci": "nyc --reporter=lcovonly --reporter=text npm test", 96 | "test-cov": "nyc --reporter=html --reporter=text npm test", 97 | "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /test/req.protocol.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.protocol', function(){ 8 | it('should return the protocol string', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.end(req.protocol); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('http', done); 18 | }) 19 | 20 | describe('when "trust proxy" is enabled', function(){ 21 | it('should respect X-Forwarded-Proto', function(done){ 22 | var app = express(); 23 | 24 | app.enable('trust proxy'); 25 | 26 | app.use(function(req, res){ 27 | res.end(req.protocol); 28 | }); 29 | 30 | request(app) 31 | .get('/') 32 | .set('X-Forwarded-Proto', 'https') 33 | .expect('https', done); 34 | }) 35 | 36 | it('should default to the socket addr if X-Forwarded-Proto not present', function(done){ 37 | var app = express(); 38 | 39 | app.enable('trust proxy'); 40 | 41 | app.use(function(req, res){ 42 | req.connection.encrypted = true; 43 | res.end(req.protocol); 44 | }); 45 | 46 | request(app) 47 | .get('/') 48 | .expect('https', done); 49 | }) 50 | 51 | it('should ignore X-Forwarded-Proto if socket addr not trusted', function(done){ 52 | var app = express(); 53 | 54 | app.set('trust proxy', '10.0.0.1'); 55 | 56 | app.use(function(req, res){ 57 | res.end(req.protocol); 58 | }); 59 | 60 | request(app) 61 | .get('/') 62 | .set('X-Forwarded-Proto', 'https') 63 | .expect('http', done); 64 | }) 65 | 66 | it('should default to http', function(done){ 67 | var app = express(); 68 | 69 | app.enable('trust proxy'); 70 | 71 | app.use(function(req, res){ 72 | res.end(req.protocol); 73 | }); 74 | 75 | request(app) 76 | .get('/') 77 | .expect('http', done); 78 | }) 79 | 80 | describe('when trusting hop count', function () { 81 | it('should respect X-Forwarded-Proto', function (done) { 82 | var app = express(); 83 | 84 | app.set('trust proxy', 1); 85 | 86 | app.use(function (req, res) { 87 | res.end(req.protocol); 88 | }); 89 | 90 | request(app) 91 | .get('/') 92 | .set('X-Forwarded-Proto', 'https') 93 | .expect('https', done); 94 | }) 95 | }) 96 | }) 97 | 98 | describe('when "trust proxy" is disabled', function(){ 99 | it('should ignore X-Forwarded-Proto', function(done){ 100 | var app = express(); 101 | 102 | app.use(function(req, res){ 103 | res.end(req.protocol); 104 | }); 105 | 106 | request(app) 107 | .get('/') 108 | .set('X-Forwarded-Proto', 'https') 109 | .expect('http', done); 110 | }) 111 | }) 112 | }) 113 | }) 114 | -------------------------------------------------------------------------------- /examples/error-pages/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | var path = require('path'); 9 | var app = module.exports = express(); 10 | var logger = require('morgan'); 11 | var silent = process.env.NODE_ENV === 'test' 12 | 13 | // general config 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'ejs'); 16 | 17 | // our custom "verbose errors" setting 18 | // which we can use in the templates 19 | // via settings['verbose errors'] 20 | app.enable('verbose errors'); 21 | 22 | // disable them in production 23 | // use $ NODE_ENV=production node examples/error-pages 24 | if (app.settings.env === 'production') app.disable('verbose errors') 25 | 26 | silent || app.use(logger('dev')); 27 | 28 | // Routes 29 | 30 | app.get('/', function(req, res){ 31 | res.render('index.ejs'); 32 | }); 33 | 34 | app.get('/404', function(req, res, next){ 35 | // trigger a 404 since no other middleware 36 | // will match /404 after this one, and we're not 37 | // responding here 38 | next(); 39 | }); 40 | 41 | app.get('/403', function(req, res, next){ 42 | // trigger a 403 error 43 | var err = new Error('not allowed!'); 44 | err.status = 403; 45 | next(err); 46 | }); 47 | 48 | app.get('/500', function(req, res, next){ 49 | // trigger a generic (500) error 50 | next(new Error('keyboard cat!')); 51 | }); 52 | 53 | // Error handlers 54 | 55 | // Since this is the last non-error-handling 56 | // middleware use()d, we assume 404, as nothing else 57 | // responded. 58 | 59 | // $ curl http://localhost:3000/notfound 60 | // $ curl http://localhost:3000/notfound -H "Accept: application/json" 61 | // $ curl http://localhost:3000/notfound -H "Accept: text/plain" 62 | 63 | app.use(function(req, res, next){ 64 | res.status(404); 65 | 66 | res.format({ 67 | html: function () { 68 | res.render('404', { url: req.url }) 69 | }, 70 | json: function () { 71 | res.json({ error: 'Not found' }) 72 | }, 73 | default: function () { 74 | res.type('txt').send('Not found') 75 | } 76 | }) 77 | }); 78 | 79 | // error-handling middleware, take the same form 80 | // as regular middleware, however they require an 81 | // arity of 4, aka the signature (err, req, res, next). 82 | // when connect has an error, it will invoke ONLY error-handling 83 | // middleware. 84 | 85 | // If we were to next() here any remaining non-error-handling 86 | // middleware would then be executed, or if we next(err) to 87 | // continue passing the error, only error-handling middleware 88 | // would remain being executed, however here 89 | // we simply respond with an error page. 90 | 91 | app.use(function(err, req, res, next){ 92 | // we may use properties of the error object 93 | // here and next(err) appropriately, or if 94 | // we possibly recovered from the error, simply next(). 95 | res.status(err.status || 500); 96 | res.render('500', { error: err }); 97 | }); 98 | 99 | /* istanbul ignore next */ 100 | if (!module.parent) { 101 | app.listen(3000); 102 | console.log('Express started on port 3000'); 103 | } 104 | -------------------------------------------------------------------------------- /test/app.options.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('OPTIONS', function(){ 7 | it('should default to the routes defined', function(done){ 8 | var app = express(); 9 | 10 | app.del('/', function(){}); 11 | app.get('/users', function(req, res){}); 12 | app.put('/users', function(req, res){}); 13 | 14 | request(app) 15 | .options('/users') 16 | .expect('Allow', 'GET,HEAD,PUT') 17 | .expect(200, 'GET,HEAD,PUT', done); 18 | }) 19 | 20 | it('should only include each method once', function(done){ 21 | var app = express(); 22 | 23 | app.del('/', function(){}); 24 | app.get('/users', function(req, res){}); 25 | app.put('/users', function(req, res){}); 26 | app.get('/users', function(req, res){}); 27 | 28 | request(app) 29 | .options('/users') 30 | .expect('Allow', 'GET,HEAD,PUT') 31 | .expect(200, 'GET,HEAD,PUT', done); 32 | }) 33 | 34 | it('should not be affected by app.all', function(done){ 35 | var app = express(); 36 | 37 | app.get('/', function(){}); 38 | app.get('/users', function(req, res){}); 39 | app.put('/users', function(req, res){}); 40 | app.all('/users', function(req, res, next){ 41 | res.setHeader('x-hit', '1'); 42 | next(); 43 | }); 44 | 45 | request(app) 46 | .options('/users') 47 | .expect('x-hit', '1') 48 | .expect('Allow', 'GET,HEAD,PUT') 49 | .expect(200, 'GET,HEAD,PUT', done); 50 | }) 51 | 52 | it('should not respond if the path is not defined', function(done){ 53 | var app = express(); 54 | 55 | app.get('/users', function(req, res){}); 56 | 57 | request(app) 58 | .options('/other') 59 | .expect(404, done); 60 | }) 61 | 62 | it('should forward requests down the middleware chain', function(done){ 63 | var app = express(); 64 | var router = new express.Router(); 65 | 66 | router.get('/users', function(req, res){}); 67 | app.use(router); 68 | app.get('/other', function(req, res){}); 69 | 70 | request(app) 71 | .options('/other') 72 | .expect('Allow', 'GET,HEAD') 73 | .expect(200, 'GET,HEAD', done); 74 | }) 75 | 76 | describe('when error occurs in response handler', function () { 77 | it('should pass error to callback', function (done) { 78 | var app = express(); 79 | var router = express.Router(); 80 | 81 | router.get('/users', function(req, res){}); 82 | 83 | app.use(function (req, res, next) { 84 | res.writeHead(200); 85 | next(); 86 | }); 87 | app.use(router); 88 | app.use(function (err, req, res, next) { 89 | res.end('true'); 90 | }); 91 | 92 | request(app) 93 | .options('/users') 94 | .expect(200, 'true', done) 95 | }) 96 | }) 97 | }) 98 | 99 | describe('app.options()', function(){ 100 | it('should override the default behavior', function(done){ 101 | var app = express(); 102 | 103 | app.options('/users', function(req, res){ 104 | res.set('Allow', 'GET'); 105 | res.send('GET'); 106 | }); 107 | 108 | app.get('/users', function(req, res){}); 109 | app.put('/users', function(req, res){}); 110 | 111 | request(app) 112 | .options('/users') 113 | .expect('GET') 114 | .expect('Allow', 'GET', done); 115 | }) 116 | }) 117 | -------------------------------------------------------------------------------- /test/res.append.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var express = require('..') 5 | var request = require('supertest') 6 | 7 | describe('res', function () { 8 | describe('.append(field, val)', function () { 9 | it('should append multiple headers', function (done) { 10 | var app = express() 11 | 12 | app.use(function (req, res, next) { 13 | res.append('Set-Cookie', 'foo=bar') 14 | next() 15 | }) 16 | 17 | app.use(function (req, res) { 18 | res.append('Set-Cookie', 'fizz=buzz') 19 | res.end() 20 | }) 21 | 22 | request(app) 23 | .get('/') 24 | .expect(200) 25 | .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) 26 | .end(done) 27 | }) 28 | 29 | it('should accept array of values', function (done) { 30 | var app = express() 31 | 32 | app.use(function (req, res, next) { 33 | res.append('Set-Cookie', ['foo=bar', 'fizz=buzz']) 34 | res.end() 35 | }) 36 | 37 | request(app) 38 | .get('/') 39 | .expect(200) 40 | .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) 41 | .end(done) 42 | }) 43 | 44 | it('should get reset by res.set(field, val)', function (done) { 45 | var app = express() 46 | 47 | app.use(function (req, res, next) { 48 | res.append('Set-Cookie', 'foo=bar') 49 | res.append('Set-Cookie', 'fizz=buzz') 50 | next() 51 | }) 52 | 53 | app.use(function (req, res) { 54 | res.set('Set-Cookie', 'pet=tobi') 55 | res.end() 56 | }); 57 | 58 | request(app) 59 | .get('/') 60 | .expect(200) 61 | .expect(shouldHaveHeaderValues('Set-Cookie', ['pet=tobi'])) 62 | .end(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('Set-Cookie', 'foo=bar') 70 | next() 71 | }) 72 | 73 | app.use(function(req, res){ 74 | res.append('Set-Cookie', 'fizz=buzz') 75 | res.end() 76 | }) 77 | 78 | request(app) 79 | .get('/') 80 | .expect(200) 81 | .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar', 'fizz=buzz'])) 82 | .end(done) 83 | }) 84 | 85 | it('should work together with res.cookie', function (done) { 86 | var app = express() 87 | 88 | app.use(function (req, res, next) { 89 | res.cookie('foo', 'bar') 90 | next() 91 | }) 92 | 93 | app.use(function (req, res) { 94 | res.append('Set-Cookie', 'fizz=buzz') 95 | res.end() 96 | }) 97 | 98 | request(app) 99 | .get('/') 100 | .expect(200) 101 | .expect(shouldHaveHeaderValues('Set-Cookie', ['foo=bar; Path=/', 'fizz=buzz'])) 102 | .end(done) 103 | }) 104 | }) 105 | }) 106 | 107 | function shouldHaveHeaderValues (key, values) { 108 | return function (res) { 109 | var headers = res.headers[key.toLowerCase()] 110 | assert.ok(headers, 'should have header "' + key + '"') 111 | assert.strictEqual(headers.length, values.length, 'should have ' + values.length + ' occurances of "' + key + '"') 112 | for (var i = 0; i < values.length; i++) { 113 | assert.strictEqual(headers[i], values[i]) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /test/req.ip.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.ip', function(){ 8 | describe('when X-Forwarded-For is present', function(){ 9 | describe('when "trust proxy" is enabled', function(){ 10 | it('should return the client addr', function(done){ 11 | var app = express(); 12 | 13 | app.enable('trust proxy'); 14 | 15 | app.use(function(req, res, next){ 16 | res.send(req.ip); 17 | }); 18 | 19 | request(app) 20 | .get('/') 21 | .set('X-Forwarded-For', 'client, p1, p2') 22 | .expect('client', done); 23 | }) 24 | 25 | it('should return the addr after trusted proxy based on count', function (done) { 26 | var app = express(); 27 | 28 | app.set('trust proxy', 2); 29 | 30 | app.use(function(req, res, next){ 31 | res.send(req.ip); 32 | }); 33 | 34 | request(app) 35 | .get('/') 36 | .set('X-Forwarded-For', 'client, p1, p2') 37 | .expect('p1', done); 38 | }) 39 | 40 | it('should return the addr after trusted proxy based on list', function (done) { 41 | var app = express() 42 | 43 | app.set('trust proxy', '10.0.0.1, 10.0.0.2, 127.0.0.1, ::1') 44 | 45 | app.get('/', function (req, res) { 46 | res.send(req.ip) 47 | }) 48 | 49 | request(app) 50 | .get('/') 51 | .set('X-Forwarded-For', '10.0.0.2, 10.0.0.3, 10.0.0.1', '10.0.0.4') 52 | .expect('10.0.0.3', done) 53 | }) 54 | 55 | it('should return the addr after trusted proxy, from sub app', function (done) { 56 | var app = express(); 57 | var sub = express(); 58 | 59 | app.set('trust proxy', 2); 60 | app.use(sub); 61 | 62 | sub.use(function (req, res, next) { 63 | res.send(req.ip); 64 | }); 65 | 66 | request(app) 67 | .get('/') 68 | .set('X-Forwarded-For', 'client, p1, p2') 69 | .expect(200, 'p1', done); 70 | }) 71 | }) 72 | 73 | describe('when "trust proxy" is disabled', function(){ 74 | it('should return the remote address', function(done){ 75 | var app = express(); 76 | 77 | app.use(function(req, res, next){ 78 | res.send(req.ip); 79 | }); 80 | 81 | var test = request(app).get('/') 82 | test.set('X-Forwarded-For', 'client, p1, p2') 83 | test.expect(200, getExpectedClientAddress(test._server), done); 84 | }) 85 | }) 86 | }) 87 | 88 | describe('when X-Forwarded-For is not present', function(){ 89 | it('should return the remote address', function(done){ 90 | var app = express(); 91 | 92 | app.enable('trust proxy'); 93 | 94 | app.use(function(req, res, next){ 95 | res.send(req.ip); 96 | }); 97 | 98 | var test = request(app).get('/') 99 | test.expect(200, getExpectedClientAddress(test._server), done) 100 | }) 101 | }) 102 | }) 103 | }) 104 | 105 | /** 106 | * Get the local client address depending on AF_NET of server 107 | */ 108 | 109 | function getExpectedClientAddress(server) { 110 | return server.address().address === '::' 111 | ? '::ffff:127.0.0.1' 112 | : '127.0.0.1'; 113 | } 114 | -------------------------------------------------------------------------------- /test/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert') 4 | var express = require('..') 5 | var request = require('supertest') 6 | 7 | describe('app', function(){ 8 | it('should inherit from event emitter', function(done){ 9 | var app = express(); 10 | app.on('foo', done); 11 | app.emit('foo'); 12 | }) 13 | 14 | it('should be callable', function(){ 15 | var app = express(); 16 | assert.equal(typeof app, 'function'); 17 | }) 18 | 19 | it('should 404 without routes', function(done){ 20 | request(express()) 21 | .get('/') 22 | .expect(404, done); 23 | }) 24 | }) 25 | 26 | describe('app.parent', function(){ 27 | it('should return the parent when mounted', function(){ 28 | var app = express() 29 | , blog = express() 30 | , blogAdmin = express(); 31 | 32 | app.use('/blog', blog); 33 | blog.use('/admin', blogAdmin); 34 | 35 | assert(!app.parent, 'app.parent'); 36 | assert.strictEqual(blog.parent, app) 37 | assert.strictEqual(blogAdmin.parent, blog) 38 | }) 39 | }) 40 | 41 | describe('app.mountpath', function(){ 42 | it('should return the mounted path', function(){ 43 | var admin = express(); 44 | var app = express(); 45 | var blog = express(); 46 | var fallback = express(); 47 | 48 | app.use('/blog', blog); 49 | app.use(fallback); 50 | blog.use('/admin', admin); 51 | 52 | assert.strictEqual(admin.mountpath, '/admin') 53 | assert.strictEqual(app.mountpath, '/') 54 | assert.strictEqual(blog.mountpath, '/blog') 55 | assert.strictEqual(fallback.mountpath, '/') 56 | }) 57 | }) 58 | 59 | describe('app.router', function(){ 60 | it('should throw with notice', function(done){ 61 | var app = express() 62 | 63 | try { 64 | app.router; 65 | } catch(err) { 66 | done(); 67 | } 68 | }) 69 | }) 70 | 71 | describe('app.path()', function(){ 72 | it('should return the canonical', function(){ 73 | var app = express() 74 | , blog = express() 75 | , blogAdmin = express(); 76 | 77 | app.use('/blog', blog); 78 | blog.use('/admin', blogAdmin); 79 | 80 | assert.strictEqual(app.path(), '') 81 | assert.strictEqual(blog.path(), '/blog') 82 | assert.strictEqual(blogAdmin.path(), '/blog/admin') 83 | }) 84 | }) 85 | 86 | describe('in development', function(){ 87 | before(function () { 88 | this.env = process.env.NODE_ENV 89 | process.env.NODE_ENV = 'development' 90 | }) 91 | 92 | after(function () { 93 | process.env.NODE_ENV = this.env 94 | }) 95 | 96 | it('should disable "view cache"', function(){ 97 | var app = express(); 98 | assert.ok(!app.enabled('view cache')) 99 | }) 100 | }) 101 | 102 | describe('in production', function(){ 103 | before(function () { 104 | this.env = process.env.NODE_ENV 105 | process.env.NODE_ENV = 'production' 106 | }) 107 | 108 | after(function () { 109 | process.env.NODE_ENV = this.env 110 | }) 111 | 112 | it('should enable "view cache"', function(){ 113 | var app = express(); 114 | assert.ok(app.enabled('view cache')) 115 | }) 116 | }) 117 | 118 | describe('without NODE_ENV', function(){ 119 | before(function () { 120 | this.env = process.env.NODE_ENV 121 | process.env.NODE_ENV = '' 122 | }) 123 | 124 | after(function () { 125 | process.env.NODE_ENV = this.env 126 | }) 127 | 128 | it('should default to development', function(){ 129 | var app = express(); 130 | assert.strictEqual(app.get('env'), 'development') 131 | }) 132 | }) 133 | -------------------------------------------------------------------------------- /test/res.set.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('..'); 4 | var request = require('supertest'); 5 | 6 | describe('res', function(){ 7 | describe('.set(field, value)', function(){ 8 | it('should set the response header field', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res){ 12 | res.set('Content-Type', 'text/x-foo; charset=utf-8').end(); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('Content-Type', 'text/x-foo; charset=utf-8') 18 | .end(done); 19 | }) 20 | 21 | it('should coerce to a string', function (done) { 22 | var app = express(); 23 | 24 | app.use(function (req, res) { 25 | res.set('X-Number', 123); 26 | res.end(typeof res.get('X-Number')); 27 | }); 28 | 29 | request(app) 30 | .get('/') 31 | .expect('X-Number', '123') 32 | .expect(200, 'string', done); 33 | }) 34 | }) 35 | 36 | describe('.set(field, values)', function(){ 37 | it('should set multiple response header fields', function(done){ 38 | var app = express(); 39 | 40 | app.use(function(req, res){ 41 | res.set('Set-Cookie', ["type=ninja", "language=javascript"]); 42 | res.send(res.get('Set-Cookie')); 43 | }); 44 | 45 | request(app) 46 | .get('/') 47 | .expect('["type=ninja","language=javascript"]', done); 48 | }) 49 | 50 | it('should coerce to an array of strings', function (done) { 51 | var app = express(); 52 | 53 | app.use(function (req, res) { 54 | res.set('X-Numbers', [123, 456]); 55 | res.end(JSON.stringify(res.get('X-Numbers'))); 56 | }); 57 | 58 | request(app) 59 | .get('/') 60 | .expect('X-Numbers', '123, 456') 61 | .expect(200, '["123","456"]', done); 62 | }) 63 | 64 | it('should not set a charset of one is already set', function (done) { 65 | var app = express(); 66 | 67 | app.use(function (req, res) { 68 | res.set('Content-Type', 'text/html; charset=lol'); 69 | res.end(); 70 | }); 71 | 72 | request(app) 73 | .get('/') 74 | .expect('Content-Type', 'text/html; charset=lol') 75 | .expect(200, done); 76 | }) 77 | 78 | it('should throw when Content-Type is an array', function (done) { 79 | var app = express() 80 | 81 | app.use(function (req, res) { 82 | res.set('Content-Type', ['text/html']) 83 | res.end() 84 | }); 85 | 86 | request(app) 87 | .get('/') 88 | .expect(500, /TypeError: Content-Type cannot be set to an Array/, done) 89 | }) 90 | }) 91 | 92 | describe('.set(object)', function(){ 93 | it('should set multiple fields', function(done){ 94 | var app = express(); 95 | 96 | app.use(function(req, res){ 97 | res.set({ 98 | 'X-Foo': 'bar', 99 | 'X-Bar': 'baz' 100 | }).end(); 101 | }); 102 | 103 | request(app) 104 | .get('/') 105 | .expect('X-Foo', 'bar') 106 | .expect('X-Bar', 'baz') 107 | .end(done); 108 | }) 109 | 110 | it('should coerce to a string', function (done) { 111 | var app = express(); 112 | 113 | app.use(function (req, res) { 114 | res.set({ 'X-Number': 123 }); 115 | res.end(typeof res.get('X-Number')); 116 | }); 117 | 118 | request(app) 119 | .get('/') 120 | .expect('X-Number', '123') 121 | .expect(200, 'string', done); 122 | }) 123 | }) 124 | }) 125 | -------------------------------------------------------------------------------- /test/req.accepts.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var express = require('../') 4 | , request = require('supertest'); 5 | 6 | describe('req', function(){ 7 | describe('.accepts(type)', function(){ 8 | it('should return true when Accept is not present', function(done){ 9 | var app = express(); 10 | 11 | app.use(function(req, res, next){ 12 | res.end(req.accepts('json') ? 'yes' : 'no'); 13 | }); 14 | 15 | request(app) 16 | .get('/') 17 | .expect('yes', done); 18 | }) 19 | 20 | it('should return true when present', function(done){ 21 | var app = express(); 22 | 23 | app.use(function(req, res, next){ 24 | res.end(req.accepts('json') ? 'yes' : 'no'); 25 | }); 26 | 27 | request(app) 28 | .get('/') 29 | .set('Accept', 'application/json') 30 | .expect('yes', done); 31 | }) 32 | 33 | it('should return false otherwise', function(done){ 34 | var app = express(); 35 | 36 | app.use(function(req, res, next){ 37 | res.end(req.accepts('json') ? 'yes' : 'no'); 38 | }); 39 | 40 | request(app) 41 | .get('/') 42 | .set('Accept', 'text/html') 43 | .expect('no', done); 44 | }) 45 | }) 46 | 47 | it('should accept an argument list of type names', function(done){ 48 | var app = express(); 49 | 50 | app.use(function(req, res, next){ 51 | res.end(req.accepts('json', 'html')); 52 | }); 53 | 54 | request(app) 55 | .get('/') 56 | .set('Accept', 'application/json') 57 | .expect('json', done); 58 | }) 59 | 60 | describe('.accepts(types)', function(){ 61 | it('should return the first when Accept is not present', function(done){ 62 | var app = express(); 63 | 64 | app.use(function(req, res, next){ 65 | res.end(req.accepts(['json', 'html'])); 66 | }); 67 | 68 | request(app) 69 | .get('/') 70 | .expect('json', done); 71 | }) 72 | 73 | it('should return the first acceptable type', function(done){ 74 | var app = express(); 75 | 76 | app.use(function(req, res, next){ 77 | res.end(req.accepts(['json', 'html'])); 78 | }); 79 | 80 | request(app) 81 | .get('/') 82 | .set('Accept', 'text/html') 83 | .expect('html', done); 84 | }) 85 | 86 | it('should return false when no match is made', function(done){ 87 | var app = express(); 88 | 89 | app.use(function(req, res, next){ 90 | res.end(req.accepts(['text/html', 'application/json']) ? 'yup' : 'nope'); 91 | }); 92 | 93 | request(app) 94 | .get('/') 95 | .set('Accept', 'foo/bar, bar/baz') 96 | .expect('nope', done); 97 | }) 98 | 99 | it('should take quality into account', function(done){ 100 | var app = express(); 101 | 102 | app.use(function(req, res, next){ 103 | res.end(req.accepts(['text/html', 'application/json'])); 104 | }); 105 | 106 | request(app) 107 | .get('/') 108 | .set('Accept', '*/html; q=.5, application/json') 109 | .expect('application/json', done); 110 | }) 111 | 112 | it('should return the first acceptable type with canonical mime types', function(done){ 113 | var app = express(); 114 | 115 | app.use(function(req, res, next){ 116 | res.end(req.accepts(['application/json', 'text/html'])); 117 | }); 118 | 119 | request(app) 120 | .get('/') 121 | .set('Accept', '*/html') 122 | .expect('text/html', done); 123 | }) 124 | }) 125 | }) 126 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var assert = require('assert'); 4 | var Buffer = require('safe-buffer').Buffer 5 | var utils = require('../lib/utils'); 6 | 7 | describe('utils.etag(body, encoding)', function(){ 8 | it('should support strings', function(){ 9 | assert.strictEqual(utils.etag('express!'), 10 | '"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') 11 | }) 12 | 13 | it('should support utf8 strings', function(){ 14 | assert.strictEqual(utils.etag('express❤', 'utf8'), 15 | '"a-JBiXf7GyzxwcrxY4hVXUwa7tmks"') 16 | }) 17 | 18 | it('should support buffer', function(){ 19 | assert.strictEqual(utils.etag(Buffer.from('express!')), 20 | '"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') 21 | }) 22 | 23 | it('should support empty string', function(){ 24 | assert.strictEqual(utils.etag(''), 25 | '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"') 26 | }) 27 | }) 28 | 29 | describe('utils.setCharset(type, charset)', function () { 30 | it('should do anything without type', function () { 31 | assert.strictEqual(utils.setCharset(), undefined); 32 | }); 33 | 34 | it('should return type if not given charset', function () { 35 | assert.strictEqual(utils.setCharset('text/html'), 'text/html'); 36 | }); 37 | 38 | it('should keep charset if not given charset', function () { 39 | assert.strictEqual(utils.setCharset('text/html; charset=utf-8'), 'text/html; charset=utf-8'); 40 | }); 41 | 42 | it('should set charset', function () { 43 | assert.strictEqual(utils.setCharset('text/html', 'utf-8'), 'text/html; charset=utf-8'); 44 | }); 45 | 46 | it('should override charset', function () { 47 | assert.strictEqual(utils.setCharset('text/html; charset=iso-8859-1', 'utf-8'), 'text/html; charset=utf-8'); 48 | }); 49 | }); 50 | 51 | describe('utils.wetag(body, encoding)', function(){ 52 | it('should support strings', function(){ 53 | assert.strictEqual(utils.wetag('express!'), 54 | 'W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') 55 | }) 56 | 57 | it('should support utf8 strings', function(){ 58 | assert.strictEqual(utils.wetag('express❤', 'utf8'), 59 | 'W/"a-JBiXf7GyzxwcrxY4hVXUwa7tmks"') 60 | }) 61 | 62 | it('should support buffer', function(){ 63 | assert.strictEqual(utils.wetag(Buffer.from('express!')), 64 | 'W/"8-O2uVAFaQ1rZvlKLT14RnuvjPIdg"') 65 | }) 66 | 67 | it('should support empty string', function(){ 68 | assert.strictEqual(utils.wetag(''), 69 | 'W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"') 70 | }) 71 | }) 72 | 73 | describe('utils.isAbsolute()', function(){ 74 | it('should support windows', function(){ 75 | assert(utils.isAbsolute('c:\\')); 76 | assert(utils.isAbsolute('c:/')); 77 | assert(!utils.isAbsolute(':\\')); 78 | }) 79 | 80 | it('should support windows unc', function(){ 81 | assert(utils.isAbsolute('\\\\foo\\bar')) 82 | }) 83 | 84 | it('should support unices', function(){ 85 | assert(utils.isAbsolute('/foo/bar')); 86 | assert(!utils.isAbsolute('foo/bar')); 87 | }) 88 | }) 89 | 90 | describe('utils.flatten(arr)', function(){ 91 | it('should flatten an array', function(){ 92 | var arr = ['one', ['two', ['three', 'four'], 'five']]; 93 | var flat = utils.flatten(arr) 94 | 95 | assert.strictEqual(flat.length, 5) 96 | assert.strictEqual(flat[0], 'one') 97 | assert.strictEqual(flat[1], 'two') 98 | assert.strictEqual(flat[2], 'three') 99 | assert.strictEqual(flat[3], 'four') 100 | assert.strictEqual(flat[4], 'five') 101 | assert.ok(flat.every(function (v) { return typeof v === 'string' })) 102 | }) 103 | }) 104 | -------------------------------------------------------------------------------- /examples/web-service/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var express = require('../../'); 8 | 9 | var app = module.exports = express(); 10 | 11 | // create an error with .status. we 12 | // can then use the property in our 13 | // custom error handler (Connect respects this prop as well) 14 | 15 | function error(status, msg) { 16 | var err = new Error(msg); 17 | err.status = status; 18 | return err; 19 | } 20 | 21 | // if we wanted to supply more than JSON, we could 22 | // use something similar to the content-negotiation 23 | // example. 24 | 25 | // here we validate the API key, 26 | // by mounting this middleware to /api 27 | // meaning only paths prefixed with "/api" 28 | // will cause this middleware to be invoked 29 | 30 | app.use('/api', function(req, res, next){ 31 | var key = req.query['api-key']; 32 | 33 | // key isn't present 34 | if (!key) return next(error(400, 'api key required')); 35 | 36 | // key is invalid 37 | if (apiKeys.indexOf(key) === -1) return next(error(401, 'invalid api key')) 38 | 39 | // all good, store req.key for route access 40 | req.key = key; 41 | next(); 42 | }); 43 | 44 | // map of valid api keys, typically mapped to 45 | // account info with some sort of database like redis. 46 | // api keys do _not_ serve as authentication, merely to 47 | // track API usage or help prevent malicious behavior etc. 48 | 49 | var apiKeys = ['foo', 'bar', 'baz']; 50 | 51 | // these two objects will serve as our faux database 52 | 53 | var repos = [ 54 | { name: 'express', url: 'https://github.com/expressjs/express' }, 55 | { name: 'stylus', url: 'https://github.com/learnboost/stylus' }, 56 | { name: 'cluster', url: 'https://github.com/learnboost/cluster' } 57 | ]; 58 | 59 | var users = [ 60 | { name: 'tobi' } 61 | , { name: 'loki' } 62 | , { name: 'jane' } 63 | ]; 64 | 65 | var userRepos = { 66 | tobi: [repos[0], repos[1]] 67 | , loki: [repos[1]] 68 | , jane: [repos[2]] 69 | }; 70 | 71 | // we now can assume the api key is valid, 72 | // and simply expose the data 73 | 74 | // example: http://localhost:3000/api/users/?api-key=foo 75 | app.get('/api/users', function (req, res) { 76 | res.send(users); 77 | }); 78 | 79 | // example: http://localhost:3000/api/repos/?api-key=foo 80 | app.get('/api/repos', function (req, res) { 81 | res.send(repos); 82 | }); 83 | 84 | // example: http://localhost:3000/api/user/tobi/repos/?api-key=foo 85 | app.get('/api/user/:name/repos', function(req, res, next){ 86 | var name = req.params.name; 87 | var user = userRepos[name]; 88 | 89 | if (user) res.send(user); 90 | else next(); 91 | }); 92 | 93 | // middleware with an arity of 4 are considered 94 | // error handling middleware. When you next(err) 95 | // it will be passed through the defined middleware 96 | // in order, but ONLY those with an arity of 4, ignoring 97 | // regular middleware. 98 | app.use(function(err, req, res, next){ 99 | // whatever you want here, feel free to populate 100 | // properties on `err` to treat it differently in here. 101 | res.status(err.status || 500); 102 | res.send({ error: err.message }); 103 | }); 104 | 105 | // our custom JSON 404 middleware. Since it's placed last 106 | // it will be the last middleware called, if all others 107 | // invoke next() and do not respond. 108 | app.use(function(req, res){ 109 | res.status(404); 110 | res.send({ error: "Sorry, can't find that" }) 111 | }); 112 | 113 | /* istanbul ignore next */ 114 | if (!module.parent) { 115 | app.listen(3000); 116 | console.log('Express started on port 3000'); 117 | } 118 | -------------------------------------------------------------------------------- /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, /