got 10.5.7 → 11.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/dist/source/{calculate-retry-delay.d.ts → as-promise/calculate-retry-delay.d.ts} +0 -0
- package/dist/source/{calculate-retry-delay.js → as-promise/calculate-retry-delay.js} +6 -7
- package/dist/source/as-promise/core.d.ts +13 -0
- package/dist/source/as-promise/core.js +124 -0
- package/dist/source/as-promise/create-rejection.d.ts +2 -0
- package/dist/source/as-promise/create-rejection.js +30 -0
- package/dist/source/as-promise/index.d.ts +5 -0
- package/dist/source/as-promise/index.js +203 -0
- package/dist/source/as-promise/types.d.ts +77 -0
- package/dist/source/as-promise/types.js +27 -0
- package/dist/source/core/index.d.ts +272 -0
- package/dist/source/core/index.js +1091 -0
- package/dist/source/{utils → core/utils}/get-body-size.d.ts +0 -0
- package/dist/source/{utils → core/utils}/get-body-size.js +0 -0
- package/dist/source/core/utils/is-form-data.d.ts +8 -0
- package/dist/source/{utils → core/utils}/is-form-data.js +0 -0
- package/dist/source/core/utils/options-to-url.d.ts +15 -0
- package/dist/source/{utils → core/utils}/options-to-url.js +5 -34
- package/dist/source/core/utils/proxy-events.d.ts +3 -0
- package/dist/source/core/utils/proxy-events.js +17 -0
- package/dist/source/{utils → core/utils}/timed-out.d.ts +0 -0
- package/dist/source/{utils → core/utils}/timed-out.js +2 -5
- package/dist/source/{utils → core/utils}/unhandle.d.ts +1 -1
- package/dist/source/{utils → core/utils}/unhandle.js +0 -0
- package/dist/source/{utils → core/utils}/url-to-options.d.ts +0 -0
- package/dist/source/{utils → core/utils}/url-to-options.js +0 -0
- package/dist/source/core/utils/weakable-map.d.ts +8 -0
- package/dist/source/core/utils/weakable-map.js +29 -0
- package/dist/source/create.d.ts +3 -79
- package/dist/source/create.js +83 -39
- package/dist/source/index.d.ts +3 -6
- package/dist/source/index.js +13 -19
- package/dist/source/types.d.ts +84 -198
- package/dist/source/types.js +0 -1
- package/package.json +41 -37
- package/readme.md +211 -97
- package/dist/source/as-promise.d.ts +0 -3
- package/dist/source/as-promise.js +0 -152
- package/dist/source/as-stream.d.ts +0 -7
- package/dist/source/as-stream.js +0 -121
- package/dist/source/errors.d.ts +0 -41
- package/dist/source/errors.js +0 -103
- package/dist/source/get-response.d.ts +0 -6
- package/dist/source/get-response.js +0 -25
- package/dist/source/known-hook-events.d.ts +0 -88
- package/dist/source/known-hook-events.js +0 -11
- package/dist/source/normalize-arguments.d.ts +0 -19
- package/dist/source/normalize-arguments.js +0 -436
- package/dist/source/progress.d.ts +0 -4
- package/dist/source/progress.js +0 -40
- package/dist/source/request-as-event-emitter.d.ts +0 -12
- package/dist/source/request-as-event-emitter.js +0 -284
- package/dist/source/utils/dynamic-require.d.ts +0 -3
- package/dist/source/utils/dynamic-require.js +0 -4
- package/dist/source/utils/is-form-data.d.ts +0 -3
- package/dist/source/utils/merge.d.ts +0 -6
- package/dist/source/utils/merge.js +0 -35
- package/dist/source/utils/options-to-url.d.ts +0 -19
- package/dist/source/utils/supports-brotli.d.ts +0 -2
- package/dist/source/utils/supports-brotli.js +0 -4
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const util_1 = require("util");
|
|
4
|
+
const stream_1 = require("stream");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const url_1 = require("url");
|
|
7
|
+
const http = require("http");
|
|
8
|
+
const http_1 = require("http");
|
|
9
|
+
const https = require("https");
|
|
10
|
+
const http_timer_1 = require("@szmarczak/http-timer");
|
|
11
|
+
const decompressResponse = require("decompress-response");
|
|
12
|
+
const cacheable_lookup_1 = require("cacheable-lookup");
|
|
13
|
+
const CacheableRequest = require("cacheable-request");
|
|
14
|
+
// @ts-ignore Missing types
|
|
15
|
+
const http2wrapper = require("http2-wrapper");
|
|
16
|
+
const lowercaseKeys = require("lowercase-keys");
|
|
17
|
+
const getStream = require("get-stream");
|
|
18
|
+
const is_1 = require("@sindresorhus/is");
|
|
19
|
+
const get_body_size_1 = require("./utils/get-body-size");
|
|
20
|
+
const is_form_data_1 = require("./utils/is-form-data");
|
|
21
|
+
const proxy_events_1 = require("./utils/proxy-events");
|
|
22
|
+
const timed_out_1 = require("./utils/timed-out");
|
|
23
|
+
const url_to_options_1 = require("./utils/url-to-options");
|
|
24
|
+
const options_to_url_1 = require("./utils/options-to-url");
|
|
25
|
+
const weakable_map_1 = require("./utils/weakable-map");
|
|
26
|
+
const kRequest = Symbol('request');
|
|
27
|
+
const kResponse = Symbol('response');
|
|
28
|
+
const kResponseSize = Symbol('responseSize');
|
|
29
|
+
const kDownloadedSize = Symbol('downloadedSize');
|
|
30
|
+
const kBodySize = Symbol('bodySize');
|
|
31
|
+
const kUploadedSize = Symbol('uploadedSize');
|
|
32
|
+
const kServerResponsesPiped = Symbol('serverResponsesPiped');
|
|
33
|
+
const kUnproxyEvents = Symbol('unproxyEvents');
|
|
34
|
+
const kIsFromCache = Symbol('isFromCache');
|
|
35
|
+
const kCancelTimeouts = Symbol('cancelTimeouts');
|
|
36
|
+
const kStartedReading = Symbol('startedReading');
|
|
37
|
+
const kStopReading = Symbol('stopReading');
|
|
38
|
+
const kTriggerRead = Symbol('triggerRead');
|
|
39
|
+
exports.kIsNormalizedAlready = Symbol('isNormalizedAlready');
|
|
40
|
+
const supportsBrotli = is_1.default.string(process.versions.brotli);
|
|
41
|
+
exports.withoutBody = new Set(['GET', 'HEAD']);
|
|
42
|
+
exports.knownHookEvents = ['init', 'beforeRequest', 'beforeRedirect', 'beforeError'];
|
|
43
|
+
function validateSearchParameters(searchParameters) {
|
|
44
|
+
// eslint-disable-next-line guard-for-in
|
|
45
|
+
for (const key in searchParameters) {
|
|
46
|
+
const value = searchParameters[key];
|
|
47
|
+
if (!is_1.default.string(value) && !is_1.default.number(value) && !is_1.default.boolean(value) && !is_1.default.null_(value)) {
|
|
48
|
+
throw new TypeError(`The \`searchParams\` value '${String(value)}' must be a string, number, boolean or null`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function isClientRequest(clientRequest) {
|
|
53
|
+
return is_1.default.object(clientRequest) && !('statusCode' in clientRequest);
|
|
54
|
+
}
|
|
55
|
+
const cacheableStore = new weakable_map_1.default();
|
|
56
|
+
const waitForOpenFile = async (file) => new Promise((resolve, reject) => {
|
|
57
|
+
const onError = (error) => {
|
|
58
|
+
reject(error);
|
|
59
|
+
};
|
|
60
|
+
file.once('error', onError);
|
|
61
|
+
file.once('open', () => {
|
|
62
|
+
file.off('error', onError);
|
|
63
|
+
resolve();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
const redirectCodes = new Set([300, 301, 302, 303, 304, 307, 308]);
|
|
67
|
+
const nonEnumerableProperties = [
|
|
68
|
+
'context',
|
|
69
|
+
'body',
|
|
70
|
+
'json',
|
|
71
|
+
'form'
|
|
72
|
+
];
|
|
73
|
+
const setNonEnumerableProperties = (sources, to) => {
|
|
74
|
+
// Non enumerable properties shall not be merged
|
|
75
|
+
const properties = {};
|
|
76
|
+
for (const source of sources) {
|
|
77
|
+
if (!source) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
for (const name of nonEnumerableProperties) {
|
|
81
|
+
if (!(name in source)) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
properties[name] = {
|
|
85
|
+
writable: true,
|
|
86
|
+
configurable: true,
|
|
87
|
+
enumerable: false,
|
|
88
|
+
// @ts-ignore TS doesn't see the check above
|
|
89
|
+
value: source[name]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
Object.defineProperties(to, properties);
|
|
94
|
+
};
|
|
95
|
+
class RequestError extends Error {
|
|
96
|
+
constructor(message, error, self) {
|
|
97
|
+
var _a;
|
|
98
|
+
super(message);
|
|
99
|
+
Error.captureStackTrace(this, this.constructor);
|
|
100
|
+
this.name = 'RequestError';
|
|
101
|
+
this.code = error.code;
|
|
102
|
+
if (self instanceof Request) {
|
|
103
|
+
Object.defineProperty(this, 'request', {
|
|
104
|
+
enumerable: false,
|
|
105
|
+
value: self
|
|
106
|
+
});
|
|
107
|
+
Object.defineProperty(this, 'response', {
|
|
108
|
+
enumerable: false,
|
|
109
|
+
value: self[kResponse]
|
|
110
|
+
});
|
|
111
|
+
Object.defineProperty(this, 'options', {
|
|
112
|
+
// This fails because of TS 3.7.2 useDefineForClassFields
|
|
113
|
+
// Ref: https://github.com/microsoft/TypeScript/issues/34972
|
|
114
|
+
enumerable: false,
|
|
115
|
+
value: self.options
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
Object.defineProperty(this, 'options', {
|
|
120
|
+
// This fails because of TS 3.7.2 useDefineForClassFields
|
|
121
|
+
// Ref: https://github.com/microsoft/TypeScript/issues/34972
|
|
122
|
+
enumerable: false,
|
|
123
|
+
value: self
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
this.timings = (_a = this.request) === null || _a === void 0 ? void 0 : _a.timings;
|
|
127
|
+
// Recover the original stacktrace
|
|
128
|
+
if (!is_1.default.undefined(error.stack)) {
|
|
129
|
+
const indexOfMessage = this.stack.indexOf(this.message) + this.message.length;
|
|
130
|
+
const thisStackTrace = this.stack.slice(indexOfMessage).split('\n').reverse();
|
|
131
|
+
const errorStackTrace = error.stack.slice(error.stack.indexOf(error.message) + error.message.length).split('\n').reverse();
|
|
132
|
+
// Remove duplicated traces
|
|
133
|
+
while (errorStackTrace.length !== 0 && errorStackTrace[0] === thisStackTrace[0]) {
|
|
134
|
+
thisStackTrace.shift();
|
|
135
|
+
}
|
|
136
|
+
this.stack = `${this.stack.slice(0, indexOfMessage)}${thisStackTrace.reverse().join('\n')}${errorStackTrace.reverse().join('\n')}`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.RequestError = RequestError;
|
|
141
|
+
class MaxRedirectsError extends RequestError {
|
|
142
|
+
constructor(request) {
|
|
143
|
+
super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request);
|
|
144
|
+
this.name = 'MaxRedirectsError';
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.MaxRedirectsError = MaxRedirectsError;
|
|
148
|
+
class HTTPError extends RequestError {
|
|
149
|
+
constructor(response) {
|
|
150
|
+
super(`Response code ${response.statusCode} (${response.statusMessage})`, {}, response.request);
|
|
151
|
+
this.name = 'HTTPError';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.HTTPError = HTTPError;
|
|
155
|
+
class CacheError extends RequestError {
|
|
156
|
+
constructor(error, request) {
|
|
157
|
+
super(error.message, error, request);
|
|
158
|
+
this.name = 'CacheError';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.CacheError = CacheError;
|
|
162
|
+
class UploadError extends RequestError {
|
|
163
|
+
constructor(error, request) {
|
|
164
|
+
super(error.message, error, request);
|
|
165
|
+
this.name = 'UploadError';
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.UploadError = UploadError;
|
|
169
|
+
class TimeoutError extends RequestError {
|
|
170
|
+
constructor(error, timings, request) {
|
|
171
|
+
super(error.message, error, request);
|
|
172
|
+
this.name = 'TimeoutError';
|
|
173
|
+
this.event = error.event;
|
|
174
|
+
this.timings = timings;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.TimeoutError = TimeoutError;
|
|
178
|
+
class ReadError extends RequestError {
|
|
179
|
+
constructor(error, request) {
|
|
180
|
+
super(error.message, error, request);
|
|
181
|
+
this.name = 'ReadError';
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.ReadError = ReadError;
|
|
185
|
+
class UnsupportedProtocolError extends RequestError {
|
|
186
|
+
constructor(options) {
|
|
187
|
+
super(`Unsupported protocol "${options.url.protocol}"`, {}, options);
|
|
188
|
+
this.name = 'UnsupportedProtocolError';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.UnsupportedProtocolError = UnsupportedProtocolError;
|
|
192
|
+
const proxiedRequestEvents = [
|
|
193
|
+
'socket',
|
|
194
|
+
'abort',
|
|
195
|
+
'connect',
|
|
196
|
+
'continue',
|
|
197
|
+
'information',
|
|
198
|
+
'upgrade',
|
|
199
|
+
'timeout'
|
|
200
|
+
];
|
|
201
|
+
class Request extends stream_1.Duplex {
|
|
202
|
+
constructor(url, options = {}, defaults) {
|
|
203
|
+
super({
|
|
204
|
+
// It needs to be zero because we're just proxying the data to another stream
|
|
205
|
+
highWaterMark: 0
|
|
206
|
+
});
|
|
207
|
+
this[kDownloadedSize] = 0;
|
|
208
|
+
this[kUploadedSize] = 0;
|
|
209
|
+
this.finalized = false;
|
|
210
|
+
this[kServerResponsesPiped] = new Set();
|
|
211
|
+
this.redirects = [];
|
|
212
|
+
this[kStopReading] = false;
|
|
213
|
+
this[kTriggerRead] = false;
|
|
214
|
+
// TODO: Remove this when targeting Node.js >= 12
|
|
215
|
+
this._progressCallbacks = [];
|
|
216
|
+
const unlockWrite = () => this._unlockWrite();
|
|
217
|
+
const lockWrite = () => this._lockWrite();
|
|
218
|
+
this.on('pipe', (source) => {
|
|
219
|
+
source.prependListener('data', unlockWrite);
|
|
220
|
+
source.on('data', lockWrite);
|
|
221
|
+
source.prependListener('end', unlockWrite);
|
|
222
|
+
source.on('end', lockWrite);
|
|
223
|
+
});
|
|
224
|
+
this.on('unpipe', (source) => {
|
|
225
|
+
source.off('data', unlockWrite);
|
|
226
|
+
source.off('data', lockWrite);
|
|
227
|
+
source.off('end', unlockWrite);
|
|
228
|
+
source.off('end', lockWrite);
|
|
229
|
+
});
|
|
230
|
+
this.on('pipe', source => {
|
|
231
|
+
if (source instanceof http_1.IncomingMessage) {
|
|
232
|
+
this.options.headers = {
|
|
233
|
+
...source.headers,
|
|
234
|
+
...this.options.headers
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
const { json, body, form } = options;
|
|
239
|
+
if (json || body || form) {
|
|
240
|
+
this._lockWrite();
|
|
241
|
+
}
|
|
242
|
+
(async (nonNormalizedOptions) => {
|
|
243
|
+
try {
|
|
244
|
+
if (nonNormalizedOptions.body instanceof fs_1.ReadStream) {
|
|
245
|
+
await waitForOpenFile(nonNormalizedOptions.body);
|
|
246
|
+
}
|
|
247
|
+
if (exports.kIsNormalizedAlready in nonNormalizedOptions) {
|
|
248
|
+
this.options = nonNormalizedOptions;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// @ts-ignore Common TypeScript bug saying that `this.constructor` is not accessible
|
|
252
|
+
this.options = this.constructor.normalizeArguments(url, nonNormalizedOptions, defaults);
|
|
253
|
+
}
|
|
254
|
+
const { url: normalizedURL } = this.options;
|
|
255
|
+
if (!normalizedURL) {
|
|
256
|
+
throw new TypeError('Missing `url` property');
|
|
257
|
+
}
|
|
258
|
+
this.requestUrl = normalizedURL.toString();
|
|
259
|
+
decodeURI(this.requestUrl);
|
|
260
|
+
await this._finalizeBody();
|
|
261
|
+
await this._makeRequest();
|
|
262
|
+
this.finalized = true;
|
|
263
|
+
this.emit('finalized');
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
if (error instanceof RequestError) {
|
|
267
|
+
this._beforeError(error);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.destroy(error);
|
|
271
|
+
}
|
|
272
|
+
})(options);
|
|
273
|
+
}
|
|
274
|
+
static normalizeArguments(url, options, defaults) {
|
|
275
|
+
var _a, _b, _c, _d;
|
|
276
|
+
const rawOptions = options;
|
|
277
|
+
if (is_1.default.object(url) && !is_1.default.urlInstance(url)) {
|
|
278
|
+
options = { ...defaults, ...url, ...options };
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
if (url && options && options.url) {
|
|
282
|
+
throw new TypeError('The `url` option is mutually exclusive with the `input` argument');
|
|
283
|
+
}
|
|
284
|
+
options = { ...defaults, ...options };
|
|
285
|
+
if (url) {
|
|
286
|
+
options.url = url;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (rawOptions && defaults) {
|
|
290
|
+
for (const key in rawOptions) {
|
|
291
|
+
// @ts-ignore Dear TypeScript, all object keys are strings (or symbols which are NOT enumerable).
|
|
292
|
+
if (is_1.default.undefined(rawOptions[key]) && !is_1.default.undefined(defaults[key])) {
|
|
293
|
+
// @ts-ignore See the note above
|
|
294
|
+
options[key] = defaults[key];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// TODO: Deprecate URL options in Got 12.
|
|
299
|
+
// Support extend-specific options
|
|
300
|
+
if (options.cache === false) {
|
|
301
|
+
options.cache = undefined;
|
|
302
|
+
}
|
|
303
|
+
// Nice type assertions
|
|
304
|
+
is_1.assert.any([is_1.default.string, is_1.default.undefined], options.method);
|
|
305
|
+
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.headers);
|
|
306
|
+
is_1.assert.any([is_1.default.string, is_1.default.urlInstance, is_1.default.undefined], options.prefixUrl);
|
|
307
|
+
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.cookieJar);
|
|
308
|
+
is_1.assert.any([is_1.default.object, is_1.default.string, is_1.default.undefined], options.searchParams);
|
|
309
|
+
is_1.assert.any([is_1.default.object, is_1.default.string, is_1.default.undefined], options.cache);
|
|
310
|
+
is_1.assert.any([is_1.default.object, is_1.default.number, is_1.default.undefined], options.timeout);
|
|
311
|
+
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.context);
|
|
312
|
+
is_1.assert.any([is_1.default.object, is_1.default.undefined], options.hooks);
|
|
313
|
+
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.decompress);
|
|
314
|
+
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.ignoreInvalidCookies);
|
|
315
|
+
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.followRedirect);
|
|
316
|
+
is_1.assert.any([is_1.default.number, is_1.default.undefined], options.maxRedirects);
|
|
317
|
+
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.throwHttpErrors);
|
|
318
|
+
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.http2);
|
|
319
|
+
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.allowGetBody);
|
|
320
|
+
is_1.assert.any([is_1.default.boolean, is_1.default.undefined], options.rejectUnauthorized);
|
|
321
|
+
// `options.method`
|
|
322
|
+
if (is_1.default.string(options.method)) {
|
|
323
|
+
options.method = options.method.toUpperCase();
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
options.method = 'GET';
|
|
327
|
+
}
|
|
328
|
+
// `options.headers`
|
|
329
|
+
if (is_1.default.undefined(options.headers)) {
|
|
330
|
+
options.headers = {};
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
options.headers = lowercaseKeys({ ...((_a = defaults) === null || _a === void 0 ? void 0 : _a.headers), ...options.headers });
|
|
334
|
+
}
|
|
335
|
+
// Disallow legacy `url.Url`
|
|
336
|
+
if ('slashes' in options) {
|
|
337
|
+
throw new TypeError('The legacy `url.Url` has been deprecated. Use `URL` instead.');
|
|
338
|
+
}
|
|
339
|
+
// `options.auth`
|
|
340
|
+
if ('auth' in options) {
|
|
341
|
+
throw new TypeError('Parameter `auth` is deprecated. Use `username` / `password` instead.');
|
|
342
|
+
}
|
|
343
|
+
// `options.prefixUrl` & `options.url`
|
|
344
|
+
if (options.prefixUrl) {
|
|
345
|
+
options.prefixUrl = options.prefixUrl.toString();
|
|
346
|
+
if (options.prefixUrl !== '' && !options.prefixUrl.endsWith('/')) {
|
|
347
|
+
options.prefixUrl += '/';
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
options.prefixUrl = '';
|
|
352
|
+
}
|
|
353
|
+
if (is_1.default.string(options.url)) {
|
|
354
|
+
if (options.url.startsWith('/')) {
|
|
355
|
+
throw new Error('`input` must not start with a slash when using `prefixUrl`');
|
|
356
|
+
}
|
|
357
|
+
options.url = options_to_url_1.default(options.prefixUrl + options.url, options);
|
|
358
|
+
}
|
|
359
|
+
else if ((is_1.default.undefined(options.url) && options.prefixUrl !== '') || options.protocol) {
|
|
360
|
+
options.url = options_to_url_1.default(options.prefixUrl, options);
|
|
361
|
+
}
|
|
362
|
+
if (options.url) {
|
|
363
|
+
// Make it possible to change `options.prefixUrl`
|
|
364
|
+
let { prefixUrl } = options;
|
|
365
|
+
Object.defineProperty(options, 'prefixUrl', {
|
|
366
|
+
set: (value) => {
|
|
367
|
+
const url = options.url;
|
|
368
|
+
if (!url.href.startsWith(value)) {
|
|
369
|
+
throw new Error(`Cannot change \`prefixUrl\` from ${prefixUrl} to ${value}: ${url.href}`);
|
|
370
|
+
}
|
|
371
|
+
options.url = new url_1.URL(value + url.href.slice(prefixUrl.length));
|
|
372
|
+
prefixUrl = value;
|
|
373
|
+
},
|
|
374
|
+
get: () => prefixUrl
|
|
375
|
+
});
|
|
376
|
+
// Protocol check
|
|
377
|
+
let { protocol } = options.url;
|
|
378
|
+
if (protocol === 'unix:') {
|
|
379
|
+
protocol = 'http:';
|
|
380
|
+
options.url = new url_1.URL(`http://unix${options.url.pathname}${options.url.search}`);
|
|
381
|
+
}
|
|
382
|
+
if (options.url.search) {
|
|
383
|
+
const triggerSearchParameters = '_GOT_INTERNAL_TRIGGER_NORMALIZATION';
|
|
384
|
+
options.url.searchParams.append(triggerSearchParameters, '');
|
|
385
|
+
options.url.searchParams.delete(triggerSearchParameters);
|
|
386
|
+
}
|
|
387
|
+
if (protocol !== 'http:' && protocol !== 'https:') {
|
|
388
|
+
throw new UnsupportedProtocolError(options);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// `options.username` & `options.password`
|
|
392
|
+
options.username = (_b = options.username, (_b !== null && _b !== void 0 ? _b : ''));
|
|
393
|
+
options.password = (_c = options.password, (_c !== null && _c !== void 0 ? _c : ''));
|
|
394
|
+
if (options.url) {
|
|
395
|
+
options.url.username = options.username;
|
|
396
|
+
options.url.password = options.password;
|
|
397
|
+
}
|
|
398
|
+
// `options.cookieJar`
|
|
399
|
+
const { cookieJar } = options;
|
|
400
|
+
if (cookieJar) {
|
|
401
|
+
let { setCookie, getCookieString } = cookieJar;
|
|
402
|
+
is_1.assert.function_(setCookie);
|
|
403
|
+
is_1.assert.function_(getCookieString);
|
|
404
|
+
/* istanbul ignore next: Horrible `tough-cookie` v3 check */
|
|
405
|
+
if (setCookie.length === 4 && getCookieString.length === 0) {
|
|
406
|
+
setCookie = util_1.promisify(setCookie.bind(options.cookieJar));
|
|
407
|
+
getCookieString = util_1.promisify(getCookieString.bind(options.cookieJar));
|
|
408
|
+
options.cookieJar = {
|
|
409
|
+
setCookie: setCookie.bind(cookieJar),
|
|
410
|
+
getCookieString: getCookieString.bind(getCookieString)
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// `options.searchParams`
|
|
415
|
+
if (options.searchParams) {
|
|
416
|
+
if (!is_1.default.string(options.searchParams) && !(options.searchParams instanceof url_1.URLSearchParams)) {
|
|
417
|
+
validateSearchParameters(options.searchParams);
|
|
418
|
+
}
|
|
419
|
+
options.searchParams = new url_1.URLSearchParams(options.searchParams);
|
|
420
|
+
// `normalizeArguments()` is also used to merge options
|
|
421
|
+
const defaultsAsOptions = defaults;
|
|
422
|
+
if (defaultsAsOptions && defaultsAsOptions.searchParams instanceof url_1.URLSearchParams) {
|
|
423
|
+
defaultsAsOptions.searchParams.forEach((value, key) => {
|
|
424
|
+
options.searchParams.append(key, value);
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
if (options.url) {
|
|
428
|
+
options.url.search = options.searchParams.toString();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// `options.cache`
|
|
432
|
+
const { cache } = options;
|
|
433
|
+
if (cache) {
|
|
434
|
+
if (!cacheableStore.has(cache)) {
|
|
435
|
+
cacheableStore.set(cache, new CacheableRequest(((requestOptions, handler) => requestOptions[kRequest](requestOptions, handler)), cache));
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// `options.dnsCache`
|
|
439
|
+
if (options.dnsCache === true) {
|
|
440
|
+
options.dnsCache = new cacheable_lookup_1.default();
|
|
441
|
+
}
|
|
442
|
+
else if (!is_1.default.undefined(options.dnsCache) && options.dnsCache !== false && !(options.dnsCache instanceof cacheable_lookup_1.default)) {
|
|
443
|
+
throw new TypeError(`Parameter \`dnsCache\` must be a CacheableLookup instance or a boolean, got ${is_1.default(options.dnsCache)}`);
|
|
444
|
+
}
|
|
445
|
+
// `options.timeout`
|
|
446
|
+
if (is_1.default.number(options.timeout)) {
|
|
447
|
+
options.timeout = { request: options.timeout };
|
|
448
|
+
}
|
|
449
|
+
else if (defaults) {
|
|
450
|
+
options.timeout = {
|
|
451
|
+
...defaults.timeout,
|
|
452
|
+
...options.timeout
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
options.timeout = { ...options.timeout };
|
|
457
|
+
}
|
|
458
|
+
// `options.context`
|
|
459
|
+
if (!options.context) {
|
|
460
|
+
options.context = {};
|
|
461
|
+
}
|
|
462
|
+
// `options.hooks`
|
|
463
|
+
options.hooks = { ...options.hooks };
|
|
464
|
+
for (const event of exports.knownHookEvents) {
|
|
465
|
+
if (event in options.hooks) {
|
|
466
|
+
if (is_1.default.array(options.hooks[event])) {
|
|
467
|
+
// See https://github.com/microsoft/TypeScript/issues/31445#issuecomment-576929044
|
|
468
|
+
options.hooks[event] = [...options.hooks[event]];
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
throw new TypeError(`Parameter \`${event}\` must be an Array, got ${is_1.default(options.hooks[event])}`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
options.hooks[event] = [];
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (defaults) {
|
|
479
|
+
for (const event of exports.knownHookEvents) {
|
|
480
|
+
const defaultHooks = defaults.hooks[event];
|
|
481
|
+
if (defaultHooks.length !== 0) {
|
|
482
|
+
// See https://github.com/microsoft/TypeScript/issues/31445#issuecomment-576929044
|
|
483
|
+
options.hooks[event] = [
|
|
484
|
+
...defaults.hooks[event],
|
|
485
|
+
...options.hooks[event]
|
|
486
|
+
];
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// Other options
|
|
491
|
+
if ('followRedirects' in options) {
|
|
492
|
+
throw new TypeError('The `followRedirects` option does not exist. Use `followRedirect` instead.');
|
|
493
|
+
}
|
|
494
|
+
if (options.agent) {
|
|
495
|
+
for (const key in options.agent) {
|
|
496
|
+
if (key !== 'http' && key !== 'https' && key !== 'http2') {
|
|
497
|
+
throw new TypeError(`Expected the \`options.agent\` properties to be \`http\`, \`https\` or \`http2\`, got \`${key}\``);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
options.maxRedirects = (_d = options.maxRedirects, (_d !== null && _d !== void 0 ? _d : 0));
|
|
502
|
+
// Set non-enumerable properties
|
|
503
|
+
setNonEnumerableProperties([defaults, options], options);
|
|
504
|
+
return options;
|
|
505
|
+
}
|
|
506
|
+
_lockWrite() {
|
|
507
|
+
const onLockedWrite = () => {
|
|
508
|
+
throw new TypeError('The payload has been already provided');
|
|
509
|
+
};
|
|
510
|
+
this.write = onLockedWrite;
|
|
511
|
+
this.end = onLockedWrite;
|
|
512
|
+
}
|
|
513
|
+
_unlockWrite() {
|
|
514
|
+
this.write = super.write;
|
|
515
|
+
this.end = super.end;
|
|
516
|
+
}
|
|
517
|
+
async _finalizeBody() {
|
|
518
|
+
const { options } = this;
|
|
519
|
+
const { headers } = options;
|
|
520
|
+
const isForm = !is_1.default.undefined(options.form);
|
|
521
|
+
const isJSON = !is_1.default.undefined(options.json);
|
|
522
|
+
const isBody = !is_1.default.undefined(options.body);
|
|
523
|
+
const hasPayload = isForm || isJSON || isBody;
|
|
524
|
+
const cannotHaveBody = exports.withoutBody.has(options.method) && !(options.method === 'GET' && options.allowGetBody);
|
|
525
|
+
this._cannotHaveBody = cannotHaveBody;
|
|
526
|
+
if (hasPayload) {
|
|
527
|
+
if (cannotHaveBody) {
|
|
528
|
+
throw new TypeError(`The \`${options.method}\` method cannot be used with a body`);
|
|
529
|
+
}
|
|
530
|
+
if ([isBody, isForm, isJSON].filter(isTrue => isTrue).length > 1) {
|
|
531
|
+
throw new TypeError('The `body`, `json` and `form` options are mutually exclusive');
|
|
532
|
+
}
|
|
533
|
+
if (isBody &&
|
|
534
|
+
!(options.body instanceof stream_1.Readable) &&
|
|
535
|
+
!is_1.default.string(options.body) &&
|
|
536
|
+
!is_1.default.buffer(options.body) &&
|
|
537
|
+
!is_form_data_1.default(options.body)) {
|
|
538
|
+
throw new TypeError('The `body` option must be a stream.Readable, string or Buffer');
|
|
539
|
+
}
|
|
540
|
+
if (isForm && !is_1.default.object(options.form)) {
|
|
541
|
+
throw new TypeError('The `form` option must be an Object');
|
|
542
|
+
}
|
|
543
|
+
{
|
|
544
|
+
// Serialize body
|
|
545
|
+
const noContentType = !is_1.default.string(headers['content-type']);
|
|
546
|
+
if (isBody) {
|
|
547
|
+
// Special case for https://github.com/form-data/form-data
|
|
548
|
+
if (is_form_data_1.default(options.body) && noContentType) {
|
|
549
|
+
headers['content-type'] = `multipart/form-data; boundary=${options.body.getBoundary()}`;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else if (isForm) {
|
|
553
|
+
if (noContentType) {
|
|
554
|
+
headers['content-type'] = 'application/x-www-form-urlencoded';
|
|
555
|
+
}
|
|
556
|
+
options.body = (new url_1.URLSearchParams(options.form)).toString();
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
if (noContentType) {
|
|
560
|
+
headers['content-type'] = 'application/json';
|
|
561
|
+
}
|
|
562
|
+
options.body = JSON.stringify(options.json);
|
|
563
|
+
}
|
|
564
|
+
const uploadBodySize = await get_body_size_1.default(options);
|
|
565
|
+
// See https://tools.ietf.org/html/rfc7230#section-3.3.2
|
|
566
|
+
// A user agent SHOULD send a Content-Length in a request message when
|
|
567
|
+
// no Transfer-Encoding is sent and the request method defines a meaning
|
|
568
|
+
// for an enclosed payload body. For example, a Content-Length header
|
|
569
|
+
// field is normally sent in a POST request even when the value is 0
|
|
570
|
+
// (indicating an empty payload body). A user agent SHOULD NOT send a
|
|
571
|
+
// Content-Length header field when the request message does not contain
|
|
572
|
+
// a payload body and the method semantics do not anticipate such a
|
|
573
|
+
// body.
|
|
574
|
+
if (is_1.default.undefined(headers['content-length']) && is_1.default.undefined(headers['transfer-encoding'])) {
|
|
575
|
+
if (!cannotHaveBody && !is_1.default.undefined(uploadBodySize)) {
|
|
576
|
+
headers['content-length'] = String(uploadBodySize);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
else if (cannotHaveBody) {
|
|
582
|
+
this._lockWrite();
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
this._unlockWrite();
|
|
586
|
+
}
|
|
587
|
+
this[kBodySize] = Number(headers['content-length']) || undefined;
|
|
588
|
+
}
|
|
589
|
+
async _onResponse(response) {
|
|
590
|
+
const { options } = this;
|
|
591
|
+
const { url } = options;
|
|
592
|
+
if (options.decompress) {
|
|
593
|
+
response = decompressResponse(response);
|
|
594
|
+
}
|
|
595
|
+
const statusCode = response.statusCode;
|
|
596
|
+
const typedResponse = response;
|
|
597
|
+
typedResponse.statusMessage = typedResponse.statusMessage === '' ? http.STATUS_CODES[statusCode] : typedResponse.statusMessage;
|
|
598
|
+
typedResponse.url = options.url.toString();
|
|
599
|
+
typedResponse.requestUrl = this.requestUrl;
|
|
600
|
+
typedResponse.redirectUrls = this.redirects;
|
|
601
|
+
typedResponse.request = this;
|
|
602
|
+
typedResponse.isFromCache = response.fromCache || false;
|
|
603
|
+
typedResponse.ip = this.ip;
|
|
604
|
+
this[kIsFromCache] = typedResponse.isFromCache;
|
|
605
|
+
this[kResponseSize] = Number(response.headers['content-length']) || undefined;
|
|
606
|
+
this[kResponse] = response;
|
|
607
|
+
response.once('end', () => {
|
|
608
|
+
this[kResponseSize] = this[kDownloadedSize];
|
|
609
|
+
this.emit('downloadProgress', this.downloadProgress);
|
|
610
|
+
});
|
|
611
|
+
response.on('error', (error) => {
|
|
612
|
+
this._beforeError(new ReadError(error, this));
|
|
613
|
+
});
|
|
614
|
+
response.once('aborted', () => {
|
|
615
|
+
if (this.aborted) {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
this._beforeError(new ReadError({
|
|
619
|
+
name: 'Error',
|
|
620
|
+
message: 'The server aborted the pending request'
|
|
621
|
+
}, this));
|
|
622
|
+
});
|
|
623
|
+
this.emit('downloadProgress', this.downloadProgress);
|
|
624
|
+
const rawCookies = response.headers['set-cookie'];
|
|
625
|
+
if (is_1.default.object(options.cookieJar) && rawCookies) {
|
|
626
|
+
let promises = rawCookies.map(async (rawCookie) => options.cookieJar.setCookie(rawCookie, url.toString()));
|
|
627
|
+
if (options.ignoreInvalidCookies) {
|
|
628
|
+
promises = promises.map(async (p) => p.catch(() => { }));
|
|
629
|
+
}
|
|
630
|
+
try {
|
|
631
|
+
await Promise.all(promises);
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
this._beforeError(error);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (options.followRedirect && response.headers.location && redirectCodes.has(statusCode)) {
|
|
639
|
+
// We're being redirected, we don't care about the response.
|
|
640
|
+
// It'd be besto to abort the request, but we can't because
|
|
641
|
+
// we would have to sacrifice the TCP connection. We don't want that.
|
|
642
|
+
response.resume();
|
|
643
|
+
if (this[kRequest]) {
|
|
644
|
+
this[kCancelTimeouts]();
|
|
645
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
646
|
+
delete this[kRequest];
|
|
647
|
+
this[kUnproxyEvents]();
|
|
648
|
+
}
|
|
649
|
+
const shouldBeGet = statusCode === 303 && options.method !== 'GET' && options.method !== 'HEAD';
|
|
650
|
+
if (shouldBeGet || !options.methodRewriting) {
|
|
651
|
+
// Server responded with "see other", indicating that the resource exists at another location,
|
|
652
|
+
// and the client should request it from that location via GET or HEAD.
|
|
653
|
+
options.method = 'GET';
|
|
654
|
+
if ('body' in options) {
|
|
655
|
+
delete options.body;
|
|
656
|
+
}
|
|
657
|
+
if ('json' in options) {
|
|
658
|
+
delete options.json;
|
|
659
|
+
}
|
|
660
|
+
if ('form' in options) {
|
|
661
|
+
delete options.form;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (this.redirects.length >= options.maxRedirects) {
|
|
665
|
+
this._beforeError(new MaxRedirectsError(this));
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
try {
|
|
669
|
+
// Handles invalid URLs. See https://github.com/sindresorhus/got/issues/604
|
|
670
|
+
const redirectBuffer = Buffer.from(response.headers.location, 'binary').toString();
|
|
671
|
+
const redirectUrl = new url_1.URL(redirectBuffer, url);
|
|
672
|
+
const redirectString = redirectUrl.toString();
|
|
673
|
+
decodeURI(redirectString);
|
|
674
|
+
// Redirecting to a different site, clear sensitive data.
|
|
675
|
+
if (redirectUrl.hostname !== url.hostname) {
|
|
676
|
+
if ('cookie' in options.headers) {
|
|
677
|
+
delete options.headers.cookie;
|
|
678
|
+
}
|
|
679
|
+
if ('authorization' in options.headers) {
|
|
680
|
+
delete options.headers.authorization;
|
|
681
|
+
}
|
|
682
|
+
if (options.username || options.password) {
|
|
683
|
+
delete options.username;
|
|
684
|
+
delete options.password;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
this.redirects.push(redirectString);
|
|
688
|
+
options.url = redirectUrl;
|
|
689
|
+
for (const hook of options.hooks.beforeRedirect) {
|
|
690
|
+
// eslint-disable-next-line no-await-in-loop
|
|
691
|
+
await hook(options, typedResponse);
|
|
692
|
+
}
|
|
693
|
+
this.emit('redirect', typedResponse, options);
|
|
694
|
+
await this._makeRequest();
|
|
695
|
+
}
|
|
696
|
+
catch (error) {
|
|
697
|
+
this._beforeError(error);
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const limitStatusCode = options.followRedirect ? 299 : 399;
|
|
703
|
+
const isOk = (statusCode >= 200 && statusCode <= limitStatusCode) || statusCode === 304;
|
|
704
|
+
if (options.throwHttpErrors && !isOk) {
|
|
705
|
+
await this._beforeError(new HTTPError(typedResponse));
|
|
706
|
+
if (this.destroyed) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
response.on('readable', () => {
|
|
711
|
+
if (this[kTriggerRead]) {
|
|
712
|
+
this._read();
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
this.on('resume', () => {
|
|
716
|
+
response.resume();
|
|
717
|
+
});
|
|
718
|
+
this.on('pause', () => {
|
|
719
|
+
response.pause();
|
|
720
|
+
});
|
|
721
|
+
response.once('end', () => {
|
|
722
|
+
this.push(null);
|
|
723
|
+
});
|
|
724
|
+
this.emit('response', response);
|
|
725
|
+
for (const destination of this[kServerResponsesPiped]) {
|
|
726
|
+
if (destination.headersSent) {
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
// eslint-disable-next-line guard-for-in
|
|
730
|
+
for (const key in response.headers) {
|
|
731
|
+
const isAllowed = options.decompress ? key !== 'content-encoding' : true;
|
|
732
|
+
const value = response.headers[key];
|
|
733
|
+
if (isAllowed) {
|
|
734
|
+
destination.setHeader(key, value);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
destination.statusCode = statusCode;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
_onRequest(request) {
|
|
741
|
+
const { options } = this;
|
|
742
|
+
const { timeout, url } = options;
|
|
743
|
+
http_timer_1.default(request);
|
|
744
|
+
this[kCancelTimeouts] = timed_out_1.default(request, timeout, url);
|
|
745
|
+
request.once('response', response => {
|
|
746
|
+
this._onResponse(response);
|
|
747
|
+
});
|
|
748
|
+
request.once('error', (error) => {
|
|
749
|
+
if (error instanceof timed_out_1.TimeoutError) {
|
|
750
|
+
error = new TimeoutError(error, this.timings, this);
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
error = new RequestError(error.message, error, this);
|
|
754
|
+
}
|
|
755
|
+
this._beforeError(error);
|
|
756
|
+
});
|
|
757
|
+
this[kUnproxyEvents] = proxy_events_1.default(request, this, proxiedRequestEvents);
|
|
758
|
+
this[kRequest] = request;
|
|
759
|
+
this.emit('uploadProgress', this.uploadProgress);
|
|
760
|
+
// Send body
|
|
761
|
+
const currentRequest = this.redirects.length === 0 ? this : request;
|
|
762
|
+
if (is_1.default.nodeStream(options.body)) {
|
|
763
|
+
options.body.pipe(currentRequest);
|
|
764
|
+
options.body.once('error', (error) => {
|
|
765
|
+
this._beforeError(new UploadError(error, this));
|
|
766
|
+
});
|
|
767
|
+
options.body.once('end', () => {
|
|
768
|
+
delete options.body;
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
this._unlockWrite();
|
|
773
|
+
if (!is_1.default.undefined(options.body)) {
|
|
774
|
+
this._writeRequest(options.body, null, () => { });
|
|
775
|
+
currentRequest.end();
|
|
776
|
+
this._lockWrite();
|
|
777
|
+
}
|
|
778
|
+
else if (this._cannotHaveBody || this._noPipe) {
|
|
779
|
+
currentRequest.end();
|
|
780
|
+
this._lockWrite();
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
this.emit('request', request);
|
|
784
|
+
}
|
|
785
|
+
async _createCacheableRequest(url, options) {
|
|
786
|
+
return new Promise((resolve, reject) => {
|
|
787
|
+
// TODO: Remove `utils/url-to-options.ts` when `cacheable-request` is fixed
|
|
788
|
+
Object.assign(options, url_to_options_1.default(url));
|
|
789
|
+
// `http-cache-semantics` checks this
|
|
790
|
+
delete options.url;
|
|
791
|
+
// This is ugly
|
|
792
|
+
const cacheRequest = cacheableStore.get(options.cache)(options, resolve);
|
|
793
|
+
// Restore options
|
|
794
|
+
options.url = url;
|
|
795
|
+
cacheRequest.once('error', (error) => {
|
|
796
|
+
if (error instanceof CacheableRequest.CacheError) {
|
|
797
|
+
reject(new CacheError(error, this));
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
reject(error);
|
|
801
|
+
});
|
|
802
|
+
cacheRequest.once('request', resolve);
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
async _makeRequest() {
|
|
806
|
+
var _a, _b;
|
|
807
|
+
const { options } = this;
|
|
808
|
+
const { url, headers, request, agent, timeout } = options;
|
|
809
|
+
for (const key in headers) {
|
|
810
|
+
if (is_1.default.undefined(headers[key])) {
|
|
811
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
812
|
+
delete headers[key];
|
|
813
|
+
}
|
|
814
|
+
else if (is_1.default.null_(headers[key])) {
|
|
815
|
+
throw new TypeError(`Use \`undefined\` instead of \`null\` to delete the \`${key}\` header`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (options.decompress && is_1.default.undefined(headers['accept-encoding'])) {
|
|
819
|
+
headers['accept-encoding'] = supportsBrotli ? 'gzip, deflate, br' : 'gzip, deflate';
|
|
820
|
+
}
|
|
821
|
+
// Set cookies
|
|
822
|
+
if (options.cookieJar) {
|
|
823
|
+
const cookieString = await options.cookieJar.getCookieString(options.url.toString());
|
|
824
|
+
if (is_1.default.nonEmptyString(cookieString)) {
|
|
825
|
+
options.headers.cookie = cookieString;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
for (const hook of options.hooks.beforeRequest) {
|
|
829
|
+
// eslint-disable-next-line no-await-in-loop
|
|
830
|
+
const result = await hook(options);
|
|
831
|
+
if (!is_1.default.undefined(result)) {
|
|
832
|
+
// @ts-ignore Skip the type mismatch to support abstract responses
|
|
833
|
+
options.request = () => result;
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (options.dnsCache && !('lookup' in options)) {
|
|
838
|
+
options.lookup = options.dnsCache.lookup;
|
|
839
|
+
}
|
|
840
|
+
// UNIX sockets
|
|
841
|
+
if (url.hostname === 'unix') {
|
|
842
|
+
const matches = /(?<socketPath>.+?):(?<path>.+)/.exec(`${url.pathname}${url.search}`);
|
|
843
|
+
if ((_a = matches) === null || _a === void 0 ? void 0 : _a.groups) {
|
|
844
|
+
const { socketPath, path } = matches.groups;
|
|
845
|
+
Object.assign(options, {
|
|
846
|
+
socketPath,
|
|
847
|
+
path,
|
|
848
|
+
host: ''
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
const isHttps = url.protocol === 'https:';
|
|
853
|
+
let fallbackFn;
|
|
854
|
+
if (options.http2) {
|
|
855
|
+
fallbackFn = http2wrapper.auto;
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
fallbackFn = isHttps ? https.request : http.request;
|
|
859
|
+
}
|
|
860
|
+
const realFn = (_b = options.request, (_b !== null && _b !== void 0 ? _b : fallbackFn));
|
|
861
|
+
const fn = options.cache ? this._createCacheableRequest.bind(this) : realFn;
|
|
862
|
+
if (agent && !options.http2) {
|
|
863
|
+
options.agent = agent[isHttps ? 'https' : 'http'];
|
|
864
|
+
}
|
|
865
|
+
options[kRequest] = realFn;
|
|
866
|
+
delete options.request;
|
|
867
|
+
delete options.timeout;
|
|
868
|
+
let requestOrResponse;
|
|
869
|
+
try {
|
|
870
|
+
requestOrResponse = await fn(url, options);
|
|
871
|
+
if (is_1.default.undefined(requestOrResponse)) {
|
|
872
|
+
requestOrResponse = fallbackFn(url, options);
|
|
873
|
+
}
|
|
874
|
+
// Restore options
|
|
875
|
+
options.request = request;
|
|
876
|
+
options.timeout = timeout;
|
|
877
|
+
options.agent = agent;
|
|
878
|
+
if (isClientRequest(requestOrResponse)) {
|
|
879
|
+
this._onRequest(requestOrResponse);
|
|
880
|
+
// Emit the response after the stream has been ended
|
|
881
|
+
}
|
|
882
|
+
else if (this.writable) {
|
|
883
|
+
this.once('finish', () => {
|
|
884
|
+
this._onResponse(requestOrResponse);
|
|
885
|
+
});
|
|
886
|
+
this._unlockWrite();
|
|
887
|
+
this.end();
|
|
888
|
+
this._lockWrite();
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
this._onResponse(requestOrResponse);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
catch (error) {
|
|
895
|
+
if (error instanceof RequestError) {
|
|
896
|
+
throw error;
|
|
897
|
+
}
|
|
898
|
+
throw new RequestError(error.message, error, this);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
async _beforeError(error) {
|
|
902
|
+
this[kStopReading] = true;
|
|
903
|
+
if (!(error instanceof RequestError)) {
|
|
904
|
+
error = new RequestError(error.message, error, this);
|
|
905
|
+
}
|
|
906
|
+
try {
|
|
907
|
+
const { response } = error;
|
|
908
|
+
if (response) {
|
|
909
|
+
response.setEncoding(this._readableState.encoding);
|
|
910
|
+
response.rawBody = await getStream.buffer(response);
|
|
911
|
+
response.body = response.rawBody.toString();
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
catch (_) { }
|
|
915
|
+
try {
|
|
916
|
+
for (const hook of this.options.hooks.beforeError) {
|
|
917
|
+
// eslint-disable-next-line no-await-in-loop
|
|
918
|
+
error = await hook(error);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
catch (error_) {
|
|
922
|
+
error = new RequestError(error_.message, error_, this);
|
|
923
|
+
}
|
|
924
|
+
this.destroy(error);
|
|
925
|
+
}
|
|
926
|
+
_read() {
|
|
927
|
+
this[kTriggerRead] = true;
|
|
928
|
+
const response = this[kResponse];
|
|
929
|
+
if (response && !this[kStopReading]) {
|
|
930
|
+
// We cannot put this in the `if` above
|
|
931
|
+
// because `.read()` also triggers the `end` event
|
|
932
|
+
if (response.readableLength) {
|
|
933
|
+
this[kTriggerRead] = false;
|
|
934
|
+
}
|
|
935
|
+
let data;
|
|
936
|
+
while ((data = response.read()) !== null) {
|
|
937
|
+
this[kDownloadedSize] += data.length;
|
|
938
|
+
this[kStartedReading] = true;
|
|
939
|
+
const progress = this.downloadProgress;
|
|
940
|
+
if (progress.percent < 1) {
|
|
941
|
+
this.emit('downloadProgress', progress);
|
|
942
|
+
}
|
|
943
|
+
this.push(data);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
_write(chunk, encoding, callback) {
|
|
948
|
+
const write = () => {
|
|
949
|
+
this._writeRequest(chunk, encoding, callback);
|
|
950
|
+
};
|
|
951
|
+
if (this.finalized) {
|
|
952
|
+
write();
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
this.once('finalized', write);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
_writeRequest(chunk, encoding, callback) {
|
|
959
|
+
this._progressCallbacks.push(() => {
|
|
960
|
+
this[kUploadedSize] += Buffer.byteLength(chunk, encoding);
|
|
961
|
+
const progress = this.uploadProgress;
|
|
962
|
+
if (progress.percent < 1) {
|
|
963
|
+
this.emit('uploadProgress', progress);
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
this[kRequest].write(chunk, encoding, (error) => {
|
|
967
|
+
if (!error && this._progressCallbacks.length !== 0) {
|
|
968
|
+
this._progressCallbacks.shift()();
|
|
969
|
+
}
|
|
970
|
+
callback(error);
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
_final(callback) {
|
|
974
|
+
const endRequest = () => {
|
|
975
|
+
// FIX: Node.js 10 calls the write callback AFTER the end callback!
|
|
976
|
+
while (this._progressCallbacks.length !== 0) {
|
|
977
|
+
this._progressCallbacks.shift()();
|
|
978
|
+
}
|
|
979
|
+
// We need to check if `this[kRequest]` is present,
|
|
980
|
+
// because it isn't when we use cache.
|
|
981
|
+
if (!(kRequest in this)) {
|
|
982
|
+
callback();
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
this[kRequest].end((error) => {
|
|
986
|
+
if (!error) {
|
|
987
|
+
this[kBodySize] = this[kUploadedSize];
|
|
988
|
+
this.emit('uploadProgress', this.uploadProgress);
|
|
989
|
+
this[kRequest].emit('upload-complete');
|
|
990
|
+
}
|
|
991
|
+
callback(error);
|
|
992
|
+
});
|
|
993
|
+
};
|
|
994
|
+
if (this.finalized) {
|
|
995
|
+
endRequest();
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
this.once('finalized', endRequest);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
_destroy(error, callback) {
|
|
1002
|
+
var _a;
|
|
1003
|
+
if (kRequest in this) {
|
|
1004
|
+
// TODO: Remove the next `if` when https://github.com/nodejs/node/issues/32851 gets fixed
|
|
1005
|
+
if (!((_a = this[kResponse]) === null || _a === void 0 ? void 0 : _a.complete)) {
|
|
1006
|
+
this[kRequest].abort();
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
this.once('finalized', () => {
|
|
1011
|
+
if (kRequest in this) {
|
|
1012
|
+
this[kRequest].abort();
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
if (error !== null && !is_1.default.undefined(error) && !(error instanceof RequestError)) {
|
|
1017
|
+
error = new RequestError(error.message, error, this);
|
|
1018
|
+
}
|
|
1019
|
+
callback(error);
|
|
1020
|
+
}
|
|
1021
|
+
get ip() {
|
|
1022
|
+
var _a;
|
|
1023
|
+
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket.remoteAddress;
|
|
1024
|
+
}
|
|
1025
|
+
get aborted() {
|
|
1026
|
+
var _a;
|
|
1027
|
+
return Boolean((_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.aborted);
|
|
1028
|
+
}
|
|
1029
|
+
get socket() {
|
|
1030
|
+
var _a;
|
|
1031
|
+
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.socket;
|
|
1032
|
+
}
|
|
1033
|
+
get downloadProgress() {
|
|
1034
|
+
let percent;
|
|
1035
|
+
if (this[kResponseSize]) {
|
|
1036
|
+
percent = this[kDownloadedSize] / this[kResponseSize];
|
|
1037
|
+
}
|
|
1038
|
+
else if (this[kResponseSize] === this[kDownloadedSize]) {
|
|
1039
|
+
percent = 1;
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
percent = 0;
|
|
1043
|
+
}
|
|
1044
|
+
return {
|
|
1045
|
+
percent,
|
|
1046
|
+
transferred: this[kDownloadedSize],
|
|
1047
|
+
total: this[kResponseSize]
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
get uploadProgress() {
|
|
1051
|
+
let percent;
|
|
1052
|
+
if (this[kBodySize]) {
|
|
1053
|
+
percent = this[kUploadedSize] / this[kBodySize];
|
|
1054
|
+
}
|
|
1055
|
+
else if (this[kBodySize] === this[kUploadedSize]) {
|
|
1056
|
+
percent = 1;
|
|
1057
|
+
}
|
|
1058
|
+
else {
|
|
1059
|
+
percent = 0;
|
|
1060
|
+
}
|
|
1061
|
+
return {
|
|
1062
|
+
percent,
|
|
1063
|
+
transferred: this[kUploadedSize],
|
|
1064
|
+
total: this[kBodySize]
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
get timings() {
|
|
1068
|
+
var _a;
|
|
1069
|
+
return (_a = this[kRequest]) === null || _a === void 0 ? void 0 : _a.timings;
|
|
1070
|
+
}
|
|
1071
|
+
get isFromCache() {
|
|
1072
|
+
return this[kIsFromCache];
|
|
1073
|
+
}
|
|
1074
|
+
pipe(destination, options) {
|
|
1075
|
+
if (this[kStartedReading]) {
|
|
1076
|
+
throw new Error('Failed to pipe. The response has been emitted already.');
|
|
1077
|
+
}
|
|
1078
|
+
if (destination instanceof http_1.ServerResponse) {
|
|
1079
|
+
this[kServerResponsesPiped].add(destination);
|
|
1080
|
+
}
|
|
1081
|
+
return super.pipe(destination, options);
|
|
1082
|
+
}
|
|
1083
|
+
unpipe(destination) {
|
|
1084
|
+
if (destination instanceof http_1.ServerResponse) {
|
|
1085
|
+
this[kServerResponsesPiped].delete(destination);
|
|
1086
|
+
}
|
|
1087
|
+
super.unpipe(destination);
|
|
1088
|
+
return this;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
exports.default = Request;
|