/
home
/
infinitibizsol
/
testingcrm.infinitibizsol.com
/
node_modules
/
fastify
/
lib
/
File Upload :
llllll
Current File: /home/infinitibizsol/testingcrm.infinitibizsol.com/node_modules/fastify/lib/reply.js
'use strict' const eos = require('stream').finished const statusCodes = require('http').STATUS_CODES const flatstr = require('flatstr') const FJS = require('fast-json-stringify') const { kSchemaResponse, kFourOhFourContext, kReplyErrorHandlerCalled, kReplySent, kReplySentOverwritten, kReplyStartTime, kReplyEndTime, kReplySerializer, kReplySerializerDefault, kReplyIsError, kReplyHeaders, kReplyTrailers, kReplyHasStatusCode, kReplyIsRunningOnErrorHook, kDisableRequestLogging } = require('./symbols.js') const { hookRunner, hookIterator, onSendHookRunner } = require('./hooks') const internals = require('./handleRequest')[Symbol.for('internals')] const loggerUtils = require('./logger') const now = loggerUtils.now const wrapThenable = require('./wrapThenable') const serializeError = FJS({ type: 'object', properties: { statusCode: { type: 'number' }, code: { type: 'string' }, error: { type: 'string' }, message: { type: 'string' } } }) const CONTENT_TYPE = { JSON: 'application/json; charset=utf-8', PLAIN: 'text/plain; charset=utf-8', OCTET: 'application/octet-stream' } const { FST_ERR_REP_INVALID_PAYLOAD_TYPE, FST_ERR_REP_ALREADY_SENT, FST_ERR_REP_SENT_VALUE, FST_ERR_SEND_INSIDE_ONERR, FST_ERR_BAD_STATUS_CODE, FST_ERR_BAD_TRAILER_NAME, FST_ERR_BAD_TRAILER_VALUE } = require('./errors') const warning = require('./warnings') function Reply (res, request, log) { this.raw = res this[kReplySent] = false this[kReplySerializer] = null this[kReplyErrorHandlerCalled] = false this[kReplyIsError] = false this[kReplyIsRunningOnErrorHook] = false this.request = request this[kReplyHeaders] = {} this[kReplyTrailers] = null this[kReplyHasStatusCode] = false this[kReplyStartTime] = undefined this.log = log } Reply.props = [] Object.defineProperties(Reply.prototype, { context: { get () { return this.request.context } }, res: { get () { warning.emit('FSTDEP002') return this.raw } }, sent: { enumerable: true, get () { return this[kReplySent] }, set (value) { if (value !== true) { throw new FST_ERR_REP_SENT_VALUE() } if (this[kReplySent]) { throw new FST_ERR_REP_ALREADY_SENT() } this[kReplySentOverwritten] = true this[kReplySent] = true } }, statusCode: { get () { return this.raw.statusCode }, set (value) { this.code(value) } }, server: { value: null, writable: true } }) Reply.prototype.hijack = function () { this[kReplySent] = true return this } Reply.prototype.send = function (payload) { if (this[kReplyIsRunningOnErrorHook] === true) { throw new FST_ERR_SEND_INSIDE_ONERR() } if (this[kReplySent]) { this.log.warn({ err: new FST_ERR_REP_ALREADY_SENT() }, 'Reply already sent') return this } if (payload instanceof Error || this[kReplyIsError] === true) { onErrorHook(this, payload, onSendHook) return this } if (payload === undefined) { onSendHook(this, payload) return this } const contentType = this.getHeader('content-type') const hasContentType = contentType !== undefined if (payload !== null) { if (Buffer.isBuffer(payload) || typeof payload.pipe === 'function') { if (hasContentType === false) { this[kReplyHeaders]['content-type'] = CONTENT_TYPE.OCTET } onSendHook(this, payload) return this } if (hasContentType === false && typeof payload === 'string') { this[kReplyHeaders]['content-type'] = CONTENT_TYPE.PLAIN onSendHook(this, payload) return this } } if (this[kReplySerializer] !== null) { if (typeof payload !== 'string') { preserializeHook(this, payload) return this } else { payload = this[kReplySerializer](payload) } // The indexOf below also matches custom json mimetypes such as 'application/hal+json' or 'application/ld+json' } else if (hasContentType === false || contentType.indexOf('json') > -1) { if (hasContentType === false) { this[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON } else { // If hasContentType === true, we have a JSON mimetype if (contentType.indexOf('charset') === -1) { // If we have simply application/json instead of a custom json mimetype if (contentType.indexOf('/json') > -1) { this[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON } else { const currContentType = this[kReplyHeaders]['content-type'] // We extract the custom mimetype part (e.g. 'hal+' from 'application/hal+json') const customJsonType = currContentType.substring( currContentType.indexOf('/'), currContentType.indexOf('json') + 4 ) // We ensure we set the header to the proper JSON content-type if necessary // (e.g. 'application/hal+json' instead of 'application/json') this[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON.replace('/json', customJsonType) } } } if (typeof payload !== 'string') { preserializeHook(this, payload) return this } } onSendHook(this, payload) return this } Reply.prototype.getHeader = function (key) { key = key.toLowerCase() const res = this.raw let value = this[kReplyHeaders][key] if (value === undefined && res.hasHeader(key)) { value = res.getHeader(key) } return value } Reply.prototype.getHeaders = function () { return { ...this.raw.getHeaders(), ...this[kReplyHeaders] } } Reply.prototype.hasHeader = function (key) { key = key.toLowerCase() if (this[kReplyHeaders][key] !== undefined) { return true } return this.raw.hasHeader(key) } Reply.prototype.removeHeader = function (key) { // Node.js does not like headers with keys set to undefined, // so we have to delete the key. delete this[kReplyHeaders][key.toLowerCase()] return this } Reply.prototype.header = function (key, value) { const _key = key.toLowerCase() // default the value to '' value = value === undefined ? '' : value if (this[kReplyHeaders][_key] && _key === 'set-cookie') { // https://tools.ietf.org/html/rfc7230#section-3.2.2 if (typeof this[kReplyHeaders][_key] === 'string') { this[kReplyHeaders][_key] = [this[kReplyHeaders][_key]] } if (Array.isArray(value)) { Array.prototype.push.apply(this[kReplyHeaders][_key], value) } else { this[kReplyHeaders][_key].push(value) } } else { this[kReplyHeaders][_key] = value } return this } Reply.prototype.headers = function (headers) { const keys = Object.keys(headers) /* eslint-disable no-var */ for (var i = 0; i !== keys.length; ++i) { const key = keys[i] this.header(key, headers[key]) } return this } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer#directives // https://httpwg.org/specs/rfc7230.html#chunked.trailer.part const INVALID_TRAILERS = new Set([ 'transfer-encoding', 'content-length', 'host', 'cache-control', 'max-forwards', 'te', 'authorization', 'set-cookie', 'content-encoding', 'content-type', 'content-range', 'trailer' ]) Reply.prototype.trailer = function (key, fn) { key = key.toLowerCase() if (INVALID_TRAILERS.has(key)) { throw new FST_ERR_BAD_TRAILER_NAME(key) } if (typeof fn !== 'function') { throw new FST_ERR_BAD_TRAILER_VALUE(key, typeof fn) } if (this[kReplyTrailers] === null) this[kReplyTrailers] = {} this[kReplyTrailers][key] = fn return this } Reply.prototype.hasTrailer = function (key) { if (this[kReplyTrailers] === null) return false return this[kReplyTrailers][key.toLowerCase()] !== undefined } Reply.prototype.removeTrailer = function (key) { if (this[kReplyTrailers] === null) return this this[kReplyTrailers][key.toLowerCase()] = undefined return this } Reply.prototype.code = function (code) { const intValue = parseInt(code) if (isNaN(intValue) || intValue < 100 || intValue > 600) { throw new FST_ERR_BAD_STATUS_CODE(code || String(code)) } this.raw.statusCode = intValue this[kReplyHasStatusCode] = true return this } Reply.prototype.status = Reply.prototype.code Reply.prototype.serialize = function (payload) { if (this[kReplySerializer] !== null) { return this[kReplySerializer](payload) } else { if (this.context && this.context[kReplySerializerDefault]) { return this.context[kReplySerializerDefault](payload, this.raw.statusCode) } else { return serialize(this.context, payload, this.raw.statusCode) } } } Reply.prototype.serializer = function (fn) { this[kReplySerializer] = fn return this } Reply.prototype.type = function (type) { this[kReplyHeaders]['content-type'] = type return this } Reply.prototype.redirect = function (code, url) { if (typeof code === 'string') { url = code code = this[kReplyHasStatusCode] ? this.raw.statusCode : 302 } this.header('location', url).code(code).send() } Reply.prototype.callNotFound = function () { notFound(this) } Reply.prototype.getResponseTime = function () { let responseTime = 0 if (this[kReplyStartTime] !== undefined) { responseTime = (this[kReplyEndTime] || now()) - this[kReplyStartTime] } return responseTime } // Make reply a thenable, so it could be used with async/await. // See // - https://github.com/fastify/fastify/issues/1864 for the discussions // - https://promisesaplus.com/ for the definition of thenable // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then for the signature Reply.prototype.then = function (fulfilled, rejected) { if (this.sent) { fulfilled() return } eos(this.raw, (err) => { // We must not treat ERR_STREAM_PREMATURE_CLOSE as // an error because it is created by eos, not by the stream. if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') { if (rejected) { rejected(err) } else { this.log && this.log.warn('unhandled rejection on reply.then') } } else { fulfilled() } }) } function preserializeHook (reply, payload) { if (reply.context.preSerialization !== null) { onSendHookRunner( reply.context.preSerialization, reply.request, reply, payload, preserializeHookEnd ) } else { preserializeHookEnd(null, reply.request, reply, payload) } } function preserializeHookEnd (err, request, reply, payload) { if (err != null) { onErrorHook(reply, err) return } try { if (reply[kReplySerializer] !== null) { payload = reply[kReplySerializer](payload) } else if (reply.context && reply.context[kReplySerializerDefault]) { payload = reply.context[kReplySerializerDefault](payload, reply.raw.statusCode) } else { payload = serialize(reply.context, payload, reply.raw.statusCode) } } catch (e) { wrapSeralizationError(e, reply) onErrorHook(reply, e) return } flatstr(payload) onSendHook(reply, payload) } function wrapSeralizationError (error, reply) { error.serialization = reply.context.config } function onSendHook (reply, payload) { if (reply.context.onSend !== null) { reply[kReplySent] = true onSendHookRunner( reply.context.onSend, reply.request, reply, payload, wrapOnSendEnd ) } else { onSendEnd(reply, payload) } } function wrapOnSendEnd (err, request, reply, payload) { if (err != null) { onErrorHook(reply, err) } else { onSendEnd(reply, payload) } } function onSendEnd (reply, payload) { const res = reply.raw const req = reply.request const statusCode = res.statusCode // we check if we need to update the trailers header and set it if (reply[kReplyTrailers] !== null) { const trailerHeaders = Object.keys(reply[kReplyTrailers]) let header = '' for (const trailerName of trailerHeaders) { if (typeof reply[kReplyTrailers][trailerName] !== 'function') continue header += ' ' header += trailerName } // it must be chunked for trailer to work reply.header('Transfer-Encoding', 'chunked') reply.header('Trailer', header.trim()) } if (payload === undefined || payload === null) { reply[kReplySent] = true // according to https://tools.ietf.org/html/rfc7230#section-3.3.2 // we cannot send a content-length for 304 and 204, and all status code // < 200 // A sender MUST NOT send a Content-Length header field in any message // that contains a Transfer-Encoding header field. // For HEAD we don't overwrite the `content-length` if (statusCode >= 200 && statusCode !== 204 && statusCode !== 304 && req.method !== 'HEAD' && reply[kReplyTrailers] === null) { reply[kReplyHeaders]['content-length'] = '0' } res.writeHead(statusCode, reply[kReplyHeaders]) sendTrailer(payload, res, reply) // avoid ArgumentsAdaptorTrampoline from V8 res.end(null, null, null) return } if (typeof payload.pipe === 'function') { reply[kReplySent] = true sendStream(payload, res, reply) return } if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) { throw new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload) } if (reply[kReplyTrailers] === null) { if (!reply[kReplyHeaders]['content-length']) { reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload) } else if (req.raw.method !== 'HEAD' && reply[kReplyHeaders]['content-length'] !== Buffer.byteLength(payload)) { reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload) } } reply[kReplySent] = true res.writeHead(statusCode, reply[kReplyHeaders]) // write payload first res.write(payload) // then send trailers sendTrailer(payload, res, reply) // avoid ArgumentsAdaptorTrampoline from V8 res.end(null, null, null) } function logStreamError (logger, err, res) { if (err.code === 'ERR_STREAM_PREMATURE_CLOSE') { if (!logger[kDisableRequestLogging]) { logger.info({ res }, 'stream closed prematurely') } } else { logger.warn({ err }, 'response terminated with an error with headers already sent') } } function sendStream (payload, res, reply) { let sourceOpen = true let errorLogged = false // set trailer when stream ended sendStreamTrailer(payload, res, reply) eos(payload, { readable: true, writable: false }, function (err) { sourceOpen = false if (err != null) { if (res.headersSent || reply.request.raw.aborted === true) { if (!errorLogged) { errorLogged = true logStreamError(reply.log, err, res) } res.destroy() } else { onErrorHook(reply, err) } } // there is nothing to do if there is not an error }) eos(res, function (err) { if (sourceOpen) { if (err != null && res.headersSent && !errorLogged) { errorLogged = true logStreamError(reply.log, err, res) } if (typeof payload.destroy === 'function') { payload.destroy() } else if (typeof payload.close === 'function') { payload.close(noop) } else if (typeof payload.abort === 'function') { payload.abort() } else { reply.log.warn('stream payload does not end properly') } } }) // streams will error asynchronously, and we want to handle that error // appropriately, e.g. a 404 for a missing file. So we cannot use // writeHead, and we need to resort to setHeader, which will trigger // a writeHead when there is data to send. if (!res.headersSent) { for (const key in reply[kReplyHeaders]) { res.setHeader(key, reply[kReplyHeaders][key]) } } else { reply.log.warn('response will send, but you shouldn\'t use res.writeHead in stream mode') } payload.pipe(res) } function sendTrailer (payload, res, reply) { if (reply[kReplyTrailers] === null) return const trailerHeaders = Object.keys(reply[kReplyTrailers]) const trailers = {} for (const trailerName of trailerHeaders) { if (typeof reply[kReplyTrailers][trailerName] !== 'function') continue trailers[trailerName] = reply[kReplyTrailers][trailerName](reply, payload) } res.addTrailers(trailers) } function sendStreamTrailer (payload, res, reply) { if (reply[kReplyTrailers] === null) return payload.on('end', () => sendTrailer(null, res, reply)) } function onErrorHook (reply, error, cb) { reply[kReplySent] = true if (reply.context.onError !== null && reply[kReplyErrorHandlerCalled] === true) { reply[kReplyIsRunningOnErrorHook] = true onSendHookRunner( reply.context.onError, reply.request, reply, error, () => handleError(reply, error, cb) ) } else { handleError(reply, error, cb) } } function handleError (reply, error, cb) { reply[kReplyIsRunningOnErrorHook] = false const res = reply.raw let statusCode = res.statusCode statusCode = (statusCode >= 400) ? statusCode : 500 // treat undefined and null as same if (error != null) { if (error.headers !== undefined) { reply.headers(error.headers) } if (error.status >= 400) { statusCode = error.status } else if (error.statusCode >= 400) { statusCode = error.statusCode } } res.statusCode = statusCode const errorHandler = reply.context.errorHandler if (errorHandler && reply[kReplyErrorHandlerCalled] === false) { reply[kReplySent] = false reply[kReplyIsError] = false reply[kReplyErrorHandlerCalled] = true // remove header is needed in here, because when we pipe to a stream // `undefined` value header will directly passed to node response reply.removeHeader('content-length') const result = errorHandler(error, reply.request, reply) if (result !== undefined) { if (result !== null && typeof result.then === 'function') { wrapThenable(result, reply) } else { reply.send(result) } } return } let payload try { const serializerFn = getSchemaSerializer(reply.context, statusCode) payload = (serializerFn === false) ? serializeError({ error: statusCodes[statusCode + ''], code: error.code, message: error.message || '', statusCode }) : serializerFn(Object.create(error, { error: { value: statusCodes[statusCode + ''] }, message: { value: error.message || '' }, statusCode: { value: statusCode } })) if (serializerFn !== false && typeof payload !== 'string') { throw new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload) } } catch (err) { // error is always FST_ERR_SCH_SERIALIZATION_BUILD because this is called from route/compileSchemasForSerialization reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed') res.statusCode = 500 payload = serializeError({ error: statusCodes['500'], code: err.code, message: err.message, statusCode: 500 }) } flatstr(payload) reply[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload) if (cb) { cb(reply, payload) return } reply[kReplySent] = true res.writeHead(res.statusCode, reply[kReplyHeaders]) res.end(payload) } function setupResponseListeners (reply) { reply[kReplyStartTime] = now() const onResFinished = err => { reply[kReplyEndTime] = now() reply.raw.removeListener('finish', onResFinished) reply.raw.removeListener('error', onResFinished) const ctx = reply.context if (ctx && ctx.onResponse !== null) { hookRunner( ctx.onResponse, onResponseIterator, reply.request, reply, onResponseCallback ) } else { onResponseCallback(err, reply.request, reply) } } reply.raw.on('finish', onResFinished) reply.raw.on('error', onResFinished) } function onResponseIterator (fn, request, reply, next) { return fn(request, reply, next) } function onResponseCallback (err, request, reply) { if (reply.log[kDisableRequestLogging]) { return } const responseTime = reply.getResponseTime() if (err != null) { reply.log.error({ res: reply, err, responseTime }, 'request errored') return } reply.log.info({ res: reply, responseTime }, 'request completed') } function buildReply (R) { const props = [...R.props] function _Reply (res, request, log) { this.raw = res this[kReplyIsError] = false this[kReplyErrorHandlerCalled] = false this[kReplySent] = false this[kReplySentOverwritten] = false this[kReplySerializer] = null this.request = request this[kReplyHeaders] = {} this[kReplyTrailers] = null this[kReplyStartTime] = undefined this[kReplyEndTime] = undefined this.log = log // eslint-disable-next-line no-var var prop // eslint-disable-next-line no-var for (var i = 0; i < props.length; i++) { prop = props[i] this[prop.key] = prop.value } } _Reply.prototype = new R() _Reply.props = props return _Reply } function notFound (reply) { reply[kReplySent] = false reply[kReplyIsError] = false if (reply.context[kFourOhFourContext] === null) { reply.log.warn('Trying to send a NotFound error inside a 404 handler. Sending basic 404 response.') reply.code(404).send('404 Not Found') return } reply.request.context = reply.context[kFourOhFourContext] // preHandler hook if (reply.context.preHandler !== null) { hookRunner( reply.context.preHandler, hookIterator, reply.request, reply, internals.preHandlerCallback ) } else { internals.preHandlerCallback(null, reply.request, reply) } } /** * This function runs when a payload that is not a string|buffer|stream or null * should be serialized to be streamed to the response. * This is the default serializer that can be customized by the user using the replySerializer * * @param {object} context the request context * @param {object} data the JSON payload to serialize * @param {number} statusCode the http status code * @returns {string} the serialized payload */ function serialize (context, data, statusCode) { const fnSerialize = getSchemaSerializer(context, statusCode) if (fnSerialize) { return fnSerialize(data) } return JSON.stringify(data) } /** * Search for the right JSON schema compiled function in the request context * setup by the route configuration `schema.response`. * It will look for the exact match (eg 200) or generic (eg 2xx) * * @param {object} context the request context * @param {number} statusCode the http status code * @returns {function|boolean} the right JSON Schema function to serialize * the reply or false if it is not set */ function getSchemaSerializer (context, statusCode) { const responseSchemaDef = context[kSchemaResponse] if (!responseSchemaDef) { return false } if (responseSchemaDef[statusCode]) { return responseSchemaDef[statusCode] } const fallbackStatusCode = (statusCode + '')[0] + 'xx' if (responseSchemaDef[fallbackStatusCode]) { return responseSchemaDef[fallbackStatusCode] } return false } function noop () { } module.exports = Reply module.exports.buildReply = buildReply module.exports.setupResponseListeners = setupResponseListeners
Copyright ©2k19 -
Hexid
|
Tex7ure