From 2b1ffab512ec65495d49443edc0a7642ec3e1aa9 Mon Sep 17 00:00:00 2001 From: Carlos Fuentes Date: Sun, 31 Mar 2024 13:26:15 +0200 Subject: [PATCH 1/2] refactor(#3023): Pass headers as array instead --- lib/core/util.js | 3 --- lib/dispatcher/client-h2.js | 6 +++++- lib/web/fetch/index.js | 23 ----------------------- test/fetch/http2.js | 3 ++- test/http2.js | 6 ++++-- 5 files changed, 11 insertions(+), 30 deletions(-) diff --git a/lib/core/util.js b/lib/core/util.js index a62396e23e0..e7b5d9c1edd 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -246,9 +246,6 @@ function bufferToLowerCasedHeaderName (value) { * @returns {Record} */ function parseHeaders (headers, obj) { - // For H2 support - if (!Array.isArray(headers)) return headers - if (obj === undefined) obj = {} for (let i = 0; i < headers.length; i += 2) { const key = headerNameToString(headers[i]) diff --git a/lib/dispatcher/client-h2.js b/lib/dispatcher/client-h2.js index d593eae4fca..e37ed9e529c 100644 --- a/lib/dispatcher/client-h2.js +++ b/lib/dispatcher/client-h2.js @@ -54,6 +54,10 @@ const { } } = http2 +function parseH2Headers (headers) { + return Object.entries(headers).flat() +} + async function connectH2 (client, socket) { client[kSocket] = socket @@ -391,7 +395,7 @@ function writeH2 (client, request) { const { [HTTP2_HEADER_STATUS]: statusCode, ...realHeaders } = headers request.onResponseStarted() - if (request.onHeaders(Number(statusCode), realHeaders, stream.resume.bind(stream), '') === false) { + if (request.onHeaders(Number(statusCode), parseH2Headers(realHeaders), stream.resume.bind(stream), '') === false) { stream.pause() } diff --git a/lib/web/fetch/index.js b/lib/web/fetch/index.js index d8c20c59bf7..58d61585c06 100644 --- a/lib/web/fetch/index.js +++ b/lib/web/fetch/index.js @@ -2141,29 +2141,6 @@ async function httpNetworkFetch ( codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim()) } location = headersList.get('location', true) - } else { - const keys = Object.keys(rawHeaders) - for (let i = 0; i < keys.length; ++i) { - // The header names are already in lowercase. - const key = keys[i] - const value = rawHeaders[key] - if (key === 'set-cookie') { - for (let j = 0; j < value.length; ++j) { - headersList.append(key, value[j], true) - } - } else { - headersList.append(key, value, true) - } - } - // For H2, The header names are already in lowercase, - // so we can avoid the `HeadersList#get` call here. - const contentEncoding = rawHeaders['content-encoding'] - if (contentEncoding) { - // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1 - // "All content-coding values are case-insensitive..." - codings = contentEncoding.toLowerCase().split(',').map((x) => x.trim()).reverse() - } - location = rawHeaders.location } this.body = new Readable({ read: resume }) diff --git a/test/fetch/http2.js b/test/fetch/http2.js index 0793a21556c..1cef1d00ce6 100644 --- a/test/fetch/http2.js +++ b/test/fetch/http2.js @@ -34,7 +34,7 @@ test('[Fetch] Issue#2311', async (t) => { res.end(body) }) - const { strictEqual } = tspl(t, { plan: 1 }) + const { strictEqual } = tspl(t, { plan: 2 }) server.listen() await once(server, 'listening') @@ -65,6 +65,7 @@ test('[Fetch] Issue#2311', async (t) => { t.after(closeClientAndServerAsPromise(client, server)) strictEqual(responseBody, expectedBody) + strictEqual(response.headers.get('x-custom-h2'), 'foo') }) test('[Fetch] Simple GET with h2', async (t) => { diff --git a/test/http2.js b/test/http2.js index 3319ffddb1e..8f77f0a76bd 100644 --- a/test/http2.js +++ b/test/http2.js @@ -851,8 +851,10 @@ test('Should handle h2 request with body (string or buffer) - dispatch', async t }, onHeaders (statusCode, headers) { t.strictEqual(statusCode, 200) - t.strictEqual(headers['content-type'], 'text/plain; charset=utf-8') - t.strictEqual(headers['x-custom-h2'], 'foo') + t.strictEqual(headers[0], 'content-type') + t.strictEqual(headers[1], 'text/plain; charset=utf-8') + t.strictEqual(headers[2], 'x-custom-h2') + t.strictEqual(headers[3], 'foo') }, onData (chunk) { response.push(chunk) From 449bcbf4e952c7b975f45048102ab14e5983b3aa Mon Sep 17 00:00:00 2001 From: Carlos Fuentes Date: Sun, 31 Mar 2024 21:52:29 +0200 Subject: [PATCH 2/2] refactor: set them as array --- lib/dispatcher/client-h2.js | 12 +++++++++++- test/http2.js | 8 ++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/dispatcher/client-h2.js b/lib/dispatcher/client-h2.js index e37ed9e529c..0a53b75e501 100644 --- a/lib/dispatcher/client-h2.js +++ b/lib/dispatcher/client-h2.js @@ -55,7 +55,17 @@ const { } = http2 function parseH2Headers (headers) { - return Object.entries(headers).flat() + // set-cookie is always an array. Duplicates are added to the array. + // For duplicate cookie headers, the values are joined together with '; '. + headers = Object.entries(headers).flat(2) + + const result = [] + + for (const header of headers) { + result.push(Buffer.from(header)) + } + + return result } async function connectH2 (client, socket) { diff --git a/test/http2.js b/test/http2.js index 8f77f0a76bd..849a0cc43ba 100644 --- a/test/http2.js +++ b/test/http2.js @@ -851,10 +851,10 @@ test('Should handle h2 request with body (string or buffer) - dispatch', async t }, onHeaders (statusCode, headers) { t.strictEqual(statusCode, 200) - t.strictEqual(headers[0], 'content-type') - t.strictEqual(headers[1], 'text/plain; charset=utf-8') - t.strictEqual(headers[2], 'x-custom-h2') - t.strictEqual(headers[3], 'foo') + t.strictEqual(headers[0].toString('utf-8'), 'content-type') + t.strictEqual(headers[1].toString('utf-8'), 'text/plain; charset=utf-8') + t.strictEqual(headers[2].toString('utf-8'), 'x-custom-h2') + t.strictEqual(headers[3].toString('utf-8'), 'foo') }, onData (chunk) { response.push(chunk)