Skip to content

Commit

Permalink
Simplify reply sent monitoring
Browse files Browse the repository at this point in the history
  • Loading branch information
sergejostir committed May 14, 2021
1 parent 1c3d4ee commit 8e61a47
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 25 deletions.
26 changes: 9 additions & 17 deletions lib/reply.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const {
kSchemaResponse,
kFourOhFourContext,
kReplyErrorHandlerCalled,
kReplySent,
kReplySentOverwritten,
kReplyStartTime,
kReplySerializer,
Expand Down Expand Up @@ -52,7 +51,6 @@ const warning = require('./warnings')

function Reply (res, request, log) {
this.raw = res
this[kReplySent] = false
this[kReplySerializer] = null
this[kReplyErrorHandlerCalled] = false
this[kReplyIsError] = false
Expand All @@ -79,19 +77,23 @@ Object.defineProperties(Reply.prototype, {
sent: {
enumerable: true,
get () {
return this[kReplySent]
// We are checking whether reply was manually marked as sent or the
// response has ended. The response.writableEnded property was added in
// Node.js v12.9.0. Since fastify supports older Node.js versions as well,
// we have to take response.finished property in consideration when
// applicable.
return (this[kReplySentOverwritten] || (typeof this.raw.writableEnded !== 'undefined' ? this.raw.writableEnded : this.raw.finished)) === true
},
set (value) {
if (value !== true) {
throw new FST_ERR_REP_SENT_VALUE()
}

if (this[kReplySent]) {
if (this.sent) {
throw new FST_ERR_REP_ALREADY_SENT()
}

this[kReplySentOverwritten] = true
this[kReplySent] = true
}
},
statusCode: {
Expand All @@ -105,7 +107,7 @@ Object.defineProperties(Reply.prototype, {
})

Reply.prototype.hijack = function () {
this[kReplySent] = true
this[kReplySentOverwritten] = true
return this
}

Expand All @@ -114,7 +116,7 @@ Reply.prototype.send = function (payload) {
throw new FST_ERR_SEND_INSIDE_ONERR()
}

if (this[kReplySent]) {
if (this.sent) {
this.log.warn({ err: new FST_ERR_REP_ALREADY_SENT() }, 'Reply already sent')
return this
}
Expand Down Expand Up @@ -373,7 +375,6 @@ function preserializeHookEnd (err, request, reply, payload) {
}

function onSendHook (reply, payload) {
reply[kReplySent] = true
if (reply.context.onSend !== null) {
onSendHookRunner(
reply.context.onSend,
Expand Down Expand Up @@ -401,8 +402,6 @@ function onSendEnd (reply, payload) {
const statusCode = res.statusCode

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.
Expand Down Expand Up @@ -432,8 +431,6 @@ function onSendEnd (reply, payload) {
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
}

reply[kReplySent] = true

res.writeHead(statusCode, reply[kReplyHeaders])

// avoid ArgumentsAdaptorTrampoline from V8
Expand Down Expand Up @@ -503,7 +500,6 @@ function sendStream (payload, res, reply) {
}

function onErrorHook (reply, error, cb) {
reply[kReplySent] = true
if (reply.context.onError !== null && reply[kReplyErrorHandlerCalled] === true) {
reply[kReplyIsRunningOnErrorHook] = true
onSendHookRunner(
Expand Down Expand Up @@ -539,7 +535,6 @@ function handleError (reply, error, cb) {

const errorHandler = reply.context.errorHandler
if (errorHandler && reply[kReplyErrorHandlerCalled] === false) {
reply[kReplySent] = false
reply[kReplyIsError] = false
reply[kReplyErrorHandlerCalled] = true
reply[kReplyHeaders]['content-length'] = undefined
Expand Down Expand Up @@ -585,7 +580,6 @@ function handleError (reply, error, cb) {
return
}

reply[kReplySent] = true
res.writeHead(res.statusCode, reply[kReplyHeaders])
res.end(payload)
}
Expand Down Expand Up @@ -647,7 +641,6 @@ function buildReply (R) {
this.raw = res
this[kReplyIsError] = false
this[kReplyErrorHandlerCalled] = false
this[kReplySent] = false
this[kReplySentOverwritten] = false
this[kReplySerializer] = null
this.request = request
Expand All @@ -660,7 +653,6 @@ function buildReply (R) {
}

function notFound (reply) {
reply[kReplySent] = false
reply[kReplyIsError] = false

if (reply.context[kFourOhFourContext] === null) {
Expand Down
1 change: 0 additions & 1 deletion lib/symbols.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const keys = {
kReplyIsError: Symbol('fastify.reply.isError'),
kReplyHeaders: Symbol('fastify.reply.headers'),
kReplyHasStatusCode: Symbol('fastify.reply.hasStatusCode'),
kReplySent: Symbol('fastify.reply.sent'),
kReplySentOverwritten: Symbol('fastify.reply.sentOverwritten'),
kReplyStartTime: Symbol('fastify.reply.startTime'),
kReplyErrorHandlerCalled: Symbol('fastify.reply.errorHandlerCalled'),
Expand Down
9 changes: 3 additions & 6 deletions lib/wrapThenable.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

const {
kReplyIsError,
kReplySent,
kReplySentOverwritten
} = require('./symbols')

Expand All @@ -16,26 +15,24 @@ function wrapThenable (thenable, reply) {

// this is for async functions that
// are using reply.send directly
if (payload !== undefined || (reply.raw.statusCode === 204 && reply[kReplySent] === false)) {
if (payload !== undefined || (reply.raw.statusCode === 204 && reply.sent === false)) {
// we use a try-catch internally to avoid adding a catch to another
// promise, increase promise perf by 10%
try {
reply.send(payload)
} catch (err) {
reply[kReplySent] = false
reply[kReplyIsError] = true
reply.send(err)
}
} else if (reply[kReplySent] === false) {
} else if (reply.sent === false) {
reply.log.error({ err: new FST_ERR_PROMISE_NOT_FULFILLED() }, "Promise may not be fulfilled with 'undefined' when statusCode is not 204")
}
}, function (err) {
if (reply[kReplySentOverwritten] === true || reply.sent === true) {
if (reply.sent === true) {
reply.log.error({ err }, 'Promise errored, but reply.sent = true was set')
return
}

reply[kReplySent] = false
reply[kReplyIsError] = true
reply.send(err)
})
Expand Down
3 changes: 2 additions & 1 deletion test/wrapThenable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const t = require('tap')
const test = t.test
const { kReplySentOverwritten } = require('../lib/symbols')
const wrapThenable = require('../lib/wrapThenable')
const Reply = require('../lib/reply')

test('should resolve immediately when reply[kReplySentOverwritten] is true', t => {
const reply = {}
Expand All @@ -15,7 +16,7 @@ test('should resolve immediately when reply[kReplySentOverwritten] is true', t =

test('should reject immediately when reply[kReplySentOverwritten] is true', t => {
t.plan(1)
const reply = { res: {} }
const reply = new Reply({}, {}, {})
reply[kReplySentOverwritten] = true
reply.log = {
error: ({ err }) => {
Expand Down

0 comments on commit 8e61a47

Please sign in to comment.