• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

expressjs / express / 14108889583

27 Mar 2025 02:18PM UTC coverage: 99.873% (-0.1%) from 100.0%
14108889583

Pull #3714

github

web-flow
Merge 1db2ea0bc into eb6d12587
Pull Request #3714: Move settings methods to app.settings and deprecate old versions

66 of 67 new or added lines in 2 files covered. (98.51%)

1 existing line in 1 file now uncovered.

789 of 790 relevant lines covered (99.87%)

568.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.42
/lib/application.js
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');
2✔
17
var debug = require('debug')('express:application');
2✔
18
var View = require('./view');
2✔
19
var http = require('node:http');
2✔
20
var methods = require('./utils').methods;
2✔
21
var compileETag = require('./utils').compileETag;
2✔
22
var compileQueryParser = require('./utils').compileQueryParser;
2✔
23
var compileTrust = require('./utils').compileTrust;
2✔
24
var resolve = require('node:path').resolve;
2✔
25
var once = require('once')
2✔
26
var Router = require('router');
2✔
27
var deprecate = require('depd')('express');
2✔
28
var StoreSettings = require('./settings');
2✔
29

30
/**
31
 * Module variables.
32
 * @private
33
 */
34

35
var slice = Array.prototype.slice;
2✔
36
var flatten = Array.prototype.flat;
2✔
37

38
/**
39
 * Application prototype.
40
 */
41

42
var app = exports = module.exports = {};
2✔
43

44
/**
45
 * Variable for trust proxy inheritance back-compat
46
 * @private
47
 */
48

49
var trustProxyDefaultSymbol = '@@symbol:trust_proxy_default';
2✔
50

51
/**
52
 * Initialize the server.
53
 *
54
 *   - setup default configuration
55
 *   - setup default middleware
56
 *   - setup route reflection methods
57
 *
58
 * @private
59
 */
60

61
app.init = function init() {
2✔
62
  var router = null;
1,882✔
63

64
  this.cache = Object.create(null);
1,882✔
65
  this.engines = Object.create(null);
1,882✔
66
  this.settings = new StoreSettings({
1,882✔
67
    setters: {
68
      'etag': function (val) {
69
        this.set('etag fn', compileETag(val))
1,910✔
70
      },
71
      'query parser': function (val) {
72
        this.set('query parser fn', compileQueryParser(val))
1,896✔
73
      },
74
      'trust proxy': function (val) {
75
        this.set('trust proxy fn', compileTrust(val))
1,944✔
76
        // trust proxy inherit back-compat
77
        this.set(trustProxyDefaultSymbol, false)
1,944✔
78
      }
79
    }
80
  })
81

82
  this.defaultConfiguration();
1,882✔
83

84
  // Setup getting to lazily add base router
85
  Object.defineProperty(this, 'router', {
1,882✔
86
    configurable: true,
87
    enumerable: true,
88
    get: function getrouter() {
89
      if (router === null) {
4,952✔
90
        router = new Router({
1,718✔
91
          caseSensitive: this.enabled('case sensitive routing'),
92
          strict: this.enabled('strict routing')
93
        });
94
      }
95

96
      return router;
4,952✔
97
    }
98
  });
99
};
100

101
/**
102
 * Initialize application configuration.
103
 * @private
104
 */
105

