Repository: sindresorhus/got (ts)

Issue:
Add a `reject` option that allow to returns error rather than throwing them
It's sometime useful to make an http and only verify the `statusCode`. For example to check is a resource is available (check `statusCode === 404`) or authorized (check `statusCode === 401`).

Current that requires to handle the error with a `try / catch`:
```js
async function isAuthorized(url) {
  try {
    const response = await got(url);
    return response.statusCode !== 401;
  } catch (err) {
    if (err.statusCode) {
      return err.statusCode !== 401;
    }
    throw err;
  }
}
```
The `reject` option would work similarly to [execa#reject](https://github.com/sindresorhus/execa#reject). In case of a 4xx error the error would be returned instead of being thrown. Other error cases (5xx, network, timeout etc...) would throw the error. 

The code above would look like this:
```js
async function isAuthorized(url) {
  return (await got(url, {reject: false})).statusCode !== 401;
}
```


Pull request description:
Add `throwHttpErrors` option
Implemented the `throwHttpErrors` option discussed in #430.

- [X] Documented
- [X] Default to true
- [X] Implemented for HTTP
- [X] Implemented for Streams (for consistentency)
- [X] Tested
---
Closes #430 

Patch:
diff --git a/index.js b/index.js
index 6c82aa8..9f82c82 100644
--- a/index.js
+++ b/index.js
@@ -337,7 +337,7 @@ function asPromise(opts) {
 						}
 					}
 
-					if (statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
+					if (opts.throwHttpErrors && statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
 						throw new got.HTTPError(statusCode, res.statusMessage, res.headers, opts);
 					}
 
@@ -425,7 +425,7 @@ function asStream(opts) {
 
 		res.pipe(output);
 
-		if (statusCode !== 304 && (statusCode < 200 || statusCode > 299)) {
+		if (opts.throwHttpErrors && statusCode !== 304 && (statusCode < 200 || statusCode > 299)) {
 			proxy.emit('error', new got.HTTPError(statusCode, res.statusMessage, res.headers, opts), null, res);
 			return;
 		}
@@ -467,7 +467,8 @@ function normalizeArguments(url, opts) {
 			retries: 2,
 			cache: false,
 			decompress: true,
-			useElectronNet: false
+			useElectronNet: false,
+			throwHttpErrors: true
 		},
 		url,
 		{
diff --git a/readme.md b/readme.md
index 5daa9cc..2b3b7fe 100644
--- a/readme.md
+++ b/readme.md
@@ -206,6 +206,14 @@ Default: `false`
 
 When used in Electron, Got will use [`electron.net`](https://electronjs.org/docs/api/net/) instead of the Node.js `http` module. According to the Electron docs, it should be fully compatible, but it's not entirely. See [#315](https://github.com/sindresorhus/got/issues/315).
 
+###### throwHttpErrors
+
+Type: `boolean`<br>
+Default: `true`
+
+Determines if a `got.HTTPError` is thrown for error responses (non-2xx status codes).
+
+If this is disabled, requests that encounter an error status code will be resolved with the `response` instead of throwing. This may be useful if you are checking for resource availability and are expecting error responses.
 
 #### Streams
 


Test patch:
diff --git a/test/http.js b/test/http.js
index e9b35e8..e10b00c 100644
--- a/test/http.js
+++ b/test/http.js
@@ -60,6 +60,10 @@ test('status code 304 doesn\'t throw', async t => {
 	t.is(response.body, '');
 });
 
+test('doesn\'t throw on throwHttpErrors === false', async t => {
+	t.is((await got(`${s.url}/404`, {throwHttpErrors: false})).body, 'not');
+});
+
 test('invalid protocol throws', async t => {
 	const err = await t.throws(got('c:/nope.com', {json: true}));
 	t.is(err.constructor, got.UnsupportedProtocolError);
diff --git a/test/stream.js b/test/stream.js
index 4788143..9e6d723 100644
--- a/test/stream.js
+++ b/test/stream.js
@@ -84,6 +84,11 @@ test('have error event #2', async t => {
 	await t.throws(pEvent(stream, 'response'), /getaddrinfo ENOTFOUND/);
 });
 
+test('have response event on throwHttpErrors === false', async t => {
+	const response = await pEvent(got.stream(`${s.url}/error`, {throwHttpErrors: false}), 'response');
+	t.is(response.statusCode, 404);
+});
+
 test('accepts option.body as Stream', async t => {
 	const stream = got.stream(`${s.url}/post`, {body: intoStream(['wow'])});
 	const data = await pEvent(stream, 'data');