Skip to content

Commit 0862ebe

Browse files
KhafraDevtsctx
andcommitted
fix casing issue when cloning Headers object
Refs: nodejs#3159 (comment) Co-authored-by: tsctx <[email protected]>
1 parent ad81122 commit 0862ebe

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

lib/web/fetch/headers.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,12 @@ webidl.converters.HeadersInit = function (V) {
602602
if (webidl.util.Type(V) === 'Object') {
603603
const iterator = Reflect.get(V, Symbol.iterator)
604604

605+
// A work-around to ensure we send the properly-cased Headers when V is a Headers object.
606+
// Read https://github.com/nodejs/undici/pull/3159#issuecomment-2075537226 before touching, please.
607+
if (!util.types.isProxy(V) && kHeadersList in V && iterator === Headers.prototype.entries) { // Headers object
608+
return V[kHeadersList].entries
609+
}
610+
605611
if (typeof iterator === 'function') {
606612
return webidl.converters['sequence<sequence<ByteString>>'](V, iterator.bind(V))
607613
}

lib/web/fetch/request.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,8 +459,9 @@ class Request {
459459
// 4. If headers is a Headers object, then for each header in its header
460460
// list, append header’s name/header’s value to this’s headers.
461461
if (headers instanceof HeadersList) {
462-
for (const [key, val] of headers) {
463-
headersList.append(key, val)
462+
for (const { 0: key, 1: val } of headers) {
463+
// Note: The header names are already in lowercase.
464+
headersList.append(key, val, true)
464465
}
465466
// Note: Copy the `set-cookie` meta-data.
466467
headersList.cookies = headers.cookies

test/fetch/headers-case.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict'
2+
3+
const { fetch, Headers } = require('../..')
4+
const { createServer } = require('node:http')
5+
const { once } = require('node:events')
6+
const { test } = require('node:test')
7+
const { tspl } = require('@matteo.collina/tspl')
8+
9+
test('Headers retain keys case-sensitive', async (t) => {
10+
const assert = tspl(t, { plan: 3 })
11+
12+
const server = createServer((req, res) => {
13+
console.log(req.rawHeaders)
14+
assert.ok(req.rawHeaders.includes('Content-Type'))
15+
16+
res.end()
17+
}).listen(0)
18+
19+
t.after(() => server.close())
20+
await once(server, 'listening')
21+
22+
for (const headers of [
23+
new Headers([['Content-Type', 'text/plain']]),
24+
{ 'Content-Type': 'text/plain' },
25+
[['Content-Type', 'text/plain']]
26+
]) {
27+
await fetch(`http://localhost:${server.address().port}`, {
28+
headers
29+
})
30+
}
31+
})

0 commit comments

Comments
 (0)