/*!1* express2* Copyright(c) 2009-2013 TJ Holowaychuk3* Copyright(c) 2013 Roman Shtylman4* Copyright(c) 2014-2015 Douglas Christopher Wilson5* MIT Licensed6*/78'use strict';910/**11* Module dependencies.12* @private13*/1415var finalhandler = require('finalhandler');16var Router = require('./router');17var methods = require('methods');18var middleware = require('./middleware/init');19var query = require('./middleware/query');20var debug = require('debug')('express:application');21var View = require('./view');22var http = require('http');23var compileETag = require('./utils').compileETag;24var compileQueryParser = require('./utils').compileQueryParser;25var compileTrust = require('./utils').compileTrust;26var deprecate = require('depd')('express');27var flatten = require('array-flatten');28var merge = require('utils-merge');29var resolve = require('path').resolve;30var setPrototypeOf = require('setprototypeof')31var slice = Array.prototype.slice;3233/**34* Application prototype.35*/3637var app = exports = module.exports = {};3839/**40* Variable for trust proxy inheritance back-compat41* @private42*/4344var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';4546/**47* Initialize the server.48*49* - setup default configuration50* - setup default middleware51* - setup route reflection methods52*53* @private54*/5556app.init = function init() {57this.cache = {};58this.engines = {};59this.settings = {};6061this.defaultConfiguration();62};6364/**65* Initialize application configuration.66* @private67*/6869app.defaultConfiguration = function defaultConfiguration() {70var env = process.env.NODE_ENV || 'development';7172// default settings73this.enable('x-powered-by');74this.set('etag', 'weak');75this.set('env', env);76this.set('query parser', 'extended');77this.set('subdomain offset', 2);78this.set('trust proxy', false);7980// trust proxy inherit back-compat81Object.defineProperty(this.settings, trustProxyDefaultSymbol, {82configurable: true,83value: true84});8586debug('booting in %s mode', env);8788this.on('mount', function onmount(parent) {89// inherit trust proxy90if (this.settings[trustProxyDefaultSymbol] === true91&& typeof parent.settings['trust proxy fn'] === 'function') {92delete this.settings['trust proxy'];93delete this.settings['trust proxy fn'];94}9596// inherit protos97setPrototypeOf(this.request, parent.request)98setPrototypeOf(this.response, parent.response)99setPrototypeOf(this.engines, parent.engines)100setPrototypeOf(this.settings, parent.settings)101});102103// setup locals104this.locals = Object.create(null);105106// top-most app is mounted at /107this.mountpath = '/';108109// default locals110this.locals.settings = this.settings;111112// default configuration113this.set('view', View);114this.set('views', resolve('views'));115this.set('jsonp callback name', 'callback');116117if (env === 'production') {118this.enable('view cache');119}120121Object.defineProperty(this, 'router', {122get: function() {123throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');124}125});126};127128/**129* lazily adds the base router if it has not yet been added.130*131* We cannot add the base router in the defaultConfiguration because132* it reads app settings which might be set after that has run.133*134* @private135*/136app.lazyrouter = function lazyrouter() {137if (!this._router) {138this._router = new Router({139caseSensitive: this.enabled('case sensitive routing'),140strict: this.enabled('strict routing')141});142143this._router.use(query(this.get('query parser fn')));144this._router.use(middleware.init(this));145}146};147148/**149* Dispatch a req, res pair into the application. Starts pipeline processing.150*151* If no callback is provided, then default error handlers will respond152* in the event of an error bubbling through the stack.153*154* @private155*/156157app.handle = function handle(req, res, callback) {158var router = this._router;159160// final handler161var done = callback || finalhandler(req, res, {162env: this.get('env'),163onerror: logerror.bind(this)164});165166// no routes167if (!router) {168debug('no routes defined on app');169done();170return;171}172173router.handle(req, res, done);174};175176/**177* Proxy `Router#use()` to add middleware to the app router.178* See Router#use() documentation for details.179*180* If the _fn_ parameter is an express app, then it will be181* mounted at the _route_ specified.182*183* @public184*/185186app.use = function use(fn) {187var offset = 0;188var path = '/';189190// default path to '/'191// disambiguate app.use([fn])192if (typeof fn !== 'function') {193var arg = fn;194195while (Array.isArray(arg) && arg.length !== 0) {196arg = arg[0];197}198199// first arg is the path200if (typeof arg !== 'function') {201offset = 1;202path = fn;203}204}205206var fns = flatten(slice.call(arguments, offset));207208if (fns.length === 0) {209throw new TypeError('app.use() requires middleware functions');210}211212// setup router213this.lazyrouter();214var router = this._router;215216fns.forEach(function (fn) {217// non-express app218if (!fn || !fn.handle || !fn.set) {219return router.use(path, fn);220}221222debug('.use app under %s', path);223fn.mountpath = path;224fn.parent = this;225226// restore .app property on req and res227router.use(path, function mounted_app(req, res, next) {228var orig = req.app;229fn.handle(req, res, function (err) {230setPrototypeOf(req, orig.request)231setPrototypeOf(res, orig.response)232next(err);233});234});235236// mounted an app237fn.emit('mount', this);238}, this);239240return this;241};242243/**244* Proxy to the app `Router#route()`245* Returns a new `Route` instance for the _path_.246*247* Routes are isolated middleware stacks for specific paths.248* See the Route api docs for details.249*250* @public251*/252253app.route = function route(path) {254this.lazyrouter();255return this._router.route(path);256};257258/**259* Register the given template engine callback `fn`260* as `ext`.261*262* By default will `require()` the engine based on the263* file extension. For example if you try to render264* a "foo.ejs" file Express will invoke the following internally:265*266* app.engine('ejs', require('ejs').__express);267*268* For engines that do not provide `.__express` out of the box,269* or if you wish to "map" a different extension to the template engine270* you may use this method. For example mapping the EJS template engine to271* ".html" files:272*273* app.engine('html', require('ejs').renderFile);274*275* In this case EJS provides a `.renderFile()` method with276* the same signature that Express expects: `(path, options, callback)`,277* though note that it aliases this method as `ejs.__express` internally278* so if you're using ".ejs" extensions you dont need to do anything.279*280* Some template engines do not follow this convention, the281* [Consolidate.js](https://github.com/tj/consolidate.js)282* library was created to map all of node's popular template283* engines to follow this convention, thus allowing them to284* work seamlessly within Express.285*286* @param {String} ext287* @param {Function} fn288* @return {app} for chaining289* @public290*/291292app.engine = function engine(ext, fn) {293if (typeof fn !== 'function') {294throw new Error('callback function required');295}296297// get file extension298var extension = ext[0] !== '.'299? '.' + ext300: ext;301302// store engine303this.engines[extension] = fn;304305return this;306};307308/**309* Proxy to `Router#param()` with one added api feature. The _name_ parameter310* can be an array of names.311*312* See the Router#param() docs for more details.313*314* @param {String|Array} name315* @param {Function} fn316* @return {app} for chaining317* @public318*/319320app.param = function param(name, fn) {321this.lazyrouter();322323if (Array.isArray(name)) {324for (var i = 0; i < name.length; i++) {325this.param(name[i], fn);326}327328return this;329}330331this._router.param(name, fn);332333return this;334};335336/**337* Assign `setting` to `val`, or return `setting`'s value.338*339* app.set('foo', 'bar');340* app.get('foo');341* // => "bar"342*343* Mounted servers inherit their parent server's settings.344*345* @param {String} setting346* @param {*} [val]347* @return {Server} for chaining348* @public349*/350351app.set = function set(setting, val) {352if (arguments.length === 1) {353// app.get(setting)354return this.settings[setting];355}356357debug('set "%s" to %o', setting, val);358359// set value360this.settings[setting] = val;361362// trigger matched settings363switch (setting) {364case 'etag':365this.set('etag fn', compileETag(val));366break;367case 'query parser':368this.set('query parser fn', compileQueryParser(val));369break;370case 'trust proxy':371this.set('trust proxy fn', compileTrust(val));372373// trust proxy inherit back-compat374Object.defineProperty(this.settings, trustProxyDefaultSymbol, {375configurable: true,376value: false377});378379break;380}381382return this;383};384385/**386* Return the app's absolute pathname387* based on the parent(s) that have388* mounted it.389*390* For example if the application was391* mounted as "/admin", which itself392* was mounted as "/blog" then the393* return value would be "/blog/admin".394*395* @return {String}396* @private397*/398399app.path = function path() {400return this.parent401? this.parent.path() + this.mountpath402: '';403};404405/**406* Check if `setting` is enabled (truthy).407*408* app.enabled('foo')409* // => false410*411* app.enable('foo')412* app.enabled('foo')413* // => true414*415* @param {String} setting416* @return {Boolean}417* @public418*/419420app.enabled = function enabled(setting) {421return Boolean(this.set(setting));422};423424/**425* Check if `setting` is disabled.426*427* app.disabled('foo')428* // => true429*430* app.enable('foo')431* app.disabled('foo')432* // => false433*434* @param {String} setting435* @return {Boolean}436* @public437*/438439app.disabled = function disabled(setting) {440return !this.set(setting);441};442443/**444* Enable `setting`.445*446* @param {String} setting447* @return {app} for chaining448* @public449*/450451app.enable = function enable(setting) {452return this.set(setting, true);453};454455/**456* Disable `setting`.457*458* @param {String} setting459* @return {app} for chaining460* @public461*/462463app.disable = function disable(setting) {464return this.set(setting, false);465};466467/**468* Delegate `.VERB(...)` calls to `router.VERB(...)`.469*/470471methods.forEach(function(method){472app[method] = function(path){473if (method === 'get' && arguments.length === 1) {474// app.get(setting)475return this.set(path);476}477478this.lazyrouter();479480var route = this._router.route(path);481route[method].apply(route, slice.call(arguments, 1));482return this;483};484});485486/**487* Special-cased "all" method, applying the given route `path`,488* middleware, and callback to _every_ HTTP method.489*490* @param {String} path491* @param {Function} ...492* @return {app} for chaining493* @public494*/495496app.all = function all(path) {497this.lazyrouter();498499var route = this._router.route(path);500var args = slice.call(arguments, 1);501502for (var i = 0; i < methods.length; i++) {503route[methods[i]].apply(route, args);504}505506return this;507};508509// del -> delete alias510511app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');512513/**514* Render the given view `name` name with `options`515* and a callback accepting an error and the516* rendered template string.517*518* Example:519*520* app.render('email', { name: 'Tobi' }, function(err, html){521* // ...522* })523*524* @param {String} name525* @param {Object|Function} options or fn526* @param {Function} callback527* @public528*/529530app.render = function render(name, options, callback) {531var cache = this.cache;532var done = callback;533var engines = this.engines;534var opts = options;535var renderOptions = {};536var view;537538// support callback function as second arg539if (typeof options === 'function') {540done = options;541opts = {};542}543544// merge app.locals545merge(renderOptions, this.locals);546547// merge options._locals548if (opts._locals) {549merge(renderOptions, opts._locals);550}551552// merge options553merge(renderOptions, opts);554555// set .cache unless explicitly provided556if (renderOptions.cache == null) {557renderOptions.cache = this.enabled('view cache');558}559560// primed cache561if (renderOptions.cache) {562view = cache[name];563}564565// view566if (!view) {567var View = this.get('view');568569view = new View(name, {570defaultEngine: this.get('view engine'),571root: this.get('views'),572engines: engines573});574575if (!view.path) {576var dirs = Array.isArray(view.root) && view.root.length > 1577? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'578: 'directory "' + view.root + '"'579var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);580err.view = view;581return done(err);582}583584// prime the cache585if (renderOptions.cache) {586cache[name] = view;587}588}589590// render591tryRender(view, renderOptions, done);592};593594/**595* Listen for connections.596*597* A node `http.Server` is returned, with this598* application (which is a `Function`) as its599* callback. If you wish to create both an HTTP600* and HTTPS server you may do so with the "http"601* and "https" modules as shown here:602*603* var http = require('http')604* , https = require('https')605* , express = require('express')606* , app = express();607*608* http.createServer(app).listen(80);609* https.createServer({ ... }, app).listen(443);610*611* @return {http.Server}612* @public613*/614615app.listen = function listen() {616var server = http.createServer(this);617return server.listen.apply(server, arguments);618};619620/**621* Log error using console.error.622*623* @param {Error} err624* @private625*/626627function logerror(err) {628/* istanbul ignore next */629if (this.get('env') !== 'test') console.error(err.stack || err.toString());630}631632/**633* Try rendering a view.634* @private635*/636637function tryRender(view, options, callback) {638try {639view.render(options, callback);640} catch (err) {641callback(err);642}643}644645646