twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

application.js (14270B)


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