fastify 4.27.0 → 5.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/.markdownlint-cli2.yaml +1 -1
  2. package/.tap/processinfo/09002e93-10ad-430c-bc86-c0576928b0ed.json +241 -0
  3. package/.tap/processinfo/ee66c5ab-635d-48b5-8be6-3dc3ceea5bfc.json +268 -0
  4. package/.tap/test-results/test/404s.test.js.tap +623 -0
  5. package/.tap/test-results/test/500s.test.js.tap +64 -0
  6. package/.tap/test-results/test/allowUnsafeRegex.test.js.tap +36 -0
  7. package/.tap/test-results/test/als.test.js.tap +15 -0
  8. package/.tap/test-results/test/async-await.test.js.tap +184 -0
  9. package/.tap/test-results/test/async-dispose.test.js.tap +8 -0
  10. package/.tap/test-results/test/async_hooks.test.js.tap +10 -0
  11. package/.tap/test-results/test/bodyLimit.test.js.tap +48 -0
  12. package/.tap/test-results/test/buffer.test.js.tap +20 -0
  13. package/.tap/test-results/test/build/error-serializer.test.js.tap +12 -0
  14. package/.tap/test-results/test/build/version.test.js.tap +7 -0
  15. package/.tap/test-results/test/case-insensitive.test.js.tap +36 -0
  16. package/.tap/test-results/test/chainable.test.js.tap +17 -0
  17. package/.tap/test-results/test/check.test.js.tap +10 -0
  18. package/.tap/test-results/test/childLoggerFactory.test.js.tap +23 -0
  19. package/.tap/test-results/test/client-timeout.test.js.tap +7 -0
  20. package/.tap/test-results/test/close-pipelining.test.js.tap +15 -0
  21. package/.tap/test-results/test/close.test.js.tap +172 -0
  22. package/.tap/test-results/test/connectionTimeout.test.js.tap +12 -0
  23. package/.tap/test-results/test/constrained-routes.test.js.tap +173 -0
  24. package/.tap/test-results/test/content-length.test.js.tap +46 -0
  25. package/.tap/test-results/test/content-parser.test.js.tap +266 -0
  26. package/.tap/test-results/test/content-type.test.js.tap +14 -0
  27. package/.tap/test-results/test/context-config.test.js.tap +41 -0
  28. package/.tap/test-results/test/copy.test.js.tap +14 -0
  29. package/.tap/test-results/test/custom-http-server.test.js.tap +30 -0
  30. package/.tap/test-results/test/custom-parser-async.test.js.tap +21 -0
  31. package/.tap/test-results/test/custom-parser.0.test.js.tap +199 -0
  32. package/.tap/test-results/test/custom-parser.1.test.js.tap +90 -0
  33. package/.tap/test-results/test/custom-parser.2.test.js.tap +22 -0
  34. package/.tap/test-results/test/custom-parser.3.test.js.tap +53 -0
  35. package/.tap/test-results/test/custom-parser.4.test.js.tap +45 -0
  36. package/.tap/test-results/test/custom-parser.5.test.js.tap +41 -0
  37. package/.tap/test-results/test/custom-querystring-parser.test.js.tap +46 -0
  38. package/.tap/test-results/test/decorator.test.js.tap +465 -0
  39. package/.tap/test-results/test/delete.test.js.tap +110 -0
  40. package/.tap/test-results/test/diagnostics-channel/404.test.js.tap +15 -0
  41. package/.tap/test-results/test/diagnostics-channel/async-delay-request.test.js.tap +25 -0
  42. package/.tap/test-results/test/diagnostics-channel/async-request.test.js.tap +24 -0
  43. package/.tap/test-results/test/diagnostics-channel/error-before-handler.test.js.tap +9 -0
  44. package/.tap/test-results/test/diagnostics-channel/error-request.test.js.tap +20 -0
  45. package/.tap/test-results/test/diagnostics-channel/error-status.test.js.tap +10 -0
  46. package/.tap/test-results/test/diagnostics-channel/init.test.js.tap +14 -0
  47. package/.tap/test-results/test/diagnostics-channel/sync-delay-request.test.js.tap +16 -0
  48. package/.tap/test-results/test/diagnostics-channel/sync-request-reply.test.js.tap +16 -0
  49. package/.tap/test-results/test/diagnostics-channel/sync-request.test.js.tap +19 -0
  50. package/.tap/test-results/test/encapsulated-child-logger-factory.test.js.tap +18 -0
  51. package/.tap/test-results/test/encapsulated-error-handler.test.js.tap +243 -0
  52. package/.tap/test-results/test/esm/errorCodes.test.mjs.tap +9 -0
  53. package/.tap/test-results/test/esm/esm.test.mjs.tap +8 -0
  54. package/.tap/test-results/test/esm/index.test.js.tap +8 -0
  55. package/.tap/test-results/test/fastify-instance.test.js.tap +114 -0
  56. package/.tap/test-results/test/findRoute.test.js.tap +37 -0
  57. package/.tap/test-results/test/fluent-schema.test.js.tap +36 -0
  58. package/.tap/test-results/test/genReqId.test.js.tap +106 -0
  59. package/.tap/test-results/test/get.test.js.tap +151 -0
  60. package/.tap/test-results/test/handler-context.test.js.tap +19 -0
  61. package/.tap/test-results/test/has-route.test.js.tap +30 -0
  62. package/.tap/test-results/test/head.test.js.tap +130 -0
  63. package/.tap/test-results/test/header-overflow.test.js.tap +16 -0
  64. package/.tap/test-results/test/hooks-async.test.js.tap +286 -0
  65. package/.tap/test-results/test/hooks.on-listen.test.js.tap +311 -0
  66. package/.tap/test-results/test/hooks.on-ready.test.js.tap +151 -0
  67. package/.tap/test-results/test/hooks.test.js.tap +966 -0
  68. package/.tap/test-results/test/http2/closing.test.js.tap +35 -0
  69. package/.tap/test-results/test/http2/constraint.test.js.tap +32 -0
  70. package/.tap/test-results/test/http2/head.test.js.tap +9 -0
  71. package/.tap/test-results/test/http2/missing-http2-module.test.js.tap +8 -0
  72. package/.tap/test-results/test/http2/plain.test.js.tap +22 -0
  73. package/.tap/test-results/test/http2/secure-with-fallback.test.js.tap +40 -0
  74. package/.tap/test-results/test/http2/secure.test.js.tap +27 -0
  75. package/.tap/test-results/test/http2/unknown-http-method.test.js.tap +9 -0
  76. package/.tap/test-results/test/https/custom-https-server.test.js.tap +10 -0
  77. package/.tap/test-results/test/https/https.test.js.tap +45 -0
  78. package/.tap/test-results/test/imports.test.js.tap +14 -0
  79. package/.tap/test-results/test/inject.test.js.tap +165 -0
  80. package/.tap/test-results/test/internals/all.test.js.tap +42 -0
  81. package/.tap/test-results/test/internals/contentTypeParser.test.js.tap +14 -0
  82. package/.tap/test-results/test/internals/context.test.js.tap +14 -0
  83. package/.tap/test-results/test/internals/decorator.test.js.tap +51 -0
  84. package/.tap/test-results/test/internals/errors.test.js.tap +1212 -0
  85. package/.tap/test-results/test/internals/handleRequest.test.js.tap +69 -0
  86. package/.tap/test-results/test/internals/hookRunner.test.js.tap +143 -0
  87. package/.tap/test-results/test/internals/hooks.test.js.tap +45 -0
  88. package/.tap/test-results/test/internals/initialConfig.test.js.tap +125 -0
  89. package/.tap/test-results/test/internals/logger.test.js.tap +71 -0
  90. package/.tap/test-results/test/internals/plugin.test.js.tap +48 -0
  91. package/.tap/test-results/test/internals/reply-serialize.test.js.tap +166 -0
  92. package/.tap/test-results/test/internals/reply.test.js.tap +688 -0
  93. package/.tap/test-results/test/internals/reqIdGenFactory.test.js.tap +74 -0
  94. package/.tap/test-results/test/internals/request-validate.test.js.tap +384 -0
  95. package/.tap/test-results/test/internals/request.test.js.tap +163 -0
  96. package/.tap/test-results/test/internals/server.test.js.tap +30 -0
  97. package/.tap/test-results/test/internals/validation.test.js.tap +121 -0
  98. package/.tap/test-results/test/keepAliveTimeout.test.js.tap +12 -0
  99. package/.tap/test-results/test/listen.1.test.js.tap +31 -0
  100. package/.tap/test-results/test/listen.2.test.js.tap +46 -0
  101. package/.tap/test-results/test/listen.3.test.js.tap +25 -0
  102. package/.tap/test-results/test/listen.4.test.js.tap +51 -0
  103. package/.tap/test-results/test/lock.test.js.tap +29 -0
  104. package/.tap/test-results/test/logger/instantiation.test.js.tap +92 -0
  105. package/.tap/test-results/test/logger/logging.test.js.tap +117 -0
  106. package/.tap/test-results/test/logger/options.test.js.tap +165 -0
  107. package/.tap/test-results/test/logger/request.test.js.tap +82 -0
  108. package/.tap/test-results/test/logger/response.test.js.tap +38 -0
  109. package/.tap/test-results/test/maxRequestsPerSocket.test.js.tap +44 -0
  110. package/.tap/test-results/test/method-missing.test.js.tap +8 -0
  111. package/.tap/test-results/test/middleware.test.js.tap +17 -0
  112. package/.tap/test-results/test/mkcalendar.test.js.tap +43 -0
  113. package/.tap/test-results/test/mkcol.test.js.tap +14 -0
  114. package/.tap/test-results/test/move.test.js.tap +15 -0
  115. package/.tap/test-results/test/noop-set.test.js.tap +8 -0
  116. package/.tap/test-results/test/nullable-validation.test.js.tap +36 -0
  117. package/.tap/test-results/test/options.error-handler.test.js.tap +186 -0
  118. package/.tap/test-results/test/options.test.js.tap +174 -0
  119. package/.tap/test-results/test/output-validation.test.js.tap +66 -0
  120. package/.tap/test-results/test/patch.error-handler.test.js.tap +206 -0
  121. package/.tap/test-results/test/patch.test.js.tap +182 -0
  122. package/.tap/test-results/test/plugin.1.test.js.tap +78 -0
  123. package/.tap/test-results/test/plugin.2.test.js.tap +102 -0
  124. package/.tap/test-results/test/plugin.3.test.js.tap +58 -0
  125. package/.tap/test-results/test/plugin.4.test.js.tap +164 -0
  126. package/.tap/test-results/test/post-empty-body.test.js.tap +8 -0
  127. package/.tap/test-results/test/pretty-print.test.js.tap +82 -0
  128. package/.tap/test-results/test/promises.test.js.tap +46 -0
  129. package/.tap/test-results/test/propfind.test.js.tap +43 -0
  130. package/.tap/test-results/test/proppatch.test.js.tap +29 -0
  131. package/.tap/test-results/test/proto-poisoning.test.js.tap +47 -0
  132. package/.tap/test-results/test/put.error-handler.test.js.tap +206 -0
  133. package/.tap/test-results/test/put.test.js.tap +182 -0
  134. package/.tap/test-results/test/register.test.js.tap +61 -0
  135. package/.tap/test-results/test/reply-code.test.js.tap +40 -0
  136. package/.tap/test-results/test/reply-earlyHints.test.js.tap +22 -0
  137. package/.tap/test-results/test/reply-error.test.js.tap +643 -0
  138. package/.tap/test-results/test/reply-trailers.test.js.tap +176 -0
  139. package/.tap/test-results/test/report.test.js.tap +43 -0
  140. package/.tap/test-results/test/request-error.test.js.tap +98 -0
  141. package/.tap/test-results/test/request-id.test.js.tap +38 -0
  142. package/.tap/test-results/test/request.deprecated.test.js.tap +13 -0
  143. package/.tap/test-results/test/requestTimeout.test.js.tap +21 -0
  144. package/.tap/test-results/test/route-hooks.test.js.tap +498 -0
  145. package/.tap/test-results/test/route-prefix.test.js.tap +195 -0
  146. package/.tap/test-results/test/route-shorthand.test.js.tap +190 -0
  147. package/.tap/test-results/test/route.1.test.js.tap +93 -0
  148. package/.tap/test-results/test/route.2.test.js.tap +28 -0
  149. package/.tap/test-results/test/route.3.test.js.tap +39 -0
  150. package/.tap/test-results/test/route.4.test.js.tap +32 -0
  151. package/.tap/test-results/test/route.5.test.js.tap +54 -0
  152. package/.tap/test-results/test/route.6.test.js.tap +81 -0
  153. package/.tap/test-results/test/route.7.test.js.tap +93 -0
  154. package/.tap/test-results/test/route.8.test.js.tap +38 -0
  155. package/.tap/test-results/test/router-options.test.js.tap +104 -0
  156. package/.tap/test-results/test/same-shape.test.js.tap +22 -0
  157. package/.tap/test-results/test/schema-examples.test.js.tap +85 -0
  158. package/.tap/test-results/test/schema-feature.test.js.tap +445 -0
  159. package/.tap/test-results/test/schema-serialization.test.js.tap +194 -0
  160. package/.tap/test-results/test/schema-special-usage.test.js.tap +186 -0
  161. package/.tap/test-results/test/schema-validation.test.js.tap +199 -0
  162. package/.tap/test-results/test/search.test.js.tap +77 -0
  163. package/.tap/test-results/test/serialize-response.test.js.tap +26 -0
  164. package/.tap/test-results/test/server.test.js.tap +65 -0
  165. package/.tap/test-results/test/set-error-handler.test.js.tap +7 -0
  166. package/.tap/test-results/test/skip-reply-send.test.js.tap +272 -0
  167. package/.tap/test-results/test/stream.1.test.js.tap +36 -0
  168. package/.tap/test-results/test/stream.2.test.js.tap +20 -0
  169. package/.tap/test-results/test/stream.3.test.js.tap +34 -0
  170. package/.tap/test-results/test/stream.4.test.js.tap +40 -0
  171. package/.tap/test-results/test/stream.5.test.js.tap +37 -0
  172. package/.tap/test-results/test/sync-routes.test.js.tap +19 -0
  173. package/.tap/test-results/test/throw.test.js.tap +116 -0
  174. package/.tap/test-results/test/trace.test.js.tap +7 -0
  175. package/.tap/test-results/test/trust-proxy.test.js.tap +109 -0
  176. package/.tap/test-results/test/type-provider.test.js.tap +12 -0
  177. package/.tap/test-results/test/unlock.test.js.tap +14 -0
  178. package/.tap/test-results/test/upgrade.test.js.tap +8 -0
  179. package/.tap/test-results/test/url-rewriting.test.js.tap +39 -0
  180. package/.tap/test-results/test/useSemicolonDelimiter.test.js.tap +33 -0
  181. package/.tap/test-results/test/validation-error-handling.test.js.tap +180 -0
  182. package/.tap/test-results/test/versioned-routes.test.js.tap +151 -0
  183. package/.tap/test-results/test/web-api.test.js.tap +51 -0
  184. package/.tap/test-results/test/wrapThenable.test.js.tap +11 -0
  185. package/.taprc +4 -8
  186. package/README.md +3 -6
  187. package/build/build-error-serializer.js +4 -1
  188. package/build/build-validation.js +5 -4
  189. package/docs/Guides/Database.md +1 -1
  190. package/docs/Guides/Delay-Accepting-Requests.md +3 -3
  191. package/docs/Guides/Ecosystem.md +2 -0
  192. package/docs/Guides/Migration-Guide-V5.md +20 -0
  193. package/docs/Guides/Write-Type-Provider.md +4 -2
  194. package/docs/Reference/ContentTypeParser.md +30 -1
  195. package/docs/Reference/Decorators.md +42 -16
  196. package/docs/Reference/Errors.md +10 -2
  197. package/docs/Reference/Hooks.md +48 -14
  198. package/docs/Reference/Logging.md +5 -5
  199. package/docs/Reference/Reply.md +29 -24
  200. package/docs/Reference/Request.md +5 -1
  201. package/docs/Reference/Routes.md +24 -28
  202. package/docs/Reference/Server.md +14 -53
  203. package/docs/Reference/Type-Providers.md +21 -26
  204. package/docs/Reference/TypeScript.md +46 -29
  205. package/docs/Reference/Warnings.md +2 -8
  206. package/eslint.config.js +27 -0
  207. package/examples/typescript-server.ts +14 -14
  208. package/fastify.d.ts +15 -14
  209. package/fastify.js +41 -15
  210. package/lib/configValidator.js +94 -76
  211. package/lib/contentTypeParser.js +54 -88
  212. package/lib/decorate.js +3 -7
  213. package/lib/error-serializer.js +77 -19
  214. package/lib/errors.js +31 -6
  215. package/lib/handleRequest.js +70 -39
  216. package/lib/httpMethods.js +34 -18
  217. package/lib/logger.js +24 -6
  218. package/lib/pluginUtils.js +5 -5
  219. package/lib/reply.js +16 -13
  220. package/lib/request.js +37 -19
  221. package/lib/route.js +7 -31
  222. package/lib/server.js +62 -123
  223. package/lib/warnings.js +28 -27
  224. package/lib/wrapThenable.js +46 -22
  225. package/package.json +38 -58
  226. package/test/404s.test.js +8 -12
  227. package/test/async-await.test.js +46 -2
  228. package/test/build/error-serializer.test.js +4 -2
  229. package/test/check.test.js +225 -0
  230. package/test/close-pipelining.test.js +2 -34
  231. package/test/close.test.js +1 -41
  232. package/test/content-parser.test.js +69 -117
  233. package/test/custom-parser.1.test.js +40 -1
  234. package/test/decorator-namespace.test._js_ +31 -0
  235. package/test/decorator.test.js +92 -43
  236. package/test/delete.test.js +21 -1
  237. package/test/diagnostics-channel/404.test.js +57 -0
  238. package/test/diagnostics-channel/async-delay-request.test.js +74 -0
  239. package/test/diagnostics-channel/async-request.test.js +72 -0
  240. package/test/diagnostics-channel/error-before-handler.test.js +36 -0
  241. package/test/diagnostics-channel/error-request.test.js +61 -0
  242. package/test/diagnostics-channel/error-status.test.js +39 -0
  243. package/test/{diagnostics-channel.test.js → diagnostics-channel/init.test.js} +6 -16
  244. package/test/diagnostics-channel/sync-delay-request.test.js +58 -0
  245. package/test/diagnostics-channel/sync-request-reply.test.js +58 -0
  246. package/test/diagnostics-channel/sync-request.test.js +61 -0
  247. package/test/encapsulated-error-handler.test.js +201 -14
  248. package/test/esm/index.test.js +2 -12
  249. package/test/findRoute.test.js +16 -0
  250. package/test/genReqId.test.js +9 -0
  251. package/test/get.test.js +28 -0
  252. package/test/has-route.test.js +18 -2
  253. package/test/helper.js +1 -5
  254. package/test/hooks.test.js +0 -4
  255. package/test/http2/constraint.test.js +22 -1
  256. package/test/http2/plain.test.js +21 -6
  257. package/test/http2/secure.test.js +12 -1
  258. package/test/https/https.test.js +57 -0
  259. package/test/inject.test.js +1 -2
  260. package/test/internals/decorator.test.js +0 -2
  261. package/test/internals/errors.test.js +57 -17
  262. package/test/internals/handleRequest.test.js +5 -1
  263. package/test/internals/initialConfig.test.js +5 -5
  264. package/test/internals/logger.test.js +31 -2
  265. package/test/internals/reply.test.js +38 -80
  266. package/test/internals/request.test.js +13 -11
  267. package/test/listen.1.test.js +5 -15
  268. package/test/listen.5.test.js +88 -0
  269. package/test/logger/instantiation.test.js +8 -8
  270. package/test/logger/logging.test.js +4 -4
  271. package/test/logger/options.test.js +102 -21
  272. package/test/logger/response.test.js +6 -6
  273. package/test/maxRequestsPerSocket.test.js +2 -5
  274. package/test/method-missing.test.js +24 -0
  275. package/test/plugin.1.test.js +2 -4
  276. package/test/plugin.2.test.js +0 -2
  277. package/test/plugin.3.test.js +0 -2
  278. package/test/plugin.4.test.js +92 -56
  279. package/test/register.test.js +2 -4
  280. package/test/reply-earlyHints.test.js +98 -0
  281. package/test/reply-error.test.js +0 -2
  282. package/test/route-hooks.test.js +0 -1
  283. package/test/route-shorthand.test.js +60 -0
  284. package/test/schema-special-usage.test.js +1 -1
  285. package/test/server.test.js +17 -2
  286. package/test/stream.2.test.js +1 -1
  287. package/test/stream.4.test.js +0 -42
  288. package/test/stream.5.test.js +2 -2
  289. package/test/trust-proxy.test.js +33 -27
  290. package/test/types/errors.test-d.ts +0 -2
  291. package/test/types/fastify.test-d.ts +14 -12
  292. package/test/types/hooks.test-d.ts +1 -0
  293. package/test/types/import.ts +1 -0
  294. package/test/types/instance.test-d.ts +10 -51
  295. package/test/types/logger.test-d.ts +43 -6
  296. package/test/types/plugin.test-d.ts +5 -2
  297. package/test/types/register.test-d.ts +2 -2
  298. package/test/types/reply.test-d.ts +13 -12
  299. package/test/types/request.test-d.ts +19 -8
  300. package/test/types/route.test-d.ts +30 -2
  301. package/test/types/schema.test-d.ts +2 -2
  302. package/test/types/serverFactory.test-d.ts +1 -1
  303. package/test/types/type-provider.test-d.ts +60 -13
  304. package/test/types/using.test-d.ts +4 -1
  305. package/test/url-rewriting.test.js +3 -2
  306. package/test/useSemicolonDelimiter.test.js +3 -6
  307. package/test/versioned-routes.test.js +1 -1
  308. package/test/web-api.test.js +0 -6
  309. package/types/content-type-parser.d.ts +3 -3
  310. package/types/context.d.ts +0 -1
  311. package/types/errors.d.ts +1 -0
  312. package/types/hooks.d.ts +6 -6
  313. package/types/instance.d.ts +28 -41
  314. package/types/logger.d.ts +3 -3
  315. package/types/plugin.d.ts +3 -3
  316. package/types/reply.d.ts +13 -14
  317. package/types/request.d.ts +5 -3
  318. package/types/route.d.ts +29 -29
  319. package/types/schema.d.ts +3 -3
  320. package/types/serverFactory.d.ts +2 -2
  321. package/types/type-provider.d.ts +22 -12
  322. package/types/utils.d.ts +18 -18
  323. package/.c8rc.json +0 -8
  324. package/.eslintrc +0 -4
  325. package/test/default-route.test.js +0 -88
  326. package/test/listen.deprecated.test.js +0 -229
  327. package/test/unsupported-httpversion.test.js +0 -31
  328. package/types/.eslintrc.json +0 -48
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { AsyncResource } = require('node:async_hooks')
4
4
  const { FifoMap: Fifo } = require('toad-cache')
