@wiajs/request 3.0.36 → 3.0.39

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,5 +1,5 @@
1
1
  /*!
2
- * wia request v3.0.36
2
+ * wia request v3.0.39
3
3
  * (c) 2022-2025 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
@@ -11,7 +11,6 @@ const assert = require('assert');
11
11
  const http = require('http');
12
12
  const https = require('https');
13
13
  const mime = require('mime-types');
14
- const stream$1 = require('node:stream');
15
14
  const url = require('url');
16
15
  const zlib = require('zlib');
17
16
 
@@ -498,7 +497,7 @@ const HostNotfoundError = utils.createErrorType('ERR_HOSTNOTFOUND', 'DNS 解析
498
497
  const ConnRefusedError = utils.createErrorType('ERR_CONNREFUSED', '连接被拒绝,目标服务器可能不可用');
499
498
  const ConnTimedoutError = utils.createErrorType('ERR_CONNTIMEDOUT', '请求超时,请检查网络连接或服务器负载');
500
499
  const ConnResetError = utils.createErrorType('ERR_CONNRESET', '连接被重置,可能是网络问题或服务器关闭了连接');
501
- let Request = class Request extends stream$1.Duplex {
500
+ let Request = class Request extends stream.Duplex {
502
501
  /**
503
502
  * Executes the next native request (initial or redirect)
504
503
  * @returns http(s) 实例
@@ -526,24 +525,88 @@ let Request = class Request extends stream$1.Duplex {
526
525
  // agents 优于 agent
527
526
  if (agents) {
528
527
  const scheme = protocol.slice(0, -1);
529
- opt.agent = agents[scheme];
528
+ // [FIX] 仅当 agents 中存在对应协议的 agent 时才覆盖 opt.agent
529
+ // 防止 agents['https'] 为空时将 opt.agent 覆盖为 undefined,导致回退到 globalAgent
530
+ if (agents[scheme]) opt.agent = agents[scheme];
530
531
  // http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
531
532
  // 代理内部会根据代理协议选择 http(s) 发起请求创建连接
532
533
  if (protocol === 'http:' && agents.http) {
533
- protocol = agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
534
+ // 注意:这里修改的是局部变量 protocol,用于选择 httpModule
535
+ // protocol 以proxy协议为准
536
+ const { proxy } = agents.http;
537
+ if (proxy && !agents.http.tunnel) protocol = proxy.protocol;
534
538
  }
535
539
  // log({scheme, agents, protocol}, 'request')
536
540
  }
537
541
  const httpModule = httpModules[protocol];
538
542
  if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`);
539
- // log({opt, protocol}, 'request')
540
- // Create the native request and set up its event handlers
541
- // @ts-ignore
543
+ // ============================================================
544
+ // [FIX] 针对 Bun 的终极修复:构建纯净的 Request Options
545
+ // ============================================================
546
+ // 1. 定义标准 Node.js http.request 接受的字段白名单
547
+ // 避免传入 agents, followRedirects 等杂质干扰 Bun 的解析
548
+ const nativeFields = [
549
+ 'agent',
550
+ 'auth',
551
+ 'createConnection',
552
+ 'defaultPort',
553
+ 'family',
554
+ 'headers',
555
+ 'hints',
556
+ 'host',
557
+ 'hostname',
558
+ 'insecureHTTPParser',
559
+ 'localAddress',
560
+ 'localPort',
561
+ 'lookup',
562
+ 'maxHeaderSize',
563
+ 'method',
564
+ 'path',
565
+ 'port',
566
+ 'protocol',
567
+ 'rejectUnauthorized',
568
+ 'servername',
569
+ 'setHost',
570
+ 'socketPath',
571
+ 'timeout',
572
+ 'signal'
573
+ ];
574
+ // 2. 创建纯净的 options 对象
575
+ const reqOpt = {};
576
+ // 3. 显式拷贝字段 (同时也解决了 [Object: null prototype] 问题)
577
+ for (const field of nativeFields){
578
+ if (opt[field] !== undefined) reqOpt[field] = opt[field];
579
+ }
580
+ // 端口修正逻辑 (保持不变)
581
+ if (!reqOpt.port) reqOpt.port = protocol === 'https:' ? 443 : 80;
582
+ else if (reqOpt.port) reqOpt.port = Number(reqOpt.port);
583
+ // 再次显式确保 agent 存在于新对象中 (防御性编程)
584
+ // if (opt.agent) reqOpt.agent = opt.agent
585
+ // ============================================================
586
+ // [FIX] 针对 Bun 的强制代理修复
587
+ // ============================================================
588
+ if (opt.agent) {
589
+ // 1. 仍然赋值 agent,以便 Node.js 环境正常工作
590
+ reqOpt.agent = opt.agent;
591
+ // 2. 【关键】针对 Bun:直接将 Agent 的创建连接方法暴露给 Request
592
+ // Bun 的 http.request 可能忽略 agent 对象,但通常会尊重 createConnection 选项
593
+ reqOpt.createConnection = (args, cb)=>{
594
+ log$1({
595
+ args
596
+ }, 'Direct createConnection triggered via options');
597
+ // 强制调用你的 Agent 的 createConnection
598
+ // 注意:需要确保 this 上下文正确指向 opt.agent
599
+ return opt.agent.createConnection(args, cb);
600
+ };
601
+ }
542
602
  log$1({
543
- httpModule,
544
- opt
603
+ reqOpt,
604
+ protocol,
605
+ agentType: reqOpt.agent ? 'custom' : 'global'
545
606
  }, 'request');
546
- const req = httpModule.request(opt, m._onResponse);
607
+ // Create the native request and set up its event handlers
608
+ // @ts-ignore
609
+ const req = httpModule.request(reqOpt, m._onResponse);
547
610
  m._currentRequest = req;
548
611
  // @ts-ignore
549
612
  req.redirectReq = m;
@@ -552,7 +615,8 @@ let Request = class Request extends stream$1.Duplex {
552
615
  // set tcp keep alive to prevent drop connection by peer
553
616
  req.on('socket', /** @param {*} socket */ (socket)=>{
554
617
  // default interval of sending ack packet is 1 minute
555
- socket.setKeepAlive(true, 1000 * 60);
618
+ // [FIX] 增加 socket 存在性检查,防止极少数情况下的 race condition
619
+ if (socket) socket.setKeepAlive(true, 1000 * 60);
556
620
  });
