source: Dev/trunk/src/node_modules/express/lib/router/index.js

Last change on this file was 484, checked in by hendrikvanantwerpen, 11 years ago

Commit node_modules, to make checkouts and builds more deterministic.

File size: 5.9 KB
Line 
1/**
2 * Module dependencies.
3 */
4
5var Route = require('./route')
6  , utils = require('../utils')
7  , methods = require('methods')
8  , debug = require('debug')('express:router')
9  , parse = require('connect').utils.parseUrl;
10
11/**
12 * Expose `Router` constructor.
13 */
14
15exports = module.exports = Router;
16
17/**
18 * Initialize a new `Router` with the given `options`.
19 *
20 * @param {Object} options
21 * @api private
22 */
23
24function Router(options) {
25  options = options || {};
26  var self = this;
27  this.map = {};
28  this.params = {};
29  this._params = [];
30  this.caseSensitive = options.caseSensitive;
31  this.strict = options.strict;
32  this.middleware = function router(req, res, next){
33    self._dispatch(req, res, next);
34  };
35}
36
37/**
38 * Register a param callback `fn` for the given `name`.
39 *
40 * @param {String|Function} name
41 * @param {Function} fn
42 * @return {Router} for chaining
43 * @api public
44 */
45
46Router.prototype.param = function(name, fn){
47  // param logic
48  if ('function' == typeof name) {
49    this._params.push(name);
50    return;
51  }
52
53  // apply param functions
54  var params = this._params
55    , len = params.length
56    , ret;
57
58  for (var i = 0; i < len; ++i) {
59    if (ret = params[i](name, fn)) {
60      fn = ret;
61    }
62  }
63
64  // ensure we end up with a
65  // middleware function
66  if ('function' != typeof fn) {
67    throw new Error('invalid param() call for ' + name + ', got ' + fn);
68  }
69
70  (this.params[name] = this.params[name] || []).push(fn);
71  return this;
72};
73
74/**
75 * Route dispatcher aka the route "middleware".
76 *
77 * @param {IncomingMessage} req
78 * @param {ServerResponse} res
79 * @param {Function} next
80 * @api private
81 */
82
83Router.prototype._dispatch = function(req, res, next){
84  var params = this.params
85    , self = this;
86
87  debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
88
89  // route dispatch
90  (function pass(i, err){
91    var paramCallbacks
92      , paramIndex = 0
93      , paramVal
94      , route
95      , keys
96      , key;
97
98    // match next route
99    function nextRoute(err) {
100      pass(req._route_index + 1, err);
101    }
102
103    // match route
104    req.route = route = self.matchRequest(req, i);
105
106    // no route
107    if (!route) return next(err);
108    debug('matched %s %s', route.method, route.path);
109
110    // we have a route
111    // start at param 0
112    req.params = route.params;
113    keys = route.keys;
114    i = 0;
115
116    // param callbacks
117    function param(err) {
118      paramIndex = 0;
119      key = keys[i++];
120      paramVal = key && req.params[key.name];
121      paramCallbacks = key && params[key.name];
122
123      try {
124        if ('route' == err) {
125          nextRoute();
126        } else if (err) {
127          i = 0;
128          callbacks(err);
129        } else if (paramCallbacks && undefined !== paramVal) {
130          paramCallback();
131        } else if (key) {
132          param();
133        } else {
134          i = 0;
135          callbacks();
136        }
137      } catch (err) {
138        param(err);
139      }
140    };
141
142    param(err);
143
144    // single param callbacks
145    function paramCallback(err) {
146      var fn = paramCallbacks[paramIndex++];
147      if (err || !fn) return param(err);
148      fn(req, res, paramCallback, paramVal, key.name);
149    }
150
151    // invoke route callbacks
152    function callbacks(err) {
153      var fn = route.callbacks[i++];
154      try {
155        if ('route' == err) {
156          nextRoute();
157        } else if (err && fn) {
158          if (fn.length < 4) return callbacks(err);
159          fn(err, req, res, callbacks);
160        } else if (fn) {
161          if (fn.length < 4) return fn(req, res, callbacks);
162          callbacks();
163        } else {
164          nextRoute(err);
165        }
166      } catch (err) {
167        callbacks(err);
168      }
169    }
170  })(0);
171};
172
173/**
174 * Attempt to match a route for `req`
175 * with optional starting index of `i`
176 * defaulting to 0.
177 *
178 * @param {IncomingMessage} req
179 * @param {Number} i
180 * @return {Route}
181 * @api private
182 */
183
184Router.prototype.matchRequest = function(req, i, head){
185  var method = req.method.toLowerCase()
186    , url = parse(req)
187    , path = url.pathname
188    , routes = this.map
189    , i = i || 0
190    , route;
191
192  // HEAD support
193  if (!head && 'head' == method) {
194    route = this.matchRequest(req, i, true);
195    if (route) return route;
196     method = 'get';
197  }
198
199  // routes for this method
200  if (routes = routes[method]) {
201
202    // matching routes
203    for (var len = routes.length; i < len; ++i) {
204      route = routes[i];
205      if (route.match(path)) {
206        req._route_index = i;
207        return route;
208      }
209    }
210  }
211};
212
213/**
214 * Attempt to match a route for `method`
215 * and `url` with optional starting
216 * index of `i` defaulting to 0.
217 *
218 * @param {String} method
219 * @param {String} url
220 * @param {Number} i
221 * @return {Route}
222 * @api private
223 */
224
225Router.prototype.match = function(method, url, i, head){
226  var req = { method: method, url: url };
227  return  this.matchRequest(req, i, head);
228};
229
230/**
231 * Route `method`, `path`, and one or more callbacks.
232 *
233 * @param {String} method
234 * @param {String} path
235 * @param {Function} callback...
236 * @return {Router} for chaining
237 * @api private
238 */
239
240Router.prototype.route = function(method, path, callbacks){
241  var method = method.toLowerCase()
242    , callbacks = utils.flatten([].slice.call(arguments, 2));
243
244  // ensure path was given
245  if (!path) throw new Error('Router#' + method + '() requires a path');
246
247  // ensure all callbacks are functions
248  callbacks.forEach(function(fn, i){
249    if ('function' == typeof fn) return;
250    var type = {}.toString.call(fn);
251    var msg = '.' + method + '() requires callback functions but got a ' + type;
252    throw new Error(msg);
253  });
254
255  // create the route
256  debug('defined %s %s', method, path);
257  var route = new Route(method, path, callbacks, {
258    sensitive: this.caseSensitive,
259    strict: this.strict
260  });
261
262  // add it
263  (this.map[method] = this.map[method] || []).push(route);
264  return this;
265};
266
267methods.forEach(function(method){
268  Router.prototype[method] = function(path){
269    var args = [method].concat([].slice.call(arguments));
270    this.route.apply(this, args);
271    return this;
272  };
273});
Note: See TracBrowser for help on using the repository browser.