Skip to content

Commit

Permalink
http: rejecting invalid body writes configurable with option rejectNo…
Browse files Browse the repository at this point in the history
…nStandardBodyWrites
  • Loading branch information
gerrard00 committed May 1, 2023
1 parent 68a2fe6 commit d29f5ef
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 25 deletions.
3 changes: 2 additions & 1 deletion doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -1346,7 +1346,8 @@ Response body size doesn't match with the specified content-length header value.

### `ERR_HTTP_CONTENT_LENGTH_MISMATCH`

An error is thrown if when writing to an HTTP response which does not allow contents.
An error is thrown if when writing to an HTTP response which does not allow
contents.

<a id="ERR_HTTP_CONTENT_LENGTH_MISMATCH"></a>

Expand Down
11 changes: 10 additions & 1 deletion lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const kUniqueHeaders = Symbol('kUniqueHeaders');
const kBytesWritten = Symbol('kBytesWritten');
const kErrored = Symbol('errored');
const kHighWaterMark = Symbol('kHighWaterMark');
const kRejectNonStandardBodyWrites = Symbol('kRejectNonStandardBodyWrites');

const nop = () => {};

Expand Down Expand Up @@ -151,6 +152,7 @@ function OutgoingMessage(options) {

this[kErrored] = null;
this[kHighWaterMark] = options?.highWaterMark ?? getDefaultHighWaterMark();
this[kRejectNonStandardBodyWrites] = options?.rejectNonStandardBodyWrites ?? true;
}
ObjectSetPrototypeOf(OutgoingMessage.prototype, Stream.prototype);
ObjectSetPrototypeOf(OutgoingMessage, Stream);
Expand Down Expand Up @@ -886,7 +888,14 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
}

if (!msg._hasBody) {
throw ERR_HTTP_BODY_NOT_ALLOWED();
if (msg[kRejectNonStandardBodyWrites]) {
throw new ERR_HTTP_BODY_NOT_ALLOWED();
} else {
debug('This type of response MUST NOT have a body. ' +
'Ignoring write() calls.');
process.nextTick(callback);
return true;
}
}

if (err) {
Expand Down
14 changes: 13 additions & 1 deletion lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,14 @@ function storeHTTPOptions(options) {
validateBoolean(joinDuplicateHeaders, 'options.joinDuplicateHeaders');
}
this.joinDuplicateHeaders = joinDuplicateHeaders;

const rejectNonStandardBodyWrites = options.rejectNonStandardBodyWrites;
if (rejectNonStandardBodyWrites !== undefined) {
validateBoolean(rejectNonStandardBodyWrites, 'options.rejectNonStandardBodyWrites');
this.rejectNonStandardBodyWrites = rejectNonStandardBodyWrites;
} else {
this.rejectNonStandardBodyWrites = true
}
}

function setupConnectionsTracking(server) {
Expand Down Expand Up @@ -1020,7 +1028,11 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
}
}

const res = new server[kServerResponse](req, { highWaterMark: socket.writableHighWaterMark });
const res = new server[kServerResponse](req,
{
highWaterMark: socket.writableHighWaterMark,
rejectNonStandardBodyWrites: server.rejectNonStandardBodyWrites
});
res._keepAliveTimeout = server.keepAliveTimeout;
res._maxRequestsPerSocket = server.maxRequestsPerSocket;
res._onPendingData = updateOutgoingData.bind(undefined,
Expand Down
1 change: 1 addition & 0 deletions lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ let maxHeaderSize;
* requireHostHeader?: boolean;
* joinDuplicateHeaders?: boolean;
* highWaterMark?: number;
* rejectNonStandardBodyWrites?: boolean;
* }} [opts]
* @param {Function} [requestListener]
* @returns {Server}
Expand Down
100 changes: 78 additions & 22 deletions test/parallel/test-http-head-throw-on-response-body-write.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,86 @@ const common = require('../common');
const assert = require('assert');
const http = require('http');

// should be using common.mustCall on both req res callbacks provided to createServer
{
const server = http.createServer((req, res) => {
assert.throws(() => {
res.write('this is content');
}, {
code: 'ERR_HTTP_BODY_NOT_ALLOWED',
name: 'Error',
message: 'Adding content for this request method or response status is not allowed.'
});
res.end();
});
server.listen(0);

server.on('listening', common.mustCall(function() {
const req = http.request({
port: this.address().port,
method: 'HEAD',
path: '/'
}, common.mustCall((res) => {
res.resume();
res.on('end', common.mustCall(function() {
server.close();
}));
}));
req.end();
}));
}

{
const server = http.createServer({
rejectNonStandardBodyWrites: true,
}, (req, res) => {
res.writeHead(204);
assert.throws(() => {
res.write('this is content');
}, {
code: 'ERR_HTTP_BODY_NOT_ALLOWED',
name: 'Error',
message: 'Adding content for this request method or response status is not allowed.'
});
res.end();
});
server.listen(0);

server.on('listening', common.mustCall(function() {
const req = http.request({
port: this.address().port,
method: 'GET',
path: '/'
}, common.mustCall((res) => {
res.resume();
res.on('end', common.mustCall(function() {
server.close();
}));
}));
req.end();
}));
}

const server = http.createServer((req, res) => {
res.writeHead(204);
assert.throws(() => {
res.write('this is content');
}, {
code: 'ERR_HTTP_BODY_NOT_ALLOWED',
name: 'Error',
message: 'Adding content for this request method or response status is not allowed.'
{
const server = http.createServer({
rejectNonStandardBodyWrites: false,
}, (req, res) => {
res.writeHead(200);
res.end('this is content');
});
res.end();
});
server.listen(0);
server.listen(0);

server.on('listening', common.mustCall(function() {
const req = http.request({
port: this.address().port,
method: 'GET',
path: '/'
}, common.mustCall((res) => {
res.resume();
res.on('end', common.mustCall(function() {
server.close();
server.on('listening', common.mustCall(function() {
const req = http.request({
port: this.address().port,
method: 'HEAD',
path: '/'
}, common.mustCall((res) => {
res.resume();
res.on('end', common.mustCall(function() {
server.close();
}));
}));
req.end();
}));
req.end();
}));
}

0 comments on commit d29f5ef

Please sign in to comment.