Skip to content

Commit af7f7b5

Browse files
authored
fix: amend header serialization algorithm (#1523)
I needed to update very old [email protected] to [email protected] which uses 2.x branch of node-fetch instead of 1.x branch. [email protected] is no longer compatible with fetch API in browsers and is incompatible with the fetch specification. Ref #1508
1 parent d89a926 commit af7f7b5

File tree

5 files changed

+78
-45
lines changed

5 files changed

+78
-45
lines changed

package-lock.json

Lines changed: 22 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
"btoa": "1.1.2",
102102
"buffer": "^5.1.0",
103103
"cookie": "^0.3.1",
104-
"cross-fetch": "0.0.8",
104+
"cross-fetch": "^3.0.4",
105105
"deep-extend": "^0.5.1",
106106
"encode-3986": "^1.0.0",
107107
"fast-json-patch": "~2.1.0",
@@ -121,9 +121,13 @@
121121
"**/test/*.js?(x)",
122122
"**/test/**/*.js?(x)"
123123
],
124+
"setupFilesAfterEnv": [
125+
"<rootDir>/test/jest.setup.js"
126+
],
124127
"testPathIgnorePatterns": [
125128
"<rootDir>/node_modules/",
126129
"<rootDir>/test/data/",
130+
"<rootDir>/test/jest.setup.js",
127131
"<rootDir>/test/specmap/data/",
128132
"<rootDir>/test/webpack-bundle/.tmp/"
129133
]

src/http.js

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'cross-fetch/polyfill' /* global fetch */
22
import qs from 'qs'
33
import jsYaml from 'js-yaml'
44
import isString from 'lodash/isString'
5+
import isFunction from 'lodash/isFunction'
56

67
// For testing
78
export const self = {
@@ -104,7 +105,6 @@ export function serializeRes(oriRes, url, {loadSpec = false} = {}) {
104105
const contentType = res.headers['content-type']
105106
const useText = loadSpec || shouldDownloadAsText(contentType)
106107

107-
// Note: Response.blob not implemented in node-fetch 1. Use buffer instead.
108108
const getBody = useText ? oriRes.text : (oriRes.blob || oriRes.buffer)
109109

110110
return getBody.call(oriRes).then((body) => {
@@ -125,29 +125,26 @@ export function serializeRes(oriRes, url, {loadSpec = false} = {}) {
125125
})
126126
}
127127

128+
function serializeHeaderValue(value) {
129+
const isMulti = value.includes(', ')
130+
131+
return isMulti ? value.split(', ') : value
132+
}
133+
128134
// Serialize headers into a hash, where mutliple-headers result in an array.
129135
//
130136
// eg: Cookie: one
131137
// Cookie: two
132138
// = { Cookie: [ "one", "two" ]
133139
export function serializeHeaders(headers = {}) {
134-
const obj = {}
135-
136-
// Iterate over headers, making multiple-headers into an array
137-
if (typeof headers.forEach === 'function') {
138-
headers.forEach((headerValue, header) => {
139-
if (obj[header] !== undefined) {
140-
obj[header] = Array.isArray(obj[header]) ? obj[header] : [obj[header]]
141-
obj[header].push(headerValue)
142-
}
143-
else {
144-
obj[header] = headerValue
145-
}
146-
})
147-
return obj
148-
}
149-
150-
return obj
140+
if (!isFunction(headers.entries)) return {}
141+
142+
return Array
143+
.from(headers.entries())
144+
.reduce((acc, [header, value]) => {
145+
acc[header] = serializeHeaderValue(value)
146+
return acc
147+
}, {})
151148
}
152149

153150
export function isFile(obj, navigatorObj) {

test/http.js

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,8 @@ describe('http', () => {
310310
expect(encodeFormOrQuery(req.query)).toEqual('id=1,2')
311311
})
312312

313-
test('should handle custom array serilization', () => {
314-
// Given
315-
fetchMock.get('*', {hello: 'world'})
313+
test('should handle custom array serialization', () => {
314+
fetchMock.get('*', {body: 'response body'})
316315
const req = {
317316
url: 'http://example.com',
318317
method: 'GET',
@@ -336,6 +335,9 @@ describe('http', () => {
336335
// Given
337336
fetchMock.get('*', (url, opts) => {
338337
return {
338+
headers: {
339+
'Content-Type': 'text/plain',
340+
},
339341
body: opts.headers.WilliamCWilliamsHeader
340342
}
341343
})
@@ -360,23 +362,32 @@ describe('http', () => {
360362
test(
361363
'should serialize fetch-like response and call serializeHeaders',
362364
() => {
363-
const headers = {
364-
Authorization: ['Basic hoop-la', 'Advanced hoop-la']
365-
}
366-
367-
const res = fetchMock.mock('http://swagger.io', {headers})
365+
// cross-fetch exposes FetchAPI methods onto global
366+
require('cross-fetch/polyfill')
367+
const response = new Response('data', { // eslint-disable-line no-undef
368+
status: 200,
369+
headers: {
370+
Authorization: 'Basic hoop-la, Advanced hoop-la',
371+
'Content-Type': 'text/plain',
372+
}
373+
})
368374

369-
return fetch('http://swagger.io').then((_res) => { // eslint-disable-line no-undef
370-
return serializeRes(_res, 'https://swagger.io')
371-
}).then((resSerialize) => {
372-
expect(resSerialize.headers).toEqual({authorization: ['Basic hoop-la', 'Advanced hoop-la']})
373-
}).then(fetchMock.restore)
375+
return serializeRes(response, 'https://swagger.io')
376+
.then((serializedResponse) => {
377+
expect(serializedResponse.headers).toEqual({
378+
authorization: ['Basic hoop-la', 'Advanced hoop-la'],
379+
'content-type': 'text/plain'
380+
})
381+
})
374382
}
375383
)
376384

377385
test(
378386
'should set .text and .data to body Blob or Buffer for binary response',
379387
() => {
388+
// cross-fetch exposes FetchAPI methods onto global
389+
require('cross-fetch/polyfill')
390+
380391
const headers = {
381392
'Content-Type': 'application/octet-stream'
382393
}
@@ -392,11 +403,11 @@ describe('http', () => {
392403
}).then((resSerialize) => {
393404
expect(resSerialize.data).toBe(resSerialize.text)
394405
if (originalRes.blob) {
395-
expect(resSerialize.data).toBeInstanceOf(Blob) // eslint-disable-line no-undef
406+
expect(Object.prototype.toString.call(resSerialize.data)).toBe('[object Blob]')
396407
}
397408
else {
398409
expect(resSerialize.data).toBeInstanceOf(Buffer)
399-
expect(resSerialize.data).toEqual(new Buffer(body))
410+
expect(resSerialize.data).toEqual(Buffer.from(body))
400411
}
401412
}).then(fetchMock.restore)
402413
}
@@ -409,7 +420,7 @@ describe('http', () => {
409420
'Content-Type': 'application/json'
410421
}
411422

412-
const body = 'body data'
423+
const body = '{}'
413424
const res = fetchMock.mock('http://swagger.io', {body, headers})
414425

415426
return fetch('http://swagger.io').then((_res) => { // eslint-disable-line no-undef

test/jest.setup.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import fetchMock from 'fetch-mock'
2+
import {Headers, Request, Response} from 'cross-fetch'
3+
4+
fetchMock.setImplementations({
5+
Promise,
6+
Request,
7+
Response,
8+
Headers
9+
})

0 commit comments

Comments
 (0)