index.js 15 KB

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