Вход Регистрация
Файл: error-kitty/node_modules/express/lib/router/index.js
Строк: 472
<?php
/**
 * Module dependencies.
 */

var Route = require('./route');
var 
Layer = require('./layer');
var 
methods = require('methods');
var 
mixin = require('utils-merge');
var 
debug = require('debug')('express:router');
var 
parseUrl = require('parseurl');
var 
utils = require('../utils');

/**
 * Module variables.
 */

var objectRegExp = /^[object (S+)]$/;
var 
slice = Array.prototype.slice;
var 
toString = Object.prototype.toString;

/**
 * Initialize a new `Router` with the given `options`.
 *
 * @param {Object} options
 * @return {Router} which is an callable function
 * @api public
 */

var proto = module.exports = function(options) {
  
options = options || {};

  function 
router(req, res, next) {
    
router.handle(req, res, next);
  }

  
// mixin Router class functions
  
router.__proto__ = proto;

  
router.params = {};
  
router._params = [];
  
router.caseSensitive = options.caseSensitive;
  
router.mergeParams = options.mergeParams;
  
router.strict = options.strict;
  
router.stack = [];

  return 
router;
};

/**
 * Map the given param placeholder `name`(s) to the given callback.
 *
 * Parameter mapping is used to provide pre-conditions to routes
 * which use normalized placeholders. For example a _:user_id_ parameter
 * could automatically load a user's information from the database without
 * any additional code,
 *
 * The callback uses the same signature as middleware, the only difference
 * being that the value of the placeholder is passed, in this case the _id_
 * of the user. Once the `next()` function is invoked, just like middleware
 * it will continue on to execute the route, or subsequent parameter functions.
 *
 * Just like in middleware, you must either respond to the request or call next
 * to avoid stalling the request.
 *
 *  app.param('user_id', function(req, res, next, id){
 *    User.find(id, function(err, user){
 *      if (err) {
 *        return next(err);
 *      } else if (!user) {
 *        return next(new Error('failed to load user'));
 *      }
 *      req.user = user;
 *      next();
 *    });
 *  });
 *
 * @param {String} name
 * @param {Function} fn
 * @return {app} for chaining
 * @api public
 */

proto.param = function(name, fn){
  
// param logic
  
if ('function' == typeof name) {
    
this._params.push(name);
    return;
  }

  
// apply param functions
  
var params = this._params;
  var 
len = params.length;
  var 
ret;

  if (
name[0] === ':') {
    
name = name.substr(1);
  }

  for (var 
i = 0; i < len; ++i) {
    if (
ret = params[i](name, fn)) {
      
fn = ret;
    }
  }

  
// ensure we end up with a
  // middleware function
  
if ('function' != typeof fn) {
    throw new 
Error('invalid param() call for ' + name + ', got ' + fn);
  }

  (
this.params[name] = this.params[name] || []).push(fn);
  return 
this;
};

/**
 * Dispatch a req, res into the router.
 *
 * @api private
 */

