From 5eb75871e4d2a0a5b02c91048f4977941958f04a Mon Sep 17 00:00:00 2001 From: OneNail Date: Tue, 3 May 2022 01:48:13 +0800 Subject: [PATCH] http2: compat support for array headers PR-URL: https://github.com/nodejs/node/pull/42901 Reviewed-By: Robert Nagy Reviewed-By: Paolo Insogna Reviewed-By: Matteo Collina Reviewed-By: Ricky Zhou <0x19951125@gmail.com> Reviewed-By: Rafael Gonzaga --- doc/api/http2.md | 2 +- lib/internal/http2/compat.js | 16 ++- ...2-compat-serverresponse-writehead-array.js | 118 +++++++++++++----- 3 files changed, 99 insertions(+), 37 deletions(-) diff --git a/doc/api/http2.md b/doc/api/http2.md index c065925e121af3..49a2d947ed0cbf 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -3922,7 +3922,7 @@ changes: * `statusCode` {number} * `statusMessage` {string} -* `headers` {Object} +* `headers` {Object|Array} * Returns: {http2.Http2ServerResponse} Sends a response header to the request. The status code is a 3-digit HTTP diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 0c4519dfff5986..c390ccb11a8ccf 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -682,9 +682,19 @@ class Http2ServerResponse extends Stream { let i; if (ArrayIsArray(headers)) { - for (i = 0; i < headers.length; i++) { - const header = headers[i]; - this[kSetHeader](header[0], header[1]); + if (headers.length && ArrayIsArray(headers[0])) { + for (i = 0; i < headers.length; i++) { + const header = headers[i]; + this[kSetHeader](header[0], header[1]); + } + } else { + if (headers.length % 2 !== 0) { + throw new ERR_INVALID_ARG_VALUE('headers', headers); + } + + for (i = 0; i < headers.length; i += 2) { + this[kSetHeader](headers[i], headers[i + 1]); + } } } else if (typeof headers === 'object') { const keys = ObjectKeys(headers); diff --git a/test/parallel/test-http2-compat-serverresponse-writehead-array.js b/test/parallel/test-http2-compat-serverresponse-writehead-array.js index c28f7329c1d0a1..1d0706f5ed945f 100644 --- a/test/parallel/test-http2-compat-serverresponse-writehead-array.js +++ b/test/parallel/test-http2-compat-serverresponse-writehead-array.js @@ -4,40 +4,92 @@ const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); -const h2 = require('http2'); - -// Http2ServerResponse.writeHead should support nested arrays - -const server = h2.createServer(); -server.listen(0, common.mustCall(() => { - const port = server.address().port; - server.once('request', common.mustCall((request, response) => { - const returnVal = response.writeHead(200, [ - ['foo', 'bar'], - ['ABC', 123], - ]); - assert.strictEqual(returnVal, response); - response.end(common.mustCall(() => { server.close(); })); +const http2 = require('http2'); + +// Http2ServerResponse.writeHead should support arrays and nested arrays + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + const returnVal = response.writeHead(200, [ + ['foo', 'bar'], + ['ABC', 123], + ]); + assert.strictEqual(returnVal, response); + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); + + request.on('response', common.mustCall((headers) => { + assert.strictEqual(headers.foo, 'bar'); + assert.strictEqual(headers.abc, '123'); + assert.strictEqual(headers[':status'], 200); + }, 1)); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); })); +} + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + const returnVal = response.writeHead(200, ['foo', 'bar', 'ABC', 123]); + assert.strictEqual(returnVal, response); + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); + + request.on('response', common.mustCall((headers) => { + assert.strictEqual(headers.foo, 'bar'); + assert.strictEqual(headers.abc, '123'); + assert.strictEqual(headers[':status'], 200); + }, 1)); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); + })); + })); +} + +{ + const server = http2.createServer(); + server.listen(0, common.mustCall(() => { + const port = server.address().port; + + server.once('request', common.mustCall((request, response) => { + try { + response.writeHead(200, ['foo', 'bar', 'ABC', 123, 'extra']); + } catch (err) { + assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE'); + } + + response.end(common.mustCall(() => { server.close(); })); + })); + + const client = http2.connect(`http://localhost:${port}`, common.mustCall(() => { + const request = client.request(); - const url = `http://localhost:${port}`; - const client = h2.connect(url, common.mustCall(() => { - const headers = { - ':path': '/', - ':method': 'GET', - ':scheme': 'http', - ':authority': `localhost:${port}` - }; - const request = client.request(headers); - request.on('response', common.mustCall((headers) => { - assert.strictEqual(headers.foo, 'bar'); - assert.strictEqual(headers.abc, '123'); - assert.strictEqual(headers[':status'], 200); - }, 1)); - request.on('end', common.mustCall(() => { - client.close(); + request.on('end', common.mustCall(() => { + client.close(); + })); + request.end(); + request.resume(); })); - request.end(); - request.resume(); })); -})); +}