582 lines · 12.2 KB · JavaScript
RawDownload
/*! * Express - HTTPServer * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca> * MIT Licensed */ /** * Module dependencies. */ var qs = require('qs')  , connect = require('connect')  , router = require('./router')  , Router = require('./router')  , view = require('./view')  , toArray = require('./utils').toArray  , methods = router.methods.concat('del', 'all')  , url = require('url')  , utils = connect.utils; /** * Expose `HTTPServer`. */ exports = module.exports = HTTPServer; /** * Server proto. */ var app = HTTPServer.prototype; /** * Initialize a new `HTTPServer` with optional `middleware`. * * @param {Array} middleware * @api public */ function HTTPServer(middleware){  connect.HTTPServer.call(this, []);  this.init(middleware);}; /** * Inherit from `connect.HTTPServer`. */ app.__proto__ = connect.HTTPServer.prototype; /** * Initialize the server. * * @param {Array} middleware * @api private */ app.init = function(middleware){  var self = this;  this.cache = {};  this.settings = {};  this.redirects = {};  this.isCallbacks = {};  this._locals = {};  this.dynamicViewHelpers = {};  this.errorHandlers = [];   this.set('env', process.env.NODE_ENV || 'development');   // expose objects to each other  this.use(function(req, res, next){    req.query = req.query || {};    res.setHeader('X-Powered-By', 'Express');    req.app = res.app = self;    req.res = res;    res.req = req;    req.next = next;    // assign req.query    if (req.url.indexOf('?') > 0) {      var query = url.parse(req.url).query;      req.query = qs.parse(query);    }    next();  });   // apply middleware  if (middleware) middleware.forEach(self.use.bind(self));   // router  this.routes = new Router(this);  this.__defineGetter__('router', function(){    this.__usedRouter = true;    return self.routes.middleware;  });   // default locals  this.locals({      settings: this.settings    , app: this  });   // default development configuration  this.configure('development', function(){    this.enable('hints');  });   // default production configuration  this.configure('production', function(){    this.enable('view cache');  });   // register error handlers on "listening"  // so that they disregard definition position.  this.on('listening', this.registerErrorHandlers.bind(this));   // route manipulation methods  methods.forEach(function(method){    self.lookup[method] = function(path){      return self.routes.lookup(method, path);    };     self.match[method] = function(path){      return self.routes.match(method, path);    };     self.remove[method] = function(path){      return self.routes.lookup(method, path).remove();    };  });   // del -> delete  self.lookup.del = self.lookup.delete;  self.match.del = self.match.delete;  self.remove.del = self.remove.delete;}; /** * Remove routes matching the given `path`. * * @param {Route} path * @return {Boolean} * @api public */ app.remove = function(path){  return this.routes.lookup('all', path).remove();}; /** * Lookup routes defined with a path * equivalent to `path`. * * @param {Stirng} path * @return {Array} * @api public */ app.lookup = function(path){  return this.routes.lookup('all', path);}; /** * Lookup routes matching the given `url`. * * @param {Stirng} url * @return {Array} * @api public */ app.match = function(url){  return this.routes.match('all', url);}; /** * When using the vhost() middleware register error handlers. */ app.onvhost = function(){  this.registerErrorHandlers();}; /** * Register error handlers. * * @return {Server} for chaining * @api public */ app.registerErrorHandlers = function(){  this.errorHandlers.forEach(function(fn){    this.use(function(err, req, res, next){      fn.apply(this, arguments);    });  }, this);  return this;}; /** * Proxy `connect.HTTPServer#use()` to apply settings to * mounted applications. * * @param {String|Function|Server} route * @param {Function|Server} middleware * @return {Server} for chaining * @api public */ app.use = function(route, middleware){  var app, base, handle;   if ('string' != typeof route) {    middleware = route, route = '/';  }   // express app  if (middleware.handle && middleware.set) app = middleware;   // restore .app property on req and res  if (app) {    app.route = route;    middleware = function(req, res, next) {      var orig = req.app;      app.handle(req, res, function(err){        req.app = res.app = orig;        next(err);      });    };  }   connect.HTTPServer.prototype.use.call(this, route, middleware);   // mounted an app, invoke the hook  // and adjust some settings  if (app) {    base = this.set('basepath') || this.route;    if ('/' == base) base = '';    base = base + (app.set('basepath') || app.route);    app.set('basepath', base);    app.parent = this;    if (app.__mounted) app.__mounted.call(app, this);  }   return this;}; /** * Assign a callback `fn` which is called * when this `Server` is passed to `Server#use()`. * * Examples: * *    var app = express.createServer() *      , blog = express.createServer(); * *    blog.mounted(function(parent){ *      // parent is app *      // "this" is blog *    }); * *    app.use(blog); * * @param {Function} fn * @return {Server} for chaining * @api public */ app.mounted = function(fn){  this.__mounted = fn;  return this;}; /** * See: view.register. * * @return {Server} for chaining * @api public */ app.register = function(){  view.register.apply(this, arguments);  return this;}; /** * Register the given view helpers `obj`. This method * can be called several times to apply additional helpers. * * @param {Object} obj * @return {Server} for chaining * @api public */ app.helpers =app.locals = function(obj){  utils.merge(this._locals, obj);  return this;}; /** * Register the given dynamic view helpers `obj`. This method * can be called several times to apply additional helpers. * * @param {Object} obj * @return {Server} for chaining * @api public */ app.dynamicHelpers = function(obj){  utils.merge(this.dynamicViewHelpers, obj);  return this;}; /** * Map the given param placeholder `name`(s) to the given callback(s). * * Param mapping is used to provide pre-conditions to routes * which us normalized placeholders. This callback has the same * signature as regular middleware, for example below when ":userId" * is used this function will be invoked in an attempt to load the user. * *      app.param('userId', 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')); *          } *        }); *      }); * * Passing a single function allows you to map logic * to the values passed to `app.param()`, for example * this is useful to provide coercion support in a concise manner. * * The following example maps regular expressions to param values * ensuring that they match, otherwise passing control to the next * route: * *      app.param(function(name, regexp){ *        if (regexp instanceof RegExp) { *          return function(req, res, next, val){ *            var captures; *            if (captures = regexp.exec(String(val))) { *              req.params[name] = captures; *              next(); *            } else { *              next('route'); *            } *          } *        } *      }); * * We can now use it as shown below, where "/commit/:commit" expects * that the value for ":commit" is at 5 or more digits. The capture * groups are then available as `req.params.commit` as we defined * in the function above. * *    app.param('commit', /^\d{5,}$/); * * For more of this useful functionality take a look * at [express-params](http://github.com/visionmedia/express-params). * * @param {String|Array|Function} name * @param {Function} fn * @return {Server} 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.routes.param(name);  // single  } else {    if (':' == name[0]) name = name.substr(1);    fns.forEach(function(fn){      self.routes.param(name, fn);    });  }   return this;}; /** * Assign a custom exception handler callback `fn`. * These handlers are always _last_ in the middleware stack. * * @param {Function} fn * @return {Server} for chaining * @api public */ app.error = function(fn){  this.errorHandlers.push(fn);  return this;}; /** * Register the given callback `fn` for the given `type`. * * @param {String} type * @param {Function} fn * @return {Server} for chaining * @api public */ app.is = function(type, fn){  if (!fn) return this.isCallbacks[type];  this.isCallbacks[type] = fn;  return this;}; /** * Assign `setting` to `val`, or return `setting`'s value. * Mounted servers inherit their parent server's settings. * * @param {String} setting * @param {String} val * @return {Server|Mixed} for chaining, or the setting value * @api public */ app.set = function(setting, val){  if (val === undefined) {    if (this.settings.hasOwnProperty(setting)) {      return this.settings[setting];    } else if (this.parent) {      return this.parent.set(setting);    }  } else {    this.settings[setting] = val;    return this;  }}; /** * Check if `setting` is enabled. * * @param {String} setting * @return {Boolean} * @api public */ app.enabled = function(setting){  return !!this.set(setting);}; /** * Check if `setting` is disabled. * * @param {String} setting * @return {Boolean} * @api public */ app.disabled = function(setting){  return !this.set(setting);}; /** * Enable `setting`. * * @param {String} setting * @return {Server} for chaining * @api public */ app.enable = function(setting){  return this.set(setting, true);}; /** * Disable `setting`. * * @param {String} setting * @return {Server} for chaining * @api public */ app.disable = function(setting){  return this.set(setting, false);}; /** * Redirect `key` to `url`. * * @param {String} key

Lines 1–500 of 582.