The LM Control website. Simple yet efficient.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

application.js 14KB

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