106
app.defaultConfiguration = function defaultConfiguration() {
2✔
107
  var env = process.env.NODE_ENV || 'development';
1,882✔
108

109
  // default settings
110
  this.settings.enable('x-powered-by')
1,882✔
111
  this.settings.set('etag', 'weak')
1,882✔
112
  this.settings.set('env', env)
1,882✔
113
  this.settings.set('query parser', 'simple')
1,882✔
114
  this.settings.set('subdomain offset', 2)
1,882✔
115
  this.settings.set('trust proxy', false)
1,882✔
116

117
  // trust proxy inherit back-compat
118
  this.settings.set(trustProxyDefaultSymbol, true)
1,882✔
119

120
  debug('booting in %s mode', env);
1,882✔
121

122
  this.on('mount', function onmount(parent) {
1,882✔
123
    // inherit trust proxy
124
    if (this.settings.get(trustProxyDefaultSymbol) === true
62✔
125
      && typeof parent.settings.get('trust proxy fn') === 'function') {
126
      this.settings.unset('trust proxy')
60✔
127
      this.settings.unset('trust proxy fn')
60✔
128
    }
129

130
    // inherit protos
131
    Object.setPrototypeOf(this.request, parent.request)
62✔
132
    Object.setPrototypeOf(this.response, parent.response)
62✔
133
    Object.setPrototypeOf(this.engines, parent.engines)
62✔
134
    this.settings.inheritFrom(parent.settings)
62✔
135
  });
136

137
  // setup locals
138
  this.locals = Object.create(null);
1,882✔
139

140
  // top-most app is mounted at /
141
  this.mountpath = '/';
1,882✔
142

143
  // default locals
144
  this.locals.settings = this.settings.settings
1,882✔
145

146
  // default configuration
147
  this.settings.set('view', View)
1,882✔
148
  this.settings.set('views', resolve('views'))
1,882✔
149
  this.settings.set('jsonp callback name', 'callback')
1,882✔
150

151
  if (env === 'production') {
1,882✔
152
    this.settings.enable('view cache')
2✔
153
  }
154
};
155

156
/**
157
 * Dispatch a req, res pair into the application. Starts pipeline processing.
158
 *
159
 * If no callback is provided, then default error handlers will respond
160
 * in the event of an error bubbling through the stack.
161
 *
162
 * @private
163
 */
164

165
app.handle = function handle(req, res, callback) {
2✔
166
  // final handler
167
  var done = callback || finalhandler(req, res, {
2,306✔
168
    env: this.settings.get('env'),
169
    onerror: logerror.bind(this)
170
  });
171

172
  // set powered by header
173
  if (this.enabled('x-powered-by')) {
2,306✔
174
    res.setHeader('X-Powered-By', 'Express');
2,300✔
175
  }
176

177
  // set circular references
178
  req.res = res;
2,306✔
179
  res.req = req;
2,306✔
180

181
  // alter the prototypes
182
  Object.setPrototypeOf(req, this.request)
2,306✔
183
  Object.setPrototypeOf(res, this.response)
2,306✔
184

185
  // setup locals
186
  if (!res.locals) {
2,306✔
187
    res.locals = Object.create(null);
2,184✔
188
  }
189

190
  this.router.handle(req, res, done);
2,306✔
191
};
192

193
/**
194
 * Proxy `Router#use()` to add middleware to the app router.
195
 * See Router#use() documentation for details.
196
 *
197
 * If the _fn_ parameter is an express app, then it will be
198
 * mounted at the _route_ specified.
199
 *
200
 * @public
201
 */
202

