]> git.cworth.org Git - obsolete/notmuch-web/blob - node_modules/express/lib/http.js
Install the "express" node module via npm
[obsolete/notmuch-web] / node_modules / express / lib / http.js
1
2 /*!
3  * Express - HTTPServer
4  * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
5  * MIT Licensed
6  */
7
8 /**
9  * Module dependencies.
10  */
11
12 var qs = require('qs')
13   , connect = require('connect')
14   , router = require('./router')
15   , methods = router.methods.concat(['del', 'all'])
16   , view = require('./view')
17   , url = require('url')
18   , utils = connect.utils;
19
20 /**
21  * Expose `HTTPServer`.
22  */
23
24 exports = module.exports = HTTPServer;
25
26 /**
27  * Server proto.
28  */
29
30 var app = HTTPServer.prototype;
31
32 /**
33  * Initialize a new `HTTPServer` with optional `middleware`.
34  *
35  * @param {Array} middleware
36  * @api public
37  */
38
39 function HTTPServer(middleware){
40   connect.HTTPServer.call(this, []);
41   this.init(middleware);
42 };
43
44 /**
45  * Inherit from `connect.HTTPServer`.
46  */
47
48 app.__proto__ = connect.HTTPServer.prototype;
49
50 /**
51  * Initialize the server.
52  *
53  * @param {Array} middleware
54  * @api private
55  */
56
57 app.init = function(middleware){
58   var self = this;
59   this.cache = {};
60   this.settings = {};
61   this.redirects = {};
62   this.isCallbacks = {};
63   this._locals = {};
64   this.dynamicViewHelpers = {};
65   this.errorHandlers = [];
66
67   this.set('home', '/');
68   this.set('env', process.env.NODE_ENV || 'development');
69
70   // expose objects to each other
71   this.use(function(req, res, next){
72     req.query = req.query || {};
73     res.setHeader('X-Powered-By', 'Express');
74     req.app = res.app = self;
75     req.res = res;
76     res.req = req;
77     req.next = next;
78     // assign req.query
79     if (req.url.indexOf('?') > 0) {
80       var query = url.parse(req.url).query;
81       req.query = qs.parse(query);
82     }
83     next();
84   });
85
86   // apply middleware
87   if (middleware) middleware.forEach(self.use.bind(self));
88
89   // use router, expose as app.get(), etc
90   var fn = router(function(app){ self.routes = app; }, this);
91   this.__defineGetter__('router', function(){
92     this.__usedRouter = true;
93     return fn;
94   });
95
96   // default locals
97   this.locals({
98       settings: this.settings
99     , app: this
100   });
101
102   // default development configuration
103   this.configure('development', function(){
104     this.enable('hints');
105   });
106
107   // default production configuration
108   this.configure('production', function(){
109     this.enable('view cache');
110   });
111
112   // register error handlers on "listening"
113   // so that they disregard definition position.
114   this.on('listening', this.registerErrorHandlers.bind(this));
115
116   // route lookup methods
117   this.remove = function(url){
118     return self.remove.all(url);
119   };
120
121   this.match = function(url){
122     return self.match.all(url);
123   };
124
125   this.lookup = function(url){
126     return self.lookup.all(url);
127   };
128
129   methods.forEach(function(method){
130     self.match[method] = function(url){
131       return self.router.match(url, 'all' == method
132         ? null
133         : method);
134     };
135
136     self.remove[method] = function(url){
137       return self.router.remove(url, 'all' == method
138         ? null
139         : method);
140     };
141
142     self.lookup[method] = function(path){
143       return self.router.lookup(path, 'all' == method
144         ? null
145         : method);
146     };
147   });
148 };
149
150 /**
151  * When using the vhost() middleware register error handlers.
152  */
153
154 app.onvhost = function(){
155   this.registerErrorHandlers();
156 };
157
158 /**
159  * Register error handlers.
160  *
161  * @return {Server} for chaining
162  * @api public
163  */
164
165 app.registerErrorHandlers = function(){
166   this.errorHandlers.forEach(function(fn){
167     this.use(function(err, req, res, next){
168       fn.apply(this, arguments);
169     });
170   }, this);
171   return this;
172 };
173
174 /**
175  * Proxy `connect.HTTPServer#use()` to apply settings to
176  * mounted applications.
177  *
178  * @param {String|Function|Server} route
179  * @param {Function|Server} middleware
180  * @return {Server} for chaining
181  * @api public
182  */
183
184 app.use = function(route, middleware){
185   var app, home, handle;
186
187   if ('string' != typeof route) {
188     middleware = route, route = '/';
189   }
190
191   // express app
192   if (middleware.handle && middleware.set) app = middleware;
193
194   // restore .app property on req and res
195   if (app) {
196     app.route = route;
197     middleware = function(req, res, next) {
198       var orig = req.app;
199       app.handle(req, res, function(err){
200         req.app = res.app = orig;
201         next(err);
202       });
203     };
204   }
205
206   connect.HTTPServer.prototype.use.call(this, route, middleware);
207
208   // mounted an app, invoke the hook
209   // and adjust some settings
210   if (app) {
211     home = app.set('home');
212     if ('/' == home) home = '';
213     app.set('home', app.route + home);
214     app.parent = this;
215     if (app.__mounted) app.__mounted.call(app, this);
216   }
217
218   return this;
219 };
220
221 /**
222  * Assign a callback `fn` which is called
223  * when this `Server` is passed to `Server#use()`.
224  *
225  * Examples:
226  *
227  *    var app = express.createServer()
228  *      , blog = express.createServer();
229  *
230  *    blog.mounted(function(parent){
231  *      // parent is app
232  *      // "this" is blog
233  *    });
234  *
235  *    app.use(blog);
236  *
237  * @param {Function} fn
238  * @return {Server} for chaining
239  * @api public
240  */
241
242 app.mounted = function(fn){
243   this.__mounted = fn;
244   return this;
245 };
246
247 /**
248  * See: view.register.
249  *
250  * @return {Server} for chaining
251  * @api public
252  */
253
254 app.register = function(){
255   view.register.apply(this, arguments);
256   return this;
257 };
258
259 /**
260  * Register the given view helpers `obj`. This method
261  * can be called several times to apply additional helpers.
262  *
263  * @param {Object} obj
264  * @return {Server} for chaining
265  * @api public
266  */
267
268 app.helpers =
269 app.locals = function(obj){
270   utils.merge(this._locals, obj);
271   return this;
272 };
273
274 /**
275  * Register the given dynamic view helpers `obj`. This method
276  * can be called several times to apply additional helpers.
277  *
278  * @param {Object} obj
279  * @return {Server} for chaining
280  * @api public
281  */
282
283 app.dynamicHelpers = function(obj){
284   utils.merge(this.dynamicViewHelpers, obj);
285   return this;
286 };
287
288 /**
289  * Map the given param placeholder `name`(s) to the given callback `fn`.
290  *
291  * Param mapping is used to provide pre-conditions to routes
292  * which us normalized placeholders. For example ":user_id" may
293  * attempt to load the user from the database, where as ":num" may
294  * pass the value through `parseInt(num, 10)`.
295  *
296  * When the callback function accepts only a single argument, the
297  * value of placeholder is passed:
298  *
299  *    app.param('page', function(n){ return parseInt(n, 10); });
300  *
301  * After which "/users/:page" would automatically provide us with
302  * an integer for `req.params.page`. If desired we could use the callback
303  * signature shown below, and immediately `next(new Error('invalid page'))`
304  * when `parseInt` fails.
305  *
306  * Alternatively the callback may accept the request, response, next, and
307  * the value, acting like middlware:
308  *
309  *     app.param('userId', function(req, res, next, id){
310  *       User.find(id, function(err, user){
311  *         if (err) {
312  *           next(err);
313  *         } else if (user) {
314  *           req.user = user;
315  *           next();
316  *         } else {
317  *           next(new Error('failed to load user'));
318  *         }
319  *       });
320  *     });
321  *
322  * Now every time ":userId" is present, the associated user object
323  * will be loaded and assigned before the route handler is invoked.
324  *
325  * @param {String|Array} name
326  * @param {Function} fn
327  * @return {Server} for chaining
328  * @api public
329  */
330
331 app.param = function(name, fn){
332   if (Array.isArray(name)) {
333     name.forEach(function(name){
334       this.param(name, fn);
335     }, this);
336   } else {
337     if (':' == name[0]) name = name.substr(1);
338     this.routes.param(name, fn);
339   }
340   return this;
341 };
342
343 /**
344  * Assign a custom exception handler callback `fn`.
345  * These handlers are always _last_ in the middleware stack.
346  *
347  * @param {Function} fn
348  * @return {Server} for chaining
349  * @api public
350  */
351
352 app.error = function(fn){
353   this.errorHandlers.push(fn);
354   return this;
355 };
356
357 /**
358  * Register the given callback `fn` for the given `type`.
359  *
360  * @param {String} type
361  * @param {Function} fn
362  * @return {Server} for chaining
363  * @api public
364  */
365
366 app.is = function(type, fn){
367   if (!fn) return this.isCallbacks[type];
368   this.isCallbacks[type] = fn;
369   return this;
370 };
371
372 /**
373  * Assign `setting` to `val`, or return `setting`'s value.
374  * Mounted servers inherit their parent server's settings.
375  *
376  * @param {String} setting
377  * @param {String} val
378  * @return {Server|Mixed} for chaining, or the setting value
379  * @api public
380  */
381
382 app.set = function(setting, val){
383   if (val === undefined) {
384     if (this.settings.hasOwnProperty(setting)) {
385       return this.settings[setting];
386     } else if (this.parent) {
387       return this.parent.set(setting);
388     }
389   } else {
390     this.settings[setting] = val;
391     return this;
392   }
393 };
394
395 /**
396  * Check if `setting` is enabled.
397  *
398  * @param {String} setting
399  * @return {Boolean}
400  * @api public
401  */
402
403 app.enabled = function(setting){
404   return !!this.set(setting);
405 };
406
407 /**
408  * Check if `setting` is disabled.
409  *
410  * @param {String} setting
411  * @return {Boolean}
412  * @api public
413  */
414
415 app.disabled = function(setting){
416   return !this.set(setting);
417 };
418
419 /**
420  * Enable `setting`.
421  *
422  * @param {String} setting
423  * @return {Server} for chaining
424  * @api public
425  */
426
427 app.enable = function(setting){
428   return this.set(setting, true);
429 };
430
431 /**
432  * Disable `setting`.
433  *
434  * @param {String} setting
435  * @return {Server} for chaining
436  * @api public
437  */
438
439 app.disable = function(setting){
440   return this.set(setting, false);
441 };
442
443 /**
444  * Redirect `key` to `url`.
445  *
446  * @param {String} key
447  * @param {String} url
448  * @return {Server} for chaining
449  * @api public
450  */
451
452 app.redirect = function(key, url){
453   this.redirects[key] = url;
454   return this;
455 };
456
457 /**
458  * Configure callback for the given `env`.
459  *
460  * @param {String} env
461  * @param {Function} fn
462  * @return {Server} for chaining
463  * @api public
464  */
465
466 app.configure = function(env, fn){
467   if ('function' == typeof env) {
468     fn = env, env = 'all';
469   }
470   if ('all' == env || env == this.settings.env) {
471     fn.call(this);
472   }
473   return this;
474 };
475
476 // Generate routing methods
477
478 function generateMethod(method){
479   app[method] = function(path){
480     var self = this;
481
482     // Lookup
483     if (1 == arguments.length) {
484       return this.router.lookup(path, 'all' == method
485         ? null
486         : method);
487     }
488
489     // Ensure router is mounted
490     if (!this.__usedRouter) this.use(this.router);
491
492     // Generate the route
493     this.routes[method].apply(this, arguments);
494     return this;
495   };
496   return arguments.callee;
497 }
498
499 methods.forEach(generateMethod);
500
501 // Alias delete as "del"
502
503 app.del = app.delete;