socketioMEANseed/server/node_modules/express/lib/router/index.js
server/node_modules/express/lib/router/index.jsRaw
1
2/**
3 * Module dependencies.
4 */
5
6var Route = require('./route');
7var Layer = require('./layer');
8var methods = require('methods');
9var mixin = require('utils-merge');
10var debug = require('debug')('express:router');
11var parseUrl = require('parseurl');
12var utils = require('../utils');
13
14/**
15 * Module variables.
16 */
17
18var objectRegExp = /^\[object (\S+)\]$/;
19var slice = Array.prototype.slice;
20var toString = Object.prototype.toString;
21
22/**
23 * Initialize a new `Router` with the given `options`.
24 *
25 * @param {Object} options
26 * @return {Router} which is an callable function
27 * @api public
28 */
29
30var proto = module.exports = function(options) {
31 options = options || {};
32
33 function router(req, res, next) {
34 router.handle(req, res, next);
35 }
36
37 // mixin Router class functions
38 router.__proto__ = proto;
39
40 router.params = {};
41 router._params = [];
42 router.caseSensitive = options.caseSensitive;
43 router.mergeParams = options.mergeParams;
44 router.strict = options.strict;
45 router.stack = [];
46
47 return router;
48};
49
50/**
51 * Map the given param placeholder `name`(s) to the given callback.
52 *
53 * Parameter mapping is used to provide pre-conditions to routes
54 * which use normalized placeholders. For example a _:user_id_ parameter
55 * could automatically load a user's information from the database without
56 * any additional code,
57 *
58 * The callback uses the same signature as middleware, the only difference
59 * being that the value of the placeholder is passed, in this case the _id_
60 * of the user. Once the `next()` function is invoked, just like middleware
61 * it will continue on to execute the route, or subsequent parameter functions.
62 *
63 * Just like in middleware, you must either respond to the request or call next
64 * to avoid stalling the request.
65 *
66 * app.param('user_id', function(req, res, next, id){
67 * User.find(id, function(err, user){
68 * if (err) {
69 * return next(err);
70 * } else if (!user) {
71 * return next(new Error('failed to load user'));
72 * }
73 * req.user = user;
74 * next();
75 * });
76 * });
77 *
78 * @param {String} name
79 * @param {Function} fn
80 * @return {app} for chaining
81 * @api public
82 */
83
84proto.param = function(name, fn){
85 // param logic
86 if ('function' == typeof name) {
87 this._params.push(name);
88 return;
89 }
90
91 // apply param functions
92 var params = this._params;
93 var len = params.length;
94 var ret;
95
96 if (name[0] === ':') {
97 name = name.substr(1);
98 }
99
100 for (var i = 0; i < len; ++i) {
101 if (ret = params[i](name, fn)) {
102 fn = ret;
103 }
104 }
105
106 // ensure we end up with a
107 // middleware function
108 if ('function' != typeof fn) {
109 throw new Error('invalid param() call for ' + name + ', got ' + fn);
110 }
111
112 (this.params[name] = this.params[name] || []).push(fn);
113 return this;
114};
115
116/**
117 * Dispatch a req, res into the router.
118 *
119 * @api private
120 */
121
122proto.handle = function(req, res, done) {
123 var self = this;
124
125 debug('dispatching %s %s', req.method, req.url);
126
127 var search = 1 + req.url.indexOf('?');
128 var pathlength = search ? search - 1 : req.url.length;
129 var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://');
130 var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
131 var idx = 0;
132 var removed = '';
133 var slashAdded = false;
134 var paramcalled = {};
135
136 // store options for OPTIONS request
137 // only used if OPTIONS request
138 var options = [];
139
140 // middleware and routes
141 var stack = self.stack;
142
143 // manage inter-router variables
144 var parentParams = req.params;
145 var parentUrl = req.baseUrl || '';
146 done = restore(done, req, 'baseUrl', 'next', 'params');
147
148 // setup next layer
149 req.next = next;
150
151 // for options requests, respond with a default if nothing else responds
152 if (req.method === 'OPTIONS') {
153 done = wrap(done, function(old, err) {
154 if (err || options.length === 0) return old(err);
155
156 var body = options.join(',');
157 return res.set('Allow', body).send(body);
158 });
159 }
160
161 // setup basic req values
162 req.baseUrl = parentUrl;
163 req.originalUrl = req.originalUrl || req.url;
164
165 next();
166
167 function next(err) {
168 var layerError = err === 'route'
169 ? null
170 : err;
171
172 var layer = stack[idx++];
173
174 if (slashAdded) {
175 req.url = req.url.substr(1);
176 slashAdded = false;
177 }
178
179 if (removed.length !== 0) {
180 req.baseUrl = parentUrl;
181 req.url = protohost + removed + req.url.substr(protohost.length);
182 removed = '';
183 }
184
185 if (!layer) {
186 setImmediate(done, layerError);
187 return;
188 }
189
190 self.match_layer(layer, req, res, function (err, path) {
191 if (err || path === undefined) {
192 return next(layerError || err);
193 }
194
195 // route object and not middleware
196 var route = layer.route;
197
198 // if final route, then we support options
199 if (route) {
200 // we don't run any routes with error first
201 if (layerError) {
202 return next(layerError);
203 }
204
205 var method = req.method;
206 var has_method = route._handles_method(method);
207
208 // build up automatic options response
209 if (!has_method && method === 'OPTIONS') {
210 options.push.apply(options, route._options());
211 }
212
213 // don't even bother
214 if (!has_method && method !== 'HEAD') {
215 return next();
216 }
217
218 // we can now dispatch to the route
219 req.route = route;
220 }
221
222 // Capture one-time layer values
223 req.params = self.mergeParams
224 ? mergeParams(layer.params, parentParams)
225 : layer.params;
226 var layerPath = layer.path;
227
228 // this should be done for the layer
229 self.process_params(layer, paramcalled, req, res, function (err) {
230 if (err) {
231 return next(layerError || err);
232 }
233
234 if (route) {
235 return layer.handle_request(req, res, next);
236 }
237
238 trim_prefix(layer, layerError, layerPath, path);
239 });
240 });
241 }
242
243 function trim_prefix(layer, layerError, layerPath, path) {
244 var c = path[layerPath.length];
245 if (c && '/' !== c && '.' !== c) return next(layerError);
246
247 // Trim off the part of the url that matches the route
248 // middleware (.use stuff) needs to have the path stripped
249 if (layerPath.length !== 0) {
250 debug('trim prefix (%s) from url %s', layerPath, req.url);
251 removed = layerPath;
252 req.url = protohost + req.url.substr(protohost.length + removed.length);
253
254 // Ensure leading slash
255 if (!fqdn && req.url[0] !== '/') {
256 req.url = '/' + req.url;
257 slashAdded = true;
258 }
259
260 // Setup base URL (no trailing slash)
261 req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
262 ? removed.substring(0, removed.length - 1)
263 : removed);
264 }
265
266 debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
267
268 if (layerError) {
269 layer.handle_error(layerError, req, res, next);
270 } else {
271 layer.handle_request(req, res, next);
272 }
273 }
274};
275
276/**
277 * Match request to a layer.
278 *
279 * @api private
280 */
281
282proto.match_layer = function match_layer(layer, req, res, done) {
283 var error = null;
284 var path;
285
286 try {
287 path = parseUrl(req).pathname;
288
289 if (!layer.match(path)) {
290 path = undefined;
291 }
292 } catch (err) {
293 error = err;
294 }
295
296 done(error, path);
297};
298
299/**
300 * Process any parameters for the layer.
301 *
302 * @api private
303 */
304
305proto.process_params = function(layer, called, req, res, done) {
306 var params = this.params;
307
308 // captured parameters from the layer, keys and values
309 var keys = layer.keys;
310
311 // fast track
312 if (!keys || keys.length === 0) {
313 return done();
314 }
315
316 var i = 0;
317 var name;
318 var paramIndex = 0;
319 var key;
320 var paramVal;
321 var paramCallbacks;
322 var paramCalled;
323
324 // process params in order
325 // param callbacks can be async
326 function param(err) {
327 if (err) {
328 return done(err);
329 }
330
331 if (i >= keys.length ) {
332 return done();
333 }
334
335 paramIndex = 0;
336 key = keys[i++];
337
338 if (!key) {
339 return done();
340 }
341
342 name = key.name;
343 paramVal = req.params[name];
344 paramCallbacks = params[name];
345 paramCalled = called[name];
346
347 if (paramVal === undefined || !paramCallbacks) {
348 return param();
349 }
350
351 // param previously called with same value or error occurred
352 if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
353 // restore value
354 req.params[name] = paramCalled.value;
355
356 // next param
357 return param(paramCalled.error);
358 }
359
360 called[name] = paramCalled = {
361 error: null,
362 match: paramVal,
363 value: paramVal
364 };
365
366 paramCallback();
367 }
368
369 // single param callbacks
370 function paramCallback(err) {
371 var fn = paramCallbacks[paramIndex++];
372
373 // store updated value
374 paramCalled.value = req.params[key.name];
375
376 if (err) {
377 // store error
378 paramCalled.error = err;
379 param(err);
380 return;
381 }
382
383 if (!fn) return param();
384
385 try {
386 fn(req, res, paramCallback, paramVal, key.name);
387 } catch (e) {
388 paramCallback(e);
389 }
390 }
391
392 param();
393};
394
395/**
396 * Use the given middleware function, with optional path, defaulting to "/".
397 *
398 * Use (like `.all`) will run for any http METHOD, but it will not add
399 * handlers for those methods so OPTIONS requests will not consider `.use`
400 * functions even if they could respond.
401 *
402 * The other difference is that _route_ path is stripped and not visible
403 * to the handler function. The main effect of this feature is that mounted
404 * handlers can operate without any code changes regardless of the "prefix"
405 * pathname.
406 *
407 * @api public
408 */
409
410proto.use = function use(fn) {
411 var offset = 0;
412 var path = '/';
413
414 // default path to '/'
415 // disambiguate router.use([fn])
416 if (typeof fn !== 'function') {
417 var arg = fn;
418
419 while (Array.isArray(arg) && arg.length !== 0) {
420 arg = arg[0];
421 }
422
423 // first arg is the path
424 if (typeof arg !== 'function') {
425 offset = 1;
426 path = fn;
427 }
428 }
429
430 var callbacks = utils.flatten(slice.call(arguments, offset));
431
432 if (callbacks.length === 0) {
433 throw new TypeError('Router.use() requires middleware functions');
434 }
435
436 callbacks.forEach(function (fn) {
437 if (typeof fn !== 'function') {
438 throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
439 }
440
441 // add the middleware
442 debug('use %s %s', path, fn.name || '<anonymous>');
443
444 var layer = new Layer(path, {
445 sensitive: this.caseSensitive,
446 strict: false,
447 end: false
448 }, fn);
449
450 layer.route = undefined;
451
452 this.stack.push(layer);
453 }, this);
454
455 return this;
456};
457
458/**
459 * Create a new Route for the given path.
460 *
461 * Each route contains a separate middleware stack and VERB handlers.
462 *
463 * See the Route api documentation for details on adding handlers
464 * and middleware to routes.
465 *
466 * @param {String} path
467 * @return {Route}
468 * @api public
469 */
470
471proto.route = function(path){
472 var route = new Route(path);
473
474 var layer = new Layer(path, {
475 sensitive: this.caseSensitive,
476 strict: this.strict,
477 end: true
478 }, route.dispatch.bind(route));
479
480 layer.route = route;
481
482 this.stack.push(layer);
483 return route;
484};
485
486// create Router#VERB functions
487methods.concat('all').forEach(function(method){
488 proto[method] = function(path){
489 var route = this.route(path)
490 route[method].apply(route, slice.call(arguments, 1));
491 return this;
492 };
493});
494
495// get type for error message
496function gettype(obj) {
497 var type = typeof obj;
498
499 if (type !== 'object') {
500 return type;
501 }
502
503 // inspect [[Class]] for objects
504 return toString.call(obj)
505 .replace(objectRegExp, '$1');
506}
507
508// merge params with parent params
509function mergeParams(params, parent) {
510 if (typeof parent !== 'object' || !parent) {
511 return params;
512 }
513
514 // make copy of parent for base
515 var obj = mixin({}, parent);
516
517 // simple non-numeric merging
518 if (!(0 in params) || !(0 in parent)) {
519 return mixin(obj, params);
520 }
521
522 var i = 0;
523 var o = 0;
524
525 // determine numeric gaps
526 while (i === o || o in parent) {
527 if (i in params) i++;
528 if (o in parent) o++;
529 }
530
531 // offset numeric indices in params before merge
532 for (i--; i >= 0; i--) {
533 params[i + o] = params[i];
534
535 // create holes for the merge when necessary
536 if (i < o) {
537 delete params[i];
538 }
539 }
540
541 return mixin(parent, params);
542}
543
544// restore obj props after function
545function restore(fn, obj) {
546 var props = new Array(arguments.length - 2);
547 var vals = new Array(arguments.length - 2);
548
549 for (var i = 0; i < props.length; i++) {
550 props[i] = arguments[i + 2];
551 vals[i] = obj[props[i]];
552 }
553
554 return function(err){
555 // restore vals
556 for (var i = 0; i < props.length; i++) {
557 obj[props[i]] = vals[i];
558 }
559
560 return fn.apply(this, arguments);
561 };
562}
563
564// wrap a function
565function wrap(old, fn) {
566 return function proxy() {
567 var args = new Array(arguments.length + 1);
568
569 args[0] = old;
570 for (var i = 0, len = arguments.length; i < len; i++) {
571 args[i + 1] = arguments[i];
572 }
573
574 fn.apply(this, args);
575 };
576}