got 6.6.3 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.js +213 -99
  2. package/package.json +33 -21
  3. package/readme.md +125 -27
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  'use strict';
2
- const EventEmitter = require('events').EventEmitter;
2
+ const EventEmitter = require('events');
3
3
  const http = require('http');
4
4
  const https = require('https');
5
5
  const PassThrough = require('stream').PassThrough;
@@ -10,48 +10,72 @@ const isStream = require('is-stream');
10
10
  const getStream = require('get-stream');
11
11
  const timedOut = require('timed-out');
12
12
  const urlParseLax = require('url-parse-lax');
13
+ const urlToOptions = require('url-to-options');
13
14
  const lowercaseKeys = require('lowercase-keys');
14
- const isRedirect = require('is-redirect');
15
- const unzipResponse = require('unzip-response');
16
- const createErrorClass = require('create-error-class');
17
- const nodeStatusCodes = require('node-status-codes');
15
+ const decompressResponse = require('decompress-response');
18
16
  const isRetryAllowed = require('is-retry-allowed');
19
- const pkg = require('./package.json');
17
+ const Buffer = require('safe-buffer').Buffer;
18
+ const isURL = require('isurl');
19
+ const isPlainObj = require('is-plain-obj');
20
+ const PCancelable = require('p-cancelable');
21
+ const pTimeout = require('p-timeout');
22
+ const pkg = require('./package');
20
23
 
21
- const isModernBuffer = (
22
- typeof Buffer.alloc === 'function' &&
23
- typeof Buffer.allocUnsafe === 'function' &&
24
- typeof Buffer.from === 'function'
25
- );
24
+ const getMethodRedirectCodes = new Set([300, 301, 302, 303, 304, 305, 307, 308]);
25
+ const allMethodRedirectCodes = new Set([300, 303, 307, 308]);
26
26
 
