strong-tie/inbound-calls
0
1'use strict'2 3const diagnostics = require('node:diagnostics_channel')4const { validate: validateSchema } = require('./validation')5const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')6const wrapThenable = require('./wrapThenable')7const {8 kReplyIsError,9 kRouteContext,10 kFourOhFourContext,11 kSupportedHTTPMethods12} = require('./symbols')13 14const channels = diagnostics.tracingChannel('fastify.request.handler')15 16function handleRequest (err, request, reply) {17 if (reply.sent === true) return18 if (err != null) {19 reply[kReplyIsError] = true20 reply.send(err)21 return22 }23 24 const method = request.raw.method25 const headers = request.headers26 const context = request[kRouteContext]27 28 if (this[kSupportedHTTPMethods].bodyless.has(method)) {29 handler(request, reply)30 return31 }32 33 if (this[kSupportedHTTPMethods].bodywith.has(method)) {34 const contentType = headers['content-type']35 const contentLength = headers['content-length']36 const transferEncoding = headers['transfer-encoding']37 38 if (contentType === undefined) {39 if (40 (contentLength === undefined || contentLength === '0') &&41 transferEncoding === undefined42 ) {43 // Request has no body to parse44 handler(request, reply)45 } else {46 context.contentTypeParser.run('', handler, request, reply)47 }48 } else {49 if (contentLength === undefined && transferEncoding === undefined && method === 'OPTIONS') {50 // OPTIONS can have a Content-Type header without a body51 handler(request, reply)52 return53 }54 context.contentTypeParser.run(contentType, handler, request, reply)55 }56 return57 }58 59 // Return 404 instead of 405 see https://github.com/fastify/fastify/pull/862 for discussion60 handler(request, reply)61}62 63function handler (request, reply) {64 try {65 if (request[kRouteContext].preValidation !== null) {66 preValidationHookRunner(67 request[kRouteContext].preValidation,68 request,69 reply,70 preValidationCallback71 )72 } else {73 preValidationCallback(null, request, reply)74 }75 } catch (err) {76 preValidationCallback(err, request, reply)77 }78}79 80function preValidationCallback (err, request, reply) {81 if (reply.sent === true) return82 83 if (err != null) {84 reply[kReplyIsError] = true85 reply.send(err)86 return87 }88 89 const validationErr = validateSchema(reply[kRouteContext], request)90 const isAsync = (validationErr && typeof validationErr.then === 'function') || false91 92 if (isAsync) {93 const cb = validationCompleted.bind(null, request, reply)94 validationErr.then(cb, cb)95 } else {96 validationCompleted(request, reply, validationErr)97 }98}99 100function validationCompleted (request, reply, validationErr) {101 if (validationErr) {102 if (reply[kRouteContext].attachValidation === false) {103 reply.send(validationErr)104 return105 }106 107 reply.request.validationError = validationErr108 }109 110 // preHandler hook111 if (request[kRouteContext].preHandler !== null) {112 preHandlerHookRunner(113 request[kRouteContext].preHandler,114 request,115 reply,116 preHandlerCallback117 )118 } else {119 preHandlerCallback(null, request, reply)120 }121}122 123function preHandlerCallback (err, request, reply) {124 if (reply.sent) return125 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.method138 }139 }140 channels.start.runStores(store, preHandlerCallbackInner, undefined, err, request, reply, store)141 }142}143 144function preHandlerCallbackInner (err, request, reply, store) {145 const context = request[kRouteContext]146 147 try {148 if (err != null) {149 reply[kReplyIsError] = true150 reply.send(err)151 if (store) {152 store.error = err153 channels.error.publish(store)154 }155 return156 }157 158 let result159 160 try {161 result = context.handler(request, reply)162 } catch (err) {163 if (store) {164 store.error = err165 channels.error.publish(store)166 }167 168 reply[kReplyIsError] = true169 reply.send(err)170 return171 }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 }179 }180 } finally {181 if (store) channels.end.publish(store)182 }183}184 185module.exports = handleRequest186module.exports[Symbol.for('internals')] = { handler, preHandlerCallback }187 