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..0a53b75e501 100644 --- a/lib/dispatcher/client-h2.js +++ b/lib/dispatcher/client-h2.js @@ -54,6 +54,20 @@ const { } } = http2 +function parseH2Headers (headers) { + // 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) { client[kSocket] = socket @@ -391,7 +405,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..849a0cc43ba 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].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)