@e-mc/request 0.13.10 → 0.14.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 +5 -5
- package/http/adapter/index.js +34 -37
- package/http/host/altsvc/index.d.ts +5 -0
- package/http/host/altsvc/index.js +2 -0
- package/http/host/index.js +8 -21
- package/index.js +235 -209
- package/package.json +3 -6
- package/util.d.ts +10 -3
- package/util.js +68 -40
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @e-mc/request
|
|
2
2
|
|
|
3
|
-
* NodeJS
|
|
3
|
+
* NodeJS 20 (Minimum 18)
|
|
4
4
|
* ES2022
|
|
5
5
|
|
|
6
6
|
## General Usage
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
## Interface
|
|
11
11
|
|
|
12
|
-
* [View Source](https://www.unpkg.com/@e-mc/types@0.
|
|
12
|
+
* [View Source](https://www.unpkg.com/@e-mc/types@0.14.1/lib/index.d.ts)
|
|
13
13
|
|
|
14
14
|
```typescript
|
|
15
15
|
import type { IModule, ModuleConstructor } from "./index";
|
|
@@ -253,9 +253,9 @@ instance.get("http://hostname/path/config.yml", options).then(data => {
|
|
|
253
253
|
|
|
254
254
|
## References
|
|
255
255
|
|
|
256
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
257
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
258
|
-
- https://www.unpkg.com/@e-mc/types@0.
|
|
256
|
+
- https://www.unpkg.com/@e-mc/types@0.14.1/lib/http.d.ts
|
|
257
|
+
- https://www.unpkg.com/@e-mc/types@0.14.1/lib/request.d.ts
|
|
258
|
+
- https://www.unpkg.com/@e-mc/types@0.14.1/lib/settings.d.ts
|
|
259
259
|
|
|
260
260
|
* https://www.npmjs.com/package/@types/node
|
|
261
261
|
|
package/http/adapter/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const http2 = require('node:http2');
|
|
5
|
+
const { load } = require('js-yaml');
|
|
6
|
+
const { alignSize, convertTime, createAbortError, errorValue, isError, isErrorCode, isObject, isString } = require('@e-mc/types');
|
|
7
|
+
const Module = require('@e-mc/module');
|
|
8
|
+
const { checkRetryable, cleanupStream, fromStatusCode, fromURL, getSize, getTransferRate, isRetryable, parseHeader } = require('@e-mc/request/util');
|
|
8
9
|
const kHttpAdapter = Symbol.for('request:http:adapter:constructor');
|
|
9
10
|
let LOG_TIMEFORMAT = 'readable';
|
|
10
11
|
const isConstructor = (value) => typeof value === 'function' && !!value.prototype?.constructor.name;
|
|
@@ -20,13 +21,13 @@ class HttpAdapter {
|
|
|
20
21
|
return value === 421 || value === 505;
|
|
21
22
|
}
|
|
22
23
|
static isDowngrade(err) {
|
|
23
|
-
return
|
|
24
|
+
return isErrorCode(err, 'ERR_HTTP2_ERROR') || isError(err) && this.isUnsupported(Math.abs(err.errno));
|
|
24
25
|
}
|
|
25
26
|
static wasAborted(err) {
|
|
26
|
-
return err
|
|
27
|
+
return isError(err) && err.message.startsWith("Aborted");
|
|
27
28
|
}
|
|
28
29
|
static isConnectionError(err) {
|
|
29
|
-
return
|
|
30
|
+
return isErrorCode(err, 'ETIMEDOUT', 'ECONNRESET');
|
|
30
31
|
}
|
|
31
32
|
static defineHostConfig({ settings }) {
|
|
32
33
|
const time_format = settings?.time_format;
|
|
@@ -128,7 +129,7 @@ class HttpAdapter {
|
|
|
128
129
|
})
|
|
129
130
|
.on('aborted', () => {
|
|
130
131
|
this.aborted = true;
|
|
131
|
-
this.terminate(
|
|
132
|
+
this.terminate(createAbortError());
|
|
132
133
|
})
|
|
133
134
|
.on('error', async (err) => {
|
|
134
135
|
if (this.aborted) {
|
|
@@ -181,7 +182,7 @@ class HttpAdapter {
|
|
|
181
182
|
})
|
|
182
183
|
.on('abort', () => {
|
|
183
184
|
this.aborted = true;
|
|
184
|
-
this.terminate(
|
|
185
|
+
this.terminate(createAbortError());
|
|
185
186
|
})
|
|
186
187
|
.on('error', err => {
|
|
187
188
|
if (this.aborted) {
|
|
@@ -247,14 +248,14 @@ class HttpAdapter {
|
|
|
247
248
|
opts.outHeaders = headers;
|
|
248
249
|
}
|
|
249
250
|
if ('outFilename' in opts) {
|
|
250
|
-
opts.outFilename =
|
|
251
|
+
opts.outFilename = parseHeader(headers, 'content-disposition');
|
|
251
252
|
}
|
|
252
253
|
if ('outContentType' in opts) {
|
|
253
254
|
opts.outContentType = headers['content-type'];
|
|
254
255
|
}
|
|
255
|
-
const pipeline = this.pipeTo ? !
|
|
256
|
+
const pipeline = this.pipeTo ? !isString(this.pipeTo) : false;
|
|
256
257
|
const enabled = opts.connected?.call(this.client, headers) !== false && !pipeline;
|
|
257
|
-
const maxBufferSize = opts.maxBufferSize ?
|
|
258
|
+
const maxBufferSize = opts.maxBufferSize ? alignSize(opts.maxBufferSize) : 0;
|
|
258
259
|
this.contentLength = parseInt(headers['content-length'] || '0');
|
|
259
260
|
const updating = opts.progressId !== undefined && !!this.instance.host && this.contentLength > 0 && !empty;
|
|
260
261
|
const readTimeout = this.instance.readTimeout;
|
|
@@ -262,7 +263,7 @@ class HttpAdapter {
|
|
|
262
263
|
this.client.once('readable', () => {
|
|
263
264
|
if (readTimeout > 0) {
|
|
264
265
|
this.timeout = setTimeout(() => {
|
|
265
|
-
this.terminate(
|
|
266
|
+
this.terminate(errorValue("Timeout was exceeded", this.uri.toString()));
|
|
266
267
|
}, readTimeout);
|
|
267
268
|
}
|
|
268
269
|
if (log) {
|
|
@@ -311,7 +312,7 @@ class HttpAdapter {
|
|
|
311
312
|
dataLength += Buffer.byteLength(chunk, encoding);
|
|
312
313
|
}
|
|
313
314
|
if (dataLength > maxBufferSize) {
|
|
314
|
-
this.terminate(
|
|
315
|
+
this.terminate(errorValue("Size limit was exceeded", this.uri.toString()));
|
|
315
316
|
}
|
|
316
317
|
}
|
|
317
318
|
});
|
|
@@ -341,7 +342,7 @@ class HttpAdapter {
|
|
|
341
342
|
}
|
|
342
343
|
if (typeof buffer === 'string') {
|
|
343
344
|
if (buffer.startsWith('\uFEFF') && encoding !== 'utf16le' && encoding !== 'utf-16le') {
|
|
344
|
-
buffer = buffer.
|
|
345
|
+
buffer = buffer.slice(1);
|
|
345
346
|
}
|
|
346
347
|
if (opts.outFormat) {
|
|
347
348
|
const { out: format, parser } = opts.outFormat;
|
|
@@ -349,7 +350,7 @@ class HttpAdapter {
|
|
|
349
350
|
try {
|
|
350
351
|
switch (format) {
|
|
351
352
|
case 'yaml':
|
|
352
|
-
result =
|
|
353
|
+
result = load(buffer, parser);
|
|
353
354
|
break;
|
|
354
355
|
case 'json5':
|
|
355
356
|
result = require(packageName = 'json5').parse(buffer);
|
|
@@ -358,18 +359,13 @@ class HttpAdapter {
|
|
|
358
359
|
result = new (require(packageName = 'fast-xml-parser').XMLParser)(parser).parse(buffer);
|
|
359
360
|
break;
|
|
360
361
|
case 'toml':
|
|
361
|
-
|
|
362
|
-
result = require('smol-toml').parse(buffer, parser);
|
|
363
|
-
}
|
|
364
|
-
catch {
|
|
365
|
-
result = require(packageName = 'toml').parse(buffer);
|
|
366
|
-
}
|
|
362
|
+
result = require(packageName = 'toml').parse(buffer);
|
|
367
363
|
break;
|
|
368
364
|
default:
|
|
369
365
|
result = JSON.parse(buffer);
|
|
370
366
|
break;
|
|
371
367
|
}
|
|
372
|
-
if (!
|
|
368
|
+
if (!isObject(result)) {
|
|
373
369
|
result = null;
|
|
374
370
|
}
|
|
375
371
|
}
|
|
@@ -396,7 +392,7 @@ class HttpAdapter {
|
|
|
396
392
|
this.terminate("No data received");
|
|
397
393
|
return;
|
|
398
394
|
}
|
|
399
|
-
dataLength = this.contentLength || (typeof this.pipeTo === 'string' ?
|
|
395
|
+
dataLength = this.contentLength || (typeof this.pipeTo === 'string' ? getSize(this.pipeTo) : 0);
|
|
400
396
|
result = encoding && !pipeline ? '' : null;
|
|
401
397
|
}
|
|
402
398
|
this.endResponse(result, dataLength, log);
|
|
@@ -416,7 +412,7 @@ class HttpAdapter {
|
|
|
416
412
|
messageUnit: this.formatMibs(dataLength),
|
|
417
413
|
messageUnitMinWidth: 9,
|
|
418
414
|
delayTime: this.delayTime,
|
|
419
|
-
bypassLog:
|
|
415
|
+
bypassLog: Module.hasLogType(32768)
|
|
420
416
|
});
|
|
421
417
|
}
|
|
422
418
|
this.resolve(result);
|
|
@@ -431,7 +427,7 @@ class HttpAdapter {
|
|
|
431
427
|
if (this.opts.method?.toUpperCase() === 'PUT' && statusCode !== 307 && statusCode !== 308) {
|
|
432
428
|
this.#options.method = 'GET';
|
|
433
429
|
}
|
|
434
|
-
this.setOpts(
|
|
430
|
+
this.setOpts(fromURL(this.opts.url, location));
|
|
435
431
|
this.init();
|
|
436
432
|
}
|
|
437
433
|
else {
|
|
@@ -455,7 +451,7 @@ class HttpAdapter {
|
|
|
455
451
|
if (HttpAdapter.wasAborted(err)) {
|
|
456
452
|
this.terminate(err);
|
|
457
453
|
}
|
|
458
|
-
else if (
|
|
454
|
+
else if (checkRetryable(err) && ++this.retries <= this.retryLimit) {
|
|
459
455
|
this.abortResponse();
|
|
460
456
|
if (HttpAdapter.isConnectionError(err)) {
|
|
461
457
|
this.retryTimeout();
|
|
@@ -494,8 +490,8 @@ class HttpAdapter {
|
|
|
494
490
|
return;
|
|
495
491
|
}
|
|
496
492
|
}
|
|
497
|
-
this.sendWarning(this.#formatRetry(
|
|
498
|
-
if (
|
|
493
|
+
this.sendWarning(this.#formatRetry(fromStatusCode(statusCode)));
|
|
494
|
+
if (isRetryable(statusCode, true)) {
|
|
499
495
|
setImmediate(this.init.bind(this));
|
|
500
496
|
}
|
|
501
497
|
else {
|
|
@@ -505,7 +501,7 @@ class HttpAdapter {
|
|
|
505
501
|
}
|
|
506
502
|
}
|
|
507
503
|
isRetry(value) {
|
|
508
|
-
return
|
|
504
|
+
return isRetryable(value) && ++this.retries <= this.retryLimit;
|
|
509
505
|
}
|
|
510
506
|
retryTimeout() {
|
|
511
507
|
this.sendWarning(this.#formatRetry("HTTP connection timeout"));
|
|
@@ -522,15 +518,15 @@ class HttpAdapter {
|
|
|
522
518
|
sendWarning(message) {
|
|
523
519
|
if (this.state.verbose) {
|
|
524
520
|
const { host, url } = this.opts;
|
|
525
|
-
this.instance.formatMessage(1024, 'HTTP' + this.httpVersion, [message, host.origin], url.toString(), { ...
|
|
521
|
+
this.instance.formatMessage(1024, 'HTTP' + this.httpVersion, [message, host.origin], url.toString(), { ...Module.LOG_STYLE_WARN });
|
|
526
522
|
}
|
|
527
523
|
}
|
|
528
524
|
formatStatus(value, hint) {
|
|
529
|
-
return value + ': ' + (hint ||
|
|
525
|
+
return value + ': ' + (hint || fromStatusCode(value)) + ` (${this.uri.toString()})`;
|
|
530
526
|
}
|
|
531
527
|
formatMibs(dataLength) {
|
|
532
528
|
if (dataLength > 0 && this.dataTime) {
|
|
533
|
-
return
|
|
529
|
+
return getTransferRate(dataLength, Math.max(1, convertTime(process.hrtime.bigint() - this.dataTime, false) * 1000));
|
|
534
530
|
}
|
|
535
531
|
}
|
|
536
532
|
close() {
|
|
@@ -541,8 +537,8 @@ class HttpAdapter {
|
|
|
541
537
|
}
|
|
542
538
|
}
|
|
543
539
|
cleanup() {
|
|
544
|
-
if (
|
|
545
|
-
|
|
540
|
+
if (isString(this.pipeTo) && this.outStream) {
|
|
541
|
+
cleanupStream(this.outStream, this.pipeTo);
|
|
546
542
|
this.outStream = null;
|
|
547
543
|
}
|
|
548
544
|
}
|
|
@@ -595,4 +591,5 @@ class HttpAdapter {
|
|
|
595
591
|
return this.state.config.redirectLimit;
|
|
596
592
|
}
|
|
597
593
|
}
|
|
594
|
+
|
|
598
595
|
module.exports = HttpAdapter;
|
package/http/host/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
2
|
+
|
|
3
|
+
const tls = require('node:tls');
|
|
4
|
+
const { alignSize, isArray } = require('@e-mc/types');
|
|
5
|
+
const HttpHostAltSvc = require('@e-mc/request/http/host/altsvc');
|
|
5
6
|
const HOST_LOCAL = new Set(['localhost']);
|
|
6
7
|
const HOST_STREAM = new Map();
|
|
7
8
|
const HOST_HTTP_1_1 = [];
|
|
@@ -47,13 +48,13 @@ class HttpHost {
|
|
|
47
48
|
}
|
|
48
49
|
static defineHostConfig({ write_stream, localhost, protocol }) {
|
|
49
50
|
for (const host in write_stream) {
|
|
50
|
-
const size =
|
|
51
|
+
const size = alignSize(+write_stream[host], 1, 1);
|
|
51
52
|
if (size > 0) {
|
|
52
53
|
const [hostname, port = '443'] = host.split(/\s*:\s*/).map((item, index) => index === 1 ? parseInt(item) > 0 ? parseInt(item).toString() : '443' : item);
|
|
53
54
|
HOST_STREAM.set(hostname + ':' + port, size);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
|
-
if (
|
|
57
|
+
if (isArray(localhost)) {
|
|
57
58
|
this.defineLocalHost(localhost);
|
|
58
59
|
}
|
|
59
60
|
if (protocol) {
|
|
@@ -83,7 +84,7 @@ class HttpHost {
|
|
|
83
84
|
this.#origin = url.origin;
|
|
84
85
|
this.localhost = HOST_LOCAL.has(hostname);
|
|
85
86
|
this.#streamSize = HOST_STREAM.get(address) || (this.localhost ? 65536 : 4096);
|
|
86
|
-
this.#altSvc = new
|
|
87
|
+
this.#altSvc = new HttpHostAltSvc(this, this.#versionData);
|
|
87
88
|
if (protocol !== 'file:' && !HOST_HTTP_1_1.includes(address = protocol + '//' + address)) {
|
|
88
89
|
if (secure) {
|
|
89
90
|
this.#version = HOST_ALPN_H2.includes(address) ? 2 : httpVersion;
|
|
@@ -233,21 +234,6 @@ class HttpHost {
|
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
}
|
|
236
|
-
didAltSvc(version) {
|
|
237
|
-
return this.#versionData[version].status !== -1;
|
|
238
|
-
}
|
|
239
|
-
nextAltSvc() {
|
|
240
|
-
return this.altSvc.next();
|
|
241
|
-
}
|
|
242
|
-
closeAltSvc(error) {
|
|
243
|
-
return this.altSvc.close(error);
|
|
244
|
-
}
|
|
245
|
-
clearAltSvc(version) {
|
|
246
|
-
this.altSvc.clear(version);
|
|
247
|
-
}
|
|
248
|
-
flagAltSvc(version, value) {
|
|
249
|
-
this.altSvc.flag(version, value);
|
|
250
|
-
}
|
|
251
237
|
reset() {
|
|
252
238
|
this.altSvc.clear();
|
|
253
239
|
this.#versionData.forEach((item, index) => {
|
|
@@ -298,4 +284,5 @@ class HttpHost {
|
|
|
298
284
|
return this.#streamSize;
|
|
299
285
|
}
|
|
300
286
|
}
|
|
287
|
+
|
|
301
288
|
module.exports = HttpHost;
|