got 8.3.2 → 9.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 DELETED
@@ -1,675 +0,0 @@
1
- 'use strict';
2
- const EventEmitter = require('events');
3
- const http = require('http');
4
- const https = require('https');
5
- const PassThrough = require('stream').PassThrough;
6
- const Transform = require('stream').Transform;
7
- const urlLib = require('url');
8
- const fs = require('fs');
9
- const querystring = require('querystring');
10
- const CacheableRequest = require('cacheable-request');
11
- const duplexer3 = require('duplexer3');
12
- const intoStream = require('into-stream');
13
- const is = require('@sindresorhus/is');
14
- const getStream = require('get-stream');
15
- const timedOut = require('timed-out');
16
- const urlParseLax = require('url-parse-lax');
17
- const urlToOptions = require('url-to-options');
18
- const lowercaseKeys = require('lowercase-keys');
19
- const decompressResponse = require('decompress-response');
20
- const mimicResponse = require('mimic-response');
21
- const isRetryAllowed = require('is-retry-allowed');
22
- const isURL = require('isurl');
23
- const PCancelable = require('p-cancelable');
24
- const pTimeout = require('p-timeout');
25
- const pify = require('pify');
26
- const Buffer = require('safe-buffer').Buffer;
27
- const pkg = require('./package.json');
28
- const errors = require('./errors');
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 => is.nodeStream(body) && is.function(body.getBoundary);
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 (is.string(body)) {
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 (is.nodeStream(body) && is.buffer(body._buffer)) {
59
- return body._buffer.length;
60
- }
61
-
62
- return null;
63
- };
64
-
65
- function requestAsEventEmitter(opts) {
66
- opts = opts || {};
67
-
68
- const ee = new EventEmitter();
69
- const requestUrl = opts.href || urlLib.resolve(urlLib.format(opts), opts.path);
70
- const redirects = [];
71
- const agents = is.object(opts.agent) ? opts.agent : null;
72
- let retryCount = 0;
73
- let redirectUrl;
74
- let uploadBodySize;
75
- let uploaded = 0;
76
-
77
- const get = opts => {
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
- });
106
-
107
- const statusCode = res.statusCode;
108
-
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'))) {
117
- res.resume();
118
-
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);
127
- return;
128
- }
129
-
130
- const bufferString = Buffer.from(res.headers.location, 'binary').toString();
131
-
132
- redirectUrl = urlLib.resolve(urlLib.format(opts), bufferString);
133
-
134
- redirects.push(redirectUrl);
135
-
136
- const redirectOpts = Object.assign({}, opts, urlLib.parse(redirectUrl));
137
-
138
- ee.emit('redirect', res, redirectOpts);
139
-
140
- get(redirectOpts);
141
-
142
- return;
143
- }
144
-
145
- setImmediate(() => {
146
- try {
147
- getResponse(res, opts, ee, redirects);
148
- } catch (e) {
149
- ee.emit('error', e);
150
- }
151
- });
152
- });
153
-
154
- cacheReq.on('error', err => {
155
- if (err instanceof CacheableRequest.RequestError) {
156
- ee.emit('error', new got.RequestError(err, opts));
157
- } else {
158
- ee.emit('error', new got.CacheError(err, opts));
159
- }
160
- });
161
-
162
- cacheReq.once('request', req => {
163
- let aborted = false;
164
- req.once('abort', _ => {
165
- aborted = true;
166
- });
167
-
168
- req.once('error', err => {
169
- clearInterval(progressInterval);
170
-
171
- if (aborted) {
172
- return;
173
- }
174
-
175
- const backoff = opts.retries(++retryCount, err);
176
-
177
- if (backoff) {
178
- setTimeout(get, backoff, opts);
179
- return;
180
- }
181
-
182
- ee.emit('error', new got.RequestError(err, opts));
183
- });
184
-
185
- ee.once('request', req => {
186
- ee.emit('uploadProgress', {
187
- percent: 0,
188
- transferred: 0,
189
- total: uploadBodySize
190
- });
191
-
192
- const socket = req.connection;
193
- if (socket) {
194
- // `._connecting` was the old property which was made public in node v6.1.0
195
- const isConnecting = socket.connecting === undefined ? socket._connecting : socket.connecting;
196
-
197
- const onSocketConnect = () => {
198
- const uploadEventFrequency = 150;
199
-
200
- progressInterval = setInterval(() => {
201
- if (socket.destroyed) {
202
- clearInterval(progressInterval);
203
- return;
204
- }
205
-
206
- const lastUploaded = uploaded;
207
- const headersSize = req._header ? Buffer.byteLength(req._header) : 0;
208
- uploaded = socket.bytesWritten - headersSize;
209
-
210
- // Prevent the known issue of `bytesWritten` being larger than body size
211
- if (uploadBodySize && uploaded > uploadBodySize) {
212
- uploaded = uploadBodySize;
213
- }
214
-
215
- // Don't emit events with unchanged progress and
216
- // prevent last event from being emitted, because
217
- // it's emitted when `response` is emitted
218
- if (uploaded === lastUploaded || uploaded === uploadBodySize) {
219
- return;
220
- }
221
-
222
- ee.emit('uploadProgress', {
223
- percent: uploadBodySize ? uploaded / uploadBodySize : 0,
224
- transferred: uploaded,
225
- total: uploadBodySize
226
- });
227
- }, uploadEventFrequency);
228
- };
229
-
230
- // Only subscribe to 'connect' event if we're actually connecting a new
231
- // socket, otherwise if we're already connected (because this is a
232
- // keep-alive connection) do not bother. This is important since we won't
233
- // get a 'connect' event for an already connected socket.
234
- if (isConnecting) {
235
- socket.once('connect', onSocketConnect);
236
- } else {
237
- onSocketConnect();
238
- }
239
- }
240
- });
241
-
242
- if (opts.gotTimeout) {
243
- clearInterval(progressInterval);
244
- timedOut(req, opts.gotTimeout);
245
- }
246
-
247
- setImmediate(() => {
248
- ee.emit('request', req);
249
- });
250
- });
251
- };
252
-
253
- setImmediate(() => {
254
- Promise.resolve(getBodySize(opts))
255
- .then(size => {
256
- uploadBodySize = size;
257
-
258
- if (
259
- is.undefined(opts.headers['content-length']) &&
260
- is.undefined(opts.headers['transfer-encoding']) &&
261
- isFormData(opts.body)
262
- ) {
263
- opts.headers['content-length'] = size;
264
- }
265
-
266
- get(opts);
267
- })
268
- .catch(err => {
269
- ee.emit('error', err);
270
- });
271
- });
272
-
273
- return ee;
274
- }
275
-
276
- function getResponse(res, opts, ee, redirects) {
277
- const downloadBodySize = Number(res.headers['content-length']) || null;
278
- let downloaded = 0;
279
-
280
- const progressStream = new Transform({
281
- transform(chunk, encoding, callback) {
282
- downloaded += chunk.length;
283
-
284
- const percent = downloadBodySize ? downloaded / downloadBodySize : 0;
285
-
286
- // Let flush() be responsible for emitting the last event
287
- if (percent < 1) {
288
- ee.emit('downloadProgress', {
289
- percent,
290
- transferred: downloaded,
291
- total: downloadBodySize
292
- });
293
- }
294
-
295
- callback(null, chunk);
296
- },
297
-
298
- flush(callback) {
299
- ee.emit('downloadProgress', {
300
- percent: 1,
301
- transferred: downloaded,
302
- total: downloadBodySize
303
- });
304
-
305
- callback();
306
- }
307
- });
308
-
309
- mimicResponse(res, progressStream);
310
- progressStream.redirectUrls = redirects;
311
-
312
- const response = opts.decompress === true &&
313
- is.function(decompressResponse) &&
314
- opts.method !== 'HEAD' ? decompressResponse(progressStream) : progressStream;
315
-
316
- if (!opts.decompress && ['gzip', 'deflate'].indexOf(res.headers['content-encoding']) !== -1) {
317
- opts.encoding = null;
318
- }
319
-
320
- ee.emit('response', response);
321
-
322
- ee.emit('downloadProgress', {
323
- percent: 0,
324
- transferred: 0,
325
- total: downloadBodySize
326
- });
327
-
328
- res.pipe(progressStream);
329
- }
330
-
331
- function asPromise(opts) {
332
- const timeoutFn = requestPromise => opts.gotTimeout && opts.gotTimeout.request ?
333
- pTimeout(requestPromise, opts.gotTimeout.request, new got.RequestError({message: 'Request timed out', code: 'ETIMEDOUT'}, opts)) :
334
- requestPromise;
335
-
336
- const proxy = new EventEmitter();
337
-
338
- const cancelable = new PCancelable((resolve, reject, onCancel) => {
339
- const ee = requestAsEventEmitter(opts);
340
- let cancelOnRequest = false;
341
-
342
- onCancel(() => {
343
- cancelOnRequest = true;
344
- });
345
-
346
- ee.on('request', req => {
347
- if (cancelOnRequest) {
348
- req.abort();
349
- }
350
-
351
- onCancel(() => {
352
- req.abort();
353
- });
354
-
355
- if (is.nodeStream(opts.body)) {
356
- opts.body.pipe(req);
357
- opts.body = undefined;
358
- return;
359
- }
360
-
361
- req.end(opts.body);
362
- });
363
-
364
- ee.on('response', res => {
365
- const stream = is.null(opts.encoding) ? getStream.buffer(res) : getStream(res, opts);
366
-
367
- stream
368
- .catch(err => reject(new got.ReadError(err, opts)))
369
- .then(data => {
370
- const statusCode = res.statusCode;
371
- const limitStatusCode = opts.followRedirect ? 299 : 399;
372
-
373
- res.body = data;
374
-
375
- if (opts.json && res.body) {
376
- try {
377
- res.body = JSON.parse(res.body);
378
- } catch (err) {
379
- if (statusCode >= 200 && statusCode < 300) {
380
- throw new got.ParseError(err, statusCode, opts, data);
381
- }
382
- }
383
- }
384
-
385
- if (opts.throwHttpErrors && statusCode !== 304 && (statusCode < 200 || statusCode > limitStatusCode)) {
386
- throw new got.HTTPError(statusCode, res.statusMessage, res.headers, opts);
387
- }
388
-
389
- resolve(res);
390
- })
391
- .catch(err => {
392
- Object.defineProperty(err, 'response', {value: res});
393
- reject(err);
394
- });
395
- });
396
-
397
- ee.once('error', reject);
398
- ee.on('redirect', proxy.emit.bind(proxy, 'redirect'));
399
- ee.on('uploadProgress', proxy.emit.bind(proxy, 'uploadProgress'));
400
- ee.on('downloadProgress', proxy.emit.bind(proxy, 'downloadProgress'));
401
- });
402
-
403
- // Preserve backwards-compatibility
404
- // TODO: Remove this in the next major version
405
- Object.defineProperty(cancelable, 'canceled', {
406
- get() {
407
- return cancelable.isCanceled;
408
- }
409
- });
410
-
411
- const promise = timeoutFn(cancelable);
412
-
413
- promise.cancel = cancelable.cancel.bind(cancelable);
414
-
415
- promise.on = (name, fn) => {
416
- proxy.on(name, fn);
417
- return promise;
418
- };
419
-
420
- return promise;
421
- }
422
-
423
- function asStream(opts) {
424
- opts.stream = true;
425
-
426
- const input = new PassThrough();
427
- const output = new PassThrough();
428
- const proxy = duplexer3(input, output);
429
- let timeout;
430
-
431
- if (opts.gotTimeout && opts.gotTimeout.request) {
432
- timeout = setTimeout(() => {
433
- proxy.emit('error', new got.RequestError({message: 'Request timed out', code: 'ETIMEDOUT'}, opts));
434
- }, opts.gotTimeout.request);
435
- }
436
-
437
- if (opts.json) {
438
- throw new Error('Got can not be used as a stream when the `json` option is used');
439
- }
440
-
441
- if (opts.body) {
442
- proxy.write = () => {
443
- throw new Error('Got\'s stream is not writable when the `body` option is used');
444
- };
445
- }
446
-
447
- const ee = requestAsEventEmitter(opts);
448
-
449
- ee.on('request', req => {
450
- proxy.emit('request', req);
451
-
452
- if (is.nodeStream(opts.body)) {
453
- opts.body.pipe(req);
454
- return;
455
- }
456
-
457
- if (opts.body) {
458
- req.end(opts.body);
459
- return;
460
- }
461
-
462
- if (opts.method === 'POST' || opts.method === 'PUT' || opts.method === 'PATCH') {
463
- input.pipe(req);
464
- return;
465
- }
466
-
467
- req.end();
468
- });
469
-
470
- ee.on('response', res => {
471
- clearTimeout(timeout);
472
-
473
- const statusCode = res.statusCode;
474
-
475
- res.on('error', err => {
476
- proxy.emit('error', new got.ReadError(err, opts));
477
- });
478
-
479
- res.pipe(output);
480
-
481
- if (opts.throwHttpErrors && statusCode !== 304 && (statusCode < 200 || statusCode > 299)) {
482
- proxy.emit('error', new got.HTTPError(statusCode, res.statusMessage, res.headers, opts), null, res);
483
- return;
484
- }
485
-
486
- proxy.emit('response', res);
487
- });
488
-
489
- ee.on('error', proxy.emit.bind(proxy, 'error'));
490
- ee.on('redirect', proxy.emit.bind(proxy, 'redirect'));
491
- ee.on('uploadProgress', proxy.emit.bind(proxy, 'uploadProgress'));
492
- ee.on('downloadProgress', proxy.emit.bind(proxy, 'downloadProgress'));
493
-
494
- return proxy;
495
- }
496
-
497
- function normalizeArguments(url, opts) {
498
- if (!is.string(url) && !is.object(url)) {
499
- throw new TypeError(`Parameter \`url\` must be a string or object, not ${is(url)}`);
500
- } else if (is.string(url)) {
501
- url = url.replace(/^unix:/, 'http://$&');
502
-
503
- try {
504
- decodeURI(url);
505
- } catch (err) {
506
- throw new Error('Parameter `url` must contain valid UTF-8 character sequences');
507
- }
508
-
509
- url = urlParseLax(url);
510
- if (url.auth) {
511
- throw new Error('Basic authentication must be done with the `auth` option');
512
- }
513
- } else if (isURL.lenient(url)) {
514
- url = urlToOptions(url);
515
- }
516
-
517
- opts = Object.assign(
518
- {
519
- path: '',
520
- retries: 2,
521
- cache: false,
522
- decompress: true,
523
- useElectronNet: false,
524
- throwHttpErrors: true
525
- },
526
- url,
527
- {
528
- protocol: url.protocol || 'http:' // Override both null/undefined with default protocol
529
- },
530
- opts
531
- );
532
-
533
- const headers = lowercaseKeys(opts.headers);
534
- for (const key of Object.keys(headers)) {
535
- if (is.nullOrUndefined(headers[key])) {
536
- delete headers[key];
537
- }
538
- }
539
-
540
- opts.headers = Object.assign({
541
- 'user-agent': `${pkg.name}/${pkg.version} (https://github.com/sindresorhus/got)`
542
- }, headers);
543
-
544
- if (opts.decompress && is.undefined(opts.headers['accept-encoding'])) {
545
- opts.headers['accept-encoding'] = 'gzip, deflate';
546
- }
547
-
548
- const query = opts.query;
549
-
550
- if (query) {
551
- if (!is.string(query)) {
552
- opts.query = querystring.stringify(query);
553
- }
554
-
555
- opts.path = `${opts.path.split('?')[0]}?${opts.query}`;
556
- delete opts.query;
557
- }
558
-
559
- if (opts.json && is.undefined(opts.headers.accept)) {
560
- opts.headers.accept = 'application/json';
561
- }
562
-
563
- const body = opts.body;
564
- if (is.nullOrUndefined(body)) {
565
- opts.method = (opts.method || 'GET').toUpperCase();
566
- } else {
567
- const headers = opts.headers;
568
- if (!is.nodeStream(body) && !is.string(body) && !is.buffer(body) && !(opts.form || opts.json)) {
569
- throw new TypeError('The `body` option must be a stream.Readable, string, Buffer or plain Object');
570
- }
571
-
572
- const canBodyBeStringified = is.plainObject(body) || is.array(body);
573
- if ((opts.form || opts.json) && !canBodyBeStringified) {
574
- throw new TypeError('The `body` option must be a plain Object or Array when the `form` or `json` option is used');
575
- }
576
-
577
- if (isFormData(body)) {
578
- // Special case for https://github.com/form-data/form-data
579
- headers['content-type'] = headers['content-type'] || `multipart/form-data; boundary=${body.getBoundary()}`;
580
- } else if (opts.form && canBodyBeStringified) {
581
- headers['content-type'] = headers['content-type'] || 'application/x-www-form-urlencoded';
582
- opts.body = querystring.stringify(body);
583
- } else if (opts.json && canBodyBeStringified) {
584
- headers['content-type'] = headers['content-type'] || 'application/json';
585
- opts.body = JSON.stringify(body);
586
- }
587
-
588
- if (is.undefined(headers['content-length']) && is.undefined(headers['transfer-encoding']) && !is.nodeStream(body)) {
589
- const length = is.string(opts.body) ? Buffer.byteLength(opts.body) : opts.body.length;
590
- headers['content-length'] = length;
591
- }
592
-
593
- // Convert buffer to stream to receive upload progress events
594
- // see https://github.com/sindresorhus/got/pull/322
595
- if (is.buffer(body)) {
596
- opts.body = intoStream(body);
597
- opts.body._buffer = body;
598
- }
599
-
600
- opts.method = (opts.method || 'POST').toUpperCase();
601
- }
602
-
603
- if (opts.hostname === 'unix') {
604
- const matches = /(.+?):(.+)/.exec(opts.path);
605
-
606
- if (matches) {
607
- opts.socketPath = matches[1];
608
- opts.path = matches[2];
609
- opts.host = null;
610
- }
611
- }
612
-
613
- if (!is.function(opts.retries)) {
614
- const retries = opts.retries;
615
-
616
- opts.retries = (iter, err) => {
617
- if (iter > retries || !isRetryAllowed(err)) {
618
- return 0;
619
- }
620
-
621
- const noise = Math.random() * 100;
622
-
623
- return ((1 << iter) * 1000) + noise;
624
- };
625
- }
626
-
627
- if (is.undefined(opts.followRedirect)) {
628
- opts.followRedirect = true;
629
- }
630
-
631
- if (opts.timeout) {
632
- if (is.number(opts.timeout)) {
633
- opts.gotTimeout = {request: opts.timeout};
634
- } else {
635
- opts.gotTimeout = opts.timeout;
636
- }
637
- delete opts.timeout;
638
- }
639
-
640
- return opts;
641
- }
642
-
643
- function got(url, opts) {
644
- try {
645
- const normalizedArgs = normalizeArguments(url, opts);
646
-
647
- if (normalizedArgs.stream) {
648
- return asStream(normalizedArgs);
649
- }
650
-
651
- return asPromise(normalizedArgs);
652
- } catch (err) {
653
- return Promise.reject(err);
654
- }
655
- }
656
-
657
- got.stream = (url, opts) => asStream(normalizeArguments(url, opts));
658
-
659
- const methods = [
660
- 'get',
661
- 'post',
662
- 'put',
663
- 'patch',
664
- 'head',
665
- 'delete'
666
- ];
667
-
668
- for (const method of methods) {
669
- got[method] = (url, opts) => got(url, Object.assign({}, opts, {method}));
670
- got.stream[method] = (url, opts) => got.stream(url, Object.assign({}, opts, {method}));
671
- }
672
-
673
- Object.assign(got, errors);
674
-
675
- module.exports = got;