Repository: sindresorhus/got (ts)

Issue:
Intermittent uncaught errors
With got version 9.2.2, I'm seeing `timed-out` crash occasionally with an unhandled `error` event. I'm using the promise API, not the stream-based one, so I expect this event to always be turned into a promise rejection.

```
TimeoutError: Timeout awaiting 'response' for 2000ms 
  at Immediate.timeoutHandler (/node_modules/got/source/timed-out.js:39:25) 
```

Pull request:
Clear `socket` timeout on error
Fixes #631

Patch:
diff --git a/.github/ISSUE_TEMPLATE/1-bug-report.md b/.github/ISSUE_TEMPLATE/1-bug-report.md
new file mode 100644
index 0000000..442cb03
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/1-bug-report.md
@@ -0,0 +1,38 @@
+---
+name: "🐞 Bug report"
+about: Something is not working as it should
+---
+
+#### Describe the bug
+
+ - Node.js version: 
+ - OS & version: 
+
+<!-- A clear and concise description of what the bug is. -->
+
+#### Actual behavior
+
+...
+
+#### Expected behavior
+
+...
+
+#### Code to reproduce
+
+```js
+...
+```
+
+<!--
+We encourage you to submit a pull request with a failing test:
+ - This will make it more likely for us to prioritize your issue.
+ - It's a good way to prove that the issue is related to Got and not your code. 
+
+Example: https://github.com/avajs/ava/blob/master/docs/01-writing-tests.md#failing-tests
+-->
+
+#### Checklist
+
+- [ ] I have read the documentation.
+- [ ] I have tried my code with the latest version of Node.js and Got.
diff --git a/.github/ISSUE_TEMPLATE/2-feature-request.md b/.github/ISSUE_TEMPLATE/2-feature-request.md
new file mode 100644
index 0000000..96adeaa
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/2-feature-request.md
@@ -0,0 +1,18 @@
+---
+name: "⭐ Feature request"
+about: Suggest an idea for Got
+---
+
+#### What problem are you trying to solve?
+
+...
+
+#### Describe the feature
+
+...
+
+<!-- Include a usage example of the feature. If the feature is currently possible with a workaround, include that too. -->
+
+#### Checklist
+
+- [ ] I have read the documentation and made sure this feature doesn't already exist.
diff --git a/.github/ISSUE_TEMPLATE/3-question.md b/.github/ISSUE_TEMPLATE/3-question.md
new file mode 100644
index 0000000..87211f0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/3-question.md
@@ -0,0 +1,12 @@
+---
+name: "❓ Question"
+about: Something is unclear or needs to be discussed
+---
+
+#### What would you like to discuss?
+
+...
+
+#### Checklist
+
+- [ ] I have read the documentation.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..9e9a219
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,6 @@
+#### Checklist
+
+- [ ] I have read the documentation.
+- [ ] I have included a pull request description of my changes.
+- [ ] I have included some tests.
+- [ ] If it's a new feature, I have included documentation updates.
diff --git a/source/normalize-arguments.js b/source/normalize-arguments.js
index f735386..3de0fab 100644
--- a/source/normalize-arguments.js
+++ b/source/normalize-arguments.js
@@ -96,7 +96,7 @@ const normalize = (url, options, defaults) => {
 	if (defaults) {
 		options = merge({}, defaults.options, options ? preNormalize(options, defaults.options) : {});
 	} else {
-		options = merge({}, options ? preNormalize(options) : {});
+		options = merge({}, preNormalize(options));
 	}
 
 	if (!is.string(url) && !is.object(url)) {
diff --git a/source/utils/timed-out.js b/source/utils/timed-out.js
index d993231..a0efde4 100644
--- a/source/utils/timed-out.js
+++ b/source/utils/timed-out.js
@@ -85,6 +85,8 @@ module.exports = (request, delays, options) => {
 		request.setTimeout(delays.socket, () => {
 			timeoutHandler(delays.socket, 'socket');
 		});
+
+		cancelers.push(() => request.setTimeout(0));
 	}
 
 	if (delays.lookup !== undefined && !request.socketPath && !net.isIP(hostname || host)) {


Test patch:
diff --git a/test/timeout.js b/test/timeout.js
index 6cff58e..c36f6df 100644
--- a/test/timeout.js
+++ b/test/timeout.js
@@ -458,3 +458,18 @@ test('no more timeouts after an error', async t => {
 	// Wait a bit more to check if there are any unhandled errors
 	await delay(10);
 });
+
+test('socket timeout is canceled on error', async t => {
+	const message = 'oh, snap!';
+
+	const promise = got(s.url, {
+		timeout: {socket: requestTimeout},
+		retry: 0
+	}).on('request', request => {
+		request.emit('error', new Error(message));
+	});
+
+	await t.throwsAsync(promise, {message});
+	// Wait a bit more to check if there are any unhandled errors
+	await delay(10);
+});