Fix license issues
[sdnc/oam.git] / dgbuilder / dgeflows / node_modules / express / lib / router / index.js
1
2 /**
3  * Module dependencies.
4  */
5
6 var Route = require('./route');
7 var Layer = require('./layer');
8 var methods = require('methods');
9 var mixin = require('utils-merge');
10 var debug = require('debug')('express:router');
11 var deprecate = require('depd')('express');
12 var parseUrl = require('parseurl');
13 var utils = require('../utils');
14
15 /**
16  * Module variables.
17  */
18
19 var objectRegExp = /^\[object (\S+)\]$/;
20 var slice = Array.prototype.slice;
21 var toString = Object.prototype.toString;
22
23 /**
24  * Initialize a new `Router` with the given `options`.
25  *
26  * @param {Object} options
27  * @return {Router} which is an callable function
28  * @api public
29  */
30
31 var proto = module.exports = function(options) {
32   options = options || {};
33
34   function router(req, res, next) {
35     router.handle(req, res, next);
36   }
37
38   // mixin Router class functions
39   router.__proto__ = proto;
40
41   router.params = {};
42   router._params = [];
43   router.caseSensitive = options.caseSensitive;
44   router.mergeParams = options.mergeParams;
45   router.strict = options.strict;
46   router.stack = [];
47
48   return router;
49 };
50
51 /**
52  * Map the given param placeholder `name`(s) to the given callback.
53  *
54  * Parameter mapping is used to provide pre-conditions to routes
55  * which use normalized placeholders. For example a _:user_id_ parameter
56  * could automatically load a user's information from the database without
57  * any additional code,
58  *
59  * The callback uses the same signature as middleware, the only difference
60  * being that the value of the placeholder is passed, in this case the _id_
61  * of the user. Once the `next()` function is invoked, just like middleware
62  * it will continue on to execute the route, or subsequent parameter functions.
63  *
64  * Just like in middleware, you must either respond to the request or call next
65  * to avoid stalling the request.
66  *
67  *  app.param('user_id', function(req, res, next, id){
68  *    User.find(id, function(err, user){
69  *      if (err) {
70  *        return next(err);
71  *      } else if (!user) {
72  *        return next(new Error('failed to load user'));
73  *      }
74  *      req.user = user;
75  *      next();
76  *    });
77  *  });
78  *
79  * @param {String} name
80  * @param {Function} fn
81  * @return {app} for chaining
82  * @api public
83  */
84
85 proto.param = function param(name, fn) {
86   // param logic
87   if (typeof name === 'function') {
88     deprecate('router.param(fn): Refactor to use path params');
89     this._params.push(name);
90     return;
91   }
92
93   // apply param functions
94   var params = this._params;
95   var len = params.length;
96   var ret;
97
98   if (name[0] === ':') {
99     deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead');
100     name = name.substr(1);
101   }
102
103   for (var i = 0; i < len; ++i) {
104     if (ret = params[i](name, fn)) {
105       fn = ret;
106     }
107   }
108
109   // ensure we end up with a
110   // middleware function
111   if ('function' != typeof fn) {
112     throw new Error('invalid param() call for ' + name + ', got ' + fn);
113   }
114
115   (this.params[name] = this.params[name] || []).push(fn);
116   return this;
117 };
118
119 /**
120  * Dispatch a req, res into the router.
121  *
122  * @api private
123  */
124
125 proto.handle = function(req, res, done) {
126   var self = this;
127
128   debug('dispatching %s %s', req.method, req.url);
129
130   var search = 1 + req.url.indexOf('?');
131   var pathlength = search ? search - 1 : req.url.length;
132   var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://');
133   var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
134   var idx = 0;
135   var removed = '';
136   var slashAdded = false;
137   var paramcalled = {};
138
139   // store options for OPTIONS request
140   // only used if OPTIONS request
141   var options = [];
142
143   // middleware and routes
144   var stack = self.stack;
145
146   // manage inter-router variables
147   var parentParams = req.params;
148   var parentUrl = req.baseUrl || '';
149   done = restore(done, req, 'baseUrl', 'next', 'params');
150
151   // setup next layer
152   req.next = next;
153
154   // for options requests, respond with a default if nothing else responds
155   if (req.method === 'OPTIONS') {
156     done = wrap(done, function(old, err) {
157       if (err || options.length === 0) return old(err);
158       sendOptionsResponse(res, options, old);
159     });
160   }
161
162   // setup basic req values
163   req.baseUrl = parentUrl;
164   req.originalUrl = req.originalUrl || req.url;
165
166   next();
167
168   function next(err) {
169     var layerError = err === 'route'
170       ? null
171       : err;
172
173     // remove added slash
174     if (slashAdded) {
175       req.url = req.url.substr(1);
176       slashAdded = false;
177     }
178
179     // restore altered req.url
180     if (removed.length !== 0) {
181       req.baseUrl = parentUrl;
182       req.url = protohost + removed + req.url.substr(protohost.length);
183       removed = '';
184     }
185
186     // no more matching layers
187     if (idx >= stack.length) {
188       setImmediate(done, layerError);
189       return;
190     }
191
192     // get pathname of request
193     var path = getPathname(req);
194
195     if (path == null) {
196       return done(layerError);
197     }
198
199     // find next matching layer
200     var layer;
201     var match;
202     var route;
203
204     while (match !== true && idx < stack.length) {
205       layer = stack[idx++];
206       match = matchLayer(layer, path);
207       route = layer.route;
208
209       if (typeof match !== 'boolean') {
210         // hold on to layerError
211         layerError = layerError || match;
212       }
213
214       if (match !== true) {
215         continue;
216       }
217
218       if (!route) {
219         // process non-route handlers normally
220         continue;
221       }
222
223       if (layerError) {
224         // routes do not match with a pending error
225         match = false;
226         continue;
227       }
228
229       var method = req.method;
230       var has_method = route._handles_method(method);
231
232       // build up automatic options response
233       if (!has_method && method === 'OPTIONS') {
234         appendMethods(options, route._options());
235       }
236
237       // don't even bother matching route
238       if (!has_method && method !== 'HEAD') {
239         match = false;
240         continue;
241       }
242     }
243
244     // no match
245     if (match !== true) {
246       return done(layerError);
247     }
248
249     // store route for dispatch on change
250     if (route) {
251       req.route = route;
252     }
253
254     // Capture one-time layer values
255     req.params = self.mergeParams
256       ? mergeParams(layer.params, parentParams)
257       : layer.params;
258     var layerPath = layer.path;
259
260     // this should be done for the layer
261     self.process_params(layer, paramcalled, req, res, function (err) {
262       if (err) {
263         return next(layerError || err);
264       }
265
266       if (route) {
267         return layer.handle_request(req, res, next);
268       }
269
270       trim_prefix(layer, layerError, layerPath, path);
271     });
272   }
273
274   function trim_prefix(layer, layerError, layerPath, path) {
275     var c = path[layerPath.length];
276     if (c && '/' !== c && '.' !== c) return next(layerError);
277
278      // Trim off the part of the url that matches the route
279      // middleware (.use stuff) needs to have the path stripped
280     if (layerPath.length !== 0) {
281       debug('trim prefix (%s) from url %s', layerPath, req.url);
282       removed = layerPath;
283       req.url = protohost + req.url.substr(protohost.length + removed.length);
284
285       // Ensure leading slash
286       if (!fqdn && req.url[0] !== '/') {
287         req.url = '/' + req.url;
288         slashAdded = true;
289       }
290
291       // Setup base URL (no trailing slash)
292       req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
293         ? removed.substring(0, removed.length - 1)
294         : removed);
295     }
296
297     debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
298
299     if (layerError) {
300       layer.handle_error(layerError, req, res, next);
301     } else {
302       layer.handle_request(req, res, next);
303     }
304   }
305 };
306
307 /**
308  * Process any parameters for the layer.
309  *
310  * @api private
311  */
312
313 proto.process_params = function(layer, called, req, res, done) {
314   var params = this.params;
315
316   // captured parameters from the layer, keys and values
317   var keys = layer.keys;
318
319   // fast track
320   if (!keys || keys.length === 0) {
321     return done();
322   }
323
324   var i = 0;
325   var name;
326   var paramIndex = 0;
327   var key;
328   var paramVal;
329   var paramCallbacks;
330   var paramCalled;
331
332   // process params in order
333   // param callbacks can be async
334   function param(err) {
335     if (err) {
336       return done(err);
337     }
338
339     if (i >= keys.length ) {
340       return done();
341     }
342
343     paramIndex = 0;
344     key = keys[i++];
345
346     if (!key) {
347       return done();
348     }
349
350     name = key.name;
351     paramVal = req.params[name];
352     paramCallbacks = params[name];
353     paramCalled = called[name];
354
355     if (paramVal === undefined || !paramCallbacks) {
356       return param();
357     }
358
359     // param previously called with same value or error occurred
360     if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
361       // restore value
362       req.params[name] = paramCalled.value;
363
364       // next param
365       return param(paramCalled.error);
366     }
367
368     called[name] = paramCalled = {
369       error: null,
370       match: paramVal,
371       value: paramVal
372     };
373
374     paramCallback();
375   }
376
377   // single param callbacks
378   function paramCallback(err) {
379     var fn = paramCallbacks[paramIndex++];
380
381     // store updated value
382     paramCalled.value = req.params[key.name];
383
384     if (err) {
385       // store error
386       paramCalled.error = err;
387       param(err);
388       return;
389     }
390
391     if (!fn) return param();
392
393     try {
394       fn(req, res, paramCallback, paramVal, key.name);
395     } catch (e) {
396       paramCallback(e);
397     }
398   }
399
400   param();
401 };
402
403 /**
404  * Use the given middleware function, with optional path, defaulting to "/".
405  *
406  * Use (like `.all`) will run for any http METHOD, but it will not add
407  * handlers for those methods so OPTIONS requests will not consider `.use`
408  * functions even if they could respond.
409  *
410  * The other difference is that _route_ path is stripped and not visible
411  * to the handler function. The main effect of this feature is that mounted
412  * handlers can operate without any code changes regardless of the "prefix"
413  * pathname.
414  *
415  * @api public
416  */
417
418 proto.use = function use(fn) {
419   var offset = 0;
420   var path = '/';
421
422   // default path to '/'
423   // disambiguate router.use([fn])
424   if (typeof fn !== 'function') {
425     var arg = fn;
426
427     while (Array.isArray(arg) && arg.length !== 0) {
428       arg = arg[0];
429     }
430
431     // first arg is the path
432     if (typeof arg !== 'function') {
433       offset = 1;
434       path = fn;
435     }
436   }
437
438   var callbacks = utils.flatten(slice.call(arguments, offset));
439
440   if (callbacks.length === 0) {
441     throw new TypeError('Router.use() requires middleware functions');
442   }
443
444   callbacks.forEach(function (fn) {
445     if (typeof fn !== 'function') {
446       throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
447     }
448
449     // add the middleware
450     debug('use %s %s', path, fn.name || '<anonymous>');
451
452     var layer = new Layer(path, {
453       sensitive: this.caseSensitive,
454       strict: false,
455       end: false
456     }, fn);
457
458     layer.route = undefined;
459
460     this.stack.push(layer);
461   }, this);
462
463   return this;
464 };
465
466 /**
467  * Create a new Route for the given path.
468  *
469  * Each route contains a separate middleware stack and VERB handlers.
470  *
471  * See the Route api documentation for details on adding handlers
472  * and middleware to routes.
473  *
474  * @param {String} path
475  * @return {Route}
476  * @api public
477  */
478
479 proto.route = function(path){
480   var route = new Route(path);
481
482   var layer = new Layer(path, {
483     sensitive: this.caseSensitive,
484     strict: this.strict,
485     end: true
486   }, route.dispatch.bind(route));
487
488   layer.route = route;
489
490   this.stack.push(layer);
491   return route;
492 };
493
494 // create Router#VERB functions
495 methods.concat('all').forEach(function(method){
496   proto[method] = function(path){
497     var route = this.route(path)
498     route[method].apply(route, slice.call(arguments, 1));
499     return this;
500   };
501 });
502
503 // append methods to a list of methods
504 function appendMethods(list, addition) {
505   for (var i = 0; i < addition.length; i++) {
506     var method = addition[i];
507     if (list.indexOf(method) === -1) {
508       list.push(method);
509     }
510   }
511 }
512
513 // get pathname of request
514 function getPathname(req) {
515   try {
516     return parseUrl(req).pathname;
517   } catch (err) {
518     return undefined;
519   }
520 }
521
522 // get type for error message
523 function gettype(obj) {
524   var type = typeof obj;
525
526   if (type !== 'object') {
527     return type;
528   }
529
530   // inspect [[Class]] for objects
531   return toString.call(obj)
532     .replace(objectRegExp, '$1');
533 }
534
535 /**
536  * Match path to a layer.
537  *
538  * @param {Layer} layer
539  * @param {string} path
540  * @private
541  */
542
543 function matchLayer(layer, path) {
544   try {
545     return layer.match(path);
546   } catch (err) {
547     return err;
548   }
549 }
550
551 // merge params with parent params
552 function mergeParams(params, parent) {
553   if (typeof parent !== 'object' || !parent) {
554     return params;
555   }
556
557   // make copy of parent for base
558   var obj = mixin({}, parent);
559
560   // simple non-numeric merging
561   if (!(0 in params) || !(0 in parent)) {
562     return mixin(obj, params);
563   }
564
565   var i = 0;
566   var o = 0;
567
568   // determine numeric gaps
569   while (i === o || o in parent) {
570     if (i in params) i++;
571     if (o in parent) o++;
572   }
573
574   // offset numeric indices in params before merge
575   for (i--; i >= 0; i--) {
576     params[i + o] = params[i];
577
578     // create holes for the merge when necessary
579     if (i < o) {
580       delete params[i];
581     }
582   }
583
584   return mixin(parent, params);
585 }
586
587 // restore obj props after function
588 function restore(fn, obj) {
589   var props = new Array(arguments.length - 2);
590   var vals = new Array(arguments.length - 2);
591
592   for (var i = 0; i < props.length; i++) {
593     props[i] = arguments[i + 2];
594     vals[i] = obj[props[i]];
595   }
596
597   return function(err){
598     // restore vals
599     for (var i = 0; i < props.length; i++) {
600       obj[props[i]] = vals[i];
601     }
602
603     return fn.apply(this, arguments);
604   };
605 }
606
607 // send an OPTIONS response
608 function sendOptionsResponse(res, options, next) {
609   try {
610     var body = options.join(',');
611     res.set('Allow', body);
612     res.send(body);
613   } catch (err) {
614     next(err);
615   }
616 }
617
618 // wrap a function
619 function wrap(old, fn) {
620   return function proxy() {
621     var args = new Array(arguments.length + 1);
622
623     args[0] = old;
624     for (var i = 0, len = arguments.length; i < len; i++) {
625       args[i + 1] = arguments[i];
626     }
627
628     fn.apply(this, args);
629   };
630 }