diff --git a/README.md b/README.md
index 19302b7..c0c00b7 100644
--- a/README.md
+++ b/README.md
@@ -476,7 +476,15 @@ The `status` method allows you to set the status code that is returned to API Ga
```javascript
api.get('/users', (req, res) => {
- res.status(304).send('Not Modified');
+ res.status(404).send('Not Found');
+});
+```
+
+**NOTE:** 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 always sent with an empty body. Any body passed to `send()`, `json()`, `html()`, etc. is discarded for these status codes.
+
+```javascript
+api.get('/users', (req, res) => {
+ res.status(204).json({ foo: 'bar' }); // sent as 204 with an empty body
});
```
@@ -489,7 +497,7 @@ res.sendStatus(200); // equivalent to res.status(200).send('OK')
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:
+As with [`status()`](#statuscode), the `1xx`, `204`, `205`, and `304` status codes are sent with an empty body:
```javascript
res.sendStatus(204); // equivalent to res.status(204).send('')
diff --git a/__tests__/responses.unit.js b/__tests__/responses.unit.js
index 6469d4d..3674c44 100644
--- a/__tests__/responses.unit.js
+++ b/__tests__/responses.unit.js
@@ -81,6 +81,22 @@ api.get('/testSendStatus403', function(req,res) {
res.sendStatus(403)
})
+api.get('/testNullBodyJson', function(req,res) {
+ res.status(204).json({ foo: 'bar' })
+})
+
+api.get('/testNullBodySend', function(req,res) {
+ res.status(205).send('Reset Content')
+})
+
+api.get('/testNullBodyHtml', function(req,res) {
+ res.status(304).html('
Not Modified
')
+})
+
+api.get('/testNonNullBodyJson', function(req,res) {
+ res.status(200).json({ foo: 'bar' })
+})
+
// Secondary route
api2.get('/testJSONPResponse', function(req,res) {
res.jsonp({ foo: 'bar' })
@@ -196,6 +212,30 @@ describe('Response Tests:', function() {
expect(result).toEqual({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 403, body: 'Forbidden', isBase64Encoded: false })
}) // end it
+ it('Null body status: json()', async function() {
+ let _event = Object.assign({},event,{ path: '/testNullBodyJson'})
+ 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('Null body status: send()', async function() {
+ let _event = Object.assign({},event,{ path: '/testNullBodySend'})
+ let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
+ expect(result).toEqual({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 205, body: '', isBase64Encoded: false })
+ }) // end it
+
+ it('Null body status: html()', async function() {
+ let _event = Object.assign({},event,{ path: '/testNullBodyHtml'})
+ let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
+ expect(result).toEqual({ multiValueHeaders: { 'content-type': ['text/html'] }, statusCode: 304, body: '', isBase64Encoded: false })
+ }) // end it
+
+ it('Non-null body status is unaffected', async function() {
+ let _event = Object.assign({},event,{ path: '/testNonNullBodyJson'})
+ let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) }))
+ expect(result).toEqual({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 200, body: '{"foo":"bar"}', isBase64Encoded: false })
+ }) // end it
+
it('JSONP response (default callback)', async function() {
let _event = Object.assign({},event,{ path: '/testJSONPResponse' })
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 b6998ee..f692ccc 100644
--- a/__tests__/utils.unit.js
+++ b/__tests__/utils.unit.js
@@ -232,27 +232,32 @@ describe("Utility Function Tests:", function () {
}); // end it
}); // end encodeBody tests
- describe("statusBodyLookup:", function () {
+ describe("isNullBodyStatus:", function () {
test.each([
- [100, ""],
- [101, ""],
- [200, "OK"],
- ["200", "OK"],
- [204, ""],
- ["204", ""],
- [205, ""],
- ["205", ""],
- [304, ""],
- ["304", ""],
- [404, "Not Found"],
- [502, "Bad Gateway"],
- [999, "Unknown"],
- ["not a number", "Unknown"]
+ [100, true],
+ [101, true],
+ [199, true],
+ [204, true],
+ [205, true],
+ [304, true],
+ ["204", true],
+ ["304", true],
+ [200, false],
+ [201, false],
+ [203, false],
+ [206, false],
+ [303, false],
+ [305, false],
+ [404, false],
+ [502, false],
+ ["200", false],
+ ["foo", false],
+ [undefined, false],
+ [null, false],
])("%s", (status, expected) => {
- expect(utils.statusBodyLookup(status)).toBe(expected);
+ expect(utils.isNullBodyStatus(status)).toBe(expected);
}); // end it
- }); // end statusBodyLookup tests
-
+ }); // end isNullBodyStatus tests
describe("extractRoutes:", function () {
it("Sample routes", function () {
diff --git a/src/lib/response.js b/src/lib/response.js
index b59ce38..81b1d0d 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.statusBodyLookup(status));
+ this.status(status).send(UTILS.statusLookup(status));
}
// Convenience method for setting CORS headers
@@ -525,6 +525,11 @@ class RESPONSE {
body = '';
}
+ // Discard the body for status codes that must not include content
+ if (UTILS.isNullBodyStatus(this._statusCode)) {
+ body = '';
+ }
+
let headers = {};
let cookies = {};
diff --git a/src/lib/utils.js b/src/lib/utils.js
index 22a02dc..8c294d1 100644
--- a/src/lib/utils.js
+++ b/src/lib/utils.js
@@ -114,16 +114,13 @@ export const statusLookup = (status) => {
return status in statusCodes ? statusCodes[status] : 'Unknown';
};
-export const statusBodyLookup = (status) => {
+// Status codes that must not include content per RFC 9110
+// https://datatracker.ietf.org/doc/html/rfc9110#name-overview-of-status-codes
+export const isNullBodyStatus = (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 <= code && code < 200) || [204, 205, 304].includes(code)) {
- return '';
- }
-
- return statusLookup(code);
+ return (
+ (code >= 100 && code < 200) || code === 204 || code === 205 || code === 304
+ );
};
// Parses routes into readable array