got 11.4.0 → 11.6.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/dist/source/as-promise/index.d.ts +1 -3
- package/dist/source/as-promise/index.js +56 -117
- package/dist/source/as-promise/normalize-arguments.d.ts +3 -0
- package/dist/source/as-promise/normalize-arguments.js +78 -0
- package/dist/source/as-promise/parse-body.d.ts +3 -0
- package/dist/source/as-promise/parse-body.js +25 -0
- package/dist/source/as-promise/types.d.ts +235 -58
- package/dist/source/as-promise/types.js +29 -16
- package/dist/source/{as-promise → core}/calculate-retry-delay.d.ts +2 -1
- package/dist/source/core/calculate-retry-delay.js +29 -0
- package/dist/source/core/index.d.ts +851 -28
- package/dist/source/core/index.js +291 -55
- package/dist/source/core/utils/dns-ip-version.js +1 -0
- package/dist/source/core/utils/get-body-size.d.ts +2 -4
- package/dist/source/core/utils/is-form-data.d.ts +2 -2
- package/dist/source/core/utils/is-response-ok.d.ts +2 -0
- package/dist/source/core/utils/is-response-ok.js +8 -0
- package/dist/source/core/utils/options-to-url.d.ts +0 -1
- package/dist/source/core/utils/timed-out.js +1 -0
- package/dist/source/core/utils/url-to-options.d.ts +0 -1
- package/dist/source/core/utils/weakable-map.d.ts +1 -1
- package/dist/source/create.js +42 -13
- package/dist/source/index.js +16 -6
- package/dist/source/types.d.ts +245 -8
- package/dist/source/utils/deep-freeze.d.ts +1 -1
- package/dist/source/utils/deprecation-warning.js +1 -1
- package/package.json +30 -29
- package/readme.md +171 -35
- package/dist/source/as-promise/calculate-retry-delay.js +0 -38
- package/dist/source/as-promise/core.d.ts +0 -13
- package/dist/source/as-promise/core.js +0 -124
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnsupportedProtocolError = exports.ReadError = exports.TimeoutError = exports.UploadError = exports.CacheError = exports.HTTPError = exports.MaxRedirectsError = exports.RequestError = exports.setNonEnumerableProperties = exports.knownHookEvents = exports.withoutBody = exports.kIsNormalizedAlready = void 0;
|
|
3
4
|
const util_1 = require("util");
|
|
4
5
|
const stream_1 = require("stream");
|
|
5
6
|
const fs_1 = require("fs");
|
|
@@ -11,7 +12,7 @@ const http_timer_1 = require("@szmarczak/http-timer");
|
|
|
11
12
|
const cacheable_lookup_1 = require("cacheable-lookup");
|
|
12
13
|
const CacheableRequest = require("cacheable-request");
|
|
13
14
|
const decompressResponse = require("decompress-response");
|
|
14
|
-
// @ts-
|
|
15
|
+
// @ts-expect-error Missing types
|
|
15
16
|
const http2wrapper = require("http2-wrapper");
|
|
16
17
|
const lowercaseKeys = require("lowercase-keys");
|
|
17
18
|
const is_1 = require("@sindresorhus/is");
|
|
@@ -24,7 +25,11 @@ const options_to_url_1 = require("./utils/options-to-url");
|
|
|
24
25
|
const weakable_map_1 = require("./utils/weakable-map");
|
|
25
26
|
const get_buffer_1 = require("./utils/get-buffer");
|
|
26
27
|
const dns_ip_version_1 = require("./utils/dns-ip-version");
|
|
28
|
+
const is_response_ok_1 = require("./utils/is-response-ok");
|
|
27
29
|
const deprecation_warning_1 = require("../utils/deprecation-warning");
|
|
30
|
+
const normalize_arguments_1 = require("../as-promise/normalize-arguments");
|
|
31
|
+
const calculate_retry_delay_1 = require("./calculate-retry-delay");
|
|
32
|
+
const globalDnsCache = new cacheable_lookup_1.default();
|
|
28
33
|
const kRequest = Symbol('request');
|
|
29
34
|
const kResponse = Symbol('response');
|
|
30
35
|
const kResponseSize = Symbol('responseSize');
|
|
@@ -41,10 +46,19 @@ const kTriggerRead = Symbol('triggerRead');
|
|
|
41
46
|
const kBody = Symbol('body');
|
|
42
47
|
const kJobs = Symbol('jobs');
|
|
43
48
|
const kOriginalResponse = Symbol('originalResponse');
|
|
49
|
+
const kRetryTimeout = Symbol('retryTimeout');
|
|
44
50
|
exports.kIsNormalizedAlready = Symbol('isNormalizedAlready');
|
|
45
51
|
const supportsBrotli = is_1.default.string(process.versions.brotli);
|
|
46
52
|
exports.withoutBody = new Set(['GET', 'HEAD']);
|
|
47
|
-
exports.knownHookEvents = [
|
|
53
|
+
exports.knownHookEvents = [
|
|
54
|
+
'init',
|
|
55
|
+
'beforeRequest',
|
|
56
|
+
'beforeRedirect',
|
|
57
|
+
'beforeError',
|
|
58
|
+
'beforeRetry',
|
|
59
|
+
// Promise-Only
|
|
60
|
+
'afterResponse'
|
|
61
|
+
];
|
|
48
62
|
function validateSearchParameters(searchParameters) {
|
|
49
63
|
// eslint-disable-next-line guard-for-in
|
|
50
64
|
for (const key in searchParameters) {
|
|
@@ -62,6 +76,7 @@ const waitForOpenFile = async (file) => new Promise((resolve, reject) => {
|
|
|
62
76
|
const onError = (error) => {
|
|
63
77
|
reject(error);
|
|
64
78
|
};
|
|
79
|
+
// Node.js 12 has incomplete types
|
|
65
80
|
if (!file.pending) {
|
|
66
81
|
resolve();
|
|
67
82
|
}
|
|
@@ -93,13 +108,17 @@ exports.setNonEnumerableProperties = (sources, to) => {
|
|
|
93
108
|
writable: true,
|
|
94
109
|
configurable: true,
|
|
95
110
|
enumerable: false,
|
|
96
|
-
// @ts-
|
|
111
|
+
// @ts-expect-error TS doesn't see the check above
|
|
97
112
|
value: source[name]
|
|
98
113
|
};
|
|
99
114
|
}
|
|
100
115
|
}
|
|
101
116
|
Object.defineProperties(to, properties);
|
|
102
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
An error to be thrown when a request fails.
|
|
120
|
+
Contains a `code` property with error class code, like `ECONNREFUSED`.
|
|
121
|
+
*/
|
|
103
122
|
class RequestError extends Error {
|
|
104
123
|
constructor(message, error, self) {
|
|
105
124
|
var _a;
|
|
@@ -146,6 +165,10 @@ class RequestError extends Error {
|
|
|
146
165
|
}
|
|
147
166
|
}
|
|
148
167
|
exports.RequestError = RequestError;
|
|
168
|
+
/**
|
|
169
|
+
An error to be thrown when the server redirects you more than ten times.
|
|
170
|
+
Includes a `response` property.
|
|
171
|
+
*/
|
|
149
172
|
class MaxRedirectsError extends RequestError {
|
|
150
173
|
constructor(request) {
|
|
151
174
|
super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request);
|
|
@@ -153,6 +176,10 @@ class MaxRedirectsError extends RequestError {
|
|
|
153
176
|
}
|
|
154
177
|
}
|
|
155
178
|
exports.MaxRedirectsError = MaxRedirectsError;
|
|
179
|
+
/**
|
|
180
|
+
An error to be thrown when the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304.
|
|
181
|
+
Includes a `response` property.
|
|
182
|
+
*/
|
|
156
183
|
class HTTPError extends RequestError {
|
|
157
184
|
constructor(response) {
|
|
158
185
|
super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request);
|
|
@@ -160,6 +187,10 @@ class HTTPError extends RequestError {
|
|
|
160
187
|
}
|
|
161
188
|
}
|
|
162
189
|
exports.HTTPError = HTTPError;
|
|
190
|
+
/**
|
|
191
|
+
An error to be thrown when a cache method fails.
|
|
192
|
+
For example, if the database goes down or there's a filesystem error.
|
|
193
|
+
*/
|
|
163
194
|
class CacheError extends RequestError {
|
|
164
195
|
constructor(error, request) {
|
|
165
196
|
super(error.message, error, request);
|
|
@@ -167,6 +198,9 @@ class CacheError extends RequestError {
|
|
|
167
198
|
}
|
|
168
199
|
}
|
|
169
200
|
exports.CacheError = CacheError;
|
|
201
|
+
/**
|
|
202
|
+
An error to be thrown when the request body is a stream and an error occurs while reading from that stream.
|
|
203
|
+
*/
|
|
170
204
|
class UploadError extends RequestError {
|
|
171
205
|
constructor(error, request) {
|
|
172
206
|
super(error.message, error, request);
|
|
@@ -174,6 +208,10 @@ class UploadError extends RequestError {
|
|
|
174
208
|
}
|
|
175
209
|
}
|
|
176
210
|
exports.UploadError = UploadError;
|
|
211
|
+
/**
|
|
212
|
+
An error to be thrown when the request is aborted due to a timeout.
|
|
213
|
+
Includes an `event` and `timings` property.
|
|
214
|
+
*/
|
|
177
215
|
class TimeoutError extends RequestError {
|
|
178
216
|
constructor(error, timings, request) {
|
|
179
217
|
super(error.message, error, request);
|
|
@@ -183,6 +221,9 @@ class TimeoutError extends RequestError {
|
|
|
183
221
|
}
|
|
184
222
|
}
|
|
185
223
|
exports.TimeoutError = TimeoutError;
|
|
224
|
+
/**
|
|
225
|
+
An error to be thrown when reading from response stream fails.
|
|
226
|
+
*/
|
|
186
227
|
class ReadError extends RequestError {
|
|
187
228
|
constructor(error, request) {
|
|
188
229
|
super(error.message, error, request);
|
|
@@ -190,6 +231,9 @@ class ReadError extends RequestError {
|
|
|
190
231
|
}
|
|
191
232
|
}
|
|
192
233
|
exports.ReadError = ReadError;
|
|
234
|
+
/**
|
|
235
|
+
An error to be thrown when given an unsupported protocol.
|
|
236
|
+
*/
|
|
193
237
|
class UnsupportedProtocolError extends RequestError {
|
|
194
238
|
constructor(options) {
|
|
195
239
|
super(`Unsupported protocol "${options.url.protocol}"`, {}, options);
|
|
@@ -208,6 +252,9 @@ const proxiedRequestEvents = [
|
|
|
208
252
|
class Request extends stream_1.Duplex {
|
|
209
253
|
constructor(url, options = {}, defaults) {
|
|
210
254
|
super({
|
|
255
|
+
// This must be false, to enable throwing after destroy
|
|
256
|
+
// It is used for retry logic in Promise API
|
|
257
|
+
autoDestroy: false,
|
|
211
258
|
// It needs to be zero because we're just proxying the data to another stream
|
|
212
259
|
highWaterMark: 0
|
|
213
260
|
});
|
|
@@ -219,6 +266,7 @@ class Request extends stream_1.Duplex {
|
|
|
219
266
|
this[kStopReading] = false;
|
|
220
267
|
this[kTriggerRead] = false;
|
|
221
268
|
this[kJobs] = [];
|
|
269
|
+
this.retryCount = 0;
|
|
222
270
|
// TODO: Remove this when targeting Node.js >= 12
|
|
223
271
|
this._progressCallbacks = [];
|
|
224
272
|
const unlockWrite = () => this._unlockWrite();
|
|
@@ -257,7 +305,7 @@ class Request extends stream_1.Duplex {
|
|
|
257
305
|
this.options = nonNormalizedOptions;
|
|
258
306
|
}
|
|
259
307
|
else {
|
|
260
|
-
// @ts-
|
|
308
|
+
// @ts-expect-error Common TypeScript bug saying that `this.constructor` is not accessible
|
|
261
309
|
this.options = this.constructor.normalizeArguments(url, nonNormalizedOptions, defaults);
|
|
262
310
|
}
|
|
263
311
|
const { url: normalizedURL } = this.options;
|
|
@@ -276,6 +324,8 @@ class Request extends stream_1.Duplex {
|
|
|
276
324
|
for (const job of this[kJobs]) {
|
|
277
325
|
job();
|
|
278
326
|
}
|
|
327
|
+
// Prevent memory leak
|
|
328
|
+
this[kJobs].length = 0;
|
|
279
329
|
this.requestInitialized = true;
|
|
280
330
|
}
|
|
281
331
|
catch (error) {
|
|
@@ -345,6 +395,7 @@ class Request extends stream_1.Duplex {
|
|
|
345
395
|
is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.certificate);
|
|
346
396
|
is_1.assert.any([is_1.default.string, is_1.default.undefined], options.https.passphrase);
|
|
347
397
|
}
|
|
398
|
+
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.cacheOptions);
|
|
348
399
|
// `options.method`
|
|
349
400
|
if (is_1.default.string(options.method)) {
|
|
350
401
|
options.method = options.method.toUpperCase();
|
|
@@ -442,6 +493,7 @@ class Request extends stream_1.Duplex {
|
|
|
442
493
|
}
|
|
443
494
|
// Set search params
|
|
444
495
|
if (options.searchParams) {
|
|
496
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
445
497
|
options.url.search = options.searchParams.toString();
|
|
446
498
|
}
|
|
447
499
|
// Protocol check
|
|
@@ -475,7 +527,7 @@ class Request extends stream_1.Duplex {
|
|
|
475
527
|
getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar));
|
|
476
528
|
options.cookieJar = {
|
|
477
529
|
setCookie,
|
|
478
|
-
getCookieString
|
|
530
|
+
getCookieString: getCookieString
|
|
479
531
|
};
|
|
480
532
|
}
|
|
481
533
|
}
|
|
@@ -483,12 +535,44 @@ class Request extends stream_1.Duplex {
|
|
|
483
535
|
const { cache } = options;
|
|
484
536
|
if (cache) {
|
|
485
537
|
if (!cacheableStore.has(cache)) {
|
|
486
|
-
cacheableStore.set(cache, new CacheableRequest(((requestOptions, handler) =>
|
|
538
|
+
cacheableStore.set(cache, new CacheableRequest(((requestOptions, handler) => {
|
|
539
|
+
const result = requestOptions[kRequest](requestOptions, handler);
|
|
540
|
+
// TODO: remove this when `cacheable-request` supports async request functions.
|
|
541
|
+
if (is_1.default.promise(result)) {
|
|
542
|
+
// @ts-expect-error
|
|
543
|
+
// We only need to implement the error handler in order to support HTTP2 caching.
|
|
544
|
+
// The result will be a promise anyway.
|
|
545
|
+
result.once = (event, handler) => {
|
|
546
|
+
if (event === 'error') {
|
|
547
|
+
result.catch(handler);
|
|
548
|
+
}
|
|
549
|
+
else if (event === 'abort') {
|
|
550
|
+
// The empty catch is needed here in case when
|
|
551
|
+
// it rejects before it's `await`ed in `_makeRequest`.
|
|
552
|
+
(async () => {
|
|
553
|
+
try {
|
|
554
|
+
const request = (await result);
|
|
555
|
+
request.once('abort', handler);
|
|
556
|
+
}
|
|
557
|
+
catch (_a) { }
|
|
558
|
+
})();
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
/* istanbul ignore next: safety check */
|
|
562
|
+
throw new Error(`Unknown HTTP2 promise event: ${event}`);
|
|
563
|
+
}
|
|
564
|
+
return result;
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
return result;
|
|
568
|
+
}), cache));
|
|
487
569
|
}
|
|
488
570
|
}
|
|
571
|
+
// `options.cacheOptions`
|
|
572
|
+
options.cacheOptions = { ...options.cacheOptions };
|
|
489
573
|
// `options.dnsCache`
|
|
490
574
|
if (options.dnsCache === true) {
|
|
491
|
-
options.dnsCache =
|
|
575
|
+
options.dnsCache = globalDnsCache;
|
|
492
576
|
}
|
|
493
577
|
else if (!is_1.default.undefined(options.dnsCache) && !options.dnsCache.lookup) {
|
|
494
578
|
throw new TypeError(`Parameter \`dnsCache\` must be a CacheableLookup instance or a boolean, got ${is_1.default(options.dnsCache)}`);
|
|
@@ -579,7 +663,7 @@ class Request extends stream_1.Duplex {
|
|
|
579
663
|
options.maxRedirects = (_d = options.maxRedirects) !== null && _d !== void 0 ? _d : 0;
|
|
580
664
|
// Set non-enumerable properties
|
|
581
665
|
exports.setNonEnumerableProperties([defaults, rawOptions], options);
|
|
582
|
-
return options;
|
|
666
|
+
return normalize_arguments_1.default(options, defaults);
|
|
583
667
|
}
|
|
584
668
|
_lockWrite() {
|
|
585
669
|
const onLockedWrite = () => {
|
|
@@ -665,7 +749,7 @@ class Request extends stream_1.Duplex {
|
|
|
665
749
|
}
|
|
666
750
|
this[kBodySize] = Number(headers['content-length']) || undefined;
|
|
667
751
|
}
|
|
668
|
-
async
|
|
752
|
+
async _onResponseBase(response) {
|
|
669
753
|
const { options } = this;
|
|
670
754
|
const { url } = options;
|
|
671
755
|
this[kOriginalResponse] = response;
|
|
@@ -681,6 +765,7 @@ class Request extends stream_1.Duplex {
|
|
|
681
765
|
typedResponse.request = this;
|
|
682
766
|
typedResponse.isFromCache = response.fromCache || false;
|
|
683
767
|
typedResponse.ip = this.ip;
|
|
768
|
+
typedResponse.retryCount = this.retryCount;
|
|
684
769
|
this[kIsFromCache] = typedResponse.isFromCache;
|
|
685
770
|
this[kResponseSize] = Number(response.headers['content-length']) || undefined;
|
|
686
771
|
this[kResponse] = response;
|
|
@@ -718,7 +803,7 @@ class Request extends stream_1.Duplex {
|
|
|
718
803
|
}
|
|
719
804
|
if (options.followRedirect && response.headers.location && redirectCodes.has(statusCode)) {
|
|
720
805
|
// We're being redirected, we don't care about the response.
|
|
721
|
-
// It'd be
|
|
806
|
+
// It'd be best to abort the request, but we can't because
|
|
722
807
|
// we would have to sacrifice the TCP connection. We don't want that.
|
|
723
808
|
response.resume();
|
|
724
809
|
if (this[kRequest]) {
|
|
@@ -765,7 +850,10 @@ class Request extends stream_1.Duplex {
|
|
|
765
850
|
delete options.headers.authorization;
|
|
766
851
|
}
|
|
767
852
|
if (options.username || options.password) {
|
|
853
|
+
// TODO: Fix this ignore.
|
|
854
|
+
// @ts-expect-error
|
|
768
855
|
delete options.username;
|
|
856
|
+
// @ts-expect-error
|
|
769
857
|
delete options.password;
|
|
770
858
|
}
|
|
771
859
|
}
|
|
@@ -784,13 +872,9 @@ class Request extends stream_1.Duplex {
|
|
|
784
872
|
}
|
|
785
873
|
return;
|
|
786
874
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
await this._beforeError(new HTTPError(typedResponse));
|
|
791
|
-
if (this.destroyed) {
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
875
|
+
if (options.isStream && options.throwHttpErrors && !is_response_ok_1.isResponseOk(typedResponse)) {
|
|
876
|
+
this._beforeError(new HTTPError(typedResponse));
|
|
877
|
+
return;
|
|
794
878
|
}
|
|
795
879
|
response.on('readable', () => {
|
|
796
880
|
if (this[kTriggerRead]) {
|
|
@@ -822,6 +906,15 @@ class Request extends stream_1.Duplex {
|
|
|
822
906
|
destination.statusCode = statusCode;
|
|
823
907
|
}
|
|
824
908
|
}
|
|
909
|
+
async _onResponse(response) {
|
|
910
|
+
try {
|
|
911
|
+
await this._onResponseBase(response);
|
|
912
|
+
}
|
|
913
|
+
catch (error) {
|
|
914
|
+
/* istanbul ignore next: better safe than sorry */
|
|
915
|
+
this._beforeError(error);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
825
918
|
_onRequest(request) {
|
|
826
919
|
const { options } = this;
|
|
827
920
|
const { timeout, url } = options;
|
|
@@ -829,11 +922,14 @@ class Request extends stream_1.Duplex {
|
|
|
829
922
|
this[kCancelTimeouts] = timed_out_1.default(request, timeout, url);
|
|
830
923
|
const responseEventName = options.cache ? 'cacheableResponse' : 'response';
|
|
831
924
|
request.once(responseEventName, (response) => {
|
|
832
|
-
this._onResponse(response);
|
|
925
|
+
void this._onResponse(response);
|
|
833
926
|
});
|
|
834
927
|
request.once('error', (error) => {
|
|
928
|
+
var _a;
|
|
835
929
|
// Force clean-up, because some packages (e.g. nock) don't do this.
|
|
836
930
|
request.destroy();
|
|
931
|
+
// Node.js <= 12.18.2 mistakenly emits the response `end` first.
|
|
932
|
+
(_a = request.res) === null || _a === void 0 ? void 0 : _a.removeAllListeners('end');
|
|
837
933
|
if (error instanceof timed_out_1.TimeoutError) {
|
|
838
934
|
error = new TimeoutError(error, this.timings, this);
|
|
839
935
|
}
|
|
@@ -853,14 +949,11 @@ class Request extends stream_1.Duplex {
|
|
|
853
949
|
body.once('error', (error) => {
|
|
854
950
|
this._beforeError(new UploadError(error, this));
|
|
855
951
|
});
|
|
856
|
-
body.once('end', () => {
|
|
857
|
-
delete options.body;
|
|
858
|
-
});
|
|
859
952
|
}
|
|
860
953
|
else {
|
|
861
954
|
this._unlockWrite();
|
|
862
955
|
if (!is_1.default.undefined(body)) {
|
|
863
|
-
this._writeRequest(body,
|
|
956
|
+
this._writeRequest(body, undefined, () => { });
|
|
864
957
|
currentRequest.end();
|
|
865
958
|
this._lockWrite();
|
|
866
959
|
}
|
|
@@ -876,26 +969,30 @@ class Request extends stream_1.Duplex {
|
|
|
876
969
|
// TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed
|
|
877
970
|
Object.assign(options, url_to_options_1.default(url));
|
|
878
971
|
// `http-cache-semantics` checks this
|
|
972
|
+
// TODO: Fix this ignore.
|
|
973
|
+
// @ts-expect-error
|
|
879
974
|
delete options.url;
|
|
975
|
+
let request;
|
|
880
976
|
// This is ugly
|
|
881
|
-
const cacheRequest = cacheableStore.get(options.cache)(options, response => {
|
|
882
|
-
const typedResponse = response;
|
|
883
|
-
const { req } = typedResponse;
|
|
977
|
+
const cacheRequest = cacheableStore.get(options.cache)(options, async (response) => {
|
|
884
978
|
// TODO: Fix `cacheable-response`
|
|
885
|
-
|
|
886
|
-
if (
|
|
887
|
-
|
|
979
|
+
response._readableState.autoDestroy = false;
|
|
980
|
+
if (request) {
|
|
981
|
+
(await request).emit('cacheableResponse', response);
|
|
888
982
|
}
|
|
889
|
-
resolve(
|
|
983
|
+
resolve(response);
|
|
890
984
|
});
|
|
891
985
|
// Restore options
|
|
892
986
|
options.url = url;
|
|
893
987
|
cacheRequest.once('error', reject);
|
|
894
|
-
cacheRequest.once('request',
|
|
988
|
+
cacheRequest.once('request', async (requestOrPromise) => {
|
|
989
|
+
request = requestOrPromise;
|
|
990
|
+
resolve(request);
|
|
991
|
+
});
|
|
895
992
|
});
|
|
896
993
|
}
|
|
897
994
|
async _makeRequest() {
|
|
898
|
-
var _a;
|
|
995
|
+
var _a, _b, _c, _d, _e;
|
|
899
996
|
const { options } = this;
|
|
900
997
|
const { headers } = options;
|
|
901
998
|
for (const key in headers) {
|
|
@@ -921,7 +1018,7 @@ class Request extends stream_1.Duplex {
|
|
|
921
1018
|
// eslint-disable-next-line no-await-in-loop
|
|
922
1019
|
const result = await hook(options);
|
|
923
1020
|
if (!is_1.default.undefined(result)) {
|
|
924
|
-
// @ts-
|
|
1021
|
+
// @ts-expect-error Skip the type mismatch to support abstract responses
|
|
925
1022
|
options.request = () => result;
|
|
926
1023
|
break;
|
|
927
1024
|
}
|
|
@@ -961,14 +1058,20 @@ class Request extends stream_1.Duplex {
|
|
|
961
1058
|
// Prepare plain HTTP request options
|
|
962
1059
|
options[kRequest] = realFn;
|
|
963
1060
|
delete options.request;
|
|
1061
|
+
// TODO: Fix this ignore.
|
|
1062
|
+
// @ts-expect-error
|
|
964
1063
|
delete options.timeout;
|
|
965
1064
|
const requestOptions = options;
|
|
1065
|
+
requestOptions.shared = (_b = options.cacheOptions) === null || _b === void 0 ? void 0 : _b.shared;
|
|
1066
|
+
requestOptions.cacheHeuristic = (_c = options.cacheOptions) === null || _c === void 0 ? void 0 : _c.cacheHeuristic;
|
|
1067
|
+
requestOptions.immutableMinTimeToLive = (_d = options.cacheOptions) === null || _d === void 0 ? void 0 : _d.immutableMinTimeToLive;
|
|
1068
|
+
requestOptions.ignoreCargoCult = (_e = options.cacheOptions) === null || _e === void 0 ? void 0 : _e.ignoreCargoCult;
|
|
966
1069
|
// If `dnsLookupIpVersion` is not present do not override `family`
|
|
967
1070
|
if (options.dnsLookupIpVersion !== undefined) {
|
|
968
1071
|
try {
|
|
969
1072
|
requestOptions.family = dns_ip_version_1.dnsLookupIpVersionToFamily(options.dnsLookupIpVersion);
|
|
970
1073
|
}
|
|
971
|
-
catch (
|
|
1074
|
+
catch (_f) {
|
|
972
1075
|
throw new Error('Invalid `dnsLookupIpVersion` option value');
|
|
973
1076
|
}
|
|
974
1077
|
}
|
|
@@ -1002,20 +1105,42 @@ class Request extends stream_1.Duplex {
|
|
|
1002
1105
|
options.request = request;
|
|
1003
1106
|
options.timeout = timeout;
|
|
1004
1107
|
options.agent = agent;
|
|
1108
|
+
// HTTPS options restore
|
|
1109
|
+
if (options.https) {
|
|
1110
|
+
if ('rejectUnauthorized' in options.https) {
|
|
1111
|
+
delete requestOptions.rejectUnauthorized;
|
|
1112
|
+
}
|
|
1113
|
+
if (options.https.checkServerIdentity) {
|
|
1114
|
+
// @ts-expect-error - This one will be removed when we remove the alias.
|
|
1115
|
+
delete requestOptions.checkServerIdentity;
|
|
1116
|
+
}
|
|
1117
|
+
if (options.https.certificateAuthority) {
|
|
1118
|
+
delete requestOptions.ca;
|
|
1119
|
+
}
|
|
1120
|
+
if (options.https.certificate) {
|
|
1121
|
+
delete requestOptions.cert;
|
|
1122
|
+
}
|
|
1123
|
+
if (options.https.key) {
|
|
1124
|
+
delete requestOptions.key;
|
|
1125
|
+
}
|
|
1126
|
+
if (options.https.passphrase) {
|
|
1127
|
+
delete requestOptions.passphrase;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1005
1130
|
if (isClientRequest(requestOrResponse)) {
|
|
1006
1131
|
this._onRequest(requestOrResponse);
|
|
1007
1132
|
// Emit the response after the stream has been ended
|
|
1008
1133
|
}
|
|
1009
1134
|
else if (this.writable) {
|
|
1010
1135
|
this.once('finish', () => {
|
|
1011
|
-
this._onResponse(requestOrResponse);
|
|
1136
|
+
void this._onResponse(requestOrResponse);
|
|
1012
1137
|
});
|
|
1013
1138
|
this._unlockWrite();
|
|
1014
1139
|
this.end();
|
|
1015
1140
|
this._lockWrite();
|
|
1016
1141
|
}
|
|
1017
1142
|
else {
|
|
1018
|
-
this._onResponse(requestOrResponse);
|
|
1143
|
+
void this._onResponse(requestOrResponse);
|
|
1019
1144
|
}
|
|
1020
1145
|
}
|
|
1021
1146
|
catch (error) {
|
|
@@ -1025,23 +1150,7 @@ class Request extends stream_1.Duplex {
|
|
|
1025
1150
|
throw new RequestError(error.message, error, this);
|
|
1026
1151
|
}
|
|
1027
1152
|
}
|
|
1028
|
-
async
|
|
1029
|
-
if (this.destroyed) {
|
|
1030
|
-
return;
|
|
1031
|
-
}
|
|
1032
|
-
this[kStopReading] = true;
|
|
1033
|
-
if (!(error instanceof RequestError)) {
|
|
1034
|
-
error = new RequestError(error.message, error, this);
|
|
1035
|
-
}
|
|
1036
|
-
try {
|
|
1037
|
-
const { response } = error;
|
|
1038
|
-
if (response) {
|
|
1039
|
-
response.setEncoding(this._readableState.encoding);
|
|
1040
|
-
response.rawBody = await get_buffer_1.default(response);
|
|
1041
|
-
response.body = response.rawBody.toString();
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
catch (_) { }
|
|
1153
|
+
async _error(error) {
|
|
1045
1154
|
try {
|
|
1046
1155
|
for (const hook of this.options.hooks.beforeError) {
|
|
1047
1156
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -1053,6 +1162,87 @@ class Request extends stream_1.Duplex {
|
|
|
1053
1162
|
}
|
|
1054
1163
|
this.destroy(error);
|
|
1055
1164
|
}
|
|
1165
|
+
_beforeError(error) {
|
|
1166
|
+
if (this[kStopReading]) {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
const { options } = this;
|
|
1170
|
+
const retryCount = this.retryCount + 1;
|
|
1171
|
+
this[kStopReading] = true;
|
|
1172
|
+
if (!(error instanceof RequestError)) {
|
|
1173
|
+
error = new RequestError(error.message, error, this);
|
|
1174
|
+
}
|
|
1175
|
+
const typedError = error;
|
|
1176
|
+
const { response } = typedError;
|
|
1177
|
+
void (async () => {
|
|
1178
|
+
if (response && !response.body) {
|
|
1179
|
+
response.setEncoding(this._readableState.encoding);
|
|
1180
|
+
try {
|
|
1181
|
+
response.rawBody = await get_buffer_1.default(response);
|
|
1182
|
+
}
|
|
1183
|
+
catch (_a) { }
|
|
1184
|
+
response.body = response.rawBody.toString();
|
|
1185
|
+
}
|
|
1186
|
+
if (this.listenerCount('retry') !== 0) {
|
|
1187
|
+
let backoff;
|
|
1188
|
+
try {
|
|
1189
|
+
let retryAfter;
|
|
1190
|
+
if (response && 'retry-after' in response.headers) {
|
|
1191
|
+
retryAfter = Number(response.headers['retry-after']);
|
|
1192
|
+
if (Number.isNaN(retryAfter)) {
|
|
1193
|
+
retryAfter = Date.parse(response.headers['retry-after']) - Date.now();
|
|
1194
|
+
if (retryAfter <= 0) {
|
|
1195
|
+
retryAfter = 1;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1199
|
+
retryAfter *= 1000;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
backoff = await options.retry.calculateDelay({
|
|
1203
|
+
attemptCount: retryCount,
|
|
1204
|
+
retryOptions: options.retry,
|
|
1205
|
+
error: typedError,
|
|
1206
|
+
retryAfter,
|
|
1207
|
+
computedValue: calculate_retry_delay_1.default({
|
|
1208
|
+
attemptCount: retryCount,
|
|
1209
|
+
retryOptions: options.retry,
|
|
1210
|
+
error: typedError,
|
|
1211
|
+
retryAfter,
|
|
1212
|
+
computedValue: 0
|
|
1213
|
+
})
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
catch (error_) {
|
|
1217
|
+
void this._error(new RequestError(error_.message, error_, this));
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
if (backoff) {
|
|
1221
|
+
const retry = async () => {
|
|
1222
|
+
try {
|
|
1223
|
+
for (const hook of this.options.hooks.beforeRetry) {
|
|
1224
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1225
|
+
await hook(this.options, typedError, retryCount);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
catch (error_) {
|
|
1229
|
+
void this._error(new RequestError(error_.message, error, this));
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
// Something forced us to abort the retry
|
|
1233
|
+
if (this.destroyed) {
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
this.destroy();
|
|
1237
|
+
this.emit('retry', retryCount, error);
|
|
1238
|
+
};
|
|
1239
|
+
this[kRetryTimeout] = setTimeout(retry, backoff);
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
void this._error(typedError);
|
|
1244
|
+
})();
|
|
1245
|
+
}
|
|
1056
1246
|
_read() {
|
|
1057
1247
|
this[kTriggerRead] = true;
|
|
1058
1248
|
const response = this[kResponse];
|
|
@@ -1074,6 +1264,7 @@ class Request extends stream_1.Duplex {
|
|
|
1074
1264
|
}
|
|
1075
1265
|
}
|
|
1076
1266
|
}
|
|
1267
|
+
// Node.js 12 has incorrect types, so the encoding must be a string
|
|
1077
1268
|
_write(chunk, encoding, callback) {
|
|
1078
1269
|
const write = () => {
|
|
1079
1270
|
this._writeRequest(chunk, encoding, callback);
|
|
@@ -1135,6 +1326,9 @@ class Request extends stream_1.Duplex {
|
|
|
1135
1326
|
}
|
|
1136
1327
|
_destroy(error, callback) {
|
|
1137
1328
|
var _a;
|
|
1329
|
+
this[kStopReading] = true;
|
|
1330
|
+
// Prevent further retries
|
|
1331
|
+
clearTimeout(this[kRetryTimeout]);
|
|
1138
1332
|
if (kRequest in this) {
|
|
1139
1333
|
this[kCancelTimeouts]();
|
|
1140
1334
|
// TODO: Remove the next `if` when these get fixed:
|
|
@@ -1148,10 +1342,19 @@ class Request extends stream_1.Duplex {
|
|
|
1148
1342
|
}
|
|
1149
1343
|
callback(error);
|
|
1150
1344
|
}
|
|
1345
|
+
get _isAboutToError() {
|
|
1346
|
+
return this[kStopReading];
|
|
1347
|
+
}
|
|
1348
|
+
/**
|
|
1349
|
+
The remote IP address.
|
|
1350
|
+
*/
|
|
1151
1351
|
get ip() {
|
|
1152
1352
|
var _a;
|
|
1153
1353
|
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket.remoteAddress;
|
|
1154
1354
|
}
|
|
1355
|
+
/**
|
|
1356
|
+
Indicates whether the request has been aborted or not.
|
|
1357
|
+
*/
|
|
1155
1358
|
get aborted() {
|
|
1156
1359
|
var _a, _b, _c;
|
|
1157
1360
|
return ((_b = (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.destroyed) !== null && _b !== void 0 ? _b : this.destroyed) && !((_c = this[kOriginalResponse]) === null || _c === void 0 ? void 0 : _c.complete);
|
|
@@ -1160,6 +1363,9 @@ class Request extends stream_1.Duplex {
|
|
|
1160
1363
|
var _a;
|
|
1161
1364
|
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket;
|
|
1162
1365
|
}
|
|
1366
|
+
/**
|
|
1367
|
+
Progress event for downloading (receiving a response).
|
|
1368
|
+
*/
|
|
1163
1369
|
get downloadProgress() {
|
|
1164
1370
|
let percent;
|
|
1165
1371
|
if (this[kResponseSize]) {
|
|
@@ -1177,6 +1383,9 @@ class Request extends stream_1.Duplex {
|
|
|
1177
1383
|
total: this[kResponseSize]
|
|
1178
1384
|
};
|
|
1179
1385
|
}
|
|
1386
|
+
/**
|
|
1387
|
+
Progress event for uploading (sending a request).
|
|
1388
|
+
*/
|
|
1180
1389
|
get uploadProgress() {
|
|
1181
1390
|
let percent;
|
|
1182
1391
|
if (this[kBodySize]) {
|
|
@@ -1194,16 +1403,43 @@ class Request extends stream_1.Duplex {
|
|
|
1194
1403
|
total: this[kBodySize]
|
|
1195
1404
|
};
|
|
1196
1405
|
}
|
|
1406
|
+
/**
|
|
1407
|
+
The object contains the following properties:
|
|
1408
|
+
|
|
1409
|
+
- `start` - Time when the request started.
|
|
1410
|
+
- `socket` - Time when a socket was assigned to the request.
|
|
1411
|
+
- `lookup` - Time when the DNS lookup finished.
|
|
1412
|
+
- `connect` - Time when the socket successfully connected.
|
|
1413
|
+
- `secureConnect` - Time when the socket securely connected.
|
|
1414
|
+
- `upload` - Time when the request finished uploading.
|
|
1415
|
+
- `response` - Time when the request fired `response` event.
|
|
1416
|
+
- `end` - Time when the response fired `end` event.
|
|
1417
|
+
- `error` - Time when the request fired `error` event.
|
|
1418
|
+
- `abort` - Time when the request fired `abort` event.
|
|
1419
|
+
- `phases`
|
|
1420
|
+
- `wait` - `timings.socket - timings.start`
|
|
1421
|
+
- `dns` - `timings.lookup - timings.socket`
|
|
1422
|
+
- `tcp` - `timings.connect - timings.lookup`
|
|
1423
|
+
- `tls` - `timings.secureConnect - timings.connect`
|
|
1424
|
+
- `request` - `timings.upload - (timings.secureConnect || timings.connect)`
|
|
1425
|
+
- `firstByte` - `timings.response - timings.upload`
|
|
1426
|
+
- `download` - `timings.end - timings.response`
|
|
1427
|
+
- `total` - `(timings.end || timings.error || timings.abort) - timings.start`
|
|
1428
|
+
|
|
1429
|
+
If something has not been measured yet, it will be `undefined`.
|
|
1430
|
+
|
|
1431
|
+
__Note__: The time is a `number` representing the milliseconds elapsed since the UNIX epoch.
|
|
1432
|
+
*/
|
|
1197
1433
|
get timings() {
|
|
1198
1434
|
var _a;
|
|
1199
1435
|
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.timings;
|
|
1200
1436
|
}
|
|
1437
|
+
/**
|
|
1438
|
+
Whether the response was retrieved from the cache.
|
|
1439
|
+
*/
|
|
1201
1440
|
get isFromCache() {
|
|
1202
1441
|
return this[kIsFromCache];
|
|
1203
1442
|
}
|
|
1204
|
-
get _response() {
|
|
1205
|
-
return this[kResponse];
|
|
1206
|
-
}
|
|
1207
1443
|
pipe(destination, options) {
|
|
1208
1444
|
if (this[kStartedReading]) {
|
|
1209
1445
|
throw new Error('Failed to pipe. The response has been emitted already.');
|