5
- const { safeParse: safeParseContentType, defaultContentType } = require('fast-content-type-parse')
6
5
  const secureJson = require('secure-json-parse')
7
6
  const {
8
7
  kDefaultJsonParse,
@@ -27,6 +26,7 @@ const {
27
26
  FST_ERR_CTP_EMPTY_JSON_BODY,
28
27
  FST_ERR_CTP_INSTANCE_ALREADY_STARTED
29
28
  } = require('./errors')
29
+ const { FSTSEC001 } = require('./warnings')
30
30
 
31
31
  function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
32
32
  this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
@@ -34,7 +34,7 @@ function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning)
34
34
  this.customParsers = new Map()
35
35
  this.customParsers.set('application/json', new Parser(true, false, bodyLimit, this[kDefaultJsonParse]))
36
36
  this.customParsers.set('text/plain', new Parser(true, false, bodyLimit, defaultPlainTextParser))
37
- this.parserList = [new ParserListItem('application/json'), new ParserListItem('text/plain')]
37
+ this.parserList = ['application/json', 'text/plain']
38
38
  this.parserRegExpList = []
39
39
  this.cache = new Fifo(100)
40
40
  }
@@ -42,9 +42,16 @@ function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning)
42
42
  ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
43
43
  const contentTypeIsString = typeof contentType === 'string'
