Clean up code and syntax issues.
[express.git] / lib / router / index.js
blobb528df256ce51e6649e992df0d623d1f4092e6aa
1 /**
2 * Module dependencies.
3 */
5 var Route = require('./route')
6 , Layer = require('./layer')
7 , methods = require('methods')
8 , debug = require('debug')('express:router')
9 , parseUrl = require('parseurl');
11 /**
12 * Initialize a new `Router` with the given `options`.
14 * @param {Object} options
15 * @return {Router} which is an callable function
16 * @api public
19 var proto = module.exports = function(options) {
20 options = options || {};
22 function router(req, res, next) {
23 router.handle(req, res, next);
26 // mixin Router class functions
27 router.__proto__ = proto;
29 router.params = {};
30 router._params = [];
31 router.caseSensitive = options.caseSensitive;
32 router.strict = options.strict;
33 router.stack = [];
35 return router;
38 /**
39 * Map the given param placeholder `name`(s) to the given callback.
41 * Parameter mapping is used to provide pre-conditions to routes
42 * which use normalized placeholders. For example a _:user_id_ parameter
43 * could automatically load a user's information from the database without
44 * any additional code,
46 * The callback uses the same signature as middleware, the only difference
47 * being that the value of the placeholder is passed, in this case the _id_
48 * of the user. Once the `next()` function is invoked, just like middleware
49 * it will continue on to execute the route, or subsequent parameter functions.
51 * Just like in middleware, you must either respond to the request or call next
52 * to avoid stalling the request.
54 * app.param('user_id', function(req, res, next, id){
55 * User.find(id, function(err, user){
56 * if (err) {
57 * return next(err);
58 * } else if (!user) {
59 * return next(new Error('failed to load user'));
60 * }
61 * req.user = user;
62 * next();
63 * });
64 * });
66 * @param {String} name
67 * @param {Function} fn
68 * @return {app} for chaining
69 * @api public
72 proto.param = function(name, fn){
73 // param logic
74 if ('function' == typeof name) {
75 this._params.push(name);
76 return;
79 // apply param functions
80 var params = this._params
81 , len = params.length
82 , ret;
84 if (name[0] === ':') {
85 name = name.substr(1);
88 for (var i = 0; i < len; ++i) {
89 if (ret = params[i](name, fn)) {
90 fn = ret;
94 // ensure we end up with a
95 // middleware function
96 if ('function' != typeof fn) {
97 throw new Error('invalid param() call for ' + name + ', got ' + fn);
100 (this.params[name] = this.params[name] || []).push(fn);
101 return this;
105 * Dispatch a req, res into the router.
107 * @api private
110 proto.handle = function(req, res, done) {
111 var self = this;
113 debug('dispatching %s %s', req.method, req.url);
115 var method = req.method.toLowerCase();
117 var search = 1 + req.url.indexOf('?');
118 var pathlength = search ? search - 1 : req.url.length;
119 var fqdn = 1 + req.url.substr(0, pathlength).indexOf('://');
120 var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
121 var idx = 0;
122 var removed = '';
123 var slashAdded = false;
125 // store options for OPTIONS request
126 // only used if OPTIONS request
127 var options = [];
129 // middleware and routes
130 var stack = self.stack;
132 // for options requests, respond with a default if nothing else responds
133 if (method === 'options') {
134 var old = done;
135 done = function(err) {
136 if (err || options.length === 0) return old(err);
138 var body = options.join(',');
139 return res.set('Allow', body).send(body);
143 (function next(err) {
144 if (err === 'route') {
145 err = undefined;
148 var layer = stack[idx++];
149 if (!layer) {
150 return done(err);
153 if (slashAdded) {
154 req.url = req.url.substr(1);
155 slashAdded = false;
158 req.url = protohost + removed + req.url.substr(protohost.length);
159 req.originalUrl = req.originalUrl || req.url;
160 removed = '';
162 try {
163 var path = parseUrl(req).pathname;
164 if (undefined == path) path = '/';
166 if (!layer.match(path)) return next(err);
168 // route object and not middleware
169 var route = layer.route;
171 // if final route, then we support options
172 if (route) {
173 // we don't run any routes with error first
174 if (err) {
175 return next(err);
178 req.route = route;
180 // we can now dispatch to the route
181 if (method === 'options' && !route.methods['options']) {
182 options.push.apply(options, route._options());
186 req.params = req.params || {};
188 for (var key in layer.params) {
189 req.params[key] = layer.params[key];
192 // this should be done for the layer
193 return self.process_params(layer, req, res, function(err) {
194 if (err) {
195 return next(err);
198 if (route) {
199 return layer.handle(req, res, next);
202 trim_prefix();
205 function trim_prefix() {
206 var c = path[layer.path.length];
207 if (c && '/' != c && '.' != c) return next(err);
209 // Trim off the part of the url that matches the route
210 // middleware (.use stuff) needs to have the path stripped
211 debug('trim prefix (%s) from url %s', removed, req.url);
212 removed = layer.path;
213 req.url = protohost + req.url.substr(protohost.length + removed.length);
215 // Ensure leading slash
216 if (!fqdn && '/' != req.url[0]) {
217 req.url = '/' + req.url;
218 slashAdded = true;
221 debug('%s %s : %s', layer.handle.name || 'anonymous', layer.path, req.originalUrl);
222 var arity = layer.handle.length;
223 if (err) {
224 if (arity === 4) {
225 layer.handle(err, req, res, next);
226 } else {
227 next(err);
229 } else if (arity < 4) {
230 layer.handle(req, res, next);
231 } else {
232 next(err);
235 } catch (err) {
236 next(err);
238 })();
242 * Process any parameters for the route.
244 * @api private
247 proto.process_params = function(route, req, res, done) {
248 var params = this.params;
250 // captured parameters from the route, keys and values
251 var keys = route.keys;
253 // fast track
254 if (!keys || keys.length === 0) {
255 return done();
258 var i = 0;
259 var paramIndex = 0;
260 var key;
261 var paramVal;
262 var paramCallbacks;
264 // process params in order
265 // param callbacks can be async
266 function param(err) {
267 if (err) {
268 return done(err);
271 if (i >= keys.length ) {
272 return done();
275 paramIndex = 0;
276 key = keys[i++];
277 paramVal = key && req.params[key.name];
278 paramCallbacks = key && params[key.name];
280 try {
281 if (paramCallbacks && undefined !== paramVal) {
282 return paramCallback();
283 } else if (key) {
284 return param();
286 } catch (err) {
287 done(err);
290 done();
293 // single param callbacks
294 function paramCallback(err) {
295 var fn = paramCallbacks[paramIndex++];
296 if (err || !fn) return param(err);
297 fn(req, res, paramCallback, paramVal, key.name);
300 param();
304 * Use the given middleware function, with optional path, defaulting to "/".
306 * Use (like `.all`) will run for any http METHOD, but it will not add
307 * handlers for those methods so OPTIONS requests will not consider `.use`
308 * functions even if they could respond.
310 * The other difference is that _route_ path is stripped and not visible
311 * to the handler function. The main effect of this feature is that mounted
312 * handlers can operate without any code changes regardless of the "prefix"
313 * pathname.
315 * @param {String|Function} route
316 * @param {Function} fn
317 * @return {app} for chaining
318 * @api public
321 proto.use = function(route, fn){
322 // default route to '/'
323 if ('string' != typeof route) {
324 fn = route;
325 route = '/';
328 if (typeof fn !== 'function') {
329 var type = {}.toString.call(fn);
330 var msg = 'Router.use() requires callback functions but got a ' + type;
331 throw new Error(msg);
334 // strip trailing slash
335 if ('/' == route[route.length - 1]) {
336 route = route.slice(0, -1);
339 var layer = new Layer(route, {
340 sensitive: this.caseSensitive,
341 strict: this.strict,
342 end: false
343 }, fn);
345 // add the middleware
346 debug('use %s %s', route || '/', fn.name || 'anonymous');
348 this.stack.push(layer);
349 return this;
353 * Create a new Route for the given path.
355 * Each route contains a separate middleware stack and VERB handlers.
357 * See the Route api documentation for details on adding handlers
358 * and middleware to routes.
360 * @param {String} path
361 * @return {Route}
362 * @api public
365 proto.route = function(path){
366 var route = new Route(path);
368 var layer = new Layer(path, {
369 sensitive: this.caseSensitive,
370 strict: this.strict,
371 end: true
372 }, route.dispatch.bind(route));
374 layer.route = route;
376 this.stack.push(layer);
377 return route;
380 // create Router#VERB functions
381 methods.concat('all').forEach(function(method){
382 proto[method] = function(path, fn){
383 this.route(path)[method](fn);
384 return this;