@wiajs/request 3.0.29 → 3.0.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/request.cjs +166 -60
- package/dist/request.mjs +166 -60
- package/lib/index.js +81 -49
- package/lib/request.js +66 -12
- package/package.json +1 -1
- package/types/index.d.ts +35 -9
- package/types/request.d.ts +4 -2
- package/types/utils.d.ts +2 -0
package/dist/request.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* wia request v3.0.
|
|
2
|
+
* wia request v3.0.31
|
|
3
3
|
* (c) 2022-2024 Sibyl Yu and contributors
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
|
+
import stream, { Duplex } from 'node:stream';
|
|
6
7
|
import { log as log$2, name } from '@wiajs/log';
|
|
7
8
|
import http from 'node:http';
|
|
8
9
|
import https from 'node:https';
|
|
9
10
|
import assert from 'node:assert';
|
|
10
11
|
import url from 'node:url';
|
|
11
|
-
import stream, { Duplex } from 'node:stream';
|
|
12
12
|
import zlib from 'node:zlib';
|
|
13
13
|
import mime from 'mime-types';
|
|
14
14
|
|
|
@@ -464,7 +464,7 @@ const log$1 = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
|
|
|
464
464
|
* @prop {*} [beforeRedirect]
|
|
465
465
|
* @prop {boolean} [followRedirects]
|
|
466
466
|
* @prop {number} [maxRedirects=21]
|
|
467
|
-
* @prop {number} [maxBodyLength =
|
|
467
|
+
* @prop {number} [maxBodyLength = -1]
|
|
468
468
|
* @prop {*} [trackRedirects]
|
|
469
469
|
* @prop {*} [data]
|
|
470
470
|
*/
|
|
@@ -475,7 +475,7 @@ const log$1 = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
|
|
|
475
475
|
* @prop {number} [responseStartTime]
|
|
476
476
|
*/
|
|
477
477
|
|
|
478
|
-
/** @typedef { http.IncomingMessage & ResponseExt} Response*/
|
|
478
|
+
/** @typedef { http.IncomingMessage & ResponseExt} Response */
|
|
479
479
|
|
|
480
480
|
const httpModules = {'http:': http, 'https:': https};
|
|
481
481
|
|
|
@@ -525,12 +525,12 @@ const writeEvents = [
|
|
|
525
525
|
'connect',
|
|
526
526
|
'continue',
|
|
527
527
|
'drain',
|
|
528
|
-
'error',
|
|
528
|
+
// 'error', // 单独处理,未注册 'error' 事件处理程序,错误将冒泡到全局导致程序崩溃
|
|
529
529
|
'finish',
|
|
530
530
|
'information',
|
|
531
531
|
'pipe',
|
|
532
532
|
// 'response', 由 processResponse 触发
|
|
533
|
-
'socket',
|
|
533
|
+
'socket', // 建立连接时触发
|
|
534
534
|
'timeout',
|
|
535
535
|
'unpipe',
|
|
536
536
|
'upgrade',
|
|
@@ -542,7 +542,7 @@ for (const ev of writeEvents)
|
|
|
542
542
|
writeEventEmit[ev] = /** @param {...any} args */ function (...args) {
|
|
543
543
|
const m = this; // 事件回调,this === clientRequest 实例
|
|
544
544
|
// log('req event', {ev})
|
|
545
|
-
m.redirectReq.emit(ev, ...args); // req
|
|
545
|
+
m.redirectReq.emit(ev, ...args); // 内部请求req 事情转发到 Request
|
|
546
546
|
};
|
|
547
547
|
|
|
548
548
|
// stream.Readable,在响应流上转发读流取事件
|
|
@@ -575,6 +575,21 @@ const MaxBodyLengthExceededError = utils.createErrorType(
|
|
|
575
575
|
|
|
576
576
|
const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end');
|
|
577
577
|
|
|
578
|
+
// request err
|
|
579
|
+
const HostNotfoundError = utils.createErrorType('ERR_HOSTNOTFOUND', 'DNS 解析失败,主机名可能无效');
|
|
580
|
+
const ConnRefusedError = utils.createErrorType(
|
|
581
|
+
'ERR_CONNREFUSED',
|
|
582
|
+
'连接被拒绝,目标服务器可能不可用'
|
|
583
|
+
);
|
|
584
|
+
const ConnTimedoutError = utils.createErrorType(
|
|
585
|
+
'ERR_CONNTIMEDOUT',
|
|
586
|
+
'请求超时,请检查网络连接或服务器负载'
|
|
587
|
+
);
|
|
588
|
+
const ConnResetError = utils.createErrorType(
|
|
589
|
+
'ERR_CONNRESET',
|
|
590
|
+
'连接被重置,可能是网络问题或服务器关闭了连接'
|
|
591
|
+
);
|
|
592
|
+
|
|
578
593
|
/**
|
|
579
594
|
* An HTTP(S) request that can be redirected
|
|
580
595
|
* wrap http.ClientRequest
|
|
@@ -774,7 +789,41 @@ class Request extends Duplex {
|
|
|
774
789
|
// 启动 startTimer
|
|
775
790
|
if (m.startTimer) m._currentRequest.once('socket', m.startTimer);
|
|
776
791
|
|
|
777
|
-
//
|
|
792
|
+
// set tcp keep alive to prevent drop connection by peer
|
|
793
|
+
req.on(
|
|
794
|
+
'socket',
|
|
795
|
+
/** @param {*} socket */ socket => {
|
|
796
|
+
// default interval of sending ack packet is 1 minute
|
|
797
|
+
socket.setKeepAlive(true, 1000 * 60);
|
|
798
|
+
}
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
// 请求error单独处理
|
|
802
|
+
// 'error' 事件处理,避免错误将冒泡到全局导致程序崩溃
|
|
803
|
+
req.on('error', err => {
|
|
804
|
+
destroyRequest(req); // 释放资源
|
|
805
|
+
// @ts-ignore
|
|
806
|
+
log$1.error({errcode: err?.code}, 'request');
|
|
807
|
+
// @ts-ignore
|
|
808
|
+
switch (err?.code) {
|
|
809
|
+
case 'ENOTFOUND':
|
|
810
|
+
m.emit('error', new HostNotfoundError());
|
|
811
|
+
break
|
|
812
|
+
case 'ECONNREFUSED':
|
|
813
|
+
m.emit('error', new ConnRefusedError());
|
|
814
|
+
break
|
|
815
|
+
case 'ETIMEDOUT':
|
|
816
|
+
m.emit('error', new ConnTimedoutError());
|
|
817
|
+
break
|
|
818
|
+
case 'ECONNRESET':
|
|
819
|
+
m.emit('error', new ConnResetError());
|
|
820
|
+
break
|
|
821
|
+
default:
|
|
822
|
+
m.emit('error', utils.createErrorType('ERR_CONNOTHER', `网络错误: ${err.message}`));
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
// 接收req事件,转发 到 request 上发射,网络关闭事件,触发 error
|
|
778
827
|
for (const ev of writeEvents) req.on(ev, writeEventEmit[ev]);
|
|
779
828
|
|
|
780
829
|
// RFC7230§5.3.1: When making a request directly to an origin server, […]
|
|
@@ -823,9 +872,11 @@ class Request extends Duplex {
|
|
|
823
872
|
return R
|
|
824
873
|
}
|
|
825
874
|
|
|
875
|
+
/**
|
|
876
|
+
* 写入错误,释放请求,触发 abort 终止事件
|
|
877
|
+
*/
|
|
826
878
|
abort() {
|
|
827
879
|
destroyRequest(this._currentRequest);
|
|
828
|
-
this._currentRequest.abort();
|
|
829
880
|
this.emit('abort');
|
|
830
881
|
}
|
|
831
882
|
|
|
@@ -889,7 +940,11 @@ class Request extends Duplex {
|
|
|
889
940
|
// log({data: chunk, encoding, cb}, 'write')
|
|
890
941
|
|
|
891
942
|
// Writing is not allowed if end has been called
|
|
892
|
-
if (m._ending)
|
|
943
|
+
if (m._ending) {
|
|
944
|
+
// throw new WriteAfterEndError()
|
|
945
|
+
m.emit('error', new WriteAfterEndError());
|
|
946
|
+
return
|
|
947
|
+
}
|
|
893
948
|
|
|
894
949
|
// ! 数据写入时连接,pipe 时可设置 header
|
|
895
950
|
if (!m._currentRequest) m.request();
|
|
@@ -1084,6 +1139,7 @@ class Request extends Duplex {
|
|
|
1084
1139
|
m.on('error', clearTimer);
|
|
1085
1140
|
m.on('response', clearTimer);
|
|
1086
1141
|
m.on('close', clearTimer);
|
|
1142
|
+
|
|
1087
1143
|
return m
|
|
1088
1144
|
}
|
|
1089
1145
|
|
|
@@ -1274,14 +1330,15 @@ class Request extends Duplex {
|
|
|
1274
1330
|
/**
|
|
1275
1331
|
* 处理响应stream
|
|
1276
1332
|
* 自动解压,透传流,需设置 decompress = false,避免解压数据
|
|
1277
|
-
* @param {
|
|
1333
|
+
* @param {Response} res
|
|
1334
|
+
* @returns {Response | stream.Readable}
|
|
1278
1335
|
*/
|
|
1279
1336
|
processStream(res) {
|
|
1280
1337
|
const m = this;
|
|
1281
1338
|
const {opt} = m;
|
|
1282
1339
|
|
|
1283
1340
|
const streams = [res];
|
|
1284
|
-
|
|
1341
|
+
let responseStream = res;
|
|
1285
1342
|
// 'transfer-encoding': 'chunked'时,无content-length,axios v1.2 不能自动解压
|
|
1286
1343
|
const responseLength = +res.headers['content-length'];
|
|
1287
1344
|
|
|
@@ -1314,6 +1371,7 @@ class Request extends Duplex {
|
|
|
1314
1371
|
case 'compress':
|
|
1315
1372
|
case 'x-compress':
|
|
1316
1373
|
// add the unzipper to the body stream processing pipeline
|
|
1374
|
+
// @ts-ignore
|
|
1317
1375
|
streams.push(zlib.createUnzip(zlibOptions));
|
|
1318
1376
|
|
|
1319
1377
|
// remove the content-encoding in order to not confuse downstream operations
|
|
@@ -1321,9 +1379,11 @@ class Request extends Duplex {
|
|
|
1321
1379
|
break
|
|
1322
1380
|
|
|
1323
1381
|
case 'deflate':
|
|
1382
|
+
// @ts-ignore
|
|
1324
1383
|
streams.push(new ZlibTransform());
|
|
1325
1384
|
|
|
1326
1385
|
// add the unzipper to the body stream processing pipeline
|
|
1386
|
+
// @ts-ignore
|
|
1327
1387
|
streams.push(zlib.createUnzip(zlibOptions));
|
|
1328
1388
|
|
|
1329
1389
|
// remove the content-encoding in order to not confuse downstream operations
|
|
@@ -1332,6 +1392,7 @@ class Request extends Duplex {
|
|
|
1332
1392
|
|
|
1333
1393
|
case 'br':
|
|
1334
1394
|
if (isBrotliSupported) {
|
|
1395
|
+
// @ts-ignore
|
|
1335
1396
|
streams.push(zlib.createBrotliDecompress(brotliOptions));
|
|
1336
1397
|
res.headers['content-encoding'] = undefined;
|
|
1337
1398
|
}
|
|
@@ -1340,10 +1401,13 @@ class Request extends Duplex {
|
|
|
1340
1401
|
}
|
|
1341
1402
|
|
|
1342
1403
|
// 响应流,用于读
|
|
1343
|
-
|
|
1404
|
+
// @ts-ignore
|
|
1405
|
+
responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
|
|
1344
1406
|
// 将内部 responseStream 可读流 映射到 redirectReq
|
|
1345
1407
|
|
|
1408
|
+
// @ts-ignore
|
|
1346
1409
|
m.responseStream = responseStream;
|
|
1410
|
+
// @ts-ignore
|
|
1347
1411
|
responseStream.redirectReq = m; // 事情触发时引用
|
|
1348
1412
|
|
|
1349
1413
|
// stream 模式,事件透传到 请求类
|
|
@@ -1515,7 +1579,9 @@ class Request extends Duplex {
|
|
|
1515
1579
|
}
|
|
1516
1580
|
|
|
1517
1581
|
/**
|
|
1518
|
-
*
|
|
1582
|
+
* 释放请求,触发error事件
|
|
1583
|
+
* 'error' event, and emit a 'close' event.
|
|
1584
|
+
* Calling this will cause remaining data in the response to be dropped and the socket to be destroyed.
|
|
1519
1585
|
* @param {*} request
|
|
1520
1586
|
* @param {*} error
|
|
1521
1587
|
*/
|
|
@@ -1524,7 +1590,7 @@ function destroyRequest(request, error) {
|
|
|
1524
1590
|
request.removeListener(ev, writeEventEmit[ev]);
|
|
1525
1591
|
}
|
|
1526
1592
|
request.on('error', utils.noop);
|
|
1527
|
-
request.destroy(error);
|
|
1593
|
+
request.destroy(error); // 触发 error 事件
|
|
1528
1594
|
}
|
|
1529
1595
|
|
|
1530
1596
|
/**
|
|
@@ -1566,9 +1632,35 @@ function isSubdomain(subdomain, domain) {
|
|
|
1566
1632
|
* 代理模式下,http or https 请求,取决于 proxy 代理服务器,而不是目的服务器。
|
|
1567
1633
|
*/
|
|
1568
1634
|
|
|
1569
|
-
|
|
1570
1635
|
const log = log$2({env: `wia:req:${name(import.meta.url)}`}); // __filename
|
|
1571
1636
|
|
|
1637
|
+
/** @typedef { import('./request').Response} Response */
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* @typedef {object} Opts
|
|
1641
|
+
* @prop {Object.<string,string>} [headers]
|
|
1642
|
+
* @prop {string} [url]
|
|
1643
|
+
* @prop {'http:' | 'https:'} [protocol]
|
|
1644
|
+
* @prop {string} [host]
|
|
1645
|
+
* @prop {string} [family]
|
|
1646
|
+
* @prop {string} [path]
|
|
1647
|
+
* @prop {string} [method]
|
|
1648
|
+
* @prop {*} [agent] - 发送请求的agent
|
|
1649
|
+
* @prop {*} [agents] - http、https agent,根据协议自动选择
|
|
1650
|
+
* @prop {*} [body] - body 数据,优先body,其次data
|
|
1651
|
+
* @prop {*} [data] - body 数据
|
|
1652
|
+
* @prop {boolean} [stream] - 以流的方式工作
|
|
1653
|
+
* @prop {boolean} [decompress=true] - 自动解压
|
|
1654
|
+
* @prop {*} [transformStream]
|
|
1655
|
+
* @prop {*} [beforeRedirect]
|
|
1656
|
+
* @prop {boolean} [followRedirects] - 自动完成重定向
|
|
1657
|
+
* @prop {number} [maxRedirects=21] - 最大重定向次数
|
|
1658
|
+
* @prop {number} [maxBodyLength = 0] - body限制,缺省不限
|
|
1659
|
+
* @prop {*} [trackRedirects]
|
|
1660
|
+
*/
|
|
1661
|
+
|
|
1662
|
+
/** @typedef {(res: Response, stream?: stream.Readable) => void} Cb*/
|
|
1663
|
+
|
|
1572
1664
|
utils.createErrorType(
|
|
1573
1665
|
'ERR_STREAM_WRITE_BEEN_ABORTED',
|
|
1574
1666
|
'Request stream has been aborted'
|
|
@@ -1581,7 +1673,7 @@ utils.createErrorType(
|
|
|
1581
1673
|
const looksLikeBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
1582
1674
|
const looksLikeV8 = utils.isFunction(Error.captureStackTrace);
|
|
1583
1675
|
if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
|
|
1584
|
-
|
|
1676
|
+
log.warn('The follow-redirects package should be excluded from browser builds.');
|
|
1585
1677
|
}
|
|
1586
1678
|
})();
|
|
1587
1679
|
|
|
@@ -1593,10 +1685,10 @@ utils.createErrorType(
|
|
|
1593
1685
|
|
|
1594
1686
|
/**
|
|
1595
1687
|
* 初始化参数
|
|
1596
|
-
* @param {
|
|
1597
|
-
* @param {
|
|
1598
|
-
* @param {
|
|
1599
|
-
* @returns
|
|
1688
|
+
* @param {string | Opts} uri/opts
|
|
1689
|
+
* @param {Opts | Cb} [opts] /cb
|
|
1690
|
+
* @param {Cb} [cb]
|
|
1691
|
+
* @returns {{opts: Opts, cb: Cb}}
|
|
1600
1692
|
*/
|
|
1601
1693
|
function init(uri, opts, cb) {
|
|
1602
1694
|
let R;
|
|
@@ -1605,39 +1697,59 @@ function init(uri, opts, cb) {
|
|
|
1605
1697
|
if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
|
|
1606
1698
|
else if (utils.isString(uri)) uri = utils.spreadUrlObject(utils.parseUrl(uri));
|
|
1607
1699
|
else {
|
|
1700
|
+
// @ts-ignore
|
|
1608
1701
|
cb = opts;
|
|
1702
|
+
// @ts-ignore
|
|
1609
1703
|
opts = uri;
|
|
1704
|
+
// @ts-ignore
|
|
1610
1705
|
const {url} = opts;
|
|
1706
|
+
// 有url,解析
|
|
1611
1707
|
if (url) {
|
|
1708
|
+
// @ts-ignore
|
|
1709
|
+
// biome-ignore lint/performance/noDelete: <explanation>
|
|
1612
1710
|
delete opts.url;
|
|
1613
1711
|
if (utils.isURL(url)) uri = utils.spreadUrlObject(url);
|
|
1614
1712
|
else if (utils.isString(url)) uri = utils.spreadUrlObject(utils.parseUrl(url));
|
|
1615
1713
|
} else {
|
|
1616
|
-
|
|
1714
|
+
// @ts-ignore
|
|
1715
|
+
opts = uri; // 不判断 utils.validateUrl(uri)
|
|
1617
1716
|
uri = {};
|
|
1618
1717
|
}
|
|
1619
1718
|
}
|
|
1620
1719
|
|
|
1621
1720
|
if (utils.isFunction(opts)) {
|
|
1721
|
+
// @ts-ignore
|
|
1622
1722
|
cb = opts;
|
|
1623
|
-
opts =
|
|
1723
|
+
opts = {};
|
|
1624
1724
|
}
|
|
1625
1725
|
|
|
1626
1726
|
// copy options
|
|
1627
1727
|
opts = {
|
|
1728
|
+
// @ts-ignore
|
|
1628
1729
|
...uri,
|
|
1629
1730
|
...opts,
|
|
1630
1731
|
};
|
|
1631
1732
|
|
|
1733
|
+
// @ts-ignore
|
|
1632
1734
|
if (!utils.isString(opts.host) && !utils.isString(opts.hostname)) opts.hostname = '::1';
|
|
1735
|
+
// @ts-ignore
|
|
1633
1736
|
if (opts.method) opts.method = opts.method.toUpperCase();
|
|
1634
1737
|
|
|
1635
|
-
|
|
1738
|
+
// @ts-ignore
|
|
1739
|
+
// follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited
|
|
1740
|
+
opts.maxBodyLength = opts.maxBodyLength ?? Number.POSITIVE_INFINITY;
|
|
1741
|
+
// @ts-ignore
|
|
1742
|
+
opts.maxRedirects = opts.maxRedirects ?? 21;
|
|
1743
|
+
// @ts-ignore
|
|
1744
|
+
if (opts.maxRedirects === 0) opts.followRedirects = false;
|
|
1745
|
+
|
|
1746
|
+
R = {opts, cb};
|
|
1636
1747
|
// log({R}, 'init')
|
|
1637
1748
|
} catch (e) {
|
|
1638
1749
|
log.err(e, 'init');
|
|
1639
1750
|
}
|
|
1640
1751
|
|
|
1752
|
+
// @ts-ignore
|
|
1641
1753
|
return R
|
|
1642
1754
|
}
|
|
1643
1755
|
|
|
@@ -1648,19 +1760,26 @@ function init(uri, opts, cb) {
|
|
|
1648
1760
|
* 注意变参 (options[, callback]) or (url[, options][, callback])
|
|
1649
1761
|
maxRedirects: _.maxRedirects,
|
|
1650
1762
|
maxBodyLength: _.maxBodyLength,
|
|
1651
|
-
* @param {
|
|
1652
|
-
* @param {
|
|
1653
|
-
* @param {
|
|
1654
|
-
* @returns
|
|
1763
|
+
* @param {string | Opts} uri /options
|
|
1764
|
+
* @param {Opts | Cb} [options] /callback
|
|
1765
|
+
* @param {Cb} [callback] /null
|
|
1766
|
+
* @returns {Request}
|
|
1655
1767
|
*/
|
|
1656
1768
|
function request(uri, options, callback) {
|
|
1657
1769
|
let R = null;
|
|
1658
1770
|
|
|
1659
1771
|
try {
|
|
1772
|
+
// @ts-ignore
|
|
1660
1773
|
const {opts, cb} = init(uri, options, callback);
|
|
1661
|
-
// log({uri, options, opts}, 'request')
|
|
1662
|
-
|
|
1774
|
+
// log.error({uri, options, opts}, 'request')
|
|
1775
|
+
|
|
1663
1776
|
const {data, stream} = opts;
|
|
1777
|
+
// data 在本函数完成处理,不传递到 request
|
|
1778
|
+
opts.data = undefined;
|
|
1779
|
+
|
|
1780
|
+
// @ts-ignore
|
|
1781
|
+
const req = new Request(opts, cb);
|
|
1782
|
+
|
|
1664
1783
|
// 非流模式,自动发送请求,流模式通过流写入发送
|
|
1665
1784
|
if (!stream) {
|
|
1666
1785
|
// 发送数据
|
|
@@ -1687,8 +1806,12 @@ function request(uri, options, callback) {
|
|
|
1687
1806
|
}
|
|
1688
1807
|
});
|
|
1689
1808
|
|
|
1809
|
+
// log.error({data}, 'request data.pipe')
|
|
1690
1810
|
data.pipe(req); // 写入数据流
|
|
1691
|
-
} else
|
|
1811
|
+
} else {
|
|
1812
|
+
// log.error({data}, 'request req.end')
|
|
1813
|
+
req.end(data); // 写入数据
|
|
1814
|
+
}
|
|
1692
1815
|
}
|
|
1693
1816
|
|
|
1694
1817
|
R = req;
|
|
@@ -1705,40 +1828,23 @@ function request(uri, options, callback) {
|
|
|
1705
1828
|
* 复杂数据,请使用 @wiajs/req库(fork from axios),该库封装了当前库,提供了更多功能
|
|
1706
1829
|
* organize params for patch, post, put, head, del
|
|
1707
1830
|
* @param {string} verb
|
|
1708
|
-
* @returns {
|
|
1831
|
+
* @returns {(url: string | Opts, opts?: Opts | Cb, cb?: Cb) => void}}
|
|
1709
1832
|
*/
|
|
1710
1833
|
function fn(verb) {
|
|
1711
1834
|
const method = verb.toUpperCase();
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1835
|
+
/**
|
|
1836
|
+
*
|
|
1837
|
+
* @param {string | Opts} uri /options
|
|
1838
|
+
* @param {Opts | Cb} [opts] /callback
|
|
1839
|
+
* @param {Cb} [cb] /null
|
|
1840
|
+
* @returns
|
|
1841
|
+
*/
|
|
1842
|
+
function fn(uri, opts, cb) {
|
|
1843
|
+
// @ts-ignore
|
|
1716
1844
|
opts.method = method;
|
|
1717
|
-
|
|
1718
|
-
const {data, stream} = opts;
|
|
1719
|
-
// 非流模式,自动发送请求,流模式通过流写入发送
|
|
1720
|
-
if (!stream) {
|
|
1721
|
-
// 发送数据
|
|
1722
|
-
if (utils.isStream(data)) {
|
|
1723
|
-
|
|
1724
|
-
data.on('end', () => {
|
|
1725
|
-
});
|
|
1726
|
-
|
|
1727
|
-
data.once(
|
|
1728
|
-
'error',
|
|
1729
|
-
/** @param {*} err */ err => {
|
|
1730
|
-
// req.destroy(err)
|
|
1731
|
-
}
|
|
1732
|
-
);
|
|
1733
|
-
|
|
1734
|
-
data.on('close', () => {
|
|
1735
|
-
});
|
|
1736
|
-
|
|
1737
|
-
data.pipe(req); // 写入数据流
|
|
1738
|
-
} else req.end(data);
|
|
1739
|
-
}
|
|
1740
|
-
return req
|
|
1845
|
+
return request(uri, opts, cb)
|
|
1741
1846
|
}
|
|
1847
|
+
return fn
|
|
1742
1848
|
}
|
|
1743
1849
|
|
|
1744
1850
|
// define like this to please codeintel/intellisense IDEs
|
package/lib/index.js
CHANGED
|
@@ -3,20 +3,42 @@
|
|
|
3
3
|
* used by axios
|
|
4
4
|
* 修改以支持http、https 代理服务器
|
|
5
5
|
* 代理模式下,http or https 请求,取决于 proxy 代理服务器,而不是目的服务器。
|
|
6
|
-
*/ import
|
|
6
|
+
*/ import stream from 'node:stream';
|
|
7
|
+
import { log as Log, name } from '@wiajs/log';
|
|
7
8
|
import Request from './request.js';
|
|
8
9
|
import utils from './utils.js';
|
|
9
10
|
const log = Log({
|
|
10
11
|
env: `wia:req:${name(import.meta.url)}`
|
|
11
12
|
}) // __filename
|
|
12
13
|
;
|
|
13
|
-
|
|
14
|
+
/** @typedef { import('./request').Response} Response */ /**
|
|
15
|
+
* @typedef {object} Opts
|
|
16
|
+
* @prop {Object.<string,string>} [headers]
|
|
17
|
+
* @prop {string} [url]
|
|
18
|
+
* @prop {'http:' | 'https:'} [protocol]
|
|
19
|
+
* @prop {string} [host]
|
|
20
|
+
* @prop {string} [family]
|
|
21
|
+
* @prop {string} [path]
|
|
22
|
+
* @prop {string} [method]
|
|
23
|
+
* @prop {*} [agent] - 发送请求的agent
|
|
24
|
+
* @prop {*} [agents] - http、https agent,根据协议自动选择
|
|
25
|
+
* @prop {*} [body] - body 数据,优先body,其次data
|
|
26
|
+
* @prop {*} [data] - body 数据
|
|
27
|
+
* @prop {boolean} [stream] - 以流的方式工作
|
|
28
|
+
* @prop {boolean} [decompress=true] - 自动解压
|
|
29
|
+
* @prop {*} [transformStream]
|
|
30
|
+
* @prop {*} [beforeRedirect]
|
|
31
|
+
* @prop {boolean} [followRedirects] - 自动完成重定向
|
|
32
|
+
* @prop {number} [maxRedirects=21] - 最大重定向次数
|
|
33
|
+
* @prop {number} [maxBodyLength = 0] - body限制,缺省不限
|
|
34
|
+
* @prop {*} [trackRedirects]
|
|
35
|
+
*/ /** @typedef {(res: Response, stream?: stream.Readable) => void} Cb*/ const WritebBeenAbortedError = utils.createErrorType('ERR_STREAM_WRITE_BEEN_ABORTED', 'Request stream has been aborted');
|
|
14
36
|
(function detectUnsupportedEnvironment() {
|
|
15
37
|
const looksLikeNode = typeof process !== 'undefined';
|
|
16
38
|
const looksLikeBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
17
39
|
const looksLikeV8 = utils.isFunction(Error.captureStackTrace);
|
|
18
40
|
if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
|
|
19
|
-
|
|
41
|
+
log.warn('The follow-redirects package should be excluded from browser builds.');
|
|
20
42
|
}
|
|
21
43
|
})();
|
|
22
44
|
/**
|
|
@@ -25,10 +47,10 @@ const WritebBeenAbortedError = utils.createErrorType('ERR_STREAM_WRITE_BEEN_ABOR
|
|
|
25
47
|
* 支持隧道及非隧道、http(s)代理
|
|
26
48
|
*/ /**
|
|
27
49
|
* 初始化参数
|
|
28
|
-
* @param {
|
|
29
|
-
* @param {
|
|
30
|
-
* @param {
|
|
31
|
-
* @returns
|
|
50
|
+
* @param {string | Opts} uri/opts
|
|
51
|
+
* @param {Opts | Cb} [opts] /cb
|
|
52
|
+
* @param {Cb} [cb]
|
|
53
|
+
* @returns {{opts: Opts, cb: Cb}}
|
|
32
54
|
*/ function init(uri, opts, cb) {
|
|
33
55
|
let R;
|
|
34
56
|
try {
|
|
@@ -36,37 +58,57 @@ const WritebBeenAbortedError = utils.createErrorType('ERR_STREAM_WRITE_BEEN_ABOR
|
|
|
36
58
|
if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
|
|
37
59
|
else if (utils.isString(uri)) uri = utils.spreadUrlObject(utils.parseUrl(uri));
|
|
38
60
|
else {
|
|
61
|
+
// @ts-ignore
|
|
39
62
|
cb = opts;
|
|
63
|
+
// @ts-ignore
|
|
40
64
|
opts = uri;
|
|
65
|
+
// @ts-ignore
|
|
41
66
|
const { url } = opts;
|
|
67
|
+
// 有url,解析
|
|
42
68
|
if (url) {
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
// biome-ignore lint/performance/noDelete: <explanation>
|
|
43
71
|
delete opts.url;
|
|
44
72
|
if (utils.isURL(url)) uri = utils.spreadUrlObject(url);
|
|
45
73
|
else if (utils.isString(url)) uri = utils.spreadUrlObject(utils.parseUrl(url));
|
|
46
74
|
} else {
|
|
47
|
-
|
|
75
|
+
// @ts-ignore
|
|
76
|
+
opts = uri // 不判断 utils.validateUrl(uri)
|
|
77
|
+
;
|
|
48
78
|
uri = {};
|
|
49
79
|
}
|
|
50
80
|
}
|
|
51
81
|
if (utils.isFunction(opts)) {
|
|
82
|
+
// @ts-ignore
|
|
52
83
|
cb = opts;
|
|
53
|
-
opts =
|
|
84
|
+
opts = {};
|
|
54
85
|
}
|
|
55
86
|
// copy options
|
|
56
87
|
opts = {
|
|
88
|
+
// @ts-ignore
|
|
57
89
|
...uri,
|
|
58
90
|
...opts
|
|
59
91
|
};
|
|
92
|
+
// @ts-ignore
|
|
60
93
|
if (!utils.isString(opts.host) && !utils.isString(opts.hostname)) opts.hostname = '::1';
|
|
94
|
+
// @ts-ignore
|
|
61
95
|
if (opts.method) opts.method = opts.method.toUpperCase();
|
|
96
|
+
// @ts-ignore
|
|
97
|
+
// follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited
|
|
98
|
+
opts.maxBodyLength = opts.maxBodyLength ?? Number.POSITIVE_INFINITY;
|
|
99
|
+
// @ts-ignore
|
|
100
|
+
opts.maxRedirects = opts.maxRedirects ?? 21;
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
if (opts.maxRedirects === 0) opts.followRedirects = false;
|
|
62
103
|
R = {
|
|
63
|
-
opts
|
|
64
|
-
cb
|
|
104
|
+
opts,
|
|
105
|
+
cb
|
|
65
106
|
};
|
|
66
107
|
// log({R}, 'init')
|
|
67
108
|
} catch (e) {
|
|
68
109
|
log.err(e, 'init');
|
|
69
110
|
}
|
|
111
|
+
// @ts-ignore
|
|
70
112
|
return R;
|
|
71
113
|
}
|
|
72
114
|
/**
|
|
@@ -76,17 +118,21 @@ const WritebBeenAbortedError = utils.createErrorType('ERR_STREAM_WRITE_BEEN_ABOR
|
|
|
76
118
|
* 注意变参 (options[, callback]) or (url[, options][, callback])
|
|
77
119
|
maxRedirects: _.maxRedirects,
|
|
78
120
|
maxBodyLength: _.maxBodyLength,
|
|
79
|
-
* @param {
|
|
80
|
-
* @param {
|
|
81
|
-
* @param {
|
|
82
|
-
* @returns
|
|
121
|
+
* @param {string | Opts} uri /options
|
|
122
|
+
* @param {Opts | Cb} [options] /callback
|
|
123
|
+
* @param {Cb} [callback] /null
|
|
124
|
+
* @returns {Request}
|
|
83
125
|
*/ function request(uri, options, callback) {
|
|
84
126
|
let R = null;
|
|
85
127
|
try {
|
|
128
|
+
// @ts-ignore
|
|
86
129
|
const { opts, cb } = init(uri, options, callback);
|
|
87
|
-
// log({uri, options, opts}, 'request')
|
|
88
|
-
const req = new Request(opts, cb);
|
|
130
|
+
// log.error({uri, options, opts}, 'request')
|
|
89
131
|
const { data, stream } = opts;
|
|
132
|
+
// data 在本函数完成处理,不传递到 request
|
|
133
|
+
opts.data = undefined;
|
|
134
|
+
// @ts-ignore
|
|
135
|
+
const req = new Request(opts, cb);
|
|
90
136
|
// 非流模式,自动发送请求,流模式通过流写入发送
|
|
91
137
|
if (!stream) {
|
|
92
138
|
// 发送数据
|
|
@@ -106,9 +152,14 @@ const WritebBeenAbortedError = utils.createErrorType('ERR_STREAM_WRITE_BEEN_ABOR
|
|
|
106
152
|
// throw new WritebBeenAbortedError()
|
|
107
153
|
}
|
|
108
154
|
});
|
|
155
|
+
// log.error({data}, 'request data.pipe')
|
|
109
156
|
data.pipe(req) // 写入数据流
|
|
110
157
|
;
|
|
111
|
-
} else
|
|
158
|
+
} else {
|
|
159
|
+
// log.error({data}, 'request req.end')
|
|
160
|
+
req.end(data) // 写入数据
|
|
161
|
+
;
|
|
162
|
+
}
|
|
112
163
|
}
|
|
113
164
|
R = req;
|
|
114
165
|
} catch (e) {
|
|
@@ -122,40 +173,21 @@ const WritebBeenAbortedError = utils.createErrorType('ERR_STREAM_WRITE_BEEN_ABOR
|
|
|
122
173
|
* 复杂数据,请使用 @wiajs/req库(fork from axios),该库封装了当前库,提供了更多功能
|
|
123
174
|
* organize params for patch, post, put, head, del
|
|
124
175
|
* @param {string} verb
|
|
125
|
-
* @returns {
|
|
176
|
+
* @returns {(url: string | Opts, opts?: Opts | Cb, cb?: Cb) => void}}
|
|
126
177
|
*/ function fn(verb) {
|
|
127
178
|
const method = verb.toUpperCase();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
179
|
+
/**
|
|
180
|
+
*
|
|
181
|
+
* @param {string | Opts} uri /options
|
|
182
|
+
* @param {Opts | Cb} [opts] /callback
|
|
183
|
+
* @param {Cb} [cb] /null
|
|
184
|
+
* @returns
|
|
185
|
+
*/ function fn(uri, opts, cb) {
|
|
186
|
+
// @ts-ignore
|
|
131
187
|
opts.method = method;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!stream) {
|
|
136
|
-
// 发送数据
|
|
137
|
-
if (utils.isStream(data)) {
|
|
138
|
-
// Send the request
|
|
139
|
-
let ended = false;
|
|
140
|
-
let errored = false;
|
|
141
|
-
data.on('end', ()=>{
|
|
142
|
-
ended = true;
|
|
143
|
-
});
|
|
144
|
-
data.once('error', /** @param {*} err */ (err)=>{
|
|
145
|
-
errored = true;
|
|
146
|
-
// req.destroy(err)
|
|
147
|
-
});
|
|
148
|
-
data.on('close', ()=>{
|
|
149
|
-
if (!ended && !errored) {
|
|
150
|
-
// throw new WritebBeenAbortedError()
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
data.pipe(req) // 写入数据流
|
|
154
|
-
;
|
|
155
|
-
} else req.end(data);
|
|
156
|
-
}
|
|
157
|
-
return req;
|
|
158
|
-
};
|
|
188
|
+
return request(uri, opts, cb);
|
|
189
|
+
}
|
|
190
|
+
return fn;
|
|
159
191
|
}
|
|
160
192
|
// define like this to please codeintel/intellisense IDEs
|
|
161
193
|
request.get = fn('get');
|