44
44
 
45
- if (!contentTypeIsString && !(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
46
- if (contentTypeIsString && contentType.length === 0) throw new FST_ERR_CTP_EMPTY_TYPE()
47
- if (typeof parserFn !== 'function') throw new FST_ERR_CTP_INVALID_HANDLER()
45
+ if (contentTypeIsString) {
46
+ contentType = contentType.trim().toLowerCase()
47
+ if (contentType.length === 0) throw new FST_ERR_CTP_EMPTY_TYPE()
48
+ } else if (!(contentType instanceof RegExp)) {
49
+ throw new FST_ERR_CTP_INVALID_TYPE()
50
+ }
51
+
52
+ if (typeof parserFn !== 'function') {
53
+ throw new FST_ERR_CTP_INVALID_HANDLER()
54
+ }
48
55
 
49
56
  if (this.existingParser(contentType)) {
50
57
  throw new FST_ERR_CTP_ALREADY_PRESENT(contentType)
@@ -63,21 +70,29 @@ ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
63
70
  parserFn
64
71
  )
65
72
 
66
- if (contentTypeIsString && contentType === '*') {
73
+ if (contentType === '*') {
67
74
  this.customParsers.set('', parser)
68
75
  } else {
69
76
  if (contentTypeIsString) {
70
- this.parserList.unshift(new ParserListItem(contentType))
77
+ this.parserList.unshift(contentType)
78
+ this.customParsers.set(contentType, parser)
71
79
  } else {
72
- contentType.isEssence = contentType.source.indexOf(';') === -1
80
+ validateRegExp(contentType)
73
81
  this.parserRegExpList.unshift(contentType)
82
+ this.customParsers.set(contentType.toString(), parser)
74
83
  }
75
- this.customParsers.set(contentType.toString(), parser)
76
84
  }
77
85
  }
78
86
 
79
87
  ContentTypeParser.prototype.hasParser = function (contentType) {
80
- return this.customParsers.has(typeof contentType === 'string' ? contentType : contentType.toString())
88
+ if (typeof contentType === 'string') {
89
+ contentType = contentType.trim().toLowerCase()
90
+ } else {
91
+ if (!(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
92
+ contentType = contentType.toString()
93
+ }
94
+
95
+ return this.customParsers.has(contentType)
81
96
  }
82
97
 
83
98
  ContentTypeParser.prototype.existingParser = function (contentType) {
@@ -92,27 +107,20 @@ ContentTypeParser.prototype.existingParser = function (contentType) {
92
107
  }
93
108
 
94
109
  ContentTypeParser.prototype.getParser = function (contentType) {
95
- if (this.hasParser(contentType)) {
96
- return this.customParsers.get(contentType)
97
- }
98
-
99
- const parser = this.cache.get(contentType)
110
+ let parser = this.customParsers.get(contentType)
100
111
  if (parser !== undefined) return parser
101
112
 
102
- const parsed = safeParseContentType(contentType)
103
-
104
- // dummyContentType always the same object
105
- // we can use === for the comparison and return early
106
- if (parsed === defaultContentType) {
107
- return this.customParsers.get('')
108
- }
113
+ parser = this.cache.get(contentType)
114
+ if (parser !== undefined) return parser
109
115
 
110
116
  // eslint-disable-next-line no-var
111
117
  for (var i = 0; i !== this.parserList.length; ++i) {
112
118
  const parserListItem = this.parserList[i]
113
- if (compareContentType(parsed, parserListItem)) {
114
- const parser = this.customParsers.get(parserListItem.name)
115
- // we set request content-type in cache to reduce parsing of MIME type
119
+ if (
120
+ contentType.slice(0, parserListItem.length) === parserListItem &&
121
+ (contentType.length === parserListItem.length || contentType.charCodeAt(parserListItem.length) === 59 /* `;` */ || contentType.charCodeAt(parserListItem.length) === 32 /* ` ` */)
122
+ ) {
123
+ parser = this.customParsers.get(parserListItem)
116
124
  this.cache.set(contentType, parser)
117
125
  return parser
118
126
  }
@@ -121,9 +129,8 @@ ContentTypeParser.prototype.getParser = function (contentType) {
121
129
  // eslint-disable-next-line no-var
122
130
  for (var j = 0; j !== this.parserRegExpList.length; ++j) {
123
131
  const parserRegExp = this.parserRegExpList[j]
124
- if (compareRegExpContentType(contentType, parsed.type, parserRegExp)) {
125
- const parser = this.customParsers.get(parserRegExp.toString())
126
- // we set request content-type in cache to reduce parsing of MIME type
132
+ if (parserRegExp.test(contentType)) {
133
+ parser = this.customParsers.get(parserRegExp.toString())
127
134
  this.cache.set(contentType, parser)
128
135
  return parser
129
136
  }
@@ -140,13 +147,19 @@ ContentTypeParser.prototype.removeAll = function () {
140
147
  }
141
148
 
142
149
  ContentTypeParser.prototype.remove = function (contentType) {
143
- if (!(typeof contentType === 'string' || contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
150
+ let parsers
144
151
 
145
- const removed = this.customParsers.delete(contentType.toString())
146
-
147
- const parsers = typeof contentType === 'string' ? this.parserList : this.parserRegExpList
152
+ if (typeof contentType === 'string') {
153
+ contentType = contentType.trim().toLowerCase()
154
+ parsers = this.parserList
155
+ } else {
156
+ if (!(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
157
+ contentType = contentType.toString()
158
+ parsers = this.parserRegExpList
159
+ }
148
160
 
149
- const idx = parsers.findIndex(ct => ct.toString() === contentType.toString())
161
+ const removed = this.customParsers.delete(contentType)
162
+ const idx = parsers.findIndex(ct => ct.toString() === contentType)
150
163
 
151
164
  if (idx > -1) {
152
165
  parsers.splice(idx, 1)
@@ -182,7 +195,7 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply
182
195
  } else {
183
196
  const result = parser.fn(request, request[kRequestPayloadStream], done)
184
197
 
185
- if (result && typeof result.then === 'function') {
198
+ if (typeof result?.then === 'function') {
186
199
  result.then(body => done(null, body), done)
187
200
  }
188
201
  }
@@ -205,9 +218,7 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply
205
218
  function rawBody (request, reply, options, parser, done) {
206
219
  const asString = parser.asString
207
220
  const limit = options.limit === null ? parser.bodyLimit : options.limit
208
- const contentLength = request.headers['content-length'] === undefined
209
- ? NaN
210
- : Number(request.headers['content-length'])
221
+ const contentLength = Number(request.headers['content-length'])
211
222
 
212
223
  if (contentLength > limit) {
213
224
  // We must close the connection as the client is going
@@ -373,60 +384,15 @@ function removeAllContentTypeParsers () {
373
384
  this[kContentTypeParser].removeAll()
374
385
  }
375
386
 
376
- function compareContentType (contentType, parserListItem) {
377
- if (parserListItem.isEssence) {
378
- // we do essence check
379
- return contentType.type.indexOf(parserListItem) !== -1
380
- } else {
381
- // when the content-type includes parameters
382
- // we do a full-text search
383
- // reject essence content-type before checking parameters
384
- if (contentType.type.indexOf(parserListItem.type) === -1) return false
385
- for (const key of parserListItem.parameterKeys) {
386
- // reject when missing parameters
387
- if (!(key in contentType.parameters)) return false
388
- // reject when parameters do not match
389
- if (contentType.parameters[key] !== parserListItem.parameters[key]) return false
390
- }
391
- return true
392
- }
393
- }
394
-
395
- function compareRegExpContentType (contentType, essenceMIMEType, regexp) {
396
- if (regexp.isEssence) {
397
- // we do essence check
398
- return regexp.test(essenceMIMEType)
399
- } else {
400
- // when the content-type includes parameters
401
- // we do a full-text match
402
- return regexp.test(contentType)
387
+ function validateRegExp (regexp) {
388
+ // RegExp should either start with ^ or include ;?
389
+ // It can ensure the user is properly detect the essence
390
+ // MIME types.
391
+ if (regexp.source[0] !== '^' && regexp.source.includes(';?') === false) {
392
+ FSTSEC001(regexp.source)
403
393
  }
404
394
  }
405
395
 
406
- function ParserListItem (contentType) {
407
- this.name = contentType
408
- // we pre-calculate all the needed information
409
- // before content-type comparison
410
- const parsed = safeParseContentType(contentType)
411
- this.isEssence = contentType.indexOf(';') === -1
412
- // we should not allow empty string for parser list item
413
- // because it would become a match-all handler
414
- if (this.isEssence === false && parsed.type === '') {
415
- // handle semicolon or empty string
416
- const tmp = contentType.split(';', 1)[0]
417
- this.type = tmp === '' ? contentType : tmp
418
- } else {
419
- this.type = parsed.type
420
- }
421
- this.parameters = parsed.parameters
422
- this.parameterKeys = Object.keys(parsed.parameters)
423
- }
424
-
425
- // used in ContentTypeParser.remove
426
- ParserListItem.prototype.toString = function () {
427
- return this.name
428
- }
429
-
430
396
  module.exports = ContentTypeParser
431
397
  module.exports.helpers = {
432
398
  buildContentTypeParser,
package/lib/decorate.js CHANGED
@@ -1,7 +1,5 @@
1
1
  'use strict'
2
2
 
3
- /* eslint no-prototype-builtins: 0 */
4
-
5
3
  const {
6
4
  kReply,
7
5
  kRequest,
@@ -13,11 +11,10 @@ const {
13
11
  FST_ERR_DEC_ALREADY_PRESENT,
14
12
  FST_ERR_DEC_MISSING_DEPENDENCY,
15
13
  FST_ERR_DEC_AFTER_START,
14
+ FST_ERR_DEC_REFERENCE_TYPE,
16
15
  FST_ERR_DEC_DEPENDENCY_INVALID_TYPE
17
16
  } = require('./errors')
18
17
 
19
- const { FSTDEP006 } = require('./warnings')
20
-
21
18
  function decorate (instance, name, fn, dependencies) {
22
19
  if (Object.prototype.hasOwnProperty.call(instance, name)) {
23
20
  throw new FST_ERR_DEC_ALREADY_PRESENT(name)
@@ -58,7 +55,7 @@ function decorateConstructor (konstructor, name, fn, dependencies) {
58
55
 
59
56
  function checkReferenceType (name, fn) {
60
57
  if (typeof fn === 'object' && fn && !(typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
61
- FSTDEP006(name)
58
+ throw new FST_ERR_DEC_REFERENCE_TYPE(name, typeof fn)
62
59
  }
63
60
  }
64
61
 
@@ -102,8 +99,7 @@ function checkDependencies (instance, name, deps) {
102
99
  throw new FST_ERR_DEC_DEPENDENCY_INVALID_TYPE(name)
103
100
  }
104
101
 
105
- // eslint-disable-next-line no-var
106
- for (var i = 0; i !== deps.length; ++i) {
102
+ for (let i = 0; i !== deps.length; ++i) {
107
103
  if (!checkExistence(instance, deps[i])) {
108
104
  throw new FST_ERR_DEC_MISSING_DEPENDENCY(deps[i])
109
105
  }
@@ -1,11 +1,9 @@
1
1
  // This file is autogenerated by build/build-error-serializer.js, do not edit
2
- /* istanbul ignore file */
2
+ /* c8 ignore start */
3
3
 
4
4
  'use strict'
5
- const { dependencies } = require('fast-json-stringify/lib/standalone')
6
-
7
- const { Serializer, Validator } = dependencies
8
5
 
6
+ const Serializer = require('fast-json-stringify/lib/serializer')
9
7
  const serializerState = {"mode":"standalone"}
10
8
  const serializer = Serializer.restoreFromState(serializerState)
11
9
 
@@ -15,6 +13,18 @@
15
13
  module.exports = function anonymous(validator,serializer
16
14
  ) {
17
15
 
16
+ const JSON_STR_BEGIN_OBJECT = '{'
17
+ const JSON_STR_END_OBJECT = '}'
18
+ const JSON_STR_BEGIN_ARRAY = '['
19
+ const JSON_STR_END_ARRAY = ']'
20
+ const JSON_STR_COMMA = ','
21
+ const JSON_STR_COLONS = ':'
22
+ const JSON_STR_QUOTE = '"'
23
+ const JSON_STR_EMPTY_OBJECT = JSON_STR_BEGIN_OBJECT + JSON_STR_END_OBJECT
24
+ const JSON_STR_EMPTY_ARRAY = JSON_STR_BEGIN_ARRAY + JSON_STR_END_ARRAY
25
+ const JSON_STR_EMPTY_STRING = JSON_STR_QUOTE + JSON_STR_QUOTE
26
+ const JSON_STR_NULL = 'null'
27
+
18
28
 
19
29
 
20
30
  // #
@@ -23,36 +33,83 @@
23
33
  ? input.toJSON()
24
34
  : input
25
35
 
26
- if (obj === null) return '{}'
36
+ if (obj === null) return JSON_STR_EMPTY_OBJECT
27
37
 
28
- let json = '{'
38
+ let value
39
+ let json = JSON_STR_BEGIN_OBJECT
29
40
  let addComma = false
30
41
 
31
- if (obj["statusCode"] !== undefined) {
32
- !addComma && (addComma = true) || (json += ',')
42
+ value = obj["statusCode"]
43
+ if (value !== undefined) {
44
+ !addComma && (addComma = true) || (json += JSON_STR_COMMA)
33
45
  json += "\"statusCode\":"
34
- json += serializer.asNumber(obj["statusCode"])
46
+ json += serializer.asNumber(value)
35
47
  }
36
48
 
37
- if (obj["code"] !== undefined) {
38
- !addComma && (addComma = true) || (json += ',')
49
+ value = obj["code"]
50
+ if (value !== undefined) {
51
+ !addComma && (addComma = true) || (json += JSON_STR_COMMA)
39
52
  json += "\"code\":"
40
- json += serializer.asString(obj["code"])
53
+
54
+ if (typeof value !== 'string') {
55
+ if (value === null) {
56
+ json += JSON_STR_EMPTY_STRING
57
+ } else if (value instanceof Date) {
58
+ json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
59
+ } else if (value instanceof RegExp) {
60
+ json += serializer.asString(value.source)
61
+ } else {
62
+ json += serializer.asString(value.toString())
63
+ }
64
+ } else {
65
+ json += serializer.asString(value)
66
+ }
67
+
41
68
  }
42
69
 
43
- if (obj["error"] !== undefined) {
44
- !addComma && (addComma = true) || (json += ',')
70
+ value = obj["error"]
71
+ if (value !== undefined) {
72
+ !addComma && (addComma = true) || (json += JSON_STR_COMMA)
45
73
  json += "\"error\":"
46
- json += serializer.asString(obj["error"])
74
+
75
+ if (typeof value !== 'string') {
76
+ if (value === null) {
77
+ json += JSON_STR_EMPTY_STRING
78
+ } else if (value instanceof Date) {
79
+ json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
80
+ } else if (value instanceof RegExp) {
81
+ json += serializer.asString(value.source)
82
+ } else {
83
+ json += serializer.asString(value.toString())
84
+ }
85
+ } else {
86
+ json += serializer.asString(value)
87
+ }
88
+
47
89
  }
48
90
 
49
- if (obj["message"] !== undefined) {
50
- !addComma && (addComma = true) || (json += ',')
91
+ value = obj["message"]
92
+ if (value !== undefined) {
93
+ !addComma && (addComma = true) || (json += JSON_STR_COMMA)
51
94
  json += "\"message\":"
52
- json += serializer.asString(obj["message"])
95
+
96
+ if (typeof value !== 'string') {
97
+ if (value === null) {
98
+ json += JSON_STR_EMPTY_STRING
99
+ } else if (value instanceof Date) {
100
+ json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
101
+ } else if (value instanceof RegExp) {
102
+ json += serializer.asString(value.source)
103
+ } else {
104
+ json += serializer.asString(value.toString())
105
+ }
106
+ } else {
107
+ json += serializer.asString(value)
108
+ }
109
+
53
110
  }
54
111
 
55
- return json + '}'
112
+ return json + JSON_STR_END_OBJECT
56
113
 
57
114
  }
58
115
 
@@ -60,3 +117,4 @@ let addComma = false
60
117
  return main
61
118
 
62
119
  }(validator, serializer)
120
+ /* c8 ignore stop */
package/lib/errors.js CHANGED
@@ -151,6 +151,10 @@ const codes = {
151
151
  'FST_ERR_DEC_AFTER_START',
152
152
  "The decorator '%s' has been added after start!"
153
153
  ),
154
+ FST_ERR_DEC_REFERENCE_TYPE: createError(
155
+ 'FST_ERR_DEC_REFERENCE_TYPE',
156
+ "The decorator '%s' of type '%s' is a reference type. Use the { getter, setter } interface instead."
157
+ ),
154
158
 
155
159
  /**
156
160
  * hooks
@@ -209,6 +213,27 @@ const codes = {
209
213
  TypeError
210
214
  ),
211
215
 
216
+ FST_ERR_LOG_INVALID_LOGGER_INSTANCE: createError(
217
+ 'FST_ERR_LOG_INVALID_LOGGER_INSTANCE',
218
+ 'loggerInstance only accepts a logger instance.',
219
+ 500,
220
+ TypeError
221
+ ),
222
+
223
+ FST_ERR_LOG_INVALID_LOGGER_CONFIG: createError(
224
+ 'FST_ERR_LOG_INVALID_LOGGER_CONFIG',
225
+ 'logger options only accepts a configuration object.',
226
+ 500,
227
+ TypeError
228
+ ),
229
+
230
+ FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED: createError(
231
+ 'FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED',
232
+ 'You cannot provide both logger and loggerInstance. Please provide only one.',
233
+ 500,
234
+ TypeError
235
+ ),
236
+
212
237
  /**
213
238
  * reply
214
239
  */
@@ -339,12 +364,6 @@ const codes = {
339
364
  'Unexpected error from async constraint',
340
365
  500
341
366
  ),
342
- FST_ERR_DEFAULT_ROUTE_INVALID_TYPE: createError(
343
- 'FST_ERR_DEFAULT_ROUTE_INVALID_TYPE',
344
- 'The defaultRoute type should be a function',
345
- 500,
346
- TypeError
347
- ),
348
367
  FST_ERR_INVALID_URL: createError(
349
368
  'FST_ERR_INVALID_URL',
350
369
  "URL must be a string. Received '%s'",
@@ -429,6 +448,12 @@ const codes = {
429
448
  'FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE',
430
449
  "The decorator '%s'%s is not present in %s"
431
450
  ),
451
+ FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER: createError(
452
+ 'FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER',
453
+ 'The %s plugin being registered mixes async and callback styles. Async plugin should not mix async and callback style.',
454
+ 500,
455
+ TypeError
456
+ ),
432
457
 
433
458
  /**
434
459
  * Avvio Errors
@@ -1,13 +1,18 @@
1
1
  'use strict'
2
2
 
3
+ const { bodylessMethods, bodyMethods } = require('./httpMethods')
4
+ const diagnostics = require('dc-polyfill')
3
5
  const { validate: validateSchema } = require('./validation')
4
6
  const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
5
7
  const wrapThenable = require('./wrapThenable')
6
8
  const {
7
9
  kReplyIsError,
8
- kRouteContext
10
+ kRouteContext,
11
+ kFourOhFourContext
9
12
  } = require('./symbols')
10
13
 
14
+ const channels = diagnostics.tracingChannel('fastify.request.handler')
15
+
11
16
  function handleRequest (err, request, reply) {
12
17
  if (reply.sent === true) return
13
18
  if (err != null) {
@@ -20,45 +25,37 @@ function handleRequest (err, request, reply) {
20
25
  const headers = request.headers
21
26
  const context = request[kRouteContext]
22
27
 
23
- if (method === 'GET' || method === 'HEAD') {
28
+ if (bodylessMethods.has(method)) {
24
29
  handler(request, reply)
25
30
  return
26
31
  }
27
32
 
28
- const contentType = headers['content-type']
33
+ if (bodyMethods.has(method)) {
34
+ const contentType = headers['content-type']
35
+ const contentLength = headers['content-length']
36
+ const transferEncoding = headers['transfer-encoding']
29
37
 
30
- if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE' || method === 'SEARCH' ||
31
- method === 'PROPFIND' || method === 'PROPPATCH' || method === 'LOCK' || method === 'REPORT' || method === 'MKCALENDAR') {
32
38
  if (contentType === undefined) {
33
39
  if (
34
- headers['transfer-encoding'] === undefined &&
35
- (headers['content-length'] === '0' || headers['content-length'] === undefined)
36
- ) { // Request has no body to parse
40
+ (contentLength === undefined || contentLength === '0') &&
41
+ transferEncoding === undefined
42
+ ) {
43
+ // Request has no body to parse
37
44
  handler(request, reply)
38
45
  } else {
39
46
  context.contentTypeParser.run('', handler, request, reply)
40
47
  }
41
48
  } else {
49
+ if (contentLength === undefined && transferEncoding === undefined && method === 'OPTIONS') {
50
+ // OPTIONS can have a Content-Type header without a body
51
+ handler(request, reply)
52
+ return
53
+ }
42
54
  context.contentTypeParser.run(contentType, handler, request, reply)
43
55
  }
44
56
  return
45
57
  }
46
58
 
47
- if (method === 'OPTIONS' || method === 'DELETE') {
48
- if (
49
- contentType !== undefined &&
50
- (
51
- headers['transfer-encoding'] !== undefined ||
52
- headers['content-length'] !== undefined
53
- )
54
- ) {
55
- context.contentTypeParser.run(contentType, handler, request, reply)
56
- } else {
57
- handler(request, reply)
58
- }
59
- return
60
- }
61
-
62
59
  // Return 404 instead of 405 see https://github.com/fastify/fastify/pull/862 for discussion
63
60
  handler(request, reply)
64
61
  }
@@ -126,28 +123,62 @@ function validationCompleted (request, reply, validationErr) {
126
123
  function preHandlerCallback (err, request, reply) {
127
124
  if (reply.sent) return
128
125
 
129
- if (err != null) {
130
- reply[kReplyIsError] = true
131
- reply.send(err)
132
- return
126
+ const context = request[kRouteContext]
127
+
128
+ if (!channels.hasSubscribers || context[kFourOhFourContext] === null) {
129
+ preHandlerCallbackInner(err, request, reply)
130
+ } else {
131
+ const store = {
132
+ request,
133
+ reply,
134
+ async: false,
135
+ route: {
136
+ url: context.config.url,
137
+ method: context.config.method
138
+ }
139
+ }
140
+ channels.start.runStores(store, preHandlerCallbackInner, undefined, err, request, reply, store)
133
141
  }
142
+ }
134
143
 
135
- let result
144
+ function preHandlerCallbackInner (err, request, reply, store) {
145
+ const context = request[kRouteContext]
136
146
 
137
147
  try {
138
- result = request[kRouteContext].handler(request, reply)
139
- } catch (err) {
140
- reply[kReplyIsError] = true
141
- reply.send(err)
142
- return
143
- }
148
+ if (err != null) {
149
+ reply[kReplyIsError] = true
150
+ reply.send(err)
151
+ if (store) {
152
+ store.error = err
153
+ channels.error.publish(store)
154
+ }
155
+ return
156
+ }
144
157
 
145
- if (result !== undefined) {
146
- if (result !== null && typeof result.then === 'function') {
147
- wrapThenable(result, reply)
148
- } else {
149
- reply.send(result)
158
+ let result
159
+
160
+ try {
161
+ result = context.handler(request, reply)
162
+ } catch (err) {
163
+ if (store) {
164
+ store.error = err
165
+ channels.error.publish(store)
166
+ }
167
+
168
+ reply[kReplyIsError] = true
169
+ reply.send(err)
170
+ return
171
+ }
172
+
173
+ if (result !== undefined) {
174
+ if (result !== null && typeof result.then === 'function') {
175
+ wrapThenable(result, reply, store)
176
+ } else {
177
+ reply.send(result)
178
+ }
150
179
  }
180
+ } finally {
181
+ if (store) channels.end.publish(store)
151
182
  }
152
183
  }
153
184