Skip to content

Commit f61009a

Browse files
sindilevichrxmarbles
authored andcommitted
feat(status): Custom status response (#192)
* Added statusOkResponse to configuration The new configuration property: statusOkResponse will allow to globally configure the HTTP response body, used to respond with on a successful healthcheck. * Memoize the value of statusOkResponse When no healthtchecks were defined, the value of statusOkResponse would be returned as is. Therefore, memoizing its string representation would boost performance. * Added statusErrorResponse to configuration The new configuration property: statusErrorResponse will allow to globally configure the HTTP response body, used to respond with on an unsuccessful healthcheck. An unsuccessful healthcheck may override the global statusErrorResponse property by setting the statusResponse property on the Error object. * Incorporated use of headers
1 parent c02925b commit f61009a

File tree

6 files changed

+464
-22
lines changed

6 files changed

+464
-22
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ const options = {
5858
caseInsensitive, // [optional] whether given health checks routes are case insensitive (defaults to false)
5959

6060
statusOk, // [optional = 200] status to be returned for successful healthchecks
61+
statusOkResponse, // [optional = { status: 'ok' }] status response to be returned for successful healthchecks
6162
statusError, // [optional = 503] status to be returned for unsuccessful healthchecks
63+
statusErrorResponse, // [optional = { status: 'error' }] status response to be returned for unsuccessful healthchecks
6264

6365
// cleanup options
6466
timeout: 1000, // [optional = 1000] number of milliseconds before forceful exiting
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict'
2+
const http = require('http')
3+
const server = http.createServer((req, res) => res.end('hello'))
4+
5+
const { createTerminus } = require('../../')
6+
const SIGNAL = 'SIGINT'
7+
8+
createTerminus(server, {
9+
healthChecks: {
10+
'/health': () => Promise.resolve()
11+
},
12+
signal: SIGNAL,
13+
beforeShutdown: () => {
14+
return new Promise((resolve) => {
15+
setTimeout(resolve, 1000)
16+
})
17+
},
18+
statusErrorResponse: { status: 'down' }
19+
})
20+
21+
server.listen(8000, () => {
22+
process.kill(process.pid, SIGNAL)
23+
})
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict'
2+
const http = require('http')
3+
const server = http.createServer((req, res) => res.end('hello'))
4+
5+
const { createTerminus } = require('../../')
6+
const SIGNAL = 'SIGINT'
7+
8+
createTerminus(server, {
9+
healthChecks: {
10+
'/health': () => Promise.resolve()
11+
},
12+
signal: SIGNAL,
13+
beforeShutdown: () => {
14+
return new Promise((resolve) => {
15+
setTimeout(resolve, 1000)
16+
})
17+
},
18+
statusError: 501
19+
})
20+
21+
server.listen(8000, () => {
22+
process.kill(process.pid, SIGNAL)
23+
})

lib/terminus.js

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,23 @@
33
const stoppable = require('stoppable')
44
const { promisify } = require('util')
55

6-
const SUCCESS_RESPONSE = JSON.stringify({
7-
status: 'ok'
8-
})
6+
let SUCCESS_RESPONSE
97

10-
const FAILURE_RESPONSE = JSON.stringify({
11-
status: 'error'
12-
})
8+
let FAILURE_RESPONSE
139

1410
function noopResolves () {
1511
return Promise.resolve()
1612
}
1713

18-
async function sendSuccess (res, { info, verbatim, statusOk, headers }) {
14+
async function sendSuccess (res, { info, verbatim, statusOk, statusOkResponse, headers }) {
15+
res.statusCode = statusOk
1916
res.setHeader('Content-Type', 'application/json')
2017
res.writeHead(statusOk, headers)
2118
if (info) {
2219
return res.end(
2320
JSON.stringify(
2421
Object.assign(
25-
{
26-
status: 'ok'
27-
},
22+
statusOkResponse,
2823
verbatim ? info : { info, details: info }
2924
)
3025
)
@@ -34,7 +29,7 @@ async function sendSuccess (res, { info, verbatim, statusOk, headers }) {
3429
}
3530

3631
async function sendFailure (res, options) {
37-
const { error, headers, onSendFailureDuringShutdown, exposeStackTraces, statusCode, statusError } = options
32+
const { error, headers, onSendFailureDuringShutdown, exposeStackTraces, statusCode, statusResponse, statusError, statusErrorResponse } = options
3833

3934
function replaceErrors (_, value) {
4035
if (value instanceof Error) {
@@ -54,16 +49,19 @@ async function sendFailure (res, options) {
5449
if (onSendFailureDuringShutdown) {
5550
await onSendFailureDuringShutdown()
5651
}
52+
res.statusCode = statusCode || statusError
53+
const responseBody = statusResponse || statusErrorResponse
5754
res.setHeader('Content-Type', 'application/json')
5855
res.writeHead(statusCode || statusError, headers)
5956
if (error) {
60-
return res.end(JSON.stringify({
61-
status: 'error',
62-
error,
63-
details: error
64-
}, replaceErrors))
57+
return res.end(JSON.stringify(
58+
Object.assign(
59+
responseBody, {
60+
error,
61+
details: error
62+
}), replaceErrors))
6563
}
66-
res.end(FAILURE_RESPONSE)
64+
res.end(statusResponse ? JSON.stringify(responseBody) : FAILURE_RESPONSE)
6765
}
6866

6967
const intialState = {
@@ -73,35 +71,39 @@ const intialState = {
7371
function noop () {}
7472

7573
function decorateWithHealthCheck (server, state, options) {
76-
const { healthChecks, logger, headers, onSendFailureDuringShutdown, sendFailuresDuringShutdown, caseInsensitive, statusOk, statusError } = options
74+
const { healthChecks, logger, headers, onSendFailureDuringShutdown, sendFailuresDuringShutdown, caseInsensitive, statusOk, statusOkResponse, statusError, statusErrorResponse } = options
7775

7876
let hasSetHandler = false
7977
const createHandler = (listener) => {
8078
const check = hasSetHandler
8179
? () => {}
8280
: async (healthCheck, res) => {
8381
if (state.isShuttingDown && sendFailuresDuringShutdown) {
84-
return sendFailure(res, { onSendFailureDuringShutdown, statusError })
82+
return sendFailure(res, { onSendFailureDuringShutdown, statusError, statusErrorResponse })
8583
}
8684
let info
8785
try {
8886
info = await healthCheck({ state })
8987
} catch (error) {
9088
logger('healthcheck failed', error)
89+
const statusCode = error.statusCode
90+
const statusResponse = error.statusResponse
9191
return sendFailure(
9292
res,
9393
{
9494
error: error.causes,
9595
headers,
9696
exposeStackTraces: healthChecks.__unsafeExposeStackTraces,
97-
statusCode: error.statusCode,
98-
statusError
97+
statusCode,
98+
statusResponse,
99+
statusError,
100+
statusErrorResponse
99101
}
100102
)
101103
}
102104
return sendSuccess(
103105
res,
104-
{ info, verbatim: healthChecks.verbatim, statusOk, headers }
106+
{ info, verbatim: healthChecks.verbatim, statusOk, statusOkResponse, headers }
105107
)
106108
}
107109

@@ -184,12 +186,18 @@ function terminus (server, options = {}) {
184186
logger = noop,
185187
caseInsensitive = false,
186188
statusOk = 200,
189+
statusOkResponse = { status: 'ok' },
187190
statusError = 503,
191+
statusErrorResponse = { status: 'error' },
188192
headers = options.headers || {}
189193
} = options
190194
const onSignal = options.onSignal || options.onSigterm || noopResolves
191195
const state = Object.assign({}, intialState)
192196

197+
SUCCESS_RESPONSE = JSON.stringify(statusOkResponse)
198+
199+
FAILURE_RESPONSE = JSON.stringify(statusErrorResponse)
200+
193201
if (Object.keys(healthChecks).length > 0) {
194202
decorateWithHealthCheck(server, state, {
195203
healthChecks,
@@ -198,7 +206,9 @@ function terminus (server, options = {}) {
198206
onSendFailureDuringShutdown,
199207
caseInsensitive,
200208
statusOk,
209+
statusOkResponse,
201210
statusError,
211+
statusErrorResponse,
202212
headers
203213
})
204214
}

0 commit comments

Comments
 (0)