27
27
  function requestAsEventEmitter(opts) {
28
28
  opts = opts || {};
29
29
 
30
30
  const ee = new EventEmitter();
31
31
  const requestUrl = opts.href || urlLib.resolve(urlLib.format(opts), opts.path);
32
- let redirectCount = 0;
32
+ const redirects = [];
33
33
  let retryCount = 0;
34
34
  let redirectUrl;
35
35
 
36
36
  const get = opts => {
37
- const fn = opts.protocol === 'https:' ? https : http;
37
+ if (opts.protocol !== 'http:' && opts.protocol !== 'https:') {
38
+ ee.emit('error', new got.UnsupportedProtocolError(opts));
39
+ return;
40
+ }
41
+
42
+ let fn = opts.protocol === 'https:' ? https : http;
43
+
44
+ if (opts.useElectronNet && process.versions.electron) {
45
+ const electron = require('electron');
46
+ fn = electron.net || electron.remote.net;
47
+ }
38
48
 
39
49
  const req = fn.request(opts, res => {
40
50
  const statusCode = res.statusCode;
41
51
 
42
- if (isRedirect(statusCode) && opts.followRedirect && 'location' in res.headers && (opts.method === 'GET' || opts.method === 'HEAD')) {
52
+ res.url = redirectUrl || requestUrl;
53
+ res.requestUrl = requestUrl;
54
+
55
+ const followRedirect = opts.followRedirect && 'location' in res.headers;
56
+ const redirectGet = followRedirect && getMethodRedirectCodes.has(statusCode);
57
+ const redirectAll = followRedirect && allMethodRedirectCodes.has(statusCode);
58
+
59
+ if (redirectAll || (redirectGet && (opts.method === 'GET' || opts.method === 'HEAD'))) {
43
60
  res.resume();
44
61
 
45
- if (++redirectCount > 10) {
46
- ee.emit('error', new got.MaxRedirectsError(statusCode, opts), null, res);
62
+ if (statusCode === 303) {
63
+ // Server responded with "see other", indicating that the resource exists at another location,
64
+ // and the client should request it from that location via GET or HEAD.
65
+ opts.method = 'GET';
66
+ }
67
+
68
+ if (redirects.length >= 10) {
69
+ ee.emit('error', new got.MaxRedirectsError(statusCode, redirects, opts), null, res);
47
70
  return;
48
71
  }
49
72
 
50
- const bufferString = isModernBuffer ?
51
- Buffer.from(res.headers.location, 'binary').toString() :
52
- new Buffer(res.headers.location, 'binary').toString();
73
+ const bufferString = Buffer.from(res.headers.location, 'binary').toString();
53
74
 
54
75
  redirectUrl = urlLib.resolve(urlLib.format(opts), bufferString);
76
+
77
+ redirects.push(redirectUrl);
78
+
55
79
  const redirectOpts = Object.assign({}, opts, urlLib.parse(redirectUrl));
56
80
 
57
81
  ee.emit('redirect', res, redirectOpts);
@@ -62,9 +86,15 @@ function requestAsEventEmitter(opts) {
62
86
  }
63
87
 
64
88
  setImmediate(() => {
65
- const response = typeof unzipResponse === 'function' && req.method !== 'HEAD' ? unzipResponse(res) : res;
66
- response.url = redirectUrl || requestUrl;
67
- response.requestUrl = requestUrl;
89
+ const response = opts.decompress === true &&
90
+ typeof decompressResponse === 'function' &&
91
+ req.method !== 'HEAD' ? decompressResponse(res) : res;
92
+
93
+ if (!opts.decompress && ['gzip', 'deflate'].indexOf(res.headers['content-encoding']) !== -1) {
94
+ opts.encoding = null;
95
+ }
96
+
97
+ response.redirectUrls = redirects;
68
98
 
69
99
  ee.emit('response', response);
70
100
  });
@@ -81,22 +111,43 @@ function requestAsEventEmitter(opts) {
81
111
  ee.emit('error', new got.RequestError(err, opts));
82
112
  });
83
113
 
84
- if (opts.timeout) {
85
- timedOut(req, opts.timeout);
114
+ if (opts.gotTimeout) {
115
+ timedOut(req, opts.gotTimeout);
86
116
  }
87
117
 
88
- setImmediate(() => ee.emit('request', req));
118
+ setImmediate(() => {
119
+ ee.emit('request', req);
120
+ });
89
121
  };
90
122
 
91
- get(opts);
123
+ setImmediate(() => {
124
+ get(opts);
125
+ });
92
126
  return ee;
93
127
  }
94
128
 
95
129
  function asPromise(opts) {
96
- return new Promise((resolve, reject) => {
130
+ const timeoutFn = requestPromise => opts.gotTimeout && opts.gotTimeout.request ?
131
+ pTimeout(requestPromise, opts.gotTimeout.request, new got.RequestError({message: 'Request timed out', code: 'ETIMEDOUT'}, opts)) :
132
+ requestPromise;
133
+
134
+ return timeoutFn(new PCancelable((onCancel, resolve, reject) => {
97
135
  const ee = requestAsEventEmitter(opts);
136
+ let cancelOnRequest = false;
137
+
138
+ onCancel(() => {
139
+ cancelOnRequest = true;
140
+ });
98
141
 
99
142
  ee.on('request', req => {
143
+ if (cancelOnRequest) {
144
+ req.abort();
145
+ }
146
+
147
+ onCancel(() => {
148
+ req.abort();
149
+ });
150
+
100
151
  if (isStream(opts.body)) {
101
152
  opts.body.pipe(req);
102
153
  opts.body = undefined;
@@ -121,12 +172,14 @@ function asPromise(opts) {
121
172
  try {
122
173
  res.body = JSON.parse(res.body);
123
174
  } catch (e) {
124
- throw new got.ParseError(e, statusCode, opts, data);
175
+ if (statusCode >= 200 && statusCode < 300) {
176
+ throw new got.ParseError(e, statusCode, opts, data);
177
+ }
125
178
  }
126
179
  }
127
180
 
128
- if (statusCode < 200 || statusCode > limitStatusCode) {
129
- throw new got.HTTPError(statusCode, opts);
181
+ if (statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
182
+ throw new got.HTTPError(statusCode, res.headers, opts);
130
183
  }
131
184
 
132
185
  resolve(res);
@@ -138,13 +191,20 @@ function asPromise(opts) {
138
191
  });
139
192
 
140
193
  ee.on('error', reject);
141
- });
194
+ }));
142
195
  }
143
196
 
144
197
  function asStream(opts) {
145
198
  const input = new PassThrough();
146
199
  const output = new PassThrough();
147
200
  const proxy = duplexer3(input, output);
201
+ let timeout;
202
+
203
+ if (opts.gotTimeout && opts.gotTimeout.request) {
204
+ timeout = setTimeout(() => {
205
+ proxy.emit('error', new got.RequestError({message: 'Request timed out', code: 'ETIMEDOUT'}, opts));
206
+ }, opts.gotTimeout.request);
207
+ }
148
208
 
149
209
  if (opts.json) {
150
210
  throw new Error('got can not be used as stream when options.json is used');
@@ -180,12 +240,14 @@ function asStream(opts) {
180
240
  });
181
241
 
182
242
  ee.on('response', res => {
243
+ clearTimeout(timeout);
244
+
183
245
  const statusCode = res.statusCode;
184
246
 
185
247
  res.pipe(output);
186
248
 
187
- if (statusCode < 200 || statusCode > 299) {
188
- proxy.emit('error', new got.HTTPError(statusCode, opts), null, res);
249
+ if (statusCode !== 304 && (statusCode < 200 || statusCode > 299)) {
250
+ proxy.emit('error', new got.HTTPError(statusCode, res.headers, opts), null, res);
189
251
  return;
190
252
  }
191
253
 
@@ -200,25 +262,29 @@ function asStream(opts) {
200
262
 
201
263
  function normalizeArguments(url, opts) {
202
264
  if (typeof url !== 'string' && typeof url !== 'object') {
203
- throw new Error(`Parameter \`url\` must be a string or object, not ${typeof url}`);
204
- }
205
-
206
- if (typeof url === 'string') {
265
+ throw new TypeError(`Parameter \`url\` must be a string or object, not ${typeof url}`);
266
+ } else if (typeof url === 'string') {
207
267
  url = url.replace(/^unix:/, 'http://$&');
208
268
  url = urlParseLax(url);
269
+ } else if (isURL.lenient(url)) {
270
+ url = urlToOptions(url);
271
+ }
209
272
 
210
- if (url.auth) {
211
- throw new Error('Basic authentication must be done with auth option');
212
- }
273
+ if (url.auth) {
274
+ throw new Error('Basic authentication must be done with auth option');
213
275
  }
214
276
 
215
277
  opts = Object.assign(
216
278
  {
217
- protocol: 'http:',
218
279
  path: '',
219
- retries: 5
280
+ retries: 2,
281
+ decompress: true,
282
+ useElectronNet: true
220
283
  },
221
284
  url,
285
+ {
286
+ protocol: url.protocol || 'http:' // Override both null/undefined with default protocol
287
+ },
222
288
  opts
223
289
  );
224
290
 
@@ -242,33 +308,41 @@ function normalizeArguments(url, opts) {
242
308
  opts.headers.accept = 'application/json';
243
309
  }
244
310
 
245
- let body = opts.body;
246
-
247
- if (body) {
248
- if (typeof body !== 'string' && !(body !== null && typeof body === 'object')) {
249
- throw new Error('options.body must be a ReadableStream, string, Buffer or plain Object');
311
+ const body = opts.body;
312
+ if (body !== null && body !== undefined) {
313
+ const headers = opts.headers;
314
+ if (!isStream(body) && typeof body !== 'string' && !Buffer.isBuffer(body) && !(opts.form || opts.json)) {
315
+ throw new TypeError('options.body must be a ReadableStream, string, Buffer or plain Object');
250
316
  }
251
317
 
252
- opts.method = opts.method || 'POST';
318
+ const canBodyBeStringified = isPlainObj(body) || Array.isArray(body);
319
+ if ((opts.form || opts.json) && !canBodyBeStringified) {
320
+ throw new TypeError('options.body must be a plain Object or Array when options.form or options.json is used');
321
+ }
253
322
 
254
323
  if (isStream(body) && typeof body.getBoundary === 'function') {
255
324
  // Special case for https://github.com/form-data/form-data
256
- opts.headers['content-type'] = opts.headers['content-type'] || `multipart/form-data; boundary=${body.getBoundary()}`;
257
- } else if (body !== null && typeof body === 'object' && !Buffer.isBuffer(body) && !isStream(body)) {
258
- opts.headers['content-type'] = opts.headers['content-type'] || 'application/x-www-form-urlencoded';
259
- body = opts.body = querystring.stringify(body);
325
+ headers['content-type'] = headers['content-type'] || `multipart/form-data; boundary=${body.getBoundary()}`;
326
+ } else if (opts.form && canBodyBeStringified) {
327
+ headers['content-type'] = headers['content-type'] || 'application/x-www-form-urlencoded';
328
+ opts.body = querystring.stringify(body);
329
+ } else if (opts.json && canBodyBeStringified) {
330
+ headers['content-type'] = headers['content-type'] || 'application/json';
331
+ opts.body = JSON.stringify(body);
260
332
  }
261
333
 
262
- if (opts.headers['content-length'] === undefined && opts.headers['transfer-encoding'] === undefined && !isStream(body)) {
263
- const length = typeof body === 'string' ? Buffer.byteLength(body) : body.length;
264
- opts.headers['content-length'] = length;
334
+ if (headers['content-length'] === undefined && headers['transfer-encoding'] === undefined && !isStream(body)) {
335
+ const length = typeof opts.body === 'string' ? Buffer.byteLength(opts.body) : opts.body.length;
336
+ headers['content-length'] = length;
265
337
  }
266
- }
267
338
 
268
- opts.method = (opts.method || 'GET').toUpperCase();
339
+ opts.method = (opts.method || 'POST').toUpperCase();
340
+ } else {
341
+ opts.method = (opts.method || 'GET').toUpperCase();
342
+ }
269
343
 
270
344
  if (opts.hostname === 'unix') {
271
- const matches = /(.+):(.+)/.exec(opts.path);
345
+ const matches = /(.+?):(.+)/.exec(opts.path);
272
346
 
273
347
  if (matches) {
274
348
  opts.socketPath = matches[1];
@@ -295,6 +369,15 @@ function normalizeArguments(url, opts) {
295
369
  opts.followRedirect = true;
296
370
  }
297
371
 
372
+ if (opts.timeout) {
373
+ if (typeof opts.timeout === 'number') {
374
+ opts.gotTimeout = {request: opts.timeout};
375
+ } else {
376
+ opts.gotTimeout = opts.timeout;
377
+ }
378
+ delete opts.timeout;
379
+ }
380
+
298
381
  return opts;
299
382
  }
300
383
 
@@ -306,7 +389,9 @@ function got(url, opts) {
306
389
  }
307
390
  }
308
391
 
309
- const helpers = [
392
+ got.stream = (url, opts) => asStream(normalizeArguments(url, opts));
393
+
394
+ const methods = [
310
395
  'get',
311
396
  'post',
312
397
  'put',
@@ -315,51 +400,80 @@ const helpers = [
315
400
  'delete'
316
401
  ];
317
402
 
318
- helpers.forEach(el => {
319
- got[el] = (url, opts) => got(url, Object.assign({}, opts, {method: el}));
320
- });
403
+ for (const method of methods) {
404
+ got[method] = (url, opts) => got(url, Object.assign({}, opts, {method}));
405
+ got.stream[method] = (url, opts) => got.stream(url, Object.assign({}, opts, {method}));
406
+ }
321
407
 
322
- got.stream = (url, opts) => asStream(normalizeArguments(url, opts));
408
+ class StdError extends Error {
409
+ constructor(message, error, opts) {
410
+ super(message);
411
+ this.name = 'StdError';
323
412
 
324
- helpers.forEach(el => {
325
- got.stream[el] = (url, opts) => got.stream(url, Object.assign({}, opts, {method: el}));
326
- });
413
+ if (error.code !== undefined) {
414
+ this.code = error.code;
415
+ }
327
416
 
328
- function stdError(error, opts) {
329
- if (error.code !== undefined) {
330
- this.code = error.code;
417
+ Object.assign(this, {
418
+ host: opts.host,
419
+ hostname: opts.hostname,
420
+ method: opts.method,
421
+ path: opts.path,
422
+ protocol: opts.protocol,
423
+ url: opts.href
424
+ });
331
425
  }
332
-
333
- Object.assign(this, {
334
- message: error.message,
335
- host: opts.host,
336
- hostname: opts.hostname,
337
- method: opts.method,
338
- path: opts.path
339
- });
340
426
  }
341
427
 
342
- got.RequestError = createErrorClass('RequestError', stdError);
343
- got.ReadError = createErrorClass('ReadError', stdError);
344
- got.ParseError = createErrorClass('ParseError', function (e, statusCode, opts, data) {
345
- stdError.call(this, e, opts);
346
- this.statusCode = statusCode;
347
- this.statusMessage = nodeStatusCodes[this.statusCode];
348
- this.message = `${e.message} in "${urlLib.format(opts)}": \n${data.slice(0, 77)}...`;
349
- });
350
-
351
- got.HTTPError = createErrorClass('HTTPError', function (statusCode, opts) {
352
- stdError.call(this, {}, opts);
353
- this.statusCode = statusCode;
354
- this.statusMessage = nodeStatusCodes[this.statusCode];
355
- this.message = `Response code ${this.statusCode} (${this.statusMessage})`;
356
- });
357
-
358
- got.MaxRedirectsError = createErrorClass('MaxRedirectsError', function (statusCode, opts) {
359
- stdError.call(this, {}, opts);
360
- this.statusCode = statusCode;
361
- this.statusMessage = nodeStatusCodes[this.statusCode];
362
- this.message = 'Redirected 10 times. Aborting.';
363
- });
428
+ got.RequestError = class extends StdError {
429
+ constructor(error, opts) {
430
+ super(error.message, error, opts);
431
+ this.name = 'RequestError';
432
+ }
433
+ };
434
+
435
+ got.ReadError = class extends StdError {
436
+ constructor(error, opts) {
437
+ super(error.message, error, opts);
438
+ this.name = 'ReadError';
439
+ }
440
+ };
441
+
442
+ got.ParseError = class extends StdError {
443
+ constructor(error, statusCode, opts, data) {
444
+ super(`${error.message} in "${urlLib.format(opts)}": \n${data.slice(0, 77)}...`, error, opts);
445
+ this.name = 'ParseError';
446
+ this.statusCode = statusCode;
447
+ this.statusMessage = http.STATUS_CODES[this.statusCode];
448
+ }
449
+ };
450
+
451
+ got.HTTPError = class extends StdError {
452
+ constructor(statusCode, headers, opts) {
453
+ const statusMessage = http.STATUS_CODES[statusCode];
454
+ super(`Response code ${statusCode} (${statusMessage})`, {}, opts);
455
+ this.name = 'HTTPError';
456
+ this.statusCode = statusCode;
457
+ this.statusMessage = statusMessage;
458
+ this.headers = headers;
459
+ }
460
+ };
461
+
462
+ got.MaxRedirectsError = class extends StdError {
463
+ constructor(statusCode, redirectUrls, opts) {
464
+ super('Redirected 10 times. Aborting.', {}, opts);
465
+ this.name = 'MaxRedirectsError';
466
+ this.statusCode = statusCode;
467
+ this.statusMessage = http.STATUS_CODES[this.statusCode];
468
+ this.redirectUrls = redirectUrls;
469
+ }
470
+ };
471
+
472
+ got.UnsupportedProtocolError = class extends StdError {
473
+ constructor(opts) {
474
+ super(`Unsupported protocol "${opts.protocol}"`, {}, opts);
475
+ this.name = 'UnsupportedProtocolError';
476
+ }
477
+ };
364
478
 
365
479
  module.exports = got;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "got",
3
- "version": "6.6.3",
3
+ "version": "7.1.0",
4
4
  "description": "Simplified HTTP requests",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/got",
@@ -14,14 +14,16 @@
14
14
  "name": "Vsevolod Strukchinsky",
15
15
  "email": "floatdrop@gmail.com",
16
16
  "url": "github.com/floatdrop"
17
+ },
18
+ {
19
+ "name": "Alexander Tesfamichael",
20
+ "email": "alex.tesfamichael@gmail.com",
21
+ "url": "alextes.me"
17
22
  }
18
23
  ],
19
24
  "engines": {
20
25
  "node": ">=4"
21
26
  },
22
- "browser": {
23
- "unzip-response": false
24
- },
25
27
  "scripts": {
26
28
  "test": "xo && nyc ava",
27
29
  "coveralls": "nyc report --reporter=text-lcov | coveralls"
@@ -42,35 +44,45 @@
42
44
  "simple",
43
45
  "curl",
44
46
  "wget",
45
- "fetch"
47
+ "fetch",
48
+ "net",
49
+ "network",
50
+ "electron"
46
51
  ],
47
52
  "dependencies": {
48
- "create-error-class": "^3.0.0",
53
+ "decompress-response": "^3.2.0",
49
54
  "duplexer3": "^0.1.4",
50
- "get-stream": "^2.3.0",
51
- "is-redirect": "^1.0.0",
55
+ "get-stream": "^3.0.0",
56
+ "is-plain-obj": "^1.1.0",
52
57
  "is-retry-allowed": "^1.0.0",
53
58
  "is-stream": "^1.0.0",
59
+ "isurl": "^1.0.0-alpha5",
54
60
  "lowercase-keys": "^1.0.0",
55
- "node-status-codes": "^2.0.0",
56
- "timed-out": "^3.0.0",
57
- "unzip-response": "^2.0.1",
58
- "url-parse-lax": "^1.0.0"
61
+ "p-cancelable": "^0.3.0",
62
+ "p-timeout": "^1.1.1",
63
+ "safe-buffer": "^5.0.1",
64
+ "timed-out": "^4.0.0",
65
+ "url-parse-lax": "^1.0.0",
66
+ "url-to-options": "^1.0.1"
59
67
  },
60
68
  "devDependencies": {
61
- "ava": "^0.16.0",
69
+ "ava": "^0.20.0",
62
70
  "coveralls": "^2.11.4",
63
71
  "form-data": "^2.1.1",
64
- "get-port": "^2.0.0",
65
- "get-stream": "^2.3.0",
72
+ "get-port": "^3.0.0",
66
73
  "into-stream": "^3.0.0",
67
- "nyc": "^8.1.0",
74
+ "nyc": "^11.0.2",
68
75
  "pem": "^1.4.4",
69
- "pify": "^2.3.0",
70
- "tempfile": "^1.1.1",
71
- "xo": "*"
76
+ "pify": "^3.0.0",
77
+ "tempfile": "^2.0.0",
78
+ "tempy": "^0.1.0",
79
+ "universal-url": "^1.0.0-alpha",
80
+ "xo": "^0.18.0"
81
+ },
82
+ "ava": {
83
+ "concurrency": 4
72
84
  },
73
- "xo": {
74
- "esnext": true
85
+ "browser": {
86
+ "decompress-response": false
75
87
  }
76
88
  }
package/readme.md CHANGED
@@ -12,14 +12,24 @@
12
12
 
13
13
  A nicer interface to the built-in [`http`](http://nodejs.org/api/http.html) module.
14
14
 
15
- It supports following redirects, promises, streams, retries, automagically handling gzip/deflate and some convenience options.
16
-
17
15
  Created because [`request`](https://github.com/request/request) is bloated *(several megabytes!)*.
18
16
 
19
17
 
20
- ## Install
18
+ ## Highlights
21
19
 
22
- **WARNING: Node.js 4 or higher is required for got@6 and above.** For older Node.js versions use [got@5](https://github.com/sindresorhus/got/tree/v5.x).
20
+ - [Promise & stream API](#api)
21
+ - [Request cancelation](#aborting-the-request)
22
+ - [Follows redirects](#followredirect)
23
+ - [Retries on network failure](#retries)
24
+ - [Handles gzip/deflate](#decompress)
25
+ - [Timeout handling](#timeout)
26
+ - [Errors with metadata](#errors)
27
+ - [JSON mode](#json)
28
+ - [WHATWG URL support](#url)
29
+ - [Electron support](#useelectronnet)
30
+
31
+
32
+ ## Install
23
33
 
24
34
  ```
25
35
  $ npm install --save got
@@ -60,21 +70,21 @@ Returns a Promise for a `response` object with a `body` property, a `url` proper
60
70
 
61
71
  ##### url
62
72
 
63
- Type: `string`, `object`
73
+ Type: `string` `Object`
64
74
 
65
- The URL to request or a [`http.request` options](https://nodejs.org/api/http.html#http_http_request_options_callback) object.
75
+ The URL to request as simple string, a [`http.request` options](https://nodejs.org/api/http.html#http_http_request_options_callback), or a [WHATWG `URL`](https://nodejs.org/api/url.html#url_class_url).
66
76
 
67
77
  Properties from `options` will override properties in the parsed `url`.
68
78
 
69
79
  ##### options
70
80
 
71
- Type: `object`
81
+ Type: `Object`
72
82
 
73
83
  Any of the [`http.request`](http://nodejs.org/api/http.html#http_http_request_options_callback) options.
74
84
 
75
85
  ###### body
76
86
 
77
- Type: `string`, `buffer`, `readableStream`, `object`
87
+ Type: `string` `Buffer` `stream.Readable`
78
88
 
79
89
  *This is mutually exclusive with stream mode.*
80
90
 
@@ -84,14 +94,23 @@ If present in `options` and `options.method` is not set, `options.method` will b
84
94
 
85
95
  If `content-length` or `transfer-encoding` is not set in `options.headers` and `body` is a string or buffer, `content-length` will be set to the body length.
86
96
 
87
- If `body` is a plain object, it will be stringified with [`querystring.stringify`](https://nodejs.org/api/querystring.html#querystring_querystring_stringify_obj_sep_eq_options) and sent as `application/x-www-form-urlencoded`.
88
-
89
97
  ###### encoding
90
98
 
91
- Type: `string`, `null`<br>
99
+ Type: `string` `null`<br>
92
100
  Default: `'utf8'`
93
101
 
94
- Encoding to be used on `setEncoding` of the response data. If `null`, the body is returned as a Buffer.
102
+ [Encoding](https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings) to be used on `setEncoding` of the response data. If `null`, the body is returned as a Buffer.
103
+
104
+ ###### form
105
+
106
+ Type: `boolean`<br>
107
+ Default: `false`
108
+
109
+ *This is mutually exclusive with stream mode.*
110
+
111
+ If set to `true` and `Content-Type` header is not set, it will be set to `application/x-www-form-urlencoded`.
112
+
113
+ `body` must be a plain object or array and will be stringified.
95
114
 
96
115
  ###### json
97
116
 
@@ -100,24 +119,30 @@ Default: `false`
100
119
 
101
120
  *This is mutually exclusive with stream mode.*
102
121
 
103
- Parse response body with `JSON.parse` and set `accept` header to `application/json`.
122
+ If set to `true` and `Content-Type` header is not set, it will be set to `application/json`.
123
+
124
+ Parse response body with `JSON.parse` and set `accept` header to `application/json`. If used in conjunction with the `form` option, the `body` will the stringified as querystring and the response parsed as JSON.
125
+
126
+ `body` must be a plain object or array and will be stringified.
104
127
 
105
128
  ###### query
106
129
 
107
- Type: `string`, `object`<br>
130
+ Type: `string` `Object`<br>
108
131
 
109
132
  Query string object that will be added to the request URL. This will override the query string in `url`.
110
133
 
111
134
  ###### timeout
112
135
 
113
- Type: `number`
136
+ Type: `number` `Object`
114
137
 
115
- Milliseconds to wait for a server to send response headers before aborting request with `ETIMEDOUT` error.
138
+ Milliseconds to wait for the server to end the response before aborting request with `ETIMEDOUT` error.
139
+
140
+ This also accepts an object with separate `connect`, `socket`, and `request` fields for connection, socket, and entire request timeouts.
116
141
 
117
142
  ###### retries
118
143
 
119
- Type: `number`, `function`<br>
120
- Default: `5`
144
+ Type: `number` `Function`<br>
145
+ Default: `2`
121
146
 
122
147
  Number of request retries when network errors happens. Delays between retries counts with function `1000 * Math.pow(2, retry) + Math.random() * 100`, where `retry` is attempt number (starts from 0).
123
148
 
@@ -132,6 +157,25 @@ Default: `true`
132
157
 
133
158
  Defines if redirect responses should be followed automatically.
134
159
 
160
+ Note that if a `303` is sent by the server in response to any request type (`POST`, `DELETE`, etc.), got will automatically
161
+ request the resource pointed to in the location header via `GET`. This is in accordance with [the spec](https://tools.ietf.org/html/rfc7231#section-6.4.4).
162
+
163
+ ###### decompress
164
+
165
+ Type: `boolean`<br>
166
+ Default: `true`
167
+
168
+ Decompress the response automatically.
169
+
170
+ If this is disabled, a compressed response is returned as a `Buffer`. This may be useful if you want to handle decompression yourself or stream the raw compressed data.
171
+
172
+ ###### useElectronNet
173
+
174
+ Type: `boolean`<br>
175
+ Default: `true`
176
+
177
+ When used in Electron, Got will automatically use [`electron.net`](https://electron.atom.io/docs/api/net/) instead of the Node.js `http` module. It should be fully compatible, but you can turn it off here if you encounter a problem. Please open an issue if you do!
178
+
135
179
 
136
180
  #### Streams
137
181
 
@@ -174,7 +218,7 @@ Sets `options.method` to the method name and makes a request.
174
218
 
175
219
  ## Errors
176
220
 
177
- Each error contains (if available) `statusCode`, `statusMessage`, `host`, `hostname`, `method` and `path` properties to make debugging easier.
221
+ Each error contains (if available) `statusCode`, `statusMessage`, `host`, `hostname`, `method`, `path`, `protocol` and `url` properties to make debugging easier.
178
222
 
179
223
  In Promise mode, the `response` is attached to the error.
180
224
 
@@ -188,15 +232,24 @@ When reading from response stream fails.
188
232
 
189
233
  #### got.ParseError
190
234
 
191
- When `json` option is enabled and `JSON.parse` fails.
235
+ When `json` option is enabled, server response code is 2xx, and `JSON.parse` fails.
192
236
 
193
237
  #### got.HTTPError
194
238
 
195
- When server response code is not 2xx. Contains `statusCode` and `statusMessage`.
239
+ When server response code is not 2xx. Includes `statusCode`, `statusMessage`, and `redirectUrls` properties.
196
240
 
197
241
  #### got.MaxRedirectsError
198
242
 
199
- When server redirects you more than 10 times.
243
+ When server redirects you more than 10 times. Includes a `redirectUrls` property, which is an array of the URLs Got was redirected to before giving up.
244
+
245
+ #### got.UnsupportedProtocolError
246
+
247
+ When given an unsupported protocol.
248
+
249
+
250
+ ## Aborting the request
251
+
252
+ The promise returned by Got has a `.cancel()` function which, when called, aborts the request.
200
253
 
201
254
 
202
255
  ## Proxies
@@ -298,8 +351,49 @@ got('http://unix:/var/run/docker.sock:/containers/json');
298
351
  got('unix:/var/run/docker.sock:/containers/json');
299
352
  ```
300
353
 
354
+ ## AWS
355
+
356
+ Requests to AWS services need to have their headers signed. This can be accomplished by using the [`aws4`](https://www.npmjs.com/package/aws4) package. This is an example for querying an ["Elasticsearch Service"](https://aws.amazon.com/elasticsearch-service/) host with a signed request.
357
+
358
+ ```js
359
+ const url = require('url');
360
+ const AWS = require('aws-sdk');
361
+ const aws4 = require('aws4');
362
+ const got = require('got');
363
+ const config = require('./config');
301
364
 
302
- ## Tip
365
+ // Reads keys from the environment or `~/.aws/credentials`. Could be a plain object.
366
+ const awsConfig = new AWS.Config({ region: config.region });
367
+
368
+ function request(uri, options) {
369
+ const awsOpts = {
370
+ region: awsConfig.region,
371
+ headers: {
372
+ accept: 'application/json',
373
+ 'content-type': 'application/json'
374
+ },
375
+ method: 'GET',
376
+ json: true
377
+ };
378
+
379
+ // We need to parse the URL before passing it to `got` so `aws4` can sign the request
380
+ const opts = Object.assign(url.parse(uri), awsOpts, options);
381
+ aws4.sign(opts, awsConfig.credentials);
382
+
383
+ return got(opts);
384
+ }
385
+
386
+ request(`https://${config.host}/production/users/1`);
387
+
388
+ request(`https://${config.host}/production/`, {
389
+ // All usual `got` options
390
+ });
391
+ ```
392
+
393
+
394
+ ## Tips
395
+
396
+ ### User Agent
303
397
 
304
398
  It's a good idea to set the `'user-agent'` header so the provider can more easily see how their resource is used. By default, it's the URL to this repo.
305
399
 
@@ -314,6 +408,10 @@ got('todomvc.com', {
314
408
  });
315
409
  ```
316
410
 
411
+ ### 304 Responses
412
+
413
+ Bear in mind, if you send an `if-modified-since` header and receive a `304 Not Modified` response, the body will be empty. It's your responsibility to cache and retrieve the body contents.
414
+
317
415
 
318
416
  ## Related
319
417
 
@@ -323,11 +421,11 @@ got('todomvc.com', {
323
421
 
324
422
  ## Created by
325
423
 
326
- [![Sindre Sorhus](https://avatars.githubusercontent.com/u/170270?v=3&s=100)](https://sindresorhus.com) | [![Vsevolod Strukchinsky](https://avatars.githubusercontent.com/u/365089?v=3&s=100)](https://github.com/floatdrop)
327
- ---|---
328
- [Sindre Sorhus](https://sindresorhus.com) | [Vsevolod Strukchinsky](https://github.com/floatdrop)
424
+ [![Sindre Sorhus](https://avatars.githubusercontent.com/u/170270?v=3&s=100)](https://sindresorhus.com) | [![Vsevolod Strukchinsky](https://avatars.githubusercontent.com/u/365089?v=3&s=100)](https://github.com/floatdrop) | [![Alexander Tesfamichael](https://avatars.githubusercontent.com/u/2011351?v=3&s=100)](https://alextes.me)
425
+ ---|---|---
426
+ [Sindre Sorhus](https://sindresorhus.com) | [Vsevolod Strukchinsky](https://github.com/floatdrop) | [Alexander Tesfamichael](https://alextes.me)
329
427
 
330
428
 
331
429
  ## License
332
430
 
333
- MIT © [Sindre Sorhus](https://sindresorhus.com)
431
+ MIT