Skip to content

Commit 8ba0fc7

Browse files
authored
feat: Throw error if request headers are not an object (#1574)
Throw if headersFieldNamesToLowerCase is not provided an object. Adds a test to cover the exception case. Going through the git history for this function, all the way back to when it was added with 4048734, I can't find a case where a non-object input was valid. Once PR # 1564 is merged in, this utility will only be called in two places: - Creating an Interceptor if the Scope was created with `options` - When http.request is called with an `options` object In both places, providing defined, but non-object values causes chaos. If the value is a truthy, non-iterable then the header was ignored during matching. For example, the following will match Interceptors as if `reqheaders` had not been provided. ```js nock('http://example.com', { reqheaders: 1 }).get('/').reply ``` However, if the value was provided as a non-plain object iterable, such as an array or string, the header matching logic would attempt to match header field names with numerical keys, rendering the whole Scope useless.
1 parent 1e7baa1 commit 8ba0fc7

File tree

3 files changed

+35
-12
lines changed

3 files changed

+35
-12
lines changed

lib/common.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,24 +201,25 @@ function isJSONContent(headers) {
201201
return contentType.startsWith('application/json')
202202
}
203203

204+
/**
205+
* Return a new object with all field names of the headers lower-cased.
206+
*
207+
* Duplicates throw an error.
208+
*/
204209
const headersFieldNamesToLowerCase = function(headers) {
205-
if (!_.isObject(headers)) {
206-
// TODO-coverage: Add a test to cover the missing condition, or remove if
207-
// not reachable.
208-
return headers
210+
if (!_.isPlainObject(headers)) {
211+
throw Error('Headers must be provided as an object')
209212
}
210213

211-
// For each key in the headers, delete its value and reinsert it with lower-case key.
212-
// Keys represent headers field names.
213214
const lowerCaseHeaders = {}
214-
_.forOwn(headers, function(fieldVal, fieldName) {
215-
const lowerCaseFieldName = fieldName.toLowerCase()
216-
if (!_.isUndefined(lowerCaseHeaders[lowerCaseFieldName])) {
217-
throw new Error(
218-
`Failed to convert header keys to lower case due to field name conflict: ${lowerCaseFieldName}`
215+
Object.entries(headers).forEach(([fieldName, fieldValue]) => {
216+
const key = fieldName.toLowerCase()
217+
if (lowerCaseHeaders[key] !== undefined) {
218+
throw Error(
219+
`Failed to convert header keys to lower case due to field name conflict: ${key}`
219220
)
220221
}
221-
lowerCaseHeaders[lowerCaseFieldName] = fieldVal
222+
lowerCaseHeaders[key] = fieldValue
222223
})
223224

224225
return lowerCaseHeaders

lib/scope.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ function startScope(basePath, options) {
2626
return new Scope(basePath, options)
2727
}
2828

29+
/**
30+
* @param {string|RegExp} basePath
31+
* @param {Object} options
32+
* @param {boolean} options.allowUnmocked
33+
* @param {string[]} options.badheaders
34+
* @param {function} options.conditionally
35+
* @param {boolean} options.encodedQueryParams
36+
* @param {function} options.filteringScope
37+
* @param {Object} options.reqheaders
38+
* @constructor
39+
*/
2940
function Scope(basePath, options) {
3041
EventEmitter.apply(this)
3142
this.keyedInterceptors = {}

tests/test_header_matching.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,17 @@ test('matches request header with regular expression', t => {
366366
)
367367
})
368368

369+
test('reqheaders throw if they are not an object', async t => {
370+
const options = {
371+
reqheaders: 'Content-Type: text/plain',
372+
}
373+
374+
t.throws(
375+
() => nock('http://example.test', options).get('/'),
376+
Error('Headers must be provided as an object')
377+
)
378+
})
379+
369380
test('request header satisfies the header function', t => {
370381
nock('http://example.test', {
371382
reqheaders: {

0 commit comments

Comments
 (0)