http: prevent writing to the body when not allowed by HTTP spec · nodejs/node@4197a9a · GitHub
Skip to content

Commit 4197a9a

Browse files
gerrard00targos
authored andcommitted
http: prevent writing to the body when not allowed by HTTP spec
PR-URL: #47732 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent e6685f9 commit 4197a9a

8 files changed

Lines changed: 142 additions & 12 deletions

File tree

doc/api/errors.md

Lines changed: 5 additions & 0 deletions

doc/api/http.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,9 +2144,10 @@ it will switch to implicit header mode and flush the implicit headers.
21442144
This sends a chunk of the response body. This method may
21452145
be called multiple times to provide successive parts of the body.
21462146

2147-
In the `node:http` module, the response body is omitted when the
2148-
request is a HEAD request. Similarly, the `204` and `304` responses
2149-
_must not_ include a message body.
2147+
Writing to the body is not allowed when the request method or response status
2148+
do not support content. If an attempt is made to write to the body for a
2149+
HEAD request or as part of a `204` or `304`response, a synchronous `Error`
2150+
with the code `ERR_HTTP_BODY_NOT_ALLOWED` is thrown.
21502151

21512152
`chunk` can be a string or a buffer. If `chunk` is a string,
21522153
the second parameter specifies how to encode it into a byte stream.

lib/_http_outgoing.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const {
6060
ERR_HTTP_HEADERS_SENT,
6161
ERR_HTTP_INVALID_HEADER_VALUE,
6262
ERR_HTTP_TRAILER_INVALID,
63+
ERR_HTTP_BODY_NOT_ALLOWED,
6364
ERR_INVALID_HTTP_TOKEN,
6465
ERR_INVALID_ARG_TYPE,
6566
ERR_INVALID_ARG_VALUE,
@@ -85,6 +86,7 @@ const kUniqueHeaders = Symbol('kUniqueHeaders');
8586
const kBytesWritten = Symbol('kBytesWritten');
8687
const kErrored = Symbol('errored');
8788
const kHighWaterMark = Symbol('kHighWaterMark');
89+
const kRejectNonStandardBodyWrites = Symbol('kRejectNonStandardBodyWrites');
8890

8991
const nop = () => {};
9092

@@ -150,6 +152,7 @@ function OutgoingMessage(options) {
150152

151153
this[kErrored] = null;
152154
this[kHighWaterMark] = options?.highWaterMark ?? getDefaultHighWaterMark();
155+
this[kRejectNonStandardBodyWrites] = options?.rejectNonStandardBodyWrites ?? false;
153156
}
154157
ObjectSetPrototypeOf(OutgoingMessage.prototype, Stream.prototype);
155158
ObjectSetPrototypeOf(OutgoingMessage, Stream);
@@ -884,6 +887,17 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
884887
err = new ERR_STREAM_DESTROYED('write');
885888
}
886889

890+
if (!msg._hasBody) {
891+
if (msg[kRejectNonStandardBodyWrites]) {
892+
throw new ERR_HTTP_BODY_NOT_ALLOWED();
893+
} else {
894+
debug('This type of response MUST NOT have a body. ' +
895+
'Ignoring write() calls.');
896+
process.nextTick(callback);
897+
return true;
898+
}
899+
}
900+
887901
if (err) {
888902
if (!msg.destroyed) {
889903
onError(msg, err, callback);
@@ -916,13 +930,6 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
916930
msg._implicitHeader();
917931
}
918932

919-
if (!msg._hasBody) {
920-
debug('This type of response MUST NOT have a body. ' +
921-
'Ignoring write() calls.');
922-
process.nextTick(callback);
923-
return true;
924-
}
925-
926933
if (!fromEnd && msg.socket && !msg.socket.writableCorked) {
927934
msg.socket.cork();
928935
process.nextTick(connectionCorkNT, msg.socket);

lib/_http_server.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,14 @@ function storeHTTPOptions(options) {
487487
validateBoolean(joinDuplicateHeaders, 'options.joinDuplicateHeaders');
488488
}
489489
this.joinDuplicateHeaders = joinDuplicateHeaders;
490+
491+
const rejectNonStandardBodyWrites = options.rejectNonStandardBodyWrites;
492+
if (rejectNonStandardBodyWrites !== undefined) {
493+
validateBoolean(rejectNonStandardBodyWrites, 'options.rejectNonStandardBodyWrites');
494+
this.rejectNonStandardBodyWrites = rejectNonStandardBodyWrites;
495+
} else {
496+
this.rejectNonStandardBodyWrites = false;
497+
}
490498
}
491499

492500
function setupConnectionsTracking(server) {
@@ -1023,7 +1031,11 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
10231031
}
10241032
}
10251033

1026-
const res = new server[kServerResponse](req, { highWaterMark: socket.writableHighWaterMark });
1034+
const res = new server[kServerResponse](req,
1035+
{
1036+
highWaterMark: socket.writableHighWaterMark,
1037+
rejectNonStandardBodyWrites: server.rejectNonStandardBodyWrites,
1038+
});
10271039
res._keepAliveTimeout = server.keepAliveTimeout;
10281040
res._maxRequestsPerSocket = server.maxRequestsPerSocket;
10291041
res._onPendingData = updateOutgoingData.bind(undefined,

lib/http.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ let maxHeaderSize;
5555
* requireHostHeader?: boolean;
5656
* joinDuplicateHeaders?: boolean;
5757
* highWaterMark?: number;
58+
* rejectNonStandardBodyWrites?: boolean;
5859
* }} [opts]
5960
* @param {Function} [requestListener]
6061
* @returns {Server}

lib/internal/errors.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,8 @@ E('ERR_HTTP2_TRAILERS_NOT_READY',
11541154
'Trailing headers cannot be sent until after the wantTrailers event is ' +
11551155
'emitted', Error);
11561156
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.', Error);
1157+
E('ERR_HTTP_BODY_NOT_ALLOWED',
1158+
'Adding content for this request method or response status is not allowed.', Error);
11571159
E('ERR_HTTP_CONTENT_LENGTH_MISMATCH',
11581160
'Response body\'s content-length of %s byte(s) does not match the content-length of %s byte(s) set in header', Error);
11591161
E('ERR_HTTP_HEADERS_SENT',

test/parallel/test-http-head-response-has-no-body-end.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const http = require('http');
2929

3030
const server = http.createServer(function(req, res) {
3131
res.writeHead(200);
32-
res.end('FAIL'); // broken: sends FAIL from hot path.
32+
res.end();
3333
});
3434
server.listen(0);
3535

Lines changed: 102 additions & 0 deletions

0 commit comments

Comments
 (0)