203
app.use = function use(fn) {
2✔
204
  var offset = 0;
1,728✔
205
  var path = '/';
1,728✔
206

207
  // default path to '/'
208
  // disambiguate app.use([fn])
209
  if (typeof fn !== 'function') {
1,728✔
210
    var arg = fn;
122✔
211

212
    while (Array.isArray(arg) && arg.length !== 0) {
122✔
213
      arg = arg[0];
12✔
214
    }
215

216
    // first arg is the path
217
    if (typeof arg !== 'function') {
122✔
218
      offset = 1;
116✔
219
      path = fn;
116✔
220
    }
221
  }
222

223
  var fns = flatten.call(slice.call(arguments, offset), Infinity);
1,728✔
224

225
  if (fns.length === 0) {
1,728✔
226
    throw new TypeError('app.use() requires a middleware function')
2✔
227
  }
228

229
  // get router
230
  var router = this.router;
1,726✔
231

232
  fns.forEach(function (fn) {
1,726✔
233
    // non-express app
234
    if (!fn || !fn.handle || !fn.set) {
1,768✔
235
      return router.use(path, fn);
1,706✔
236
    }
237

238
    debug('.use app under %s', path);
62✔
239
    fn.mountpath = path;
62✔
240
    fn.parent = this;
62✔
241

242
    // restore .app property on req and res
243
    router.use(path, function mounted_app(req, res, next) {
62✔
244
      var orig = req.app;
114✔
245
      fn.handle(req, res, function (err) {
114✔
246
        Object.setPrototypeOf(req, orig.request)
64✔
247
        Object.setPrototypeOf(res, orig.response)
64✔
248
        next(err);
64✔
249
      });
250
    });
251

252
    // mounted an app
253
    fn.emit('mount', this);
62✔
254
  }, this);
255

256
  return this;
1,718✔
257
};
258

259
/**
260
 * Proxy to the app `Router#route()`
261
 * Returns a new `Route` instance for the _path_.
262
 *
263
 * Routes are isolated middleware stacks for specific paths.
264
 * See the Route api docs for details.
265
 *
266
 * @public
267
 */
268

269
app.route = function route(path) {
2✔
270
  return this.router.route(path);
882✔
271
};
272

273
/**
274
 * Register the given template engine callback `fn`
275
 * as `ext`.
276
 *
277
 * By default will `require()` the engine based on the
278
 * file extension. For example if you try to render
279
 * a "foo.ejs" file Express will invoke the following internally:
280
 *
281
 *     app.engine('ejs', require('ejs').__express);
282
 *
283
 * For engines that do not provide `.__express` out of the box,
284
 * or if you wish to "map" a different extension to the template engine
285
 * you may use this method. For example mapping the EJS template engine to
286
 * ".html" files:
287
 *
288
 *     app.engine('html', require('ejs').renderFile);
289
 *
290
 * In this case EJS provides a `.renderFile()` method with
291
 * the same signature that Express expects: `(path, options, callback)`,
292
 * though note that it aliases this method as `ejs.__express` internally
293
 * so if you're using ".ejs" extensions you don't need to do anything.
294
 *
295
 * Some template engines do not follow this convention, the
296
 * [Consolidate.js](https://github.com/tj/consolidate.js)
297
 * library was created to map all of node's popular template
298
 * engines to follow this convention, thus allowing them to
299
 * work seamlessly within Express.
300
 *
301
 * @param {String} ext
302
 * @param {Function} fn
303
 * @return {app} for chaining
304
 * @public
305
 */
306

307
app.engine = function engine(ext, fn) {
2✔
308
  if (typeof fn !== 'function') {
86✔
309
    throw new Error('callback function required');
2✔
310
  }
311

312
  // get file extension
313
  var extension = ext[0] !== '.'
84✔
314
    ? '.' + ext
315
    : ext;
316

317
  // store engine
318
  this.engines[extension] = fn;
84✔
319

320
  return this;
84✔
321
};
322

323
/**
324
 * Proxy to `Router#param()` with one added api feature. The _name_ parameter
325
 * can be an array of names.
326
 *
327
 * See the Router#param() docs for more details.
328
 *
329
 * @param {String|Array} name
330
 * @param {Function} fn
331
 * @return {app} for chaining
332
 * @public
333
 */
334

335
app.param = function param(name, fn) {
2✔
336
  if (Array.isArray(name)) {
42✔
337
    for (var i = 0; i < name.length; i++) {
4✔
338
      this.param(name[i], fn);
8✔
339
    }
340

341
    return this;
4✔
342
  }
343

344
  this.router.param(name, fn);
38✔
345

346
  return this;
38✔
347
};
348

