| /** |
| * Module dependencies. |
| */ |
| |
| var connect = require('connect') |
| , Router = require('./router') |
| , methods = require('methods') |
| , middleware = require('./middleware') |
| , debug = require('debug')('express:application') |
| , locals = require('./utils').locals |
| , View = require('./view') |
| , utils = connect.utils |
| , path = require('path') |
| , http = require('http') |
| , join = path.join; |
| |
| /** |
| * Application prototype. |
| */ |
| |
| var app = exports = module.exports = {}; |
| |
| /** |
| * Initialize the server. |
| * |
| * - setup default configuration |
| * - setup default middleware |
| * - setup route reflection methods |
| * |
| * @api private |
| */ |
| |
| app.init = function(){ |
| this.cache = {}; |
| this.settings = {}; |
| this.engines = {}; |
| this.defaultConfiguration(); |
| }; |
| |
| /** |
| * Initialize application configuration. |
| * |
| * @api private |
| */ |
| |
| app.defaultConfiguration = function(){ |
| // default settings |
| this.enable('x-powered-by'); |
| this.enable('etag'); |
| this.set('env', process.env.NODE_ENV || 'development'); |
| this.set('subdomain offset', 2); |
| debug('booting in %s mode', this.get('env')); |
| |
| // implicit middleware |
| this.use(connect.query()); |
| this.use(middleware.init(this)); |
| |
| // inherit protos |
| this.on('mount', function(parent){ |
| this.request.__proto__ = parent.request; |
| this.response.__proto__ = parent.response; |
| this.engines.__proto__ = parent.engines; |
| this.settings.__proto__ = parent.settings; |
| }); |
| |
| // router |
| this._router = new Router(this); |
| this.routes = this._router.map; |
| this.__defineGetter__('router', function(){ |
| this._usedRouter = true; |
| this._router.caseSensitive = this.enabled('case sensitive routing'); |
| this._router.strict = this.enabled('strict routing'); |
| return this._router.middleware; |
| }); |
| |
| // setup locals |
| this.locals = locals(this); |
| |
| // default locals |
| this.locals.settings = this.settings; |
| |
| // default configuration |
| this.set('view', View); |
| this.set('views', process.cwd() + '/views'); |
| this.set('jsonp callback name', 'callback'); |
| |
| this.configure('development', function(){ |
| this.set('json spaces', 2); |
| }); |
| |
| this.configure('production', function(){ |
| this.enable('view cache'); |
| }); |
| }; |
| |
| /** |
| * Proxy `connect#use()` to apply settings to |
| * mounted applications. |
| * |
| * @param {String|Function|Server} route |
| * @param {Function|Server} fn |
| * @return {app} for chaining |
| * @api public |
| */ |
| |
| app.use = function(route, fn){ |
| var app; |
| |
| // default route to '/' |
| if ('string' != typeof route) fn = route, route = '/'; |
| |
| // express app |
| if (fn.handle && fn.set) app = fn; |
| |
| // restore .app property on req and res |
| if (app) { |
| app.route = route; |
| fn = function(req, res, next) { |
| var orig = req.app; |
| app.handle(req, res, function(err){ |
| req.__proto__ = orig.request; |
| res.__proto__ = orig.response; |
| next(err); |
| }); |
| }; |
| } |
| |
| connect.proto.use.call(this, route, fn); |
| |
| // mounted an app |
| if (app) { |
| app.parent = this; |
| app.emit('mount', this); |
| } |
| |
| return this; |
| }; |
| |
| /** |
| * Register the given template engine callback `fn` |
| * as `ext`. |
| * |
| * By default will `require()` the engine based on the |
| * file extension. For example if you try to render |
| * a "foo.jade" file Express will invoke the following internally: |
| * |
| * app.engine('jade', require('jade').__express); |
| * |
| * For engines that do not provide `.__express` out of the box, |
| * or if you wish to "map" a different extension to the template engine |
| * you may use this method. For example mapping the EJS template engine to |
| * ".html" files: |
| * |
| * app.engine('html', require('ejs').renderFile); |
| * |
| * In this case EJS provides a `.renderFile()` method with |
| * the same signature that Express expects: `(path, options, callback)`, |
| * though note that it aliases this method as `ejs.__express` internally |
| * so if you're using ".ejs" extensions you dont need to do anything. |
| * |
| * Some template engines do not follow this convention, the |
| * [Consolidate.js](https://github.com/visionmedia/consolidate.js) |
| * library was created to map all of node's popular template |
| * engines to follow this convention, thus allowing them to |
| * work seamlessly within Express. |
| * |
| * @param {String} ext |
| * @param {Function} fn |
| * @return {app} for chaining |
| * @api public |
| */ |
| |
| app.engine = function(ext, fn){ |
| if ('function' != typeof fn) throw new Error('callback function required'); |
| if ('.' != ext[0]) ext = '.' + ext; |
| this.engines[ext] = fn; |
| return this; |
| }; |
| |
| /** |
| * Map the given param placeholder `name`(s) to the given callback(s). |
| * |
| * Parameter mapping is used to provide pre-conditions to routes |
| * which use normalized placeholders. For example a _:user_id_ parameter |
| * could automatically load a user's information from the database without |
| * any additional code, |
| * |
| * The callback uses the same signature as middleware, the only differencing |
| * being that the value of the placeholder is passed, in this case the _id_ |
| * of the user. Once the `next()` function is invoked, just like middleware |
| * it will continue on to execute the route, or subsequent parameter functions. |
| * |
| * app.param('user_id', function(req, res, next, id){ |
| * User.find(id, function(err, user){ |
| * if (err) { |
| * next(err); |
| * } else if (user) { |
| * req.user = user; |
| * next(); |
| * } else { |
| * next(new Error('failed to load user')); |
| * } |
| * }); |
| * }); |
| * |
| * @param {String|Array} name |
| * @param {Function} fn |
| * @return {app} for chaining |
| * @api public |
| */ |
| |
| app.param = function(name, fn){ |
| var self = this |
| , fns = [].slice.call(arguments, 1); |
| |
| // array |
| if (Array.isArray(name)) { |
| name.forEach(function(name){ |
| fns.forEach(function(fn){ |
| self.param(name, fn); |
| }); |
| }); |
| // param logic |
| } else if ('function' == typeof name) { |
| this._router.param(name); |
| // single |
| } else { |
| if (':' == name[0]) name = name.substr(1); |
| fns.forEach(function(fn){ |
| self._router.param(name, fn); |
| }); |
| } |
| |
| return this; |
| }; |
| |
| /** |
| * Assign `setting` to `val`, or return `setting`'s value. |
| * |
| * app.set('foo', 'bar'); |
| * app.get('foo'); |
| * // => "bar" |
| * |
| * Mounted servers inherit their parent server's settings. |
| * |
| * @param {String} setting |
| * @param {String} val |
| * @return {Server} for chaining |
| * @api public |
| */ |
| |
| app.set = function(setting, val){ |
| if (1 == arguments.length) { |
| return this.settings[setting]; |
| } else { |
| this.settings[setting] = val; |
| return this; |
| } |
| }; |
| |
| /** |
| * Return the app's absolute pathname |
| * based on the parent(s) that have |
| * mounted it. |
| * |
| * For example if the application was |
| * mounted as "/admin", which itself |
| * was mounted as "/blog" then the |
| * return value would be "/blog/admin". |
| * |
| * @return {String} |
| * @api private |
| */ |
| |
| app.path = function(){ |
| return this.parent |
| ? this.parent.path() + this.route |
| : ''; |
| }; |
| |
| /** |
| * Check if `setting` is enabled (truthy). |
| * |
| * app.enabled('foo') |
| * // => false |
| * |
| * app.enable('foo') |
| * app.enabled('foo') |
| * // => true |
| * |
| * @param {String} setting |
| * @return {Boolean} |
| * @api public |
| */ |
| |
| app.enabled = function(setting){ |
| return !!this.set(setting); |
| }; |
| |
| /** |
| * Check if `setting` is disabled. |
| * |
| * app.disabled('foo') |
| * // => true |
| * |
| * app.enable('foo') |
| * app.disabled('foo') |
| * // => false |
| * |
| * @param {String} setting |
| * @return {Boolean} |
| * @api public |
| */ |
| |
| app.disabled = function(setting){ |
| return !this.set(setting); |
| }; |
| |
| /** |
| * Enable `setting`. |
| * |
| * @param {String} setting |
| * @return {app} for chaining |
| * @api public |
| */ |
| |
| app.enable = function(setting){ |
| return this.set(setting, true); |
| }; |
| |
| /** |
| * Disable `setting`. |
| * |
| * @param {String} setting |
| * @return {app} for chaining |
| * @api public |
| */ |
| |
| app.disable = function(setting){ |
| return this.set(setting, false); |
| }; |
| |
| /** |
| * Configure callback for zero or more envs, |
| * when no `env` is specified that callback will |
| * be invoked for all environments. Any combination |
| * can be used multiple times, in any order desired. |
| * |
| * Examples: |
| * |
| * app.configure(function(){ |
| * // executed for all envs |
| * }); |
| * |
| * app.configure('stage', function(){ |
| * // executed staging env |
| * }); |
| * |
| * app.configure('stage', 'production', function(){ |
| * // executed for stage and production |
| * }); |
| * |
| * Note: |
| * |
| * These callbacks are invoked immediately, and |
| * are effectively sugar for the following: |
| * |
| * var env = process.env.NODE_ENV || 'development'; |
| * |
| * switch (env) { |
| * case 'development': |
| * ... |
| * break; |
| * case 'stage': |
| * ... |
| * break; |
| * case 'production': |
| * ... |
| * break; |
| * } |
| * |
| * @param {String} env... |
| * @param {Function} fn |
| * @return {app} for chaining |
| * @api public |
| */ |
| |
| app.configure = function(env, fn){ |
| var envs = 'all' |
| , args = [].slice.call(arguments); |
| fn = args.pop(); |
| if (args.length) envs = args; |
| if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this); |
| return this; |
| }; |
| |
| /** |
| * Delegate `.VERB(...)` calls to `router.VERB(...)`. |
| */ |
| |
| methods.forEach(function(method){ |
| app[method] = function(path){ |
| if ('get' == method && 1 == arguments.length) return this.set(path); |
| |
| // deprecated |
| if (Array.isArray(path)) { |
| console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0'); |
| } |
| |
| // if no router attached yet, attach the router |
| if (!this._usedRouter) this.use(this.router); |
| |
| // setup route |
| this._router[method].apply(this._router, arguments); |
| return this; |
| }; |
| }); |
| |
| /** |
| * Special-cased "all" method, applying the given route `path`, |
| * middleware, and callback to _every_ HTTP method. |
| * |
| * @param {String} path |
| * @param {Function} ... |
| * @return {app} for chaining |
| * @api public |
| */ |
| |
| app.all = function(path){ |
| var args = arguments; |
| methods.forEach(function(method){ |
| app[method].apply(this, args); |
| }, this); |
| return this; |
| }; |
| |
| // del -> delete alias |
| |
| app.del = app.delete; |
| |
| /** |
| * Render the given view `name` name with `options` |
| * and a callback accepting an error and the |
| * rendered template string. |
| * |
| * Example: |
| * |
| * app.render('email', { name: 'Tobi' }, function(err, html){ |
| * // ... |
| * }) |
| * |
| * @param {String} name |
| * @param {String|Function} options or fn |
| * @param {Function} fn |
| * @api public |
| */ |
| |
| app.render = function(name, options, fn){ |
| var opts = {} |
| , cache = this.cache |
| , engines = this.engines |
| , view; |
| |
| // support callback function as second arg |
| if ('function' == typeof options) { |
| fn = options, options = {}; |
| } |
| |
| // merge app.locals |
| utils.merge(opts, this.locals); |
| |
| // merge options._locals |
| if (options._locals) utils.merge(opts, options._locals); |
| |
| // merge options |
| utils.merge(opts, options); |
| |
| // set .cache unless explicitly provided |
| opts.cache = null == opts.cache |
| ? this.enabled('view cache') |
| : opts.cache; |
| |
| // primed cache |
| if (opts.cache) view = cache[name]; |
| |
| // view |
| if (!view) { |
| view = new (this.get('view'))(name, { |
| defaultEngine: this.get('view engine'), |
| root: this.get('views'), |
| engines: engines |
| }); |
| |
| if (!view.path) { |
| var err = new Error('Failed to lookup view "' + name + '"'); |
| err.view = view; |
| return fn(err); |
| } |
| |
| // prime the cache |
| if (opts.cache) cache[name] = view; |
| } |
| |
| // render |
| try { |
| view.render(opts, fn); |
| } catch (err) { |
| fn(err); |
| } |
| }; |
| |
| /** |
| * Listen for connections. |
| * |
| * A node `http.Server` is returned, with this |
| * application (which is a `Function`) as its |
| * callback. If you wish to create both an HTTP |
| * and HTTPS server you may do so with the "http" |
| * and "https" modules as shown here: |
| * |
| * var http = require('http') |
| * , https = require('https') |
| * , express = require('express') |
| * , app = express(); |
| * |
| * http.createServer(app).listen(80); |
| * https.createServer({ ... }, app).listen(443); |
| * |
| * @return {http.Server} |
| * @api public |
| */ |
| |
| app.listen = function(){ |
| var server = http.createServer(this); |
| return server.listen.apply(server, arguments); |
| }; |