github.com/cloudcredo/cloudrocker@v0.0.0-20160108110610-1320f8cc2dfd/sample-apps/node/node_modules/express/lib/router/index.js (about)

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