index.js 15 KB

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