Skip to content

Commit fcd05fa

Browse files
authored
fix: Don't crash when receiving non-string, non-array headers (#197)
1 parent caf01fb commit fcd05fa

File tree

3 files changed

+40
-6
lines changed

3 files changed

+40
-6
lines changed

.changeset/polite-trainers-camp.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@apollo/datasource-rest': patch
3+
---
4+
5+
Don't crash when receiving non-string, non-array headers

src/HTTPCache.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -331,12 +331,12 @@ function cachePolicyHeadersToNodeFetchHeadersInit(
331331
): NodeFetchHeadersInit {
332332
const headerList = [];
333333
for (const [name, value] of Object.entries(headers)) {
334-
if (typeof value === 'string') {
335-
headerList.push([name, value]);
336-
} else if (value) {
334+
if (Array.isArray(value)) {
337335
for (const subValue of value) {
338336
headerList.push([name, subValue]);
339337
}
338+
} else if (value) {
339+
headerList.push([name, value]);
340340
}
341341
}
342342
return headerList;
@@ -354,10 +354,10 @@ function cachePolicyHeadersToFetcherHeadersInit(
354354
): Record<string, string> {
355355
const headerRecord = Object.create(null);
356356
for (const [name, value] of Object.entries(headers)) {
357-
if (typeof value === 'string') {
358-
headerRecord[name] = value;
359-
} else if (value) {
357+
if (Array.isArray(value)) {
360358
headerRecord[name] = value.join(', ');
359+
} else if (value) {
360+
headerRecord[name] = value;
361361
}
362362
}
363363
return headerRecord;

src/__tests__/RESTDataSource.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,6 +1848,35 @@ describe('RESTDataSource', () => {
18481848
await dataSource.getFoo(2, true);
18491849
});
18501850

1851+
it('should not crash in revalidation flow header handling when sending non-array non-string headers', async () => {
1852+
jest.useFakeTimers({ doNotFake: ['nextTick'] });
1853+
1854+
const dataSource = new (class extends RESTDataSource {
1855+
override baseURL = apiUrl;
1856+
1857+
getFoo(id: number) {
1858+
return this.fetch(`foo/${id}`, {
1859+
headers: {
1860+
// @ts-expect-error
1861+
x: 1,
1862+
},
1863+
cacheOptions: { ttl: 1 },
1864+
});
1865+
}
1866+
})();
1867+
1868+
nock(apiUrl).get('/foo/1').times(2).reply(200);
1869+
1870+
await dataSource.getFoo(1);
1871+
await dataSource.getFoo(1);
1872+
1873+
jest.advanceTimersByTime(1000);
1874+
1875+
await dataSource.getFoo(1);
1876+
1877+
jest.useRealTimers();
1878+
});
1879+
18511880
describe('raw header access when using node-fetch', () => {
18521881
it('for a non-cacheable request', async () => {
18531882
const dataSource = new (class extends RESTDataSource {

0 commit comments

Comments
 (0)