proto.handle = function(req, res, done) {
  var 
self = this;

  
debug('dispatching %s %s', req.method, req.url);

  var 
search = 1 + req.url.indexOf('?');
  var 
pathlength = search ? search - 1 : req.url.length;
  var 
fqdn = 1 + req.url.substr(0, pathlength).indexOf('://');
  var 
protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
  var 
idx = 0;
  var 
removed = '';
  var 
slashAdded = false;
  var 
paramcalled = {};

  
// store options for OPTIONS request
  // only used if OPTIONS request
  
var options = [];

  
// middleware and routes
  
var stack = self.stack;

  
// manage inter-router variables
  
var parentParams = req.params;
  var 
parentUrl = req.baseUrl || '';
  
done = restore(done, req, 'baseUrl', 'next', 'params');

  
// setup next layer
  
req.next = next;

  
// for options requests, respond with a default if nothing else responds
  
if (req.method === 'OPTIONS') {
    
done = wrap(done, function(old, err) {
      if (
err || options.length === 0) return old(err);

      var 
body = options.join(',');
      return 
res.set('Allow', body).send(body);
    });
  }

  
// setup basic req values
  
req.baseUrl = parentUrl;
  
req.originalUrl = req.originalUrl || req.url;

  
next();

  function 
next(err) {
    var 
layerError = err === 'route'
      
? null
      
: err;

    var 
layer = stack[idx++];

    if (
slashAdded) {
      
req.url = req.url.substr(1);
      
slashAdded = false;
    }

    if (
removed.length !== 0) {
      
req.baseUrl = parentUrl;
      
req.url = protohost + removed + req.url.substr(protohost.length);
      
removed = '';
    }

    if (!
layer) {
      return 
done(layerError);
    }

    
self.match_layer(layer, req, res, function (err, path) {
      if (
err || path === undefined) {
        return 
next(layerError || err);
      }

      
// route object and not middleware
      
var route = layer.route;

      
// if final route, then we support options
      
if (route) {
        
// we don't run any routes with error first
        
if (layerError) {
          return 
next(layerError);
        }

        var 
method = req.method;
        var 
has_method = route._handles_method(method);

        
// build up automatic options response
        
if (!has_method && method === 'OPTIONS') {
          
options.push.apply(options, route._options());
        }

        
// don't even bother
        
if (!has_method && method !== 'HEAD') {
          return 
next();
        }

        
// we can now dispatch to the route
        
req.route = route;
      }

      
// Capture one-time layer values
      
req.params = self.mergeParams
        
? mergeParams(layer.params, parentParams)
        : 
layer.params;
      var 
layerPath = layer.path;

      
// this should be done for the layer
      
self.process_params(layer, paramcalled, req, res, function (err) {
        if (
err) {
          return 
next(layerError || err);
        }

        if (
route) {
          return 
layer.handle_request(req, res, next);
        }

        
trim_prefix(layer, layerError, layerPath, path);
      });
    });
  }

  function 
trim_prefix(layer, layerError, layerPath, path) {
    var 
c = path[layerPath.length];
    if (
c && '/' !== c && '.' !== c) return next(layerError);

     
// Trim off the part of the url that matches the route
     // middleware (.use stuff) needs to have the path stripped
    
if (layerPath.length !== 0) {
      
debug('trim prefix (%s) from url %s', layerPath, req.url);
      
removed = layerPath;
      
req.url = protohost + req.url.substr(protohost.length + removed.length);

      
// Ensure leading slash
      
if (!fqdn && req.url[0] !== '/') {
        
req.url = '/' + req.url;
        
slashAdded = true;
      }

      
// Setup base URL (no trailing slash)
      
req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
        
? removed.substring(0, removed.length - 1)
        : 
removed);
    }

    
debug('%s %s : %s', layer.name, layerPath, req.originalUrl);

    if (
layerError) {
      
layer.handle_error(layerError, req, res, next);
    } else {
      
layer.handle_request(req, res, next);
    }
  }
};

/**
 * Match request to a layer.
 *
 * @api private
 */

proto.match_layer = function match_layer(layer, req, res, done) {
  var 
error = null;
  var 
path;

  try {
    
path = parseUrl(req).pathname;

    if (!
layer.match(path)) {
      
path = undefined;
    }
  } catch (
err) {
    
error = err;
  }

  
done(error, path);
};

/**
 * Process any parameters for the layer.
 *
 * @api private
 */

proto.process_params = function(layer, called, req, res, done) {
  var 
params = this.params;

  
// captured parameters from the layer, keys and values
  
var keys = layer.keys;

  
// fast track
  
if (!keys || keys.length === 0) {
    return 
done();
  }

  var 
i = 0;
  var 
name;
  var 
paramIndex = 0;
  var 
key;
  var 
paramVal;
  var 
paramCallbacks;
  var 
paramCalled;

  
// process params in order
  // param callbacks can be async
  
function param(err) {
    if (
err) {
      return 
done(err);
    }

    if (
i >= keys.length ) {
      return 
done();
    }

    
paramIndex = 0;
    
key = keys[i++];

    if (!
key) {
      return 
done();
    }

    
name = key.name;
    
paramVal = req.params[name];
    
paramCallbacks = params[name];
    
paramCalled = called[name];

    if (
paramVal === undefined || !paramCallbacks) {
      return 
param();
    }

    
// param previously called with same value or error occurred
    
if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
      
// restore value
      
req.params[name] = paramCalled.value;

      
// next param
      
return param(paramCalled.error);
    }

    
called[name] = paramCalled = {
      
error: null,
      
match: paramVal,
      
value: paramVal
    
};

    
paramCallback();
  }

  
// single param callbacks
  
function paramCallback(err) {
    var 
fn = paramCallbacks[paramIndex++];

    
// store updated value
    
paramCalled.value = req.params[key.name];

    if (
err) {
      
// store error
      
paramCalled.error = err;
      
param(err);
      return;
    }

    if (!
fn) return param();

    try {
      
fn(req, res, paramCallback, paramVal, key.name);
    } catch (
e) {
      
paramCallback(e);
    }
  }

  
