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.
- package/index.js +213 -99
- package/package.json +33 -21
- package/readme.md +125 -27
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const EventEmitter = require('events')
|
|
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
|
|
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
|
|
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
|
|
22
|
-
|
|
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
|
-
|
|
32
|
+
const redirects = [];
|
|
33
33
|
let retryCount = 0;
|
|
34
34
|
let redirectUrl;
|
|
35
35
|
|
|
36
36
|
const get = opts => {
|
|
37
|
-
|
|
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
|
-
|
|
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 (
|
|
46
|
-
|
|
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 =
|
|
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 =
|
|
66
|
-
|
|
67
|
-
|
|
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.
|
|
85
|
-
timedOut(req, opts.
|
|
114
|
+
if (opts.gotTimeout) {
|
|
115
|
+
timedOut(req, opts.gotTimeout);
|
|
86
116
|
}
|
|
87
117
|
|
|
88
|
-
setImmediate(() =>
|
|
118
|
+
setImmediate(() => {
|
|
119
|
+
ee.emit('request', req);
|
|
120
|
+
});
|
|
89
121
|
};
|
|
90
122
|
|
|
91
|
-
|
|
123
|
+
setImmediate(() => {
|
|
124
|
+
get(opts);
|
|
125
|
+
});
|
|
92
126
|
return ee;
|
|
93
127
|
}
|
|
94
128
|
|
|
95
129
|
function asPromise(opts) {
|
|
96
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
211
|
-
|
|
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:
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (typeof body !== 'string' && !(body
|
|
249
|
-
throw new
|
|
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
|
-
|
|
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
|
-
|
|
257
|
-
} else if (
|
|
258
|
-
|
|
259
|
-
|
|
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 (
|
|
263
|
-
const length = typeof body === 'string' ? Buffer.byteLength(body) : body.length;
|
|
264
|
-
|
|
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
|
-
|
|
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 = /(
|
|
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
|
-
|
|
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
|
-
|
|
319
|
-
got[
|
|
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
|
-
|
|
408
|
+
class StdError extends Error {
|
|
409
|
+
constructor(message, error, opts) {
|
|
410
|
+
super(message);
|
|
411
|
+
this.name = 'StdError';
|
|
323
412
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
413
|
+
if (error.code !== undefined) {
|
|
414
|
+
this.code = error.code;
|
|
415
|
+
}
|
|
327
416
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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 =
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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": "
|
|
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
|
-
"
|
|
53
|
+
"decompress-response": "^3.2.0",
|
|
49
54
|
"duplexer3": "^0.1.4",
|
|
50
|
-
"get-stream": "^
|
|
51
|
-
"is-
|
|
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
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
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.
|
|
69
|
+
"ava": "^0.20.0",
|
|
62
70
|
"coveralls": "^2.11.4",
|
|
63
71
|
"form-data": "^2.1.1",
|
|
64
|
-
"get-port": "^
|
|
65
|
-
"get-stream": "^2.3.0",
|
|
72
|
+
"get-port": "^3.0.0",
|
|
66
73
|
"into-stream": "^3.0.0",
|
|
67
|
-
"nyc": "^
|
|
74
|
+
"nyc": "^11.0.2",
|
|
68
75
|
"pem": "^1.4.4",
|
|
69
|
-
"pify": "^
|
|
70
|
-
"tempfile": "^
|
|
71
|
-
"
|
|
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
|
-
"
|
|
74
|
-
"
|
|
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
|
-
##
|
|
18
|
+
## Highlights
|
|
21
19
|
|
|
22
|
-
|
|
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
|
|
73
|
+
Type: `string` `Object`
|
|
64
74
|
|
|
65
|
-
The URL to request
|
|
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: `
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
120
|
-
Default: `
|
|
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 `
|
|
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.
|
|
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
|
-
|
|
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
|
-
[](https://sindresorhus.com) | [](https://github.com/floatdrop)
|
|
327
|
-
|
|
328
|
-
[Sindre Sorhus](https://sindresorhus.com) | [Vsevolod Strukchinsky](https://github.com/floatdrop)
|
|
424
|
+
[](https://sindresorhus.com) | [](https://github.com/floatdrop) | [](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
|
|
431
|
+
MIT
|