Skip to content

Commit 03a4615

Browse files
committed
If request method is HEAD don't allow writing body bytes.
1 parent e2e3f5c commit 03a4615

File tree

5 files changed

+65
-11
lines changed

5 files changed

+65
-11
lines changed

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: 5 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,
@@ -884,6 +885,10 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
884885
err = new ERR_STREAM_DESTROYED('write');
885886
}
886887

888+
if (!msg._hasBody) {
889+
throw ERR_HTTP_BODY_NOT_ALLOWED()
890+
}
891+
887892
if (err) {
888893
if (!msg.destroyed) {
889894
onError(msg, err, callback);
@@ -916,13 +921,6 @@ function write_(msg, chunk, encoding, callback, fromEnd) {
916921
msg._implicitHeader();
917922
}
918923

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-
926924
if (!fromEnd && msg.socket && !msg.socket.writableCorked) {
927925
msg.socket.cork();
928926
process.nextTick(connectionCorkNT, msg.socket);

lib/internal/errors.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,8 @@ E('ERR_HTTP2_TRAILERS_NOT_READY',
11591159
'Trailing headers cannot be sent until after the wantTrailers event is ' +
11601160
'emitted', Error);
11611161
E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.', Error);
1162+
E('ERR_HTTP_BODY_NOT_ALLOWED',
1163+
'Adding content for this request method or response status is not allowed.', Error);
11621164
E('ERR_HTTP_CONTENT_LENGTH_MISMATCH',
11631165
'Response body\'s content-length of %s byte(s) does not match the content-length of %s byte(s) set in header', Error);
11641166
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: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
'use strict';
23+
const common = require('../common');
24+
const assert = require('assert');
25+
const http = require('http');
26+
27+
28+
const server = http.createServer((req, res) => {
29+
res.writeHead(204);
30+
assert.throws(() => {
31+
res.write('this is content');
32+
}, {
33+
code: 'ERR_HTTP_BODY_NOT_ALLOWED',
34+
name: 'Error',
35+
message: 'Adding content for this request method or response status is not allowed.'
36+
})
37+
res.end()
38+
});
39+
server.listen(0);
40+
41+
server.on('listening', common.mustCall(function() {
42+
const req = http.request({
43+
port: this.address().port,
44+
method: 'GET',
45+
path: '/'
46+
}, common.mustCall((res) => {
47+
res.resume()
48+
res.on('end', common.mustCall(function() {
49+
server.close();
50+
}));
51+
}));
52+
req.end();
53+
}));

0 commit comments

Comments
 (0)