Skip to content

Commit

Permalink
fix: http2 header parsing (#3047)
Browse files Browse the repository at this point in the history
  • Loading branch information
climba03003 committed Apr 3, 2024
1 parent dde070b commit 7c9c0c2
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 6 deletions.
18 changes: 12 additions & 6 deletions lib/dispatcher/client-h2.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,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))
for (const [name, value] of Object.entries(headers)) {
// h2 may concat the header value by array
// e.g. Set-Cookie
if (Array.isArray(value)) {
for (const subvalue of value) {
// we need to provide each header value of header name
// because the headers handler expect name-value pair
result.push(Buffer.from(name), Buffer.from(subvalue))
}
} else {
result.push(Buffer.from(name), Buffer.from(value))
}
}

return result
Expand Down
45 changes: 45 additions & 0 deletions test/fetch/http2.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,3 +462,48 @@ test('Issue #2386', async (t) => {
controller.abort()
ok(true)
})

test('Issue #3046', async (t) => {
const server = createSecureServer(pem)

const { strictEqual, deepStrictEqual } = tspl(t, { plan: 6 })

server.on('stream', async (stream, headers) => {
strictEqual(headers[':method'], 'GET')
strictEqual(headers[':path'], '/')
strictEqual(headers[':scheme'], 'https')

stream.respond({
'set-cookie': ['hello=world', 'foo=bar'],
'content-type': 'text/html; charset=utf-8',
':status': 200
})

stream.end('<h1>Hello World</h1>')
})

server.listen(0)
await once(server, 'listening')

const client = new Client(`https://localhost:${server.address().port}`, {
connect: {
rejectUnauthorized: false
},
allowH2: true
})

t.after(closeClientAndServerAsPromise(client, server))

const response = await fetch(
`https://localhost:${server.address().port}/`,
// Needs to be passed to disable the reject unauthorized
{
method: 'GET',
dispatcher: client
}
)

strictEqual(response.status, 200)
strictEqual(response.headers.get('content-type'), 'text/html; charset=utf-8')
deepStrictEqual(response.headers.getSetCookie(), ['hello=world', 'foo=bar'])
})

0 comments on commit 7c9c0c2

Please sign in to comment.