postal.js/example/node/node_modules/express/lib/http.js

606 lines
12 KiB
JavaScript

/*!
* 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
* @param {String} url
* @return {Server} for chaining
* @api public
*/
app.redirect = function ( key, url ) {
this.redirects[key] = url;
return this;
};
/**
* 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
* });
*
* @param {String} env...
* @param {Function} fn
* @return {Server} for chaining
* @api public
*/
app.configure = function ( env, fn ) {
var envs = 'all'
, args = toArray( 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 `.route(VERB, ...)`.
*/
methods.forEach( function ( method ) {
app[method] = function ( path ) {
if ( 1 == arguments.length ) {
return this.routes.lookup( method, path );
}
var args = [method].concat( toArray( arguments ) );
if ( !this.__usedRouter ) {
this.use( this.router );
}
return this.routes._route.apply( this.routes, args );
}
} );
/**
* Special-cased "all" method, applying the given route `path`,
* middleware, and callback to _every_ HTTP method.
*
* @param {String} path
* @param {Function} ...
* @return {Server} for chaining
* @api public
*/
app.all = function ( path ) {
var args = arguments;
if ( 1 == args.length ) {
return this.routes.lookup( 'all', path );
}
methods.forEach( function ( method ) {
if ( 'all' == method || 'del' == method ) {
return;
}
app[method].apply( this, args );
}, this );
return this;
};
// del -> delete alias
app.del = app.delete;