557
621
  // 请求error单独处理
558
622
  // 'error' 事件处理,避免错误将冒泡到全局导致程序崩溃
@@ -561,8 +625,9 @@ let Request = class Request extends stream$1.Duplex {
561
625
  ;
562
626
  // @ts-ignore
563
627
  log$1.error({
564
- errcode: err == null ? void 0 : err.code
565
- }, 'request');
628
+ errcode: err == null ? void 0 : err.code,
629
+ msg: err == null ? void 0 : err.message
630
+ }, 'request error');
566
631
  // @ts-ignore
567
632
  switch(err == null ? void 0 : err.code){
568
633
  case 'ENOTFOUND':
@@ -577,6 +642,10 @@ let Request = class Request extends stream$1.Duplex {
577
642
  case 'ECONNRESET':
578
643
  m.emit('error', new ConnResetError());
579
644
  break;
645
+ // [FIX] 捕获证书错误,给予更友好的提示(虽然修复后不应再出现此错误)
646
+ case 'DEPTH_ZERO_SELF_SIGNED_CERT':
647
+ m.emit('error', utils.createErrorType('ERR_TLS_CERT', `SSL证书错误(自签名): ${err.message}`));
648
+ break;
580
649
  default:
581
650
  m.emit('error', utils.createErrorType('ERR_CONNOTHER', `网络错误: ${err.message}`));
582
651
  }
@@ -1052,7 +1121,7 @@ let Request = class Request extends stream$1.Duplex {
1052
1121
  }
1053
1122
  // 响应流,用于读
1054
1123
  // @ts-ignore
1055
- responseStream = streams.length > 1 ? stream$1.pipeline(streams, utils.noop) : streams[0];
1124
+ responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
1056
1125
  // 将内部 responseStream 可读流 映射到 redirectReq
1057
1126
  // @ts-ignore
1058
1127
  m.responseStream = responseStream;
@@ -1078,7 +1147,7 @@ let Request = class Request extends stream$1.Duplex {
1078
1147
  }
1079
1148
  // 可读流结束,触发 finished,方便上层清理
1080
1149
  // A cleanup function which removes all registered listeners.
1081
- const offListeners = stream$1.finished(responseStream, ()=>{
1150
+ const offListeners = stream.finished(responseStream, ()=>{
1082
1151
  offListeners() // cleanup
1083
1152
  ;
1084
1153
  this.emit('finished');
package/dist/request.mjs CHANGED
@@ -1,15 +1,14 @@
1
1
  /*!
2
- * wia request v3.0.36
2
+ * wia request v3.0.39
3
3
  * (c) 2022-2025 Sibyl Yu and contributors
4
4
  * Released under the MIT License.
5
5
  */
6
6
  import { log as log$2, name } from '@wiajs/log';
7
- import stream from 'stream';
7
+ import stream, { Duplex } from 'stream';
8
8
  import assert from 'assert';
9
9
  import http from 'http';
10
10
  import https from 'https';
11
11
  import mime from 'mime-types';
12
- import stream$1, { Duplex } from 'node:stream';
13
12
  import url from 'url';
14
13
  import zlib from 'zlib';
15
14
 
@@ -523,24 +522,88 @@ let Request = class Request extends Duplex {
523
522
  // agents 优于 agent
524
523
  if (agents) {
525
524
  const scheme = protocol.slice(0, -1);
526
- opt.agent = agents[scheme];
525
+ // [FIX] 仅当 agents 中存在对应协议的 agent 时才覆盖 opt.agent
526
+ // 防止 agents['https'] 为空时将 opt.agent 覆盖为 undefined,导致回退到 globalAgent
527
+ if (agents[scheme]) opt.agent = agents[scheme];
527
528
  // http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
528
529
  // 代理内部会根据代理协议选择 http(s) 发起请求创建连接
529
530
  if (protocol === 'http:' && agents.http) {
530
- protocol = agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
531
+ // 注意:这里修改的是局部变量 protocol,用于选择 httpModule
532
+ // protocol 以proxy协议为准
533
+ const { proxy } = agents.http;
534
+ if (proxy && !agents.http.tunnel) protocol = proxy.protocol;
531
535
  }
532
536
  // log({scheme, agents, protocol}, 'request')
533
537
  }
534
538
  const httpModule = httpModules[protocol];
535
539
  if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`);
536
- // log({opt, protocol}, 'request')
537
- // Create the native request and set up its event handlers
538
- // @ts-ignore
540
+ // ============================================================
541
+ // [FIX] 针对 Bun 的终极修复:构建纯净的 Request Options
542
+ // ============================================================
543
+ // 1. 定义标准 Node.js http.request 接受的字段白名单
544
+ // 避免传入 agents, followRedirects 等杂质干扰 Bun 的解析
545
+ const nativeFields = [
546
+ 'agent',
547
+ 'auth',
548
+ 'createConnection',
549
+ 'defaultPort',
550
+ 'family',
551
+ 'headers',
552
+ 'hints',
553
+ 'host',
554
+ 'hostname',
555
+ 'insecureHTTPParser',
556
+ 'localAddress',
557
+ 'localPort',
558
+ 'lookup',
559
+ 'maxHeaderSize',
560
+ 'method',
561
+ 'path',
562
+ 'port',
563
+ 'protocol',
564
+ 'rejectUnauthorized',
565
+ 'servername',
566
+ 'setHost',
567
+ 'socketPath',
568
+ 'timeout',
569
+ 'signal'
570
+ ];
571
+ // 2. 创建纯净的 options 对象
572
+ const reqOpt = {};
573
+ // 3. 显式拷贝字段 (同时也解决了 [Object: null prototype] 问题)
574
+ for (const field of nativeFields){
575
+ if (opt[field] !== undefined) reqOpt[field] = opt[field];
576
+ }
577
+ // 端口修正逻辑 (保持不变)
578
+ if (!reqOpt.port) reqOpt.port = protocol === 'https:' ? 443 : 80;
579
+ else if (reqOpt.port) reqOpt.port = Number(reqOpt.port);
580
+ // 再次显式确保 agent 存在于新对象中 (防御性编程)
581
+ // if (opt.agent) reqOpt.agent = opt.agent
582
+ // ============================================================
583
+ // [FIX] 针对 Bun 的强制代理修复
584
+ // ============================================================
585
+ if (opt.agent) {
586
+ // 1. 仍然赋值 agent,以便 Node.js 环境正常工作
587
+ reqOpt.agent = opt.agent;
588
+ // 2. 【关键】针对 Bun:直接将 Agent 的创建连接方法暴露给 Request
589
+ // Bun 的 http.request 可能忽略 agent 对象,但通常会尊重 createConnection 选项
590
+ reqOpt.createConnection = (args, cb)=>{
591
+ log$1({
592
+ args
593
+ }, 'Direct createConnection triggered via options');
594
+ // 强制调用你的 Agent 的 createConnection
595
+ // 注意:需要确保 this 上下文正确指向 opt.agent
596
+ return opt.agent.createConnection(args, cb);
597
+ };
598
+ }
539
599
  log$1({
540
- httpModule,
541
- opt
600
+ reqOpt,
601
+ protocol,
602
+ agentType: reqOpt.agent ? 'custom' : 'global'
542
603
  }, 'request');
543
- const req = httpModule.request(opt, m._onResponse);
604
+ // Create the native request and set up its event handlers
605
+ // @ts-ignore
606
+ const req = httpModule.request(reqOpt, m._onResponse);
544
607
  m._currentRequest = req;
545
608
  // @ts-ignore
546
609
  req.redirectReq = m;
@@ -549,7 +612,8 @@ let Request = class Request extends Duplex {
549
612
  // set tcp keep alive to prevent drop connection by peer
550
613
  req.on('socket', /** @param {*} socket */ (socket)=>{
551
614
  // default interval of sending ack packet is 1 minute
552
- socket.setKeepAlive(true, 1000 * 60);
615
+ // [FIX] 增加 socket 存在性检查,防止极少数情况下的 race condition
616
+ if (socket) socket.setKeepAlive(true, 1000 * 60);
553
617
  });
554
618
  // 请求error单独处理
555
619
  // 'error' 事件处理,避免错误将冒泡到全局导致程序崩溃
@@ -558,8 +622,9 @@ let Request = class Request extends Duplex {
558
622
  ;
559
623
  // @ts-ignore
560
624
  log$1.error({
561
- errcode: err == null ? void 0 : err.code
562
- }, 'request');
625
+ errcode: err == null ? void 0 : err.code,
626
+ msg: err == null ? void 0 : err.message
627
+ }, 'request error');
563
628
  // @ts-ignore
564
629
  switch(err == null ? void 0 : err.code){
565
630
  case 'ENOTFOUND':
@@ -574,6 +639,10 @@ let Request = class Request extends Duplex {
574
639
  case 'ECONNRESET':
575
640
  m.emit('error', new ConnResetError());
576
641
  break;
642
+ // [FIX] 捕获证书错误,给予更友好的提示(虽然修复后不应再出现此错误)
643
+ case 'DEPTH_ZERO_SELF_SIGNED_CERT':
644
+ m.emit('error', utils.createErrorType('ERR_TLS_CERT', `SSL证书错误(自签名): ${err.message}`));
645
+ break;
577
646
  default:
578
647
  m.emit('error', utils.createErrorType('ERR_CONNOTHER', `网络错误: ${err.message}`));
579
648
  }
@@ -1049,7 +1118,7 @@ let Request = class Request extends Duplex {
1049
1118
  }
1050
1119
  // 响应流,用于读
1051
1120
  // @ts-ignore
1052
- responseStream = streams.length > 1 ? stream$1.pipeline(streams, utils.noop) : streams[0];
1121
+ responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
1053
1122
  // 将内部 responseStream 可读流 映射到 redirectReq
1054
1123
  // @ts-ignore
1055
1124
  m.responseStream = responseStream;
@@ -1075,7 +1144,7 @@ let Request = class Request extends Duplex {
1075
1144
  }
1076
1145
  // 可读流结束,触发 finished,方便上层清理
1077
1146
  // A cleanup function which removes all registered listeners.
1078
- const offListeners = stream$1.finished(responseStream, ()=>{
1147
+ const offListeners = stream.finished(responseStream, ()=>{
1079
1148
  offListeners() // cleanup
1080
1149
  ;
1081
1150
  this.emit('finished');
package/lib/request.js CHANGED
@@ -6,7 +6,7 @@ import assert from 'assert';
6
6
  import http from 'http';
7
7
  import https from 'https';
8
8
  import mime from 'mime-types';
9
- import stream, { Duplex } from 'node:stream';
9
+ import stream, { Duplex } from 'stream';
10
10
  import url from 'url';
11
11
  import zlib from 'zlib';
12
12
  import ZlibTransform from './ZlibTransform.js';
@@ -163,24 +163,88 @@ let Request = class Request extends Duplex {
163
163
  // agents 优于 agent
164
164
  if (agents) {
165
165
  const scheme = protocol.slice(0, -1);
166
- opt.agent = agents[scheme];
166
+ // [FIX] 仅当 agents 中存在对应协议的 agent 时才覆盖 opt.agent
167
+ // 防止 agents['https'] 为空时将 opt.agent 覆盖为 undefined,导致回退到 globalAgent
168
+ if (agents[scheme]) opt.agent = agents[scheme];
167
169
  // http 非隧道代理模式,模块以代理主机为准,其他以目的网址为准
168
170
  // 代理内部会根据代理协议选择 http(s) 发起请求创建连接
169
171
  if (protocol === 'http:' && agents.http) {
170
- protocol = agents.http.proxy && !agents.http.tunnel ? agents.http.proxy.protocol : protocol;
172
+ // 注意:这里修改的是局部变量 protocol,用于选择 httpModule
173
+ // protocol 以proxy协议为准
174
+ const { proxy } = agents.http;
175
+ if (proxy && !agents.http.tunnel) protocol = proxy.protocol;
171
176
  }
172
177
  // log({scheme, agents, protocol}, 'request')
173
178
  }
174
179
  const httpModule = httpModules[protocol];
175
180
  if (!httpModule) throw TypeError(`Unsupported protocol: ${protocol}`);
176
- // log({opt, protocol}, 'request')
177
- // Create the native request and set up its event handlers
178
- // @ts-ignore
181
+ // ============================================================
182
+ // [FIX] 针对 Bun 的终极修复:构建纯净的 Request Options
183
+ // ============================================================
184
+ // 1. 定义标准 Node.js http.request 接受的字段白名单
185
+ // 避免传入 agents, followRedirects 等杂质干扰 Bun 的解析
186
+ const nativeFields = [
187
+ 'agent',
188
+ 'auth',
189
+ 'createConnection',
190
+ 'defaultPort',
191
+ 'family',
192
+ 'headers',
193
+ 'hints',
194
+ 'host',
195
+ 'hostname',
196
+ 'insecureHTTPParser',
197
+ 'localAddress',
198
+ 'localPort',
199
+ 'lookup',
200
+ 'maxHeaderSize',
201
+ 'method',
202
+ 'path',
203
+ 'port',
204
+ 'protocol',
205
+ 'rejectUnauthorized',
206
+ 'servername',
207
+ 'setHost',
208
+ 'socketPath',
209
+ 'timeout',
210
+ 'signal'
211
+ ];
212
+ // 2. 创建纯净的 options 对象
213
+ const reqOpt = {};
214
+ // 3. 显式拷贝字段 (同时也解决了 [Object: null prototype] 问题)
215
+ for (const field of nativeFields){
216
+ if (opt[field] !== undefined) reqOpt[field] = opt[field];
217
+ }
218
+ // 端口修正逻辑 (保持不变)
219
+ if (!reqOpt.port) reqOpt.port = protocol === 'https:' ? 443 : 80;
220
+ else if (reqOpt.port) reqOpt.port = Number(reqOpt.port);
221
+ // 再次显式确保 agent 存在于新对象中 (防御性编程)
222
+ // if (opt.agent) reqOpt.agent = opt.agent
223
+ // ============================================================
224
+ // [FIX] 针对 Bun 的强制代理修复
225
+ // ============================================================
226
+ if (opt.agent) {
227
+ // 1. 仍然赋值 agent,以便 Node.js 环境正常工作
228
+ reqOpt.agent = opt.agent;
229
+ // 2. 【关键】针对 Bun:直接将 Agent 的创建连接方法暴露给 Request
230
+ // Bun 的 http.request 可能忽略 agent 对象,但通常会尊重 createConnection 选项
231
+ reqOpt.createConnection = (args, cb)=>{
232
+ log({
233
+ args
234
+ }, 'Direct createConnection triggered via options');
235
+ // 强制调用你的 Agent 的 createConnection
236
+ // 注意:需要确保 this 上下文正确指向 opt.agent
237
+ return opt.agent.createConnection(args, cb);
238
+ };
239
+ }
179
240
  log({
180
- httpModule,
181
- opt
241
+ reqOpt,
242
+ protocol,
243
+ agentType: reqOpt.agent ? 'custom' : 'global'
182
244
  }, 'request');
183
- const req = httpModule.request(opt, m._onResponse);
245
+ // Create the native request and set up its event handlers
246
+ // @ts-ignore
247
+ const req = httpModule.request(reqOpt, m._onResponse);
184
248
  m._currentRequest = req;
185
249
  // @ts-ignore
186
250
  req.redirectReq = m;
@@ -189,7 +253,8 @@ let Request = class Request extends Duplex {
189
253
  // set tcp keep alive to prevent drop connection by peer
190
254
  req.on('socket', /** @param {*} socket */ (socket)=>{
191
255
  // default interval of sending ack packet is 1 minute
192
- socket.setKeepAlive(true, 1000 * 60);
256
+ // [FIX] 增加 socket 存在性检查,防止极少数情况下的 race condition
257
+ if (socket) socket.setKeepAlive(true, 1000 * 60);
193
258
  });
194
259
  // 请求error单独处理
195
260
  // 'error' 事件处理,避免错误将冒泡到全局导致程序崩溃
@@ -198,8 +263,9 @@ let Request = class Request extends Duplex {
198
263
  ;
199
264
  // @ts-ignore
200
265
  log.error({
201
- errcode: err == null ? void 0 : err.code
202
- }, 'request');
266
+ errcode: err == null ? void 0 : err.code,
267
+ msg: err == null ? void 0 : err.message
268
+ }, 'request error');
203
269
  // @ts-ignore
204
270
  switch(err == null ? void 0 : err.code){
205
271
  case 'ENOTFOUND':
@@ -214,6 +280,10 @@ let Request = class Request extends Duplex {
214
280
  case 'ECONNRESET':
215
281
  m.emit('error', new ConnResetError());
216
282
  break;
283
+ // [FIX] 捕获证书错误,给予更友好的提示(虽然修复后不应再出现此错误)
284
+ case 'DEPTH_ZERO_SELF_SIGNED_CERT':
285
+ m.emit('error', utils.createErrorType('ERR_TLS_CERT', `SSL证书错误(自签名): ${err.message}`));
286
+ break;
217
287
  default:
218
288
  m.emit('error', utils.createErrorType('ERR_CONNOTHER', `网络错误: ${err.message}`));
219
289
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@wiajs/request",
3
3
  "description": "Stream HTTP request client.",
4
4
  "keywords": ["http", "simple", "util", "utility"],
5
- "version": "3.0.36",
5
+ "version": "3.0.39",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "types": "types/index.d.ts",
@@ -43,7 +43,7 @@
43
43
  "access": "public"
44
44
  },
45
45
  "engines": {
46
- "node": ">= 6"
46
+ "node": ">= 13.14"
47
47
  },
48
48
  "dependencies": {
49
49
  "@wiajs/log": "^4.3.18",