arnaucube
arnaucube/thoughtsPublic Self-hosted repository
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}