You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
3.2 KiB

  1. /*!
  2. * express
  3. * Copyright(c) 2009-2013 TJ Holowaychuk
  4. * Copyright(c) 2013 Roman Shtylman
  5. * Copyright(c) 2014-2015 Douglas Christopher Wilson
  6. * MIT Licensed
  7. */
  8. 'use strict';
  9. /**
  10. * Module dependencies.
  11. * @private
  12. */
  13. var pathRegexp = require('path-to-regexp');
  14. var debug = require('debug')('express:router:layer');
  15. /**
  16. * Module variables.
  17. * @private
  18. */
  19. var hasOwnProperty = Object.prototype.hasOwnProperty;
  20. /**
  21. * Module exports.
  22. * @public
  23. */
  24. module.exports = Layer;
  25. function Layer(path, options, fn) {
  26. if (!(this instanceof Layer)) {
  27. return new Layer(path, options, fn);
  28. }
  29. debug('new %o', path)
  30. var opts = options || {};
  31. this.handle = fn;
  32. this.name = fn.name || '<anonymous>';
  33. this.params = undefined;
  34. this.path = undefined;
  35. this.regexp = pathRegexp(path, this.keys = [], opts);
  36. // set fast path flags
  37. this.regexp.fast_star = path === '*'
  38. this.regexp.fast_slash = path === '/' && opts.end === false
  39. }
  40. /**
  41. * Handle the error for the layer.
  42. *
  43. * @param {Error} error
  44. * @param {Request} req
  45. * @param {Response} res
  46. * @param {function} next
  47. * @api private
  48. */
  49. Layer.prototype.handle_error = function handle_error(error, req, res, next) {
  50. var fn = this.handle;
  51. if (fn.length !== 4) {
  52. // not a standard error handler
  53. return next(error);
  54. }
  55. try {
  56. fn(error, req, res, next);
  57. } catch (err) {
  58. next(err);
  59. }
  60. };
  61. /**
  62. * Handle the request for the layer.
  63. *
  64. * @param {Request} req
  65. * @param {Response} res
  66. * @param {function} next
  67. * @api private
  68. */
  69. Layer.prototype.handle_request = function handle(req, res, next) {
  70. var fn = this.handle;
  71. if (fn.length > 3) {
  72. // not a standard request handler
  73. return next();
  74. }
  75. try {
  76. fn(req, res, next);
  77. } catch (err) {
  78. next(err);
  79. }
  80. };
  81. /**
  82. * Check if this route matches `path`, if so
  83. * populate `.params`.
  84. *
  85. * @param {String} path
  86. * @return {Boolean}
  87. * @api private
  88. */
  89. Layer.prototype.match = function match(path) {
  90. var match
  91. if (path != null) {
  92. // fast path non-ending match for / (any path matches)
  93. if (this.regexp.fast_slash) {
  94. this.params = {}
  95. this.path = ''
  96. return true
  97. }
  98. // fast path for * (everything matched in a param)
  99. if (this.regexp.fast_star) {
  100. this.params = {'0': decode_param(path)}
  101. this.path = path
  102. return true
  103. }
  104. // match the path
  105. match = this.regexp.exec(path)
  106. }
  107. if (!match) {
  108. this.params = undefined;
  109. this.path = undefined;
  110. return false;
  111. }
  112. // store values
  113. this.params = {};
  114. this.path = match[0]
  115. var keys = this.keys;
  116. var params = this.params;
  117. for (var i = 1; i < match.length; i++) {
  118. var key = keys[i - 1];
  119. var prop = key.name;
  120. var val = decode_param(match[i])
  121. if (val !== undefined || !(hasOwnProperty.call(params, prop))) {
  122. params[prop] = val;
  123. }
  124. }
  125. return true;
  126. };
  127. /**
  128. * Decode param value.
  129. *
  130. * @param {string} val
  131. * @return {string}
  132. * @private
  133. */
  134. function decode_param(val) {
  135. if (typeof val !== 'string' || val.length === 0) {
  136. return val;
  137. }
  138. try {
  139. return decodeURIComponent(val);
  140. } catch (err) {
  141. if (err instanceof URIError) {
  142. err.message = 'Failed to decode param \'' + val + '\'';
  143. err.status = err.statusCode = 400;
  144. }
  145. throw err;
  146. }
  147. }