Repository: sindresorhus/got (ts)

Issue:
Headers are case sensitive
When supplying headers (notably "Accept"), the keys are key sensitive, which, for me, broke my apps since I was supplying `{json: true}` alongside a header object with an `{Accept: "application/something.else"}`

The normalize-arguments code then overwrote my Accept header with the accept header.
I can submit a PR to fix this if you like, otherwise giving it a gotcha note on the docs would be nice.

Pull request description:
Transform user set headers to lowercase
There's one thing that should happen which is to lowercase all the headers passed so no duplicate lowercase keys can exist. Because 1. we have logic that reads and writes headers, but 2. we have no clue what node will do when we pass it duplicate header keys.

The reason this is hard to test is that we use `cacheable-request` and by the time the `request` event fires the headers have already been merged, with keys set later in time overwriting ones set earlier in time. So I test by setting some header in uppercase I know with got's current implementation is followed by a key set in lowercase before everything is passed to `cacheable-request`. That's brittle. If someone has a better idea let me know.

Fixes #612 .

Patch:
diff --git a/package.json b/package.json
index 2504692..3212f75 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "got",
-	"version": "9.2.1",
+	"version": "9.2.2",
 	"description": "Simplified HTTP requests",
 	"license": "MIT",
 	"repository": "sindresorhus/got",
@@ -39,6 +39,7 @@
 		"decompress-response": "^3.3.0",
 		"duplexer3": "^0.1.4",
 		"get-stream": "^4.0.0",
+		"lowercase-keys": "^1.0.1",
 		"mimic-response": "^1.0.1",
 		"p-cancelable": "^0.5.0",
 		"to-readable-stream": "^1.0.0",
diff --git a/readme.md b/readme.md
index 51f7bf6..c86dad4 100644
--- a/readme.md
+++ b/readme.md
@@ -35,9 +35,10 @@ Got is for Node.js. For browsers, we recommend [Ky](https://github.com/sindresor
 - [Errors with metadata](#errors)
 - [JSON mode](#json)
 - [WHATWG URL support](#url)
-- [Electron support](#useelectronnet)
+- [Hooks](https://github.com/sindresorhus/got#hooks)
 - [Instances with custom defaults](#instances)
 - [Composable](advanced-creation.md#merging-instances)
+- [Electron support](#useelectronnet)
 - [Used by ~2000 packages and ~500K repos](https://github.com/sindresorhus/got/network/dependents)
 - Actively maintained
 
diff --git a/source/normalize-arguments.js b/source/normalize-arguments.js
index c066207..a70159b 100644
--- a/source/normalize-arguments.js
+++ b/source/normalize-arguments.js
@@ -3,6 +3,7 @@ const {URL, URLSearchParams} = require('url'); // TODO: Use the `URL` global whe
 const is = require('@sindresorhus/is');
 const toReadableStream = require('to-readable-stream');
 const urlParseLax = require('url-parse-lax');
+const lowercaseKeys = require('lowercase-keys');
 const isRetryOnNetworkErrorAllowed = require('./is-retry-on-network-error-allowed');
 const urlToOptions = require('./url-to-options');
 const isFormData = require('./is-form-data');
@@ -15,10 +16,15 @@ const retryAfterStatusCodes = new Set([413, 429, 503]);
 // While `normalize` does `preNormalize` + handles things related to dynamic options, like URL, headers, body, etc.
 const preNormalize = options => {
 	options = {
-		headers: {},
 		...options
 	};
 
+	if (is.nullOrUndefined(options.headers)) {
+		options.headers = {};
+	} else {
+		options.headers = lowercaseKeys(options.headers);
+	}
+
 	if (options.baseUrl && !options.baseUrl.toString().endsWith('/')) {
 		options.baseUrl += '/';
 	}


Test patch:
diff --git a/test/headers.js b/test/headers.js
index 1f74f40..9cc7ded 100644
--- a/test/headers.js
+++ b/test/headers.js
@@ -73,11 +73,11 @@ test('host', async t => {
 test('transform names to lowercase', async t => {
 	const headers = (await got(s.url, {
 		headers: {
-			'USER-AGENT': 'test'
+			'ACCEPT-ENCODING': 'identity'
 		},
 		json: true
 	})).body;
-	t.is(headers['user-agent'], 'test');
+	t.is(headers['accept-encoding'], 'identity');
 });
 
 test('setting content-length to 0', async t => {