@e-mc/request 0.9.9 → 0.10.1
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/README.md +7 -4
- package/http/host/index.js +38 -9
- package/index.js +833 -667
- package/package.json +3 -3
- package/util.d.ts +2 -0
- package/util.js +51 -19
package/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
2
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const fs = require("fs");
|
|
5
|
+
const crypto = require("crypto");
|
|
5
6
|
const child_process = require("child_process");
|
|
6
7
|
const http = require("http");
|
|
7
8
|
const https = require("https");
|
|
@@ -15,16 +16,17 @@ const combined = require("combined-stream");
|
|
|
15
16
|
const pm = require("picomatch");
|
|
16
17
|
const yaml = require("js-yaml");
|
|
17
18
|
const which = require("which");
|
|
18
|
-
const types_1 = require("@e-mc/types");
|
|
19
19
|
const module_1 = require("@e-mc/module");
|
|
20
|
-
const
|
|
20
|
+
const types_1 = require("@e-mc/types");
|
|
21
21
|
const util_1 = require("@e-mc/request/util");
|
|
22
|
+
const host_1 = require("@e-mc/request/http/host");
|
|
22
23
|
const kSession = Symbol('session');
|
|
23
24
|
const kHttpVersion = Symbol('httpVersion');
|
|
24
25
|
const kIpVersion = Symbol('ipVersion');
|
|
25
26
|
const kAgentTimeout = Symbol('agentTimeout');
|
|
26
27
|
const kHeaders = Symbol('headers');
|
|
27
28
|
const kCerts = Symbol('certs');
|
|
29
|
+
const kBaseURL = Symbol('baseURL');
|
|
28
30
|
const kDownloading = Symbol('downloading');
|
|
29
31
|
const kSingleton = Symbol('singleton');
|
|
30
32
|
const kHostInfo = Symbol('hostInfo');
|
|
@@ -33,8 +35,9 @@ const kPendingDns = Symbol('pendingDns');
|
|
|
33
35
|
const kConnectHttp = Symbol('connectHttp');
|
|
34
36
|
const kStatusOn = Symbol('statusOn');
|
|
35
37
|
const kHeadersOn = Symbol('headersOn');
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
+
const SUPPORTED_NODE20 = (0, types_1.supported)(20);
|
|
39
|
+
const REGEXP_PEMCERT = /^-{3,}[ \t]*BEGIN[ \t].+\n-{3,}[ \t]*END[ \t][^-]+-{3,}$/s;
|
|
40
|
+
const REGEXP_GLOBWITHIN = /\\\?|(?:(?<!\\)(?:\*|\[!?[^!\]]+\]|\{(?:[^,]+,)+[^}]+\}|[!?+*@]\((?:[^|]+\|)*[^)]+\)|\?.*\?|\?$))/;
|
|
38
41
|
const HTTP = {
|
|
39
42
|
HOST: {},
|
|
40
43
|
HEADERS: {},
|
|
@@ -52,13 +55,14 @@ const DNS = {
|
|
|
52
55
|
FAMILY: 0
|
|
53
56
|
};
|
|
54
57
|
const ARIA2 = {
|
|
55
|
-
BIN: which.sync(PLATFORM_WIN32 ? 'aria2c.exe' : 'aria2c', { nothrow: true }) || '',
|
|
58
|
+
BIN: which.sync(module_1.PLATFORM_WIN32 ? 'aria2c.exe' : 'aria2c', { nothrow: true }) || '',
|
|
56
59
|
EXEC_UID: undefined,
|
|
57
60
|
EXEC_GID: undefined,
|
|
58
61
|
PID_TIMER: null,
|
|
59
62
|
PID_QUEUE: [],
|
|
60
63
|
UPDATE_STATUS: 0,
|
|
61
64
|
UPDATE_BROADCAST_ONLY: false,
|
|
65
|
+
CHECK_INTEGRITY: false,
|
|
62
66
|
BT_STOP_TIMEOUT: 60,
|
|
63
67
|
BT_TRACKER_CONNECT_TIMEOUT: 30,
|
|
64
68
|
BT_TRACKER_TIMEOUT: 60,
|
|
@@ -118,15 +122,6 @@ function setDnsCache(hostname, value, expires) {
|
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
|
-
function isConnectionTimeout(err) {
|
|
122
|
-
switch (err instanceof Error && err.code) {
|
|
123
|
-
case 'ETIMEDOUT':
|
|
124
|
-
case 'ECONNRESET':
|
|
125
|
-
return true;
|
|
126
|
-
default:
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
125
|
function setOutgoingHeaders(output, headers) {
|
|
131
126
|
for (const href in headers) {
|
|
132
127
|
output[href] = (0, util_1.normalizeHeaders)(headers[href]);
|
|
@@ -134,18 +129,17 @@ function setOutgoingHeaders(output, headers) {
|
|
|
134
129
|
}
|
|
135
130
|
function getProxySettings(request, agentTimeout) {
|
|
136
131
|
const proxy = request.proxy;
|
|
137
|
-
if (proxy
|
|
138
|
-
const
|
|
139
|
-
const address = (!module_1.isURL(proxy.address) ? port === 80 ? 'http://' : 'https://' : '') + proxy.address;
|
|
132
|
+
if (proxy && (proxy.origin || proxy.address && proxy.port)) {
|
|
133
|
+
const origin = proxy.origin || ((!module_1.isURL(proxy.address) ? (0, util_1.asInt)(proxy.port) === 80 ? 'http://' : 'https://' : '') + proxy.address + ':' + proxy.port);
|
|
140
134
|
try {
|
|
141
|
-
let host = new URL(
|
|
135
|
+
let host = new URL(origin), include, exclude;
|
|
142
136
|
if (proxy.username) {
|
|
143
137
|
host = new URL(host.protocol + '//' + (0, util_1.getBasicAuth)(proxy.username, proxy.password) + host.host);
|
|
144
138
|
}
|
|
145
139
|
if ((0, types_1.isArray)(proxy.include)) {
|
|
146
140
|
include = proxy.include;
|
|
147
141
|
}
|
|
148
|
-
|
|
142
|
+
if ((0, types_1.isArray)(proxy.exclude)) {
|
|
149
143
|
exclude = proxy.exclude;
|
|
150
144
|
}
|
|
151
145
|
return { host, include, exclude, keepAlive: proxy.keep_alive, agentTimeout };
|
|
@@ -156,14 +150,18 @@ function getProxySettings(request, agentTimeout) {
|
|
|
156
150
|
return null;
|
|
157
151
|
}
|
|
158
152
|
function closeTorrent(pid) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
153
|
+
if (typeof pid === 'number') {
|
|
154
|
+
const index = ARIA2.PID_QUEUE.findIndex(value => pid === value[0]);
|
|
155
|
+
if (index !== -1) {
|
|
156
|
+
ARIA2.PID_QUEUE.splice(index, 1);
|
|
157
|
+
}
|
|
162
158
|
}
|
|
163
159
|
}
|
|
164
160
|
function clearDnsLookup() {
|
|
165
161
|
DNS.CACHE = Object.create(null);
|
|
166
|
-
|
|
162
|
+
for (const value of DNS.TIMEOUT) {
|
|
163
|
+
clearTimeout(value);
|
|
164
|
+
}
|
|
167
165
|
DNS.TIMEOUT.length = 0;
|
|
168
166
|
}
|
|
169
167
|
function resetHttpHost(version) {
|
|
@@ -194,15 +192,8 @@ function resetHttpHost(version) {
|
|
|
194
192
|
function validateCerts(certs) {
|
|
195
193
|
const text = {};
|
|
196
194
|
const file = {};
|
|
197
|
-
const checkFile = (values) => {
|
|
198
|
-
for (let pathname of values) {
|
|
199
|
-
if (module_1.isPath(pathname = path.resolve(pathname))) {
|
|
200
|
-
return pathname;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
195
|
for (let origin in certs) {
|
|
205
|
-
let { ca, cert, key, passphrase, version: minVersion } = certs[origin], file_ca, file_cert, file_key, keyObject;
|
|
196
|
+
let { ca, cert, key, passphrase, ciphers, version: minVersion } = certs[origin], file_ca, file_cert, file_key, keyObject;
|
|
206
197
|
try {
|
|
207
198
|
origin = new URL(origin).origin;
|
|
208
199
|
}
|
|
@@ -241,16 +232,16 @@ function validateCerts(certs) {
|
|
|
241
232
|
}
|
|
242
233
|
file_key = checkFile(key);
|
|
243
234
|
key = key.map(value => Request.readTLSKey(value)).filter(value => value);
|
|
244
|
-
if (key.length) {
|
|
235
|
+
if (key.length > 0) {
|
|
245
236
|
if (!Array.isArray(passphrase)) {
|
|
246
237
|
passphrase = passphrase ? [passphrase] : [];
|
|
247
238
|
}
|
|
248
|
-
keyObject = key.length > 1 || passphrase.length ? key.map((pem, index) => ({ pem, passphrase: passphrase[index] })) : key[0];
|
|
239
|
+
keyObject = key.length > 1 || passphrase.length > 0 ? key.map((pem, index) => ({ pem, passphrase: passphrase[index] })) : key[0];
|
|
249
240
|
}
|
|
250
241
|
}
|
|
251
242
|
}
|
|
252
243
|
if (ca || cert) {
|
|
253
|
-
text[origin] = { ca, cert, key: keyObject, version: minVersion };
|
|
244
|
+
text[origin] = { ca, cert, key: keyObject, ciphers, version: minVersion };
|
|
254
245
|
}
|
|
255
246
|
if (file_ca || file_cert) {
|
|
256
247
|
file[origin] = { ca: file_ca, cert: file_cert, key: file_key };
|
|
@@ -258,32 +249,42 @@ function validateCerts(certs) {
|
|
|
258
249
|
}
|
|
259
250
|
return [text, file];
|
|
260
251
|
}
|
|
261
|
-
function
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
this[kDownloading].delete(outAbort);
|
|
252
|
+
function checkFile(values) {
|
|
253
|
+
for (let pathname of values) {
|
|
254
|
+
if (module_1.isPath(pathname = path.resolve(pathname))) {
|
|
255
|
+
return pathname;
|
|
256
|
+
}
|
|
267
257
|
}
|
|
268
|
-
request.destroy(reason);
|
|
269
258
|
}
|
|
270
|
-
function
|
|
259
|
+
function parseSize(value, zero) {
|
|
260
|
+
if (zero && (value === '0' || value === 0)) {
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
263
|
+
if ((0, types_1.isString)(value) && /^\d+[KM]$/.test(value = value.trim().toUpperCase())) {
|
|
264
|
+
return value;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function copySearchParams(url, base) {
|
|
268
|
+
base.searchParams.forEach((value, key) => {
|
|
269
|
+
if (!url.searchParams.has(key)) {
|
|
270
|
+
url.searchParams.append(key, value);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function checkEncoding(request, response, statusCode, outStream, contentEncoding) {
|
|
271
275
|
switch (statusCode) {
|
|
272
276
|
case 206:
|
|
273
277
|
request.emit('error', (0, types_1.errorValue)("Aborted", 'Partial content'));
|
|
274
278
|
case 204:
|
|
275
279
|
return;
|
|
276
|
-
default:
|
|
277
|
-
contentEncoding = contentEncoding.trim();
|
|
278
|
-
if (!contentEncoding) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
contentEncoding = contentEncoding.toLowerCase();
|
|
282
|
-
break;
|
|
283
280
|
}
|
|
281
|
+
if (!contentEncoding) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
contentEncoding = contentEncoding.trim().toLowerCase();
|
|
284
285
|
const chunkSize = outStream?.writableHighWaterMark;
|
|
285
286
|
let pipeTo;
|
|
286
|
-
if (contentEncoding.
|
|
287
|
+
if (!contentEncoding.includes(',')) {
|
|
287
288
|
pipeTo = decompressEncoding(contentEncoding, chunkSize);
|
|
288
289
|
}
|
|
289
290
|
else {
|
|
@@ -297,10 +298,18 @@ function checkEncoding(request, response, statusCode, outStream, contentEncoding
|
|
|
297
298
|
}
|
|
298
299
|
if (pipeTo) {
|
|
299
300
|
if (outStream) {
|
|
300
|
-
stream.pipeline(response, pipeTo, outStream, err =>
|
|
301
|
+
stream.pipeline(response, pipeTo, outStream, err => {
|
|
302
|
+
if (err) {
|
|
303
|
+
response.emit('error', err);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
301
306
|
}
|
|
302
307
|
else {
|
|
303
|
-
stream.pipeline(response, pipeTo, err =>
|
|
308
|
+
stream.pipeline(response, pipeTo, err => {
|
|
309
|
+
if (err) {
|
|
310
|
+
response.emit('error', err);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
304
313
|
}
|
|
305
314
|
return pipeTo;
|
|
306
315
|
}
|
|
@@ -322,10 +331,577 @@ function decompressEncoding(value, chunkSize) {
|
|
|
322
331
|
break;
|
|
323
332
|
}
|
|
324
333
|
}
|
|
334
|
+
function abortHeaders(href, request, options) {
|
|
335
|
+
const reason = (0, types_1.errorValue)("Aborted by client", href);
|
|
336
|
+
const outAbort = options.outAbort;
|
|
337
|
+
if (outAbort) {
|
|
338
|
+
outAbort.abort(reason);
|
|
339
|
+
this[kDownloading].delete(outAbort);
|
|
340
|
+
}
|
|
341
|
+
request.destroy(reason);
|
|
342
|
+
}
|
|
343
|
+
function resetAria2() {
|
|
344
|
+
clearInterval(ARIA2.PID_TIMER);
|
|
345
|
+
ARIA2.PID_TIMER = null;
|
|
346
|
+
}
|
|
347
|
+
function escapeShellQuote(value) {
|
|
348
|
+
value = value.replaceAll('"', '\\"');
|
|
349
|
+
return module_1.PLATFORM_WIN32 ? value : value.replace(/[ $`]/g, capture => (capture !== ' ' ? '\\' : '') + '\\' + capture);
|
|
350
|
+
}
|
|
351
|
+
function failedDns(err, pending) {
|
|
352
|
+
for (const cb of pending) {
|
|
353
|
+
if (SUPPORTED_NODE20) {
|
|
354
|
+
cb(err, []);
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
cb(err, '', 0);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
pending.length = 0;
|
|
361
|
+
}
|
|
362
|
+
function successDns(instance, value, pending, connected) {
|
|
363
|
+
instance[kConnectDns][value] = connected;
|
|
364
|
+
setDnsCache(value, connected, null);
|
|
365
|
+
if (SUPPORTED_NODE20) {
|
|
366
|
+
for (const cb of pending) {
|
|
367
|
+
cb(null, connected);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
const { address, family } = connected[0];
|
|
372
|
+
for (const cb of pending) {
|
|
373
|
+
cb(null, address, family);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
pending.length = 0;
|
|
377
|
+
}
|
|
378
|
+
const configureDns = (family, options) => family === 0 ? options : { family, hints: family === 6 ? dns.V4MAPPED : 0 };
|
|
379
|
+
const ignoreOpt = (opts, ...values) => !opts?.some(item => values.includes(item));
|
|
380
|
+
const escapeQuote = (value) => value.replace(/[\\"]/g, capture => '\\' + capture);
|
|
381
|
+
class Fetch {
|
|
382
|
+
static isUnsupported(value) {
|
|
383
|
+
return value === 421 || value === 505;
|
|
384
|
+
}
|
|
385
|
+
static isDowngrade(err) {
|
|
386
|
+
return err instanceof Error && (err.code === 'ERR_HTTP2_ERROR' || this.isUnsupported(Math.abs(err.errno)));
|
|
387
|
+
}
|
|
388
|
+
static wasAborted(err) {
|
|
389
|
+
return err instanceof Error && err.message.startsWith("Aborted");
|
|
390
|
+
}
|
|
391
|
+
static isConnectionTimeout(err) {
|
|
392
|
+
switch (err instanceof Error && err.code) {
|
|
393
|
+
case 'ETIMEDOUT':
|
|
394
|
+
case 'ECONNRESET':
|
|
395
|
+
return true;
|
|
396
|
+
default:
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
constructor(instance, uri, opts) {
|
|
401
|
+
this.instance = instance;
|
|
402
|
+
this.opts = opts;
|
|
403
|
+
this.retries = 0;
|
|
404
|
+
this.redirects = 0;
|
|
405
|
+
this.closed = false;
|
|
406
|
+
this.aborted = false;
|
|
407
|
+
this.timeout = null;
|
|
408
|
+
this.outStream = null;
|
|
409
|
+
this.status = opts.silent === false || !opts.silent && !instance[kSingleton];
|
|
410
|
+
this.log = this.status && LOG_HTTP && LOG_TIMEPROCESS;
|
|
411
|
+
this.startTime = this.log ? process.hrtime() : 0;
|
|
412
|
+
this.setConfig(uri);
|
|
413
|
+
}
|
|
414
|
+
async start() {
|
|
415
|
+
return new Promise((resolve, reject) => {
|
|
416
|
+
this.promise = [resolve, reject];
|
|
417
|
+
this.init();
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
init() {
|
|
421
|
+
if (this.retries > 0) {
|
|
422
|
+
this.cleanup();
|
|
423
|
+
this.aborted = false;
|
|
424
|
+
}
|
|
425
|
+
this.setWriteStream();
|
|
426
|
+
const config = this.config;
|
|
427
|
+
const client = this.instance.open(this.uri, config);
|
|
428
|
+
this.client = client;
|
|
429
|
+
if (config.httpVersion === 2) {
|
|
430
|
+
client
|
|
431
|
+
.on('response', (headers, flags) => {
|
|
432
|
+
if (this.destroyed) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const statusCode = headers[':status'];
|
|
436
|
+
if (statusCode < 300) {
|
|
437
|
+
this.acceptResponse(headers);
|
|
438
|
+
}
|
|
439
|
+
else if (statusCode < 400) {
|
|
440
|
+
this.redirectResponse(statusCode, headers.location);
|
|
441
|
+
}
|
|
442
|
+
else if (statusCode === 401 ||
|
|
443
|
+
statusCode === 402 ||
|
|
444
|
+
statusCode === 403 ||
|
|
445
|
+
statusCode === 404 ||
|
|
446
|
+
statusCode === 407 ||
|
|
447
|
+
statusCode === 410) {
|
|
448
|
+
this.rejectError(this.formatStatus(statusCode));
|
|
449
|
+
}
|
|
450
|
+
else if (this.isRetry(statusCode)) {
|
|
451
|
+
this.retryResponse(statusCode, headers['retry-after']);
|
|
452
|
+
}
|
|
453
|
+
else if (Fetch.isUnsupported(statusCode)) {
|
|
454
|
+
this.retryDownload(true, this.formatNgFlags(http2.constants.NGHTTP2_PROTOCOL_ERROR, statusCode));
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
switch (flags) {
|
|
458
|
+
case http2.constants.NGHTTP2_PROTOCOL_ERROR:
|
|
459
|
+
case http2.constants.NGHTTP2_INADEQUATE_SECURITY:
|
|
460
|
+
case http2.constants.NGHTTP2_HTTP_1_1_REQUIRED:
|
|
461
|
+
this.retryDownload(true, this.formatNgFlags(flags, statusCode, headers.location));
|
|
462
|
+
break;
|
|
463
|
+
default:
|
|
464
|
+
this.retryDownload(false, this.formatStatus(statusCode));
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
})
|
|
469
|
+
.on('unknownProtocol', () => {
|
|
470
|
+
if (!this.aborted) {
|
|
471
|
+
this.retryDownload(true, 'Unknown protocol (HTTP/2)');
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
.on('aborted', () => {
|
|
475
|
+
this.aborted = true;
|
|
476
|
+
this.rejectError((0, types_1.createAbortError)());
|
|
477
|
+
})
|
|
478
|
+
.on('error', async (err) => {
|
|
479
|
+
if (this.aborted) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (Fetch.wasAborted(err)) {
|
|
483
|
+
this.errorResponse(err);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
switch (!Fetch.isDowngrade(err) && await config.host.hasProtocol(2)) {
|
|
487
|
+
case 1:
|
|
488
|
+
this.errorResponse(err);
|
|
489
|
+
break;
|
|
490
|
+
case 2:
|
|
491
|
+
this.retryDownload(false, err);
|
|
492
|
+
break;
|
|
493
|
+
default:
|
|
494
|
+
this.retryDownload(true, err);
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
client
|
|
502
|
+
.on('response', res => {
|
|
503
|
+
if (this.destroyed) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const statusCode = res.statusCode;
|
|
507
|
+
if (statusCode < 300) {
|
|
508
|
+
this.acceptResponse(res.headers);
|
|
509
|
+
}
|
|
510
|
+
else if (statusCode < 400) {
|
|
511
|
+
this.redirectResponse(statusCode, res.headers.location);
|
|
512
|
+
}
|
|
513
|
+
else if (this.isRetry(statusCode)) {
|
|
514
|
+
this.retryResponse(statusCode, res.headers['retry-after']);
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
this.abortResponse();
|
|
518
|
+
this.rejectError(this.formatStatus(statusCode));
|
|
519
|
+
}
|
|
520
|
+
})
|
|
521
|
+
.on('abort', () => {
|
|
522
|
+
this.aborted = true;
|
|
523
|
+
this.rejectError((0, types_1.createAbortError)());
|
|
524
|
+
})
|
|
525
|
+
.on('error', err => {
|
|
526
|
+
if (!this.aborted) {
|
|
527
|
+
this.errorResponse(err);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
client.on('timeout', () => {
|
|
532
|
+
if (this.aborted) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
this.abortResponse();
|
|
536
|
+
if (++this.retries <= this.retryLimit) {
|
|
537
|
+
this.retryTimeout();
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
this.rejectError(this.formatStatus(408));
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
setConfig(uri) {
|
|
545
|
+
this.uri = uri;
|
|
546
|
+
this.config = this.instance.opts(uri, this.opts);
|
|
547
|
+
}
|
|
548
|
+
setWriteStream() {
|
|
549
|
+
const pipeTo = this.pipeTo;
|
|
550
|
+
if (typeof pipeTo === 'string') {
|
|
551
|
+
try {
|
|
552
|
+
this.outStream = fs.createWriteStream(pipeTo, { emitClose: false, highWaterMark: this.config.host.streamSize });
|
|
553
|
+
this.config.outStream = this.outStream;
|
|
554
|
+
}
|
|
555
|
+
catch (err) {
|
|
556
|
+
this.rejectError(err);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
this.config.outStream = pipeTo;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
abortResponse() {
|
|
564
|
+
if (this.closed) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
if (this.timeout) {
|
|
568
|
+
clearTimeout(this.timeout);
|
|
569
|
+
}
|
|
570
|
+
this.aborted = true;
|
|
571
|
+
if (this.outAbort) {
|
|
572
|
+
if (!this.client.aborted) {
|
|
573
|
+
this.outAbort.abort();
|
|
574
|
+
}
|
|
575
|
+
this.downloading.delete(this.outAbort);
|
|
576
|
+
}
|
|
577
|
+
this.client.destroy();
|
|
578
|
+
}
|
|
579
|
+
retryDownload(downgrade, message) {
|
|
580
|
+
if (this.aborted) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
this.abortResponse();
|
|
584
|
+
if (downgrade) {
|
|
585
|
+
const host = this.config.host;
|
|
586
|
+
host.failed(2);
|
|
587
|
+
if (host.version > 1) {
|
|
588
|
+
if (this.status) {
|
|
589
|
+
this.instance.formatMessage(1024, 'HTTP2', ["Unsupported protocol", host.origin], message, { failed: true });
|
|
590
|
+
}
|
|
591
|
+
host.version = 1;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
this.opts.httpVersion = 1;
|
|
595
|
+
this.init();
|
|
596
|
+
}
|
|
597
|
+
acceptResponse(headers) {
|
|
598
|
+
const { instance, config, client, opts, pipeTo } = this;
|
|
599
|
+
const parent = this.instance.host;
|
|
600
|
+
const progressId = config.progressId;
|
|
601
|
+
if ('outHeaders' in opts) {
|
|
602
|
+
opts.outHeaders = headers;
|
|
603
|
+
}
|
|
604
|
+
if ('outFilename' in opts) {
|
|
605
|
+
opts.outFilename = (0, util_1.parseHeader)(headers, 'content-disposition');
|
|
606
|
+
}
|
|
607
|
+
const pipeline = pipeTo ? !(0, types_1.isString)(pipeTo) : false;
|
|
608
|
+
const enabled = config.connected?.call(client, headers) !== false && !pipeline;
|
|
609
|
+
const maxBufferSize = config.maxBufferSize ? (0, types_1.alignSize)(config.maxBufferSize) : 0;
|
|
610
|
+
const contentLength = parent && progressId !== undefined ? parseInt(headers['content-length'] || '0') : 0;
|
|
611
|
+
let log = this.log, buffer = null, dataLength = 0, mibsTime, delayTime;
|
|
612
|
+
if (log || contentLength > 0 || instance.readTimeout > 0) {
|
|
613
|
+
client.once('readable', () => {
|
|
614
|
+
if (instance.readTimeout > 0) {
|
|
615
|
+
this.timeout = setTimeout(() => {
|
|
616
|
+
this.abortResponse();
|
|
617
|
+
this.rejectError((0, types_1.errorValue)("Timeout was exceeded", this.uri.toString()));
|
|
618
|
+
}, instance.readTimeout);
|
|
619
|
+
}
|
|
620
|
+
if (log) {
|
|
621
|
+
switch (instance.settings?.time_format || LOG_TIMEFORMAT) {
|
|
622
|
+
case 'readable':
|
|
623
|
+
delayTime = process.hrtime(this.startTime);
|
|
624
|
+
break;
|
|
625
|
+
case 'relative':
|
|
626
|
+
delayTime = Date.now() - instance.startTime;
|
|
627
|
+
break;
|
|
628
|
+
case 'none':
|
|
629
|
+
if (opts.silent !== false) {
|
|
630
|
+
log = false;
|
|
631
|
+
}
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
mibsTime = process.hrtime();
|
|
636
|
+
if (contentLength > 0) {
|
|
637
|
+
parent.updateProgress("request", progressId, 0, 0, mibsTime);
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
if (enabled) {
|
|
642
|
+
const encoding = opts.encoding;
|
|
643
|
+
client.on('data', (chunk) => {
|
|
644
|
+
if (buffer) {
|
|
645
|
+
if (typeof buffer === 'string') {
|
|
646
|
+
buffer += typeof chunk === 'string' ? chunk : chunk.toString(encoding);
|
|
647
|
+
}
|
|
648
|
+
else if (Array.isArray(buffer)) {
|
|
649
|
+
buffer.push(typeof chunk === 'string' ? Buffer.from(chunk, encoding) : chunk);
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
buffer = Buffer.concat([buffer, typeof chunk === 'string' ? Buffer.from(chunk, encoding) : chunk]);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
buffer = typeof chunk === 'string' ? chunk : [chunk];
|
|
657
|
+
}
|
|
658
|
+
if (contentLength > 0) {
|
|
659
|
+
dataLength += Buffer.byteLength(chunk, encoding);
|
|
660
|
+
parent.updateProgress("request", progressId, dataLength, contentLength);
|
|
661
|
+
}
|
|
662
|
+
if (maxBufferSize > 0) {
|
|
663
|
+
if (contentLength === 0) {
|
|
664
|
+
dataLength += Buffer.byteLength(chunk, encoding);
|
|
665
|
+
}
|
|
666
|
+
if (dataLength > maxBufferSize) {
|
|
667
|
+
this.abortResponse();
|
|
668
|
+
this.rejectError((0, types_1.errorValue)("Size limit was exceeded", this.uri.toString()));
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
client.once('end', () => {
|
|
674
|
+
if (this.closed || this.aborted) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (this.timeout) {
|
|
678
|
+
clearTimeout(this.timeout);
|
|
679
|
+
}
|
|
680
|
+
if (this.outAbort) {
|
|
681
|
+
this.downloading.delete(this.outAbort);
|
|
682
|
+
}
|
|
683
|
+
this.closed = true;
|
|
684
|
+
const encoding = opts.encoding;
|
|
685
|
+
let result, messageUnit, titleBgColor;
|
|
686
|
+
if (buffer) {
|
|
687
|
+
if (Array.isArray(buffer)) {
|
|
688
|
+
buffer = Buffer.concat(buffer);
|
|
689
|
+
if (encoding) {
|
|
690
|
+
buffer = buffer.toString(encoding);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
dataLength = Buffer.byteLength(buffer, encoding);
|
|
694
|
+
if (mibsTime) {
|
|
695
|
+
messageUnit = (0, util_1.getTransferRate)(dataLength, (0, types_1.convertTime)(process.hrtime(mibsTime), false) * 1000);
|
|
696
|
+
}
|
|
697
|
+
if (contentLength > 0) {
|
|
698
|
+
parent.updateProgress("request", progressId, dataLength, dataLength);
|
|
699
|
+
}
|
|
700
|
+
if (typeof buffer === 'string') {
|
|
701
|
+
if (buffer.startsWith('\uFEFF') && encoding !== 'utf16le' && encoding !== 'utf-16le') {
|
|
702
|
+
buffer = buffer.substring(1);
|
|
703
|
+
}
|
|
704
|
+
if (config.outFormat) {
|
|
705
|
+
const { out: format, parser } = config.outFormat;
|
|
706
|
+
let packageName;
|
|
707
|
+
try {
|
|
708
|
+
switch (format) {
|
|
709
|
+
case 'yaml':
|
|
710
|
+
result = yaml.load(buffer, parser);
|
|
711
|
+
break;
|
|
712
|
+
case 'json5':
|
|
713
|
+
result = require(packageName = 'json5').parse(buffer);
|
|
714
|
+
break;
|
|
715
|
+
case 'xml':
|
|
716
|
+
result = new (require(packageName = 'fast-xml-parser').XMLParser)(parser).parse(buffer);
|
|
717
|
+
break;
|
|
718
|
+
case 'toml':
|
|
719
|
+
result = require(packageName = 'toml').parse(buffer);
|
|
720
|
+
break;
|
|
721
|
+
default:
|
|
722
|
+
result = JSON.parse(buffer);
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
if (!(0, types_1.isObject)(result)) {
|
|
726
|
+
result = null;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
catch (err) {
|
|
730
|
+
if (this.status && !(packageName && instance.checkPackage(err, packageName))) {
|
|
731
|
+
instance.writeFail(["Unable to parse URI response", format], err, 1024);
|
|
732
|
+
}
|
|
733
|
+
result = null;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (result === undefined) {
|
|
738
|
+
result = buffer;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
if (contentLength > 0) {
|
|
743
|
+
parent.updateProgress("request", progressId, 0, contentLength);
|
|
744
|
+
}
|
|
745
|
+
if (enabled && instance.readExpect === 'always') {
|
|
746
|
+
this.rejectError("No data received");
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
result = encoding && !pipeline ? '' : null;
|
|
750
|
+
titleBgColor = 'bgBlue';
|
|
751
|
+
}
|
|
752
|
+
if (log) {
|
|
753
|
+
instance.writeTimeProcess('HTTP' + config.httpVersion, config.statusMessage || this.uri.toString(), this.startTime, { type: 1024, queue: !!parent, titleBgColor, messageUnit, messageUnitMinWidth: 9, delayTime, bypassLog: LOG_STDOUT });
|
|
754
|
+
}
|
|
755
|
+
this.promise[0](result);
|
|
756
|
+
});
|
|
757
|
+
config.host.success(this.httpVersion);
|
|
758
|
+
}
|
|
759
|
+
redirectResponse(statusCode, location) {
|
|
760
|
+
this.abortResponse();
|
|
761
|
+
if (location) {
|
|
762
|
+
if (this.config.follow_redirect === false) {
|
|
763
|
+
this.rejectError(this.formatStatus(statusCode, "Follow redirect was disabled"));
|
|
764
|
+
}
|
|
765
|
+
else if (++this.redirects <= this.redirectLimit) {
|
|
766
|
+
this.setConfig(Request.fromURL(this.config.url, location));
|
|
767
|
+
this.init();
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
this.rejectError(this.formatStatus(statusCode, "Exceeded redirect limit"));
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
this.rejectError(this.formatStatus(statusCode, "Missing redirect location"));
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
errorResponse(err) {
|
|
778
|
+
this.abortResponse();
|
|
779
|
+
if (Fetch.wasAborted(err)) {
|
|
780
|
+
this.rejectError(err);
|
|
781
|
+
}
|
|
782
|
+
else if ((0, util_1.checkRetryable)(err) && ++this.retries <= this.retryLimit) {
|
|
783
|
+
if (Fetch.isConnectionTimeout(err)) {
|
|
784
|
+
this.retryTimeout();
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
setTimeout(() => {
|
|
788
|
+
this.init();
|
|
789
|
+
}, this.retryWait);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
this.config.host.error(this.httpVersion);
|
|
794
|
+
this.rejectError(err);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
retryResponse(statusCode, retryAfter) {
|
|
798
|
+
this.abortResponse();
|
|
799
|
+
if (retryAfter && this.retryAfter > 0) {
|
|
800
|
+
let offset = +retryAfter || new Date(retryAfter);
|
|
801
|
+
if (offset instanceof Date) {
|
|
802
|
+
offset = Math.max(0, offset.getTime() - Date.now());
|
|
803
|
+
}
|
|
804
|
+
else {
|
|
805
|
+
offset *= 1000;
|
|
806
|
+
}
|
|
807
|
+
if (offset > 0) {
|
|
808
|
+
if (offset <= this.retryAfter) {
|
|
809
|
+
this.sendWarning(`Retry After (${retryAfter})`);
|
|
810
|
+
setTimeout(() => {
|
|
811
|
+
this.init();
|
|
812
|
+
}, offset);
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
this.rejectError(this.formatStatus(statusCode));
|
|
816
|
+
}
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
this.sendWarning(this.formatRetry(Request.fromStatusCode(statusCode)));
|
|
821
|
+
if ((0, util_1.isRetryable)(statusCode, true)) {
|
|
822
|
+
process.nextTick(this.init.bind(this));
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
setTimeout(() => {
|
|
826
|
+
this.init();
|
|
827
|
+
}, this.retryWait);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
isRetry(value) {
|
|
831
|
+
return (0, util_1.isRetryable)(value) && ++this.retries <= this.retryLimit;
|
|
832
|
+
}
|
|
833
|
+
retryTimeout() {
|
|
834
|
+
this.sendWarning(this.formatRetry("HTTP connection timeout"));
|
|
835
|
+
this.init();
|
|
836
|
+
}
|
|
837
|
+
rejectError(err) {
|
|
838
|
+
if (this.timeout) {
|
|
839
|
+
clearTimeout(this.timeout);
|
|
840
|
+
}
|
|
841
|
+
if (!this.closed) {
|
|
842
|
+
this.closed = true;
|
|
843
|
+
this.cleanup();
|
|
844
|
+
this.promise[1](typeof err === 'string' ? new Error(err) : err);
|
|
845
|
+
}
|
|
846
|
+
if (this.outAbort) {
|
|
847
|
+
this.downloading.delete(this.outAbort);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
sendWarning(message) {
|
|
851
|
+
if (this.status) {
|
|
852
|
+
const { host, url } = this.config;
|
|
853
|
+
this.instance.formatMessage(1024, 'HTTP' + this.httpVersion, [message, host.origin], url.toString(), { ...module_1.LOG_STYLE_WARN });
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
formatStatus(value, hint) {
|
|
857
|
+
return value + ': ' + (hint || Request.fromStatusCode(value)) + ` (${this.uri.toString()})`;
|
|
858
|
+
}
|
|
859
|
+
formatNgFlags(value, statusCode, location) {
|
|
860
|
+
return location ? `Using HTTP 1.1 for URL redirect (${location})` : this.formatStatus(statusCode, value ? 'NGHTTP2 Error ' + value : '');
|
|
861
|
+
}
|
|
862
|
+
formatRetry(message) {
|
|
863
|
+
return message + ` (${this.retries} / ${this.retryLimit})`;
|
|
864
|
+
}
|
|
865
|
+
cleanup() {
|
|
866
|
+
if ((0, types_1.isString)(this.pipeTo) && this.outStream) {
|
|
867
|
+
(0, util_1.cleanupStream)(this.outStream, this.pipeTo);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
get destroyed() {
|
|
871
|
+
return this.client.destroyed || this.httpVersion === 2 && this.client.aborted;
|
|
872
|
+
}
|
|
873
|
+
get downloading() {
|
|
874
|
+
return this.instance[kDownloading];
|
|
875
|
+
}
|
|
876
|
+
get httpVersion() {
|
|
877
|
+
return this.config.httpVersion;
|
|
878
|
+
}
|
|
879
|
+
get outAbort() {
|
|
880
|
+
return this.config.outAbort;
|
|
881
|
+
}
|
|
882
|
+
get pipeTo() {
|
|
883
|
+
return this.opts.pipeTo;
|
|
884
|
+
}
|
|
885
|
+
get retryLimit() {
|
|
886
|
+
return this.settings.retryLimit;
|
|
887
|
+
}
|
|
888
|
+
get retryWait() {
|
|
889
|
+
return this.settings.retryWait;
|
|
890
|
+
}
|
|
891
|
+
get retryAfter() {
|
|
892
|
+
return this.settings.retryAfter;
|
|
893
|
+
}
|
|
894
|
+
get redirectLimit() {
|
|
895
|
+
return this.settings.redirectLimit;
|
|
896
|
+
}
|
|
897
|
+
get settings() {
|
|
898
|
+
return this.instance['_config'];
|
|
899
|
+
}
|
|
900
|
+
}
|
|
325
901
|
class Request extends module_1 {
|
|
326
902
|
static async purgeMemory(percent = 1, limit = 0, parent) {
|
|
327
903
|
if (percent >= 1) {
|
|
328
|
-
resetHttpHost();
|
|
904
|
+
resetHttpHost(0);
|
|
329
905
|
clearDnsLookup();
|
|
330
906
|
}
|
|
331
907
|
else if (percent === 0) {
|
|
@@ -341,16 +917,8 @@ class Request extends module_1 {
|
|
|
341
917
|
return false;
|
|
342
918
|
}
|
|
343
919
|
const { request, download } = settings;
|
|
344
|
-
if (download
|
|
345
|
-
let { bin, exec, update_status, max_concurrent_downloads, max_connection_per_server, bt_stop_timeout, bt_tracker_connect_timeout, bt_tracker_timeout, min_split_size, disk_cache, lowest_speed_limit, always_resume, file_allocation, conf_path } = download.aria2;
|
|
346
|
-
const parseSize = (value, zero) => {
|
|
347
|
-
if (zero && (value === '0' || value === 0)) {
|
|
348
|
-
return 0;
|
|
349
|
-
}
|
|
350
|
-
if ((0, types_1.isString)(value) && /^\d+[KM]$/.test(value = value.trim().toUpperCase())) {
|
|
351
|
-
return value;
|
|
352
|
-
}
|
|
353
|
-
};
|
|
920
|
+
if (download?.aria2) {
|
|
921
|
+
let { bin, exec, update_status, max_concurrent_downloads, max_connection_per_server, check_integrity, bt_stop_timeout, bt_tracker_connect_timeout, bt_tracker_timeout, min_split_size, disk_cache, lowest_speed_limit, always_resume, file_allocation, conf_path } = download.aria2;
|
|
354
922
|
if (bin === false) {
|
|
355
923
|
ARIA2.BIN = '';
|
|
356
924
|
}
|
|
@@ -375,6 +943,9 @@ class Request extends module_1 {
|
|
|
375
943
|
if ((max_concurrent_downloads = (0, util_1.asInt)(max_concurrent_downloads)) > 0) {
|
|
376
944
|
ARIA2.MAX_CONCURRENT_DOWNLOADS = max_concurrent_downloads;
|
|
377
945
|
}
|
|
946
|
+
if (typeof check_integrity === 'boolean') {
|
|
947
|
+
ARIA2.CHECK_INTEGRITY = check_integrity;
|
|
948
|
+
}
|
|
378
949
|
if ((max_connection_per_server = (0, util_1.asInt)(max_connection_per_server)) > 0) {
|
|
379
950
|
ARIA2.MAX_CONNECTION_PER_SERVER = max_connection_per_server;
|
|
380
951
|
}
|
|
@@ -387,7 +958,7 @@ class Request extends module_1 {
|
|
|
387
958
|
if ((bt_tracker_timeout = (0, util_1.asInt)(bt_tracker_timeout)) > 0) {
|
|
388
959
|
ARIA2.BT_TRACKER_TIMEOUT = bt_tracker_timeout;
|
|
389
960
|
}
|
|
390
|
-
if (min_split_size = parseSize(min_split_size)) {
|
|
961
|
+
if (min_split_size = parseSize(min_split_size, false)) {
|
|
391
962
|
ARIA2.MIN_SPLIT_SIZE = min_split_size;
|
|
392
963
|
}
|
|
393
964
|
if ((disk_cache = parseSize(disk_cache, true)) !== undefined) {
|
|
@@ -414,7 +985,7 @@ class Request extends module_1 {
|
|
|
414
985
|
ARIA2.CONF_PATH = conf_path;
|
|
415
986
|
}
|
|
416
987
|
}
|
|
417
|
-
if (
|
|
988
|
+
if (request) {
|
|
418
989
|
let { read_timeout, agent, use, headers, certs } = request, agent_timeout;
|
|
419
990
|
if ((read_timeout = (0, util_1.fromSeconds)(read_timeout)) >= 0) {
|
|
420
991
|
READ_TIMEOUT = read_timeout;
|
|
@@ -436,7 +1007,7 @@ class Request extends module_1 {
|
|
|
436
1007
|
}
|
|
437
1008
|
this.defineHttpAgent({ keepAlive: keep_alive, timeout: agent_timeout });
|
|
438
1009
|
}
|
|
439
|
-
if (
|
|
1010
|
+
if (use) {
|
|
440
1011
|
let { http_version, accept_encoding } = use;
|
|
441
1012
|
if (typeof accept_encoding === 'boolean') {
|
|
442
1013
|
ACCEPT_ENCODING = accept_encoding;
|
|
@@ -448,10 +1019,10 @@ class Request extends module_1 {
|
|
|
448
1019
|
break;
|
|
449
1020
|
}
|
|
450
1021
|
}
|
|
451
|
-
if ((0, types_1.
|
|
1022
|
+
if ((0, types_1.isPlainObject)(headers)) {
|
|
452
1023
|
setOutgoingHeaders(HTTP.HEADERS, headers);
|
|
453
1024
|
}
|
|
454
|
-
if ((0, types_1.
|
|
1025
|
+
if ((0, types_1.isPlainObject)(certs)) {
|
|
455
1026
|
[TLS.TEXT, TLS.FILE] = validateCerts(certs);
|
|
456
1027
|
}
|
|
457
1028
|
HTTP.PROXY = getProxySettings(request, agent_timeout);
|
|
@@ -463,6 +1034,7 @@ class Request extends module_1 {
|
|
|
463
1034
|
LOG_TIMEFORMAT = time_format;
|
|
464
1035
|
break;
|
|
465
1036
|
}
|
|
1037
|
+
host_1.defineHostConfig(request);
|
|
466
1038
|
}
|
|
467
1039
|
LOG_HTTP = this.hasLogType(1024);
|
|
468
1040
|
LOG_TIMEPROCESS = this.hasLogType(256);
|
|
@@ -470,7 +1042,7 @@ class Request extends module_1 {
|
|
|
470
1042
|
return true;
|
|
471
1043
|
}
|
|
472
1044
|
static readCACert(value, cache) {
|
|
473
|
-
return (
|
|
1045
|
+
return (this.isCert(value) ? value : this.readText(value, cache)).trim();
|
|
474
1046
|
}
|
|
475
1047
|
static readTLSCert(value, cache) {
|
|
476
1048
|
return this.readCACert(value, cache);
|
|
@@ -479,7 +1051,7 @@ class Request extends module_1 {
|
|
|
479
1051
|
return this.readCACert(value, cache);
|
|
480
1052
|
}
|
|
481
1053
|
static isCert(value) {
|
|
482
|
-
return
|
|
1054
|
+
return typeof value === 'string' && (value = value.trim()).length > 0 && REGEXP_PEMCERT.test(value);
|
|
483
1055
|
}
|
|
484
1056
|
static fromURL(url, value) {
|
|
485
1057
|
if (this.isURL(value)) {
|
|
@@ -489,7 +1061,8 @@ class Request extends module_1 {
|
|
|
489
1061
|
return url.protocol + '//' + (auth && (auth + '@')) + url.hostname + (url.port ? ':' + url.port : '') + (value.startsWith('/') ? '' : '/') + value;
|
|
490
1062
|
}
|
|
491
1063
|
static fromStatusCode(value) {
|
|
492
|
-
|
|
1064
|
+
value = +value;
|
|
1065
|
+
if (value < 200) {
|
|
493
1066
|
switch (value) {
|
|
494
1067
|
case 100:
|
|
495
1068
|
return 'Continue';
|
|
@@ -698,14 +1271,15 @@ class Request extends module_1 {
|
|
|
698
1271
|
this[_b] = null;
|
|
699
1272
|
this[_c] = null;
|
|
700
1273
|
this[_d] = null;
|
|
701
|
-
this[_e] =
|
|
1274
|
+
this[_e] = null;
|
|
702
1275
|
this[_f] = Object.create(null);
|
|
703
|
-
this[_g] =
|
|
704
|
-
this[_h] =
|
|
1276
|
+
this[_g] = Object.create(null);
|
|
1277
|
+
this[_h] = [{}, {}];
|
|
705
1278
|
this[_j] = null;
|
|
706
|
-
this[_k] =
|
|
707
|
-
this[_l] =
|
|
708
|
-
this[_m] =
|
|
1279
|
+
this[_k] = null;
|
|
1280
|
+
this[_l] = new Set();
|
|
1281
|
+
this[_m] = {};
|
|
1282
|
+
this[_o] = [Object.create(null)];
|
|
709
1283
|
if ((0, types_1.isPlainObject)(data)) {
|
|
710
1284
|
const { headers, read_timeout, agent, use, connect, certs } = data;
|
|
711
1285
|
const timeout = (0, util_1.fromSeconds)(data.timeout);
|
|
@@ -764,11 +1338,11 @@ class Request extends module_1 {
|
|
|
764
1338
|
log.length = 0;
|
|
765
1339
|
}
|
|
766
1340
|
else {
|
|
767
|
-
if (log.length && !this.host?.aborted) {
|
|
1341
|
+
if (log.length > 0 && !this.host?.aborted) {
|
|
768
1342
|
const output = [];
|
|
769
1343
|
let count = 0;
|
|
770
1344
|
this[kConnectHttp].forEach((protocol, index) => {
|
|
771
|
-
var
|
|
1345
|
+
var _p;
|
|
772
1346
|
const title = 'HTTP' + (index + 1);
|
|
773
1347
|
for (const origin in protocol) {
|
|
774
1348
|
const value = protocol[origin];
|
|
@@ -778,7 +1352,7 @@ class Request extends module_1 {
|
|
|
778
1352
|
if (item[1] === title && Array.isArray(item[2]) && item[2][0].startsWith(origin)) {
|
|
779
1353
|
item[1] = '';
|
|
780
1354
|
item[2][0] = item[2][0].substring(origin.length);
|
|
781
|
-
(
|
|
1355
|
+
(_p = item[4]).titleBgColor && (_p.titleBgColor = undefined);
|
|
782
1356
|
item[4].titleJustify = 'right';
|
|
783
1357
|
args.push(item);
|
|
784
1358
|
log.splice(i--, 1);
|
|
@@ -796,8 +1370,7 @@ class Request extends module_1 {
|
|
|
796
1370
|
return b[2] - a[2];
|
|
797
1371
|
});
|
|
798
1372
|
const width = count.toString().length;
|
|
799
|
-
|
|
800
|
-
const [title, origin, downloads, messages] = item;
|
|
1373
|
+
for (const [title, origin, downloads, messages] of output) {
|
|
801
1374
|
const options = { ...title === 'HTTP1' ? module_1.LOG_STYLE_NOTICE : module_1.LOG_STYLE_INFO };
|
|
802
1375
|
if (messages.length === 1) {
|
|
803
1376
|
const message = messages[0];
|
|
@@ -808,12 +1381,12 @@ class Request extends module_1 {
|
|
|
808
1381
|
}
|
|
809
1382
|
else {
|
|
810
1383
|
this.formatMessage(1024, title, [origin, 'downloads: ' + downloads.toString().padStart(width)], '', options);
|
|
811
|
-
|
|
1384
|
+
for (const args of messages) {
|
|
812
1385
|
args[4].titleIndent = true;
|
|
813
1386
|
this.formatMessage(...args);
|
|
814
|
-
}
|
|
1387
|
+
}
|
|
815
1388
|
}
|
|
816
|
-
}
|
|
1389
|
+
}
|
|
817
1390
|
}
|
|
818
1391
|
}
|
|
819
1392
|
super.flushLog();
|
|
@@ -829,7 +1402,9 @@ class Request extends module_1 {
|
|
|
829
1402
|
}
|
|
830
1403
|
}
|
|
831
1404
|
abort(reason) {
|
|
832
|
-
this[kDownloading]
|
|
1405
|
+
for (const item of this[kDownloading]) {
|
|
1406
|
+
item.abort(reason);
|
|
1407
|
+
}
|
|
833
1408
|
this.close();
|
|
834
1409
|
if (!this[kSingleton]) {
|
|
835
1410
|
super.abort(reason);
|
|
@@ -929,11 +1504,11 @@ class Request extends module_1 {
|
|
|
929
1504
|
setDnsCache(hostname, this[kConnectDns][hostname] = [{ address, family }], timeout);
|
|
930
1505
|
}
|
|
931
1506
|
lookupDns(hostname) {
|
|
932
|
-
var
|
|
1507
|
+
var _p;
|
|
933
1508
|
const resolved = this[kConnectDns][hostname] || DNS.CACHE[hostname];
|
|
934
1509
|
if (resolved) {
|
|
935
1510
|
return (...args) => {
|
|
936
|
-
if (
|
|
1511
|
+
if (SUPPORTED_NODE20) {
|
|
937
1512
|
args[2](null, resolved);
|
|
938
1513
|
}
|
|
939
1514
|
else {
|
|
@@ -941,36 +1516,20 @@ class Request extends module_1 {
|
|
|
941
1516
|
}
|
|
942
1517
|
};
|
|
943
1518
|
}
|
|
944
|
-
const pending = (
|
|
1519
|
+
const pending = (_p = this[kPendingDns])[hostname] || (_p[hostname] = []);
|
|
945
1520
|
return (value, options, callback) => {
|
|
946
1521
|
if (pending.push(callback) === 1) {
|
|
947
|
-
const configure = (family) => family === 0 ? options : { family, hints: family === 6 ? dns.V4MAPPED : 0 };
|
|
948
|
-
const success = (connected) => {
|
|
949
|
-
setDnsCache(value, this[kConnectDns][value] = connected);
|
|
950
|
-
if (SUPPORT_NODEJS20) {
|
|
951
|
-
pending.forEach(cb => cb(null, connected));
|
|
952
|
-
}
|
|
953
|
-
else {
|
|
954
|
-
const { address, family } = connected[0];
|
|
955
|
-
pending.forEach(cb => cb(null, address, family));
|
|
956
|
-
}
|
|
957
|
-
pending.length = 0;
|
|
958
|
-
};
|
|
959
|
-
const failed = (err) => {
|
|
960
|
-
pending.forEach(cb => SUPPORT_NODEJS20 ? cb(err, []) : cb(err, '', 0));
|
|
961
|
-
pending.length = 0;
|
|
962
|
-
};
|
|
963
1522
|
let ipVersion = this.ipVersion;
|
|
964
|
-
dns.lookup(value,
|
|
1523
|
+
dns.lookup(value, configureDns(ipVersion, options), (err, address, family) => {
|
|
965
1524
|
if (!err) {
|
|
966
|
-
|
|
1525
|
+
successDns(this, value, pending, typeof address === 'string' ? [{ address, family }] : address);
|
|
967
1526
|
return;
|
|
968
1527
|
}
|
|
969
1528
|
switch (err.code) {
|
|
970
1529
|
case 'ENOTFOUND':
|
|
971
1530
|
case 'EBADNAME':
|
|
972
1531
|
case 'ENODATA':
|
|
973
|
-
|
|
1532
|
+
failedDns(err, pending);
|
|
974
1533
|
return;
|
|
975
1534
|
case 'ETIMEOUT':
|
|
976
1535
|
case 'ECONNREFUSED':
|
|
@@ -980,18 +1539,18 @@ class Request extends module_1 {
|
|
|
980
1539
|
break;
|
|
981
1540
|
default:
|
|
982
1541
|
if (ipVersion === 0) {
|
|
983
|
-
|
|
1542
|
+
failedDns(err, pending);
|
|
984
1543
|
return;
|
|
985
1544
|
}
|
|
986
1545
|
ipVersion = ipVersion === 4 ? 6 : 4;
|
|
987
1546
|
break;
|
|
988
1547
|
}
|
|
989
|
-
dns.lookup(value,
|
|
1548
|
+
dns.lookup(value, configureDns(ipVersion, options), (err, address, family) => {
|
|
990
1549
|
if (!err) {
|
|
991
|
-
|
|
1550
|
+
successDns(this, value, pending, typeof address === 'string' ? [{ address, family }] : address);
|
|
992
1551
|
}
|
|
993
1552
|
else {
|
|
994
|
-
|
|
1553
|
+
failedDns(err, pending);
|
|
995
1554
|
}
|
|
996
1555
|
});
|
|
997
1556
|
});
|
|
@@ -1005,12 +1564,10 @@ class Request extends module_1 {
|
|
|
1005
1564
|
if (!include && !exclude && !localhost) {
|
|
1006
1565
|
return proxy;
|
|
1007
1566
|
}
|
|
1008
|
-
if ((0, types_1.isArray)(include)) {
|
|
1009
|
-
return
|
|
1010
|
-
}
|
|
1011
|
-
if (Array.isArray(exclude) && !exclude.some(value => uri.startsWith(value))) {
|
|
1012
|
-
return proxy;
|
|
1567
|
+
if ((0, types_1.isArray)(include) && !include.some(value => REGEXP_GLOBWITHIN.test(value) ? pm.isMatch(uri, value) : uri.startsWith(value)) || (0, types_1.isArray)(exclude) && exclude.some(value => REGEXP_GLOBWITHIN.test(value) ? pm.isMatch(uri, value) : uri.startsWith(value))) {
|
|
1568
|
+
return;
|
|
1013
1569
|
}
|
|
1570
|
+
return proxy;
|
|
1014
1571
|
}
|
|
1015
1572
|
}
|
|
1016
1573
|
statusOn(code, globUrl = '*', callback) {
|
|
@@ -1054,12 +1611,7 @@ class Request extends module_1 {
|
|
|
1054
1611
|
return Promise.reject((0, types_1.errorMessage)("aria2", "Binary not found"));
|
|
1055
1612
|
}
|
|
1056
1613
|
if (typeof uri === 'string' && module_1.isURL(uri)) {
|
|
1057
|
-
|
|
1058
|
-
uri = new URL(uri);
|
|
1059
|
-
}
|
|
1060
|
-
catch (err) {
|
|
1061
|
-
return Promise.reject(err);
|
|
1062
|
-
}
|
|
1614
|
+
uri = new URL(uri);
|
|
1063
1615
|
}
|
|
1064
1616
|
let pathname, headers, binOpts, silent;
|
|
1065
1617
|
if (options) {
|
|
@@ -1070,7 +1622,7 @@ class Request extends module_1 {
|
|
|
1070
1622
|
({ pathname, headers, binOpts, silent } = options);
|
|
1071
1623
|
if ((0, types_1.isArray)(binOpts)) {
|
|
1072
1624
|
let next = false;
|
|
1073
|
-
binOpts = binOpts.filter(opt => !((0, types_1.isString)(opt) && /^-[a-z]
|
|
1625
|
+
binOpts = binOpts.filter(opt => !((0, types_1.isString)(opt) && /^-[a-z].*$/i.test(opt.trim()))).map((opt) => {
|
|
1074
1626
|
if (next) {
|
|
1075
1627
|
if (!module_1.asString(opt).startsWith('--')) {
|
|
1076
1628
|
return [];
|
|
@@ -1108,7 +1660,7 @@ class Request extends module_1 {
|
|
|
1108
1660
|
return [opt.toString()];
|
|
1109
1661
|
default:
|
|
1110
1662
|
if ((0, types_1.isArray)(opt)) {
|
|
1111
|
-
return opt.filter(item => (0, types_1.isString)(item)).map(
|
|
1663
|
+
return opt.filter(item => (0, types_1.isString)(item)).map(item => module_1.sanitizeArgs(item));
|
|
1112
1664
|
}
|
|
1113
1665
|
break;
|
|
1114
1666
|
}
|
|
@@ -1135,12 +1687,8 @@ class Request extends module_1 {
|
|
|
1135
1687
|
if (uri instanceof URL) {
|
|
1136
1688
|
({ protocol, origin, username, password, href: uri } = uri);
|
|
1137
1689
|
}
|
|
1138
|
-
const escapeQuote = (value) => {
|
|
1139
|
-
value = value.replace(/"/g, '\\"');
|
|
1140
|
-
return PLATFORM_WIN32 ? value : value.replace(/[ $`]/g, capture => (capture !== ' ' ? '\\' : '') + '\\' + capture);
|
|
1141
|
-
};
|
|
1142
1690
|
const init = [
|
|
1143
|
-
`--dir="${
|
|
1691
|
+
`--dir="${escapeShellQuote(pathname)}"`,
|
|
1144
1692
|
'--download-result=full',
|
|
1145
1693
|
'--follow-torrent=mem',
|
|
1146
1694
|
'--follow-metalink=mem',
|
|
@@ -1188,13 +1736,15 @@ class Request extends module_1 {
|
|
|
1188
1736
|
'--file-allocation=' + ARIA2.FILE_ALLOCATION,
|
|
1189
1737
|
'--max-tries=' + (retryLimit + 1)
|
|
1190
1738
|
];
|
|
1191
|
-
const ignoreOpt = (...values) => !binOpts?.some(item => values.includes(item));
|
|
1192
1739
|
if (ARIA2.MAX_CONCURRENT_DOWNLOADS) {
|
|
1193
1740
|
opts.push('--max-concurrent-downloads=' + ARIA2.MAX_CONCURRENT_DOWNLOADS);
|
|
1194
1741
|
}
|
|
1195
1742
|
if (ARIA2.MAX_CONNECTION_PER_SERVER) {
|
|
1196
1743
|
opts.push('--max-connection-per-server=' + ARIA2.MAX_CONNECTION_PER_SERVER);
|
|
1197
1744
|
}
|
|
1745
|
+
if (ARIA2.CHECK_INTEGRITY) {
|
|
1746
|
+
opts.push('--check-integrity=true');
|
|
1747
|
+
}
|
|
1198
1748
|
if (ARIA2.MIN_SPLIT_SIZE) {
|
|
1199
1749
|
opts.push('--min-split-size=' + ARIA2.MIN_SPLIT_SIZE);
|
|
1200
1750
|
}
|
|
@@ -1205,7 +1755,7 @@ class Request extends module_1 {
|
|
|
1205
1755
|
opts.push('--lowest-speed-limit=' + ARIA2.LOWEST_SPEED_LIMIT);
|
|
1206
1756
|
}
|
|
1207
1757
|
if (ARIA2.CONF_PATH) {
|
|
1208
|
-
opts.push(`--conf-path="${
|
|
1758
|
+
opts.push(`--conf-path="${escapeShellQuote(ARIA2.CONF_PATH)}"`);
|
|
1209
1759
|
}
|
|
1210
1760
|
if (!ARIA2.ALWAYS_RESUME) {
|
|
1211
1761
|
opts.push('--always-resume=false', '--keep-unfinished-download-result=false');
|
|
@@ -1223,7 +1773,7 @@ class Request extends module_1 {
|
|
|
1223
1773
|
if (this.agentTimeout === 0) {
|
|
1224
1774
|
opts.push('--enable-http-keep-alive=false');
|
|
1225
1775
|
}
|
|
1226
|
-
const baseHeaders = ignoreOpt('--header') && this.headersOf(uri);
|
|
1776
|
+
const baseHeaders = ignoreOpt(binOpts, '--header') && this.headersOf(uri);
|
|
1227
1777
|
if (headers || baseHeaders) {
|
|
1228
1778
|
if (baseHeaders) {
|
|
1229
1779
|
headers = headers ? { ...baseHeaders, ...headers } : baseHeaders;
|
|
@@ -1233,47 +1783,50 @@ class Request extends module_1 {
|
|
|
1233
1783
|
if (!Array.isArray(items)) {
|
|
1234
1784
|
items = [items.toString()];
|
|
1235
1785
|
}
|
|
1236
|
-
args.push(...items.map(value => `--header="${name}: ${
|
|
1786
|
+
args.push(...items.map(value => `--header="${name}: ${escapeShellQuote(value)}"`));
|
|
1237
1787
|
}
|
|
1238
1788
|
}
|
|
1239
1789
|
if (origin) {
|
|
1240
1790
|
const secure = this[kCerts]?.[1][origin] || this.host && TLS.FILE[origin];
|
|
1241
1791
|
if (secure) {
|
|
1242
|
-
if (secure.ca && ignoreOpt('--ca-certificate')) {
|
|
1243
|
-
args.push(`--ca-certificate="${
|
|
1792
|
+
if (secure.ca && ignoreOpt(binOpts, '--ca-certificate')) {
|
|
1793
|
+
args.push(`--ca-certificate="${escapeShellQuote(secure.ca)}"`);
|
|
1244
1794
|
}
|
|
1245
|
-
if (secure.cert && ignoreOpt('--certificate', '--private-key')) {
|
|
1246
|
-
args.push(`--certificate="${
|
|
1795
|
+
if (secure.cert && ignoreOpt(binOpts, '--certificate', '--private-key')) {
|
|
1796
|
+
args.push(`--certificate="${escapeShellQuote(secure.cert)}"`);
|
|
1247
1797
|
if (secure.key) {
|
|
1248
|
-
args.push(`--private-key="${
|
|
1798
|
+
args.push(`--private-key="${escapeShellQuote(secure.key)}"`);
|
|
1249
1799
|
}
|
|
1250
1800
|
}
|
|
1251
1801
|
}
|
|
1252
1802
|
}
|
|
1253
1803
|
switch (protocol = protocol.substring(0, protocol.length - 1)) {
|
|
1254
|
-
case 'ftp':
|
|
1255
1804
|
case 'http':
|
|
1256
|
-
case 'https':
|
|
1805
|
+
case 'https':
|
|
1806
|
+
if (ignoreOpt(binOpts, '--http-no-cache')) {
|
|
1807
|
+
args.push('--http-no-cache=true');
|
|
1808
|
+
}
|
|
1809
|
+
case 'ftp': {
|
|
1257
1810
|
const prefix = protocol === 'https' ? 'http' : protocol;
|
|
1258
|
-
if (ignoreOpt(`--${prefix}-user`, `--${prefix}-passwd`)) {
|
|
1811
|
+
if (ignoreOpt(binOpts, `--${prefix}-user`, `--${prefix}-passwd`)) {
|
|
1259
1812
|
if (username) {
|
|
1260
|
-
args.push(`--${prefix}-user="${
|
|
1813
|
+
args.push(`--${prefix}-user="${escapeShellQuote(decodeURIComponent(username))}"`);
|
|
1261
1814
|
}
|
|
1262
1815
|
if (password) {
|
|
1263
|
-
args.push(`--${prefix}-passwd="${
|
|
1816
|
+
args.push(`--${prefix}-passwd="${escapeShellQuote(decodeURIComponent(password))}"`);
|
|
1264
1817
|
}
|
|
1265
1818
|
}
|
|
1266
1819
|
const proxy = this.proxyOf(uri);
|
|
1267
|
-
if (proxy && ignoreOpt(`--${protocol}-proxy-user`, `--${protocol}-proxy-passwd`)) {
|
|
1820
|
+
if (proxy && ignoreOpt(binOpts, `--${protocol}-proxy-user`, `--${protocol}-proxy-passwd`)) {
|
|
1268
1821
|
({ origin, username, password } = proxy.host);
|
|
1269
|
-
if (ignoreOpt(`--${protocol}-proxy`)) {
|
|
1822
|
+
if (ignoreOpt(binOpts, `--${protocol}-proxy`)) {
|
|
1270
1823
|
args.push(`--${protocol}-proxy="${origin}"`);
|
|
1271
1824
|
}
|
|
1272
1825
|
if (username) {
|
|
1273
|
-
args.push(`--${protocol}-proxy-user="${
|
|
1826
|
+
args.push(`--${protocol}-proxy-user="${escapeShellQuote(decodeURIComponent(username))}"`);
|
|
1274
1827
|
}
|
|
1275
1828
|
if (password) {
|
|
1276
|
-
args.push(`--${protocol}-proxy-passwd="${
|
|
1829
|
+
args.push(`--${protocol}-proxy-passwd="${escapeShellQuote(decodeURIComponent(password))}"`);
|
|
1277
1830
|
}
|
|
1278
1831
|
}
|
|
1279
1832
|
break;
|
|
@@ -1291,7 +1844,7 @@ class Request extends module_1 {
|
|
|
1291
1844
|
}
|
|
1292
1845
|
args = binOpts.concat(args);
|
|
1293
1846
|
}
|
|
1294
|
-
if (args.length) {
|
|
1847
|
+
if (args.length > 0) {
|
|
1295
1848
|
if (module_1.hasLogType(32768)) {
|
|
1296
1849
|
this.formatMessage(32768, 'ARIA2', ARIA2.BIN, args.join(' '), { ...module_1.LOG_STYLE_WARN });
|
|
1297
1850
|
}
|
|
@@ -1299,7 +1852,7 @@ class Request extends module_1 {
|
|
|
1299
1852
|
this.addLog(types_1.STATUS_TYPE.INFO, path.basename(ARIA2.BIN) + ' ' + args.join(' '), "aria2");
|
|
1300
1853
|
}
|
|
1301
1854
|
}
|
|
1302
|
-
opts.push(`"${
|
|
1855
|
+
opts.push(`"${escapeShellQuote(uri)}"`);
|
|
1303
1856
|
args = args.concat(init, opts);
|
|
1304
1857
|
const startTime = Date.now();
|
|
1305
1858
|
let out = '', message = '', aborted;
|
|
@@ -1340,29 +1893,31 @@ class Request extends module_1 {
|
|
|
1340
1893
|
break;
|
|
1341
1894
|
}
|
|
1342
1895
|
case 'ERR':
|
|
1343
|
-
fs.unlink(file, err =>
|
|
1896
|
+
fs.unlink(file, err => {
|
|
1897
|
+
if (err && !silent && !this[kSingleton]) {
|
|
1898
|
+
this.writeFail(["Unable to delete file", path.basename(file)], err, { type: 32, fatal: false });
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1344
1901
|
break;
|
|
1345
1902
|
}
|
|
1346
1903
|
}
|
|
1347
1904
|
}
|
|
1348
|
-
if (result.length && !silent && LOG_HTTP && LOG_TIMEPROCESS) {
|
|
1905
|
+
if (result.length > 0 && !silent && LOG_HTTP && LOG_TIMEPROCESS) {
|
|
1349
1906
|
this.writeTimeProcess("aria2", uri, startTime, { type: 1024, queue: true, messageUnit, messageUnitMinWidth: 9, bypassLog: true });
|
|
1350
1907
|
}
|
|
1351
|
-
this.addLog(result.length ? types_1.STATUS_TYPE.INFO : types_1.STATUS_TYPE.ERROR, out, currentTime, currentTime - startTime, "aria2", uri);
|
|
1908
|
+
this.addLog(result.length > 0 ? types_1.STATUS_TYPE.INFO : types_1.STATUS_TYPE.ERROR, out, currentTime, currentTime - startTime, "aria2", uri);
|
|
1352
1909
|
resolve(result);
|
|
1353
1910
|
})
|
|
1354
|
-
.on('error', err =>
|
|
1911
|
+
.on('error', err => {
|
|
1912
|
+
errorResponse(pid, err);
|
|
1913
|
+
});
|
|
1355
1914
|
stdout.setEncoding('utf-8').on('data', (value) => out += value);
|
|
1356
1915
|
stderr.setEncoding('utf-8').on('data', (value) => message += value);
|
|
1357
1916
|
if (pid !== undefined && module_1.isFile(uri, 'torrent')) {
|
|
1358
1917
|
if (!ARIA2.PID_TIMER) {
|
|
1359
|
-
const clearTimer = () => {
|
|
1360
|
-
clearInterval(ARIA2.PID_TIMER);
|
|
1361
|
-
ARIA2.PID_TIMER = null;
|
|
1362
|
-
};
|
|
1363
1918
|
ARIA2.PID_TIMER = setInterval(() => {
|
|
1364
1919
|
if (ARIA2.PID_QUEUE.length === 0) {
|
|
1365
|
-
|
|
1920
|
+
resetAria2();
|
|
1366
1921
|
return;
|
|
1367
1922
|
}
|
|
1368
1923
|
for (const item of ARIA2.PID_QUEUE) {
|
|
@@ -1376,7 +1931,7 @@ class Request extends module_1 {
|
|
|
1376
1931
|
if (ARIA2.UPDATE_STATUS && !silent) {
|
|
1377
1932
|
const item = ARIA2.PID_QUEUE.shift();
|
|
1378
1933
|
if (!item) {
|
|
1379
|
-
|
|
1934
|
+
resetAria2();
|
|
1380
1935
|
return;
|
|
1381
1936
|
}
|
|
1382
1937
|
if ((!ARIA2.UPDATE_BROADCAST_ONLY || item[2]) && this.host?.logState !== 0) {
|
|
@@ -1388,7 +1943,7 @@ class Request extends module_1 {
|
|
|
1388
1943
|
const current = (0, types_1.getLogCurrent)();
|
|
1389
1944
|
progressBar = current?.type === 128 && current.title === "aria2";
|
|
1390
1945
|
}
|
|
1391
|
-
this.formatMessage(128, "aria2", ['Downloading...', (0, types_1.formatTime)(startTime, true)], (PLATFORM_WIN32 ? 'taskkill /f /pid' : 'kill') + ` ${item[0]} -> ` + item[1], { ...module_1.LOG_STYLE_INFO, progressBar, broadcastId: item[2] });
|
|
1946
|
+
this.formatMessage(128, "aria2", ['Downloading...', (0, types_1.formatTime)(startTime, true)], (module_1.PLATFORM_WIN32 ? 'taskkill /f /pid' : 'kill') + ` ${item[0]} -> ` + item[1], { ...module_1.LOG_STYLE_INFO, progressBar, broadcastId: item[2] });
|
|
1392
1947
|
}
|
|
1393
1948
|
ARIA2.PID_QUEUE.push(item);
|
|
1394
1949
|
}
|
|
@@ -1407,21 +1962,30 @@ class Request extends module_1 {
|
|
|
1407
1962
|
return this.get(uri, options);
|
|
1408
1963
|
}
|
|
1409
1964
|
opts(url, options) {
|
|
1410
|
-
var
|
|
1411
|
-
|
|
1965
|
+
var _p, _q, _r, _s;
|
|
1966
|
+
const base = this[kBaseURL];
|
|
1967
|
+
if (base) {
|
|
1968
|
+
if (typeof url === 'string') {
|
|
1969
|
+
url = new URL(url, base[0]);
|
|
1970
|
+
}
|
|
1971
|
+
if (!options?.base) {
|
|
1972
|
+
copySearchParams(url, base[0]);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
else if (typeof url === 'string') {
|
|
1412
1976
|
url = new URL(url);
|
|
1413
1977
|
}
|
|
1414
1978
|
let host;
|
|
1415
1979
|
if (this.host) {
|
|
1416
|
-
host = (
|
|
1980
|
+
host = (_p = HTTP.HOST)[_q = url.origin] || (_p[_q] = new host_1(url, HTTP.VERSION));
|
|
1417
1981
|
}
|
|
1418
1982
|
else {
|
|
1419
|
-
host = (
|
|
1983
|
+
host = (_r = this[kHostInfo])[_s = url.origin] || (_r[_s] = new host_1(url, this.httpVersion || 1));
|
|
1420
1984
|
}
|
|
1421
1985
|
return { ...options, host, url };
|
|
1422
1986
|
}
|
|
1423
1987
|
open(uri, options) {
|
|
1424
|
-
var
|
|
1988
|
+
var _p, _q;
|
|
1425
1989
|
let { host, url, httpVersion, method = 'GET', search, encoding, format, headers, postData, keepAlive, agentTimeout, socketPath, timeout = this._config.connectTimeout, outStream } = options;
|
|
1426
1990
|
const getting = method === 'GET';
|
|
1427
1991
|
const posting = method === 'POST';
|
|
@@ -1460,15 +2024,33 @@ class Request extends module_1 {
|
|
|
1460
2024
|
url = uri;
|
|
1461
2025
|
uri = url.toString();
|
|
1462
2026
|
}
|
|
2027
|
+
const base = this[kBaseURL];
|
|
1463
2028
|
if (!host) {
|
|
1464
|
-
({ host, url } = this.opts(url || uri));
|
|
2029
|
+
({ host, url } = this.opts(url || uri, { base: options.base }));
|
|
1465
2030
|
options.host = host;
|
|
1466
2031
|
options.url = url;
|
|
1467
2032
|
}
|
|
1468
2033
|
else if (!url) {
|
|
1469
|
-
|
|
2034
|
+
if (base) {
|
|
2035
|
+
url = new URL(uri, base[0]);
|
|
2036
|
+
if (!options.base) {
|
|
2037
|
+
copySearchParams(url, base[0]);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
else {
|
|
2041
|
+
url = new URL(uri);
|
|
2042
|
+
}
|
|
1470
2043
|
options.url = url;
|
|
1471
2044
|
}
|
|
2045
|
+
if (options.base) {
|
|
2046
|
+
this[kBaseURL] = [url, httpVersion, headers];
|
|
2047
|
+
}
|
|
2048
|
+
else if (base) {
|
|
2049
|
+
httpVersion ?? (httpVersion = base[1]);
|
|
2050
|
+
if (base[2]) {
|
|
2051
|
+
headers = { ...base[2], ...headers };
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
1472
2054
|
if (search) {
|
|
1473
2055
|
for (const param in search) {
|
|
1474
2056
|
url.searchParams.append(param, search[param]);
|
|
@@ -1479,18 +2061,18 @@ class Request extends module_1 {
|
|
|
1479
2061
|
const pathname = url.pathname + (socketPath ? '' : url.search);
|
|
1480
2062
|
const proxy = this.proxyOf(uri, localhost);
|
|
1481
2063
|
const version = this.httpVersion;
|
|
1482
|
-
let request, ca, cert, key, minVersion, baseHeaders = this.headersOf(uri);
|
|
2064
|
+
let request, ca, cert, key, ciphers, minVersion, baseHeaders = this.headersOf(uri);
|
|
1483
2065
|
if (getting && this.acceptEncoding && !localhost && !baseHeaders?.['accept-encoding']) {
|
|
1484
|
-
(
|
|
2066
|
+
(_p = (headers || (headers = {})))['accept-encoding'] || (_p['accept-encoding'] = 'gzip, deflate, br' + (LIB_ZSTD ? ', zstd' : ''));
|
|
1485
2067
|
}
|
|
1486
2068
|
if (secure) {
|
|
1487
2069
|
const certs = this[kCerts]?.[0][origin] || this.host && TLS.TEXT[origin];
|
|
1488
2070
|
if (certs) {
|
|
1489
|
-
({ ca, cert, key, version: minVersion } = certs);
|
|
2071
|
+
({ ca, cert, key, ciphers, version: minVersion } = certs);
|
|
1490
2072
|
}
|
|
1491
2073
|
}
|
|
1492
2074
|
if (!proxy && httpVersion !== 1 && ((httpVersion || host.version) === 2 && version !== 1 || secure && version === 2 && host.failed(2, true) === 0)) {
|
|
1493
|
-
request = ((
|
|
2075
|
+
request = ((_q = this[kSession][0])[origin] || (_q[origin] = http2.connect(origin, { lookup: this.lookupDns(hostname), ca, cert, key, ciphers, minVersion, settings: localhost ? { maxFrameSize: 16777215, enablePush: false } : { enablePush: false } }))).request({ ...baseHeaders, ...host_1.getBasicAuth(url), ...headers, ':path': pathname, ':method': method });
|
|
1494
2076
|
if (getting) {
|
|
1495
2077
|
const listenerMap = {};
|
|
1496
2078
|
const onEvent = request.on.bind(request);
|
|
@@ -1500,21 +2082,24 @@ class Request extends module_1 {
|
|
|
1500
2082
|
connected = true;
|
|
1501
2083
|
const statusCode = response[':status'];
|
|
1502
2084
|
if (this.matchStatus(statusCode, url, response, request, options) && statusCode >= 200 && statusCode < 300) {
|
|
1503
|
-
emitter = checkEncoding(request, request, statusCode, outStream, response['content-encoding'])
|
|
1504
|
-
if (emitter) {
|
|
2085
|
+
if (emitter = checkEncoding(request, request, statusCode, outStream, response['content-encoding'])) {
|
|
1505
2086
|
for (const event in listenerMap) {
|
|
1506
|
-
|
|
1507
|
-
|
|
2087
|
+
const [name, type] = event.split('-');
|
|
2088
|
+
for (const listener of listenerMap[event]) {
|
|
1508
2089
|
if (name !== 'error') {
|
|
1509
2090
|
request.removeListener(name, listener);
|
|
1510
2091
|
}
|
|
1511
2092
|
emitter[type](name === 'end' ? 'finish' : name, listener);
|
|
1512
|
-
}
|
|
2093
|
+
}
|
|
1513
2094
|
}
|
|
1514
2095
|
}
|
|
1515
2096
|
else {
|
|
1516
2097
|
if (outStream) {
|
|
1517
|
-
stream.pipeline(request, outStream, err =>
|
|
2098
|
+
stream.pipeline(request, outStream, err => {
|
|
2099
|
+
if (err) {
|
|
2100
|
+
request.emit('error', err);
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
1518
2103
|
}
|
|
1519
2104
|
if (encoding) {
|
|
1520
2105
|
request.setEncoding((0, types_1.getEncoding)(encoding));
|
|
@@ -1528,7 +2113,7 @@ class Request extends module_1 {
|
|
|
1528
2113
|
this.matchHeaders(url, response, request, options);
|
|
1529
2114
|
});
|
|
1530
2115
|
request.on = function (event, listener) {
|
|
1531
|
-
var
|
|
2116
|
+
var _p;
|
|
1532
2117
|
switch (event) {
|
|
1533
2118
|
case 'data':
|
|
1534
2119
|
case 'error':
|
|
@@ -1538,7 +2123,7 @@ class Request extends module_1 {
|
|
|
1538
2123
|
break;
|
|
1539
2124
|
}
|
|
1540
2125
|
if (!connected) {
|
|
1541
|
-
(listenerMap[
|
|
2126
|
+
(listenerMap[_p = event + '-on'] || (listenerMap[_p] = [])).push(listener);
|
|
1542
2127
|
}
|
|
1543
2128
|
default:
|
|
1544
2129
|
onEvent(event, listener);
|
|
@@ -1547,7 +2132,7 @@ class Request extends module_1 {
|
|
|
1547
2132
|
return this;
|
|
1548
2133
|
};
|
|
1549
2134
|
request.once = function (event, listener) {
|
|
1550
|
-
var
|
|
2135
|
+
var _p;
|
|
1551
2136
|
switch (event) {
|
|
1552
2137
|
case 'data':
|
|
1553
2138
|
case 'error':
|
|
@@ -1557,7 +2142,7 @@ class Request extends module_1 {
|
|
|
1557
2142
|
break;
|
|
1558
2143
|
}
|
|
1559
2144
|
if (!connected) {
|
|
1560
|
-
(listenerMap[
|
|
2145
|
+
(listenerMap[_p = event + '-once'] || (listenerMap[_p] = [])).push(listener);
|
|
1561
2146
|
}
|
|
1562
2147
|
default:
|
|
1563
2148
|
onceEvent(event, listener);
|
|
@@ -1593,10 +2178,9 @@ class Request extends module_1 {
|
|
|
1593
2178
|
agent = new (secure ? https.Agent : http.Agent)({ keepAlive: true, timeout: agentTimeout });
|
|
1594
2179
|
}
|
|
1595
2180
|
else if (agentTimeout !== 0) {
|
|
1596
|
-
keepAlive = this.keepAlive || false;
|
|
1597
2181
|
agentTimeout ?? (agentTimeout = this.agentTimeout);
|
|
1598
|
-
if (keepAlive || agentTimeout > 0) {
|
|
1599
|
-
agent = new (secure ? https.Agent : http.Agent)({ keepAlive, timeout: agentTimeout });
|
|
2182
|
+
if (this.keepAlive !== null || agentTimeout > 0) {
|
|
2183
|
+
agent = new (secure ? https.Agent : http.Agent)({ keepAlive: this.keepAlive ?? true, timeout: agentTimeout });
|
|
1600
2184
|
}
|
|
1601
2185
|
}
|
|
1602
2186
|
}
|
|
@@ -1613,6 +2197,7 @@ class Request extends module_1 {
|
|
|
1613
2197
|
ca,
|
|
1614
2198
|
cert,
|
|
1615
2199
|
key,
|
|
2200
|
+
ciphers,
|
|
1616
2201
|
minVersion,
|
|
1617
2202
|
headers,
|
|
1618
2203
|
lookup: this.lookupDns(hostname),
|
|
@@ -1645,12 +2230,22 @@ class Request extends module_1 {
|
|
|
1645
2230
|
source = response;
|
|
1646
2231
|
}
|
|
1647
2232
|
response
|
|
1648
|
-
.on('close', () =>
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
2233
|
+
.on('close', () => {
|
|
2234
|
+
request.emit('close');
|
|
2235
|
+
})
|
|
2236
|
+
.on('error', err => {
|
|
2237
|
+
request.emit('error', err);
|
|
2238
|
+
})
|
|
2239
|
+
.once('readable', () => {
|
|
2240
|
+
request.emit('readable');
|
|
2241
|
+
});
|
|
2242
|
+
source.on('data', chunk => {
|
|
2243
|
+
request.emit('data', chunk);
|
|
2244
|
+
});
|
|
1652
2245
|
if (response !== source) {
|
|
1653
|
-
source.on('error', err =>
|
|
2246
|
+
source.on('error', err => {
|
|
2247
|
+
request.emit('error', err);
|
|
2248
|
+
});
|
|
1654
2249
|
}
|
|
1655
2250
|
if (!posting) {
|
|
1656
2251
|
if (version === 2 && incoming.upgrade?.includes('h2')) {
|
|
@@ -1666,9 +2261,15 @@ class Request extends module_1 {
|
|
|
1666
2261
|
}
|
|
1667
2262
|
else {
|
|
1668
2263
|
response
|
|
1669
|
-
.on('close', () =>
|
|
1670
|
-
|
|
1671
|
-
|
|
2264
|
+
.on('close', () => {
|
|
2265
|
+
request.emit('close');
|
|
2266
|
+
})
|
|
2267
|
+
.on('error', err => {
|
|
2268
|
+
request.emit('error', err);
|
|
2269
|
+
})
|
|
2270
|
+
.once('end', () => {
|
|
2271
|
+
request.emit('end');
|
|
2272
|
+
});
|
|
1672
2273
|
}
|
|
1673
2274
|
if (this._config.timeout) {
|
|
1674
2275
|
response.setTimeout(this._config.timeout, () => request.emit('timeout'));
|
|
@@ -1684,7 +2285,9 @@ class Request extends module_1 {
|
|
|
1684
2285
|
const ac = new AbortController();
|
|
1685
2286
|
this[kDownloading].add(options.outAbort = ac);
|
|
1686
2287
|
stream.addAbortSignal(ac.signal, request);
|
|
1687
|
-
this.signal.addEventListener('abort', () =>
|
|
2288
|
+
this.signal.addEventListener('abort', () => {
|
|
2289
|
+
ac.abort(new Error("Aborted by process"));
|
|
2290
|
+
}, { once: true });
|
|
1688
2291
|
}
|
|
1689
2292
|
if (posting) {
|
|
1690
2293
|
if ((0, types_1.isString)(postData) || Buffer.isBuffer(postData)) {
|
|
@@ -1744,7 +2347,7 @@ class Request extends module_1 {
|
|
|
1744
2347
|
let valid;
|
|
1745
2348
|
if ((0, types_1.isArray)(parts)) {
|
|
1746
2349
|
const write = combined.create();
|
|
1747
|
-
const boundary =
|
|
2350
|
+
const boundary = crypto.randomUUID().replaceAll('-', '');
|
|
1748
2351
|
contentType = "multipart/form-data" + `; boundary="${boundary}"`;
|
|
1749
2352
|
let contentLength = 0;
|
|
1750
2353
|
const createPart = (name, filename, type) => {
|
|
@@ -1761,7 +2364,6 @@ class Request extends module_1 {
|
|
|
1761
2364
|
contentLength += Buffer.byteLength(disposition);
|
|
1762
2365
|
}
|
|
1763
2366
|
};
|
|
1764
|
-
const escapeQuote = (value) => value.replace(/[\\"]/g, capture => '\\' + capture);
|
|
1765
2367
|
if ((0, types_1.isPlainObject)(data)) {
|
|
1766
2368
|
for (const name in data) {
|
|
1767
2369
|
addValue(name, data[name]);
|
|
@@ -1772,46 +2374,41 @@ class Request extends module_1 {
|
|
|
1772
2374
|
continue;
|
|
1773
2375
|
}
|
|
1774
2376
|
if (target) {
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2377
|
+
if (typeof target === 'string') {
|
|
2378
|
+
filename || (filename = path.basename(target));
|
|
2379
|
+
type || (type = module_1.lookupMime(filename));
|
|
2380
|
+
target = fs.readFileSync(target);
|
|
2381
|
+
}
|
|
2382
|
+
else if (target instanceof stream.Readable) {
|
|
2383
|
+
const chunks = [];
|
|
2384
|
+
for await (const chunk of target) {
|
|
2385
|
+
chunks.push(chunk);
|
|
1780
2386
|
}
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
2387
|
+
target = Buffer.concat(chunks);
|
|
2388
|
+
}
|
|
2389
|
+
if (!Buffer.isBuffer(target)) {
|
|
2390
|
+
throw (0, types_1.errorMessage)('File', "Unknown", "multipart/form-data");
|
|
2391
|
+
}
|
|
2392
|
+
if (!type || !filename) {
|
|
2393
|
+
const result = await module_1.resolveMime(target);
|
|
2394
|
+
let ext;
|
|
2395
|
+
if (result) {
|
|
2396
|
+
type || (type = result.mime);
|
|
2397
|
+
ext = result.ext;
|
|
1787
2398
|
}
|
|
1788
|
-
if (
|
|
1789
|
-
|
|
2399
|
+
else if (type) {
|
|
2400
|
+
ext = module_1.lookupMime(type, true);
|
|
1790
2401
|
}
|
|
1791
|
-
if (
|
|
1792
|
-
|
|
1793
|
-
let ext;
|
|
1794
|
-
if (result) {
|
|
1795
|
-
type || (type = result.mime);
|
|
1796
|
-
ext = result.ext;
|
|
1797
|
-
}
|
|
1798
|
-
else if (type) {
|
|
1799
|
-
ext = module_1.lookupMime(type, true);
|
|
1800
|
-
}
|
|
1801
|
-
if (ext && !filename) {
|
|
1802
|
-
filename = (0, types_1.generateUUID)() + '.' + ext;
|
|
1803
|
-
}
|
|
2402
|
+
if (ext && !filename) {
|
|
2403
|
+
filename = crypto.randomUUID() + '.' + ext;
|
|
1804
2404
|
}
|
|
1805
|
-
const disposition = createPart(name, filename, type || "application/octet-stream");
|
|
1806
|
-
write.append(disposition);
|
|
1807
|
-
write.append(target);
|
|
1808
|
-
write.append("\r\n");
|
|
1809
|
-
contentLength += Buffer.byteLength(disposition) + target.length + 2;
|
|
1810
|
-
valid = true;
|
|
1811
|
-
}
|
|
1812
|
-
catch (err) {
|
|
1813
|
-
return Promise.reject(err);
|
|
1814
2405
|
}
|
|
2406
|
+
const disposition = createPart(name, filename, type || "application/octet-stream");
|
|
2407
|
+
write.append(disposition);
|
|
2408
|
+
write.append(target);
|
|
2409
|
+
write.append("\r\n");
|
|
2410
|
+
contentLength += Buffer.byteLength(disposition) + target.length + 2;
|
|
2411
|
+
valid = true;
|
|
1815
2412
|
}
|
|
1816
2413
|
else {
|
|
1817
2414
|
addValue(name, value);
|
|
@@ -1854,437 +2451,7 @@ class Request extends module_1 {
|
|
|
1854
2451
|
if (this.readExpect === 'string') {
|
|
1855
2452
|
opts.encoding = (0, types_1.getEncoding)(opts.encoding);
|
|
1856
2453
|
}
|
|
1857
|
-
return new
|
|
1858
|
-
const pipeTo = opts.pipeTo;
|
|
1859
|
-
const status = opts.silent === false || !opts.silent && !this[kSingleton];
|
|
1860
|
-
let log = status && LOG_HTTP && LOG_TIMEPROCESS, retries = 0, redirects = 0, closed, timeout, outStream;
|
|
1861
|
-
const startTime = log ? process.hrtime() : 0;
|
|
1862
|
-
const throwError = (err, outAbort) => {
|
|
1863
|
-
if (timeout) {
|
|
1864
|
-
clearTimeout(timeout);
|
|
1865
|
-
}
|
|
1866
|
-
if (!closed) {
|
|
1867
|
-
closed = true;
|
|
1868
|
-
if (outStream && (0, types_1.isString)(pipeTo)) {
|
|
1869
|
-
(0, util_1.cleanupStream)(outStream, pipeTo);
|
|
1870
|
-
}
|
|
1871
|
-
reject(typeof err === 'string' ? new Error(err) : err);
|
|
1872
|
-
}
|
|
1873
|
-
if (outAbort) {
|
|
1874
|
-
this[kDownloading].delete(outAbort);
|
|
1875
|
-
}
|
|
1876
|
-
};
|
|
1877
|
-
const formatStatus = (value, hint) => value + ': ' + (hint || Request.fromStatusCode(value)) + ` (${uri.toString()})`;
|
|
1878
|
-
(function downloadUri(href, httpVersion) {
|
|
1879
|
-
let outAbort;
|
|
1880
|
-
try {
|
|
1881
|
-
const request = this.opts(href, opts);
|
|
1882
|
-
if (outStream && (0, types_1.isString)(pipeTo)) {
|
|
1883
|
-
(0, util_1.cleanupStream)(outStream, pipeTo);
|
|
1884
|
-
}
|
|
1885
|
-
if (pipeTo) {
|
|
1886
|
-
if (typeof pipeTo === 'string') {
|
|
1887
|
-
outStream = fs.createWriteStream(pipeTo, { emitClose: false, highWaterMark: request.host.localhost ? 65536 : 4096 });
|
|
1888
|
-
request.outStream = outStream;
|
|
1889
|
-
}
|
|
1890
|
-
else {
|
|
1891
|
-
request.outStream = pipeTo;
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
if (httpVersion) {
|
|
1895
|
-
request.httpVersion = httpVersion;
|
|
1896
|
-
}
|
|
1897
|
-
const client = this.open(href, request);
|
|
1898
|
-
const { host, url, encoding, progressId, outFormat } = request;
|
|
1899
|
-
let buffer, aborted;
|
|
1900
|
-
({ httpVersion, outAbort } = request);
|
|
1901
|
-
const isAborted = () => client.destroyed || httpVersion === 2 && client.aborted;
|
|
1902
|
-
const isRetry = (value) => (0, util_1.isRetryable)(value) && ++retries <= this._config.retryLimit;
|
|
1903
|
-
const isUnsupported = (value) => value === 421 || value === 505;
|
|
1904
|
-
const isDowngrade = (err) => err instanceof Error && (err.code === 'ERR_HTTP2_ERROR' || isUnsupported(Math.abs(err.errno)));
|
|
1905
|
-
const wasAborted = (err) => err instanceof Error && err.message.startsWith("Aborted");
|
|
1906
|
-
const sendWarning = (message) => status && LOG_HTTP && this.formatMessage(1024, 'HTTP' + httpVersion, [message, host.origin], url.toString(), { ...module_1.LOG_STYLE_WARN });
|
|
1907
|
-
const formatRetry = (message) => message + ` (${retries} / ${this._config.retryLimit})`;
|
|
1908
|
-
const formatNgFlags = (value, statusCode, location) => location ? `Using HTTP 1.1 for URL redirect (${location})` : formatStatus(statusCode, value ? 'NGHTTP2 Error ' + value : '');
|
|
1909
|
-
const abortResponse = () => {
|
|
1910
|
-
if (closed) {
|
|
1911
|
-
return;
|
|
1912
|
-
}
|
|
1913
|
-
if (timeout) {
|
|
1914
|
-
clearTimeout(timeout);
|
|
1915
|
-
}
|
|
1916
|
-
buffer = null;
|
|
1917
|
-
aborted = true;
|
|
1918
|
-
if (outAbort) {
|
|
1919
|
-
if (!client.aborted) {
|
|
1920
|
-
outAbort.abort();
|
|
1921
|
-
}
|
|
1922
|
-
this[kDownloading].delete(outAbort);
|
|
1923
|
-
}
|
|
1924
|
-
client.destroy();
|
|
1925
|
-
};
|
|
1926
|
-
const retryTimeout = () => {
|
|
1927
|
-
sendWarning(formatRetry('Connection timeout'));
|
|
1928
|
-
downloadUri.call(this, href);
|
|
1929
|
-
};
|
|
1930
|
-
const acceptResponse = (headers) => {
|
|
1931
|
-
if ('outHeaders' in opts) {
|
|
1932
|
-
opts.outHeaders = headers;
|
|
1933
|
-
}
|
|
1934
|
-
if ('outFilename' in opts) {
|
|
1935
|
-
opts.outFilename = (0, util_1.parseHeader)(headers, 'content-disposition');
|
|
1936
|
-
}
|
|
1937
|
-
const pipeline = pipeTo ? !(0, types_1.isString)(pipeTo) : false;
|
|
1938
|
-
const enabled = request.connected?.call(client, headers) !== false && !pipeline;
|
|
1939
|
-
const maxBufferSize = request.maxBufferSize ? (0, types_1.alignSize)(request.maxBufferSize) : 0;
|
|
1940
|
-
const { host: parent, readTimeout } = this;
|
|
1941
|
-
const contentLength = parent && progressId !== undefined ? parseInt(headers['content-length'] || '0') : 0;
|
|
1942
|
-
let dataLength = 0, mibsTime, delayTime;
|
|
1943
|
-
if (log || readTimeout > 0 || contentLength > 0) {
|
|
1944
|
-
client.once('readable', () => {
|
|
1945
|
-
if (readTimeout > 0) {
|
|
1946
|
-
timeout = setTimeout(() => {
|
|
1947
|
-
abortResponse();
|
|
1948
|
-
throwError((0, types_1.errorValue)("Timeout was exceeded", href.toString()));
|
|
1949
|
-
}, readTimeout);
|
|
1950
|
-
}
|
|
1951
|
-
if (log) {
|
|
1952
|
-
switch (this.settings.time_format || LOG_TIMEFORMAT) {
|
|
1953
|
-
case 'readable':
|
|
1954
|
-
delayTime = process.hrtime(startTime);
|
|
1955
|
-
break;
|
|
1956
|
-
case 'relative':
|
|
1957
|
-
delayTime = Date.now() - this.startTime;
|
|
1958
|
-
break;
|
|
1959
|
-
case 'none':
|
|
1960
|
-
if (opts.silent !== false) {
|
|
1961
|
-
log = false;
|
|
1962
|
-
}
|
|
1963
|
-
break;
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
mibsTime = process.hrtime();
|
|
1967
|
-
if (contentLength > 0) {
|
|
1968
|
-
parent.updateProgress("request", progressId, 0, 0, mibsTime);
|
|
1969
|
-
}
|
|
1970
|
-
});
|
|
1971
|
-
}
|
|
1972
|
-
if (enabled) {
|
|
1973
|
-
client.on('data', (chunk) => {
|
|
1974
|
-
if (buffer) {
|
|
1975
|
-
if (typeof buffer === 'string') {
|
|
1976
|
-
buffer += typeof chunk === 'string' ? chunk : chunk.toString(encoding);
|
|
1977
|
-
}
|
|
1978
|
-
else if (Array.isArray(buffer)) {
|
|
1979
|
-
buffer.push(typeof chunk === 'string' ? Buffer.from(chunk, encoding) : chunk);
|
|
1980
|
-
}
|
|
1981
|
-
else {
|
|
1982
|
-
buffer = Buffer.concat([buffer, typeof chunk === 'string' ? Buffer.from(chunk, encoding) : chunk]);
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
else {
|
|
1986
|
-
buffer = typeof chunk === 'string' ? chunk : [chunk];
|
|
1987
|
-
}
|
|
1988
|
-
if (contentLength > 0) {
|
|
1989
|
-
dataLength += Buffer.byteLength(chunk, encoding);
|
|
1990
|
-
parent.updateProgress("request", progressId, dataLength, contentLength);
|
|
1991
|
-
}
|
|
1992
|
-
if (maxBufferSize > 0) {
|
|
1993
|
-
if (contentLength === 0) {
|
|
1994
|
-
dataLength += Buffer.byteLength(chunk, encoding);
|
|
1995
|
-
}
|
|
1996
|
-
if (dataLength > maxBufferSize) {
|
|
1997
|
-
abortResponse();
|
|
1998
|
-
throwError((0, types_1.errorValue)("Size limit was exceeded", href.toString()));
|
|
1999
|
-
}
|
|
2000
|
-
}
|
|
2001
|
-
});
|
|
2002
|
-
}
|
|
2003
|
-
client.once('end', () => {
|
|
2004
|
-
if (closed || aborted) {
|
|
2005
|
-
return;
|
|
2006
|
-
}
|
|
2007
|
-
if (timeout) {
|
|
2008
|
-
clearTimeout(timeout);
|
|
2009
|
-
}
|
|
2010
|
-
if (outAbort) {
|
|
2011
|
-
this[kDownloading].delete(outAbort);
|
|
2012
|
-
}
|
|
2013
|
-
closed = true;
|
|
2014
|
-
let result, messageUnit, titleBgColor;
|
|
2015
|
-
if (buffer) {
|
|
2016
|
-
if (Array.isArray(buffer)) {
|
|
2017
|
-
buffer = Buffer.concat(buffer);
|
|
2018
|
-
if (encoding) {
|
|
2019
|
-
buffer = buffer.toString(encoding);
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
dataLength = Buffer.byteLength(buffer, encoding);
|
|
2023
|
-
if (mibsTime) {
|
|
2024
|
-
messageUnit = (0, util_1.getTransferRate)(dataLength, (0, types_1.convertTime)(process.hrtime(mibsTime), false) * 1000);
|
|
2025
|
-
}
|
|
2026
|
-
if (contentLength > 0) {
|
|
2027
|
-
parent.updateProgress("request", progressId, dataLength, dataLength);
|
|
2028
|
-
}
|
|
2029
|
-
if (typeof buffer === 'string') {
|
|
2030
|
-
if (buffer.startsWith('\uFEFF') && encoding !== 'utf16le' && encoding !== 'utf-16le') {
|
|
2031
|
-
buffer = buffer.substring(1);
|
|
2032
|
-
}
|
|
2033
|
-
if (outFormat) {
|
|
2034
|
-
const { out: format, parser } = outFormat;
|
|
2035
|
-
let packageName;
|
|
2036
|
-
try {
|
|
2037
|
-
switch (format) {
|
|
2038
|
-
case 'yaml':
|
|
2039
|
-
result = yaml.load(buffer, parser);
|
|
2040
|
-
break;
|
|
2041
|
-
case 'json5':
|
|
2042
|
-
result = require(packageName = 'json5').parse(buffer);
|
|
2043
|
-
break;
|
|
2044
|
-
case 'xml':
|
|
2045
|
-
result = new (require(packageName = 'fast-xml-parser').XMLParser)(parser).parse(buffer);
|
|
2046
|
-
break;
|
|
2047
|
-
case 'toml':
|
|
2048
|
-
result = require(packageName = 'toml').parse(buffer);
|
|
2049
|
-
break;
|
|
2050
|
-
default:
|
|
2051
|
-
result = JSON.parse(buffer);
|
|
2052
|
-
break;
|
|
2053
|
-
}
|
|
2054
|
-
if (!(0, types_1.isObject)(result)) {
|
|
2055
|
-
result = null;
|
|
2056
|
-
}
|
|
2057
|
-
}
|
|
2058
|
-
catch (err) {
|
|
2059
|
-
if (status && !(packageName && this.checkPackage(err, packageName))) {
|
|
2060
|
-
this.writeFail(['Unable to parse URI response', format], err, 1024);
|
|
2061
|
-
}
|
|
2062
|
-
result = null;
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
|
-
if (result === undefined) {
|
|
2067
|
-
result = buffer;
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
else {
|
|
2071
|
-
if (contentLength > 0) {
|
|
2072
|
-
parent.updateProgress("request", progressId, 0, contentLength);
|
|
2073
|
-
}
|
|
2074
|
-
if (enabled && this.readExpect === 'always') {
|
|
2075
|
-
throwError('No data received');
|
|
2076
|
-
return;
|
|
2077
|
-
}
|
|
2078
|
-
result = encoding && !pipeline ? '' : null;
|
|
2079
|
-
titleBgColor = 'bgBlue';
|
|
2080
|
-
}
|
|
2081
|
-
if (log) {
|
|
2082
|
-
this.writeTimeProcess('HTTP' + httpVersion, request.statusMessage || url.toString(), startTime, { type: 1024, queue: !!this.host, titleBgColor, messageUnit, messageUnitMinWidth: 9, delayTime, bypassLog: LOG_STDOUT });
|
|
2083
|
-
}
|
|
2084
|
-
resolve(result);
|
|
2085
|
-
});
|
|
2086
|
-
host.success(httpVersion);
|
|
2087
|
-
};
|
|
2088
|
-
const redirectResponse = (statusCode, location) => {
|
|
2089
|
-
abortResponse();
|
|
2090
|
-
if (location) {
|
|
2091
|
-
if (request.follow_redirect === false) {
|
|
2092
|
-
throwError(formatStatus(statusCode, 'Follow redirect was disabled'));
|
|
2093
|
-
}
|
|
2094
|
-
else if (++redirects <= this._config.redirectLimit) {
|
|
2095
|
-
downloadUri.call(this, Request.fromURL(url, location));
|
|
2096
|
-
}
|
|
2097
|
-
else {
|
|
2098
|
-
throwError(formatStatus(statusCode, 'Exceeded redirect limit'));
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
else {
|
|
2102
|
-
throwError(formatStatus(statusCode, 'Missing redirect location'));
|
|
2103
|
-
}
|
|
2104
|
-
};
|
|
2105
|
-
const errorResponse = (err) => {
|
|
2106
|
-
abortResponse();
|
|
2107
|
-
if (wasAborted(err)) {
|
|
2108
|
-
throwError(err);
|
|
2109
|
-
}
|
|
2110
|
-
else if ((0, util_1.checkRetryable)(err) && ++retries <= this._config.retryLimit) {
|
|
2111
|
-
if (isConnectionTimeout(err)) {
|
|
2112
|
-
retryTimeout();
|
|
2113
|
-
}
|
|
2114
|
-
else {
|
|
2115
|
-
setTimeout(() => downloadUri.call(this, href), this._config.retryWait);
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
else {
|
|
2119
|
-
host.error(httpVersion);
|
|
2120
|
-
throwError(err);
|
|
2121
|
-
}
|
|
2122
|
-
};
|
|
2123
|
-
const retryResponse = (statusCode, retryAfter) => {
|
|
2124
|
-
abortResponse();
|
|
2125
|
-
if (retryAfter && this._config.retryAfter > 0) {
|
|
2126
|
-
let offset = +retryAfter || new Date(retryAfter);
|
|
2127
|
-
if (offset instanceof Date) {
|
|
2128
|
-
offset = Math.max(0, offset.getTime() - Date.now());
|
|
2129
|
-
}
|
|
2130
|
-
else {
|
|
2131
|
-
offset *= 1000;
|
|
2132
|
-
}
|
|
2133
|
-
if (offset > 0) {
|
|
2134
|
-
if (offset <= this._config.retryAfter) {
|
|
2135
|
-
sendWarning(`Retry After (${retryAfter})`);
|
|
2136
|
-
setTimeout(() => downloadUri.call(this, href), offset);
|
|
2137
|
-
}
|
|
2138
|
-
else {
|
|
2139
|
-
throwError(formatStatus(statusCode));
|
|
2140
|
-
}
|
|
2141
|
-
return;
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
sendWarning(formatRetry(Request.fromStatusCode(statusCode)));
|
|
2145
|
-
if ((0, util_1.isRetryable)(statusCode, true)) {
|
|
2146
|
-
process.nextTick(downloadUri.bind(this), href);
|
|
2147
|
-
}
|
|
2148
|
-
else {
|
|
2149
|
-
setTimeout(() => downloadUri.call(this, href), this._config.retryWait);
|
|
2150
|
-
}
|
|
2151
|
-
};
|
|
2152
|
-
if (httpVersion === 2) {
|
|
2153
|
-
const retryDownload = (downgrade, message) => {
|
|
2154
|
-
if (aborted) {
|
|
2155
|
-
return;
|
|
2156
|
-
}
|
|
2157
|
-
abortResponse();
|
|
2158
|
-
if (downgrade) {
|
|
2159
|
-
host.failed(2);
|
|
2160
|
-
if (host.version > 1) {
|
|
2161
|
-
if (status && LOG_HTTP) {
|
|
2162
|
-
this.formatMessage(1024, 'HTTP2', ['Unsupported protocol', host.origin], message, { failed: true });
|
|
2163
|
-
}
|
|
2164
|
-
host.version = 1;
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
downloadUri.call(this, href, 1);
|
|
2168
|
-
};
|
|
2169
|
-
client
|
|
2170
|
-
.on('response', (headers, flags) => {
|
|
2171
|
-
if (isAborted()) {
|
|
2172
|
-
return;
|
|
2173
|
-
}
|
|
2174
|
-
const statusCode = headers[':status'];
|
|
2175
|
-
if (statusCode < 300) {
|
|
2176
|
-
acceptResponse(headers);
|
|
2177
|
-
}
|
|
2178
|
-
else if (statusCode < 400) {
|
|
2179
|
-
redirectResponse(statusCode, headers.location);
|
|
2180
|
-
}
|
|
2181
|
-
else if (statusCode === 401 ||
|
|
2182
|
-
statusCode === 402 ||
|
|
2183
|
-
statusCode === 403 ||
|
|
2184
|
-
statusCode === 404 ||
|
|
2185
|
-
statusCode === 407 ||
|
|
2186
|
-
statusCode === 410) {
|
|
2187
|
-
throwError(formatStatus(statusCode), outAbort);
|
|
2188
|
-
}
|
|
2189
|
-
else if (isRetry(statusCode)) {
|
|
2190
|
-
retryResponse(statusCode, headers['retry-after']);
|
|
2191
|
-
}
|
|
2192
|
-
else if (isUnsupported(statusCode)) {
|
|
2193
|
-
retryDownload(true, formatNgFlags(http2.constants.NGHTTP2_PROTOCOL_ERROR, statusCode));
|
|
2194
|
-
}
|
|
2195
|
-
else {
|
|
2196
|
-
switch (flags) {
|
|
2197
|
-
case http2.constants.NGHTTP2_PROTOCOL_ERROR:
|
|
2198
|
-
case http2.constants.NGHTTP2_INADEQUATE_SECURITY:
|
|
2199
|
-
case http2.constants.NGHTTP2_HTTP_1_1_REQUIRED:
|
|
2200
|
-
retryDownload(true, formatNgFlags(flags, statusCode, headers.location));
|
|
2201
|
-
break;
|
|
2202
|
-
default:
|
|
2203
|
-
retryDownload(false, formatStatus(statusCode));
|
|
2204
|
-
break;
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
})
|
|
2208
|
-
.on('unknownProtocol', () => {
|
|
2209
|
-
if (!aborted) {
|
|
2210
|
-
retryDownload(true, 'Unknown protocol (HTTP/2)');
|
|
2211
|
-
}
|
|
2212
|
-
})
|
|
2213
|
-
.on('aborted', () => {
|
|
2214
|
-
aborted = true;
|
|
2215
|
-
throwError((0, types_1.createAbortError)(), outAbort);
|
|
2216
|
-
})
|
|
2217
|
-
.on('error', async (err) => {
|
|
2218
|
-
if (aborted) {
|
|
2219
|
-
return;
|
|
2220
|
-
}
|
|
2221
|
-
if (wasAborted(err)) {
|
|
2222
|
-
errorResponse(err);
|
|
2223
|
-
}
|
|
2224
|
-
else {
|
|
2225
|
-
switch (!isDowngrade(err) && await host.hasProtocol(2)) {
|
|
2226
|
-
case 1:
|
|
2227
|
-
errorResponse(err);
|
|
2228
|
-
break;
|
|
2229
|
-
case 2:
|
|
2230
|
-
retryDownload(false, err);
|
|
2231
|
-
break;
|
|
2232
|
-
default:
|
|
2233
|
-
retryDownload(true, err);
|
|
2234
|
-
break;
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
});
|
|
2238
|
-
}
|
|
2239
|
-
else {
|
|
2240
|
-
client
|
|
2241
|
-
.on('response', res => {
|
|
2242
|
-
if (isAborted()) {
|
|
2243
|
-
return;
|
|
2244
|
-
}
|
|
2245
|
-
const statusCode = res.statusCode;
|
|
2246
|
-
if (statusCode < 300) {
|
|
2247
|
-
acceptResponse(res.headers);
|
|
2248
|
-
}
|
|
2249
|
-
else if (statusCode < 400) {
|
|
2250
|
-
redirectResponse(statusCode, res.headers.location);
|
|
2251
|
-
}
|
|
2252
|
-
else if (isRetry(statusCode)) {
|
|
2253
|
-
retryResponse(statusCode, res.headers['retry-after']);
|
|
2254
|
-
}
|
|
2255
|
-
else {
|
|
2256
|
-
abortResponse();
|
|
2257
|
-
throwError(formatStatus(statusCode));
|
|
2258
|
-
}
|
|
2259
|
-
})
|
|
2260
|
-
.on('abort', () => {
|
|
2261
|
-
aborted = true;
|
|
2262
|
-
throwError((0, types_1.createAbortError)(), outAbort);
|
|
2263
|
-
})
|
|
2264
|
-
.on('error', err => {
|
|
2265
|
-
if (!aborted) {
|
|
2266
|
-
errorResponse(err);
|
|
2267
|
-
}
|
|
2268
|
-
});
|
|
2269
|
-
}
|
|
2270
|
-
client.on('timeout', () => {
|
|
2271
|
-
if (aborted) {
|
|
2272
|
-
return;
|
|
2273
|
-
}
|
|
2274
|
-
abortResponse();
|
|
2275
|
-
if (++retries <= this._config.retryLimit) {
|
|
2276
|
-
retryTimeout();
|
|
2277
|
-
}
|
|
2278
|
-
else {
|
|
2279
|
-
throwError(formatStatus(408));
|
|
2280
|
-
}
|
|
2281
|
-
});
|
|
2282
|
-
}
|
|
2283
|
-
catch (err) {
|
|
2284
|
-
throwError(err, outAbort);
|
|
2285
|
-
}
|
|
2286
|
-
}).bind(this)(uri);
|
|
2287
|
-
});
|
|
2454
|
+
return new Fetch(this, uri, opts).start();
|
|
2288
2455
|
}
|
|
2289
2456
|
reset() {
|
|
2290
2457
|
this[kPendingDns] = Object.create(null);
|
|
@@ -2393,10 +2560,9 @@ class Request extends module_1 {
|
|
|
2393
2560
|
return this[kIpVersion];
|
|
2394
2561
|
}
|
|
2395
2562
|
get settings() {
|
|
2396
|
-
var
|
|
2397
|
-
return (
|
|
2563
|
+
var _p;
|
|
2564
|
+
return (_p = this.module).settings || (_p.settings = {});
|
|
2398
2565
|
}
|
|
2399
2566
|
}
|
|
2400
|
-
_a = kSingleton, _b = kHttpVersion, _c = kHeaders, _d = kCerts, _e =
|
|
2401
|
-
|
|
2567
|
+
_a = kSingleton, _b = kHttpVersion, _c = kHeaders, _d = kCerts, _e = kBaseURL, _f = kConnectDns, _g = kPendingDns, _h = kConnectHttp, _j = kStatusOn, _k = kHeadersOn, _l = kDownloading, _m = kHostInfo, _o = kSession;
|
|
2402
2568
|
module.exports = Request;
|