arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
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}