From 6464cd6b2e0c1556f42a40d5e74602af04a62ae9 Mon Sep 17 00:00:00 2001 From: wwwzbwcom Date: Fri, 9 Jul 2021 15:59:35 +0800 Subject: [PATCH 01/14] http: server check Host header, to meet RFC 7230 5.4 requirement --- lib/_http_server.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/_http_server.js b/lib/_http_server.js index 1783a4073706..e0c9e8a42a53 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -1021,6 +1021,18 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { req, res, socket, state, server)); let handled = false; + const isHttp11 = (req.httpVersionMajor === 1 && req.httpVersionMinor === 1); + + // From RFC 2730 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 + // A server MUST respond with a 400 (Bad Request) status code to any + // HTTP/1.1 request message that lacks a Host header field and to any + // request message that contains more than one Host header field or a + // Host header field with an invalid field-value. + if (req.headers.host === undefined && isHttp11) { + res.writeHead(400); + res.end(); + return 0; + } if (req.httpVersionMajor === 1 && req.httpVersionMinor === 1) { const isRequestsLimitSet = ( From cb11d613a7e2ba85d05d637878e1fe1fffc1b43a Mon Sep 17 00:00:00 2001 From: wwwzbwcom Date: Wed, 14 Jul 2021 17:34:49 +0800 Subject: [PATCH 02/14] http: fix all test after adding Host requirement in server --- test/parallel/test-http-chunked-304.js | 2 +- test/parallel/test-http-client-headers-array.js | 17 +++++++++++++---- test/parallel/test-http-content-length.js | 6 +++--- test/parallel/test-http-header-badrequest.js | 2 +- .../test-http-insecure-parser-per-stream.js | 4 ++++ test/parallel/test-http-insecure-parser.js | 1 + test/parallel/test-http-malformed-request.js | 2 +- .../test-http-max-header-size-per-stream.js | 1 + test/parallel/test-http-max-headers-count.js | 8 +++++--- .../test-http-pipeline-assertionerror-finish.js | 2 +- ...st-http-pipeline-requests-connection-leak.js | 2 +- ...est-http-pipeline-socket-parser-typeerror.js | 2 +- ...test-http-req-close-robust-from-tampering.js | 1 + test/parallel/test-http-response-splitting.js | 2 +- ...-http-server-request-timeout-delayed-body.js | 1 + ...p-server-request-timeout-interrupted-body.js | 1 + ...est-http-server-request-timeout-keepalive.js | 2 +- test/parallel/test-http-server-unconsume.js | 2 +- test/parallel/test-http-server.js | 13 +++++++++---- test/parallel/test-http-set-trailers.js | 2 +- test/parallel/test-http-status-message.js | 5 ++++- test/parallel/test-http-upgrade-server.js | 4 +++- 22 files changed, 56 insertions(+), 26 deletions(-) diff --git a/test/parallel/test-http-chunked-304.js b/test/parallel/test-http-chunked-304.js index b94d776595f8..8328642fc737 100644 --- a/test/parallel/test-http-chunked-304.js +++ b/test/parallel/test-http-chunked-304.js @@ -47,7 +47,7 @@ function test(statusCode) { const conn = net.createConnection( server.address().port, common.mustCall(() => { - conn.write('GET / HTTP/1.1\r\n\r\n'); + conn.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); let resp = ''; conn.setEncoding('utf8'); diff --git a/test/parallel/test-http-client-headers-array.js b/test/parallel/test-http-client-headers-array.js index d7e8d39c3c25..4b78256bcaf8 100644 --- a/test/parallel/test-http-client-headers-array.js +++ b/test/parallel/test-http-client-headers-array.js @@ -10,7 +10,8 @@ function execute(options) { const expectHeaders = { 'x-foo': 'boom', 'cookie': 'a=1; b=2; c=3', - 'connection': 'keep-alive' + 'connection': 'keep-alive', + 'host': 'example.com', }; // no Host header when you set headers an array @@ -43,13 +44,20 @@ function execute(options) { // Should be the same except for implicit Host header on the first two execute({ headers: { 'x-foo': 'boom', 'cookie': 'a=1; b=2; c=3' } }); execute({ headers: { 'x-foo': 'boom', 'cookie': [ 'a=1', 'b=2', 'c=3' ] } }); -execute({ headers: [[ 'x-foo', 'boom' ], [ 'cookie', 'a=1; b=2; c=3' ]] }); execute({ headers: [ - [ 'x-foo', 'boom' ], [ 'cookie', [ 'a=1', 'b=2', 'c=3' ]], + [ 'x-foo', 'boom' ], + [ 'cookie', 'a=1; b=2; c=3' ], + [ 'Host', 'example.com' ], +] }); +execute({ headers: [ + [ 'x-foo', 'boom' ], + [ 'cookie', [ 'a=1', 'b=2', 'c=3' ]], + [ 'Host', 'example.com' ], ] }); execute({ headers: [ [ 'x-foo', 'boom' ], [ 'cookie', 'a=1' ], - [ 'cookie', 'b=2' ], [ 'cookie', 'c=3'], + [ 'cookie', 'b=2' ], [ 'cookie', 'c=3' ], + [ 'Host', 'example.com'], ] }); // Authorization and Host header both missing from the second @@ -58,4 +66,5 @@ execute({ auth: 'foo:bar', headers: execute({ auth: 'foo:bar', headers: [ [ 'x-foo', 'boom' ], [ 'cookie', 'a=1' ], [ 'cookie', 'b=2' ], [ 'cookie', 'c=3'], + [ 'Host', 'example.com'], ] }); diff --git a/test/parallel/test-http-content-length.js b/test/parallel/test-http-content-length.js index 193ee5a8831e..eca32894f573 100644 --- a/test/parallel/test-http-content-length.js +++ b/test/parallel/test-http-content-length.js @@ -28,15 +28,18 @@ const server = http.createServer(function(req, res) { switch (req.url.substr(1)) { case 'multiple-writes': + delete req.headers.host; assert.deepStrictEqual(req.headers, expectedHeadersMultipleWrites); res.write('hello'); res.end('world'); break; case 'end-with-data': + delete req.headers.host; assert.deepStrictEqual(req.headers, expectedHeadersEndWithData); res.end('hello world'); break; case 'empty': + delete req.headers.host; assert.deepStrictEqual(req.headers, expectedHeadersEndNoData); res.end(); break; @@ -56,7 +59,6 @@ server.listen(0, function() { path: '/multiple-writes' }); req.removeHeader('Date'); - req.removeHeader('Host'); req.write('hello '); req.end('world'); req.on('response', function(res) { @@ -70,7 +72,6 @@ server.listen(0, function() { path: '/end-with-data' }); req.removeHeader('Date'); - req.removeHeader('Host'); req.end('hello world'); req.on('response', function(res) { assert.deepStrictEqual(res.headers, { ...expectedHeadersEndWithData, 'keep-alive': 'timeout=1' }); @@ -83,7 +84,6 @@ server.listen(0, function() { path: '/empty' }); req.removeHeader('Date'); - req.removeHeader('Host'); req.end(); req.on('response', function(res) { assert.deepStrictEqual(res.headers, { ...expectedHeadersEndNoData, 'keep-alive': 'timeout=1' }); diff --git a/test/parallel/test-http-header-badrequest.js b/test/parallel/test-http-header-badrequest.js index 8294ca9fe208..1e6fb6e32164 100644 --- a/test/parallel/test-http-header-badrequest.js +++ b/test/parallel/test-http-header-badrequest.js @@ -17,7 +17,7 @@ server.listen(0, mustCall(() => { let received = ''; c.on('connect', mustCall(() => { - c.write('GET /blah HTTP/1.1\r\n\r\n'); + c.write('GET /blah HTTP/1.1\r\nHost: example.com\r\n\r\n'); })); c.on('data', mustCall((data) => { received += data.toString(); diff --git a/test/parallel/test-http-insecure-parser-per-stream.js b/test/parallel/test-http-insecure-parser-per-stream.js index 2ba245c8f419..daee5811724e 100644 --- a/test/parallel/test-http-insecure-parser-per-stream.js +++ b/test/parallel/test-http-insecure-parser-per-stream.js @@ -22,6 +22,7 @@ const MakeDuplexPair = require('../common/duplexpair'); serverSide.resume(); // Dump the request serverSide.end('HTTP/1.1 200 OK\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + 'Content-Length: 0\r\n' + '\r\n\r\n'); @@ -39,6 +40,7 @@ const MakeDuplexPair = require('../common/duplexpair'); serverSide.resume(); // Dump the request serverSide.end('HTTP/1.1 200 OK\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + 'Content-Length: 0\r\n' + '\r\n\r\n'); @@ -62,6 +64,7 @@ const MakeDuplexPair = require('../common/duplexpair'); server.emit('connection', serverSide); clientSide.write('GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + '\r\n\r\n'); } @@ -77,6 +80,7 @@ const MakeDuplexPair = require('../common/duplexpair'); server.emit('connection', serverSide); clientSide.write('GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + '\r\n\r\n'); } diff --git a/test/parallel/test-http-insecure-parser.js b/test/parallel/test-http-insecure-parser.js index c9a3aa23c142..7fad29f3eec4 100644 --- a/test/parallel/test-http-insecure-parser.js +++ b/test/parallel/test-http-insecure-parser.js @@ -19,6 +19,7 @@ server.listen(0, common.mustCall(function() { client.write( 'GET / HTTP/1.1\r\n' + 'Content-Type: text/te\x08t\r\n' + + 'Host: example.com' + 'Connection: close\r\n\r\n'); } ); diff --git a/test/parallel/test-http-malformed-request.js b/test/parallel/test-http-malformed-request.js index 781c629bfacd..1f2fecc11750 100644 --- a/test/parallel/test-http-malformed-request.js +++ b/test/parallel/test-http-malformed-request.js @@ -41,7 +41,7 @@ server.listen(0); server.on('listening', function() { const c = net.createConnection(this.address().port); c.on('connect', function() { - c.write('GET /hello?foo=%99bar HTTP/1.1\r\n\r\n'); + c.write('GET /hello?foo=%99bar HTTP/1.1\r\nHost: example.com\r\n\r\n'); c.end(); }); }); diff --git a/test/parallel/test-http-max-header-size-per-stream.js b/test/parallel/test-http-max-header-size-per-stream.js index 5edb8d3a954c..c043fce21a84 100644 --- a/test/parallel/test-http-max-header-size-per-stream.js +++ b/test/parallel/test-http-max-header-size-per-stream.js @@ -62,6 +62,7 @@ const MakeDuplexPair = require('../common/duplexpair'); server.emit('connection', serverSide); clientSide.write('GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + '\r\n\r\n'); } diff --git a/test/parallel/test-http-max-headers-count.js b/test/parallel/test-http-max-headers-count.js index 94685b228845..f5c807a846fe 100644 --- a/test/parallel/test-http-max-headers-count.js +++ b/test/parallel/test-http-max-headers-count.js @@ -27,7 +27,9 @@ const http = require('http'); let requests = 0; let responses = 0; -const headers = {}; +const headers = { + host: 'example.com' +}; const N = 100; for (let i = 0; i < N; ++i) { headers[`key${i}`] = i; @@ -56,8 +58,8 @@ server.maxHeadersCount = max; server.listen(0, function() { const maxAndExpected = [ // for client [20, 20], - [1200, 103], - [0, N + 3], // Connection, Date and Transfer-Encoding + [1200, 104], + [0, N + 4], // Connection, Date and Transfer-Encoding ]; doRequest(); diff --git a/test/parallel/test-http-pipeline-assertionerror-finish.js b/test/parallel/test-http-pipeline-assertionerror-finish.js index c82161bf00e2..7721cb7fea3b 100644 --- a/test/parallel/test-http-pipeline-assertionerror-finish.js +++ b/test/parallel/test-http-pipeline-assertionerror-finish.js @@ -27,7 +27,7 @@ const server = http .listen(0, function() { const s = net.connect(this.address().port); - const big = 'GET / HTTP/1.1\r\n\r\n'.repeat(COUNT); + const big = 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'.repeat(COUNT); s.write(big); s.resume(); diff --git a/test/parallel/test-http-pipeline-requests-connection-leak.js b/test/parallel/test-http-pipeline-requests-connection-leak.js index aa5db56f7786..cfbcef82b2c0 100644 --- a/test/parallel/test-http-pipeline-requests-connection-leak.js +++ b/test/parallel/test-http-pipeline-requests-connection-leak.js @@ -26,7 +26,7 @@ const server = http }); }) .listen(0, function() { - const req = 'GET / HTTP/1.1\r\n\r\n'.repeat(COUNT); + const req = 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'.repeat(COUNT); client = net.connect(this.address().port, function() { client.write(req); }); diff --git a/test/parallel/test-http-pipeline-socket-parser-typeerror.js b/test/parallel/test-http-pipeline-socket-parser-typeerror.js index 8987b822c264..e092154b971f 100644 --- a/test/parallel/test-http-pipeline-socket-parser-typeerror.js +++ b/test/parallel/test-http-pipeline-socket-parser-typeerror.js @@ -50,7 +50,7 @@ const server = http .listen(0, () => { const s = net.connect(server.address().port); more = () => { - s.write('GET / HTTP/1.1\r\n\r\n'); + s.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); }; done = () => { s.write( diff --git a/test/parallel/test-http-req-close-robust-from-tampering.js b/test/parallel/test-http-req-close-robust-from-tampering.js index 46ae0c0e2971..edfdb309a7e4 100644 --- a/test/parallel/test-http-req-close-robust-from-tampering.js +++ b/test/parallel/test-http-req-close-robust-from-tampering.js @@ -17,6 +17,7 @@ server.listen(0, common.mustCall(() => { const req = [ 'POST / HTTP/1.1', + 'Host: example.com', 'Content-Length: 11', '', 'hello world', diff --git a/test/parallel/test-http-response-splitting.js b/test/parallel/test-http-response-splitting.js index 88f94e6658b1..25d8348dd9a7 100644 --- a/test/parallel/test-http-response-splitting.js +++ b/test/parallel/test-http-response-splitting.js @@ -53,7 +53,7 @@ const server = http.createServer((req, res) => { res.end('ok'); }); server.listen(0, () => { - const end = 'HTTP/1.1\r\n\r\n'; + const end = 'HTTP/1.1\r\nHost: example.com\r\n\r\n'; const client = net.connect({ port: server.address().port }, () => { client.write(`GET ${str} ${end}`); client.write(`GET / ${end}`); diff --git a/test/parallel/test-http-server-request-timeout-delayed-body.js b/test/parallel/test-http-server-request-timeout-delayed-body.js index ac2120ef3937..fdfbbb2f4cb4 100644 --- a/test/parallel/test-http-server-request-timeout-delayed-body.js +++ b/test/parallel/test-http-server-request-timeout-delayed-body.js @@ -47,6 +47,7 @@ server.listen(0, common.mustCall(() => { client.resume(); client.write('POST / HTTP/1.1\r\n'); + client.write('Host: example.com\r\n'); client.write('Content-Length: 20\r\n'); client.write('Connection: close\r\n'); client.write('\r\n'); diff --git a/test/parallel/test-http-server-request-timeout-interrupted-body.js b/test/parallel/test-http-server-request-timeout-interrupted-body.js index aa4843a48373..82c2f3171736 100644 --- a/test/parallel/test-http-server-request-timeout-interrupted-body.js +++ b/test/parallel/test-http-server-request-timeout-interrupted-body.js @@ -58,6 +58,7 @@ server.listen(0, common.mustCall(() => { client.resume(); client.write('POST / HTTP/1.1\r\n'); + client.write('Host: example.com\r\n'); client.write('Content-Length: 20\r\n'); client.write('Connection: close\r\n'); client.write('\r\n'); diff --git a/test/parallel/test-http-server-request-timeout-keepalive.js b/test/parallel/test-http-server-request-timeout-keepalive.js index a3f9bccc8dea..bc7384b53411 100644 --- a/test/parallel/test-http-server-request-timeout-keepalive.js +++ b/test/parallel/test-http-server-request-timeout-keepalive.js @@ -12,7 +12,7 @@ const { connect } = require('net'); function performRequestWithDelay(client, firstDelay, secondDelay, closeAfter) { client.resume(); - client.write('GET / HTTP/1.1\r\n'); + client.write('GET / HTTP/1.1\r\nHost: example.com\r\n'); setTimeout(() => { client.write('Connection: '); diff --git a/test/parallel/test-http-server-unconsume.js b/test/parallel/test-http-server-unconsume.js index a8a307e53b5a..e19fd8b3875a 100644 --- a/test/parallel/test-http-server-unconsume.js +++ b/test/parallel/test-http-server-unconsume.js @@ -18,7 +18,7 @@ const net = require('net'); server.close(); }).listen(0, function() { const socket = net.connect(this.address().port, function() { - socket.write('PUT / HTTP/1.1\r\n\r\n'); + socket.write('PUT / HTTP/1.1\r\nHost: example.com\r\n\r\n'); socket.once('data', function() { socket.end('hello world'); diff --git a/test/parallel/test-http-server.js b/test/parallel/test-http-server.js index fcbb8cd4b917..a26912c409a2 100644 --- a/test/parallel/test-http-server.js +++ b/test/parallel/test-http-server.js @@ -89,7 +89,9 @@ server.on('listening', function() { c.setEncoding('utf8'); c.on('connect', function() { - c.write('GET /hello?hello=world&foo=b==ar HTTP/1.1\r\n\r\n'); + c.write( + 'GET /hello?hello=world&foo=b==ar HTTP/1.1\r\n' + + 'Host: example.com\r\n\r\n'); requests_sent += 1; }); @@ -97,13 +99,16 @@ server.on('listening', function() { server_response += chunk; if (requests_sent === 1) { - c.write('POST /quit HTTP/1.1\r\n\r\n'); + c.write( + 'POST /quit HTTP/1.1\r\n' + + 'Host: example.com\r\n\r\n' + ); requests_sent += 1; } if (requests_sent === 2) { - c.write('GET / HTTP/1.1\r\nX-X: foo\r\n\r\n' + - 'GET / HTTP/1.1\r\nX-X: bar\r\n\r\n'); + c.write('GET / HTTP/1.1\r\nX-X: foo\r\nHost: example.com\r\n\r\n' + + 'GET / HTTP/1.1\r\nX-X: bar\r\nHost: example.com\r\n\r\n'); // Note: we are making the connection half-closed here // before we've gotten the response from the server. This // is a pretty bad thing to do and not really supported diff --git a/test/parallel/test-http-set-trailers.js b/test/parallel/test-http-set-trailers.js index 17a936f528e9..26b6c275588d 100644 --- a/test/parallel/test-http-set-trailers.js +++ b/test/parallel/test-http-set-trailers.js @@ -59,7 +59,7 @@ function testHttp11(port, callback) { let tid; c.on('connect', function() { - c.write('GET / HTTP/1.1\r\n\r\n'); + c.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); tid = setTimeout(common.mustNotCall(), 2000, 'Couldn\'t find last chunk.'); }); diff --git a/test/parallel/test-http-status-message.js b/test/parallel/test-http-status-message.js index 29b31a35a570..bdb667ca4448 100644 --- a/test/parallel/test-http-status-message.js +++ b/test/parallel/test-http-status-message.js @@ -38,7 +38,10 @@ function test() { const client = net.connect( this.address().port, function() { - client.write('GET / HTTP/1.1\r\nConnection: close\r\n\r\n'); + client.write( + 'GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + + 'Connection: close\r\n\r\n'); } ); client.on('data', function(chunk) { diff --git a/test/parallel/test-http-upgrade-server.js b/test/parallel/test-http-upgrade-server.js index 5a660c4072a9..b04d488b88a4 100644 --- a/test/parallel/test-http-upgrade-server.js +++ b/test/parallel/test-http-upgrade-server.js @@ -87,6 +87,7 @@ function test_upgrade_with_listener() { conn.on('connect', function() { writeReq(conn, 'GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + '\r\n' + @@ -124,6 +125,7 @@ function test_upgrade_no_listener() { conn.on('connect', function() { writeReq(conn, 'GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + '\r\n'); @@ -146,7 +148,7 @@ function test_standard_http() { conn.setEncoding('utf8'); conn.on('connect', function() { - writeReq(conn, 'GET / HTTP/1.1\r\n\r\n'); + writeReq(conn, 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); }); conn.once('data', function(data) { From 14f23b89b9d0fbf415689526d98926eba8652a3f Mon Sep 17 00:00:00 2001 From: wwwzbwcom Date: Wed, 14 Jul 2021 19:25:28 +0800 Subject: [PATCH 03/14] http: fix more test, include new test and https --- test/parallel/test-http.js | 4 +++- test/parallel/test-https-agent-create-connection.js | 2 +- test/parallel/test-https-insecure-parse-per-stream.js | 4 ++++ test/parallel/test-https-max-header-size-per-stream.js | 4 ++++ test/parallel/test-https-max-headers-count.js | 8 +++++--- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/test/parallel/test-http.js b/test/parallel/test-http.js index 1d5b74b87975..3c1b28b02097 100644 --- a/test/parallel/test-http.js +++ b/test/parallel/test-http.js @@ -113,7 +113,9 @@ server.on('listening', () => { path: '/world', headers: [ ['Cookie', 'abc=123'], ['Cookie', 'def=456'], - ['Cookie', 'ghi=789'] ], + ['Cookie', 'ghi=789'], + ['Host', 'example.com'], + ], agent: agent }, common.mustCall((res) => { const cookieHeaders = req._header.match(/^Cookie: .+$/img); diff --git a/test/parallel/test-https-agent-create-connection.js b/test/parallel/test-https-agent-create-connection.js index 1ac2c840b0d5..c053b7f24b80 100644 --- a/test/parallel/test-https-agent-create-connection.js +++ b/test/parallel/test-https-agent-create-connection.js @@ -22,7 +22,7 @@ const expectCertError = /^Error: unable to verify the first certificate$/; const checkRequest = (socket, server) => { let result = ''; socket.on('connect', common.mustCall((data) => { - socket.write('GET / HTTP/1.1\r\n\r\n'); + socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); socket.end(); })); socket.on('data', common.mustCall((chunk) => { diff --git a/test/parallel/test-https-insecure-parse-per-stream.js b/test/parallel/test-https-insecure-parse-per-stream.js index 645fbcf26376..26af3bc6fe22 100644 --- a/test/parallel/test-https-insecure-parse-per-stream.js +++ b/test/parallel/test-https-insecure-parse-per-stream.js @@ -37,6 +37,7 @@ const certFixture = { serverSide.resume(); // Dump the request serverSide.end('HTTP/1.1 200 OK\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + 'Content-Length: 0\r\n' + '\r\n\r\n'); @@ -55,6 +56,7 @@ const certFixture = { serverSide.resume(); // Dump the request serverSide.end('HTTP/1.1 200 OK\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + 'Content-Length: 0\r\n' + '\r\n\r\n'); @@ -81,6 +83,7 @@ const certFixture = { }); client.write( 'GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + '\r\n\r\n'); client.end(); @@ -107,6 +110,7 @@ const certFixture = { }); client.write( 'GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + '\r\n\r\n'); client.end(); diff --git a/test/parallel/test-https-max-header-size-per-stream.js b/test/parallel/test-https-max-header-size-per-stream.js index 796af5e6e087..f10fced777c2 100644 --- a/test/parallel/test-https-max-header-size-per-stream.js +++ b/test/parallel/test-https-max-header-size-per-stream.js @@ -38,6 +38,7 @@ const certFixture = { serverSide.resume(); // Dump the request serverSide.end('HTTP/1.1 200 OK\r\n' + + 'Host: example.com\r\n' + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + 'Content-Length: 0\r\n' + '\r\n\r\n'); @@ -55,6 +56,7 @@ const certFixture = { serverSide.resume(); // Dump the request serverSide.end('HTTP/1.1 200 OK\r\n' + + 'Host: example.com\r\n' + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + 'Content-Length: 0\r\n' + '\r\n\r\n'); @@ -81,6 +83,7 @@ const certFixture = { }); client.write( 'GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + '\r\n\r\n'); client.end(); @@ -107,6 +110,7 @@ const certFixture = { }); client.write( 'GET / HTTP/1.1\r\n' + + 'Host: example.com\r\n' + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + '\r\n\r\n'); client.end(); diff --git a/test/parallel/test-https-max-headers-count.js b/test/parallel/test-https-max-headers-count.js index 0fdd38c73ed6..42614b15b40f 100644 --- a/test/parallel/test-https-max-headers-count.js +++ b/test/parallel/test-https-max-headers-count.js @@ -16,7 +16,9 @@ const serverOptions = { let requests = 0; let responses = 0; -const headers = {}; +const headers = { + host: 'example.com' +}; const N = 100; for (let i = 0; i < N; ++i) { headers[`key${i}`] = i; @@ -45,8 +47,8 @@ server.maxHeadersCount = max; server.listen(0, common.mustCall(() => { const maxAndExpected = [ // for client [20, 20], - [1200, 103], - [0, N + 3], // Connection, Date and Transfer-Encoding + [1200, 104], + [0, N + 4], // Connection, Date and Transfer-Encoding ]; const doRequest = common.mustCall(() => { const max = maxAndExpected[responses][0]; From 3b12797c756f587fdf980920291680ac68c78a30 Mon Sep 17 00:00:00 2001 From: wwwzbwcom Date: Thu, 15 Jul 2021 13:13:17 +0800 Subject: [PATCH 04/14] https: fix test-https-host-headers --- test/parallel/test-http.js | 2 +- test/parallel/test-https-host-headers.js | 2 +- test/parallel/test-https-insecure-parse-per-stream.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-http.js b/test/parallel/test-http.js index 3c1b28b02097..366fb42d1569 100644 --- a/test/parallel/test-http.js +++ b/test/parallel/test-http.js @@ -115,7 +115,7 @@ server.on('listening', () => { ['Cookie', 'def=456'], ['Cookie', 'ghi=789'], ['Host', 'example.com'], - ], + ], agent: agent }, common.mustCall((res) => { const cookieHeaders = req._header.match(/^Cookie: .+$/img); diff --git a/test/parallel/test-https-host-headers.js b/test/parallel/test-https-host-headers.js index b8dcda320fdd..326294293018 100644 --- a/test/parallel/test-https-host-headers.js +++ b/test/parallel/test-https-host-headers.js @@ -25,7 +25,7 @@ const httpsServer = https.createServer({ } res.writeHead(200, {}); res.end('ok'); -}, 9)).listen(0, common.mustCall(function(err) { +}, 6)).listen(0, common.mustCall(function(err) { debug(`test https server listening on port ${this.address().port}`); assert.ifError(err); https.get({ diff --git a/test/parallel/test-https-insecure-parse-per-stream.js b/test/parallel/test-https-insecure-parse-per-stream.js index 26af3bc6fe22..73e69d056913 100644 --- a/test/parallel/test-https-insecure-parse-per-stream.js +++ b/test/parallel/test-https-insecure-parse-per-stream.js @@ -56,7 +56,7 @@ const certFixture = { serverSide.resume(); // Dump the request serverSide.end('HTTP/1.1 200 OK\r\n' + - 'Host: example.com\r\n' + + 'Host: example.com\r\n' + 'Hello: foo\x08foo\r\n' + 'Content-Length: 0\r\n' + '\r\n\r\n'); From ca6387c49f3b96419db1e026f057379c7c033191 Mon Sep 17 00:00:00 2001 From: wwwzbwcom Date: Thu, 15 Jul 2021 14:00:49 +0800 Subject: [PATCH 05/14] http: remove extra comment --- lib/_http_server.js | 24 +++++++++++------------- test/parallel/test-https-host-headers.js | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index e0c9e8a42a53..8819b69c6556 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -1021,20 +1021,10 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { req, res, socket, state, server)); let handled = false; - const isHttp11 = (req.httpVersionMajor === 1 && req.httpVersionMinor === 1); - // From RFC 2730 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 - // A server MUST respond with a 400 (Bad Request) status code to any - // HTTP/1.1 request message that lacks a Host header field and to any - // request message that contains more than one Host header field or a - // Host header field with an invalid field-value. - if (req.headers.host === undefined && isHttp11) { - res.writeHead(400); - res.end(); - return 0; - } + const isHttp11 = (req.httpVersionMajor === 1 && req.httpVersionMinor === 1); - if (req.httpVersionMajor === 1 && req.httpVersionMinor === 1) { + if (isHttp11) { const isRequestsLimitSet = ( typeof server.maxRequestsPerSocket === 'number' && server.maxRequestsPerSocket > 0 @@ -1055,9 +1045,17 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { } else if (req.headers.expect !== undefined) { handled = true; + // From RFC 2730 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 + // A server MUST respond with a 400 (Bad Request) status code to any + // HTTP/1.1 request message that lacks a Host header field + if (req.headers.host === undefined && isHttp11) { + res.writeHead(400); + res.end(); + return 0; + } + if (RegExpPrototypeExec(continueExpression, req.headers.expect) !== null) { res._expect_continue = true; - if (server.listenerCount('checkContinue') > 0) { server.emit('checkContinue', req, res); } else { diff --git a/test/parallel/test-https-host-headers.js b/test/parallel/test-https-host-headers.js index 326294293018..b8dcda320fdd 100644 --- a/test/parallel/test-https-host-headers.js +++ b/test/parallel/test-https-host-headers.js @@ -25,7 +25,7 @@ const httpsServer = https.createServer({ } res.writeHead(200, {}); res.end('ok'); -}, 6)).listen(0, common.mustCall(function(err) { +}, 9)).listen(0, common.mustCall(function(err) { debug(`test https server listening on port ${this.address().port}`); assert.ifError(err); https.get({ From 1945900d7ebdb3cfbeec428ac5f81512011bfc2f Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Wed, 23 Nov 2022 14:38:15 +0100 Subject: [PATCH 06/14] fix: typo Co-authored-by: Luigi Pinca --- lib/_http_server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index 8819b69c6556..db2ca3deb81a 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -1045,7 +1045,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { } else if (req.headers.expect !== undefined) { handled = true; - // From RFC 2730 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 + // From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 // A server MUST respond with a 400 (Bad Request) status code to any // HTTP/1.1 request message that lacks a Host header field if (req.headers.host === undefined && isHttp11) { From fdf9822c8a330f7d471fe16ec1f2e70455066502 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Wed, 23 Nov 2022 16:21:41 +0100 Subject: [PATCH 07/14] fix: removed unnecessary http11 check Co-authored-by: mscdex --- lib/_http_server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index db2ca3deb81a..d0a2b12c9885 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -1048,7 +1048,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { // From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 // A server MUST respond with a 400 (Bad Request) status code to any // HTTP/1.1 request message that lacks a Host header field - if (req.headers.host === undefined && isHttp11) { + if (req.headers.host === undefined) { res.writeHead(400); res.end(); return 0; From bf49f88439fb23512fe1982a90db1999afc67468 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Wed, 23 Nov 2022 17:37:44 +0100 Subject: [PATCH 08/14] fix: removed variable --- lib/_http_server.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index d0a2b12c9885..ffd90c29b24d 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -1022,9 +1022,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { let handled = false; - const isHttp11 = (req.httpVersionMajor === 1 && req.httpVersionMinor === 1); - - if (isHttp11) { + if (req.httpVersionMajor === 1 && req.httpVersionMinor === 1) { const isRequestsLimitSet = ( typeof server.maxRequestsPerSocket === 'number' && server.maxRequestsPerSocket > 0 From e3983edb3bdcae4dd02ce2296dd072237019d80c Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Thu, 24 Nov 2022 17:28:23 +0100 Subject: [PATCH 09/14] http: added test and fixed behaviour --- lib/_http_server.js | 20 ++++++++++-------- test/parallel/test-http-max-headers-count.js | 2 +- .../parallel/test-http-request-host-header.js | 21 +++++++++++++++++++ test/parallel/test-https-max-headers-count.js | 2 +- 4 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 test/parallel/test-http-request-host-header.js diff --git a/lib/_http_server.js b/lib/_http_server.js index ffd90c29b24d..ba7dee480102 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -1022,7 +1022,18 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { let handled = false; + if (req.httpVersionMajor === 1 && req.httpVersionMinor === 1) { + + // From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 + // A server MUST respond with a 400 (Bad Request) status code to any + // HTTP/1.1 request message that lacks a Host header field + if (req.headers.host === undefined) { + res.writeHead(400); + res.end(); + return 0; + } + const isRequestsLimitSet = ( typeof server.maxRequestsPerSocket === 'number' && server.maxRequestsPerSocket > 0 @@ -1043,15 +1054,6 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { } else if (req.headers.expect !== undefined) { handled = true; - // From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 - // A server MUST respond with a 400 (Bad Request) status code to any - // HTTP/1.1 request message that lacks a Host header field - if (req.headers.host === undefined) { - res.writeHead(400); - res.end(); - return 0; - } - if (RegExpPrototypeExec(continueExpression, req.headers.expect) !== null) { res._expect_continue = true; if (server.listenerCount('checkContinue') > 0) { diff --git a/test/parallel/test-http-max-headers-count.js b/test/parallel/test-http-max-headers-count.js index f5c807a846fe..27bb1a1c3c04 100644 --- a/test/parallel/test-http-max-headers-count.js +++ b/test/parallel/test-http-max-headers-count.js @@ -59,7 +59,7 @@ server.listen(0, function() { const maxAndExpected = [ // for client [20, 20], [1200, 104], - [0, N + 4], // Connection, Date and Transfer-Encoding + [0, N + 4], // Host and Connection ]; doRequest(); diff --git a/test/parallel/test-http-request-host-header.js b/test/parallel/test-http-request-host-header.js new file mode 100644 index 000000000000..6b89d97bab28 --- /dev/null +++ b/test/parallel/test-http-request-host-header.js @@ -0,0 +1,21 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); + +const server = http.createServer(common.mustNotCall((req, res) => { + res.writeHead(200); + res.end(); +})); + +// From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 +// A server MUST respond with a 400 (Bad Request) status code to any +// HTTP/1.1 request message that lacks a Host header field +server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port, headers: [] }, (res) => { + assert.strictEqual(res.statusCode, 400); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); +})); diff --git a/test/parallel/test-https-max-headers-count.js b/test/parallel/test-https-max-headers-count.js index 42614b15b40f..6b188b0780eb 100644 --- a/test/parallel/test-https-max-headers-count.js +++ b/test/parallel/test-https-max-headers-count.js @@ -48,7 +48,7 @@ server.listen(0, common.mustCall(() => { const maxAndExpected = [ // for client [20, 20], [1200, 104], - [0, N + 4], // Connection, Date and Transfer-Encoding + [0, N + 4], // Host and Connection ]; const doRequest = common.mustCall(() => { const max = maxAndExpected[responses][0]; From 74650da73cf4280e9efcb47ac7b94b7cfac17400 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Thu, 24 Nov 2022 18:29:50 +0100 Subject: [PATCH 10/14] http: updated tests and close connection --- lib/_http_server.js | 2 +- test/parallel/test-http-keep-alive-drop-requests.js | 1 + test/parallel/test-http-keep-alive-max-requests.js | 2 ++ test/parallel/test-http-request-host-header.js | 1 + test/parallel/test-http-server-close-all.js | 2 +- test/parallel/test-http-server-close-idle.js | 2 +- test/parallel/test-http-server-headers-timeout-keepalive.js | 2 +- test/parallel/test-http-server-headers-timeout-pipelining.js | 2 +- test/parallel/test-http-server-request-timeout-pipelining.js | 2 +- test/parallel/test-http-server-request-timeouts-mixed.js | 2 +- test/parallel/test-https-host-headers.js | 2 +- test/parallel/test-https-keep-alive-drop-requests.js | 2 +- test/parallel/test-https-server-close-all.js | 2 +- test/parallel/test-https-server-close-idle.js | 2 +- 14 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index ba7dee480102..849f0e62cb5c 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -1029,7 +1029,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { // A server MUST respond with a 400 (Bad Request) status code to any // HTTP/1.1 request message that lacks a Host header field if (req.headers.host === undefined) { - res.writeHead(400); + res.writeHead(400, ['Connection', 'close']); res.end(); return 0; } diff --git a/test/parallel/test-http-keep-alive-drop-requests.js b/test/parallel/test-http-keep-alive-drop-requests.js index 2a4702d631e5..a69497a0c1a0 100644 --- a/test/parallel/test-http-keep-alive-drop-requests.js +++ b/test/parallel/test-http-keep-alive-drop-requests.js @@ -8,6 +8,7 @@ const assert = require('assert'); function request(socket) { socket.write('GET / HTTP/1.1\r\n'); socket.write('Connection: keep-alive\r\n'); + socket.write('Host: localhost\r\n'); socket.write('\r\n\r\n'); } diff --git a/test/parallel/test-http-keep-alive-max-requests.js b/test/parallel/test-http-keep-alive-max-requests.js index 160891e500e4..0516a06da651 100644 --- a/test/parallel/test-http-keep-alive-max-requests.js +++ b/test/parallel/test-http-keep-alive-max-requests.js @@ -24,12 +24,14 @@ function writeRequest(socket, withBody) { socket.write('POST / HTTP/1.1\r\n'); socket.write('Connection: keep-alive\r\n'); socket.write('Content-Type: text/plain\r\n'); + socket.write('Host: localhost\r\n'); socket.write(`Content-Length: ${bodySent.length}\r\n\r\n`); socket.write(`${bodySent}\r\n`); socket.write('\r\n\r\n'); } else { socket.write('GET / HTTP/1.1\r\n'); socket.write('Connection: keep-alive\r\n'); + socket.write('Host: localhost\r\n'); socket.write('\r\n\r\n'); } } diff --git a/test/parallel/test-http-request-host-header.js b/test/parallel/test-http-request-host-header.js index 6b89d97bab28..c88c10808a2f 100644 --- a/test/parallel/test-http-request-host-header.js +++ b/test/parallel/test-http-request-host-header.js @@ -14,6 +14,7 @@ const server = http.createServer(common.mustNotCall((req, res) => { server.listen(0, common.mustCall(() => { http.get({ port: server.address().port, headers: [] }, (res) => { assert.strictEqual(res.statusCode, 400); + assert.strictEqual(res.headers.connection, 'close'); res.resume().on('end', common.mustCall(() => { server.close(); })); diff --git a/test/parallel/test-http-server-close-all.js b/test/parallel/test-http-server-close-all.js index c4c211d97979..7d4561dbe2be 100644 --- a/test/parallel/test-http-server-close-all.js +++ b/test/parallel/test-http-server-close-all.js @@ -50,7 +50,7 @@ server.listen(0, function() { client2.on('close', common.mustCall()); - client2.write('GET / HTTP/1.1\r\n\r\n'); + client2.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); })); client1.on('close', common.mustCall()); diff --git a/test/parallel/test-http-server-close-idle.js b/test/parallel/test-http-server-close-idle.js index 361ccf990fab..af99d1b42c20 100644 --- a/test/parallel/test-http-server-close-idle.js +++ b/test/parallel/test-http-server-close-idle.js @@ -60,7 +60,7 @@ server.listen(0, function() { client2Closed = true; })); - client2.write('GET / HTTP/1.1\r\n\r\n'); + client2.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); })); client1.on('close', common.mustCall(() => { diff --git a/test/parallel/test-http-server-headers-timeout-keepalive.js b/test/parallel/test-http-server-headers-timeout-keepalive.js index 32c9aec071e8..493c730cb7a9 100644 --- a/test/parallel/test-http-server-headers-timeout-keepalive.js +++ b/test/parallel/test-http-server-headers-timeout-keepalive.js @@ -12,7 +12,7 @@ const { connect } = require('net'); function performRequestWithDelay(client, firstDelay, secondDelay, closeAfter) { client.resume(); - client.write('GET / HTTP/1.1\r\n'); + client.write('GET / HTTP/1.1\r\nHost: example.com\r\n'); setTimeout(() => { client.write('Connection: '); diff --git a/test/parallel/test-http-server-headers-timeout-pipelining.js b/test/parallel/test-http-server-headers-timeout-pipelining.js index d0329a95cb19..5d7ea8bc76d4 100644 --- a/test/parallel/test-http-server-headers-timeout-pipelining.js +++ b/test/parallel/test-http-server-headers-timeout-pipelining.js @@ -66,7 +66,7 @@ server.listen(0, common.mustCall(() => { // Send two requests using pipelining. Delay before finishing the second one client.resume(); - client.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nConnection: '); + client.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\nConnection: '); // Complete the request setTimeout(() => { diff --git a/test/parallel/test-http-server-request-timeout-pipelining.js b/test/parallel/test-http-server-request-timeout-pipelining.js index 0c7e87160fef..62a4b784d591 100644 --- a/test/parallel/test-http-server-request-timeout-pipelining.js +++ b/test/parallel/test-http-server-request-timeout-pipelining.js @@ -60,7 +60,7 @@ server.listen(0, common.mustCall(() => { // Send two requests using pipelining. Delay before finishing the second one client.resume(); - client.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nConnection: '); + client.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\nConnection: '); // Complete the request setTimeout(() => { diff --git a/test/parallel/test-http-server-request-timeouts-mixed.js b/test/parallel/test-http-server-request-timeouts-mixed.js index 918aa159b6b3..ec3cce916115 100644 --- a/test/parallel/test-http-server-request-timeouts-mixed.js +++ b/test/parallel/test-http-server-request-timeouts-mixed.js @@ -7,7 +7,7 @@ const { connect } = require('net'); // This test validates that request are correct checked for both requests and headers timeout in various situations. -const requestBodyPart1 = 'POST / HTTP/1.1\r\nContent-Length: 20\r\n'; +const requestBodyPart1 = 'POST / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 20\r\n'; const requestBodyPart2 = 'Connection: close\r\n\r\n1234567890'; const requestBodyPart3 = '1234567890'; diff --git a/test/parallel/test-https-host-headers.js b/test/parallel/test-https-host-headers.js index b8dcda320fdd..326294293018 100644 --- a/test/parallel/test-https-host-headers.js +++ b/test/parallel/test-https-host-headers.js @@ -25,7 +25,7 @@ const httpsServer = https.createServer({ } res.writeHead(200, {}); res.end('ok'); -}, 9)).listen(0, common.mustCall(function(err) { +}, 6)).listen(0, common.mustCall(function(err) { debug(`test https server listening on port ${this.address().port}`); assert.ifError(err); https.get({ diff --git a/test/parallel/test-https-keep-alive-drop-requests.js b/test/parallel/test-https-keep-alive-drop-requests.js index d2c7f78e0bff..78ee0c199a8a 100644 --- a/test/parallel/test-https-keep-alive-drop-requests.js +++ b/test/parallel/test-https-keep-alive-drop-requests.js @@ -13,7 +13,7 @@ const tls = require('tls'); const { readKey } = require('../common/fixtures'); function request(socket) { - socket.write('GET / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n\r\n'); + socket.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n\r\n'); } // https options diff --git a/test/parallel/test-https-server-close-all.js b/test/parallel/test-https-server-close-all.js index 1c9092ff5c92..170d95db8f91 100644 --- a/test/parallel/test-https-server-close-all.js +++ b/test/parallel/test-https-server-close-all.js @@ -60,7 +60,7 @@ server.listen(0, function() { client2.on('close', common.mustCall()); - client2.write('GET / HTTP/1.1\r\n\r\n'); + client2.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n'); })); client1.on('close', common.mustCall()); diff --git a/test/parallel/test-https-server-close-idle.js b/test/parallel/test-https-server-close-idle.js index 7f093c47cd86..62a01ac743f4 100644 --- a/test/parallel/test-https-server-close-idle.js +++ b/test/parallel/test-https-server-close-idle.js @@ -70,7 +70,7 @@ server.listen(0, function() { client2Closed = true; })); - client2.write('GET / HTTP/1.1\r\n\r\n'); + client2.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n'); })); client1.on('close', common.mustCall(() => { From bf5c456f5c114e133884de9ac73ce8ad26dd9711 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Thu, 24 Nov 2022 18:47:46 +0100 Subject: [PATCH 11/14] fix: linting --- test/parallel/test-http-server-headers-timeout-pipelining.js | 3 ++- test/parallel/test-http-server-request-timeout-pipelining.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-http-server-headers-timeout-pipelining.js b/test/parallel/test-http-server-headers-timeout-pipelining.js index 5d7ea8bc76d4..4f1055599083 100644 --- a/test/parallel/test-http-server-headers-timeout-pipelining.js +++ b/test/parallel/test-http-server-headers-timeout-pipelining.js @@ -66,7 +66,8 @@ server.listen(0, common.mustCall(() => { // Send two requests using pipelining. Delay before finishing the second one client.resume(); - client.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\nConnection: '); + client.write(`GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n + GET / HTTP/1.1\r\nHost: localhost\r\nConnection: `); // Complete the request setTimeout(() => { diff --git a/test/parallel/test-http-server-request-timeout-pipelining.js b/test/parallel/test-http-server-request-timeout-pipelining.js index 62a4b784d591..6644630a633c 100644 --- a/test/parallel/test-http-server-request-timeout-pipelining.js +++ b/test/parallel/test-http-server-request-timeout-pipelining.js @@ -60,7 +60,8 @@ server.listen(0, common.mustCall(() => { // Send two requests using pipelining. Delay before finishing the second one client.resume(); - client.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\nConnection: '); + client.write(`GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive + \r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\nConnection: `); // Complete the request setTimeout(() => { From 08b4b48ee1f8adc90b70f7ef4c67841bb9c59420 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Thu, 24 Nov 2022 19:44:29 +0100 Subject: [PATCH 12/14] fix: linting error --- test/parallel/test-http-server-headers-timeout-pipelining.js | 4 ++-- test/parallel/test-http-server-request-timeout-pipelining.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/parallel/test-http-server-headers-timeout-pipelining.js b/test/parallel/test-http-server-headers-timeout-pipelining.js index 4f1055599083..7e6c6626fafc 100644 --- a/test/parallel/test-http-server-headers-timeout-pipelining.js +++ b/test/parallel/test-http-server-headers-timeout-pipelining.js @@ -66,8 +66,8 @@ server.listen(0, common.mustCall(() => { // Send two requests using pipelining. Delay before finishing the second one client.resume(); - client.write(`GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n - GET / HTTP/1.1\r\nHost: localhost\r\nConnection: `); + client.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n' + + 'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: '); // Complete the request setTimeout(() => { diff --git a/test/parallel/test-http-server-request-timeout-pipelining.js b/test/parallel/test-http-server-request-timeout-pipelining.js index 6644630a633c..30b8875bc774 100644 --- a/test/parallel/test-http-server-request-timeout-pipelining.js +++ b/test/parallel/test-http-server-request-timeout-pipelining.js @@ -60,8 +60,8 @@ server.listen(0, common.mustCall(() => { // Send two requests using pipelining. Delay before finishing the second one client.resume(); - client.write(`GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive - \r\n\r\nGET / HTTP/1.1\r\nHost: localhost\r\nConnection: `); + client.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n' + + 'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: '); // Complete the request setTimeout(() => { From 1939f67f22473a92b3b992bdf7ca2eb2a158551b Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Fri, 25 Nov 2022 17:54:32 +0100 Subject: [PATCH 13/14] fix: fixed flaky tests --- test/parallel/test-http-keep-alive-pipeline-max-requests.js | 1 + test/parallel/test-http-server-keep-alive-defaults.js | 1 + test/parallel/test-http-server-keep-alive-max-requests-null.js | 1 + 3 files changed, 3 insertions(+) diff --git a/test/parallel/test-http-keep-alive-pipeline-max-requests.js b/test/parallel/test-http-keep-alive-pipeline-max-requests.js index 5549440d3dd5..6a07eb2638c8 100644 --- a/test/parallel/test-http-keep-alive-pipeline-max-requests.js +++ b/test/parallel/test-http-keep-alive-pipeline-max-requests.js @@ -21,6 +21,7 @@ function assertResponse(headers, body, expectClosed) { function writeRequest(socket) { socket.write('POST / HTTP/1.1\r\n'); + socket.write('Host: localhost\r\n'); socket.write('Connection: keep-alive\r\n'); socket.write('Content-Type: text/plain\r\n'); socket.write(`Content-Length: ${bodySent.length}\r\n\r\n`); diff --git a/test/parallel/test-http-server-keep-alive-defaults.js b/test/parallel/test-http-server-keep-alive-defaults.js index 6f3abc30b573..7efebd786285 100644 --- a/test/parallel/test-http-server-keep-alive-defaults.js +++ b/test/parallel/test-http-server-keep-alive-defaults.js @@ -16,6 +16,7 @@ function assertResponse(headers, body, expectClosed) { function writeRequest(socket) { socket.write('POST / HTTP/1.1\r\n'); socket.write('Connection: keep-alive\r\n'); + socket.write('Host: localhost\r\n'); socket.write('Content-Type: text/plain\r\n'); socket.write(`Content-Length: ${bodySent.length}\r\n\r\n`); socket.write(`${bodySent}\r\n`); diff --git a/test/parallel/test-http-server-keep-alive-max-requests-null.js b/test/parallel/test-http-server-keep-alive-max-requests-null.js index fee882be8562..0b5acfe4dc08 100644 --- a/test/parallel/test-http-server-keep-alive-max-requests-null.js +++ b/test/parallel/test-http-server-keep-alive-max-requests-null.js @@ -16,6 +16,7 @@ function assertResponse(headers, body, expectClosed) { function writeRequest(socket) { socket.write('POST / HTTP/1.1\r\n'); socket.write('Connection: keep-alive\r\n'); + socket.write('Host: localhost\r\n'); socket.write('Content-Type: text/plain\r\n'); socket.write(`Content-Length: ${bodySent.length}\r\n\r\n`); socket.write(`${bodySent}\r\n`); From 077986bb41de011d5dab315ca53c870c8ded70cc Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Mon, 28 Nov 2022 10:49:55 +0100 Subject: [PATCH 14/14] http: created requireHostHeader field to enable host header check --- doc/api/http.md | 4 ++ lib/_http_server.js | 10 +++- lib/http.js | 1 + .../parallel/test-http-request-host-header.js | 51 +++++++++++++------ 4 files changed, 49 insertions(+), 17 deletions(-) diff --git a/doc/api/http.md b/doc/api/http.md index 276739366328..0355df275f41 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -3185,6 +3185,10 @@ changes: * `uniqueHeaders` {Array} A list of response headers that should be sent only once. If the header's value is an array, the items will be joined using `; `. + * `requireHostHeader` {boolean} It forces the server to respond with + a 400 (Bad Request) status code to any HTTP/1.1 request message + that lacks a Host header (as mandated by the specification). + **Default:** `true`. * `requestListener` {Function} diff --git a/lib/_http_server.js b/lib/_http_server.js index 849f0e62cb5c..3c85d343bda5 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -473,6 +473,14 @@ function storeHTTPOptions(options) { } else { this.connectionsCheckingInterval = 30_000; // 30 seconds } + + const requireHostHeader = options.requireHostHeader; + if (requireHostHeader !== undefined) { + validateBoolean(requireHostHeader, 'options.requireHostHeader'); + this.requireHostHeader = requireHostHeader; + } else { + this.requireHostHeader = true; + } } function setupConnectionsTracking(server) { @@ -1028,7 +1036,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { // From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 // A server MUST respond with a 400 (Bad Request) status code to any // HTTP/1.1 request message that lacks a Host header field - if (req.headers.host === undefined) { + if (server.requireHostHeader && req.headers.host === undefined) { res.writeHead(400, ['Connection', 'close']); res.end(); return 0; diff --git a/lib/http.js b/lib/http.js index 6835789836d6..9e99048a0b65 100644 --- a/lib/http.js +++ b/lib/http.js @@ -52,6 +52,7 @@ let maxHeaderSize; * ServerResponse?: ServerResponse; * insecureHTTPParser?: boolean; * maxHeaderSize?: number; + * requireHostHeader?: boolean * }} [opts] * @param {Function} [requestListener] * @returns {Server} diff --git a/test/parallel/test-http-request-host-header.js b/test/parallel/test-http-request-host-header.js index c88c10808a2f..1a66ccfc6fc0 100644 --- a/test/parallel/test-http-request-host-header.js +++ b/test/parallel/test-http-request-host-header.js @@ -3,20 +3,39 @@ const common = require('../common'); const assert = require('assert'); const http = require('http'); -const server = http.createServer(common.mustNotCall((req, res) => { - res.writeHead(200); - res.end(); -})); +{ + const server = http.createServer(common.mustNotCall((req, res) => { + res.writeHead(200); + res.end(); + })); -// From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 -// A server MUST respond with a 400 (Bad Request) status code to any -// HTTP/1.1 request message that lacks a Host header field -server.listen(0, common.mustCall(() => { - http.get({ port: server.address().port, headers: [] }, (res) => { - assert.strictEqual(res.statusCode, 400); - assert.strictEqual(res.headers.connection, 'close'); - res.resume().on('end', common.mustCall(() => { - server.close(); - })); - }); -})); + // From RFC 7230 5.4 https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 + // A server MUST respond with a 400 (Bad Request) status code to any + // HTTP/1.1 request message that lacks a Host header field + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port, headers: [] }, (res) => { + assert.strictEqual(res.statusCode, 400); + assert.strictEqual(res.headers.connection, 'close'); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +} + +{ + const server = http.createServer({ requireHostHeader: false }, common.mustCall((req, res) => { + res.writeHead(200, ['test', '1']); + res.end(); + })); + + server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port, headers: [] }, (res) => { + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers.test, '1'); + res.resume().on('end', common.mustCall(() => { + server.close(); + })); + }); + })); +}