got 11.5.2 → 11.7.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 +44 -125
- 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 +206 -24
- package/dist/source/as-promise/types.js +18 -7
- 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 +823 -5
- package/dist/source/core/index.js +250 -58
- 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/create.js +16 -7
- package/dist/source/index.js +3 -2
- package/dist/source/types.d.ts +240 -1
- package/package.json +20 -16
- package/readme.md +234 -21
- 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 -127
|
@@ -25,7 +25,10 @@ const options_to_url_1 = require("./utils/options-to-url");
|
|
|
25
25
|
const weakable_map_1 = require("./utils/weakable-map");
|
|
26
26
|
const get_buffer_1 = require("./utils/get-buffer");
|
|
27
27
|
const dns_ip_version_1 = require("./utils/dns-ip-version");
|
|
28
|
+
const is_response_ok_1 = require("./utils/is-response-ok");
|
|
28
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");
|
|
29
32
|
const globalDnsCache = new cacheable_lookup_1.default();
|
|
30
33
|
const kRequest = Symbol('request');
|
|
31
34
|
const kResponse = Symbol('response');
|
|
@@ -43,10 +46,19 @@ const kTriggerRead = Symbol('triggerRead');
|
|
|
43
46
|
const kBody = Symbol('body');
|
|
44
47
|
const kJobs = Symbol('jobs');
|
|
45
48
|
const kOriginalResponse = Symbol('originalResponse');
|
|
49
|
+
const kRetryTimeout = Symbol('retryTimeout');
|
|
46
50
|
exports.kIsNormalizedAlready = Symbol('isNormalizedAlready');
|
|
47
51
|
const supportsBrotli = is_1.default.string(process.versions.brotli);
|
|
48
52
|
exports.withoutBody = new Set(['GET', 'HEAD']);
|
|
49
|
-
exports.knownHookEvents = [
|
|
53
|
+
exports.knownHookEvents = [
|
|
54
|
+
'init',
|
|
55
|
+
'beforeRequest',
|
|
56
|
+
'beforeRedirect',
|
|
57
|
+
'beforeError',
|
|
58
|
+
'beforeRetry',
|
|
59
|
+
// Promise-Only
|
|
60
|
+
'afterResponse'
|
|
61
|
+
];
|
|
50
62
|
function validateSearchParameters(searchParameters) {
|
|
51
63
|
// eslint-disable-next-line guard-for-in
|
|
52
64
|
for (const key in searchParameters) {
|
|
@@ -103,6 +115,10 @@ exports.setNonEnumerableProperties = (sources, to) => {
|
|
|
103
115
|
}
|
|
104
116
|
Object.defineProperties(to, properties);
|
|
105
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
An error to be thrown when a request fails.
|
|
120
|
+
Contains a `code` property with error class code, like `ECONNREFUSED`.
|
|
121
|
+
*/
|
|
106
122
|
class RequestError extends Error {
|
|
107
123
|
constructor(message, error, self) {
|
|
108
124
|
var _a;
|
|
@@ -149,6 +165,10 @@ class RequestError extends Error {
|
|
|
149
165
|
}
|
|
150
166
|
}
|
|
151
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
|
+
*/
|
|
152
172
|
class MaxRedirectsError extends RequestError {
|
|
153
173
|
constructor(request) {
|
|
154
174
|
super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request);
|
|
@@ -156,6 +176,10 @@ class MaxRedirectsError extends RequestError {
|
|
|
156
176
|
}
|
|
157
177
|
}
|
|
158
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
|
+
*/
|
|
159
183
|
class HTTPError extends RequestError {
|
|
160
184
|
constructor(response) {
|
|
161
185
|
super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request);
|
|
@@ -163,6 +187,10 @@ class HTTPError extends RequestError {
|
|
|
163
187
|
}
|
|
164
188
|
}
|
|
165
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
|
+
*/
|
|
166
194
|
class CacheError extends RequestError {
|
|
167
195
|
constructor(error, request) {
|
|
168
196
|
super(error.message, error, request);
|
|
@@ -170,6 +198,9 @@ class CacheError extends RequestError {
|
|
|
170
198
|
}
|
|
171
199
|
}
|
|
172
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
|
+
*/
|
|
173
204
|
class UploadError extends RequestError {
|
|
174
205
|
constructor(error, request) {
|
|
175
206
|
super(error.message, error, request);
|
|
@@ -177,6 +208,10 @@ class UploadError extends RequestError {
|
|
|
177
208
|
}
|
|
178
209
|
}
|
|
179
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
|
+
*/
|
|
180
215
|
class TimeoutError extends RequestError {
|
|
181
216
|
constructor(error, timings, request) {
|
|
182
217
|
super(error.message, error, request);
|
|
@@ -186,6 +221,9 @@ class TimeoutError extends RequestError {
|
|
|
186
221
|
}
|
|
187
222
|
}
|
|
188
223
|
exports.TimeoutError = TimeoutError;
|
|
224
|
+
/**
|
|
225
|
+
An error to be thrown when reading from response stream fails.
|
|
226
|
+
*/
|
|
189
227
|
class ReadError extends RequestError {
|
|
190
228
|
constructor(error, request) {
|
|
191
229
|
super(error.message, error, request);
|
|
@@ -193,6 +231,9 @@ class ReadError extends RequestError {
|
|
|
193
231
|
}
|
|
194
232
|
}
|
|
195
233
|
exports.ReadError = ReadError;
|
|
234
|
+
/**
|
|
235
|
+
An error to be thrown when given an unsupported protocol.
|
|
236
|
+
*/
|
|
196
237
|
class UnsupportedProtocolError extends RequestError {
|
|
197
238
|
constructor(options) {
|
|
198
239
|
super(`Unsupported protocol "${options.url.protocol}"`, {}, options);
|
|
@@ -211,6 +252,9 @@ const proxiedRequestEvents = [
|
|
|
211
252
|
class Request extends stream_1.Duplex {
|
|
212
253
|
constructor(url, options = {}, defaults) {
|
|
213
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,
|
|
214
258
|
// It needs to be zero because we're just proxying the data to another stream
|
|
215
259
|
highWaterMark: 0
|
|
216
260
|
});
|
|
@@ -222,6 +266,7 @@ class Request extends stream_1.Duplex {
|
|
|
222
266
|
this[kStopReading] = false;
|
|
223
267
|
this[kTriggerRead] = false;
|
|
224
268
|
this[kJobs] = [];
|
|
269
|
+
this.retryCount = 0;
|
|
225
270
|
// TODO: Remove this when targeting Node.js >= 12
|
|
226
271
|
this._progressCallbacks = [];
|
|
227
272
|
const unlockWrite = () => this._unlockWrite();
|
|
@@ -250,18 +295,28 @@ class Request extends stream_1.Duplex {
|
|
|
250
295
|
if (json || body || form) {
|
|
251
296
|
this._lockWrite();
|
|
252
297
|
}
|
|
253
|
-
(
|
|
254
|
-
|
|
298
|
+
if (exports.kIsNormalizedAlready in options) {
|
|
299
|
+
this.options = options;
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
255
302
|
try {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
303
|
+
// @ts-expect-error Common TypeScript bug saying that `this.constructor` is not accessible
|
|
304
|
+
this.options = this.constructor.normalizeArguments(url, options, defaults);
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
// TODO: Move this to `_destroy()`
|
|
308
|
+
if (is_1.default.nodeStream(options.body)) {
|
|
309
|
+
options.body.destroy();
|
|
261
310
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
311
|
+
this.destroy(error);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
(async () => {
|
|
316
|
+
var _a;
|
|
317
|
+
try {
|
|
318
|
+
if (this.options.body instanceof fs_1.ReadStream) {
|
|
319
|
+
await waitForOpenFile(this.options.body);
|
|
265
320
|
}
|
|
266
321
|
const { url: normalizedURL } = this.options;
|
|
267
322
|
if (!normalizedURL) {
|
|
@@ -279,6 +334,8 @@ class Request extends stream_1.Duplex {
|
|
|
279
334
|
for (const job of this[kJobs]) {
|
|
280
335
|
job();
|
|
281
336
|
}
|
|
337
|
+
// Prevent memory leak
|
|
338
|
+
this[kJobs].length = 0;
|
|
282
339
|
this.requestInitialized = true;
|
|
283
340
|
}
|
|
284
341
|
catch (error) {
|
|
@@ -291,10 +348,10 @@ class Request extends stream_1.Duplex {
|
|
|
291
348
|
this.destroy(error);
|
|
292
349
|
}
|
|
293
350
|
}
|
|
294
|
-
})(
|
|
351
|
+
})();
|
|
295
352
|
}
|
|
296
353
|
static normalizeArguments(url, options, defaults) {
|
|
297
|
-
var _a, _b, _c, _d;
|
|
354
|
+
var _a, _b, _c, _d, _e;
|
|
298
355
|
const rawOptions = options;
|
|
299
356
|
if (is_1.default.object(url) && !is_1.default.urlInstance(url)) {
|
|
300
357
|
options = { ...defaults, ...url, ...options };
|
|
@@ -347,7 +404,9 @@ class Request extends stream_1.Duplex {
|
|
|
347
404
|
is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.key);
|
|
348
405
|
is_1.assert.any([is_1.default.string, is_1.default.object, is_1.default.array, is_1.default.undefined], options.https.certificate);
|
|
349
406
|
is_1.assert.any([is_1.default.string, is_1.default.undefined], options.https.passphrase);
|
|
407
|
+
is_1.assert.any([is_1.default.string, is_1.default.buffer, is_1.default.array, is_1.default.undefined], options.https.pfx);
|
|
350
408
|
}
|
|
409
|
+
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.cacheOptions);
|
|
351
410
|
// `options.method`
|
|
352
411
|
if (is_1.default.string(options.method)) {
|
|
353
412
|
options.method = options.method.toUpperCase();
|
|
@@ -405,15 +464,15 @@ class Request extends stream_1.Duplex {
|
|
|
405
464
|
options.username = (_b = options.username) !== null && _b !== void 0 ? _b : '';
|
|
406
465
|
options.password = (_c = options.password) !== null && _c !== void 0 ? _c : '';
|
|
407
466
|
// `options.prefixUrl` & `options.url`
|
|
408
|
-
if (options.prefixUrl) {
|
|
467
|
+
if (is_1.default.undefined(options.prefixUrl)) {
|
|
468
|
+
options.prefixUrl = (_d = defaults === null || defaults === void 0 ? void 0 : defaults.prefixUrl) !== null && _d !== void 0 ? _d : '';
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
409
471
|
options.prefixUrl = options.prefixUrl.toString();
|
|
410
472
|
if (options.prefixUrl !== '' && !options.prefixUrl.endsWith('/')) {
|
|
411
473
|
options.prefixUrl += '/';
|
|
412
474
|
}
|
|
413
475
|
}
|
|
414
|
-
else {
|
|
415
|
-
options.prefixUrl = '';
|
|
416
|
-
}
|
|
417
476
|
if (is_1.default.string(options.url)) {
|
|
418
477
|
if (options.url.startsWith('/')) {
|
|
419
478
|
throw new Error('`input` must not start with a slash when using `prefixUrl`');
|
|
@@ -424,6 +483,9 @@ class Request extends stream_1.Duplex {
|
|
|
424
483
|
options.url = options_to_url_1.default(options.prefixUrl, options);
|
|
425
484
|
}
|
|
426
485
|
if (options.url) {
|
|
486
|
+
if ('port' in options) {
|
|
487
|
+
delete options.port;
|
|
488
|
+
}
|
|
427
489
|
// Make it possible to change `options.prefixUrl`
|
|
428
490
|
let { prefixUrl } = options;
|
|
429
491
|
Object.defineProperty(options, 'prefixUrl', {
|
|
@@ -479,9 +541,7 @@ class Request extends stream_1.Duplex {
|
|
|
479
541
|
getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar));
|
|
480
542
|
options.cookieJar = {
|
|
481
543
|
setCookie,
|
|
482
|
-
|
|
483
|
-
// @ts-expect-error TypeScript thinks that promisifying callback(error, string) will result in Promise<void>
|
|
484
|
-
getCookieString
|
|
544
|
+
getCookieString: getCookieString
|
|
485
545
|
};
|
|
486
546
|
}
|
|
487
547
|
}
|
|
@@ -522,6 +582,8 @@ class Request extends stream_1.Duplex {
|
|
|
522
582
|
}), cache));
|
|
523
583
|
}
|
|
524
584
|
}
|
|
585
|
+
// `options.cacheOptions`
|
|
586
|
+
options.cacheOptions = { ...options.cacheOptions };
|
|
525
587
|
// `options.dnsCache`
|
|
526
588
|
if (options.dnsCache === true) {
|
|
527
589
|
options.dnsCache = globalDnsCache;
|
|
@@ -601,6 +663,9 @@ class Request extends stream_1.Duplex {
|
|
|
601
663
|
if ('passphrase' in options) {
|
|
602
664
|
deprecation_warning_1.default('"options.passphrase" was never documented, please use "options.https.passphrase"');
|
|
603
665
|
}
|
|
666
|
+
if ('pfx' in options) {
|
|
667
|
+
deprecation_warning_1.default('"options.pfx" was never documented, please use "options.https.pfx"');
|
|
668
|
+
}
|
|
604
669
|
// Other options
|
|
605
670
|
if ('followRedirects' in options) {
|
|
606
671
|
throw new TypeError('The `followRedirects` option does not exist. Use `followRedirect` instead.');
|
|
@@ -612,10 +677,10 @@ class Request extends stream_1.Duplex {
|
|
|
612
677
|
}
|
|
613
678
|
}
|
|
614
679
|
}
|
|
615
|
-
options.maxRedirects = (
|
|
680
|
+
options.maxRedirects = (_e = options.maxRedirects) !== null && _e !== void 0 ? _e : 0;
|
|
616
681
|
// Set non-enumerable properties
|
|
617
682
|
exports.setNonEnumerableProperties([defaults, rawOptions], options);
|
|
618
|
-
return options;
|
|
683
|
+
return normalize_arguments_1.default(options, defaults);
|
|
619
684
|
}
|
|
620
685
|
_lockWrite() {
|
|
621
686
|
const onLockedWrite = () => {
|
|
@@ -717,6 +782,7 @@ class Request extends stream_1.Duplex {
|
|
|
717
782
|
typedResponse.request = this;
|
|
718
783
|
typedResponse.isFromCache = response.fromCache || false;
|
|
719
784
|
typedResponse.ip = this.ip;
|
|
785
|
+
typedResponse.retryCount = this.retryCount;
|
|
720
786
|
this[kIsFromCache] = typedResponse.isFromCache;
|
|
721
787
|
this[kResponseSize] = Number(response.headers['content-length']) || undefined;
|
|
722
788
|
this[kResponse] = response;
|
|
@@ -754,7 +820,7 @@ class Request extends stream_1.Duplex {
|
|
|
754
820
|
}
|
|
755
821
|
if (options.followRedirect && response.headers.location && redirectCodes.has(statusCode)) {
|
|
756
822
|
// We're being redirected, we don't care about the response.
|
|
757
|
-
// It'd be
|
|
823
|
+
// It'd be best to abort the request, but we can't because
|
|
758
824
|
// we would have to sacrifice the TCP connection. We don't want that.
|
|
759
825
|
response.resume();
|
|
760
826
|
if (this[kRequest]) {
|
|
@@ -777,6 +843,8 @@ class Request extends stream_1.Duplex {
|
|
|
777
843
|
if ('form' in options) {
|
|
778
844
|
delete options.form;
|
|
779
845
|
}
|
|
846
|
+
this[kBody] = undefined;
|
|
847
|
+
delete options.headers['content-length'];
|
|
780
848
|
}
|
|
781
849
|
if (this.redirects.length >= options.maxRedirects) {
|
|
782
850
|
this._beforeError(new MaxRedirectsError(this));
|
|
@@ -790,7 +858,7 @@ class Request extends stream_1.Duplex {
|
|
|
790
858
|
const redirectString = redirectUrl.toString();
|
|
791
859
|
decodeURI(redirectString);
|
|
792
860
|
// Redirecting to a different site, clear sensitive data.
|
|
793
|
-
if (redirectUrl.hostname !== url.hostname) {
|
|
861
|
+
if (redirectUrl.hostname !== url.hostname || redirectUrl.port !== url.port) {
|
|
794
862
|
if ('host' in options.headers) {
|
|
795
863
|
delete options.headers.host;
|
|
796
864
|
}
|
|
@@ -801,10 +869,14 @@ class Request extends stream_1.Duplex {
|
|
|
801
869
|
delete options.headers.authorization;
|
|
802
870
|
}
|
|
803
871
|
if (options.username || options.password) {
|
|
804
|
-
|
|
805
|
-
|
|
872
|
+
options.username = '';
|
|
873
|
+
options.password = '';
|
|
806
874
|
}
|
|
807
875
|
}
|
|
876
|
+
else {
|
|
877
|
+
redirectUrl.username = options.username;
|
|
878
|
+
redirectUrl.password = options.password;
|
|
879
|
+
}
|
|
808
880
|
this.redirects.push(redirectString);
|
|
809
881
|
options.url = redirectUrl;
|
|
810
882
|
for (const hook of options.hooks.beforeRedirect) {
|
|
@@ -820,16 +892,9 @@ class Request extends stream_1.Duplex {
|
|
|
820
892
|
}
|
|
821
893
|
return;
|
|
822
894
|
}
|
|
823
|
-
|
|
824
|
-
const isOk = (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304;
|
|
825
|
-
if (options.throwHttpErrors && !isOk) {
|
|
826
|
-
// Normally we would have to use `void [await] this._beforeError(error)` everywhere,
|
|
827
|
-
// but since there's `void (async () => { ... })()` inside of it, we don't have to.
|
|
895
|
+
if (options.isStream && options.throwHttpErrors && !is_response_ok_1.isResponseOk(typedResponse)) {
|
|
828
896
|
this._beforeError(new HTTPError(typedResponse));
|
|
829
|
-
|
|
830
|
-
if (this[kStopReading]) {
|
|
831
|
-
return;
|
|
832
|
-
}
|
|
897
|
+
return;
|
|
833
898
|
}
|
|
834
899
|
response.on('readable', () => {
|
|
835
900
|
if (this[kTriggerRead]) {
|
|
@@ -866,6 +931,7 @@ class Request extends stream_1.Duplex {
|
|
|
866
931
|
await this._onResponseBase(response);
|
|
867
932
|
}
|
|
868
933
|
catch (error) {
|
|
934
|
+
/* istanbul ignore next: better safe than sorry */
|
|
869
935
|
this._beforeError(error);
|
|
870
936
|
}
|
|
871
937
|
}
|
|
@@ -903,9 +969,6 @@ class Request extends stream_1.Duplex {
|
|
|
903
969
|
body.once('error', (error) => {
|
|
904
970
|
this._beforeError(new UploadError(error, this));
|
|
905
971
|
});
|
|
906
|
-
body.once('end', () => {
|
|
907
|
-
delete options.body;
|
|
908
|
-
});
|
|
909
972
|
}
|
|
910
973
|
else {
|
|
911
974
|
this._unlockWrite();
|
|
@@ -926,6 +989,8 @@ class Request extends stream_1.Duplex {
|
|
|
926
989
|
// TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed
|
|
927
990
|
Object.assign(options, url_to_options_1.default(url));
|
|
928
991
|
// `http-cache-semantics` checks this
|
|
992
|
+
// TODO: Fix this ignore.
|
|
993
|
+
// @ts-expect-error
|
|
929
994
|
delete options.url;
|
|
930
995
|
let request;
|
|
931
996
|
// This is ugly
|
|
@@ -947,7 +1012,7 @@ class Request extends stream_1.Duplex {
|
|
|
947
1012
|
});
|
|
948
1013
|
}
|
|
949
1014
|
async _makeRequest() {
|
|
950
|
-
var _a;
|
|
1015
|
+
var _a, _b, _c, _d, _e;
|
|
951
1016
|
const { options } = this;
|
|
952
1017
|
const { headers } = options;
|
|
953
1018
|
for (const key in headers) {
|
|
@@ -978,6 +1043,9 @@ class Request extends stream_1.Duplex {
|
|
|
978
1043
|
break;
|
|
979
1044
|
}
|
|
980
1045
|
}
|
|
1046
|
+
if (options.body && this[kBody] !== options.body) {
|
|
1047
|
+
this[kBody] = options.body;
|
|
1048
|
+
}
|
|
981
1049
|
const { agent, request, timeout, url } = options;
|
|
982
1050
|
if (options.dnsCache && !('lookup' in options)) {
|
|
983
1051
|
options.lookup = options.dnsCache.lookup;
|
|
@@ -1013,14 +1081,20 @@ class Request extends stream_1.Duplex {
|
|
|
1013
1081
|
// Prepare plain HTTP request options
|
|
1014
1082
|
options[kRequest] = realFn;
|
|
1015
1083
|
delete options.request;
|
|
1084
|
+
// TODO: Fix this ignore.
|
|
1085
|
+
// @ts-expect-error
|
|
1016
1086
|
delete options.timeout;
|
|
1017
1087
|
const requestOptions = options;
|
|
1088
|
+
requestOptions.shared = (_b = options.cacheOptions) === null || _b === void 0 ? void 0 : _b.shared;
|
|
1089
|
+
requestOptions.cacheHeuristic = (_c = options.cacheOptions) === null || _c === void 0 ? void 0 : _c.cacheHeuristic;
|
|
1090
|
+
requestOptions.immutableMinTimeToLive = (_d = options.cacheOptions) === null || _d === void 0 ? void 0 : _d.immutableMinTimeToLive;
|
|
1091
|
+
requestOptions.ignoreCargoCult = (_e = options.cacheOptions) === null || _e === void 0 ? void 0 : _e.ignoreCargoCult;
|
|
1018
1092
|
// If `dnsLookupIpVersion` is not present do not override `family`
|
|
1019
1093
|
if (options.dnsLookupIpVersion !== undefined) {
|
|
1020
1094
|
try {
|
|
1021
1095
|
requestOptions.family = dns_ip_version_1.dnsLookupIpVersionToFamily(options.dnsLookupIpVersion);
|
|
1022
1096
|
}
|
|
1023
|
-
catch (
|
|
1097
|
+
catch (_f) {
|
|
1024
1098
|
throw new Error('Invalid `dnsLookupIpVersion` option value');
|
|
1025
1099
|
}
|
|
1026
1100
|
}
|
|
@@ -1044,6 +1118,9 @@ class Request extends stream_1.Duplex {
|
|
|
1044
1118
|
if (options.https.passphrase) {
|
|
1045
1119
|
requestOptions.passphrase = options.https.passphrase;
|
|
1046
1120
|
}
|
|
1121
|
+
if (options.https.pfx) {
|
|
1122
|
+
requestOptions.pfx = options.https.pfx;
|
|
1123
|
+
}
|
|
1047
1124
|
}
|
|
1048
1125
|
try {
|
|
1049
1126
|
let requestOrResponse = await fn(url, requestOptions);
|
|
@@ -1060,6 +1137,7 @@ class Request extends stream_1.Duplex {
|
|
|
1060
1137
|
delete requestOptions.rejectUnauthorized;
|
|
1061
1138
|
}
|
|
1062
1139
|
if (options.https.checkServerIdentity) {
|
|
1140
|
+
// @ts-expect-error - This one will be removed when we remove the alias.
|
|
1063
1141
|
delete requestOptions.checkServerIdentity;
|
|
1064
1142
|
}
|
|
1065
1143
|
if (options.https.certificateAuthority) {
|
|
@@ -1074,6 +1152,9 @@ class Request extends stream_1.Duplex {
|
|
|
1074
1152
|
if (options.https.passphrase) {
|
|
1075
1153
|
delete requestOptions.passphrase;
|
|
1076
1154
|
}
|
|
1155
|
+
if (options.https.pfx) {
|
|
1156
|
+
delete requestOptions.pfx;
|
|
1157
|
+
}
|
|
1077
1158
|
}
|
|
1078
1159
|
if (isClientRequest(requestOrResponse)) {
|
|
1079
1160
|
this._onRequest(requestOrResponse);
|
|
@@ -1098,34 +1179,97 @@ class Request extends stream_1.Duplex {
|
|
|
1098
1179
|
throw new RequestError(error.message, error, this);
|
|
1099
1180
|
}
|
|
1100
1181
|
}
|
|
1182
|
+
async _error(error) {
|
|
1183
|
+
try {
|
|
1184
|
+
for (const hook of this.options.hooks.beforeError) {
|
|
1185
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1186
|
+
error = await hook(error);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
catch (error_) {
|
|
1190
|
+
error = new RequestError(error_.message, error_, this);
|
|
1191
|
+
}
|
|
1192
|
+
this.destroy(error);
|
|
1193
|
+
}
|
|
1101
1194
|
_beforeError(error) {
|
|
1102
|
-
if (this
|
|
1195
|
+
if (this[kStopReading]) {
|
|
1103
1196
|
return;
|
|
1104
1197
|
}
|
|
1198
|
+
const { options } = this;
|
|
1199
|
+
const retryCount = this.retryCount + 1;
|
|
1105
1200
|
this[kStopReading] = true;
|
|
1106
1201
|
if (!(error instanceof RequestError)) {
|
|
1107
1202
|
error = new RequestError(error.message, error, this);
|
|
1108
1203
|
}
|
|
1204
|
+
const typedError = error;
|
|
1205
|
+
const { response } = typedError;
|
|
1109
1206
|
void (async () => {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
response.setEncoding(this._readableState.encoding);
|
|
1207
|
+
if (response && !response.body) {
|
|
1208
|
+
response.setEncoding(this._readableState.encoding);
|
|
1209
|
+
try {
|
|
1114
1210
|
response.rawBody = await get_buffer_1.default(response);
|
|
1115
1211
|
response.body = response.rawBody.toString();
|
|
1116
1212
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1213
|
+
catch (_a) { }
|
|
1214
|
+
}
|
|
1215
|
+
if (this.listenerCount('retry') !== 0) {
|
|
1216
|
+
let backoff;
|
|
1217
|
+
try {
|
|
1218
|
+
let retryAfter;
|
|
1219
|
+
if (response && 'retry-after' in response.headers) {
|
|
1220
|
+
retryAfter = Number(response.headers['retry-after']);
|
|
1221
|
+
if (Number.isNaN(retryAfter)) {
|
|
1222
|
+
retryAfter = Date.parse(response.headers['retry-after']) - Date.now();
|
|
1223
|
+
if (retryAfter <= 0) {
|
|
1224
|
+
retryAfter = 1;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
else {
|
|
1228
|
+
retryAfter *= 1000;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
backoff = await options.retry.calculateDelay({
|
|
1232
|
+
attemptCount: retryCount,
|
|
1233
|
+
retryOptions: options.retry,
|
|
1234
|
+
error: typedError,
|
|
1235
|
+
retryAfter,
|
|
1236
|
+
computedValue: calculate_retry_delay_1.default({
|
|
1237
|
+
attemptCount: retryCount,
|
|
1238
|
+
retryOptions: options.retry,
|
|
1239
|
+
error: typedError,
|
|
1240
|
+
retryAfter,
|
|
1241
|
+
computedValue: 0
|
|
1242
|
+
})
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
catch (error_) {
|
|
1246
|
+
void this._error(new RequestError(error_.message, error_, this));
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
if (backoff) {
|
|
1250
|
+
const retry = async () => {
|
|
1251
|
+
try {
|
|
1252
|
+
for (const hook of this.options.hooks.beforeRetry) {
|
|
1253
|
+
// eslint-disable-next-line no-await-in-loop
|
|
1254
|
+
await hook(this.options, typedError, retryCount);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
catch (error_) {
|
|
1258
|
+
void this._error(new RequestError(error_.message, error, this));
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
// Something forced us to abort the retry
|
|
1262
|
+
if (this.destroyed) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
this.destroy();
|
|
1266
|
+
this.emit('retry', retryCount, error);
|
|
1267
|
+
};
|
|
1268
|
+
this[kRetryTimeout] = setTimeout(retry, backoff);
|
|
1269
|
+
return;
|
|
1123
1270
|
}
|
|
1124
1271
|
}
|
|
1125
|
-
|
|
1126
|
-
error = new RequestError(error_.message, error_, this);
|
|
1127
|
-
}
|
|
1128
|
-
this.destroy(error);
|
|
1272
|
+
void this._error(typedError);
|
|
1129
1273
|
})();
|
|
1130
1274
|
}
|
|
1131
1275
|
_read() {
|
|
@@ -1162,6 +1306,10 @@ class Request extends stream_1.Duplex {
|
|
|
1162
1306
|
}
|
|
1163
1307
|
}
|
|
1164
1308
|
_writeRequest(chunk, encoding, callback) {
|
|
1309
|
+
if (this[kRequest].destroyed) {
|
|
1310
|
+
// Probably the `ClientRequest` instance will throw
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1165
1313
|
this._progressCallbacks.push(() => {
|
|
1166
1314
|
this[kUploadedSize] += Buffer.byteLength(chunk, encoding);
|
|
1167
1315
|
const progress = this.uploadProgress;
|
|
@@ -1212,6 +1360,8 @@ class Request extends stream_1.Duplex {
|
|
|
1212
1360
|
_destroy(error, callback) {
|
|
1213
1361
|
var _a;
|
|
1214
1362
|
this[kStopReading] = true;
|
|
1363
|
+
// Prevent further retries
|
|
1364
|
+
clearTimeout(this[kRetryTimeout]);
|
|
1215
1365
|
if (kRequest in this) {
|
|
1216
1366
|
this[kCancelTimeouts]();
|
|
1217
1367
|
// TODO: Remove the next `if` when these get fixed:
|
|
@@ -1225,10 +1375,19 @@ class Request extends stream_1.Duplex {
|
|
|
1225
1375
|
}
|
|
1226
1376
|
callback(error);
|
|
1227
1377
|
}
|
|
1378
|
+
get _isAboutToError() {
|
|
1379
|
+
return this[kStopReading];
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
The remote IP address.
|
|
1383
|
+
*/
|
|
1228
1384
|
get ip() {
|
|
1229
1385
|
var _a;
|
|
1230
1386
|
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket.remoteAddress;
|
|
1231
1387
|
}
|
|
1388
|
+
/**
|
|
1389
|
+
Indicates whether the request has been aborted or not.
|
|
1390
|
+
*/
|
|
1232
1391
|
get aborted() {
|
|
1233
1392
|
var _a, _b, _c;
|
|
1234
1393
|
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);
|
|
@@ -1237,6 +1396,9 @@ class Request extends stream_1.Duplex {
|
|
|
1237
1396
|
var _a;
|
|
1238
1397
|
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket;
|
|
1239
1398
|
}
|
|
1399
|
+
/**
|
|
1400
|
+
Progress event for downloading (receiving a response).
|
|
1401
|
+
*/
|
|
1240
1402
|
get downloadProgress() {
|
|
1241
1403
|
let percent;
|
|
1242
1404
|
if (this[kResponseSize]) {
|
|
@@ -1254,6 +1416,9 @@ class Request extends stream_1.Duplex {
|
|
|
1254
1416
|
total: this[kResponseSize]
|
|
1255
1417
|
};
|
|
1256
1418
|
}
|
|
1419
|
+
/**
|
|
1420
|
+
Progress event for uploading (sending a request).
|
|
1421
|
+
*/
|
|
1257
1422
|
get uploadProgress() {
|
|
1258
1423
|
let percent;
|
|
1259
1424
|
if (this[kBodySize]) {
|
|
@@ -1271,16 +1436,43 @@ class Request extends stream_1.Duplex {
|
|
|
1271
1436
|
total: this[kBodySize]
|
|
1272
1437
|
};
|
|
1273
1438
|
}
|
|
1439
|
+
/**
|
|
1440
|
+
The object contains the following properties:
|
|
1441
|
+
|
|
1442
|
+
- `start` - Time when the request started.
|
|
1443
|
+
- `socket` - Time when a socket was assigned to the request.
|
|
1444
|
+
- `lookup` - Time when the DNS lookup finished.
|
|
1445
|
+
- `connect` - Time when the socket successfully connected.
|
|
1446
|
+
- `secureConnect` - Time when the socket securely connected.
|
|
1447
|
+
- `upload` - Time when the request finished uploading.
|
|
1448
|
+
- `response` - Time when the request fired `response` event.
|
|
1449
|
+
- `end` - Time when the response fired `end` event.
|
|
1450
|
+
- `error` - Time when the request fired `error` event.
|
|
1451
|
+
- `abort` - Time when the request fired `abort` event.
|
|
1452
|
+
- `phases`
|
|
1453
|
+
- `wait` - `timings.socket - timings.start`
|
|
1454
|
+
- `dns` - `timings.lookup - timings.socket`
|
|
1455
|
+
- `tcp` - `timings.connect - timings.lookup`
|
|
1456
|
+
- `tls` - `timings.secureConnect - timings.connect`
|
|
1457
|
+
- `request` - `timings.upload - (timings.secureConnect || timings.connect)`
|
|
1458
|
+
- `firstByte` - `timings.response - timings.upload`
|
|
1459
|
+
- `download` - `timings.end - timings.response`
|
|
1460
|
+
- `total` - `(timings.end || timings.error || timings.abort) - timings.start`
|
|
1461
|
+
|
|
1462
|
+
If something has not been measured yet, it will be `undefined`.
|
|
1463
|
+
|
|
1464
|
+
__Note__: The time is a `number` representing the milliseconds elapsed since the UNIX epoch.
|
|
1465
|
+
*/
|
|
1274
1466
|
get timings() {
|
|
1275
1467
|
var _a;
|
|
1276
1468
|
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.timings;
|
|
1277
1469
|
}
|
|
1470
|
+
/**
|
|
1471
|
+
Whether the response was retrieved from the cache.
|
|
1472
|
+
*/
|
|
1278
1473
|
get isFromCache() {
|
|
1279
1474
|
return this[kIsFromCache];
|
|
1280
1475
|
}
|
|
1281
|
-
get _response() {
|
|
1282
|
-
return this[kResponse];
|
|
1283
|
-
}
|
|
1284
1476
|
pipe(destination, options) {
|
|
1285
1477
|
if (this[kStartedReading]) {
|
|
1286
1478
|
throw new Error('Failed to pipe. The response has been emitted already.');
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isResponseOk = void 0;
|
|
4
|
+
exports.isResponseOk = (response) => {
|
|
5
|
+
const { statusCode } = response;
|
|
6
|
+
const limitStatusCode = response.request.options.followRedirect ? 299 : 399;
|
|
7
|
+
return (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304;
|
|
8
|
+
};
|