diff --git a/lib/core/constants.js b/lib/core/constants.js new file mode 100644 index 00000000000..0f827cc4ae0 --- /dev/null +++ b/lib/core/constants.js @@ -0,0 +1,116 @@ +/** @type {Record} */ +const headerNameLowerCasedRecord = {} + +// https://developer.mozilla.org/docs/Web/HTTP/Headers +const wellknownHeaderNames = [ + 'Accept', + 'Accept-Encoding', + 'Accept-Language', + 'Accept-Ranges', + 'Access-Control-Allow-Credentials', + 'Access-Control-Allow-Headers', + 'Access-Control-Allow-Methods', + 'Access-Control-Allow-Origin', + 'Access-Control-Expose-Headers', + 'Access-Control-Max-Age', + 'Access-Control-Request-Headers', + 'Access-Control-Request-Method', + 'Age', + 'Allow', + 'Alt-Svc', + 'Alt-Used', + 'Authorization', + 'Cache-Control', + 'Clear-Site-Data', + 'Connection', + 'Content-Disposition', + 'Content-Encoding', + 'Content-Language', + 'Content-Length', + 'Content-Location', + 'Content-Range', + 'Content-Security-Policy', + 'Content-Security-Policy-Report-Only', + 'Content-Type', + 'Cookie', + 'Cross-Origin-Embedder-Policy', + 'Cross-Origin-Opener-Policy', + 'Cross-Origin-Resource-Policy', + 'Date', + 'Device-Memory', + 'Downlink', + 'ECT', + 'ETag', + 'Expect', + 'Expect-CT', + 'Expires', + 'Forwarded', + 'From', + 'Host', + 'If-Match', + 'If-Modified-Since', + 'If-None-Match', + 'If-Range', + 'If-Unmodified-Since', + 'Keep-Alive', + 'Last-Modified', + 'Link', + 'Location', + 'Max-Forwards', + 'Origin', + 'Permissions-Policy', + 'Pragma', + 'Proxy-Authenticate', + 'Proxy-Authorization', + 'RTT', + 'Range', + 'Referer', + 'Referrer-Policy', + 'Refresh', + 'Retry-After', + 'Sec-WebSocket-Accept', + 'Sec-WebSocket-Extensions', + 'Sec-WebSocket-Key', + 'Sec-WebSocket-Protocol', + 'Sec-WebSocket-Version', + 'Server', + 'Server-Timing', + 'Service-Worker-Allowed', + 'Service-Worker-Navigation-Preload', + 'Set-Cookie', + 'SourceMap', + 'Strict-Transport-Security', + 'Supports-Loading-Mode', + 'TE', + 'Timing-Allow-Origin', + 'Trailer', + 'Transfer-Encoding', + 'Upgrade', + 'Upgrade-Insecure-Requests', + 'User-Agent', + 'Vary', + 'Via', + 'WWW-Authenticate', + 'X-Content-Type-Options', + 'X-DNS-Prefetch-Control', + 'X-Frame-Options', + 'X-Permitted-Cross-Domain-Policies', + 'X-Powered-By', + 'X-Requested-With', + 'X-XSS-Protection' +] + +for (let i = 0; i < wellknownHeaderNames.length; ++i) { + const key = wellknownHeaderNames[i] + const lowerCasedKey = key.toLowerCase() + headerNameLowerCasedRecord[key] = headerNameLowerCasedRecord[lowerCasedKey] = + lowerCasedKey +} + +// Note: object prototypes should not be able to be referenced. e.g. `Object#hasOwnProperty`. +Object.setPrototypeOf(headerNameLowerCasedRecord, null) + +module.exports = { + wellknownHeaderNames, + headerNameLowerCasedRecord +} diff --git a/lib/core/util.js b/lib/core/util.js index 82cf3260b9f..5a28529b663 100644 --- a/lib/core/util.js +++ b/lib/core/util.js @@ -9,6 +9,7 @@ const { InvalidArgumentError } = require('./errors') const { Blob } = require('buffer') const nodeUtil = require('util') const { stringify } = require('querystring') +const { headerNameLowerCasedRecord } = require('./constants') const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v)) @@ -223,19 +224,21 @@ function parseHeaders (headers, obj = {}) { if (!Array.isArray(headers)) return headers for (let i = 0; i < headers.length; i += 2) { - const key = headers[i].toString().toLowerCase() - let val = obj[key] + const key = headers[i].toString() + const lowerCasedKey = headerNameLowerCasedRecord[key] ?? key.toLowerCase() + let val = obj[lowerCasedKey] if (!val) { - if (Array.isArray(headers[i + 1])) { - obj[key] = headers[i + 1].map(x => x.toString('utf8')) + const headersValue = headers[i + 1] + if (typeof headersValue === 'string') { + obj[lowerCasedKey] = headersValue } else { - obj[key] = headers[i + 1].toString('utf8') + obj[lowerCasedKey] = Array.isArray(headersValue) ? headersValue.map(x => x.toString('utf8')) : headersValue.toString('utf8') } } else { if (!Array.isArray(val)) { val = [val] - obj[key] = val + obj[lowerCasedKey] = val } val.push(headers[i + 1].toString('utf8')) } diff --git a/test/util.js b/test/util.js index cd5c4ed3044..75a4d8c1617 100644 --- a/test/util.js +++ b/test/util.js @@ -6,6 +6,7 @@ const { Stream } = require('stream') const { EventEmitter } = require('events') const util = require('../lib/core/util') +const { headerNameLowerCasedRecord } = require('../lib/core/constants') const { InvalidArgumentError } = require('../lib/core/errors') test('isStream', (t) => { @@ -121,3 +122,8 @@ test('buildURL', (t) => { t.end() }) + +test('headerNameLowerCasedRecord', (t) => { + t.plan(1) + t.ok(typeof headerNameLowerCasedRecord.hasOwnProperty === 'undefined') +})