summaryrefslogtreecommitdiff
path: root/node_modules/express/lib/router
diff options
context:
space:
mode:
authorJules Laplace <jules@okfoc.us>2012-09-24 16:22:07 -0400
committerJules Laplace <jules@okfoc.us>2012-09-24 16:22:07 -0400
commit686106d544ecc3b6ffd4db2b665d3bc879a58d8c (patch)
treea5b5e50237cef70e12f0745371896e96f5f6d578 /node_modules/express/lib/router
ok
Diffstat (limited to 'node_modules/express/lib/router')
-rw-r--r--node_modules/express/lib/router/collection.js53
-rw-r--r--node_modules/express/lib/router/index.js398
-rw-r--r--node_modules/express/lib/router/methods.js70
-rw-r--r--node_modules/express/lib/router/route.js88
4 files changed, 609 insertions, 0 deletions
diff --git a/node_modules/express/lib/router/collection.js b/node_modules/express/lib/router/collection.js
new file mode 100644
index 0000000..991a9a2
--- /dev/null
+++ b/node_modules/express/lib/router/collection.js
@@ -0,0 +1,53 @@
+
+/*!
+ * Express - router - Collection
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+/**
+ * Expose `Collection`.
+ */
+
+module.exports = Collection;
+
+/**
+ * Initialize a new route `Collection`
+ * with the given `router`.
+ *
+ * @param {Router} router
+ * @api private
+ */
+
+function Collection(router) {
+ Array.apply(this, arguments);
+ this.router = router;
+}
+
+/**
+ * Inherit from `Array.prototype`.
+ */
+
+Collection.prototype.__proto__ = Array.prototype;
+
+/**
+ * Remove the routes in this collection.
+ *
+ * @return {Collection} of routes removed
+ * @api public
+ */
+
+Collection.prototype.remove = function(){
+ var router = this.router
+ , len = this.length
+ , ret = new Collection(this.router);
+
+ for (var i = 0; i < len; ++i) {
+ if (router.remove(this[i])) {
+ ret.push(this[i]);
+ }
+ }
+
+ return ret;
+};
+
diff --git a/node_modules/express/lib/router/index.js b/node_modules/express/lib/router/index.js
new file mode 100644
index 0000000..ff1498b
--- /dev/null
+++ b/node_modules/express/lib/router/index.js
@@ -0,0 +1,398 @@
+
+/*!
+ * Express - Router
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var Route = require('./route')
+ , Collection = require('./collection')
+ , utils = require('../utils')
+ , parse = require('url').parse
+ , toArray = utils.toArray;
+
+/**
+ * Expose `Router` constructor.
+ */
+
+exports = module.exports = Router;
+
+/**
+ * Expose HTTP methods.
+ */
+
+var methods = exports.methods = require('./methods');
+
+/**
+ * Initialize a new `Router` with the given `app`.
+ *
+ * @param {express.HTTPServer} app
+ * @api private
+ */
+
+function Router(app) {
+ var self = this;
+ this.app = app;
+ this.routes = {};
+ this.params = {};
+ this._params = [];
+
+ this.middleware = function(req, res, next){
+ self._dispatch(req, res, next);
+ };
+}
+
+/**
+ * Register a param callback `fn` for the given `name`.
+ *
+ * @param {String|Function} name
+ * @param {Function} fn
+ * @return {Router} for chaining
+ * @api public
+ */
+
+Router.prototype.param = function(name, fn){
+ // param logic
+ if ('function' == typeof name) {
+ this._params.push(name);
+ return;
+ }
+
+ // apply param functions
+ var params = this._params
+ , len = params.length
+ , ret;
+
+ 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;
+};
+
+/**
+ * Return a `Collection` of all routes defined.
+ *
+ * @return {Collection}
+ * @api public
+ */
+
+Router.prototype.all = function(){
+ return this.find(function(){
+ return true;
+ });
+};
+
+/**
+ * Remove the given `route`, returns
+ * a bool indicating if the route was present
+ * or not.
+ *
+ * @param {Route} route
+ * @return {Boolean}
+ * @api public
+ */
+
+Router.prototype.remove = function(route){
+ var routes = this.routes[route.method]
+ , len = routes.length;
+
+ for (var i = 0; i < len; ++i) {
+ if (route == routes[i]) {
+ routes.splice(i, 1);
+ return true;
+ }
+ }
+};
+
+/**
+ * Return routes with route paths matching `path`.
+ *
+ * @param {String} method
+ * @param {String} path
+ * @return {Collection}
+ * @api public
+ */
+
+Router.prototype.lookup = function(method, path){
+ return this.find(function(route){
+ return path == route.path
+ && (route.method == method
+ || method == 'all');
+ });
+};
+
+/**
+ * Return routes with regexps that match the given `url`.
+ *
+ * @param {String} method
+ * @param {String} url
+ * @return {Collection}
+ * @api public
+ */
+
+Router.prototype.match = function(method, url){
+ return this.find(function(route){
+ return route.match(url)
+ && (route.method == method
+ || method == 'all');
+ });
+};
+
+/**
+ * Find routes based on the return value of `fn`
+ * which is invoked once per route.
+ *
+ * @param {Function} fn
+ * @return {Collection}
+ * @api public
+ */
+
+Router.prototype.find = function(fn){
+ var len = methods.length
+ , ret = new Collection(this)
+ , method
+ , routes
+ , route;
+
+ for (var i = 0; i < len; ++i) {
+ method = methods[i];
+ routes = this.routes[method];
+ if (!routes) continue;
+ for (var j = 0, jlen = routes.length; j < jlen; ++j) {
+ route = routes[j];
+ if (fn(route)) ret.push(route);
+ }
+ }
+
+ return ret;
+};
+
+/**
+ * Route dispatcher aka the route "middleware".
+ *
+ * @param {IncomingMessage} req
+ * @param {ServerResponse} res
+ * @param {Function} next
+ * @api private
+ */
+
+Router.prototype._dispatch = function(req, res, next){
+ var params = this.params
+ , self = this;
+
+ // route dispatch
+ (function pass(i, err){
+ var paramCallbacks
+ , paramIndex = 0
+ , paramVal
+ , route
+ , keys
+ , key
+ , ret;
+
+ // match next route
+ function nextRoute(err) {
+ pass(req._route_index + 1, err);
+ }
+
+ // match route
+ req.route = route = self._match(req, i);
+
+ // implied OPTIONS
+ if (!route && 'OPTIONS' == req.method) return self._options(req, res);
+
+ // no route
+ if (!route) return next(err);
+
+ // we have a route
+ // start at param 0
+ req.params = route.params;
+ keys = route.keys;
+ i = 0;
+
+ // param callbacks
+ function param(err) {
+ paramIndex = 0;
+ key = keys[i++];
+ paramVal = key && req.params[key.name];
+ paramCallbacks = key && params[key.name];
+
+ try {
+ if ('route' == err) {
+ nextRoute();
+ } else if (err) {
+ i = 0;
+ callbacks(err);
+ } else if (paramCallbacks && undefined !== paramVal) {
+ paramCallback();
+ } else if (key) {
+ param();
+ } else {
+ i = 0;
+ callbacks();
+ }
+ } catch (err) {
+ param(err);
+ }
+ };
+
+ param(err);
+
+ // single param callbacks
+ function paramCallback(err) {
+ var fn = paramCallbacks[paramIndex++];
+ if (err || !fn) return param(err);
+ fn(req, res, paramCallback, paramVal, key.name);
+ }
+
+ // invoke route callbacks
+ function callbacks(err) {
+ var fn = route.callbacks[i++];
+ try {
+ if ('route' == err) {
+ nextRoute();
+ } else if (err && fn) {
+ if (fn.length < 4) return callbacks(err);
+ fn(err, req, res, callbacks);
+ } else if (fn) {
+ fn(req, res, callbacks);
+ } else {
+ nextRoute(err);
+ }
+ } catch (err) {
+ callbacks(err);
+ }
+ }
+ })(0);
+};
+
+/**
+ * Respond to __OPTIONS__ method.
+ *
+ * @param {IncomingMessage} req
+ * @param {ServerResponse} res
+ * @api private
+ */
+
+Router.prototype._options = function(req, res){
+ var path = parse(req.url).pathname
+ , body = this._optionsFor(path).join(',');
+ res.send(body, { Allow: body });
+};
+
+/**
+ * Return an array of HTTP verbs or "options" for `path`.
+ *
+ * @param {String} path
+ * @return {Array}
+ * @api private
+ */
+
+Router.prototype._optionsFor = function(path){
+ var self = this;
+ return methods.filter(function(method){
+ var routes = self.routes[method];
+ if (!routes || 'options' == method) return;
+ for (var i = 0, len = routes.length; i < len; ++i) {
+ if (routes[i].match(path)) return true;
+ }
+ }).map(function(method){
+ return method.toUpperCase();
+ });
+};
+
+/**
+ * Attempt to match a route for `req`
+ * starting from offset `i`.
+ *
+ * @param {IncomingMessage} req
+ * @param {Number} i
+ * @return {Route}
+ * @api private
+ */
+
+Router.prototype._match = function(req, i){
+ var method = req.method.toLowerCase()
+ , url = parse(req.url)
+ , path = url.pathname
+ , routes = this.routes
+ , captures
+ , route
+ , keys;
+
+ // pass HEAD to GET routes
+ if ('head' == method) method = 'get';
+
+ // routes for this method
+ if (routes = routes[method]) {
+
+ // matching routes
+ for (var len = routes.length; i < len; ++i) {
+ route = routes[i];
+ if (captures = route.match(path)) {
+ keys = route.keys;
+ route.params = [];
+
+ // params from capture groups
+ for (var j = 1, jlen = captures.length; j < jlen; ++j) {
+ var key = keys[j-1]
+ , val = 'string' == typeof captures[j]
+ ? decodeURIComponent(captures[j])
+ : captures[j];
+ if (key) {
+ route.params[key.name] = val;
+ } else {
+ route.params.push(val);
+ }
+ }
+
+ // all done
+ req._route_index = i;
+ return route;
+ }
+ }
+ }
+};
+
+/**
+ * Route `method`, `path`, and one or more callbacks.
+ *
+ * @param {String} method
+ * @param {String} path
+ * @param {Function} callback...
+ * @return {Router} for chaining
+ * @api private
+ */
+
+Router.prototype._route = function(method, path, callbacks){
+ var app = this.app
+ , callbacks = utils.flatten(toArray(arguments, 2));
+
+ // ensure path was given
+ if (!path) throw new Error('app.' + method + '() requires a path');
+
+ // create the route
+ var route = new Route(method, path, callbacks, {
+ sensitive: app.enabled('case sensitive routes')
+ , strict: app.enabled('strict routing')
+ });
+
+ // add it
+ (this.routes[method] = this.routes[method] || [])
+ .push(route);
+ return this;
+};
diff --git a/node_modules/express/lib/router/methods.js b/node_modules/express/lib/router/methods.js
new file mode 100644
index 0000000..e19787b
--- /dev/null
+++ b/node_modules/express/lib/router/methods.js
@@ -0,0 +1,70 @@
+
+/*!
+ * Express - router - methods
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+/**
+ * Hypertext Transfer Protocol -- HTTP/1.1
+ * http://www.ietf.org/rfc/rfc2616.txt
+ */
+
+var RFC2616 = ['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'];
+
+/**
+ * HTTP Extensions for Distributed Authoring -- WEBDAV
+ * http://www.ietf.org/rfc/rfc2518.txt
+ */
+
+var RFC2518 = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK'];
+
+/**
+ * Versioning Extensions to WebDAV
+ * http://www.ietf.org/rfc/rfc3253.txt
+ */
+
+var RFC3253 = ['VERSION-CONTROL', 'REPORT', 'CHECKOUT', 'CHECKIN', 'UNCHECKOUT', 'MKWORKSPACE', 'UPDATE', 'LABEL', 'MERGE', 'BASELINE-CONTROL', 'MKACTIVITY'];
+
+/**
+ * Ordered Collections Protocol (WebDAV)
+ * http://www.ietf.org/rfc/rfc3648.txt
+ */
+
+var RFC3648 = ['ORDERPATCH'];
+
+/**
+ * Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
+ * http://www.ietf.org/rfc/rfc3744.txt
+ */
+
+var RFC3744 = ['ACL'];
+
+/**
+ * Web Distributed Authoring and Versioning (WebDAV) SEARCH
+ * http://www.ietf.org/rfc/rfc5323.txt
+ */
+
+var RFC5323 = ['SEARCH'];
+
+/**
+ * PATCH Method for HTTP
+ * http://www.ietf.org/rfc/rfc5789.txt
+ */
+
+var RFC5789 = ['PATCH'];
+
+/**
+ * Expose the methods.
+ */
+
+module.exports = [].concat(
+ RFC2616
+ , RFC2518
+ , RFC3253
+ , RFC3648
+ , RFC3744
+ , RFC5323
+ , RFC5789).map(function(method){
+ return method.toLowerCase();
+ });
diff --git a/node_modules/express/lib/router/route.js b/node_modules/express/lib/router/route.js
new file mode 100644
index 0000000..7f2965c
--- /dev/null
+++ b/node_modules/express/lib/router/route.js
@@ -0,0 +1,88 @@
+
+/*!
+ * Express - router - Route
+ * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
+ * MIT Licensed
+ */
+
+/**
+ * Expose `Route`.
+ */
+
+module.exports = Route;
+
+/**
+ * Initialize `Route` with the given HTTP `method`, `path`,
+ * and an array of `callbacks` and `options`.
+ *
+ * Options:
+ *
+ * - `sensitive` enable case-sensitive routes
+ * - `strict` enable strict matching for trailing slashes
+ *
+ * @param {String} method
+ * @param {String} path
+ * @param {Array} callbacks
+ * @param {Object} options.
+ * @api private
+ */
+
+function Route(method, path, callbacks, options) {
+ options = options || {};
+ this.path = path;
+ this.method = method;
+ this.callbacks = callbacks;
+ this.regexp = normalize(path
+ , this.keys = []
+ , options.sensitive
+ , options.strict);
+}
+
+/**
+ * Check if this route matches `path` and return captures made.
+ *
+ * @param {String} path
+ * @return {Array}
+ * @api private
+ */
+
+Route.prototype.match = function(path){
+ return this.regexp.exec(path);
+};
+
+/**
+ * Normalize the given path string,
+ * returning a regular expression.
+ *
+ * An empty array should be passed,
+ * which will contain the placeholder
+ * key names. For example "/user/:id" will
+ * then contain ["id"].
+ *
+ * @param {String|RegExp} path
+ * @param {Array} keys
+ * @param {Boolean} sensitive
+ * @param {Boolean} strict
+ * @return {RegExp}
+ * @api private
+ */
+
+function normalize(path, keys, sensitive, strict) {
+ if (path instanceof RegExp) return path;
+ path = path
+ .concat(strict ? '' : '/?')
+ .replace(/\/\(/g, '(?:/')
+ .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
+ keys.push({ name: key, optional: !! optional });
+ slash = slash || '';
+ return ''
+ + (optional ? '' : slash)
+ + '(?:'
+ + (optional ? slash : '')
+ + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ + (optional || '');
+ })
+ .replace(/([\/.])/g, '\\$1')
+ .replace(/\*/g, '(.*)');
+ return new RegExp('^' + path + '$', sensitive ? '' : 'i');
+}