grpc-libp2p-client 0.0.39 → 0.0.41

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/grpc.js CHANGED
@@ -366,7 +366,6 @@
366
366
  this.dynamicTable.unshift([name, value]);
367
367
  this.dynamicTableSize += size;
368
368
  }
369
- this.dynamicTable.push([name, value]);
370
369
  };
371
370
  // 获取索引的头部
372
371
  HPACK.prototype.getIndexedHeader = function (index) {
@@ -483,22 +482,29 @@
483
482
  // Huffman编码实现
484
483
  HPACK.prototype.huffmanEncode = function (bytes) {
485
484
  var result = [];
485
+ // 使用高精度浮点数累积位,避免 JS 32-bit 有符号整数在位数 >31 时溢出。
486
+ // Huffman 码最长 30 bits,加上未输出的最多 7 bits = 37 bits,超过 32-bit 安全范围。
487
+ // Number 可精确表示 2^53 以内的整数,足够累积多个码字。
486
488
  var current = 0;
487
489
  var bits = 0;
488
490
  for (var i = 0; i < bytes.length; i++) {
489
491
  var b = bytes[i];
490
492
  var code = this.huffmanTable.codes[b];
491
493
  var length_3 = this.huffmanTable.lengths[b];
494
+ // 用乘法左移替代 <<,避免 32-bit 截断
495
+ current = current * (1 << length_3) + code;
492
496
  bits += length_3;
493
- current = (current << length_3) | code;
494
497
  while (bits >= 8) {
495
498
  bits -= 8;
496
- result.push((current >> bits) & 0xFF);
499
+ result.push(Math.floor(current / (1 << bits)) & 0xFF);
500
+ // 保留低 bits 位
501
+ current = current % (1 << bits);
497
502
  }
498
503
  }
499
- // 处理剩余的位
504
+ // 处理剩余的位(用 EOS 填充 1)
500
505
  if (bits > 0) {
501
- current = (current << (8 - bits)) | ((1 << (8 - bits)) - 1);
506
+ var pad = 8 - bits;
507
+ current = current * (1 << pad) + ((1 << pad) - 1);
502
508
  result.push(current & 0xFF);
503
509
  }
504
510
  return new Uint8Array(result);
@@ -521,17 +527,25 @@
521
527
  headers.set(name_3, value);
522
528
  index = newIndex;
523
529
  }
524
- else if ((firstByte & 0x20) !== 0) { // 001xxxxx - Dynamic Table Size Update
525
- index++; // 简单跳过,实际应该更新动态表大小
530
+ else if ((firstByte & 0x20) !== 0) { // 001xxxxx - Dynamic Table Size Update (RFC 7541 §6.3)
531
+ var _c = this.decodeInteger(buffer, index, 5), newSize = _c[0], newIndex = _c[1];
532
+ this.maxDynamicTableSize = newSize;
533
+ // evict entries that exceed the new limit
534
+ while (this.dynamicTableSize > this.maxDynamicTableSize && this.dynamicTable.length > 0) {
535
+ var entry = this.dynamicTable.pop();
536
+ if (entry)
537
+ this.dynamicTableSize -= entry[0].length + entry[1].length + 32;
538
+ }
539
+ index = newIndex;
526
540
  }
527
541
  else if ((firstByte & 0x10) !== 0) { // 0001xxxx - Literal Header Field Never Indexed
528
- var _c = this.decodeLiteralHeaderWithoutIndexing(buffer, index), name_4 = _c[0], value = _c[1], newIndex = _c[2];
542
+ var _d = this.decodeLiteralHeaderWithoutIndexing(buffer, index), name_4 = _d[0], value = _d[1], newIndex = _d[2];
529
543
  if (name_4 && value)
530
544
  headers.set(name_4, value);
531
545
  index = newIndex;
532
546
  }
533
547
  else { // 0000xxxx - Literal Header Field without Indexing
534
- var _d = this.decodeLiteralHeaderWithoutIndexing(buffer, index), name_5 = _d[0], value = _d[1], newIndex = _d[2];
548
+ var _e = this.decodeLiteralHeaderWithoutIndexing(buffer, index), name_5 = _e[0], value = _e[1], newIndex = _e[2];
535
549
  if (name_5 && value)
536
550
  headers.set(name_5, value);
537
551
  index = newIndex;
@@ -594,18 +608,18 @@
594
608
  if (staticIndex <= 0) {
595
609
  return ['', '', newIndex];
596
610
  }
597
- var headerField = this.staticTable[staticIndex];
611
+ var headerField = this.getIndexedHeader(staticIndex);
598
612
  if (!headerField) {
599
613
  return ['', '', newIndex];
600
614
  }
601
615
  return [headerField[0], headerField[1], newIndex];
602
616
  };
603
617
  HPACK.prototype.decodeLiteralHeaderWithIndexing = function (buffer, index) {
604
- var _a = this.decodeInteger(buffer, index, 6), staticIndex = _a[0], nameIndex = _a[1];
605
- index = nameIndex;
618
+ var _a = this.decodeInteger(buffer, index, 6), nameIndex = _a[0], nextIndex = _a[1];
619
+ index = nextIndex;
606
620
  var name;
607
- if (staticIndex > 0) {
608
- var headerField = this.staticTable[staticIndex];
621
+ if (nameIndex > 0) {
622
+ var headerField = this.getIndexedHeader(nameIndex);
609
623
  name = headerField ? headerField[0] : '';
610
624
  }
611
625
  else {
@@ -614,10 +628,26 @@
614
628
  index = newIndex;
615
629
  }
616
630
  var _c = this.decodeLiteralString(buffer, index), value = _c[0], finalIndex = _c[1];
631
+ // RFC 7541 §6.2.1: Literal Header Field with Incremental Indexing must add to dynamic table
632
+ this.addToDynamicTable(name, value);
617
633
  return [name, value, finalIndex];
618
634
  };
619
635
  HPACK.prototype.decodeLiteralHeaderWithoutIndexing = function (buffer, index) {
620
- return this.decodeLiteralHeaderWithIndexing(buffer, index);
636
+ // RFC 7541 §6.2.2 / §6.2.3: 4-bit prefix, do NOT add to dynamic table
637
+ var _a = this.decodeInteger(buffer, index, 4), nameIndex = _a[0], nextIndex = _a[1];
638
+ index = nextIndex;
639
+ var name;
640
+ if (nameIndex > 0) {
641
+ var headerField = this.getIndexedHeader(nameIndex);
642
+ name = headerField ? headerField[0] : '';
643
+ }
644
+ else {
645
+ var _b = this.decodeLiteralString(buffer, index), decodedName = _b[0], newIndex = _b[1];
646
+ name = decodedName;
647
+ index = newIndex;
648
+ }
649
+ var _c = this.decodeLiteralString(buffer, index), value = _c[0], finalIndex = _c[1];
650
+ return [name, value, finalIndex];
621
651
  };
622
652
  // 直接转换为字符串的方法
623
653
  HPACK.prototype.huffmanDecodeToString = function (bytes) {
@@ -711,9 +741,11 @@
711
741
  };
712
742
  var defaultSettings = (_a = {},
713
743
  _a[SETTINGS_PARAMETERS.HEADER_TABLE_SIZE] = 4096,
714
- _a[SETTINGS_PARAMETERS.ENABLE_PUSH] = 1,
744
+ // gRPC 客户端不使用 Server Push,禁用以避免无效的 PUSH_PROMISE 处理
745
+ _a[SETTINGS_PARAMETERS.ENABLE_PUSH] = 0,
715
746
  _a[SETTINGS_PARAMETERS.MAX_CONCURRENT_STREAMS] = 100,
716
- _a[SETTINGS_PARAMETERS.INITIAL_WINDOW_SIZE] = 16 << 10,
747
+ // 匹配 parser 的实际接收缓冲区大小(4MB),避免服务端在单流上过早被限速
748
+ _a[SETTINGS_PARAMETERS.INITIAL_WINDOW_SIZE] = 4 << 20,
717
749
  _a[SETTINGS_PARAMETERS.MAX_FRAME_SIZE] = 16 << 10,
718
750
  _a[SETTINGS_PARAMETERS.MAX_HEADER_LIST_SIZE] = 8192,
719
751
  _a);
@@ -779,8 +811,8 @@
779
811
  // Message-Data
780
812
  grpcMessage.set(data, 5);
781
813
  // 然后将完整的 gRPC 消息分割成多个 HTTP/2 DATA 帧
782
- // HTTP/2 帧头为 9 字节
783
- var maxDataPerFrame = maxFrameSize - 9;
814
+ // maxFrameSize 是 payload 上限(RFC 7540 §6.5.2 MAX_FRAME_SIZE),不含 9 字节帧头
815
+ var maxDataPerFrame = maxFrameSize;
784
816
  for (var offset = 0; offset < grpcMessage.length; offset += maxDataPerFrame) {
785
817
  var remaining = grpcMessage.length - offset;
786
818
  var chunkSize = Math.min(maxDataPerFrame, remaining);
@@ -813,14 +845,15 @@
813
845
  var flags = endStream ? 0x01 : 0x0; // END_STREAM flag
814
846
  return Http2Frame.createFrame(0x0, flags, streamId, framedData);
815
847
  };
816
- Http2Frame.createHeadersFrame = function (streamId, path, endHeaders, token) {
848
+ Http2Frame.createHeadersFrame = function (streamId, path, endHeaders, token, authority) {
817
849
  if (endHeaders === void 0) { endHeaders = true; }
850
+ if (authority === void 0) { authority = 'localhost'; }
818
851
  // gRPC-Web 需要的标准 headers
819
852
  var headersList = {
820
853
  ':path': path,
821
854
  ':method': 'POST',
822
855
  ':scheme': 'http',
823
- ':authority': 'localhost',
856
+ ':authority': authority,
824
857
  'content-type': 'application/grpc+proto',
825
858
  'user-agent': 'grpc-web-client/0.1',
826
859
  'accept': 'application/grpc+proto',
@@ -954,7 +987,11 @@
954
987
  var HTTP2Parser = /** @class */ (function () {
955
988
  function HTTP2Parser(writer, options) {
956
989
  var _a;
957
- this.buffer = new Uint8Array(0);
990
+ /** 分段缓冲:避免每次 chunk 到达时 O(n) 全量拷贝 */
991
+ this.bufferChunks = [];
992
+ this.bufferTotalLength = 0;
993
+ this.bufferChunks = [];
994
+ this.bufferTotalLength = 0;
958
995
  this.settingsAckReceived = false;
959
996
  this.peerSettingsReceived = false;
960
997
  // 初始化连接级别的流控制窗口大小(默认值:65,535)
@@ -968,15 +1005,51 @@
968
1005
  this.sendStreamWindows = new Map();
969
1006
  this.peerInitialStreamWindow = 65535;
970
1007
  this.sendWindowWaiters = [];
1008
+ this.settingsAckWaiters = [];
1009
+ this.peerSettingsWaiters = [];
1010
+ this.endOfStreamWaiters = [];
971
1011
  // 结束标志
972
1012
  this.endFlag = false;
973
1013
  this.writer = writer;
974
1014
  this.compatibilityMode = (_a = options === null || options === void 0 ? void 0 : options.compatibilityMode) !== null && _a !== void 0 ? _a : false;
975
1015
  }
1016
+ Object.defineProperty(HTTP2Parser.prototype, "buffer", {
1017
+ /** 兼容旧代码读取 buffer —— 仅在必须全量访问时调用 _flattenBuffer() */
1018
+ get: function () { return this._flattenBuffer(); },
1019
+ set: function (v) { this.bufferChunks = v.length ? [v] : []; this.bufferTotalLength = v.length; },
1020
+ enumerable: false,
1021
+ configurable: true
1022
+ });
1023
+ /** 将所有分段合并为一个连续 Uint8Array(仅在必要时调用)*/
1024
+ HTTP2Parser.prototype._flattenBuffer = function () {
1025
+ if (this.bufferChunks.length === 0)
1026
+ return new Uint8Array(0);
1027
+ if (this.bufferChunks.length === 1)
1028
+ return this.bufferChunks[0];
1029
+ var out = new Uint8Array(this.bufferTotalLength);
1030
+ var off = 0;
1031
+ for (var _i = 0, _a = this.bufferChunks; _i < _a.length; _i++) {
1032
+ var c = _a[_i];
1033
+ out.set(c, off);
1034
+ off += c.length;
1035
+ }
1036
+ return out;
1037
+ };
1038
+ /** 唤醒所有发送窗口等待者 */
1039
+ HTTP2Parser.prototype._wakeWindowWaiters = function () {
1040
+ var ws = this.sendWindowWaiters.splice(0);
1041
+ for (var _i = 0, ws_1 = ws; _i < ws_1.length; _i++) {
1042
+ var w = ws_1[_i];
1043
+ try {
1044
+ w.resolve();
1045
+ }
1046
+ catch ( /* ignore */_a) { /* ignore */ }
1047
+ }
1048
+ };
976
1049
  // 持续处理流数据
977
1050
  HTTP2Parser.prototype.processStream = function (stream) {
978
1051
  return __awaiter(this, void 0, void 0, function () {
979
- var chunk, e_1_1, error_1;
1052
+ var chunk, e_1_1, error_1, errMsg;
980
1053
  var _a, stream_1, stream_1_1;
981
1054
  var _b, e_1, _c, _d;
982
1055
  var _e;
@@ -1020,7 +1093,6 @@
1020
1093
  case 12:
1021
1094
  // Stream 结束后的清理工作
1022
1095
  if (!this.compatibilityMode && !this.endFlag) {
1023
- this.endFlag = true;
1024
1096
  try {
1025
1097
  (_e = this.onEnd) === null || _e === void 0 ? void 0 : _e.call(this);
1026
1098
  }
@@ -1028,9 +1100,23 @@
1028
1100
  console.error("Error during onEnd callback:", err);
1029
1101
  }
1030
1102
  }
1103
+ // 无论何种模式,stream 结束时都通知 waitForEndOfStream 等待者,
1104
+ // 防止 compatibilityMode=true(server-streaming)时 waitForEndOfStream(0) 永久挂死
1105
+ if (!this.endFlag) {
1106
+ this._notifyEndOfStream();
1107
+ }
1031
1108
  return [3 /*break*/, 14];
1032
1109
  case 13:
1033
1110
  error_1 = _f.sent();
1111
+ // 确保 waitForEndOfStream 等待者得到通知,防止 operationPromise 后台挂死
1112
+ if (!this.endFlag) {
1113
+ this._notifyEndOfStream();
1114
+ }
1115
+ errMsg = error_1 instanceof Error ? error_1.message : String(error_1);
1116
+ if (/cleanup/i.test(errMsg)) {
1117
+ // 预期的主动清理,无需 re-throw,.catch in index.ts 不需要处理它
1118
+ return [2 /*return*/];
1119
+ }
1034
1120
  console.error("Error processing stream:", error_1);
1035
1121
  throw error_1;
1036
1122
  case 14: return [2 /*return*/];
@@ -1038,45 +1124,61 @@
1038
1124
  });
1039
1125
  });
1040
1126
  };
1041
- // 处理单个数据块
1127
+ // 处理单个数据块 — 分段列表追加,避免每次 O(n) 全量拷贝
1042
1128
  HTTP2Parser.prototype._processChunk = function (chunk) {
1043
1129
  // chunk 是 Uint8ArrayList 或 Uint8Array
1044
1130
  var newData = 'subarray' in chunk && typeof chunk.subarray === 'function'
1045
1131
  ? chunk.subarray()
1046
1132
  : chunk;
1047
- // 原作者之前的 O(N) 内存拷贝优化被保留,去掉了存在 onEnd 竞态的 setTimeout
1048
- var newBuffer = new Uint8Array(this.buffer.length + newData.length);
1049
- newBuffer.set(this.buffer);
1050
- newBuffer.set(newData, this.buffer.length);
1051
- this.buffer = newBuffer;
1133
+ // 追加到分段列表,O(1),不拷贝历史数据
1134
+ if (newData.length > 0) {
1135
+ this.bufferChunks.push(newData);
1136
+ this.bufferTotalLength += newData.length;
1137
+ }
1138
+ // 将所有分段合并为一块后处理帧(只合并一次,后续 slice 替换)
1139
+ // 仅在确实有完整帧时才触发合并,碎片仅 push 不合并
1140
+ if (this.bufferTotalLength < 9)
1141
+ return;
1142
+ // 合并一次
1143
+ var flat = this._flattenBuffer();
1144
+ this.bufferChunks = [flat];
1145
+ // bufferTotalLength 保持不变
1052
1146
  // 持续处理所有完整的帧
1053
1147
  var readOffset = 0;
1054
- while (this.buffer.length - readOffset >= 9) {
1148
+ while (flat.length - readOffset >= 9) {
1055
1149
  // 判断是否有HTTP/2前导
1056
- if (this.buffer.length - readOffset >= 24 && this.isHttp2Preface(this.buffer.subarray(readOffset))) {
1150
+ if (flat.length - readOffset >= 24 && this.isHttp2Preface(flat.subarray(readOffset))) {
1057
1151
  readOffset += 24;
1058
1152
  // 发送SETTINGS帧
1059
1153
  var settingFrame = Http2Frame.createSettingsFrame();
1060
1154
  this.writer.write(settingFrame);
1061
1155
  continue;
1062
1156
  }
1063
- var frameHeader = this._parseFrameHeader(this.buffer.subarray(readOffset));
1157
+ var frameHeader = this._parseFrameHeader(flat.subarray(readOffset));
1064
1158
  var totalFrameLength = 9 + frameHeader.length;
1065
1159
  // 检查是否有完整的帧
1066
- if (this.buffer.length - readOffset < totalFrameLength) {
1160
+ if (flat.length - readOffset < totalFrameLength) {
1067
1161
  break;
1068
1162
  }
1069
- // 获取完整帧数据
1070
- var frameData = this.buffer.subarray(readOffset, readOffset + totalFrameLength);
1163
+ // 获取完整帧数据(subarray 视图,零拷贝)
1164
+ var frameData = flat.subarray(readOffset, readOffset + totalFrameLength);
1071
1165
  // 处理不同类型的帧
1072
1166
  this._handleFrame(frameHeader, frameData).catch(function (err) {
1073
1167
  console.error("Error handling frame:", err);
1074
1168
  });
1075
- // 移动偏移量
1076
1169
  readOffset += totalFrameLength;
1077
1170
  }
1171
+ // 保留未消费的尾部字节(slice 一次,后续仍分段追加)
1078
1172
  if (readOffset > 0) {
1079
- this.buffer = this.buffer.slice(readOffset);
1173
+ if (readOffset >= flat.length) {
1174
+ this.bufferChunks = [];
1175
+ this.bufferTotalLength = 0;
1176
+ }
1177
+ else {
1178
+ var remaining = flat.slice(readOffset);
1179
+ this.bufferChunks = [remaining];
1180
+ this.bufferTotalLength = remaining.length;
1181
+ }
1080
1182
  }
1081
1183
  };
1082
1184
  HTTP2Parser.prototype.isHttp2Preface = function (buffer) {
@@ -1088,11 +1190,7 @@
1088
1190
  }
1089
1191
  return true;
1090
1192
  };
1091
- // 移除之前的 for await 循环代码
1092
- HTTP2Parser.prototype._oldProcessStream_removed = function () {
1093
- // 这个方法已被上面的事件驱动实现替代
1094
- };
1095
- // 等待SETTINGS ACK
1193
+ // 等待SETTINGS ACK 事件驱动,无轮询
1096
1194
  HTTP2Parser.prototype.waitForSettingsAck = function () {
1097
1195
  var _this = this;
1098
1196
  return new Promise(function (resolve, reject) {
@@ -1100,20 +1198,32 @@
1100
1198
  resolve();
1101
1199
  return;
1102
1200
  }
1103
- var interval = setInterval(function () {
1104
- if (_this.settingsAckReceived) {
1105
- clearInterval(interval);
1106
- clearTimeout(timeout);
1107
- resolve();
1108
- }
1109
- }, 100);
1201
+ var waiter = { resolve: resolve, reject: reject };
1202
+ _this.settingsAckWaiters.push(waiter);
1110
1203
  var timeout = setTimeout(function () {
1111
- clearInterval(interval);
1204
+ var idx = _this.settingsAckWaiters.indexOf(waiter);
1205
+ if (idx >= 0)
1206
+ _this.settingsAckWaiters.splice(idx, 1);
1112
1207
  reject(new Error("Settings ACK timeout"));
1113
1208
  }, 30000);
1209
+ // 覆盖 resolve 以便超时前自动清理定时器
1210
+ waiter.resolve = function () { clearTimeout(timeout); resolve(); };
1211
+ waiter.reject = function (e) { clearTimeout(timeout); reject(e); };
1114
1212
  });
1115
1213
  };
1116
- // 等待接收来自对端的 SETTINGS(非 ACK
1214
+ /** 内部调用:SETTINGS ACK 收到时唤醒所有等待者 */
1215
+ HTTP2Parser.prototype._notifySettingsAck = function () {
1216
+ this.settingsAckReceived = true;
1217
+ var ws = this.settingsAckWaiters.splice(0);
1218
+ for (var _i = 0, ws_2 = ws; _i < ws_2.length; _i++) {
1219
+ var w = ws_2[_i];
1220
+ try {
1221
+ w.resolve();
1222
+ }
1223
+ catch ( /* ignore */_a) { /* ignore */ }
1224
+ }
1225
+ };
1226
+ // 等待接收来自对端的 SETTINGS(非 ACK)— 事件驱动,无轮询
1117
1227
  HTTP2Parser.prototype.waitForPeerSettings = function (timeoutMs) {
1118
1228
  var _this = this;
1119
1229
  if (timeoutMs === void 0) { timeoutMs = 30000; }
@@ -1122,19 +1232,30 @@
1122
1232
  resolve();
1123
1233
  return;
1124
1234
  }
1125
- var interval = setInterval(function () {
1126
- if (_this.peerSettingsReceived) {
1127
- clearInterval(interval);
1128
- clearTimeout(timeout);
1129
- resolve();
1130
- }
1131
- }, 100);
1235
+ var waiter = { resolve: resolve, reject: reject };
1236
+ _this.peerSettingsWaiters.push(waiter);
1132
1237
  var timeout = setTimeout(function () {
1133
- clearInterval(interval);
1238
+ var idx = _this.peerSettingsWaiters.indexOf(waiter);
1239
+ if (idx >= 0)
1240
+ _this.peerSettingsWaiters.splice(idx, 1);
1134
1241
  reject(new Error("Peer SETTINGS timeout"));
1135
1242
  }, timeoutMs);
1243
+ waiter.resolve = function () { clearTimeout(timeout); resolve(); };
1244
+ waiter.reject = function (e) { clearTimeout(timeout); reject(e); };
1136
1245
  });
1137
1246
  };
1247
+ /** 内部调用:收到对端 SETTINGS(非 ACK)时唤醒等待者 */
1248
+ HTTP2Parser.prototype._notifyPeerSettings = function () {
1249
+ this.peerSettingsReceived = true;
1250
+ var ws = this.peerSettingsWaiters.splice(0);
1251
+ for (var _i = 0, ws_3 = ws; _i < ws_3.length; _i++) {
1252
+ var w = ws_3[_i];
1253
+ try {
1254
+ w.resolve();
1255
+ }
1256
+ catch ( /* ignore */_a) { /* ignore */ }
1257
+ }
1258
+ };
1138
1259
  // 注册我们要发送数据的出站流(用于初始化该流的对端窗口)
1139
1260
  HTTP2Parser.prototype.registerOutboundStream = function (streamId) {
1140
1261
  if (!this.sendStreamWindows.has(streamId)) {
@@ -1164,75 +1285,67 @@
1164
1285
  this.sendConnWindow = Math.min(0x7fffffff, this.sendConnWindow + bytes);
1165
1286
  var cur = (_a = this.sendStreamWindows.get(streamId)) !== null && _a !== void 0 ? _a : 0;
1166
1287
  this.sendStreamWindows.set(streamId, Math.min(0x7fffffff, cur + bytes));
1288
+ // 窗口增大,唤醒等待者
1289
+ this._wakeWindowWaiters();
1167
1290
  };
1168
- // 等待可用发送窗口(两个窗口都需要 >0)
1169
- HTTP2Parser.prototype.waitForSendWindow = function (streamId_1) {
1170
- return __awaiter(this, arguments, void 0, function (streamId, minBytes, timeoutMs) {
1171
- var start;
1172
- var _this = this;
1173
- if (minBytes === void 0) { minBytes = 1; }
1174
- if (timeoutMs === void 0) { timeoutMs = 30000; }
1175
- return __generator(this, function (_a) {
1176
- start = Date.now();
1177
- return [2 /*return*/, new Promise(function (resolve, reject) {
1178
- var interval = null;
1179
- var settled = false;
1180
- var check = function () {
1181
- var _a = _this.getSendWindows(streamId), conn = _a.conn, stream = _a.stream;
1182
- if (conn >= minBytes && stream >= minBytes) {
1183
- if (!settled) {
1184
- settled = true;
1185
- if (interval) {
1186
- clearInterval(interval);
1187
- interval = null;
1188
- }
1189
- resolve();
1190
- }
1191
- return true;
1192
- }
1193
- if (Date.now() - start > timeoutMs) {
1194
- if (!settled) {
1195
- settled = true;
1196
- if (interval) {
1197
- clearInterval(interval);
1198
- interval = null;
1199
- }
1200
- reject(new Error('Send window wait timeout'));
1201
- }
1202
- return true;
1203
- }
1204
- return false;
1205
- };
1206
- if (check())
1207
- return;
1208
- var tick = function () {
1209
- if (!check()) ;
1210
- };
1211
- var wake = function () { tick(); };
1212
- // 简单的等待模型:依赖 WINDOW_UPDATE 到达时调用 wake
1213
- _this.sendWindowWaiters.push(wake);
1214
- // 同时做一个轻微的轮询,防止错过唤醒
1215
- interval = setInterval(function () {
1216
- if (check() && interval) {
1217
- clearInterval(interval);
1218
- interval = null;
1219
- }
1220
- }, 50);
1221
- })];
1222
- });
1291
+ // 等待可用发送窗口 — 事件驱动,WINDOW_UPDATE/SETTINGS 收到时直接唤醒
1292
+ HTTP2Parser.prototype.waitForSendWindow = function (streamId, minBytes, timeoutMs) {
1293
+ var _this = this;
1294
+ if (minBytes === void 0) { minBytes = 1; }
1295
+ if (timeoutMs === void 0) { timeoutMs = 30000; }
1296
+ var _a = this.getSendWindows(streamId), conn = _a.conn, stream = _a.stream;
1297
+ if (conn >= minBytes && stream >= minBytes)
1298
+ return Promise.resolve();
1299
+ return new Promise(function (resolve, reject) {
1300
+ var settled = false;
1301
+ var timeout = timeoutMs > 0
1302
+ ? setTimeout(function () {
1303
+ if (settled)
1304
+ return;
1305
+ settled = true;
1306
+ var idx = _this.sendWindowWaiters.findIndex(function (w) { return w.resolve === resolveWrap; });
1307
+ if (idx >= 0)
1308
+ _this.sendWindowWaiters.splice(idx, 1);
1309
+ reject(new Error('Send window wait timeout'));
1310
+ }, timeoutMs)
1311
+ : undefined;
1312
+ var resolveWrap = function () {
1313
+ if (settled)
1314
+ return;
1315
+ var _a = _this.getSendWindows(streamId), c2 = _a.conn, s2 = _a.stream;
1316
+ if (c2 >= minBytes && s2 >= minBytes) {
1317
+ settled = true;
1318
+ if (timeout)
1319
+ clearTimeout(timeout);
1320
+ resolve();
1321
+ }
1322
+ else {
1323
+ // 窗口仍不够,重新入队等待下一次更新
1324
+ _this.sendWindowWaiters.push({ resolve: resolveWrap, reject: rejectWrap });
1325
+ }
1326
+ };
1327
+ var rejectWrap = function (e) {
1328
+ if (settled)
1329
+ return;
1330
+ settled = true;
1331
+ if (timeout)
1332
+ clearTimeout(timeout);
1333
+ reject(e);
1334
+ };
1335
+ _this.sendWindowWaiters.push({ resolve: resolveWrap, reject: rejectWrap });
1223
1336
  });
1224
1337
  };
1225
1338
  // 处理单个帧
1226
1339
  HTTP2Parser.prototype._handleFrame = function (frameHeader, frameData) {
1227
1340
  return __awaiter(this, void 0, void 0, function () {
1228
- var settingsPayload, settings, initialWindowDelta_1, maxConcurrentStreams, i, id, value, payload, waiters, streamWindowUpdate, connWindowUpdate, inc, cur, waiters, info, body, view, lastStreamId, errorCode;
1341
+ var settingsPayload, settings, initialWindowDelta_1, maxConcurrentStreams, i, id, value, payload, dataLength, streamWindowUpdate, connWindowUpdate, result, cur, info, body, view, lastStreamId, errorCode;
1229
1342
  var _this = this;
1230
- var _a, _b, _c, _d, _e;
1231
- return __generator(this, function (_f) {
1343
+ var _a, _b, _c, _d, _e, _f, _g;
1344
+ return __generator(this, function (_h) {
1232
1345
  switch (frameHeader.type) {
1233
1346
  case FRAME_TYPES.SETTINGS:
1234
1347
  if ((frameHeader.flags & FRAME_FLAGS.ACK) === FRAME_FLAGS.ACK) {
1235
- this.settingsAckReceived = true;
1348
+ this._notifySettingsAck();
1236
1349
  }
1237
1350
  else {
1238
1351
  settingsPayload = frameData.slice(9);
@@ -1241,10 +1354,10 @@
1241
1354
  maxConcurrentStreams = void 0;
1242
1355
  for (i = 0; i < settingsPayload.length; i += 6) {
1243
1356
  id = (settingsPayload[i] << 8) | settingsPayload[i + 1];
1244
- value = (settingsPayload[i + 2] << 24) |
1357
+ value = ((settingsPayload[i + 2] << 24) |
1245
1358
  (settingsPayload[i + 3] << 16) |
1246
1359
  (settingsPayload[i + 4] << 8) |
1247
- settingsPayload[i + 5];
1360
+ settingsPayload[i + 5]) >>> 0;
1248
1361
  settings[id] = value;
1249
1362
  if (id === 4) {
1250
1363
  // SETTINGS_INITIAL_WINDOW_SIZE
@@ -1283,45 +1396,41 @@
1283
1396
  if (this.onSettings) {
1284
1397
  this.onSettings(frameHeader);
1285
1398
  }
1286
- // 标记已收到对端 SETTINGS
1287
- this.peerSettingsReceived = true;
1288
- waiters = this.sendWindowWaiters.splice(0);
1289
- waiters.forEach(function (fn) { try {
1290
- fn();
1291
- }
1292
- catch (e) {
1293
- console.debug('waiter error', e);
1294
- } });
1399
+ // 标记已收到对端 SETTINGS 并唤醒等待者
1400
+ this._notifyPeerSettings();
1401
+ // 唤醒发送窗口等待者(以防部分实现通过 SETTINGS 改变有效窗口)
1402
+ this._wakeWindowWaiters();
1295
1403
  }
1296
1404
  break;
1297
- case FRAME_TYPES.DATA:
1405
+ case FRAME_TYPES.DATA: {
1298
1406
  // 处理数据帧
1299
1407
  if (this.onData) {
1300
1408
  this.onData(frameData.slice(9), frameHeader); // 跳过帧头
1301
1409
  }
1302
- // 更新流窗口和连接窗口
1303
- try {
1304
- // 更新流级别的窗口
1305
- if (frameHeader.streamId !== 0) {
1306
- streamWindowUpdate = Http2Frame.createWindowUpdateFrame(frameHeader.streamId, (_a = frameHeader.length) !== null && _a !== void 0 ? _a : 0);
1307
- this.writer.write(streamWindowUpdate);
1410
+ dataLength = (_a = frameHeader.length) !== null && _a !== void 0 ? _a : 0;
1411
+ if (dataLength > 0) {
1412
+ try {
1413
+ // 更新流级别的窗口
1414
+ if (frameHeader.streamId !== 0) {
1415
+ streamWindowUpdate = Http2Frame.createWindowUpdateFrame(frameHeader.streamId, dataLength);
1416
+ this.writer.write(streamWindowUpdate);
1417
+ }
1418
+ connWindowUpdate = Http2Frame.createWindowUpdateFrame(0, dataLength);
1419
+ this.writer.write(connWindowUpdate);
1420
+ }
1421
+ catch (err) {
1422
+ console.error("[HTTP2] Error sending window update:", err);
1308
1423
  }
1309
- connWindowUpdate = Http2Frame.createWindowUpdateFrame(0, (_b = frameHeader.length) !== null && _b !== void 0 ? _b : 0);
1310
- this.writer.write(connWindowUpdate);
1311
- }
1312
- catch (err) {
1313
- console.error("[HTTP2] Error sending window update:", err);
1314
1424
  }
1315
1425
  //判断是否是最后一个帧
1316
1426
  if ((frameHeader.flags & FRAME_FLAGS.END_STREAM) ===
1317
1427
  FRAME_FLAGS.END_STREAM) {
1318
- this.endFlag = true;
1319
- if (this.onEnd) {
1320
- this.onEnd();
1321
- }
1428
+ (_b = this.onEnd) === null || _b === void 0 ? void 0 : _b.call(this);
1429
+ this._notifyEndOfStream();
1322
1430
  return [2 /*return*/];
1323
1431
  }
1324
1432
  break;
1433
+ }
1325
1434
  case FRAME_TYPES.HEADERS:
1326
1435
  // 处理头部帧
1327
1436
  if (this.onHeaders) {
@@ -1330,35 +1439,26 @@
1330
1439
  //判断是否是最后一个帧
1331
1440
  if ((frameHeader.flags & FRAME_FLAGS.END_STREAM) ===
1332
1441
  FRAME_FLAGS.END_STREAM) {
1333
- this.endFlag = true;
1334
- if (this.onEnd) {
1335
- this.onEnd();
1336
- }
1442
+ (_c = this.onEnd) === null || _c === void 0 ? void 0 : _c.call(this);
1443
+ this._notifyEndOfStream();
1337
1444
  return [2 /*return*/];
1338
1445
  }
1339
1446
  break;
1340
1447
  case FRAME_TYPES.WINDOW_UPDATE:
1341
- // 处理窗口更新帧
1342
- this.handleWindowUpdateFrame(frameHeader, frameData);
1343
- // 更新发送窗口(对端接收窗口)
1448
+ // 处理窗口更新帧(同时更新接收侧诊断计数器和发送侧流控窗口,只解析一次)
1344
1449
  try {
1345
- inc = this.parseWindowUpdateFrame(frameData, frameHeader).windowSizeIncrement;
1450
+ result = this.handleWindowUpdateFrame(frameHeader, frameData);
1451
+ // 更新发送方向窗口(对端的接收窗口)
1346
1452
  if (frameHeader.streamId === 0) {
1347
- this.sendConnWindow += inc;
1453
+ this.sendConnWindow += result.windowSizeIncrement;
1348
1454
  }
1349
1455
  else {
1350
- cur = (_c = this.sendStreamWindows.get(frameHeader.streamId)) !== null && _c !== void 0 ? _c : this.peerInitialStreamWindow;
1351
- this.sendStreamWindows.set(frameHeader.streamId, cur + inc);
1456
+ cur = (_d = this.sendStreamWindows.get(frameHeader.streamId)) !== null && _d !== void 0 ? _d : this.peerInitialStreamWindow;
1457
+ this.sendStreamWindows.set(frameHeader.streamId, cur + result.windowSizeIncrement);
1352
1458
  }
1353
- waiters = this.sendWindowWaiters.splice(0);
1354
- waiters.forEach(function (fn) { try {
1355
- fn();
1356
- }
1357
- catch (e) {
1358
- console.debug('waiter error', e);
1359
- } });
1459
+ this._wakeWindowWaiters();
1360
1460
  }
1361
- catch ( /* ignore WINDOW_UPDATE parse errors */_g) { /* ignore WINDOW_UPDATE parse errors */ }
1461
+ catch ( /* ignore WINDOW_UPDATE parse errors (e.g. increment=0 is RFC PROTOCOL_ERROR) */_j) { /* ignore WINDOW_UPDATE parse errors (e.g. increment=0 is RFC PROTOCOL_ERROR) */ }
1362
1462
  break;
1363
1463
  case FRAME_TYPES.PING:
1364
1464
  // 处理PING帧
@@ -1380,20 +1480,20 @@
1380
1480
  info = {};
1381
1481
  }
1382
1482
  }
1383
- catch ( /* ignore GOAWAY parse errors */_h) { /* ignore GOAWAY parse errors */ }
1483
+ catch ( /* ignore GOAWAY parse errors */_k) { /* ignore GOAWAY parse errors */ }
1384
1484
  try {
1385
- (_d = this.onGoaway) === null || _d === void 0 ? void 0 : _d.call(this, info !== null && info !== void 0 ? info : {});
1485
+ (_e = this.onGoaway) === null || _e === void 0 ? void 0 : _e.call(this, info !== null && info !== void 0 ? info : {});
1386
1486
  }
1387
1487
  catch (err) {
1388
1488
  console.error('Error during GOAWAY callback:', err);
1389
1489
  }
1390
- this.endFlag = true;
1391
1490
  try {
1392
- (_e = this.onEnd) === null || _e === void 0 ? void 0 : _e.call(this);
1491
+ (_f = this.onEnd) === null || _f === void 0 ? void 0 : _f.call(this);
1393
1492
  }
1394
1493
  catch (err) {
1395
1494
  console.error('Error during GOAWAY onEnd callback:', err);
1396
1495
  }
1496
+ this._notifyEndOfStream();
1397
1497
  break;
1398
1498
  }
1399
1499
  // case FRAME_TYPES.PUSH_PROMISE:
@@ -1401,10 +1501,8 @@
1401
1501
  // this.handlePushPromiseFrame(frameHeader, frameData);
1402
1502
  // break;
1403
1503
  case FRAME_TYPES.RST_STREAM:
1404
- this.endFlag = true;
1405
- if (this.onEnd) {
1406
- this.onEnd();
1407
- }
1504
+ (_g = this.onEnd) === null || _g === void 0 ? void 0 : _g.call(this);
1505
+ this._notifyEndOfStream();
1408
1506
  break;
1409
1507
  default:
1410
1508
  console.debug("Unknown frame type:", frameHeader.type);
@@ -1417,7 +1515,8 @@
1417
1515
  var length = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
1418
1516
  var type = buffer[3];
1419
1517
  var flags = buffer[4];
1420
- var streamId = (buffer[5] << 24) | (buffer[6] << 16) | (buffer[7] << 8) | buffer[8];
1518
+ // RFC 7540 §4.1: most significant bit is reserved and MUST be ignored on receipt
1519
+ var streamId = ((buffer[5] << 24) | (buffer[6] << 16) | (buffer[7] << 8) | buffer[8]) & 0x7fffffff;
1421
1520
  return {
1422
1521
  length: length,
1423
1522
  type: type,
@@ -1446,53 +1545,42 @@
1446
1545
  throw error;
1447
1546
  }
1448
1547
  };
1449
- //等待流结束
1548
+ // 等待流结束 — 事件驱动,onEnd 触发时直接唤醒,无 setInterval 轮询
1450
1549
  HTTP2Parser.prototype.waitForEndOfStream = function (waitTime) {
1451
1550
  var _this = this;
1452
1551
  return new Promise(function (resolve, reject) {
1453
- // If the stream has already ended, resolve immediately
1454
1552
  if (_this.endFlag) {
1455
1553
  resolve();
1456
1554
  return;
1457
1555
  }
1458
- // 如果是0 ,则不设置超时
1459
- var timeout = null;
1460
- if (waitTime > 0) {
1461
- timeout = setTimeout(function () {
1462
- clearInterval(interval);
1556
+ var waiter = { resolve: resolve, reject: reject };
1557
+ _this.endOfStreamWaiters.push(waiter);
1558
+ var timeout = waitTime > 0
1559
+ ? setTimeout(function () {
1560
+ var idx = _this.endOfStreamWaiters.indexOf(waiter);
1561
+ if (idx >= 0)
1562
+ _this.endOfStreamWaiters.splice(idx, 1);
1463
1563
  reject(new Error("End of stream timeout"));
1464
- }, waitTime);
1465
- }
1466
- // Check interval for real-time endFlag monitoring
1467
- var checkInterval = 100; // Check every 100 milliseconds
1468
- // Set an interval to check the endFlag regularly
1469
- var interval = setInterval(function () {
1470
- if (_this.endFlag) {
1471
- if (timeout !== null) {
1472
- clearTimeout(timeout);
1473
- }
1474
- clearInterval(interval);
1475
- resolve();
1476
- }
1477
- }, checkInterval);
1478
- // If the onEnd is triggered externally, it should now be marked manually
1479
- var originalOnEnd = _this.onEnd;
1480
- _this.onEnd = function () {
1481
- if (!_this.endFlag) {
1482
- // The external trigger may set endFlag; if not, handle here
1483
- _this.endFlag = true;
1484
- }
1485
- if (timeout !== null) {
1486
- clearTimeout(timeout);
1487
- }
1488
- clearInterval(interval);
1489
- resolve();
1490
- if (originalOnEnd) {
1491
- originalOnEnd(); // Call the original onEnd function if set
1492
- }
1493
- };
1564
+ }, waitTime)
1565
+ : null;
1566
+ waiter.resolve = function () { if (timeout)
1567
+ clearTimeout(timeout); resolve(); };
1568
+ waiter.reject = function (e) { if (timeout)
1569
+ clearTimeout(timeout); reject(e); };
1494
1570
  });
1495
1571
  };
1572
+ /** 内部调用:流结束时唤醒所有 waitForEndOfStream 等待者 */
1573
+ HTTP2Parser.prototype._notifyEndOfStream = function () {
1574
+ this.endFlag = true;
1575
+ var ws = this.endOfStreamWaiters.splice(0);
1576
+ for (var _i = 0, ws_4 = ws; _i < ws_4.length; _i++) {
1577
+ var w = ws_4[_i];
1578
+ try {
1579
+ w.resolve();
1580
+ }
1581
+ catch ( /* ignore */_a) { /* ignore */ }
1582
+ }
1583
+ };
1496
1584
  // 解析 WINDOW_UPDATE 帧
1497
1585
  HTTP2Parser.prototype.parseWindowUpdateFrame = function (frameBuffer, frameHeader) {
1498
1586
  // WINDOW_UPDATE帧的payload固定为4字节
@@ -1886,6 +1974,8 @@
1886
1974
  this.lastBytesDrainedSeen = 0;
1887
1975
  this.lastBpWarnAt = 0;
1888
1976
  this.isHandlingError = false; // 防止重复错误处理
1977
+ /** drain 事件驱动等待者,替代 flush() 中的 setInterval 轮询 */
1978
+ this.drainWaiters = [];
1889
1979
  // 事件系统
1890
1980
  this.listeners = new Map();
1891
1981
  // 验证 stream 参数
@@ -2016,10 +2106,12 @@
2016
2106
  _l.trys.push([4, 7, , 8]);
2017
2107
  canContinue = this.stream.send(chunk);
2018
2108
  if (!!canContinue) return [3 /*break*/, 6];
2019
- // 等待 drain 事件
2020
- return [4 /*yield*/, this.stream.onDrain()];
2109
+ // 传入 abort signal,当流被 abort 时 onDrain() 会立即 reject,
2110
+ // 避免在 abort 路径下永久挂住
2111
+ return [4 /*yield*/, this.stream.onDrain({ signal: this.abortController.signal })];
2021
2112
  case 5:
2022
- // 等待 drain 事件
2113
+ // 传入 abort signal,当流被 abort 时 onDrain() 会立即 reject,
2114
+ // 避免在 abort 路径下永久挂住
2023
2115
  _l.sent();
2024
2116
  _l.label = 6;
2025
2117
  case 6: return [3 /*break*/, 8];
@@ -2054,7 +2146,11 @@
2054
2146
  if (e_1) throw e_1.error;
2055
2147
  return [7 /*endfinally*/];
2056
2148
  case 15: return [7 /*endfinally*/];
2057
- case 16: return [2 /*return*/];
2149
+ case 16:
2150
+ // pipeline 正常结束(stream 关闭或 pushable 耗尽)—— 确保资源清理
2151
+ // 若已通过 abort() 触发则 cleanup() 内部幂等处理
2152
+ this.cleanup();
2153
+ return [2 /*return*/];
2058
2154
  }
2059
2155
  });
2060
2156
  });
@@ -2064,7 +2160,7 @@
2064
2160
  var self = this;
2065
2161
  return function (source) {
2066
2162
  return __asyncGenerator(this, arguments, function () {
2067
- var _a, source_1, source_1_1, chunk, now, e_2_1;
2163
+ var _a, source_1, source_1_1, chunk, now, ws, _i, ws_1, fn, e_2_1;
2068
2164
  var _b, e_2, _c, _d;
2069
2165
  return __generator(this, function (_e) {
2070
2166
  switch (_e.label) {
@@ -2101,6 +2197,17 @@
2101
2197
  self.lastDrainEventAt = now;
2102
2198
  self.dispatchEvent(new CustomEvent('drain', { detail: { drained: self.bytesDrained, queueSize: self.queueSize } }));
2103
2199
  }
2200
+ // 唤醒所有在等 flush() 或背压解除 的 drainWaiters(队列降低时就可唤醒)
2201
+ if (self.drainWaiters.length > 0) {
2202
+ ws = self.drainWaiters.splice(0);
2203
+ for (_i = 0, ws_1 = ws; _i < ws_1.length; _i++) {
2204
+ fn = ws_1[_i];
2205
+ try {
2206
+ fn();
2207
+ }
2208
+ catch ( /* ignore */_f) { /* ignore */ }
2209
+ }
2210
+ }
2104
2211
  // 记录本次已消耗字节,用于看门狗判断是否前进
2105
2212
  self.lastBytesDrainedSeen = self.bytesDrained;
2106
2213
  }
@@ -2237,17 +2344,17 @@
2237
2344
  };
2238
2345
  StreamWriter.prototype.writeChunks = function (buffer) {
2239
2346
  return __awaiter(this, void 0, void 0, function () {
2240
- var offset, end, chunk;
2347
+ var src, offset, end, chunk;
2241
2348
  return __generator(this, function (_a) {
2242
2349
  switch (_a.label) {
2243
2350
  case 0:
2351
+ src = new Uint8Array(buffer);
2244
2352
  offset = 0;
2245
2353
  _a.label = 1;
2246
2354
  case 1:
2247
- if (!(offset < buffer.byteLength)) return [3 /*break*/, 4];
2248
- end = Math.min(offset + this.options.chunkSize, buffer.byteLength);
2249
- chunk = new Uint8Array(end - offset);
2250
- chunk.set(new Uint8Array(buffer.slice(offset, end)));
2355
+ if (!(offset < src.byteLength)) return [3 /*break*/, 4];
2356
+ end = Math.min(offset + this.options.chunkSize, src.byteLength);
2357
+ chunk = src.subarray(offset, end);
2251
2358
  return [4 /*yield*/, this.retryableWrite(chunk)];
2252
2359
  case 2:
2253
2360
  _a.sent();
@@ -2264,7 +2371,6 @@
2264
2371
  StreamWriter.prototype.retryableWrite = function (chunk_1) {
2265
2372
  return __awaiter(this, arguments, void 0, function (chunk, attempt) {
2266
2373
  var currentSize, threshold, err_3, delay_1;
2267
- var _this = this;
2268
2374
  if (attempt === void 0) { attempt = 0; }
2269
2375
  return __generator(this, function (_a) {
2270
2376
  switch (_a.label) {
@@ -2275,7 +2381,7 @@
2275
2381
  }
2276
2382
  _a.label = 1;
2277
2383
  case 1:
2278
- _a.trys.push([1, 5, , 8]);
2384
+ _a.trys.push([1, 4, , 7]);
2279
2385
  currentSize = this.queueSize;
2280
2386
  threshold = this.options.bufferSize * 0.7;
2281
2387
  if (!(currentSize > threshold)) return [3 /*break*/, 3];
@@ -2288,94 +2394,92 @@
2288
2394
  if (this.abortController.signal.aborted) {
2289
2395
  throw new Error('Stream aborted during backpressure monitoring');
2290
2396
  }
2291
- return [4 /*yield*/, new Promise(function (resolve, reject) {
2292
- try {
2293
- _this.p.push(chunk);
2294
- }
2295
- catch (err) {
2296
- reject(err);
2297
- }
2298
- resolve();
2299
- })];
2397
+ // push 是同步操作,直接调用即可
2398
+ this.p.push(chunk);
2399
+ return [3 /*break*/, 7];
2300
2400
  case 4:
2301
- _a.sent();
2302
- return [3 /*break*/, 8];
2303
- case 5:
2304
2401
  err_3 = _a.sent();
2305
- if (!(attempt < this.options.retries)) return [3 /*break*/, 7];
2402
+ if (!(!this.abortController.signal.aborted && attempt < this.options.retries)) return [3 /*break*/, 6];
2306
2403
  delay_1 = this.calculateRetryDelay(attempt);
2307
2404
  return [4 /*yield*/, new Promise(function (r) { return setTimeout(r, delay_1); })];
2308
- case 6:
2405
+ case 5:
2309
2406
  _a.sent();
2310
2407
  return [2 /*return*/, this.retryableWrite(chunk, attempt + 1)];
2311
- case 7: throw err_3;
2312
- case 8: return [2 /*return*/];
2408
+ case 6: throw err_3;
2409
+ case 7: return [2 /*return*/];
2313
2410
  }
2314
2411
  });
2315
2412
  });
2316
2413
  };
2317
2414
  StreamWriter.prototype.monitorBackpressure = function () {
2318
2415
  return __awaiter(this, void 0, void 0, function () {
2319
- var currentSize, baseThreshold, criticalThreshold, pressure, waitTime, retryCount, maxRetries, now;
2416
+ var baseThreshold, criticalThreshold, maxRounds, _loop_1, this_1, i, state_1, now;
2417
+ var _this = this;
2320
2418
  return __generator(this, function (_a) {
2321
2419
  switch (_a.label) {
2322
2420
  case 0:
2323
- currentSize = this.queueSize;
2324
- baseThreshold = this.options.bufferSize * 0.7 // 降低基础阈值,更早检测
2325
- ;
2326
- criticalThreshold = this.options.bufferSize * 0.9 // 临界阈值
2327
- ;
2328
- // 快速路径:无背压时直接返回
2329
- if (currentSize < baseThreshold) {
2421
+ baseThreshold = this.options.bufferSize * 0.7;
2422
+ criticalThreshold = this.options.bufferSize * 0.9;
2423
+ // 快速路径
2424
+ if (this.queueSize < baseThreshold) {
2330
2425
  if (this.isBackpressure) {
2331
2426
  this.isBackpressure = false;
2332
- this.dispatchBackpressureEvent({
2333
- currentSize: currentSize,
2334
- averageSize: this.getAverageQueueSize(),
2335
- threshold: baseThreshold,
2336
- waitingTime: 0
2337
- });
2427
+ this.dispatchBackpressureEvent({ currentSize: this.queueSize, averageSize: this.getAverageQueueSize(), threshold: baseThreshold, waitingTime: 0 });
2338
2428
  }
2339
2429
  return [2 /*return*/];
2340
2430
  }
2341
- // 进入背压状态
2342
2431
  if (!this.isBackpressure) {
2343
2432
  this.isBackpressure = true;
2344
- this.dispatchBackpressureEvent({
2345
- currentSize: currentSize,
2346
- averageSize: this.getAverageQueueSize(),
2347
- threshold: baseThreshold,
2348
- waitingTime: 0
2349
- });
2350
- }
2351
- pressure = currentSize / this.options.bufferSize;
2352
- if (currentSize >= criticalThreshold) {
2353
- // 临界状态:长时间等待
2354
- waitTime = 50 + Math.min(200, pressure * 100);
2355
- }
2356
- else {
2357
- // 轻度背压:短时间等待
2358
- waitTime = Math.min(20, pressure * 30);
2433
+ this.dispatchBackpressureEvent({ currentSize: this.queueSize, averageSize: this.getAverageQueueSize(), threshold: baseThreshold, waitingTime: 0 });
2359
2434
  }
2360
- retryCount = 0;
2361
- maxRetries = 3;
2435
+ maxRounds = 3;
2436
+ _loop_1 = function (i) {
2437
+ var isCritical, waitMs;
2438
+ return __generator(this, function (_b) {
2439
+ switch (_b.label) {
2440
+ case 0:
2441
+ if (this_1.abortController.signal.aborted)
2442
+ return [2 /*return*/, "break"];
2443
+ if (this_1.queueSize < baseThreshold)
2444
+ return [2 /*return*/, "break"];
2445
+ isCritical = this_1.queueSize >= criticalThreshold;
2446
+ waitMs = isCritical ? 100 : 30;
2447
+ return [4 /*yield*/, new Promise(function (resolve) {
2448
+ var done = false;
2449
+ var timer = setTimeout(function () { if (!done) {
2450
+ done = true;
2451
+ resolve();
2452
+ } }, waitMs);
2453
+ _this.drainWaiters.push(function () { if (!done) {
2454
+ done = true;
2455
+ clearTimeout(timer);
2456
+ resolve();
2457
+ } });
2458
+ })];
2459
+ case 1:
2460
+ _b.sent();
2461
+ return [2 /*return*/];
2462
+ }
2463
+ });
2464
+ };
2465
+ this_1 = this;
2466
+ i = 0;
2362
2467
  _a.label = 1;
2363
2468
  case 1:
2364
- if (!(this.queueSize >= baseThreshold && retryCount < maxRetries)) return [3 /*break*/, 3];
2365
- if (this.abortController.signal.aborted)
2366
- return [3 /*break*/, 3];
2367
- return [4 /*yield*/, new Promise(function (r) { return setTimeout(r, waitTime); })];
2469
+ if (!(i < maxRounds)) return [3 /*break*/, 4];
2470
+ return [5 /*yield**/, _loop_1(i)];
2368
2471
  case 2:
2369
- _a.sent();
2370
- retryCount++;
2371
- // 动态调整等待时间
2372
- waitTime = Math.min(waitTime * 1.5, 100);
2373
- return [3 /*break*/, 1];
2472
+ state_1 = _a.sent();
2473
+ if (state_1 === "break")
2474
+ return [3 /*break*/, 4];
2475
+ _a.label = 3;
2374
2476
  case 3:
2375
- // 如果仍然背压但达到最大重试次数,记录警告但继续执行
2477
+ i++;
2478
+ return [3 /*break*/, 1];
2479
+ case 4:
2376
2480
  if (this.queueSize >= baseThreshold) {
2377
2481
  now = Date.now();
2378
- if (now - this.lastBpWarnAt > 1000) { // 节流警告
2482
+ if (now - this.lastBpWarnAt > 1000) {
2379
2483
  this.lastBpWarnAt = now;
2380
2484
  console.warn("Stream writer: High backpressure detected (".concat(this.queueSize, " bytes), continuing anyway"));
2381
2485
  }
@@ -2503,15 +2607,27 @@
2503
2607
  if (!this.abortController.signal.aborted) {
2504
2608
  this.abortController.abort();
2505
2609
  }
2506
- // 立即拒绝所有待处理的写入任务,避免它们继续执行
2610
+ // 执行所有待处理的写入任务:它们会检查 signal.aborted 并立即 resolve,
2611
+ // 不执行的话调用方的 Promise 会永远挂住
2507
2612
  var pendingTasks = this.writeQueue.splice(0);
2508
- pendingTasks.forEach(function () {
2509
- // 这些任务的 Promise 会在执行时因为检查到 aborted 而被拒绝
2510
- });
2613
+ for (var _i = 0, pendingTasks_1 = pendingTasks; _i < pendingTasks_1.length; _i++) {
2614
+ var task = pendingTasks_1[_i];
2615
+ task().catch(function () { });
2616
+ }
2617
+ // 唤醒所有 drainWaiters(flush / monitorBackpressure 中的等待者),
2618
+ // 让它们检查 signal.aborted 并立即 resolve,不必等到各自的超时
2619
+ var ws = this.drainWaiters.splice(0);
2620
+ for (var _a = 0, ws_2 = ws; _a < ws_2.length; _a++) {
2621
+ var fn = ws_2[_a];
2622
+ try {
2623
+ fn();
2624
+ }
2625
+ catch ( /* ignore */_b) { /* ignore */ }
2626
+ }
2511
2627
  try {
2512
2628
  this.p.end();
2513
2629
  }
2514
- catch (_a) {
2630
+ catch (_c) {
2515
2631
  // Ignore errors when ending pushable
2516
2632
  }
2517
2633
  if (this.watchdogTimer) {
@@ -2523,30 +2639,49 @@
2523
2639
  // 默认超时 10s,避免无限等待
2524
2640
  StreamWriter.prototype.flush = function () {
2525
2641
  return __awaiter(this, arguments, void 0, function (timeoutMs) {
2526
- var start;
2642
+ var _this = this;
2527
2643
  if (timeoutMs === void 0) { timeoutMs = 10000; }
2528
2644
  return __generator(this, function (_a) {
2529
2645
  switch (_a.label) {
2530
2646
  case 0:
2531
- start = Date.now();
2532
2647
  // 快速路径
2533
2648
  if (this.queueSize <= 0 && !this.isProcessingQueue && this.writeQueue.length === 0)
2534
2649
  return [2 /*return*/];
2535
- _a.label = 1;
2536
- case 1:
2537
2650
  if (this.abortController.signal.aborted)
2538
2651
  return [2 /*return*/];
2539
- if (this.queueSize <= 0 && !this.isProcessingQueue && this.writeQueue.length === 0)
2540
- return [2 /*return*/];
2541
- if (Date.now() - start > timeoutMs) {
2542
- console.warn("Stream writer: flush timeout with ".concat(this.queueSize, " bytes still queued"));
2543
- return [2 /*return*/];
2544
- }
2545
- return [4 /*yield*/, new Promise(function (r) { return setTimeout(r, 10); })];
2546
- case 2:
2652
+ return [4 /*yield*/, new Promise(function (resolve) {
2653
+ // 已经清空
2654
+ if (_this.queueSize <= 0 && !_this.isProcessingQueue && _this.writeQueue.length === 0) {
2655
+ resolve();
2656
+ return;
2657
+ }
2658
+ var done = false;
2659
+ var timer = setTimeout(function () {
2660
+ if (!done) {
2661
+ done = true;
2662
+ console.warn("Stream writer: flush timeout with ".concat(_this.queueSize, " bytes still queued"));
2663
+ resolve();
2664
+ }
2665
+ }, timeoutMs);
2666
+ // 由 createTransform 在每个 chunk 被下游消耗后唤醒
2667
+ var check = function () {
2668
+ if (_this.abortController.signal.aborted || (_this.queueSize <= 0 && !_this.isProcessingQueue && _this.writeQueue.length === 0)) {
2669
+ if (!done) {
2670
+ done = true;
2671
+ clearTimeout(timer);
2672
+ resolve();
2673
+ }
2674
+ }
2675
+ else {
2676
+ // 下次 drain 时再检查
2677
+ _this.drainWaiters.push(check);
2678
+ }
2679
+ };
2680
+ _this.drainWaiters.push(check);
2681
+ })];
2682
+ case 1:
2547
2683
  _a.sent();
2548
- return [3 /*break*/, 1];
2549
- case 3: return [2 /*return*/];
2684
+ return [2 /*return*/];
2550
2685
  }
2551
2686
  });
2552
2687
  });
@@ -2855,45 +2990,65 @@
2855
2990
  Libp2pGrpcClient.prototype.setToken = function (token) {
2856
2991
  this.token = token;
2857
2992
  };
2993
+ /** 从 peerAddr 提取 HTTP/2 :authority 字段(host:port 格式) */
2994
+ Libp2pGrpcClient.prototype.getAuthority = function () {
2995
+ try {
2996
+ var addr = this.peerAddr.toString();
2997
+ var ip4 = addr.match(/\/ip4\/(\d[\d.]+)\/tcp\/(\d+)/);
2998
+ if (ip4)
2999
+ return "".concat(ip4[1], ":").concat(ip4[2]);
3000
+ var ip6 = addr.match(/\/ip6\/([^/]+)\/tcp\/(\d+)/);
3001
+ if (ip6)
3002
+ return "[".concat(ip6[1], "]:").concat(ip6[2]);
3003
+ var dns = addr.match(/\/dns(?:4|6)?\/([.\w-]+)\/tcp\/(\d+)/);
3004
+ if (dns)
3005
+ return "".concat(dns[1], ":").concat(dns[2]);
3006
+ }
3007
+ catch ( /* ignore */_a) { /* ignore */ }
3008
+ return 'localhost';
3009
+ };
2858
3010
  Libp2pGrpcClient.prototype.unaryCall = function (method_1, requestData_1) {
2859
3011
  return __awaiter(this, arguments, void 0, function (method, requestData, timeout) {
2860
- var stream, responseData, responseBuffer, responseDataExpectedLength, hpack, exitFlag, errMsg, isResponseComplete, connection, state, streamSlotAcquired, connectionKey_1, err_2, streamManager, streamId_1, writer_1, parser, preface, settingFrme, headerFrame, dataFrames, frameSendTimeout, _i, dataFrames_1, df, err_3;
3012
+ var stream, responseData, responseBuffer, responseDataExpectedLength, headerPartialBuffer, hpack, exitFlag, errMsg, isResponseComplete, notifyResponseComplete, connection, state, streamSlotAcquired, writerRef, connectionKey_1, err_2, streamManager, streamId_1, writer_1, parser, preface, settingFrme, headerFrame, dataFrames, frameSendTimeout, _i, dataFrames_1, df, err_3;
2861
3013
  var _this = this;
2862
3014
  if (timeout === void 0) { timeout = 30000; }
2863
- return __generator(this, function (_b) {
2864
- switch (_b.label) {
3015
+ return __generator(this, function (_c) {
3016
+ switch (_c.label) {
2865
3017
  case 0:
2866
3018
  stream = null;
2867
3019
  responseData = null;
2868
3020
  responseBuffer = [];
2869
3021
  responseDataExpectedLength = -1;
3022
+ headerPartialBuffer = [];
2870
3023
  hpack = new HPACK();
2871
3024
  exitFlag = false;
2872
3025
  errMsg = "";
2873
3026
  isResponseComplete = false;
3027
+ notifyResponseComplete = null;
2874
3028
  connection = null;
2875
3029
  state = null;
2876
3030
  streamSlotAcquired = false;
2877
- _b.label = 1;
3031
+ writerRef = null;
3032
+ _c.label = 1;
2878
3033
  case 1:
2879
- _b.trys.push([1, 23, 24, 27]);
3034
+ _c.trys.push([1, 23, 24, 29]);
2880
3035
  return [4 /*yield*/, this.acquireConnection(false)];
2881
3036
  case 2:
2882
3037
  // const stream = await this.node.dialProtocol(this.peerAddr, this.protocol)
2883
- connection = _b.sent();
3038
+ connection = _c.sent();
2884
3039
  connectionKey_1 = this.peerAddr.toString();
2885
3040
  state = this.getConnectionState(connection);
2886
- _b.label = 3;
3041
+ _c.label = 3;
2887
3042
  case 3:
2888
- _b.trys.push([3, 5, , 6]);
3043
+ _c.trys.push([3, 5, , 6]);
2889
3044
  return [4 /*yield*/, this.waitForStreamSlot(state, undefined, timeout)];
2890
3045
  case 4:
2891
- _b.sent();
3046
+ _c.sent();
2892
3047
  state.activeStreams += 1;
2893
3048
  streamSlotAcquired = true;
2894
3049
  return [3 /*break*/, 6];
2895
3050
  case 5:
2896
- err_2 = _b.sent();
3051
+ err_2 = _c.sent();
2897
3052
  console.warn("[unaryCall] waiting for stream slot failed:", err_2);
2898
3053
  throw err_2;
2899
3054
  case 6: return [4 /*yield*/, connection.newStream(this.protocol, {
@@ -2901,14 +3056,15 @@
2901
3056
  negotiateFully: false,
2902
3057
  })];
2903
3058
  case 7:
2904
- stream = _b.sent();
3059
+ stream = _c.sent();
2905
3060
  streamManager = this.getStreamManagerFor(connection);
2906
3061
  return [4 /*yield*/, streamManager.getNextAppLevelStreamId()];
2907
3062
  case 8:
2908
- streamId_1 = _b.sent();
3063
+ streamId_1 = _c.sent();
2909
3064
  writer_1 = new StreamWriter(stream, {
2910
3065
  bufferSize: 16 * 1024 * 1024,
2911
3066
  });
3067
+ writerRef = writer_1;
2912
3068
  try {
2913
3069
  writer_1.addEventListener("backpressure", function (e) {
2914
3070
  var d = e.detail || {};
@@ -2930,7 +3086,7 @@
2930
3086
  catch ( /* ignore ping write errors */_b) { /* ignore ping write errors */ }
2931
3087
  });
2932
3088
  }
2933
- catch ( /* ignore addEventListener errors */_c) { /* ignore addEventListener errors */ }
3089
+ catch ( /* ignore addEventListener errors */_d) { /* ignore addEventListener errors */ }
2934
3090
  parser = new HTTP2Parser(writer_1);
2935
3091
  parser.onGoaway = function (info) {
2936
3092
  console.warn("[unaryCall] GOAWAY received from server", info);
@@ -2940,6 +3096,7 @@
2940
3096
  }
2941
3097
  exitFlag = true;
2942
3098
  errMsg = "GOAWAY received: code=".concat(info.errorCode);
3099
+ notifyResponseComplete === null || notifyResponseComplete === void 0 ? void 0 : notifyResponseComplete(); // 唤醒等待中的 Promise
2943
3100
  try {
2944
3101
  connection === null || connection === void 0 ? void 0 : connection.close();
2945
3102
  }
@@ -2958,86 +3115,99 @@
2958
3115
  parser.registerOutboundStream(streamId_1);
2959
3116
  responseDataExpectedLength = -1; // 重置期望长度
2960
3117
  responseBuffer = []; // 重置缓冲区
3118
+ headerPartialBuffer = []; // 重置跨帧头部缓冲
2961
3119
  parser.onData = function (payload, frameHeader) {
2962
3120
  //接收数据
2963
3121
  if (responseDataExpectedLength === -1) {
2964
3122
  //grpc消息头部未读取
3123
+ // 如果有跨帧积累的部分头字节,先与本帧 payload 合并
3124
+ var effectivePayload = payload;
3125
+ if (headerPartialBuffer.length > 0) {
3126
+ headerPartialBuffer.push(payload);
3127
+ var totalLen = headerPartialBuffer.reduce(function (s, c) { return s + c.length; }, 0);
3128
+ effectivePayload = new Uint8Array(totalLen);
3129
+ var off = 0;
3130
+ for (var _i = 0, headerPartialBuffer_1 = headerPartialBuffer; _i < headerPartialBuffer_1.length; _i++) {
3131
+ var c = headerPartialBuffer_1[_i];
3132
+ effectivePayload.set(c, off);
3133
+ off += c.length;
3134
+ }
3135
+ headerPartialBuffer = [];
3136
+ }
2965
3137
  //提取gRPC消息头部
2966
- if (payload.length < 5) {
3138
+ if (effectivePayload.length < 5) {
3139
+ // 头部字节不足 5,先缓存,等待后续帧补全
3140
+ headerPartialBuffer.push(effectivePayload);
2967
3141
  return;
2968
3142
  }
2969
- var lengthBytes = payload.slice(1, 5); // 消息长度的4字节
2970
- responseDataExpectedLength = new DataView(lengthBytes.buffer, lengthBytes.byteOffset).getUint32(0, false); // big-endian
2971
- if (responseDataExpectedLength < 0) {
2972
- throw new Error("Invalid gRPC message length");
2973
- }
2974
- if (responseDataExpectedLength + 5 > payload.length) {
3143
+ var lengthBytes = effectivePayload.slice(1, 5); // 消息长度的4字节
3144
+ responseDataExpectedLength = new DataView(lengthBytes.buffer, lengthBytes.byteOffset).getUint32(0, false); // big-endian(getUint32 返回无符号整数,结果不会为负)
3145
+ if (responseDataExpectedLength + 5 > effectivePayload.length) {
2975
3146
  // 如果当前 payload 不足以包含完整的 gRPC 消息,缓存数据
2976
- var grpcData = payload.subarray(5);
3147
+ var grpcData = effectivePayload.subarray(5);
2977
3148
  responseBuffer.push(grpcData);
2978
3149
  responseDataExpectedLength -= grpcData.length; // 更新期望长度
2979
3150
  return;
2980
3151
  }
2981
3152
  else {
2982
- // 如果当前 payload 足以包含完整的 gRPC 消息,重置缓冲区
2983
- var grpcData = payload.subarray(5); // 提取完整的 gRPC 消息
3153
+ // payload 已包含完整的 gRPC 消息体,精确截取(避免尾部多余字节污染)
3154
+ var msgLen = responseDataExpectedLength;
3155
+ var grpcData = effectivePayload.slice(5, 5 + msgLen);
2984
3156
  responseBuffer.push(grpcData);
2985
3157
  responseData = grpcData;
2986
3158
  isResponseComplete = true;
2987
- responseDataExpectedLength = -1; // 重置期望长度
3159
+ responseDataExpectedLength = -1;
3160
+ notifyResponseComplete === null || notifyResponseComplete === void 0 ? void 0 : notifyResponseComplete();
2988
3161
  }
2989
3162
  }
2990
3163
  else if (responseDataExpectedLength > 0) {
2991
3164
  //grpc消息头部已读取
2992
- responseBuffer.push(payload); // 将数据添加到缓冲区
2993
- responseDataExpectedLength -= payload.length; // 更新期望长度
3165
+ responseDataExpectedLength -= payload.length;
2994
3166
  if (responseDataExpectedLength <= 0) {
2995
- // 如果缓冲区中的数据已经完全处理,重置缓冲区
3167
+ // 超收时截掉多余字节
3168
+ var exactPayload = responseDataExpectedLength < 0
3169
+ ? payload.slice(0, payload.length + responseDataExpectedLength)
3170
+ : payload;
3171
+ responseBuffer.push(exactPayload);
2996
3172
  responseData = new Uint8Array(responseBuffer.reduce(function (sum, chunk) { return sum + chunk.length; }, 0));
2997
3173
  var offset = 0;
2998
- for (var _i = 0, responseBuffer_1 = responseBuffer; _i < responseBuffer_1.length; _i++) {
2999
- var chunk = responseBuffer_1[_i];
3174
+ for (var _a = 0, responseBuffer_1 = responseBuffer; _a < responseBuffer_1.length; _a++) {
3175
+ var chunk = responseBuffer_1[_a];
3000
3176
  responseData.set(chunk, offset);
3001
3177
  offset += chunk.length;
3002
3178
  }
3003
3179
  responseDataExpectedLength = -1;
3004
- isResponseComplete = true; // 设置响应完成标志
3180
+ isResponseComplete = true;
3181
+ notifyResponseComplete === null || notifyResponseComplete === void 0 ? void 0 : notifyResponseComplete();
3005
3182
  }
3006
- }
3007
- // 检查是否是流的最后一个帧(END_STREAM 标志)
3008
- if (frameHeader && frameHeader.flags & 0x1 && !isResponseComplete) {
3009
- // END_STREAM flag
3010
- // 合并所有缓冲的数据
3011
- var totalLength = responseBuffer.reduce(function (sum, chunk) { return sum + chunk.length; }, 0);
3012
- responseData = new Uint8Array(totalLength);
3013
- var offset = 0;
3014
- for (var _a = 0, responseBuffer_2 = responseBuffer; _a < responseBuffer_2.length; _a++) {
3015
- var chunk = responseBuffer_2[_a];
3016
- responseData.set(chunk, offset);
3017
- offset += chunk.length;
3183
+ else {
3184
+ responseBuffer.push(payload); // 还不完整,继续累积
3018
3185
  }
3019
- isResponseComplete = true;
3020
3186
  }
3021
- };
3022
- parser.onEnd = function () {
3023
- //接收结束
3024
- if (!isResponseComplete) {
3025
- isResponseComplete = true; // 设置响应完成标志
3026
- if (responseBuffer.length === 0) {
3027
- responseData = new Uint8Array(); // 如果没有数据,返回空数组
3028
- }
3029
- else {
3030
- // 合并所有缓冲的数据
3031
- var totalLength = responseBuffer.reduce(function (sum, chunk) { return sum + chunk.length; }, 0);
3187
+ // END_STREAM 兜底:数据路径已处理大多数情况;此分支仅在边缘情况下触发
3188
+ if (frameHeader && frameHeader.flags & 0x1 && !isResponseComplete) {
3189
+ if (responseBuffer.length > 0) {
3190
+ var totalLength = responseBuffer.reduce(function (sum, c) { return sum + c.length; }, 0);
3032
3191
  responseData = new Uint8Array(totalLength);
3033
3192
  var offset = 0;
3034
- for (var _i = 0, responseBuffer_3 = responseBuffer; _i < responseBuffer_3.length; _i++) {
3035
- var chunk = responseBuffer_3[_i];
3193
+ for (var _b = 0, responseBuffer_2 = responseBuffer; _b < responseBuffer_2.length; _b++) {
3194
+ var chunk = responseBuffer_2[_b];
3036
3195
  responseData.set(chunk, offset);
3037
3196
  offset += chunk.length;
3038
3197
  }
3039
- isResponseComplete = true;
3040
3198
  }
3199
+ else {
3200
+ responseData = new Uint8Array(0);
3201
+ }
3202
+ isResponseComplete = true;
3203
+ notifyResponseComplete === null || notifyResponseComplete === void 0 ? void 0 : notifyResponseComplete();
3204
+ }
3205
+ };
3206
+ parser.onEnd = function () {
3207
+ // 流结束时若响应未标记完成(空响应 / 纯 trailers),强制标记并唤醒等待者
3208
+ if (!isResponseComplete) {
3209
+ isResponseComplete = true;
3210
+ notifyResponseComplete === null || notifyResponseComplete === void 0 ? void 0 : notifyResponseComplete();
3041
3211
  }
3042
3212
  };
3043
3213
  parser.onSettings = function () {
@@ -3051,103 +3221,124 @@
3051
3221
  else if (plainHeaders.get("grpc-status") !== undefined) {
3052
3222
  exitFlag = true;
3053
3223
  errMsg = plainHeaders.get("grpc-message") || "gRPC call failed";
3224
+ notifyResponseComplete === null || notifyResponseComplete === void 0 ? void 0 : notifyResponseComplete(); // 唤醒等待中的 Promise
3054
3225
  }
3055
3226
  };
3056
3227
  // 启动后台流处理,捕获任何异步错误
3057
3228
  parser.processStream(stream).catch(function (error) {
3058
- console.error('Error in processStream:', error);
3059
- exitFlag = true;
3060
- if (!errMsg) {
3061
- errMsg = error instanceof Error ? error.message : 'Stream processing failed';
3229
+ // 若响应已完整收到(isResponseComplete=true),后置的网络层错误属于正常的
3230
+ // 连接拆除过程(如服务端 RST、连接关闭),不影响已成功的调用结果,静默忽略。
3231
+ // 若响应尚未完成,才记录错误并唤醒等待者,触发超时/错误路径。
3232
+ if (!isResponseComplete) {
3233
+ console.error('Error in processStream:', error);
3234
+ exitFlag = true;
3235
+ if (!errMsg) {
3236
+ errMsg = error instanceof Error ? error.message : 'Stream processing failed';
3237
+ }
3238
+ notifyResponseComplete === null || notifyResponseComplete === void 0 ? void 0 : notifyResponseComplete(); // 流处理异常也需唤醒等待者
3062
3239
  }
3063
3240
  });
3064
3241
  preface = Http2Frame.createPreface();
3065
3242
  return [4 /*yield*/, writer_1.write(preface)];
3066
3243
  case 9:
3067
- _b.sent();
3244
+ _c.sent();
3068
3245
  settingFrme = Http2Frame.createSettingsFrame();
3069
3246
  return [4 /*yield*/, writer_1.write(settingFrme)];
3070
3247
  case 10:
3071
- _b.sent();
3248
+ _c.sent();
3072
3249
  // 等待对端 SETTINGS 或 ACK,择一即可,避免偶发握手竞态
3250
+ // 注意:未胜出的 promise 内部有超时定时器,它们最终会 reject。
3251
+ // 必须绑定 .catch(…) 消除错误,否则在 Node.js 新版本中会导致 UnhandledPromiseRejection 崩溃。
3073
3252
  return [4 /*yield*/, Promise.race([
3074
- parser.waitForPeerSettings(1000),
3075
- parser.waitForSettingsAck(),
3253
+ parser.waitForPeerSettings(1000).catch(function () { }),
3254
+ parser.waitForSettingsAck().catch(function () { }),
3076
3255
  new Promise(function (res) { return setTimeout(res, 300); }),
3077
3256
  ])];
3078
3257
  case 11:
3079
3258
  // 等待对端 SETTINGS 或 ACK,择一即可,避免偶发握手竞态
3080
- _b.sent();
3081
- headerFrame = Http2Frame.createHeadersFrame(streamId_1, method, true, this.token);
3259
+ // 注意:未胜出的 promise 内部有超时定时器,它们最终会 reject。
3260
+ // 必须绑定 .catch(…) 消除错误,否则在 Node.js 新版本中会导致 UnhandledPromiseRejection 崩溃。
3261
+ _c.sent();
3262
+ headerFrame = Http2Frame.createHeadersFrame(streamId_1, method, true, this.token, this.getAuthority());
3082
3263
  return [4 /*yield*/, writer_1.write(headerFrame)];
3083
3264
  case 12:
3084
- _b.sent();
3265
+ _c.sent();
3085
3266
  dataFrames = Http2Frame.createDataFrames(streamId_1, requestData, true);
3086
3267
  frameSendTimeout = timeout > 0 ? timeout : DEFAULT_SEND_WINDOW_TIMEOUT;
3087
3268
  _i = 0, dataFrames_1 = dataFrames;
3088
- _b.label = 13;
3269
+ _c.label = 13;
3089
3270
  case 13:
3090
3271
  if (!(_i < dataFrames_1.length)) return [3 /*break*/, 16];
3091
3272
  df = dataFrames_1[_i];
3092
3273
  return [4 /*yield*/, this.sendFrameWithFlowControl(parser, streamId_1, df, writer_1, undefined, frameSendTimeout)];
3093
3274
  case 14:
3094
- _b.sent();
3095
- _b.label = 15;
3275
+ _c.sent();
3276
+ _c.label = 15;
3096
3277
  case 15:
3097
3278
  _i++;
3098
3279
  return [3 /*break*/, 13];
3099
3280
  case 16:
3100
- // 等待responseData 不为空,或超时
3281
+ // 等待 responseData 不为空,或超时(事件驱动,不轮询)
3101
3282
  return [4 /*yield*/, new Promise(function (resolve, reject) {
3283
+ if (isResponseComplete || exitFlag) {
3284
+ resolve();
3285
+ return;
3286
+ }
3102
3287
  var t = setTimeout(function () {
3288
+ notifyResponseComplete = null;
3103
3289
  reject(new Error("gRPC response timeout"));
3104
3290
  }, timeout);
3105
- var checkResponse = function () {
3106
- if (isResponseComplete || exitFlag) {
3107
- // 使用新的完成标志
3108
- clearTimeout(t);
3109
- resolve(responseData);
3110
- }
3111
- else {
3112
- setTimeout(checkResponse, 50);
3113
- }
3291
+ notifyResponseComplete = function () {
3292
+ clearTimeout(t);
3293
+ notifyResponseComplete = null;
3294
+ resolve();
3114
3295
  };
3115
- checkResponse();
3116
3296
  })];
3117
3297
  case 17:
3118
- // 等待responseData 不为空,或超时
3119
- _b.sent();
3120
- _b.label = 18;
3298
+ // 等待 responseData 不为空,或超时(事件驱动,不轮询)
3299
+ _c.sent();
3300
+ _c.label = 18;
3121
3301
  case 18:
3122
- _b.trys.push([18, 20, , 21]);
3302
+ _c.trys.push([18, 20, , 21]);
3123
3303
  return [4 /*yield*/, writer_1.flush(timeout)];
3124
3304
  case 19:
3125
- _b.sent();
3305
+ _c.sent();
3126
3306
  return [3 /*break*/, 21];
3127
3307
  case 20:
3128
- _b.sent();
3308
+ _c.sent();
3129
3309
  return [3 /*break*/, 21];
3130
3310
  case 21: return [4 /*yield*/, writer_1.end()];
3131
3311
  case 22:
3132
- _b.sent();
3133
- return [3 /*break*/, 27];
3312
+ _c.sent();
3313
+ return [3 /*break*/, 29];
3134
3314
  case 23:
3135
- err_3 = _b.sent();
3315
+ err_3 = _c.sent();
3136
3316
  console.error("unaryCall error:", err_3);
3137
3317
  throw err_3;
3138
3318
  case 24:
3139
- if (!stream) return [3 /*break*/, 26];
3140
- return [4 /*yield*/, stream.close()];
3319
+ // 必须先 abort writer(立即强制停止 pushable + stream),再 close stream。
3320
+ // 若顺序颠倒:stream.close() 会等待服务端半关闭确认,网络异常时永久挂住,
3321
+ // 导致 writer.abort() 永远不执行 → watchdog 定时器 / pushable 泄漏。
3322
+ // writer.abort() 内部幂等,成功路径下 writer.end() 已调用 cleanup(),安全。
3323
+ writerRef === null || writerRef === void 0 ? void 0 : writerRef.abort('unaryCall cleanup');
3324
+ if (!stream) return [3 /*break*/, 28];
3325
+ _c.label = 25;
3141
3326
  case 25:
3142
- _b.sent();
3143
- _b.label = 26;
3327
+ _c.trys.push([25, 27, , 28]);
3328
+ return [4 /*yield*/, stream.close()];
3144
3329
  case 26:
3330
+ _c.sent();
3331
+ return [3 /*break*/, 28];
3332
+ case 27:
3333
+ _c.sent();
3334
+ return [3 /*break*/, 28];
3335
+ case 28:
3145
3336
  if (streamSlotAcquired && state) {
3146
3337
  state.activeStreams = Math.max(0, state.activeStreams - 1);
3147
3338
  this.notifyStreamSlotAvailable(state);
3148
3339
  }
3149
3340
  return [7 /*endfinally*/];
3150
- case 27:
3341
+ case 29:
3151
3342
  if (exitFlag) {
3152
3343
  throw new Error(errMsg);
3153
3344
  }
@@ -3176,7 +3367,7 @@
3176
3367
  */
3177
3368
  Libp2pGrpcClient.prototype.Call = function (method_1, requestData_1) {
3178
3369
  return __awaiter(this, arguments, void 0, function (method, requestData, timeout, mode, onDataCallback, dataSourceCallback, onEndCallback, onErrorCallback, context, options) {
3179
- var internalController, timeoutHandle, stream, profile, useFlowControl, cancelOperation, timeoutPromise, operationPromise, error_1;
3370
+ var internalController, timeoutHandle, stream, contextAbortHandler, profile, useFlowControl, cancelOperation, timeoutPromise, operationPromise, error_1;
3180
3371
  var _this = this;
3181
3372
  var _a;
3182
3373
  if (timeout === void 0) { timeout = 30000; }
@@ -3203,17 +3394,16 @@
3203
3394
  };
3204
3395
  // 如果提供了外部信号,监听它
3205
3396
  if (context === null || context === void 0 ? void 0 : context.signal) {
3206
- // 如果外部信号已经触发中止,立即返回
3397
+ // 如果外部信号已经触发中止,立即返回——避免启动 IIFE 后在 catch 中再次调用 onErrorCallback
3207
3398
  if (context.signal.aborted) {
3208
3399
  if (onErrorCallback) {
3209
3400
  onErrorCallback(new Error("Operation aborted by context"));
3210
3401
  }
3211
- cancelOperation();
3402
+ return [2 /*return*/, cancelOperation];
3212
3403
  }
3213
- // 监听外部的abort事件
3214
- context.signal.addEventListener("abort", function () {
3215
- cancelOperation();
3216
- });
3404
+ // 监听外部的abort事件(保存引用以便后续移除,防止内存泄漏)
3405
+ contextAbortHandler = function () { cancelOperation(); };
3406
+ context.signal.addEventListener("abort", contextAbortHandler);
3217
3407
  }
3218
3408
  timeoutPromise = new Promise(function (_, reject) {
3219
3409
  timeoutHandle = setTimeout(function () {
@@ -3222,58 +3412,83 @@
3222
3412
  }, timeout);
3223
3413
  });
3224
3414
  operationPromise = (function () { return __awaiter(_this, void 0, void 0, function () {
3225
- var messageBuffer, expectedMessageLength, hpack, connection, connectionKey, state, streamSlotAcquired, err_4, err_5, streamManager, streamId_2, writer_2, parser_1, sendWindowTimeout_1, writeFrame_1, writeDataFrames_1, preface, settingFrame, headerFrame, dfs, dfs0, batchSize_1, processingQueue_1, isProcessing_1, processNextBatch_1, addToQueue_1, _a, _b, _c, chunkOrChunks, chunksToProcess, addPromises, e_1_1, error_2, remainingQueue, queueWaitStart, maxQueueWaitMs, remainingQueue, finalFrame, err_6, err_7, conns, _i, conns_1, c;
3415
+ var errorCallbackFired, reportError, msgChunks, msgTotalLen, expectedMessageLength, flattenMsgBuffer, hpack, connection, connectionKey, state, streamSlotAcquired, writer, err_4, err_5, streamManager, streamId_2, parser_1, sendWindowTimeout_1, writeFrame_1, writeDataFrames_1, preface, settingFrame, headerFrame, dfs, dfs0, batchSize_1, processingQueue_1, batchDoneWaiters_1, isProcessing_1, _notifyBatchDone_1, processNextBatch_1, addToQueue_1, _a, _b, _c, chunkOrChunks, chunksToProcess, addPromises, e_1_1, error_2, remainingQueue, finalFrame, err_6, conns, _i, conns_1, c;
3226
3416
  var _this = this;
3227
- var _g, e_1, _h, _j;
3228
- var _k, _l, _m;
3229
- return __generator(this, function (_o) {
3230
- switch (_o.label) {
3417
+ var _h, e_1, _j, _k;
3418
+ var _l, _m, _o;
3419
+ return __generator(this, function (_p) {
3420
+ switch (_p.label) {
3231
3421
  case 0:
3232
- messageBuffer = new Uint8Array(0);
3422
+ errorCallbackFired = false;
3423
+ reportError = function (err) {
3424
+ if (errorCallbackFired)
3425
+ return;
3426
+ errorCallbackFired = true;
3427
+ internalController.abort();
3428
+ if (onErrorCallback)
3429
+ onErrorCallback(err);
3430
+ };
3431
+ msgChunks = [];
3432
+ msgTotalLen = 0;
3233
3433
  expectedMessageLength = -1;
3434
+ flattenMsgBuffer = function () {
3435
+ if (msgChunks.length === 0)
3436
+ return new Uint8Array(0);
3437
+ if (msgChunks.length === 1)
3438
+ return msgChunks[0];
3439
+ var out = new Uint8Array(msgTotalLen);
3440
+ var off = 0;
3441
+ for (var _i = 0, msgChunks_1 = msgChunks; _i < msgChunks_1.length; _i++) {
3442
+ var c = msgChunks_1[_i];
3443
+ out.set(c, off);
3444
+ off += c.length;
3445
+ }
3446
+ return out;
3447
+ };
3234
3448
  hpack = new HPACK();
3235
3449
  connection = null;
3236
3450
  connectionKey = null;
3237
3451
  state = null;
3238
3452
  streamSlotAcquired = false;
3239
- _o.label = 1;
3453
+ writer = null;
3454
+ _p.label = 1;
3240
3455
  case 1:
3241
- _o.trys.push([1, 49, 50, 64]);
3456
+ _p.trys.push([1, 49, 50, 64]);
3242
3457
  // 检查是否已经中止
3243
3458
  if (internalController.signal.aborted) {
3244
3459
  throw new Error("Operation aborted");
3245
3460
  }
3246
3461
  if (!(options === null || options === void 0 ? void 0 : options.freshConnection)) return [3 /*break*/, 5];
3247
- _o.label = 2;
3462
+ _p.label = 2;
3248
3463
  case 2:
3249
- _o.trys.push([2, 4, , 5]);
3464
+ _p.trys.push([2, 4, , 5]);
3250
3465
  this.connectionPool.delete(this.peerAddr.toString());
3251
3466
  return [4 /*yield*/, this.node.hangUp(this.peerAddr)];
3252
3467
  case 3:
3253
- _o.sent();
3468
+ _p.sent();
3254
3469
  console.warn("[Call] hangUp existing connection before dialing due to freshConnection=true");
3255
3470
  return [3 /*break*/, 5];
3256
3471
  case 4:
3257
- err_4 = _o.sent();
3472
+ err_4 = _p.sent();
3258
3473
  console.warn("[Call] hangUp failed or not supported, proceeding to dial", err_4);
3259
3474
  return [3 /*break*/, 5];
3260
3475
  case 5: return [4 /*yield*/, this.acquireConnection(Boolean(options === null || options === void 0 ? void 0 : options.freshConnection))];
3261
3476
  case 6:
3262
- connection = _o.sent();
3477
+ connection = _p.sent();
3263
3478
  connectionKey = this.peerAddr.toString();
3264
3479
  state = this.getConnectionState(connection);
3265
3480
  if (!state) return [3 /*break*/, 10];
3266
- _o.label = 7;
3481
+ _p.label = 7;
3267
3482
  case 7:
3268
- _o.trys.push([7, 9, , 10]);
3483
+ _p.trys.push([7, 9, , 10]);
3269
3484
  return [4 /*yield*/, this.waitForStreamSlot(state, internalController.signal, timeout)];
3270
3485
  case 8:
3271
- _o.sent();
3486
+ _p.sent();
3272
3487
  state.activeStreams += 1;
3273
3488
  streamSlotAcquired = true;
3274
3489
  return [3 /*break*/, 10];
3275
3490
  case 9:
3276
- err_5 = _o.sent();
3491
+ err_5 = _p.sent();
3277
3492
  console.warn("[Call] waiting for stream slot failed:", err_5);
3278
3493
  throw err_5;
3279
3494
  case 10: return [4 /*yield*/, connection.newStream(this.protocol, {
@@ -3282,23 +3497,23 @@
3282
3497
  negotiateFully: false,
3283
3498
  })];
3284
3499
  case 11:
3285
- stream = _o.sent();
3500
+ stream = _p.sent();
3286
3501
  streamManager = this.getStreamManagerFor(connection);
3287
3502
  return [4 /*yield*/, streamManager.getNextAppLevelStreamId()];
3288
3503
  case 12:
3289
- streamId_2 = _o.sent();
3290
- writer_2 = new StreamWriter(stream, {
3504
+ streamId_2 = _p.sent();
3505
+ writer = new StreamWriter(stream, {
3291
3506
  bufferSize: 16 * 1024 * 1024,
3292
3507
  });
3293
3508
  try {
3294
- writer_2.addEventListener("backpressure", function (e) {
3509
+ writer.addEventListener("backpressure", function (e) {
3295
3510
  var d = e.detail || {};
3296
3511
  console.warn("[stream ".concat(streamId_2, "] backpressure current=").concat(d.currentSize, " avg=").concat(d.averageSize, " threshold=").concat(d.threshold));
3297
3512
  });
3298
- writer_2.addEventListener("drain", function () {
3513
+ writer.addEventListener("drain", function () {
3299
3514
  // drain event - no action needed
3300
3515
  });
3301
- writer_2.addEventListener("stalled", function (e) {
3516
+ writer.addEventListener("stalled", function (e) {
3302
3517
  var _a;
3303
3518
  var d = e.detail || {};
3304
3519
  console.warn("[stream ".concat(streamId_2, "] stalled queue=").concat(d.queueSize, " drained=").concat(d.drained, " since=").concat(d.sinceMs, "ms \u2014 sending PING"));
@@ -3306,13 +3521,13 @@
3306
3521
  var payload = new Uint8Array(8);
3307
3522
  (_a = crypto.getRandomValues) === null || _a === void 0 ? void 0 : _a.call(crypto, payload);
3308
3523
  var ping = Http2Frame.createFrame(0x6, 0x0, 0, payload);
3309
- writer_2.write(ping);
3524
+ writer.write(ping);
3310
3525
  }
3311
3526
  catch ( /* ignore ping write errors */_b) { /* ignore ping write errors */ }
3312
3527
  });
3313
3528
  }
3314
- catch ( /* ignore addEventListener errors */_p) { /* ignore addEventListener errors */ }
3315
- parser_1 = new HTTP2Parser(writer_2, {
3529
+ catch ( /* ignore addEventListener errors */_q) { /* ignore addEventListener errors */ }
3530
+ parser_1 = new HTTP2Parser(writer, {
3316
3531
  compatibilityMode: !useFlowControl,
3317
3532
  });
3318
3533
  parser_1.onGoaway = function (info) {
@@ -3323,10 +3538,8 @@
3323
3538
  if (state) {
3324
3539
  _this.rejectStreamWaiters(state, new Error("Connection received GOAWAY"));
3325
3540
  }
3326
- if (onErrorCallback) {
3327
- onErrorCallback(new Error("GOAWAY received: code=".concat(info.errorCode)));
3328
- }
3329
- internalController.abort();
3541
+ // reportError 统一完成:标记已报错 + abort + 触发回调(幂等,不会重复触发)
3542
+ reportError(new Error("GOAWAY received: code=".concat(info.errorCode)));
3330
3543
  try {
3331
3544
  connection === null || connection === void 0 ? void 0 : connection.close();
3332
3545
  }
@@ -3351,7 +3564,7 @@
3351
3564
  switch (_a.label) {
3352
3565
  case 0:
3353
3566
  if (!useFlowControl) return [3 /*break*/, 2];
3354
- return [4 /*yield*/, this.sendFrameWithFlowControl(parser_1, streamId_2, frame, writer_2, internalController.signal, sendWindowTimeout_1)];
3567
+ return [4 /*yield*/, this.sendFrameWithFlowControl(parser_1, streamId_2, frame, writer, internalController.signal, sendWindowTimeout_1)];
3355
3568
  case 1:
3356
3569
  _a.sent();
3357
3570
  return [3 /*break*/, 4];
@@ -3359,7 +3572,7 @@
3359
3572
  if (internalController.signal.aborted) {
3360
3573
  throw new Error("Operation aborted");
3361
3574
  }
3362
- return [4 /*yield*/, writer_2.write(frame)];
3575
+ return [4 /*yield*/, writer.write(frame)];
3363
3576
  case 3:
3364
3577
  _a.sent();
3365
3578
  _a.label = 4;
@@ -3390,51 +3603,44 @@
3390
3603
  }); };
3391
3604
  // 在各个回调中检查是否已中止
3392
3605
  parser_1.onData = function (payload) { return __awaiter(_this, void 0, void 0, function () {
3393
- var newBuffer, lengthBytes, completeMessage;
3606
+ var flat, lengthBytes, flat, completeMessage, remaining;
3394
3607
  return __generator(this, function (_a) {
3395
- // 检查是否已中止
3396
- if (internalController.signal.aborted) {
3608
+ if (internalController.signal.aborted)
3397
3609
  return [2 /*return*/];
3398
- }
3399
3610
  try {
3400
- newBuffer = new Uint8Array(messageBuffer.length + payload.length);
3401
- newBuffer.set(messageBuffer);
3402
- newBuffer.set(payload, messageBuffer.length);
3403
- messageBuffer = newBuffer;
3611
+ // 追加到分段列表,O(1),不拷贝历史数据
3612
+ msgChunks.push(payload);
3613
+ msgTotalLen += payload.length;
3404
3614
  // 处理缓冲区中的完整消息
3405
- while (messageBuffer.length > 0) {
3406
- // 如果已经中止,停止处理
3407
- if (internalController.signal.aborted) {
3615
+ while (msgTotalLen > 0) {
3616
+ if (internalController.signal.aborted)
3408
3617
  return [2 /*return*/];
3618
+ // 读取 gRPC 消息头(5字节)
3619
+ if (expectedMessageLength === -1 && msgTotalLen >= 5) {
3620
+ flat = flattenMsgBuffer();
3621
+ msgChunks = [flat];
3622
+ lengthBytes = flat.slice(1, 5);
3623
+ expectedMessageLength = new DataView(lengthBytes.buffer, lengthBytes.byteOffset).getUint32(0, false);
3409
3624
  }
3410
- // 如果还没有读取消息长度,且缓冲区有足够数据
3411
- if (expectedMessageLength === -1 && messageBuffer.length >= 5) {
3412
- lengthBytes = messageBuffer.slice(1, 5);
3413
- expectedMessageLength = new DataView(lengthBytes.buffer, lengthBytes.byteOffset).getUint32(0, false); // big-endian
3414
- }
3415
- // 如果知道期望长度且有足够数据
3416
- if (expectedMessageLength !== -1 &&
3417
- messageBuffer.length >= expectedMessageLength + 5) {
3418
- completeMessage = messageBuffer.slice(5, expectedMessageLength + 5);
3419
- // 调用回调处理这个完整消息
3625
+ // 有完整消息
3626
+ if (expectedMessageLength !== -1 && msgTotalLen >= expectedMessageLength + 5) {
3627
+ flat = flattenMsgBuffer();
3628
+ msgChunks = [flat];
3629
+ completeMessage = flat.slice(5, expectedMessageLength + 5);
3420
3630
  onDataCallback(completeMessage);
3421
- // 移除已处理的消息,保留剩余数据
3422
- messageBuffer = messageBuffer.slice(expectedMessageLength + 5);
3631
+ remaining = flat.slice(expectedMessageLength + 5);
3632
+ msgChunks = remaining.length > 0 ? [remaining] : [];
3633
+ msgTotalLen = remaining.length;
3423
3634
  expectedMessageLength = -1;
3424
3635
  }
3425
3636
  else {
3426
- // 没有足够数据构成完整消息,等待更多数据
3427
3637
  break;
3428
3638
  }
3429
3639
  }
3430
3640
  }
3431
3641
  catch (error) {
3432
- if (onErrorCallback) {
3433
- onErrorCallback(error);
3434
- }
3435
- else {
3436
- throw error;
3437
- }
3642
+ // reportError 统一报错并中止,防止 onEndCallback 在数据处理异常后仍被调用
3643
+ reportError(error);
3438
3644
  }
3439
3645
  return [2 /*return*/];
3440
3646
  });
@@ -3444,7 +3650,7 @@
3444
3650
  if (internalController.signal.aborted)
3445
3651
  return;
3446
3652
  var ackSettingFrame = Http2Frame.createSettingsAckFrame();
3447
- writer_2.write(ackSettingFrame);
3653
+ writer.write(ackSettingFrame);
3448
3654
  };
3449
3655
  parser_1.onHeaders = function (headers) {
3450
3656
  // 检查是否已中止
@@ -3454,20 +3660,16 @@
3454
3660
  if (plainHeaders.get("grpc-status") === "0") ;
3455
3661
  else if (plainHeaders.get("grpc-status") !== undefined) {
3456
3662
  var errMsg = plainHeaders.get("grpc-message") || "gRPC call failed";
3457
- var err = new Error(errMsg);
3458
- if (onErrorCallback) {
3459
- onErrorCallback(err);
3460
- }
3461
- else {
3462
- throw err;
3463
- }
3663
+ // reportError 统一完成:标记已报错 + abort + 触发回调(幂等,不会重复触发)
3664
+ reportError(new Error(errMsg));
3464
3665
  }
3465
3666
  };
3466
3667
  // 启动后台流处理
3467
3668
  parser_1.processStream(stream).catch(function (error) {
3468
- console.error('Error in processStream:', error);
3469
- if (onErrorCallback) {
3470
- onErrorCallback(error);
3669
+ // abort() 触发的清理错误属于预期行为,不打印错误日志,不重复触发回调
3670
+ if (!internalController.signal.aborted) {
3671
+ console.error('Error in processStream:', error);
3672
+ reportError(error);
3471
3673
  }
3472
3674
  });
3473
3675
  // 检查是否已中止
@@ -3475,56 +3677,52 @@
3475
3677
  throw new Error("Operation aborted");
3476
3678
  }
3477
3679
  preface = Http2Frame.createPreface();
3478
- return [4 /*yield*/, writer_2.write(preface)];
3680
+ return [4 /*yield*/, writer.write(preface)];
3479
3681
  case 13:
3480
- _o.sent();
3682
+ _p.sent();
3481
3683
  // 检查是否已中止
3482
3684
  if (internalController.signal.aborted) {
3483
3685
  throw new Error("Operation aborted");
3484
3686
  }
3485
3687
  settingFrame = Http2Frame.createSettingsFrame();
3486
- return [4 /*yield*/, writer_2.write(settingFrame)];
3688
+ return [4 /*yield*/, writer.write(settingFrame)];
3487
3689
  case 14:
3488
- _o.sent();
3690
+ _p.sent();
3489
3691
  // 检查是否已中止
3490
3692
  if (internalController.signal.aborted) {
3491
3693
  throw new Error("Operation aborted");
3492
3694
  }
3493
3695
  return [4 /*yield*/, Promise.race([
3494
- parser_1.waitForPeerSettings(1000),
3495
- parser_1.waitForSettingsAck(),
3696
+ parser_1.waitForPeerSettings(1000).catch(function () { }),
3697
+ parser_1.waitForSettingsAck().catch(function () { }),
3496
3698
  new Promise(function (res) { return setTimeout(res, 300); }),
3497
3699
  ])];
3498
3700
  case 15:
3499
- _o.sent();
3701
+ _p.sent();
3500
3702
  // 检查是否已中止
3501
3703
  if (internalController.signal.aborted) {
3502
3704
  throw new Error("Operation aborted");
3503
3705
  }
3504
- // 检查是否已中止
3505
- if (internalController.signal.aborted) {
3506
- throw new Error("Operation aborted");
3507
- }
3508
- headerFrame = Http2Frame.createHeadersFrame(streamId_2, method, true, this.token);
3706
+ headerFrame = Http2Frame.createHeadersFrame(streamId_2, method, true, this.token, this.getAuthority());
3509
3707
  if (!(mode === "unary" || mode === "server-streaming")) return [3 /*break*/, 18];
3510
- return [4 /*yield*/, writer_2.write(headerFrame)];
3708
+ return [4 /*yield*/, writer.write(headerFrame)];
3511
3709
  case 16:
3512
- _o.sent();
3710
+ _p.sent();
3513
3711
  dfs = Http2Frame.createDataFrames(streamId_2, requestData, true);
3514
3712
  return [4 /*yield*/, writeDataFrames_1(dfs)];
3515
3713
  case 17:
3516
- _o.sent();
3714
+ _p.sent();
3517
3715
  // 检查是否已中止
3518
3716
  if (internalController.signal.aborted) {
3519
3717
  throw new Error("Operation aborted");
3520
3718
  }
3521
- return [3 /*break*/, 47];
3719
+ return [3 /*break*/, 45];
3522
3720
  case 18:
3523
3721
  if (!((mode === "client-streaming" || mode === "bidirectional") &&
3524
- dataSourceCallback)) return [3 /*break*/, 47];
3525
- return [4 /*yield*/, writer_2.write(headerFrame)];
3722
+ dataSourceCallback)) return [3 /*break*/, 45];
3723
+ return [4 /*yield*/, writer.write(headerFrame)];
3526
3724
  case 19:
3527
- _o.sent();
3725
+ _p.sent();
3528
3726
  // 检查是否已中止
3529
3727
  if (internalController.signal.aborted) {
3530
3728
  throw new Error("Operation aborted");
@@ -3533,12 +3731,23 @@
3533
3731
  dfs0 = Http2Frame.createDataFrames(streamId_2, requestData, false);
3534
3732
  return [4 /*yield*/, writeDataFrames_1(dfs0)];
3535
3733
  case 20:
3536
- _o.sent();
3537
- _o.label = 21;
3734
+ _p.sent();
3735
+ _p.label = 21;
3538
3736
  case 21:
3539
3737
  batchSize_1 = (options === null || options === void 0 ? void 0 : options.batchSize) || 10;
3540
3738
  processingQueue_1 = [];
3739
+ batchDoneWaiters_1 = [];
3541
3740
  isProcessing_1 = false;
3741
+ _notifyBatchDone_1 = function () {
3742
+ var ws = batchDoneWaiters_1.splice(0);
3743
+ for (var _i = 0, ws_1 = ws; _i < ws_1.length; _i++) {
3744
+ var fn = ws_1[_i];
3745
+ try {
3746
+ fn();
3747
+ }
3748
+ catch ( /* ignore */_a) { /* ignore */ }
3749
+ }
3750
+ };
3542
3751
  processNextBatch_1 = function () { return __awaiter(_this, void 0, void 0, function () {
3543
3752
  var currentBatch, _i, currentBatch_1, item, frames_2, item, frames1, error_3;
3544
3753
  return __generator(this, function (_a) {
@@ -3599,10 +3808,13 @@
3599
3808
  case 10:
3600
3809
  isProcessing_1 = false;
3601
3810
  // 如果队列中还有数据,继续处理
3602
- if (processingQueue_1.length > 0 &&
3603
- !internalController.signal.aborted) {
3604
- // 使用 setTimeout 避免阻塞,让新数据有机会加入队列
3605
- setTimeout(function () { return processNextBatch_1(); }, 0);
3811
+ if (processingQueue_1.length > 0 && !internalController.signal.aborted) {
3812
+ // 直接递归调用(已是 async,自动让出事件循环)
3813
+ processNextBatch_1().catch(function (err) { console.error("Error in processNextBatch:", err); });
3814
+ }
3815
+ else {
3816
+ // 队列清空,唤醒等待者
3817
+ _notifyBatchDone_1();
3606
3818
  }
3607
3819
  return [7 /*endfinally*/];
3608
3820
  case 11: return [2 /*return*/];
@@ -3625,20 +3837,20 @@
3625
3837
  }
3626
3838
  });
3627
3839
  };
3628
- _o.label = 22;
3840
+ _p.label = 22;
3629
3841
  case 22:
3630
- _o.trys.push([22, 36, , 37]);
3631
- _o.label = 23;
3842
+ _p.trys.push([22, 36, , 37]);
3843
+ _p.label = 23;
3632
3844
  case 23:
3633
- _o.trys.push([23, 29, 30, 35]);
3845
+ _p.trys.push([23, 29, 30, 35]);
3634
3846
  _a = true, _b = __asyncValues(dataSourceCallback());
3635
- _o.label = 24;
3847
+ _p.label = 24;
3636
3848
  case 24: return [4 /*yield*/, _b.next()];
3637
3849
  case 25:
3638
- if (!(_c = _o.sent(), _g = _c.done, !_g)) return [3 /*break*/, 28];
3639
- _j = _c.value;
3850
+ if (!(_c = _p.sent(), _h = _c.done, !_h)) return [3 /*break*/, 28];
3851
+ _k = _c.value;
3640
3852
  _a = false;
3641
- chunkOrChunks = _j;
3853
+ chunkOrChunks = _k;
3642
3854
  // 检查是否已中止
3643
3855
  if (internalController.signal.aborted) {
3644
3856
  throw new Error("Operation aborted");
@@ -3653,23 +3865,23 @@
3653
3865
  return [4 /*yield*/, Promise.all(addPromises)];
3654
3866
  case 26:
3655
3867
  // 等待当前批次的chunks被添加到队列(不等待处理完成)
3656
- _o.sent();
3657
- _o.label = 27;
3868
+ _p.sent();
3869
+ _p.label = 27;
3658
3870
  case 27:
3659
3871
  _a = true;
3660
3872
  return [3 /*break*/, 24];
3661
3873
  case 28: return [3 /*break*/, 35];
3662
3874
  case 29:
3663
- e_1_1 = _o.sent();
3875
+ e_1_1 = _p.sent();
3664
3876
  e_1 = { error: e_1_1 };
3665
3877
  return [3 /*break*/, 35];
3666
3878
  case 30:
3667
- _o.trys.push([30, , 33, 34]);
3668
- if (!(!_a && !_g && (_h = _b.return))) return [3 /*break*/, 32];
3669
- return [4 /*yield*/, _h.call(_b)];
3879
+ _p.trys.push([30, , 33, 34]);
3880
+ if (!(!_a && !_h && (_j = _b.return))) return [3 /*break*/, 32];
3881
+ return [4 /*yield*/, _j.call(_b)];
3670
3882
  case 31:
3671
- _o.sent();
3672
- _o.label = 32;
3883
+ _p.sent();
3884
+ _p.label = 32;
3673
3885
  case 32: return [3 /*break*/, 34];
3674
3886
  case 33:
3675
3887
  if (e_1) throw e_1.error;
@@ -3677,7 +3889,7 @@
3677
3889
  case 34: return [7 /*endfinally*/];
3678
3890
  case 35: return [3 /*break*/, 37];
3679
3891
  case 36:
3680
- error_2 = _o.sent();
3892
+ error_2 = _p.sent();
3681
3893
  remainingQueue = processingQueue_1.splice(0);
3682
3894
  remainingQueue.forEach(function (item) {
3683
3895
  try {
@@ -3688,81 +3900,95 @@
3688
3900
  }
3689
3901
  });
3690
3902
  throw error_2;
3691
- case 37:
3692
- queueWaitStart = Date.now();
3693
- maxQueueWaitMs = timeout;
3694
- _o.label = 38;
3695
- case 38:
3696
- if (!(processingQueue_1.length > 0 || isProcessing_1)) return [3 /*break*/, 40];
3697
- if (internalController.signal.aborted) {
3698
- throw new Error("Operation aborted");
3699
- }
3700
- // 防止无限等待
3701
- if (Date.now() - queueWaitStart > maxQueueWaitMs) {
3702
- remainingQueue = processingQueue_1.splice(0);
3703
- remainingQueue.forEach(function (item) {
3704
- try {
3705
- item.reject(new Error("Queue wait timeout"));
3903
+ case 37:
3904
+ // 等待所有剩余的数据处理完成(事件驱动,无 10ms 轮询)
3905
+ return [4 /*yield*/, new Promise(function (resolve, reject) {
3906
+ var check = function () {
3907
+ if (internalController.signal.aborted) {
3908
+ reject(new Error("Operation aborted"));
3909
+ return;
3706
3910
  }
3707
- catch (err) {
3708
- console.warn("Error rejecting timeout promise:", err);
3911
+ if (processingQueue_1.length === 0 && !isProcessing_1) {
3912
+ resolve();
3913
+ return;
3709
3914
  }
3710
- });
3711
- throw new Error("Queue processing timeout");
3712
- }
3713
- return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 10); })];
3714
- case 39:
3715
- _o.sent();
3716
- return [3 /*break*/, 38];
3717
- case 40:
3915
+ // processNextBatch 结束时会通知这里
3916
+ batchDoneWaiters_1.push(check);
3917
+ };
3918
+ check();
3919
+ })];
3920
+ case 38:
3921
+ // 等待所有剩余的数据处理完成(事件驱动,无 10ms 轮询)
3922
+ _p.sent();
3718
3923
  // 检查是否已中止
3719
3924
  if (internalController.signal.aborted) {
3720
3925
  throw new Error("Operation aborted");
3721
3926
  }
3722
- finalFrame = Http2Frame.createDataFrame(streamId_2, new Uint8Array(), true);
3927
+ finalFrame = Http2Frame.createFrame(0x0, 0x01, streamId_2, new Uint8Array(0));
3723
3928
  return [4 /*yield*/, writeFrame_1(finalFrame)];
3929
+ case 39:
3930
+ _p.sent();
3931
+ _p.label = 40;
3932
+ case 40:
3933
+ _p.trys.push([40, 42, , 43]);
3934
+ return [4 /*yield*/, writer.flush(timeout)];
3724
3935
  case 41:
3725
- _o.sent();
3726
- _o.label = 42;
3936
+ _p.sent();
3937
+ return [3 /*break*/, 43];
3727
3938
  case 42:
3728
- _o.trys.push([42, 44, , 45]);
3729
- return [4 /*yield*/, writer_2.flush(timeout)];
3730
- case 43:
3731
- _o.sent();
3732
- return [3 /*break*/, 45];
3939
+ _p.sent();
3940
+ return [3 /*break*/, 43];
3941
+ case 43: return [4 /*yield*/, writer.end()];
3733
3942
  case 44:
3734
- _o.sent();
3735
- return [3 /*break*/, 45];
3736
- case 45: return [4 /*yield*/, writer_2.end()];
3737
- case 46:
3738
- _o.sent();
3739
- _o.label = 47;
3740
- case 47:
3943
+ _p.sent();
3944
+ _p.label = 45;
3945
+ case 45:
3741
3946
  // 检查是否已中止
3742
3947
  if (internalController.signal.aborted) {
3743
3948
  throw new Error("Operation aborted");
3744
3949
  }
3950
+ if (!!internalController.signal.aborted) return [3 /*break*/, 48];
3745
3951
  return [4 /*yield*/, parser_1.waitForEndOfStream(0)];
3746
- case 48:
3747
- _o.sent();
3748
- if (onEndCallback) {
3952
+ case 46:
3953
+ _p.sent();
3954
+ // Yield one microtask tick so that processStream.catch (which calls
3955
+ // reportError + internalController.abort()) has a chance to run before
3956
+ // we check abort status. Without this yield, if the stream died
3957
+ // unexpectedly (network error), onEndCallback and onErrorCallback
3958
+ // could both fire because _notifyEndOfStream() is called in
3959
+ // processStream's catch block before the re-throw schedules the
3960
+ // .catch handler as a microtask.
3961
+ return [4 /*yield*/, Promise.resolve()];
3962
+ case 47:
3963
+ // Yield one microtask tick so that processStream.catch (which calls
3964
+ // reportError + internalController.abort()) has a chance to run before
3965
+ // we check abort status. Without this yield, if the stream died
3966
+ // unexpectedly (network error), onEndCallback and onErrorCallback
3967
+ // could both fire because _notifyEndOfStream() is called in
3968
+ // processStream's catch block before the re-throw schedules the
3969
+ // .catch handler as a microtask.
3970
+ _p.sent();
3971
+ if (!internalController.signal.aborted && onEndCallback) {
3749
3972
  onEndCallback();
3750
3973
  }
3751
- return [3 /*break*/, 64];
3974
+ _p.label = 48;
3975
+ case 48: return [3 /*break*/, 64];
3752
3976
  case 49:
3753
- err_6 = _o.sent();
3977
+ err_6 = _p.sent();
3754
3978
  // 如果是由于取消导致的错误,使用特定的错误消息
3755
3979
  if (internalController.signal.aborted &&
3756
3980
  err_6 instanceof Error &&
3757
3981
  err_6.message === "Operation aborted") {
3758
- if (onErrorCallback) {
3982
+ // onHeaders / onGoaway / processStream 错误已通过 reportError 处理,
3983
+ // 此处仅在回调尚未触发时才报告(外部取消/超时场景)
3984
+ if (!errorCallbackFired && onErrorCallback) {
3759
3985
  onErrorCallback(new Error("Operation cancelled by user"));
3760
3986
  }
3761
3987
  }
3762
- else if (onErrorCallback) {
3988
+ else if (!errorCallbackFired && onErrorCallback) {
3763
3989
  onErrorCallback(err_6);
3764
3990
  }
3765
- else {
3991
+ else if (!errorCallbackFired) {
3766
3992
  if (err_6 instanceof Error) {
3767
3993
  console.error("asyncCall error:", err_6.message);
3768
3994
  }
@@ -3773,45 +3999,57 @@
3773
3999
  return [3 /*break*/, 64];
3774
4000
  case 50:
3775
4001
  clearTimeout(timeoutHandle);
4002
+ // 移除外部 abort 监听器,防止 AbortController 复用时触发迟到的 cancelOperation()
4003
+ if (contextAbortHandler && (context === null || context === void 0 ? void 0 : context.signal)) {
4004
+ context.signal.removeEventListener("abort", contextAbortHandler);
4005
+ }
4006
+ // 首先标记操作已结束(正常或异常),确保 processStream.catch 不会把
4007
+ // writer.abort() 产生的 'Call cleanup' 错误误判为真实错误并触发 onErrorCallback。
4008
+ // internalController.abort() 是幂等的,重复调用安全。
4009
+ internalController.abort();
4010
+ // 必须先 abort writer(立即强制停止 pushable + stream),再 close stream。
4011
+ // 若顺序颠倒:stream.close() 等待服务端半关闭确认,网络异常时永久挂住,
4012
+ // writer.abort() 永远不执行 → watchdog / pushable 泄漏。
4013
+ // abort() 内部幂等,重复调用安全。
4014
+ writer === null || writer === void 0 ? void 0 : writer.abort('Call cleanup');
3776
4015
  if (!stream) return [3 /*break*/, 54];
3777
- _o.label = 51;
4016
+ _p.label = 51;
3778
4017
  case 51:
3779
- _o.trys.push([51, 53, , 54]);
4018
+ _p.trys.push([51, 53, , 54]);
3780
4019
  return [4 /*yield*/, stream.close()];
3781
4020
  case 52:
3782
- _o.sent();
4021
+ _p.sent();
3783
4022
  return [3 /*break*/, 54];
3784
4023
  case 53:
3785
- err_7 = _o.sent();
3786
- console.error("Error closing stream:", err_7);
4024
+ _p.sent();
3787
4025
  return [3 /*break*/, 54];
3788
4026
  case 54:
3789
4027
  if (!(options === null || options === void 0 ? void 0 : options.freshConnection)) return [3 /*break*/, 63];
3790
- _o.label = 55;
4028
+ _p.label = 55;
3791
4029
  case 55:
3792
- _o.trys.push([55, 62, , 63]);
3793
- conns = ((_l = (_k = this.node).getConnections) === null || _l === void 0 ? void 0 : _l.call(_k, this.peerAddr)) || [];
4030
+ _p.trys.push([55, 62, , 63]);
4031
+ conns = ((_m = (_l = this.node).getConnections) === null || _m === void 0 ? void 0 : _m.call(_l, this.peerAddr)) || [];
3794
4032
  _i = 0, conns_1 = conns;
3795
- _o.label = 56;
4033
+ _p.label = 56;
3796
4034
  case 56:
3797
4035
  if (!(_i < conns_1.length)) return [3 /*break*/, 61];
3798
4036
  c = conns_1[_i];
3799
- _o.label = 57;
4037
+ _p.label = 57;
3800
4038
  case 57:
3801
- _o.trys.push([57, 59, , 60]);
3802
- return [4 /*yield*/, ((_m = c.close) === null || _m === void 0 ? void 0 : _m.call(c))];
4039
+ _p.trys.push([57, 59, , 60]);
4040
+ return [4 /*yield*/, ((_o = c.close) === null || _o === void 0 ? void 0 : _o.call(c))];
3803
4041
  case 58:
3804
- _o.sent();
4042
+ _p.sent();
3805
4043
  return [3 /*break*/, 60];
3806
4044
  case 59:
3807
- _o.sent();
4045
+ _p.sent();
3808
4046
  return [3 /*break*/, 60];
3809
4047
  case 60:
3810
4048
  _i++;
3811
4049
  return [3 /*break*/, 56];
3812
4050
  case 61: return [3 /*break*/, 63];
3813
4051
  case 62:
3814
- _o.sent();
4052
+ _p.sent();
3815
4053
  return [3 /*break*/, 63];
3816
4054
  case 63:
3817
4055
  if (streamSlotAcquired && state) {