diff node_modules/express/lib/router/index.js @ 73:0c3a2942ddee

now using express to server static content
author Rob Canning <rc@kiben.net>
date Sun, 29 Jun 2014 12:11:51 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/node_modules/express/lib/router/index.js	Sun Jun 29 12:11:51 2014 +0000
@@ -0,0 +1,452 @@
+/**
+ * Module dependencies.
+ */
+
+var Route = require('./route');
+var Layer = require('./layer');
+var methods = require('methods');
+var debug = require('debug')('express:router');
+var parseUrl = require('parseurl');
+var slice = Array.prototype.slice;
+
+/**
+ * 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.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 method = req.method.toLowerCase();
+
+  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 parent = req.next;
+  var parentUrl = req.baseUrl || '';
+  done = wrap(done, function(old, err) {
+    req.baseUrl = parentUrl;
+    req.next = parent;
+    old(err);
+  });
+  req.next = next;
+
+  // for options requests, respond with a default if nothing else responds
+  if (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);
+    });
+  }
+
+  next();
+
+  function next(err) {
+    if (err === 'route') {
+      err = undefined;
+    }
+
+    var layer = stack[idx++];
+    var layerPath;
+
+    if (!layer) {
+      return done(err);
+    }
+
+    if (slashAdded) {
+      req.url = req.url.substr(1);
+      slashAdded = false;
+    }
+
+    req.baseUrl = parentUrl;
+    req.url = protohost + removed + req.url.substr(protohost.length);
+    req.originalUrl = req.originalUrl || req.url;
+    removed = '';
+
+    try {
+      var path = parseUrl(req).pathname;
+      if (undefined == path) path = '/';
+
+      if (!layer.match(path)) return next(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 (err) {
+          return next(err);
+        }
+
+        req.route = route;
+
+        // we can now dispatch to the route
+        if (method === 'options' && !route.methods['options']) {
+          options.push.apply(options, route._options());
+        }
+      }
+
+      // Capture one-time layer values
+      req.params = layer.params;
+      layerPath = layer.path;
+
+      // this should be done for the layer
+      return self.process_params(layer, paramcalled, req, res, function(err) {
+        if (err) {
+          return next(err);
+        }
+
+        if (route) {
+          return layer.handle(req, res, next);
+        }
+
+        trim_prefix();
+      });
+
+    } catch (err) {
+      next(err);
+    }
+
+    function trim_prefix() {
+      var c = path[layerPath.length];
+      if (c && '/' != c && '.' != c) return next(err);
+
+      // Trim off the part of the url that matches the route
+      // middleware (.use stuff) needs to have the path stripped
+      removed = layerPath;
+      if (removed.length) {
+        debug('trim prefix (%s) from url %s', layerPath, req.url);
+        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)
+      if (removed.length && removed.substr(-1) === '/') {
+        req.baseUrl = parentUrl + removed.substring(0, removed.length - 1);
+      } else {
+        req.baseUrl = parentUrl + removed;
+      }
+
+      debug('%s %s : %s', layer.handle.name || 'anonymous', layerPath, req.originalUrl);
+      var arity = layer.handle.length;
+      try {
+        if (err && arity === 4) {
+          layer.handle(err, req, res, next);
+        } else if (!err && arity < 4) {
+          layer.handle(req, res, next);
+        } else {
+          next(err);
+        }
+      } catch (err) {
+        next(err);
+      }
+    }
+  }
+
+  function wrap(old, fn) {
+    return function () {
+      var args = [old].concat(slice.call(arguments));
+      fn.apply(this, args);
+    };
+  }
+};
+
+/**
+ * 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
+    };
+
+    try {
+      return paramCallback();
+    } catch (err) {
+      return done(err);
+    }
+  }
+
+  // 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();
+
+    fn(req, res, paramCallback, paramVal, key.name);
+  }
+
+  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.
+ *
+ * @param {String|Function} route
+ * @param {Function} fn
+ * @return {app} for chaining
+ * @api public
+ */
+
+proto.use = function(route, fn){
+  // default route to '/'
+  if ('string' != typeof route) {
+    fn = route;
+    route = '/';
+  }
+
+  if (typeof fn !== 'function') {
+    var type = {}.toString.call(fn);
+    var msg = 'Router.use() requires callback functions but got a ' + type;
+    throw new Error(msg);
+  }
+
+  // strip trailing slash
+  if ('/' == route[route.length - 1]) {
+    route = route.slice(0, -1);
+  }
+
+  var layer = new Layer(route, {
+    sensitive: this.caseSensitive,
+    strict: this.strict,
+    end: false
+  }, fn);
+
+  // add the middleware
+  debug('use %s %s', route || '/', fn.name || 'anonymous');
+
+  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}
+ * @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;
+  };
+});