got 6.7.0 → 8.0.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 +427 -104
- package/license +4 -16
- package/package.json +92 -76
- package/readme.md +291 -42
package/index.js
CHANGED
|
@@ -3,47 +3,136 @@ const EventEmitter = require('events');
|
|
|
3
3
|
const http = require('http');
|
|
4
4
|
const https = require('https');
|
|
5
5
|
const PassThrough = require('stream').PassThrough;
|
|
6
|
+
const Transform = require('stream').Transform;
|
|
6
7
|
const urlLib = require('url');
|
|
8
|
+
const fs = require('fs');
|
|
7
9
|
const querystring = require('querystring');
|
|
10
|
+
const CacheableRequest = require('cacheable-request');
|
|
8
11
|
const duplexer3 = require('duplexer3');
|
|
12
|
+
const intoStream = require('into-stream');
|
|
9
13
|
const isStream = require('is-stream');
|
|
10
14
|
const getStream = require('get-stream');
|
|
11
15
|
const timedOut = require('timed-out');
|
|
12
16
|
const urlParseLax = require('url-parse-lax');
|
|
17
|
+
const urlToOptions = require('url-to-options');
|
|
13
18
|
const lowercaseKeys = require('lowercase-keys');
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const createErrorClass = require('create-error-class');
|
|
19
|
+
const decompressResponse = require('decompress-response');
|
|
20
|
+
const mimicResponse = require('mimic-response');
|
|
17
21
|
const isRetryAllowed = require('is-retry-allowed');
|
|
22
|
+
const isURL = require('isurl');
|
|
23
|
+
const isPlainObj = require('is-plain-obj');
|
|
24
|
+
const PCancelable = require('p-cancelable');
|
|
25
|
+
const pTimeout = require('p-timeout');
|
|
26
|
+
const pify = require('pify');
|
|
18
27
|
const Buffer = require('safe-buffer').Buffer;
|
|
19
|
-
const pkg = require('./package');
|
|
28
|
+
const pkg = require('./package.json');
|
|
29
|
+
|
|
30
|
+
const getMethodRedirectCodes = new Set([300, 301, 302, 303, 304, 305, 307, 308]);
|
|
31
|
+
const allMethodRedirectCodes = new Set([300, 303, 307, 308]);
|
|
32
|
+
|
|
33
|
+
const isFormData = body => isStream(body) && typeof body.getBoundary === 'function';
|
|
34
|
+
|
|
35
|
+
const getBodySize = opts => {
|
|
36
|
+
const body = opts.body;
|
|
37
|
+
|
|
38
|
+
if (opts.headers['content-length']) {
|
|
39
|
+
return Number(opts.headers['content-length']);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!body && !opts.stream) {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (typeof body === 'string') {
|
|
47
|
+
return Buffer.byteLength(body);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (isFormData(body)) {
|
|
51
|
+
return pify(body.getLength.bind(body))();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (body instanceof fs.ReadStream) {
|
|
55
|
+
return pify(fs.stat)(body.path).then(stat => stat.size);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (isStream(body) && Buffer.isBuffer(body._buffer)) {
|
|
59
|
+
return body._buffer.length;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return null;
|
|
63
|
+
};
|
|
20
64
|
|
|
21
65
|
function requestAsEventEmitter(opts) {
|
|
22
66
|
opts = opts || {};
|
|
23
67
|
|
|
24
68
|
const ee = new EventEmitter();
|
|
25
69
|
const requestUrl = opts.href || urlLib.resolve(urlLib.format(opts), opts.path);
|
|
26
|
-
|
|
70
|
+
const redirects = [];
|
|
71
|
+
const agents = typeof opts.agent === 'object' ? opts.agent : null;
|
|
27
72
|
let retryCount = 0;
|
|
28
73
|
let redirectUrl;
|
|
74
|
+
let uploadBodySize;
|
|
75
|
+
let uploaded = 0;
|
|
29
76
|
|
|
30
77
|
const get = opts => {
|
|
31
|
-
|
|
78
|
+
if (opts.protocol !== 'http:' && opts.protocol !== 'https:') {
|
|
79
|
+
ee.emit('error', new got.UnsupportedProtocolError(opts));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let fn = opts.protocol === 'https:' ? https : http;
|
|
84
|
+
|
|
85
|
+
if (agents) {
|
|
86
|
+
const protocolName = opts.protocol === 'https:' ? 'https' : 'http';
|
|
87
|
+
opts.agent = agents[protocolName] || opts.agent;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (opts.useElectronNet && process.versions.electron) {
|
|
91
|
+
const electron = require('electron');
|
|
92
|
+
fn = electron.net || electron.remote.net;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let progressInterval;
|
|
96
|
+
|
|
97
|
+
const cacheableRequest = new CacheableRequest(fn.request, opts.cache);
|
|
98
|
+
const cacheReq = cacheableRequest(opts, res => {
|
|
99
|
+
clearInterval(progressInterval);
|
|
100
|
+
|
|
101
|
+
ee.emit('uploadProgress', {
|
|
102
|
+
percent: 1,
|
|
103
|
+
transferred: uploaded,
|
|
104
|
+
total: uploadBodySize
|
|
105
|
+
});
|
|
32
106
|
|
|
33
|
-
const req = fn.request(opts, res => {
|
|
34
107
|
const statusCode = res.statusCode;
|
|
35
108
|
|
|
36
|
-
|
|
109
|
+
res.url = redirectUrl || requestUrl;
|
|
110
|
+
res.requestUrl = requestUrl;
|
|
111
|
+
|
|
112
|
+
const followRedirect = opts.followRedirect && 'location' in res.headers;
|
|
113
|
+
const redirectGet = followRedirect && getMethodRedirectCodes.has(statusCode);
|
|
114
|
+
const redirectAll = followRedirect && allMethodRedirectCodes.has(statusCode);
|
|
115
|
+
|
|
116
|
+
if (redirectAll || (redirectGet && (opts.method === 'GET' || opts.method === 'HEAD'))) {
|
|
37
117
|
res.resume();
|
|
38
118
|
|
|
39
|
-
if (
|
|
40
|
-
|
|
119
|
+
if (statusCode === 303) {
|
|
120
|
+
// Server responded with "see other", indicating that the resource exists at another location,
|
|
121
|
+
// and the client should request it from that location via GET or HEAD.
|
|
122
|
+
opts.method = 'GET';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (redirects.length >= 10) {
|
|
126
|
+
ee.emit('error', new got.MaxRedirectsError(statusCode, redirects, opts), null, res);
|
|
41
127
|
return;
|
|
42
128
|
}
|
|
43
129
|
|
|
44
130
|
const bufferString = Buffer.from(res.headers.location, 'binary').toString();
|
|
45
131
|
|
|
46
132
|
redirectUrl = urlLib.resolve(urlLib.format(opts), bufferString);
|
|
133
|
+
|
|
134
|
+
redirects.push(redirectUrl);
|
|
135
|
+
|
|
47
136
|
const redirectOpts = Object.assign({}, opts, urlLib.parse(redirectUrl));
|
|
48
137
|
|
|
49
138
|
ee.emit('redirect', res, redirectOpts);
|
|
@@ -53,44 +142,169 @@ function requestAsEventEmitter(opts) {
|
|
|
53
142
|
return;
|
|
54
143
|
}
|
|
55
144
|
|
|
145
|
+
const downloadBodySize = Number(res.headers['content-length']) || null;
|
|
146
|
+
let downloaded = 0;
|
|
147
|
+
|
|
56
148
|
setImmediate(() => {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
149
|
+
const progressStream = new Transform({
|
|
150
|
+
transform(chunk, encoding, callback) {
|
|
151
|
+
downloaded += chunk.length;
|
|
152
|
+
|
|
153
|
+
const percent = downloadBodySize ? downloaded / downloadBodySize : 0;
|
|
154
|
+
|
|
155
|
+
// Let flush() be responsible for emitting the last event
|
|
156
|
+
if (percent < 1) {
|
|
157
|
+
ee.emit('downloadProgress', {
|
|
158
|
+
percent,
|
|
159
|
+
transferred: downloaded,
|
|
160
|
+
total: downloadBodySize
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
callback(null, chunk);
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
flush(callback) {
|
|
168
|
+
ee.emit('downloadProgress', {
|
|
169
|
+
percent: 1,
|
|
170
|
+
transferred: downloaded,
|
|
171
|
+
total: downloadBodySize
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
callback();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
mimicResponse(res, progressStream);
|
|
179
|
+
progressStream.redirectUrls = redirects;
|
|
180
|
+
|
|
181
|
+
const response = opts.decompress === true &&
|
|
182
|
+
typeof decompressResponse === 'function' &&
|
|
183
|
+
opts.method !== 'HEAD' ? decompressResponse(progressStream) : progressStream;
|
|
184
|
+
|
|
185
|
+
if (!opts.decompress && ['gzip', 'deflate'].indexOf(res.headers['content-encoding']) !== -1) {
|
|
186
|
+
opts.encoding = null;
|
|
187
|
+
}
|
|
60
188
|
|
|
61
189
|
ee.emit('response', response);
|
|
190
|
+
|
|
191
|
+
ee.emit('downloadProgress', {
|
|
192
|
+
percent: 0,
|
|
193
|
+
transferred: 0,
|
|
194
|
+
total: downloadBodySize
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
res.pipe(progressStream);
|
|
62
198
|
});
|
|
63
199
|
});
|
|
64
200
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return;
|
|
201
|
+
cacheReq.on('error', err => {
|
|
202
|
+
if (err instanceof CacheableRequest.RequestError) {
|
|
203
|
+
ee.emit('error', new got.RequestError(err, opts));
|
|
204
|
+
} else {
|
|
205
|
+
ee.emit('error', new got.CacheError(err, opts));
|
|
71
206
|
}
|
|
72
|
-
|
|
73
|
-
ee.emit('error', new got.RequestError(err, opts));
|
|
74
207
|
});
|
|
75
208
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
209
|
+
cacheReq.once('request', req => {
|
|
210
|
+
req.once('error', err => {
|
|
211
|
+
clearInterval(progressInterval);
|
|
212
|
+
|
|
213
|
+
const backoff = opts.retries(++retryCount, err);
|
|
214
|
+
|
|
215
|
+
if (backoff) {
|
|
216
|
+
setTimeout(get, backoff, opts);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
ee.emit('error', new got.RequestError(err, opts));
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
ee.once('request', req => {
|
|
224
|
+
ee.emit('uploadProgress', {
|
|
225
|
+
percent: 0,
|
|
226
|
+
transferred: 0,
|
|
227
|
+
total: uploadBodySize
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
req.connection.once('connect', () => {
|
|
231
|
+
const uploadEventFrequency = 150;
|
|
232
|
+
|
|
233
|
+
progressInterval = setInterval(() => {
|
|
234
|
+
const lastUploaded = uploaded;
|
|
235
|
+
const headersSize = Buffer.byteLength(req._header);
|
|
236
|
+
uploaded = req.connection.bytesWritten - headersSize;
|
|
237
|
+
|
|
238
|
+
// Prevent the known issue of `bytesWritten` being larger than body size
|
|
239
|
+
if (uploadBodySize && uploaded > uploadBodySize) {
|
|
240
|
+
uploaded = uploadBodySize;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Don't emit events with unchanged progress and
|
|
244
|
+
// prevent last event from being emitted, because
|
|
245
|
+
// it's emitted when `response` is emitted
|
|
246
|
+
if (uploaded === lastUploaded || uploaded === uploadBodySize) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
79
249
|
|
|
80
|
-
|
|
81
|
-
|
|
250
|
+
ee.emit('uploadProgress', {
|
|
251
|
+
percent: uploadBodySize ? uploaded / uploadBodySize : 0,
|
|
252
|
+
transferred: uploaded,
|
|
253
|
+
total: uploadBodySize
|
|
254
|
+
});
|
|
255
|
+
}, uploadEventFrequency);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (opts.gotTimeout) {
|
|
260
|
+
clearInterval(progressInterval);
|
|
261
|
+
timedOut(req, opts.gotTimeout);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
setImmediate(() => {
|
|
265
|
+
ee.emit('request', req);
|
|
266
|
+
});
|
|
82
267
|
});
|
|
83
268
|
};
|
|
84
269
|
|
|
85
|
-
|
|
270
|
+
setImmediate(() => {
|
|
271
|
+
Promise.resolve(getBodySize(opts))
|
|
272
|
+
.then(size => {
|
|
273
|
+
uploadBodySize = size;
|
|
274
|
+
get(opts);
|
|
275
|
+
})
|
|
276
|
+
.catch(err => {
|
|
277
|
+
ee.emit('error', err);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
86
281
|
return ee;
|
|
87
282
|
}
|
|
88
283
|
|
|
89
284
|
function asPromise(opts) {
|
|
90
|
-
|
|
285
|
+
const timeoutFn = requestPromise => opts.gotTimeout && opts.gotTimeout.request ?
|
|
286
|
+
pTimeout(requestPromise, opts.gotTimeout.request, new got.RequestError({message: 'Request timed out', code: 'ETIMEDOUT'}, opts)) :
|
|
287
|
+
requestPromise;
|
|
288
|
+
|
|
289
|
+
const proxy = new EventEmitter();
|
|
290
|
+
|
|
291
|
+
const cancelable = new PCancelable((onCancel, resolve, reject) => {
|
|
91
292
|
const ee = requestAsEventEmitter(opts);
|
|
293
|
+
let cancelOnRequest = false;
|
|
294
|
+
|
|
295
|
+
onCancel(() => {
|
|
296
|
+
cancelOnRequest = true;
|
|
297
|
+
});
|
|
92
298
|
|
|
93
299
|
ee.on('request', req => {
|
|
300
|
+
if (cancelOnRequest) {
|
|
301
|
+
req.abort();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
onCancel(() => {
|
|
305
|
+
req.abort();
|
|
306
|
+
});
|
|
307
|
+
|
|
94
308
|
if (isStream(opts.body)) {
|
|
95
309
|
opts.body.pipe(req);
|
|
96
310
|
opts.body = undefined;
|
|
@@ -114,13 +328,15 @@ function asPromise(opts) {
|
|
|
114
328
|
if (opts.json && res.body) {
|
|
115
329
|
try {
|
|
116
330
|
res.body = JSON.parse(res.body);
|
|
117
|
-
} catch (
|
|
118
|
-
|
|
331
|
+
} catch (err) {
|
|
332
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
333
|
+
throw new got.ParseError(err, statusCode, opts, data);
|
|
334
|
+
}
|
|
119
335
|
}
|
|
120
336
|
}
|
|
121
337
|
|
|
122
|
-
if (statusCode < 200 || statusCode > limitStatusCode) {
|
|
123
|
-
throw new got.HTTPError(statusCode, opts);
|
|
338
|
+
if (statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
|
|
339
|
+
throw new got.HTTPError(statusCode, res.statusMessage, res.headers, opts);
|
|
124
340
|
}
|
|
125
341
|
|
|
126
342
|
resolve(res);
|
|
@@ -131,22 +347,45 @@ function asPromise(opts) {
|
|
|
131
347
|
});
|
|
132
348
|
});
|
|
133
349
|
|
|
134
|
-
ee.
|
|
350
|
+
ee.once('error', reject);
|
|
351
|
+
ee.on('redirect', proxy.emit.bind(proxy, 'redirect'));
|
|
352
|
+
ee.on('uploadProgress', proxy.emit.bind(proxy, 'uploadProgress'));
|
|
353
|
+
ee.on('downloadProgress', proxy.emit.bind(proxy, 'downloadProgress'));
|
|
135
354
|
});
|
|
355
|
+
|
|
356
|
+
const promise = timeoutFn(cancelable);
|
|
357
|
+
|
|
358
|
+
promise.cancel = cancelable.cancel.bind(cancelable);
|
|
359
|
+
|
|
360
|
+
promise.on = (name, fn) => {
|
|
361
|
+
proxy.on(name, fn);
|
|
362
|
+
return promise;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
return promise;
|
|
136
366
|
}
|
|
137
367
|
|
|
138
368
|
function asStream(opts) {
|
|
369
|
+
opts.stream = true;
|
|
370
|
+
|
|
139
371
|
const input = new PassThrough();
|
|
140
372
|
const output = new PassThrough();
|
|
141
373
|
const proxy = duplexer3(input, output);
|
|
374
|
+
let timeout;
|
|
375
|
+
|
|
376
|
+
if (opts.gotTimeout && opts.gotTimeout.request) {
|
|
377
|
+
timeout = setTimeout(() => {
|
|
378
|
+
proxy.emit('error', new got.RequestError({message: 'Request timed out', code: 'ETIMEDOUT'}, opts));
|
|
379
|
+
}, opts.gotTimeout.request);
|
|
380
|
+
}
|
|
142
381
|
|
|
143
382
|
if (opts.json) {
|
|
144
|
-
throw new Error('
|
|
383
|
+
throw new Error('Got can not be used as a stream when the `json` option is used');
|
|
145
384
|
}
|
|
146
385
|
|
|
147
386
|
if (opts.body) {
|
|
148
387
|
proxy.write = () => {
|
|
149
|
-
throw new Error('
|
|
388
|
+
throw new Error('Got\'s stream is not writable when the `body` option is used');
|
|
150
389
|
};
|
|
151
390
|
}
|
|
152
391
|
|
|
@@ -174,52 +413,67 @@ function asStream(opts) {
|
|
|
174
413
|
});
|
|
175
414
|
|
|
176
415
|
ee.on('response', res => {
|
|
416
|
+
clearTimeout(timeout);
|
|
417
|
+
|
|
177
418
|
const statusCode = res.statusCode;
|
|
178
419
|
|
|
179
420
|
res.pipe(output);
|
|
180
421
|
|
|
181
|
-
if (statusCode < 200 || statusCode > 299) {
|
|
182
|
-
proxy.emit('error', new got.HTTPError(statusCode, opts), null, res);
|
|
422
|
+
if (statusCode !== 304 && (statusCode < 200 || statusCode > 299)) {
|
|
423
|
+
proxy.emit('error', new got.HTTPError(statusCode, res.statusMessage, res.headers, opts), null, res);
|
|
183
424
|
return;
|
|
184
425
|
}
|
|
185
426
|
|
|
186
427
|
proxy.emit('response', res);
|
|
187
428
|
});
|
|
188
429
|
|
|
189
|
-
ee.on('redirect', proxy.emit.bind(proxy, 'redirect'));
|
|
190
430
|
ee.on('error', proxy.emit.bind(proxy, 'error'));
|
|
431
|
+
ee.on('redirect', proxy.emit.bind(proxy, 'redirect'));
|
|
432
|
+
ee.on('uploadProgress', proxy.emit.bind(proxy, 'uploadProgress'));
|
|
433
|
+
ee.on('downloadProgress', proxy.emit.bind(proxy, 'downloadProgress'));
|
|
191
434
|
|
|
192
435
|
return proxy;
|
|
193
436
|
}
|
|
194
437
|
|
|
195
438
|
function normalizeArguments(url, opts) {
|
|
196
439
|
if (typeof url !== 'string' && typeof url !== 'object') {
|
|
197
|
-
throw new
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (typeof url === 'string') {
|
|
440
|
+
throw new TypeError(`Parameter \`url\` must be a string or object, not ${typeof url}`);
|
|
441
|
+
} else if (typeof url === 'string') {
|
|
201
442
|
url = url.replace(/^unix:/, 'http://$&');
|
|
202
443
|
url = urlParseLax(url);
|
|
203
|
-
|
|
204
444
|
if (url.auth) {
|
|
205
|
-
throw new Error('Basic authentication must be done with auth option');
|
|
445
|
+
throw new Error('Basic authentication must be done with the `auth` option');
|
|
206
446
|
}
|
|
447
|
+
} else if (isURL.lenient(url)) {
|
|
448
|
+
url = urlToOptions(url);
|
|
207
449
|
}
|
|
208
450
|
|
|
209
451
|
opts = Object.assign(
|
|
210
452
|
{
|
|
211
|
-
protocol: 'http:',
|
|
212
453
|
path: '',
|
|
213
|
-
retries:
|
|
454
|
+
retries: 2,
|
|
455
|
+
cache: false,
|
|
456
|
+
decompress: true,
|
|
457
|
+
useElectronNet: false
|
|
214
458
|
},
|
|
215
459
|
url,
|
|
460
|
+
{
|
|
461
|
+
protocol: url.protocol || 'http:' // Override both null/undefined with default protocol
|
|
462
|
+
},
|
|
216
463
|
opts
|
|
217
464
|
);
|
|
218
465
|
|
|
466
|
+
const headers = lowercaseKeys(opts.headers);
|
|
467
|
+
for (const key of Object.keys(headers)) {
|
|
468
|
+
if (headers[key] === null || headers[key] === undefined) {
|
|
469
|
+
delete headers[key];
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
219
473
|
opts.headers = Object.assign({
|
|
220
474
|
'user-agent': `${pkg.name}/${pkg.version} (https://github.com/sindresorhus/got)`,
|
|
221
475
|
'accept-encoding': 'gzip,deflate'
|
|
222
|
-
},
|
|
476
|
+
}, headers);
|
|
223
477
|
|
|
224
478
|
const query = opts.query;
|
|
225
479
|
|
|
@@ -236,33 +490,48 @@ function normalizeArguments(url, opts) {
|
|
|
236
490
|
opts.headers.accept = 'application/json';
|
|
237
491
|
}
|
|
238
492
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (typeof body !== 'string' && !(body
|
|
243
|
-
throw new
|
|
493
|
+
const body = opts.body;
|
|
494
|
+
if (body !== null && body !== undefined) {
|
|
495
|
+
const headers = opts.headers;
|
|
496
|
+
if (!isStream(body) && typeof body !== 'string' && !Buffer.isBuffer(body) && !(opts.form || opts.json)) {
|
|
497
|
+
throw new TypeError('The `body` option must be a stream.Readable, string, Buffer or plain Object');
|
|
244
498
|
}
|
|
245
499
|
|
|
246
|
-
|
|
500
|
+
const canBodyBeStringified = isPlainObj(body) || Array.isArray(body);
|
|
501
|
+
if ((opts.form || opts.json) && !canBodyBeStringified) {
|
|
502
|
+
throw new TypeError('The `body` option must be a plain Object or Array when the `form` or `json` option is used');
|
|
503
|
+
}
|
|
247
504
|
|
|
248
|
-
if (
|
|
505
|
+
if (isFormData(body)) {
|
|
249
506
|
// Special case for https://github.com/form-data/form-data
|
|
250
|
-
|
|
251
|
-
} else if (
|
|
252
|
-
|
|
253
|
-
|
|
507
|
+
headers['content-type'] = headers['content-type'] || `multipart/form-data; boundary=${body.getBoundary()}`;
|
|
508
|
+
} else if (opts.form && canBodyBeStringified) {
|
|
509
|
+
headers['content-type'] = headers['content-type'] || 'application/x-www-form-urlencoded';
|
|
510
|
+
opts.body = querystring.stringify(body);
|
|
511
|
+
} else if (opts.json && canBodyBeStringified) {
|
|
512
|
+
headers['content-type'] = headers['content-type'] || 'application/json';
|
|
513
|
+
opts.body = JSON.stringify(body);
|
|
254
514
|
}
|
|
255
515
|
|
|
256
|
-
if (
|
|
257
|
-
const length = typeof body === 'string' ? Buffer.byteLength(body) : body.length;
|
|
258
|
-
|
|
516
|
+
if (headers['content-length'] === undefined && headers['transfer-encoding'] === undefined && !isStream(body)) {
|
|
517
|
+
const length = typeof opts.body === 'string' ? Buffer.byteLength(opts.body) : opts.body.length;
|
|
518
|
+
headers['content-length'] = length;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Convert buffer to stream to receive upload progress events
|
|
522
|
+
// see https://github.com/sindresorhus/got/pull/322
|
|
523
|
+
if (Buffer.isBuffer(body)) {
|
|
524
|
+
opts.body = intoStream(body);
|
|
525
|
+
opts.body._buffer = body;
|
|
259
526
|
}
|
|
260
|
-
}
|
|
261
527
|
|
|
262
|
-
|
|
528
|
+
opts.method = (opts.method || 'POST').toUpperCase();
|
|
529
|
+
} else {
|
|
530
|
+
opts.method = (opts.method || 'GET').toUpperCase();
|
|
531
|
+
}
|
|
263
532
|
|
|
264
533
|
if (opts.hostname === 'unix') {
|
|
265
|
-
const matches = /(
|
|
534
|
+
const matches = /(.+?):(.+)/.exec(opts.path);
|
|
266
535
|
|
|
267
536
|
if (matches) {
|
|
268
537
|
opts.socketPath = matches[1];
|
|
@@ -289,6 +558,15 @@ function normalizeArguments(url, opts) {
|
|
|
289
558
|
opts.followRedirect = true;
|
|
290
559
|
}
|
|
291
560
|
|
|
561
|
+
if (opts.timeout) {
|
|
562
|
+
if (typeof opts.timeout === 'number') {
|
|
563
|
+
opts.gotTimeout = {request: opts.timeout};
|
|
564
|
+
} else {
|
|
565
|
+
opts.gotTimeout = opts.timeout;
|
|
566
|
+
}
|
|
567
|
+
delete opts.timeout;
|
|
568
|
+
}
|
|
569
|
+
|
|
292
570
|
return opts;
|
|
293
571
|
}
|
|
294
572
|
|
|
@@ -300,7 +578,9 @@ function got(url, opts) {
|
|
|
300
578
|
}
|
|
301
579
|
}
|
|
302
580
|
|
|
303
|
-
|
|
581
|
+
got.stream = (url, opts) => asStream(normalizeArguments(url, opts));
|
|
582
|
+
|
|
583
|
+
const methods = [
|
|
304
584
|
'get',
|
|
305
585
|
'post',
|
|
306
586
|
'put',
|
|
@@ -309,51 +589,94 @@ const helpers = [
|
|
|
309
589
|
'delete'
|
|
310
590
|
];
|
|
311
591
|
|
|
312
|
-
|
|
313
|
-
got[
|
|
314
|
-
});
|
|
592
|
+
for (const method of methods) {
|
|
593
|
+
got[method] = (url, opts) => got(url, Object.assign({}, opts, {method}));
|
|
594
|
+
got.stream[method] = (url, opts) => got.stream(url, Object.assign({}, opts, {method}));
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
class StdError extends Error {
|
|
598
|
+
constructor(message, error, opts) {
|
|
599
|
+
super(message);
|
|
600
|
+
Error.captureStackTrace(this, this.constructor);
|
|
601
|
+
this.name = 'StdError';
|
|
315
602
|
|
|
316
|
-
|
|
603
|
+
if (error.code !== undefined) {
|
|
604
|
+
this.code = error.code;
|
|
605
|
+
}
|
|
317
606
|
|
|
318
|
-
|
|
319
|
-
|
|
607
|
+
Object.assign(this, {
|
|
608
|
+
host: opts.host,
|
|
609
|
+
hostname: opts.hostname,
|
|
610
|
+
method: opts.method,
|
|
611
|
+
path: opts.path,
|
|
612
|
+
protocol: opts.protocol,
|
|
613
|
+
url: opts.href
|
|
614
|
+
});
|
|
615
|
+
}
|
|
320
616
|
}
|
|
321
617
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
618
|
+
got.CacheError = class extends StdError {
|
|
619
|
+
constructor(error, opts) {
|
|
620
|
+
super(error.message, error, opts);
|
|
621
|
+
this.name = 'CacheError';
|
|
325
622
|
}
|
|
623
|
+
};
|
|
326
624
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
625
|
+
got.RequestError = class extends StdError {
|
|
626
|
+
constructor(error, opts) {
|
|
627
|
+
super(error.message, error, opts);
|
|
628
|
+
this.name = 'RequestError';
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
got.ReadError = class extends StdError {
|
|
633
|
+
constructor(error, opts) {
|
|
634
|
+
super(error.message, error, opts);
|
|
635
|
+
this.name = 'ReadError';
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
got.ParseError = class extends StdError {
|
|
640
|
+
constructor(error, statusCode, opts, data) {
|
|
641
|
+
super(`${error.message} in "${urlLib.format(opts)}": \n${data.slice(0, 77)}...`, error, opts);
|
|
642
|
+
this.name = 'ParseError';
|
|
643
|
+
this.statusCode = statusCode;
|
|
644
|
+
this.statusMessage = http.STATUS_CODES[this.statusCode];
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
got.HTTPError = class extends StdError {
|
|
649
|
+
constructor(statusCode, statusMessage, headers, opts) {
|
|
650
|
+
if (statusMessage) {
|
|
651
|
+
statusMessage = statusMessage.replace(/\r?\n/g, ' ').trim();
|
|
652
|
+
} else {
|
|
653
|
+
statusMessage = http.STATUS_CODES[statusCode];
|
|
654
|
+
}
|
|
655
|
+
super(`Response code ${statusCode} (${statusMessage})`, {}, opts);
|
|
656
|
+
this.name = 'HTTPError';
|
|
657
|
+
this.statusCode = statusCode;
|
|
658
|
+
this.statusMessage = statusMessage;
|
|
659
|
+
this.headers = headers;
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
got.MaxRedirectsError = class extends StdError {
|
|
664
|
+
constructor(statusCode, redirectUrls, opts) {
|
|
665
|
+
super('Redirected 10 times. Aborting.', {}, opts);
|
|
666
|
+
this.name = 'MaxRedirectsError';
|
|
667
|
+
this.statusCode = statusCode;
|
|
668
|
+
this.statusMessage = http.STATUS_CODES[this.statusCode];
|
|
669
|
+
this.redirectUrls = redirectUrls;
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
got.UnsupportedProtocolError = class extends StdError {
|
|
674
|
+
constructor(opts) {
|
|
675
|
+
super(`Unsupported protocol "${opts.protocol}"`, {}, opts);
|
|
676
|
+
this.name = 'UnsupportedProtocolError';
|
|
677
|
+
}
|
|
678
|
+
};
|
|
335
679
|
|
|
336
|
-
got.
|
|
337
|
-
got.ReadError = createErrorClass('ReadError', stdError);
|
|
338
|
-
got.ParseError = createErrorClass('ParseError', function (e, statusCode, opts, data) {
|
|
339
|
-
stdError.call(this, e, opts);
|
|
340
|
-
this.statusCode = statusCode;
|
|
341
|
-
this.statusMessage = http.STATUS_CODES[this.statusCode];
|
|
342
|
-
this.message = `${e.message} in "${urlLib.format(opts)}": \n${data.slice(0, 77)}...`;
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
got.HTTPError = createErrorClass('HTTPError', function (statusCode, opts) {
|
|
346
|
-
stdError.call(this, {}, opts);
|
|
347
|
-
this.statusCode = statusCode;
|
|
348
|
-
this.statusMessage = http.STATUS_CODES[this.statusCode];
|
|
349
|
-
this.message = `Response code ${this.statusCode} (${this.statusMessage})`;
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
got.MaxRedirectsError = createErrorClass('MaxRedirectsError', function (statusCode, opts) {
|
|
353
|
-
stdError.call(this, {}, opts);
|
|
354
|
-
this.statusCode = statusCode;
|
|
355
|
-
this.statusMessage = http.STATUS_CODES[this.statusCode];
|
|
356
|
-
this.message = 'Redirected 10 times. Aborting.';
|
|
357
|
-
});
|
|
680
|
+
got.CancelError = PCancelable.CancelError;
|
|
358
681
|
|
|
359
682
|
module.exports = got;
|