349
/**
350
 * Assign `setting` to `val`, or return `setting`'s value.
351
 *
352
 *    app.set('foo', 'bar');
353
 *    app.set('foo');
354
 *    // => "bar"
355
 *
356
 * Mounted servers inherit their parent server's settings.
357
 *
358
 * @param {String} setting
359
 * @param {*} [val]
360
 * @return {Server} for chaining
361
 * @public
362
 */
363

364
app.set = deprecate.function(function set(setting, val) {
2✔
365
  this.settings.set(setting, val)
180✔
366
  return this
178✔
367
}, 'app.set: Use app.settings.set instead')
368

369
/**
370
 * Return the app's absolute pathname
371
 * based on the parent(s) that have
372
 * mounted it.
373
 *
374
 * For example if the application was
375
 * mounted as "/admin", which itself
376
 * was mounted as "/blog" then the
377
 * return value would be "/blog/admin".
378
 *
379
 * @return {String}
380
 * @private
381
 */
382

383
app.path = function path() {
2✔
384
  return this.parent
12✔
385
    ? this.parent.path() + this.mountpath
386
    : '';
387
};
388

389
/**
390
 * Check if `setting` is enabled (truthy).
391
 *
392
 *    app.enabled('foo')
393
 *    // => false
394
 *
395
 *    app.enable('foo')
396
 *    app.enabled('foo')
397
 *    // => true
398
 *
399
 * @param {String} setting
400
 * @return {Boolean}
401
 * @public
402
 */
403

404
app.enabled = deprecate.function(function enabled(setting) {
2✔
405
  return this.settings.enabled(setting)
5,912✔
406
}, 'app.enabled: Use app.settings.endabled instead')
407

408
/**
409
 * Check if `setting` is disabled.
410
 *
411
 *    app.disabled('foo')
412
 *    // => true
413
 *
414
 *    app.enable('foo')
415
 *    app.disabled('foo')
416
 *    // => false
417
 *
418
 * @param {String} setting
419
 * @return {Boolean}
420
 * @public
421
 */
422

423
app.disabled = deprecate.function(function disabled(setting) {
2✔
NEW
UNCOV
424
  return this.settings.disabled(setting)
×
425
}, 'app.disabled: Use app.settings.disabled instead')
426

427
/**
428
 * Enable `setting`.
429
 *
430
 * @param {String} setting
431
 * @return {app} for chaining
432
 * @public
433
 */
434

435
app.enable = deprecate.function(function enable(setting) {
2✔
436
  this.settings.set(setting, true)
74✔
437
  return this
74✔
438
}, 'app.enable: Use app.settings.enable instead')
439

440
/**
441
 * Disable `setting`.
442
 *
443
 * @param {String} setting
444
 * @return {app} for chaining
445
 * @public
446
 */
447

448
app.disable = deprecate.function(function disable(setting) {
2✔
449
  this.settings.set(setting, false)
12✔
450
  return this
12✔
451
}, 'app.disable: Use app.settings.disable instead')
452

453
/**
454
 * Delegate `.VERB(...)` calls to `router.VERB(...)`.
455
 */
456

457
methods.forEach(function (method) {
2✔
458
  app[method] = function (path) {
68✔
459
    if (method === 'get' && arguments.length === 1) {
4,066✔
460
      // app.get(setting)
461
      return this.settings.get(path)
3,216✔
462
    }
463

464
    var route = this.route(path);
850✔
465
    route[method].apply(route, slice.call(arguments, 1));
850✔
466
    return this;
784✔
467
  };
468
});
469

470
/**
471
 * Special-cased "all" method, applying the given route `path`,
472
 * middleware, and callback to _every_ HTTP method.
473
 *
474
 * @param {String} path
475
 * @param {Function} ...
476
 * @return {app} for chaining
477
 * @public
478
 */
479

480
app.all = function all(path) {
2✔
481
  var route = this.route(path);
12✔
482
  var args = slice.call(arguments, 1);
12✔
483

484
  for (var i = 0; i < methods.length; i++) {
12✔
485
    route[methods[i]].apply(route, args);
408✔
486
  }
487

488
  return this;
12✔
489
};
490

