You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					663 lines
				
				14 KiB
			
		
		
			
		
	
	
					663 lines
				
				14 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * express
							 | 
						||
| 
								 | 
							
								 * Copyright(c) 2009-2013 TJ Holowaychuk
							 | 
						||
| 
								 | 
							
								 * Copyright(c) 2013 Roman Shtylman
							 | 
						||
| 
								 | 
							
								 * Copyright(c) 2014-2015 Douglas Christopher Wilson
							 | 
						||
| 
								 | 
							
								 * MIT Licensed
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Module dependencies.
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var Route = require('./route');
							 | 
						||
| 
								 | 
							
								var Layer = require('./layer');
							 | 
						||
| 
								 | 
							
								var methods = require('methods');
							 | 
						||
| 
								 | 
							
								var mixin = require('utils-merge');
							 | 
						||
| 
								 | 
							
								var debug = require('debug')('express:router');
							 | 
						||
| 
								 | 
							
								var deprecate = require('depd')('express');
							 | 
						||
| 
								 | 
							
								var flatten = require('array-flatten');
							 | 
						||
| 
								 | 
							
								var parseUrl = require('parseurl');
							 | 
						||
| 
								 | 
							
								var setPrototypeOf = require('setprototypeof')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Module variables.
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var proto = module.exports = function(options) {
							 | 
						||
| 
								 | 
							
								  var opts = options || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function router(req, res, next) {
							 | 
						||
| 
								 | 
							
								    router.handle(req, res, next);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // mixin Router class functions
							 | 
						||
| 
								 | 
							
								  setPrototypeOf(router, proto)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  router.params = {};
							 | 
						||
| 
								 | 
							
								  router._params = [];
							 | 
						||
| 
								 | 
							
								  router.caseSensitive = opts.caseSensitive;
							 | 
						||
| 
								 | 
							
								  router.mergeParams = opts.mergeParams;
							 | 
						||
| 
								 | 
							
								  router.strict = opts.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
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								proto.param = function param(name, fn) {
							 | 
						||
| 
								 | 
							
								  // param logic
							 | 
						||
| 
								 | 
							
								  if (typeof name === 'function') {
							 | 
						||
| 
								 | 
							
								    deprecate('router.param(fn): Refactor to use path params');
							 | 
						||
| 
								 | 
							
								    this._params.push(name);
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // apply param functions
							 | 
						||
| 
								 | 
							
								  var params = this._params;
							 | 
						||
| 
								 | 
							
								  var len = params.length;
							 | 
						||
| 
								 | 
							
								  var ret;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (name[0] === ':') {
							 | 
						||
| 
								 | 
							
								    deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead');
							 | 
						||
| 
								 | 
							
								    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.
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								proto.handle = function handle(req, res, out) {
							 | 
						||
| 
								 | 
							
								  var self = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  debug('dispatching %s %s', req.method, req.url);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var idx = 0;
							 | 
						||
| 
								 | 
							
								  var protohost = getProtohost(req.url) || ''
							 | 
						||
| 
								 | 
							
								  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 || '';
							 | 
						||
| 
								 | 
							
								  var done = restore(out, 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);
							 | 
						||
| 
								 | 
							
								      sendOptionsResponse(res, options, old);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // setup basic req values
							 | 
						||
| 
								 | 
							
								  req.baseUrl = parentUrl;
							 | 
						||
| 
								 | 
							
								  req.originalUrl = req.originalUrl || req.url;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  next();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function next(err) {
							 | 
						||
| 
								 | 
							
								    var layerError = err === 'route'
							 | 
						||
| 
								 | 
							
								      ? null
							 | 
						||
| 
								 | 
							
								      : err;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // remove added slash
							 | 
						||
| 
								 | 
							
								    if (slashAdded) {
							 | 
						||
| 
								 | 
							
								      req.url = req.url.substr(1);
							 | 
						||
| 
								 | 
							
								      slashAdded = false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // restore altered req.url
							 | 
						||
| 
								 | 
							
								    if (removed.length !== 0) {
							 | 
						||
| 
								 | 
							
								      req.baseUrl = parentUrl;
							 | 
						||
| 
								 | 
							
								      req.url = protohost + removed + req.url.substr(protohost.length);
							 | 
						||
| 
								 | 
							
								      removed = '';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // signal to exit router
							 | 
						||
| 
								 | 
							
								    if (layerError === 'router') {
							 | 
						||
| 
								 | 
							
								      setImmediate(done, null)
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // no more matching layers
							 | 
						||
| 
								 | 
							
								    if (idx >= stack.length) {
							 | 
						||
| 
								 | 
							
								      setImmediate(done, layerError);
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // get pathname of request
							 | 
						||
| 
								 | 
							
								    var path = getPathname(req);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (path == null) {
							 | 
						||
| 
								 | 
							
								      return done(layerError);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // find next matching layer
							 | 
						||
| 
								 | 
							
								    var layer;
							 | 
						||
| 
								 | 
							
								    var match;
							 | 
						||
| 
								 | 
							
								    var route;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while (match !== true && idx < stack.length) {
							 | 
						||
| 
								 | 
							
								      layer = stack[idx++];
							 | 
						||
| 
								 | 
							
								      match = matchLayer(layer, path);
							 | 
						||
| 
								 | 
							
								      route = layer.route;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (typeof match !== 'boolean') {
							 | 
						||
| 
								 | 
							
								        // hold on to layerError
							 | 
						||
| 
								 | 
							
								        layerError = layerError || match;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (match !== true) {
							 | 
						||
| 
								 | 
							
								        continue;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!route) {
							 | 
						||
| 
								 | 
							
								        // process non-route handlers normally
							 | 
						||
| 
								 | 
							
								        continue;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (layerError) {
							 | 
						||
| 
								 | 
							
								        // routes do not match with a pending error
							 | 
						||
| 
								 | 
							
								        match = false;
							 | 
						||
| 
								 | 
							
								        continue;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var method = req.method;
							 | 
						||
| 
								 | 
							
								      var has_method = route._handles_method(method);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // build up automatic options response
							 | 
						||
| 
								 | 
							
								      if (!has_method && method === 'OPTIONS') {
							 | 
						||
| 
								 | 
							
								        appendMethods(options, route._options());
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // don't even bother matching route
							 | 
						||
| 
								 | 
							
								      if (!has_method && method !== 'HEAD') {
							 | 
						||
| 
								 | 
							
								        match = false;
							 | 
						||
| 
								 | 
							
								        continue;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // no match
							 | 
						||
| 
								 | 
							
								    if (match !== true) {
							 | 
						||
| 
								 | 
							
								      return done(layerError);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // store route for dispatch on change
							 | 
						||
| 
								 | 
							
								    if (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) {
							 | 
						||
| 
								 | 
							
								    if (layerPath.length !== 0) {
							 | 
						||
| 
								 | 
							
								      // Validate path breaks on a path separator
							 | 
						||
| 
								 | 
							
								      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
							 | 
						||
| 
								 | 
							
								      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 (!protohost && 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);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Process any parameters for the layer.
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								proto.process_params = function process_params(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++];
							 | 
						||
| 
								 | 
							
								    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.match === paramVal
							 | 
						||
| 
								 | 
							
								      || (paramCalled.error && paramCalled.error !== 'route'))) {
							 | 
						||
| 
								 | 
							
								      // 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.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								proto.use = function use(fn) {
							 | 
						||
| 
								 | 
							
								  var offset = 0;
							 | 
						||
| 
								 | 
							
								  var path = '/';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // 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 = flatten(slice.call(arguments, offset));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (callbacks.length === 0) {
							 | 
						||
| 
								 | 
							
								    throw new TypeError('Router.use() requires a middleware function')
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (var i = 0; i < callbacks.length; i++) {
							 | 
						||
| 
								 | 
							
								    var fn = callbacks[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (typeof fn !== 'function') {
							 | 
						||
| 
								 | 
							
								      throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // add the middleware
							 | 
						||
| 
								 | 
							
								    debug('use %o %s', path, fn.name || '<anonymous>')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var layer = new Layer(path, {
							 | 
						||
| 
								 | 
							
								      sensitive: this.caseSensitive,
							 | 
						||
| 
								 | 
							
								      strict: false,
							 | 
						||
| 
								 | 
							
								      end: false
							 | 
						||
| 
								 | 
							
								    }, fn);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    layer.route = undefined;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.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}
							 | 
						||
| 
								 | 
							
								 * @public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								proto.route = function route(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;
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// append methods to a list of methods
							 | 
						||
| 
								 | 
							
								function appendMethods(list, addition) {
							 | 
						||
| 
								 | 
							
								  for (var i = 0; i < addition.length; i++) {
							 | 
						||
| 
								 | 
							
								    var method = addition[i];
							 | 
						||
| 
								 | 
							
								    if (list.indexOf(method) === -1) {
							 | 
						||
| 
								 | 
							
								      list.push(method);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// get pathname of request
							 | 
						||
| 
								 | 
							
								function getPathname(req) {
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    return parseUrl(req).pathname;
							 | 
						||
| 
								 | 
							
								  } catch (err) {
							 | 
						||
| 
								 | 
							
								    return undefined;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Get get protocol + host for a URL
							 | 
						||
| 
								 | 
							
								function getProtohost(url) {
							 | 
						||
| 
								 | 
							
								  if (typeof url !== 'string' || url.length === 0 || url[0] === '/') {
							 | 
						||
| 
								 | 
							
								    return undefined
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var searchIndex = url.indexOf('?')
							 | 
						||
| 
								 | 
							
								  var pathLength = searchIndex !== -1
							 | 
						||
| 
								 | 
							
								    ? searchIndex
							 | 
						||
| 
								 | 
							
								    : url.length
							 | 
						||
| 
								 | 
							
								  var fqdnIndex = url.substr(0, pathLength).indexOf('://')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return fqdnIndex !== -1
							 | 
						||
| 
								 | 
							
								    ? url.substr(0, url.indexOf('/', 3 + fqdnIndex))
							 | 
						||
| 
								 | 
							
								    : undefined
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Match path to a layer.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Layer} layer
							 | 
						||
| 
								 | 
							
								 * @param {string} path
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function matchLayer(layer, path) {
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    return layer.match(path);
							 | 
						||
| 
								 | 
							
								  } catch (err) {
							 | 
						||
| 
								 | 
							
								    return err;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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 in params) {
							 | 
						||
| 
								 | 
							
								    i++;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (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(obj, 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 () {
							 | 
						||
| 
								 | 
							
								    // restore vals
							 | 
						||
| 
								 | 
							
								    for (var i = 0; i < props.length; i++) {
							 | 
						||
| 
								 | 
							
								      obj[props[i]] = vals[i];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return fn.apply(this, arguments);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// send an OPTIONS response
							 | 
						||
| 
								 | 
							
								function sendOptionsResponse(res, options, next) {
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    var body = options.join(',');
							 | 
						||
| 
								 | 
							
								    res.set('Allow', body);
							 | 
						||
| 
								 | 
							
								    res.send(body);
							 | 
						||
| 
								 | 
							
								  } catch (err) {
							 | 
						||
| 
								 | 
							
								    next(err);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 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);
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 |