From 3921ab94209bcd662e5336284e7afd8b97debd1e Mon Sep 17 00:00:00 2001 From: eloquent Date: Sat, 1 Aug 2026 11:28:45 -0400 Subject: [PATCH 1/3] don't write a response body for null body status codes in sendStatus --- __tests__/utils.unit.js | 17 +++++++++++++++++ src/lib/response.js | 2 +- src/lib/utils.js | 10 ++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/__tests__/utils.unit.js b/__tests__/utils.unit.js index 888d4ab..d17f78e 100644 --- a/__tests__/utils.unit.js +++ b/__tests__/utils.unit.js @@ -232,6 +232,23 @@ describe("Utility Function Tests:", function () { }); // end it }); // end encodeBody tests + describe("statusBodyLookup:", function () { + test.each([ + [100, ""], + [101, ""], + [200, "OK"], + [204, ""], + [205, ""], + [304, ""], + [404, "Not Found"], + [502, "Bad Gateway"], + [999, "Unknown"], + ])("%d", (status, expected) => { + expect(utils.statusBodyLookup(status)).toBe(expected); + }); // end it + }); // end statusBodyLookup tests + + describe("extractRoutes:", function () { it("Sample routes", function () { // Create an api instance diff --git a/src/lib/response.js b/src/lib/response.js index fe1e26b..b59ce38 100644 --- a/src/lib/response.js +++ b/src/lib/response.js @@ -415,7 +415,7 @@ class RESPONSE { // Convenience method for sending status codes sendStatus(status) { - this.status(status).send(UTILS.statusLookup(status)); + this.status(status).send(UTILS.statusBodyLookup(status)); } // Convenience method for setting CORS headers diff --git a/src/lib/utils.js b/src/lib/utils.js index 23a0e68..231ca86 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -114,6 +114,16 @@ export const statusLookup = (status) => { return status in statusCodes ? statusCodes[status] : 'Unknown'; }; +export const statusBodyLookup = (status) => { + // The following status codes must not have a response body + // according to rfc 9110 + if ((100 <= status && status < 200) || [204, 205, 304].includes(status)) { + return ''; + } + + return statusLookup(status); +}; + // Parses routes into readable array const extractRoutes = (routes, table = []) => { // Loop through all routes From da2d1bb3c7a459c4c0e0df46aa0f236582ab51dc Mon Sep 17 00:00:00 2001 From: eloquent Date: Thu, 6 Aug 2026 19:56:10 -0400 Subject: [PATCH 2/3] address comments from copilot --- __tests__/responses.unit.js | 10 ++++++++++ __tests__/utils.unit.js | 7 ++++++- src/lib/utils.js | 6 ++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/__tests__/responses.unit.js b/__tests__/responses.unit.js index b535b6b..6469d4d 100644 --- a/__tests__/responses.unit.js +++ b/__tests__/responses.unit.js @@ -73,6 +73,10 @@ api.get('/testSendStatus', function(req,res) { res.sendStatus(200) }) +api.get('/testSendStatus204', function(req,res) { + res.sendStatus(204) +}) + api.get('/testSendStatus403', function(req,res) { res.sendStatus(403) }) @@ -180,6 +184,12 @@ describe('Response Tests:', function() { expect(result).toEqual({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 200, body: 'OK', isBase64Encoded: false }) }) // end it + it('sendStatus 204', async function() { + let _event = Object.assign({},event,{ path: '/testSendStatus204'}) + let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) })) + expect(result).toEqual({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 204, body: '', isBase64Encoded: false }) + }) // end it + it('sendStatus 403', async function() { let _event = Object.assign({},event,{ path: '/testSendStatus403'}) let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) })) diff --git a/__tests__/utils.unit.js b/__tests__/utils.unit.js index d17f78e..b6998ee 100644 --- a/__tests__/utils.unit.js +++ b/__tests__/utils.unit.js @@ -237,13 +237,18 @@ describe("Utility Function Tests:", function () { [100, ""], [101, ""], [200, "OK"], + ["200", "OK"], [204, ""], + ["204", ""], [205, ""], + ["205", ""], [304, ""], + ["304", ""], [404, "Not Found"], [502, "Bad Gateway"], [999, "Unknown"], - ])("%d", (status, expected) => { + ["not a number", "Unknown"] + ])("%s", (status, expected) => { expect(utils.statusBodyLookup(status)).toBe(expected); }); // end it }); // end statusBodyLookup tests diff --git a/src/lib/utils.js b/src/lib/utils.js index 231ca86..22a02dc 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -115,13 +115,15 @@ export const statusLookup = (status) => { }; export const statusBodyLookup = (status) => { + const code = typeof status === 'string' ? Number(status) : status; + // The following status codes must not have a response body // according to rfc 9110 - if ((100 <= status && status < 200) || [204, 205, 304].includes(status)) { + if ((100 <= code && code < 200) || [204, 205, 304].includes(code)) { return ''; } - return statusLookup(status); + return statusLookup(code); }; // Parses routes into readable array From 06afba5f1ce081266f3f01e9f9f008bfea26fe10 Mon Sep 17 00:00:00 2001 From: Naor Peled Date: Sat, 8 Aug 2026 19:09:58 +0300 Subject: [PATCH 3/3] docs: update sendStatus docs for null body status codes --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e1bd30..19302b7 100644 --- a/README.md +++ b/README.md @@ -486,10 +486,16 @@ The `sendStatus` method sets the status code and returns its string representati ```javascript res.sendStatus(200); // equivalent to res.status(200).send('OK') -res.sendStatus(304); // equivalent to res.status(304).send('Not Modified') res.sendStatus(403); // equivalent to res.status(403).send('Forbidden') ``` +Status codes that [must not carry content](https://datatracker.ietf.org/doc/html/rfc9110#name-overview-of-status-codes) per RFC 9110 — `1xx`, `204`, `205`, and `304` — are sent with an empty body instead: + +```javascript +res.sendStatus(204); // equivalent to res.status(204).send('') +res.sendStatus(304); // equivalent to res.status(304).send('') +``` + **NOTE:** If an unsupported status code is provided, it will return 'Unknown' as the body. ### header(key, value [,append])