Skip to content

Commit 090b4b0

Browse files
authored
fix(node-fetch): respect set-cookies given in HeadersInit (#2079)
1 parent 3d043c5 commit 090b4b0

File tree

3 files changed

+56
-15
lines changed

3 files changed

+56
-15
lines changed

.changeset/curvy-insects-cheer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@whatwg-node/node-fetch': patch
3+
---
4+
5+
Fix the bug when `set-cookies` given is ignored in `HeadersInit`

packages/node-fetch/src/Headers.ts

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ export class PonyfillHeaders implements Headers {
1111
private _map: Map<string, string> | undefined;
1212
private objectNormalizedKeysOfHeadersInit: string[] = [];
1313
private objectOriginalKeysOfHeadersInit: string[] = [];
14-
private _setCookies: string[] = [];
14+
private _setCookies?: string[];
1515

1616
constructor(private headersInit?: PonyfillHeadersInit) {}
1717

1818
// perf: we don't need to build `this.map` for Requests, as we can access the headers directly
1919
private _get(key: string) {
2020
const normalized = key.toLowerCase();
21-
if (normalized === 'set-cookie') {
21+
if (normalized === 'set-cookie' && this._setCookies?.length) {
2222
return this._setCookies.join(', ');
2323
}
2424
// If the map is built, reuse it
@@ -32,7 +32,16 @@ export class PonyfillHeaders implements Headers {
3232
}
3333

3434
if (Array.isArray(this.headersInit)) {
35-
return this.headersInit.find(header => header[0].toLowerCase() === normalized)?.[1] || null;
35+
const found = this.headersInit.filter(
36+
([headerKey]) => headerKey.toLowerCase() === normalized,
37+
);
38+
if (found.length === 0) {
39+
return null;
40+
}
41+
if (found.length === 1) {
42+
return found[0][1];
43+
}
44+
return found.map(([, value]) => value).join(', ');
3645
} else if (isHeadersLike(this.headersInit)) {
3746
return this.headersInit.get(normalized);
3847
} else {
@@ -61,21 +70,23 @@ export class PonyfillHeaders implements Headers {
6170
// I could do a getter here, but I'm too lazy to type `getter`.
6271
private getMap() {
6372
if (!this._map) {
73+
this._setCookies = [];
6474
if (this.headersInit != null) {
6575
if (Array.isArray(this.headersInit)) {
6676
this._map = new Map();
67-
this.headersInit.forEach(([key, value]) => {
77+
for (const [key, value] of this.headersInit) {
6878
const normalizedKey = key.toLowerCase();
6979
if (normalizedKey === 'set-cookie') {
7080
this._setCookies.push(value);
71-
return;
81+
continue;
7282
}
73-
this._map!.set(normalizedKey, value);
74-
});
83+
this._map.set(normalizedKey, value);
84+
}
7585
} else if (isHeadersLike(this.headersInit)) {
7686
this._map = new Map();
7787
this.headersInit.forEach((value, key) => {
7888
if (key === 'set-cookie') {
89+
this._setCookies ||= [];
7990
this._setCookies.push(value);
8091
return;
8192
}
@@ -88,6 +99,7 @@ export class PonyfillHeaders implements Headers {
8899
if (initValue != null) {
89100
const normalizedKey = initKey.toLowerCase();
90101
if (normalizedKey === 'set-cookie') {
102+
this._setCookies ||= [];
91103
this._setCookies.push(initValue);
92104
continue;
93105
}
@@ -106,6 +118,7 @@ export class PonyfillHeaders implements Headers {
106118
append(name: string, value: string): void {
107119
const key = name.toLowerCase();
108120
if (key === 'set-cookie') {
121+
this._setCookies ||= [];
109122
this._setCookies.push(value);
110123
return;
111124
}
@@ -121,12 +134,12 @@ export class PonyfillHeaders implements Headers {
121134
return null;
122135
}
123136

124-
return value;
137+
return value.toString();
125138
}
126139

127140
has(name: string): boolean {
128141
if (name === 'set-cookie') {
129-
return this._setCookies.length > 0;
142+
return !!this._setCookies?.length;
130143
}
131144
return !!this._get(name); // we might need to check if header exists and not just check if it's not nullable
132145
}
@@ -150,7 +163,7 @@ export class PonyfillHeaders implements Headers {
150163
}
151164

152165
forEach(callback: (value: string, key: string, parent: Headers) => void): void {
153-
this._setCookies.forEach(setCookie => {
166+
this._setCookies?.forEach(setCookie => {
154167
callback(setCookie, 'set-cookie', this);
155168
});
156169
if (!this._map) {
@@ -179,7 +192,7 @@ export class PonyfillHeaders implements Headers {
179192
}
180193

181194
*_keys(): IterableIterator<string> {
182-
if (this._setCookies.length) {
195+
if (this._setCookies?.length) {
183196
yield 'set-cookie';
184197
}
185198
if (!this._map) {
@@ -204,7 +217,9 @@ export class PonyfillHeaders implements Headers {
204217
}
205218

206219
*_values(): IterableIterator<string> {
207-
yield* this._setCookies;
220+
if (this._setCookies?.length) {
221+
yield* this._setCookies;
222+
}
208223
if (!this._map) {
209224
if (this.headersInit) {
210225
if (Array.isArray(this.headersInit)) {
@@ -227,7 +242,9 @@ export class PonyfillHeaders implements Headers {
227242
}
228243

229244
*_entries(): IterableIterator<[string, string]> {
230-
yield* this._setCookies.map(cookie => ['set-cookie', cookie] as [string, string]);
245+
if (this._setCookies?.length) {
246+
yield* this._setCookies.map(cookie => ['set-cookie', cookie] as [string, string]);
247+
}
231248
if (!this._map) {
232249
if (this.headersInit) {
233250
if (Array.isArray(this.headersInit)) {
@@ -250,7 +267,10 @@ export class PonyfillHeaders implements Headers {
250267
}
251268

252269
getSetCookie() {
253-
return this._setCookies;
270+
if (!this._setCookies) {
271+
this.getMap();
272+
}
273+
return this._setCookies!;
254274
}
255275

256276
[Symbol.iterator](): HeadersIterator<[string, string]> {
@@ -261,7 +281,7 @@ export class PonyfillHeaders implements Headers {
261281
const record: Record<string, string[] | string> = {};
262282
this.forEach((value, key) => {
263283
if (key === 'set-cookie') {
264-
record['set-cookie'] = this._setCookies;
284+
record['set-cookie'] = this._setCookies || [];
265285
} else {
266286
record[key] = value?.includes(',') ? value.split(',').map(el => el.trim()) : value;
267287
}

packages/node-fetch/tests/Headers.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,20 @@ describe('Headers', () => {
6161
headers.set('X-Header', null!);
6262
expect(inspect(headers)).toBe("Headers { 'x-header': null }");
6363
});
64+
describe('Set-Cookie', () => {
65+
it('handles values in the given map for get method', () => {
66+
const headers = new PonyfillHeaders([
67+
['set-cookie', 'a=b'],
68+
['set-cookie', 'c=d'],
69+
]);
70+
expect(headers.get('Set-Cookie')).toBe('a=b, c=d');
71+
});
72+
it('handles values in the given map for getSetCookie method', () => {
73+
const headers = new PonyfillHeaders([
74+
['set-cookie', 'a=b'],
75+
['set-cookie', 'c=d'],
76+
]);
77+
expect(headers.getSetCookie()).toEqual(['a=b', 'c=d']);
78+
});
79+
});
6480
});

0 commit comments

Comments
 (0)