thoughts/node_modules/express/lib/application.js
node_modules/express/lib/application.jsRaw
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
16var finalhandler = require('finalhandler');
17var Router = require('./router');
18var methods = require('methods');
19var middleware = require('./middleware/init');
20var query = require('./middleware/query');
21var debug = require('debug')('express:application');
22var View = require('./view');
23var http = require('http');
24var compileETag = require('./utils').compileETag;
25var compileQueryParser = require('./utils').compileQueryParser;
26var compileTrust = require('./utils').compileTrust;
27var deprecate = require('depd')('express');
28var flatten = require('array-flatten');
29var merge = require('utils-merge');
30var resolve = require('path').resolve;
31var slice = Array.prototype.slice;
32
33/**
34 * Application prototype.
35 */
36
37var app = exports = module.exports = {};
38
39/**
40 * Variable for trust proxy inheritance back-compat
41 * @private
42 */
43
44var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
45
46/**
47 * Initialize the server.
48 *
49 * - setup default configuration
50 * - setup default middleware
51 * - setup route reflection methods
52 *
53 * @private
54 */
55
56app.init = function init() {
57 this.cache = {};
58 this.engines = {};
59 this.settings = {};
60
61 this.defaultConfiguration();
62};
63
64/**
65 * Initialize application configuration.
66 * @private
67 */
68
69app.defaultConfiguration = function defaultConfiguration() {
70 var env = process.env.NODE_ENV || 'development';
71
72 // default settings
73 this.enable('x-powered-by');
74 this.set('etag', 'weak');
75 this.set('env', env);
76 this.set('query parser', 'extended');
77 this.set('subdomain offset', 2);
78 this.set('trust proxy', false);
79
80 // trust proxy inherit back-compat
81 Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
82 configurable: true,
83 value: true
84 });
85
86 debug('booting in %s mode', env);
87
88 this.on('mount', function onmount(parent) {
89 // inherit trust proxy
90 if (this.settings[trustProxyDefaultSymbol] === true
91 && typeof parent.settings['trust proxy fn'] === 'function') {
92 delete this.settings['trust proxy'];
93 delete this.settings['trust proxy fn'];
94 }
95
96 // inherit protos
97 this.request.__proto__ = parent.request;
98 this.response.__proto__ = parent.response;
99 this.engines.__proto__ = parent.engines;
100 this.settings.__proto__ = parent.settings;
101 });
102
103 // setup locals
104 this.locals = Object.create(null);
105
106 // top-most app is mounted at /
107 this.mountpath = '/';
108
109 // default locals
110 this.locals.settings = this.settings;
111
112 // default configuration
113 this.set('view', View);
114 this.set('views', resolve('views'));
115 this.set('jsonp callback name', 'callback');
116
117 if (env === 'production') {
118 this.enable('view cache');
119 }
120
121 Object.defineProperty(this, 'router', {
122 get: function() {
123 throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
124 }
125 });
126};
127
128/**
129 * lazily adds the base router if it has not yet been added.
130 *
131 * We cannot add the base router in the defaultConfiguration because
132 * it reads app settings which might be set after that has run.
133 *
134 * @private
135 */
136app.lazyrouter = function lazyrouter() {
137 if (!this._router) {
138 this._router = new Router({
139 caseSensitive: this.enabled('case sensitive routing'),
140 strict: this.enabled('strict routing')
141 });
142
143 this._router.use(query(this.get('query parser fn')));
144 this._router.use(middleware.init(this));
145 }
146};
147
148/**
149 * Dispatch a req, res pair into the application. Starts pipeline processing.
150 *
151 * If no callback is provided, then default error handlers will respond
152 * in the event of an error bubbling through the stack.
153 *
154 * @private
155 */
156
157app.handle = function handle(req, res, callback) {
158 var router = this._router;
159
160 // final handler
161 var done = callback || finalhandler(req, res, {
162 env: this.get('env'),
163 onerror: logerror.bind(this)
164 });
165
166 // no routes
167 if (!router) {
168 debug('no routes defined on app');
169 done();
170 return;
171 }
172
173 router.handle(req, res, done);
174};
175
176/**
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 be
181 * mounted at the _route_ specified.
182 *
183 * @public
184 */
185
186app.use = function use(fn) {
187 var offset = 0;
188 var path = '/';
189
190 // default path to '/'
191 // disambiguate app.use([fn])
192 if (typeof fn !== 'function') {
193 var arg = fn;
194
195 while (Array.isArray(arg) && arg.length !== 0) {
196 arg = arg[0];
197 }
198
199 // first arg is the path
200 if (typeof arg !== 'function') {
201 offset = 1;
202 path = fn;
203 }
204 }
205
206 var fns = flatten(slice.call(arguments, offset));
207
208 if (fns.length === 0) {
209 throw new TypeError('app.use() requires middleware functions');
210 }
211
212 // setup router
213 this.lazyrouter();
214 var router = this._router;
215
216 fns.forEach(function (fn) {
217 // non-express app
218 if (!fn || !fn.handle || !fn.set) {
219 return router.use(path, fn);
220 }
221
222 debug('.use app under %s', path);
223 fn.mountpath = path;
224 fn.parent = this;
225
226 // restore .app property on req and res
227 router.use(path, function mounted_app(req, res, next) {
228 var orig = req.app;
229 fn.handle(req, res, function (err) {
230 req.__proto__ = orig.request;
231 res.__proto__ = orig.response;
232 next(err);
233 });
234 });
235
236 // mounted an app
237 fn.emit('mount', this);
238 }, this);
239
240 return this;
241};
242
243/**
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 * @public
251 */
252
253app.route = function route(path) {
254 this.lazyrouter();
255 return this._router.route(path);
256};
257
258/**
259 * Register the given template engine callback `fn`
260 * as `ext`.
261 *
262 * By default will `require()` the engine based on the
263 * file extension. For example if you try to render
264 * a "foo.jade" file Express will invoke the following internally:
265 *
266 * app.engine('jade', require('jade').__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 engine
270 * you may use this method. For example mapping the EJS template engine to
271 * ".html" files:
272 *
273 * app.engine('html', require('ejs').renderFile);
274 *
275 * In this case EJS provides a `.renderFile()` method with
276 * the same signature that Express expects: `(path, options, callback)`,
277 * though note that it aliases this method as `ejs.__express` internally
278 * so if you're using ".ejs" extensions you dont need to do anything.
279 *
280 * Some template engines do not follow this convention, the
281 * [Consolidate.js](https://github.com/tj/consolidate.js)
282 * library was created to map all of node's popular template
283 * engines to follow this convention, thus allowing them to
284 * work seamlessly within Express.
285 *
286 * @param {String} ext
287 * @param {Function} fn
288 * @return {app} for chaining
289 * @public
290 */
291
292app.engine = function engine(ext, fn) {
293 if (typeof fn !== 'function') {
294 throw new Error('callback function required');
295 }
296
297 // get file extension
298 var extension = ext[0] !== '.'
299 ? '.' + ext
300 : ext;
301
302 // store engine
303 this.engines[extension] = fn;
304
305 return this;
306};
307
308/**
309 * Proxy to `Router#param()` with one added api feature. The _name_ parameter
310 * can be an array of names.
311 *
312 * See the Router#param() docs for more details.
313 *
314 * @param {String|Array} name
315 * @param {Function} fn
316 * @return {app} for chaining
317 * @public
318 */
319
320app.param = function param(name, fn) {
321 this.lazyrouter();
322
323 if (Array.isArray(name)) {
324 for (var i = 0; i < name.length; i++) {
325 this.param(name[i], fn);
326 }
327
328 return this;
329 }
330
331 this._router.param(name, fn);
332
333 return this;
334};
335
336/**
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} setting
346 * @param {*} [val]
347 * @return {Server} for chaining
348 * @public
349 */
350
351app.set = function set(setting, val) {
352 if (arguments.length === 1) {
353 // app.get(setting)
354 return this.settings[setting];
355 }
356
357 debug('set "%s" to %o', setting, val);
358
359 // set value
360 this.settings[setting] = val;
361
362 // trigger matched settings
363 switch (setting) {
364 case 'etag':
365 this.set('etag fn', compileETag(val));
366 break;
367 case 'query parser':
368 this.set('query parser fn', compileQueryParser(val));
369 break;
370 case 'trust proxy':
371 this.set('trust proxy fn', compileTrust(val));
372
373 // trust proxy inherit back-compat
374 Object.defineProperty(this.settings, trustProxyDefaultSymbol, {
375 configurable: true,
376 value: false
377 });
378
379 break;
380 }
381
382 return this;
383};
384
385/**
386 * Return the app's absolute pathname
387 * based on the parent(s) that have
388 * mounted it.
389 *
390 * For example if the application was
391 * mounted as "/admin", which itself
392 * was mounted as "/blog" then the
393 * return value would be "/blog/admin".
394 *
395 * @return {String}
396 * @private
397 */
398
399app.path = function path() {
400 return this.parent
401 ? this.parent.path() + this.mountpath
402 : '';
403};
404
405/**
406 * Check if `setting` is enabled (truthy).
407 *
408 * app.enabled('foo')
409 * // => false
410 *
411 * app.enable('foo')
412 * app.enabled('foo')
413 * // => true
414 *
415 * @param {String} setting
416 * @return {Boolean}
417 * @public
418 */
419
420app.enabled = function enabled(setting) {
421 return Boolean(this.set(setting));
422};
423
424/**
425 * Check if `setting` is disabled.
426 *
427 * app.disabled('foo')
428 * // => true
429 *
430 * app.enable('foo')
431 * app.disabled('foo')
432 * // => false
433 *
434 * @param {String} setting
435 * @return {Boolean}
436 * @public
437 */
438
439app.disabled = function disabled(setting) {
440 return !this.set(setting);
441};
442
443/**
444 * Enable `setting`.
445 *
446 * @param {String} setting
447 * @return {app} for chaining
448 * @public
449 */
450
451app.enable = function enable(setting) {
452 return this.set(setting, true);
453};
454
455/**
456 * Disable `setting`.
457 *
458 * @param {String} setting
459 * @return {app} for chaining
460 * @public
461 */
462
463app.disable = function disable(setting) {
464 return this.set(setting, false);
465};
466
467/**
468 * Delegate `.VERB(...)` calls to `router.VERB(...)`.
469 */
470
471methods.forEach(function(method){
472 app[method] = function(path){
473 if (method === 'get' && arguments.length === 1) {
474 // app.get(setting)
475 return this.set(path);
476 }
477
478 this.lazyrouter();
479
480 var route = this._router.route(path);
481 route[method].apply(route, slice.call(arguments, 1));
482 return this;
483 };
484});
485
486/**
487 * Special-cased "all" method, applying the given route `path`,
488 * middleware, and callback to _every_ HTTP method.
489 *
490 * @param {String} path
491 * @param {Function} ...
492 * @return {app} for chaining
493 * @public
494 */
495
496app.all = function all(path) {
497 this.lazyrouter();
498
499 var route = this._router.route(path);
500 var args = slice.call(arguments, 1);
501
502 for (var i = 0; i < methods.length; i++) {
503 route[methods[i]].apply(route, args);
504 }
505
506 return this;
507};
508
509// del -> delete alias
510
511app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');
512
513/**
514 * Render the given view `name` name with `options`
515 * and a callback accepting an error and the
516 * rendered template string.
517 *
518 * Example:
519 *
520 * app.render('email', { name: 'Tobi' }, function(err, html){
521 * // ...
522 * })
523 *
524 * @param {String} name
525 * @param {Object|Function} options or fn
526 * @param {Function} callback
527 * @public
528 */
529
530app.render = function render(name, options, callback) {
531 var cache = this.cache;
532 var done = callback;
533 var engines = this.engines;
534 var opts = options;
535 var renderOptions = {};
536 var view;
537
538 // support callback function as second arg
539 if (typeof options === 'function') {
540 done = options;
541 opts = {};
542 }
543
544 // merge app.locals
545 merge(renderOptions, this.locals);
546
547 // merge options._locals
548 if (opts._locals) {
549 merge(renderOptions, opts._locals);
550 }
551
552 // merge options
553 merge(renderOptions, opts);
554
555 // set .cache unless explicitly provided
556 if (renderOptions.cache == null) {
557 renderOptions.cache = this.enabled('view cache');
558 }
559
560 // primed cache
561 if (renderOptions.cache) {
562 view = cache[name];
563 }
564
565 // view
566 if (!view) {
567 var View = this.get('view');
568
569 view = new View(name, {
570 defaultEngine: this.get('view engine'),
571 root: this.get('views'),
572 engines: engines
573 });
574
575 if (!view.path) {
576 var dirs = Array.isArray(view.root) && view.root.length > 1
577 ? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'
578 : 'directory "' + view.root + '"'
579 var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
580 err.view = view;
581 return done(err);
582 }
583
584 // prime the cache
585 if (renderOptions.cache) {
586 cache[name] = view;
587 }
588 }
589
590 // render
591 tryRender(view, renderOptions, done);
592};
593
594/**
595 * Listen for connections.
596 *
597 * A node `http.Server` is returned, with this
598 * application (which is a `Function`) as its
599 * callback. If you wish to create both an HTTP
600 * 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 * @public
613 */
614
615app.listen = function listen() {
616 var server = http.createServer(this);
617 return server.listen.apply(server, arguments);
618};
619
620/**
621 * Log error using console.error.
622 *
623 * @param {Error} err
624 * @private
625 */
626
627function logerror(err) {
628 /* istanbul ignore next */
629 if (this.get('env') !== 'test') console.error(err.stack || err.toString());
630}
631
632/**
633 * Try rendering a view.
634 * @private
635 */
636
637function tryRender(view, options, callback) {
638 try {
639 view.render(options, callback);
640 } catch (err) {
641 callback(err);
642 }
643}