param();
};

/**
 * Use the given middleware function, with optional path, defaulting to "/".
 *
 * Use (like `.all`) will run for any http METHOD, but it will not add
 * handlers for those methods so OPTIONS requests will not consider `.use`
 * functions even if they could respond.
 *
 * The other difference is that _route_ path is stripped and not visible
 * to the handler function. The main effect of this feature is that mounted
 * handlers can operate without any code changes regardless of the "prefix"
 * pathname.
 *
 * @api public
 */

proto.use = function use(fn) {
  var 
offset = 0;
  var 
path = '/';
  var 
self = this;

  
// default path to '/'
  // disambiguate router.use([fn])
  
if (typeof fn !== 'function') {
    var 
arg = fn;

    while (Array.
isArray(arg) && arg.length !== 0) {
      
arg = arg[0];
    }

    
// first arg is the path
    
if (typeof arg !== 'function') {
      
offset = 1;
      
path = fn;
    }
  }

  var 
callbacks = utils.flatten(slice.call(arguments, offset));

  if (
callbacks.length === 0) {
    throw new 
TypeError('Router.use() requires middleware functions');
  }

  
callbacks.forEach(function (fn) {
    if (
typeof fn !== 'function') {
      throw new 
TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
    }

    
// add the middleware
    
debug('use %s %s', path, fn.name || '<anonymous>');

    var 
layer = new Layer(path, {
      
sensitive: self.caseSensitive,
      
strict: false,
      
end: false
    
}, fn);

    
layer.route = undefined;

    
self.stack.push(layer);
  });

  return 
this;
};

/**
 * Create a new Route for the given path.
 *
 * Each route contains a separate middleware stack and VERB handlers.
 *
 * See the Route api documentation for details on adding handlers
 * and middleware to routes.
 *
 * @param {String} path
 * @return {Route}
 * @api public
 */

proto.route = function(path){
  var 
route = new Route(path);

  var 
layer = new Layer(path, {
    
sensitive: this.caseSensitive,
    
strict: this.strict,
    
end: true
  
}, route.dispatch.bind(route));

  
layer.route = route;

  
this.stack.push(layer);
  return 
route;
};

// create Router#VERB functions
methods.concat('all').forEach(function(method){
  
proto[method] = function(path){
    var 
route = this.route(path)
    
route[method].apply(route, slice.call(arguments, 1));
    return 
this;
  };
});

// get type for error message
function gettype(obj) {
  var 
type = typeof obj;

  if (
type !== 'object') {
    return 
type;
  }

  
// inspect [[Class]] for objects
  
return toString.call(obj)
    .
replace(objectRegExp, '$1');
}

// merge params with parent params
function mergeParams(params, parent) {
  if (
typeof parent !== 'object' || !parent) {
    return 
params;
  }

  
// make copy of parent for base
  
var obj = mixin({}, parent);

  
// simple non-numeric merging
  
if (!(0 in params) || !(0 in parent)) {
    return 
mixin(obj, params);
  }

  var 
i = 0;
  var 
o = 0;

  
// determine numeric gaps
  
while (i === o || o in parent) {
    if (
i in params) i++;
    if (
o in parent) o++;
  }

  
// offset numeric indices in params before merge
  
for (i--; i >= 0; i--) {
    
params[i + o] = params[i];

    
// create holes for the merge when necessary
    
if (i < o) {
      
delete params[i];
    }
  }

  return 
mixin(parent, params);
}

// restore obj props after function
function restore(fn, obj) {
  var 
props = new Array(arguments.length - 2);
  var 
vals = new Array(arguments.length - 2);

  for (var 
i = 0; i < props.length; i++) {
    
props[i] = arguments[i + 2];
    
vals[i] = obj[props[i]];
  }

  return function(
err){
    
// restore vals
    
for (var i = 0; i < props.length; i++) {
      
obj[props[i]] = vals[i];
    }

    return 
fn.apply(this, arguments);
  };
}

// wrap a function
function wrap(old, fn) {
  return function 
proxy() {
    var 
args = new Array(arguments.length + 1);

    
args[0] = old;
    for (var 
i = 0, len = arguments.length; i < len; i++) {
      
args[i + 1] = arguments[i];
    }

    
fn.apply(this, args);
  };
}
?>
Онлайн: 2
Реклама