@wiajs/request 3.0.29 → 3.0.30

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 CHANGED
@@ -1,16 +1,16 @@
1
1
  /*!
2
- * wia request v3.0.29
2
+ * wia request v3.0.30
3
3
  * (c) 2022-2024 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
6
6
  'use strict';
7
7
 
8
+ const stream = require('node:stream');
8
9
  const log$2 = require('@wiajs/log');
9
10
  const http = require('node:http');
10
11
  const https = require('node:https');
11
12
  const assert = require('node:assert');
12
13
  const url = require('node:url');
13
- const stream = require('node:stream');
14
14
  const zlib = require('node:zlib');
15
15
  const mime = require('mime-types');
16
16
 
@@ -467,7 +467,7 @@ const log$1 = log$2.log({env: `wia:req:${log$2.name((typeof document === 'undefi
467
467
  * @prop {*} [beforeRedirect]
468
468
  * @prop {boolean} [followRedirects]
469
469
  * @prop {number} [maxRedirects=21]
470
- * @prop {number} [maxBodyLength = 0]
470
+ * @prop {number} [maxBodyLength = -1]
471
471
  * @prop {*} [trackRedirects]
472
472
  * @prop {*} [data]
473
473
  */
@@ -478,7 +478,7 @@ const log$1 = log$2.log({env: `wia:req:${log$2.name((typeof document === 'undefi
478
478
  * @prop {number} [responseStartTime]
479
479
  */
480
480
 
481
- /** @typedef { http.IncomingMessage & ResponseExt} Response*/
481
+ /** @typedef { http.IncomingMessage & ResponseExt} Response */
482
482
 
483
483
  const httpModules = {'http:': http, 'https:': https};
484
484
 
@@ -528,12 +528,12 @@ const writeEvents = [
528
528
  'connect',
529
529
  'continue',
530
530
  'drain',
531
- 'error',
531
+ // 'error', // 单独处理,未注册 'error' 事件处理程序,错误将冒泡到全局导致程序崩溃
532
532
  'finish',
533
533
  'information',
534
534
  'pipe',
535
535
  // 'response', 由 processResponse 触发
536
- 'socket',
536
+ 'socket', // 建立连接时触发
537
537
  'timeout',
538
538
  'unpipe',
539
539
  'upgrade',
@@ -545,7 +545,7 @@ for (const ev of writeEvents)
545
545
  writeEventEmit[ev] = /** @param {...any} args */ function (...args) {
546
546
  const m = this; // 事件回调,this === clientRequest 实例
547
547
  // log('req event', {ev})
548
- m.redirectReq.emit(ev, ...args); // req 事情映射到 redirectReq 上触发
548
+ m.redirectReq.emit(ev, ...args); // 内部请求req 事情转发到 Request
549
549
  };
550
550
 
551
551
  // stream.Readable,在响应流上转发读流取事件
@@ -578,6 +578,21 @@ const MaxBodyLengthExceededError = utils.createErrorType(
578
578
 
579
579
  const WriteAfterEndError = utils.createErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end');
580
580
 
581
+ // request err
582
+ const HostNotfoundError = utils.createErrorType('ERR_HOSTNOTFOUND', 'DNS 解析失败,主机名可能无效');
583
+ const ConnRefusedError = utils.createErrorType(
584
+ 'ERR_CONNREFUSED',
585
+ '连接被拒绝,目标服务器可能不可用'
586
+ );
587
+ const ConnTimedoutError = utils.createErrorType(
588
+ 'ERR_CONNTIMEDOUT',
589
+ '请求超时,请检查网络连接或服务器负载'
590
+ );
591
+ const ConnResetError = utils.createErrorType(
592
+ 'ERR_CONNRESET',
593
+ '连接被重置,可能是网络问题或服务器关闭了连接'
594
+ );
595
+
581
596
  /**
582
597
  * An HTTP(S) request that can be redirected
583
598
  * wrap http.ClientRequest
@@ -777,7 +792,39 @@ class Request extends stream.Duplex {
777
792
  // 启动 startTimer
778
793
  if (m.startTimer) m._currentRequest.once('socket', m.startTimer);
779
794
 
780
- // 接收req事件,转发 redirectReq 发射
795
+ // set tcp keep alive to prevent drop connection by peer
796
+ req.on(
797
+ 'socket',
798
+ /** @param {*} socket */ socket => {
799
+ // default interval of sending ack packet is 1 minute
800
+ socket.setKeepAlive(true, 1000 * 60);
801
+ }
802
+ );
803
+
804
+ // 请求error单独处理
805
+ // 'error' 事件处理,避免错误将冒泡到全局导致程序崩溃
806
+ req.on('error', err => {
807
+ destroyRequest(req); // 释放资源
808
+ log$1.error({errcode: err.code}, 'request');
809
+ switch (err.code) {
810
+ case 'ENOTFOUND':
811
+ m.emit('error', new HostNotfoundError());
812
+ break
813
+ case 'ECONNREFUSED':
814
+ m.emit('error', new ConnRefusedError());
815
+ break
816
+ case 'ETIMEDOUT':
817
+ m.emit('error', new ConnTimedoutError());
818
+ break
819
+ case 'ECONNRESET':
820
+ m.emit('error', new ConnResetError());
821
+ break
822
+ default:
823
+ m.emit('error', utils.createErrorType('ERR_CONNOTHER', `网络错误: ${err.message}`));
824
+ }
825
+ });
826
+
827
+ // 接收req事件,转发 到 request 上发射,网络关闭事件,触发 error
781
828
  for (const ev of writeEvents) req.on(ev, writeEventEmit[ev]);
782
829
 
783
830
  // RFC7230§5.3.1: When making a request directly to an origin server, […]
@@ -826,9 +873,11 @@ class Request extends stream.Duplex {
826
873
  return R
827
874
  }
828
875
 
876
+ /**
877
+ * 写入错误,释放请求,触发 abort 终止事件
878
+ */
829
879
  abort() {
830
880
  destroyRequest(this._currentRequest);
831
- this._currentRequest.abort();
832
881
  this.emit('abort');
833
882
  }
834
883
 
@@ -892,7 +941,11 @@ class Request extends stream.Duplex {
892
941
  // log({data: chunk, encoding, cb}, 'write')
893
942
 
894
943
  // Writing is not allowed if end has been called
895
- if (m._ending) throw new WriteAfterEndError()
944
+ if (m._ending) {
945
+ // throw new WriteAfterEndError()
946
+ m.emit('error', new WriteAfterEndError());
947
+ return
948
+ }
896
949
 
897
950
  // ! 数据写入时连接,pipe 时可设置 header
898
951
  if (!m._currentRequest) m.request();
@@ -1087,6 +1140,7 @@ class Request extends stream.Duplex {
1087
1140
  m.on('error', clearTimer);
1088
1141
  m.on('response', clearTimer);
1089
1142
  m.on('close', clearTimer);
1143
+
1090
1144
  return m
1091
1145
  }
1092
1146
 
@@ -1277,14 +1331,15 @@ class Request extends stream.Duplex {
1277
1331
  /**
1278
1332
  * 处理响应stream
1279
1333
  * 自动解压,透传流,需设置 decompress = false,避免解压数据
1280
- * @param {*} res
1334
+ * @param {Response} res
1335
+ * @returns {Response | stream.Readable}
1281
1336
  */
1282
1337
  processStream(res) {
1283
1338
  const m = this;
1284
1339
  const {opt} = m;
1285
1340
 
1286
1341
  const streams = [res];
1287
-
1342
+ let responseStream = res;
1288
1343
  // 'transfer-encoding': 'chunked'时,无content-length,axios v1.2 不能自动解压
1289
1344
  const responseLength = +res.headers['content-length'];
1290
1345
 
@@ -1317,6 +1372,7 @@ class Request extends stream.Duplex {
1317
1372
  case 'compress':
1318
1373
  case 'x-compress':
1319
1374
  // add the unzipper to the body stream processing pipeline
1375
+ // @ts-ignore
1320
1376
  streams.push(zlib.createUnzip(zlibOptions));
1321
1377
 
1322
1378
  // remove the content-encoding in order to not confuse downstream operations
@@ -1324,9 +1380,11 @@ class Request extends stream.Duplex {
1324
1380
  break
1325
1381
 
1326
1382
  case 'deflate':
1383
+ // @ts-ignore
1327
1384
  streams.push(new ZlibTransform());
1328
1385
 
1329
1386
  // add the unzipper to the body stream processing pipeline
1387
+ // @ts-ignore
1330
1388
  streams.push(zlib.createUnzip(zlibOptions));
1331
1389
 
1332
1390
  // remove the content-encoding in order to not confuse downstream operations
@@ -1335,6 +1393,7 @@ class Request extends stream.Duplex {
1335
1393
 
1336
1394
  case 'br':
1337
1395
  if (isBrotliSupported) {
1396
+ // @ts-ignore
1338
1397
  streams.push(zlib.createBrotliDecompress(brotliOptions));
1339
1398
  res.headers['content-encoding'] = undefined;
1340
1399
  }
@@ -1343,10 +1402,13 @@ class Request extends stream.Duplex {
1343
1402
  }
1344
1403
 
1345
1404
  // 响应流,用于读
1346
- const responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
1405
+ // @ts-ignore
1406
+ responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
1347
1407
  // 将内部 responseStream 可读流 映射到 redirectReq
1348
1408
 
1409
+ // @ts-ignore
1349
1410
  m.responseStream = responseStream;
1411
+ // @ts-ignore
1350
1412
  responseStream.redirectReq = m; // 事情触发时引用
1351
1413
 
1352
1414
  // stream 模式,事件透传到 请求类
@@ -1518,7 +1580,9 @@ class Request extends stream.Duplex {
1518
1580
  }
1519
1581
 
1520
1582
  /**
1521
- *
1583
+ * 释放请求,触发error事件
1584
+ * 'error' event, and emit a 'close' event.
1585
+ * Calling this will cause remaining data in the response to be dropped and the socket to be destroyed.
1522
1586
  * @param {*} request
1523
1587
  * @param {*} error
1524
1588
  */
@@ -1527,7 +1591,7 @@ function destroyRequest(request, error) {
1527
1591
  request.removeListener(ev, writeEventEmit[ev]);
1528
1592
  }
1529
1593
  request.on('error', utils.noop);
1530
- request.destroy(error);
1594
+ request.destroy(error); // 触发 error 事件
1531
1595
  }
1532
1596
 
1533
1597
  /**
@@ -1569,9 +1633,35 @@ function isSubdomain(subdomain, domain) {
1569
1633
  * 代理模式下,http or https 请求,取决于 proxy 代理服务器,而不是目的服务器。
1570
1634
  */
1571
1635
 
1572
-
1573
1636
  const log = log$2.log({env: `wia:req:${log$2.name((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('request.cjs', document.baseURI).href)))}`}); // __filename
1574
1637
 
1638
+ /** @typedef { import('./request').Response} Response */
1639
+
1640
+ /**
1641
+ * @typedef {object} Opts
1642
+ * @prop {Object.<string,string>} [headers]
1643
+ * @prop {string} [url]
1644
+ * @prop {'http:' | 'https:'} [protocol]
1645
+ * @prop {string} [host]
1646
+ * @prop {string} [family]
1647
+ * @prop {string} [path]
1648
+ * @prop {string} [method]
1649
+ * @prop {*} [agent] - 发送请求的agent
1650
+ * @prop {*} [agents] - http、https agent,根据协议自动选择
1651
+ * @prop {*} [body] - body 数据,优先body,其次data
1652
+ * @prop {*} [data] - body 数据
1653
+ * @prop {boolean} [stream] - 以流的方式工作
1654
+ * @prop {boolean} [decompress=true] - 自动解压
1655
+ * @prop {*} [transformStream]
1656
+ * @prop {*} [beforeRedirect]
1657
+ * @prop {boolean} [followRedirects] - 自动完成重定向
1658
+ * @prop {number} [maxRedirects=21] - 最大重定向次数
1659
+ * @prop {number} [maxBodyLength = 0] - body限制,缺省不限
1660
+ * @prop {*} [trackRedirects]
1661
+ */
1662
+
1663
+ /** @typedef {(res: Response, stream?: stream.Readable) => void} Cb*/
1664
+
1575
1665
  utils.createErrorType(
1576
1666
  'ERR_STREAM_WRITE_BEEN_ABORTED',
1577
1667
  'Request stream has been aborted'
@@ -1596,10 +1686,10 @@ utils.createErrorType(
1596
1686
 
1597
1687
  /**
1598
1688
  * 初始化参数
1599
- * @param {*} uri/opts
1600
- * @param {*} opts/cb
1601
- * @param {*} cb/null
1602
- * @returns
1689
+ * @param {string | Opts} uri/opts
1690
+ * @param {Opts | Cb} [opts] /cb
1691
+ * @param {Cb} [cb]
1692
+ * @returns {{opts: Opts, cb: Cb}}
1603
1693
  */
1604
1694
  function init(uri, opts, cb) {
1605
1695
  let R;
@@ -1608,39 +1698,55 @@ function init(uri, opts, cb) {
1608
1698
  if (utils.isURL(uri)) uri = utils.spreadUrlObject(uri);
1609
1699
  else if (utils.isString(uri)) uri = utils.spreadUrlObject(utils.parseUrl(uri));
1610
1700
  else {
1701
+ // @ts-ignore
1611
1702
  cb = opts;
1703
+ // @ts-ignore
1612
1704
  opts = uri;
1705
+ // @ts-ignore
1613
1706
  const {url} = opts;
1707
+ // 有url,解析
1614
1708
  if (url) {
1709
+ // @ts-ignore
1710
+ // biome-ignore lint/performance/noDelete: <explanation>
1615
1711
  delete opts.url;
1616
1712
  if (utils.isURL(url)) uri = utils.spreadUrlObject(url);
1617
1713
  else if (utils.isString(url)) uri = utils.spreadUrlObject(utils.parseUrl(url));
1618
1714
  } else {
1619
- opts = utils.validateUrl(uri);
1715
+ // @ts-ignore
1716
+ opts = uri; // 不判断 utils.validateUrl(uri)
1620
1717
  uri = {};
1621
1718
  }
1622
1719
  }
1623
1720
 
1624
1721
  if (utils.isFunction(opts)) {
1722
+ // @ts-ignore
1625
1723
  cb = opts;
1626
- opts = null;
1724
+ opts = {};
1627
1725
  }
1628
1726
 
1629
1727
  // copy options
1630
1728
  opts = {
1729
+ // @ts-ignore
1631
1730
  ...uri,
1632
1731
  ...opts,
1633
1732
  };
1634
1733
 
1734
+ // @ts-ignore
1635
1735
  if (!utils.isString(opts.host) && !utils.isString(opts.hostname)) opts.hostname = '::1';
1736
+ // @ts-ignore
1636
1737
  if (opts.method) opts.method = opts.method.toUpperCase();
1637
1738
 
1638
- R = {opts: opts, cb: cb};
1739
+ // @ts-ignore
1740
+ // follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited
1741
+ opts.maxBodyLength = opts.maxBodyLength ?? Number.POSITIVE_INFINITY;
1742
+
1743
+ R = {opts, cb};
1639
1744
  // log({R}, 'init')
1640
1745
  } catch (e) {
1641
1746
  log.err(e, 'init');
1642
1747
  }
1643
1748
 
1749
+ // @ts-ignore
1644
1750
  return R
1645
1751
  }
1646
1752
 
@@ -1651,19 +1757,26 @@ function init(uri, opts, cb) {
1651
1757
  * 注意变参 (options[, callback]) or (url[, options][, callback])
1652
1758
  maxRedirects: _.maxRedirects,
1653
1759
  maxBodyLength: _.maxBodyLength,
1654
- * @param {*} uri/options
1655
- * @param {*} options/callback
1656
- * @param {*} callback/null
1657
- * @returns
1760
+ * @param {string | Opts} uri /options
1761
+ * @param {Opts | Cb} [options] /callback
1762
+ * @param {Cb} [callback] /null
1763
+ * @returns {Request}
1658
1764
  */
1659
1765
  function request(uri, options, callback) {
1660
1766
  let R = null;
1661
1767
 
1662
1768
  try {
1769
+ // @ts-ignore
1663
1770
  const {opts, cb} = init(uri, options, callback);
1664
- // log({uri, options, opts}, 'request')
1665
- const req = new Request(opts, cb);
1771
+ // log.error({uri, options, opts}, 'request')
1772
+
1666
1773
  const {data, stream} = opts;
1774
+ // data 在本函数完成处理,不传递到 request
1775
+ opts.data = undefined;
1776
+
1777
+ // @ts-ignore
1778
+ const req = new Request(opts, cb);
1779
+
1667
1780
  // 非流模式,自动发送请求,流模式通过流写入发送
1668
1781
  if (!stream) {
1669
1782
  // 发送数据
@@ -1690,8 +1803,12 @@ function request(uri, options, callback) {
1690
1803
  }
1691
1804
  });
1692
1805
 
1806
+ // log.error({data}, 'request data.pipe')
1693
1807
  data.pipe(req); // 写入数据流
1694
- } else req.end(data);
1808
+ } else {
1809
+ // log.error({data}, 'request req.end')
1810
+ req.end(data); // 写入数据
1811
+ }
1695
1812
  }
1696
1813
 
1697
1814
  R = req;
@@ -1708,40 +1825,23 @@ function request(uri, options, callback) {
1708
1825
  * 复杂数据,请使用 @wiajs/req库(fork from axios),该库封装了当前库,提供了更多功能
1709
1826
  * organize params for patch, post, put, head, del
1710
1827
  * @param {string} verb
1711
- * @returns {Request} Duplex
1828
+ * @returns {(url: string | Opts, opts?: Opts | Cb, cb?: Cb) => void}}
1712
1829
  */
1713
1830
  function fn(verb) {
1714
1831
  const method = verb.toUpperCase();
1715
-
1716
- // @ts-ignore
1717
- return (uri, options, callback) => {
1718
- const {opts, cb} = init(uri, options, callback);
1832
+ /**
1833
+ *
1834
+ * @param {string | Opts} uri /options
1835
+ * @param {Opts | Cb} [opts] /callback
1836
+ * @param {Cb} [cb] /null
1837
+ * @returns
1838
+ */
1839
+ function fn(uri, opts, cb) {
1840
+ // @ts-ignore
1719
1841
  opts.method = method;
1720
- const req = new Request(opts, cb);
1721
- const {data, stream} = opts;
1722
- // 非流模式,自动发送请求,流模式通过流写入发送
1723
- if (!stream) {
1724
- // 发送数据
1725
- if (utils.isStream(data)) {
1726
-
1727
- data.on('end', () => {
1728
- });
1729
-
1730
- data.once(
1731
- 'error',
1732
- /** @param {*} err */ err => {
1733
- // req.destroy(err)
1734
- }
1735
- );
1736
-
1737
- data.on('close', () => {
1738
- });
1739
-
1740
- data.pipe(req); // 写入数据流
1741
- } else req.end(data);
1742
- }
1743
- return req
1842
+ return request(uri, opts, cb)
1744
1843
  }
1844
+ return fn
1745
1845
  }
1746
1846
 
1747
1847
  // define like this to please codeintel/intellisense IDEs