491
/**
492
 * Render the given view `name` name with `options`
493
 * and a callback accepting an error and the
494
 * rendered template string.
495
 *
496
 * Example:
497
 *
498
 *    app.render('email', { name: 'Tobi' }, function(err, html){
499
 *      // ...
500
 *    })
501
 *
502
 * @param {String} name
503
 * @param {Object|Function} options or fn
504
 * @param {Function} callback
505
 * @public
506
 */
507

508
app.render = function render(name, options, callback) {
2✔
509
  var cache = this.cache;
162✔
510
  var done = callback;
162✔
511
  var engines = this.engines;
162✔
512
  var opts = options;
162✔
513
  var view;
514

515
  // support callback function as second arg
516
  if (typeof options === 'function') {
162✔
517
    done = options;
44✔
518
    opts = {};
44✔
519
  }
520

521
  // merge options
522
  var renderOptions = { ...this.locals, ...opts._locals, ...opts };
162✔
523

524
  // set .cache unless explicitly provided
525
  if (renderOptions.cache == null) {
162✔
526
    renderOptions.cache = this.settings.enabled('view cache')
158✔
527
  }
528

529
  // primed cache
530
  if (renderOptions.cache) {
162✔
531
    view = cache[name];
8✔
532
  }
533

534
  // view
535
  if (!view) {
162✔
536
    var View = this.settings.get('view')
158✔
537

538
    view = new View(name, {
158✔
539
      defaultEngine: this.settings.get('view engine'),
540
      root: this.settings.get('views'),
541
      engines: engines
542
    });
543

544
    if (!view.path) {
154✔
545
      var dirs = Array.isArray(view.root) && view.root.length > 1
6✔
546
        ? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'
547
        : 'directory "' + view.root + '"'
548
      var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
6✔
549
      err.view = view;
6✔
550
      return done(err);
6✔
551
    }
552

553
    // prime the cache
554
    if (renderOptions.cache) {
148✔
555
      cache[name] = view;
4✔
556
    }
557
  }
558

559
  // render
560
  tryRender(view, renderOptions, done);
152✔
561
};
562

563
/**
564
 * Listen for connections.
565
 *
566
 * A node `http.Server` is returned, with this
567
 * application (which is a `Function`) as its
568
 * callback. If you wish to create both an HTTP
569
 * and HTTPS server you may do so with the "http"
570
 * and "https" modules as shown here:
571
 *
572
 *    var http = require('node:http')
573
 *      , https = require('node:https')
574
 *      , express = require('express')
575
 *      , app = express();
576
 *
577
 *    http.createServer(app).listen(80);
578
 *    https.createServer({ ... }, app).listen(443);
579
 *
580
 * @return {http.Server}
581
 * @public
582
 */
583

584
app.listen = function listen() {
2✔
585
  var server = http.createServer(this)
12✔
586
  var args = Array.prototype.slice.call(arguments)
12✔
587
  if (typeof args[args.length - 1] === 'function') {
12✔
588
    var done = args[args.length - 1] = once(args[args.length - 1])
6✔
589
    server.once('error', done)
6✔
590
  }
591
  return server.listen.apply(server, args)
12✔
592
}
593

594
/**
595
 * Log error using console.error.
596
 *
597
 * @param {Error} err
598
 * @private
599
 */
600

601
function logerror(err) {
602
  /* istanbul ignore next */
603
  if (this.settings.get('env') !== 'test') console.error(err.stack || err.toString())
604
}
605

606
/**
607
 * Try rendering a view.
608
 * @private
609
 */
610

611
function tryRender(view, options, callback) {
612
  try {
152✔
613
    view.render(options, callback);
152✔
614
  } catch (err) {
615
    callback(err);
2✔
616
  }
617
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc