From 8c4c5fcb61bd0b3bcbf6c8141ce2ea12db10fd01 Mon Sep 17 00:00:00 2001 From: qwerzl Date: Wed, 22 Jan 2025 00:06:26 +0800 Subject: [PATCH 1/5] http: return `Content-Length` header for `HEAD`s In the current http library, all responses without body will not return the Content-Length header. Fixes: https://github.com/nodejs/node/issues/56680 --- lib/_http_outgoing.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 23b850d1522c97..acc8ba08e7a577 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -547,7 +547,7 @@ function _storeHeader(firstLine, headers) { } if (!state.contLen && !state.te) { - if (!this._hasBody) { + if (!this._hasBody && this.req.method !== 'HEAD') { // Make sure we don't end the 0\r\n\r\n at the end of the message. this.chunkedEncoding = false; } else if (!this.useChunkedEncodingByDefault) { @@ -932,7 +932,7 @@ function strictContentLength(msg) { return ( msg.strictContentLength && msg._contentLength != null && - msg._hasBody && + (msg._hasBody || msg.req.method === 'HEAD') && !msg._removedContLen && !msg.chunkedEncoding && !msg.hasHeader('transfer-encoding') From f129867d485843cb76b7440f96330fc67a386580 Mon Sep 17 00:00:00 2001 From: qwerzl Date: Wed, 22 Jan 2025 10:34:26 +0800 Subject: [PATCH 2/5] test: make sure content-length headers are sent for HEADs --- ...test-http-head-response-has-no-body-end-implicit-headers.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js b/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js index 5ebd9f8a90bd92..fded2c8d079047 100644 --- a/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js +++ b/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js @@ -1,6 +1,7 @@ 'use strict'; const common = require('../common'); const http = require('http'); +const assert = require('assert'); // This test is to make sure that when the HTTP server // responds to a HEAD request with data to res.end, @@ -18,6 +19,8 @@ server.on('listening', common.mustCall(function() { method: 'HEAD', path: '/' }, common.mustCall(function(res) { + assert(res.headers['content-length'] !== undefined, 'Content-Length header is missing'); + res.on('end', common.mustCall(function() { server.close(); })); From ece78770acde15d2781b8827153903009d2124f0 Mon Sep 17 00:00:00 2001 From: Tom Tang Date: Wed, 22 Jan 2025 20:10:57 +0800 Subject: [PATCH 3/5] test: fix assertion style issues Co-authored-by: Livia Medeiros --- .../test-http-head-response-has-no-body-end-implicit-headers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js b/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js index fded2c8d079047..69d765caf8ce23 100644 --- a/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js +++ b/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js @@ -19,7 +19,7 @@ server.on('listening', common.mustCall(function() { method: 'HEAD', path: '/' }, common.mustCall(function(res) { - assert(res.headers['content-length'] !== undefined, 'Content-Length header is missing'); + assert.notStrictEqual(res.headers['content-length'], undefined, 'Expected Content-Length header to be present'); res.on('end', common.mustCall(function() { server.close(); From 645845970823f2b5e115dd43414f65b65073c69e Mon Sep 17 00:00:00 2001 From: qwerzl Date: Thu, 23 Jan 2025 00:31:22 +0800 Subject: [PATCH 4/5] test: test for raw HEAD responses --- .../test-http-head-response-correct-raw.js | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/parallel/test-http-head-response-correct-raw.js diff --git a/test/parallel/test-http-head-response-correct-raw.js b/test/parallel/test-http-head-response-correct-raw.js new file mode 100644 index 00000000000000..384b39ffe35422 --- /dev/null +++ b/test/parallel/test-http-head-response-correct-raw.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const http = require('http'); +const assert = require('assert'); +const net = require('net'); + +// This test is to make sure that when the HTTP server +// responds to a HEAD request, it returns the correct +// raw response. + +// Body that should be sent if the request were GET. +const body = 'Test body'; + +// Regex to test that the raw response is correct. +// Date can change. +const regex = new RegExp( + `HTTP/1[.]1 200 OK\\r\\nContent-Type: text/plain\\r\\nDate: .+\\r\\nConnection: keep-alive\\r\\nKeep-Alive: timeout=5\\r\\nContent-Length: ${body.length}\\r\\n\\r\\n` +); + +const server = http.createServer(function(req, res) { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end(body); +}); + +server.listen(0); + +server.on('listening', common.mustCall(function() { + const s = new net.Socket(); + s.connect(this.address().port, this.address().host); + s.write(`HEAD / HTTP/1.1\r\nHost: ${this.address()}:${this.address().port}\r\n\r\n`); + s.on('data', function(d) { + assert(regex.test(d.toString())); + s.end(); + server.close(); + }); +})); From 0f4470f5d0836bbb60c4635d383f47b7870279a8 Mon Sep 17 00:00:00 2001 From: qwerzl Date: Thu, 23 Jan 2025 00:31:53 +0800 Subject: [PATCH 5/5] test: test for actual content-length instead of undefined --- ...t-http-head-response-has-no-body-end-implicit-headers.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js b/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js index 69d765caf8ce23..9911be47711fb7 100644 --- a/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js +++ b/test/parallel/test-http-head-response-has-no-body-end-implicit-headers.js @@ -19,7 +19,11 @@ server.on('listening', common.mustCall(function() { method: 'HEAD', path: '/' }, common.mustCall(function(res) { - assert.notStrictEqual(res.headers['content-length'], undefined, 'Expected Content-Length header to be present'); + assert.strictEqual( + res.headers['content-length'], + '4', + new Error('Expected Content-Length header to be of length 4') + ); res.on('end', common.mustCall(function() { server.close();