grpc-libp2p-client 0.0.38 → 0.0.40

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.
@@ -239,7 +239,6 @@ class HPACK {
239
239
  this.dynamicTable.unshift([name, value]);
240
240
  this.dynamicTableSize += size;
241
241
  }
242
- this.dynamicTable.push([name, value]);
243
242
  }
244
243
  // 获取索引的头部
245
244
  getIndexedHeader(index) {
@@ -356,22 +355,29 @@ class HPACK {
356
355
  // Huffman编码实现
357
356
  huffmanEncode(bytes) {
358
357
  const result = [];
358
+ // 使用高精度浮点数累积位,避免 JS 32-bit 有符号整数在位数 >31 时溢出。
359
+ // Huffman 码最长 30 bits,加上未输出的最多 7 bits = 37 bits,超过 32-bit 安全范围。
360
+ // Number 可精确表示 2^53 以内的整数,足够累积多个码字。
359
361
  let current = 0;
360
362
  let bits = 0;
361
363
  for (let i = 0; i < bytes.length; i++) {
362
364
  const b = bytes[i];
363
365
  const code = this.huffmanTable.codes[b];
364
366
  const length = this.huffmanTable.lengths[b];
367
+ // 用乘法左移替代 <<,避免 32-bit 截断
368
+ current = current * (1 << length) + code;
365
369
  bits += length;
366
- current = (current << length) | code;
367
370
  while (bits >= 8) {
368
371
  bits -= 8;
369
- result.push((current >> bits) & 0xFF);
372
+ result.push(Math.floor(current / (1 << bits)) & 0xFF);
373
+ // 保留低 bits 位
374
+ current = current % (1 << bits);
370
375
  }
371
376
  }
372
- // 处理剩余的位
377
+ // 处理剩余的位(用 EOS 填充 1)
373
378
  if (bits > 0) {
374
- current = (current << (8 - bits)) | ((1 << (8 - bits)) - 1);
379
+ const pad = 8 - bits;
380
+ current = current * (1 << pad) + ((1 << pad) - 1);
375
381
  result.push(current & 0xFF);
376
382
  }
377
383
  return new Uint8Array(result);
@@ -394,8 +400,16 @@ class HPACK {
394
400
  headers.set(name, value);
395
401
  index = newIndex;
396
402
  }
397
- else if ((firstByte & 0x20) !== 0) { // 001xxxxx - Dynamic Table Size Update
398
- index++; // 简单跳过,实际应该更新动态表大小
403
+ else if ((firstByte & 0x20) !== 0) { // 001xxxxx - Dynamic Table Size Update (RFC 7541 §6.3)
404
+ const [newSize, newIndex] = this.decodeInteger(buffer, index, 5);
405
+ this.maxDynamicTableSize = newSize;
406
+ // evict entries that exceed the new limit
407
+ while (this.dynamicTableSize > this.maxDynamicTableSize && this.dynamicTable.length > 0) {
408
+ const entry = this.dynamicTable.pop();
409
+ if (entry)
410
+ this.dynamicTableSize -= entry[0].length + entry[1].length + 32;
411
+ }
412
+ index = newIndex;
399
413
  }
400
414
  else if ((firstByte & 0x10) !== 0) { // 0001xxxx - Literal Header Field Never Indexed
401
415
  const [name, value, newIndex] = this.decodeLiteralHeaderWithoutIndexing(buffer, index);
@@ -467,18 +481,18 @@ class HPACK {
467
481
  if (staticIndex <= 0) {
468
482
  return ['', '', newIndex];
469
483
  }
470
- const headerField = this.staticTable[staticIndex];
484
+ const headerField = this.getIndexedHeader(staticIndex);
471
485
  if (!headerField) {
472
486
  return ['', '', newIndex];
473
487
  }
474
488
  return [headerField[0], headerField[1], newIndex];
475
489
  }
476
490
  decodeLiteralHeaderWithIndexing(buffer, index) {
477
- const [staticIndex, nameIndex] = this.decodeInteger(buffer, index, 6);
478
- index = nameIndex;
491
+ const [nameIndex, nextIndex] = this.decodeInteger(buffer, index, 6);
492
+ index = nextIndex;
479
493
  let name;
480
- if (staticIndex > 0) {
481
- const headerField = this.staticTable[staticIndex];
494
+ if (nameIndex > 0) {
495
+ const headerField = this.getIndexedHeader(nameIndex);
482
496
  name = headerField ? headerField[0] : '';
483
497
  }
484
498
  else {
@@ -487,10 +501,26 @@ class HPACK {
487
501
  index = newIndex;
488
502
  }
489
503
  const [value, finalIndex] = this.decodeLiteralString(buffer, index);
504
+ // RFC 7541 §6.2.1: Literal Header Field with Incremental Indexing must add to dynamic table
505
+ this.addToDynamicTable(name, value);
490
506
  return [name, value, finalIndex];
491
507
  }
492
508
  decodeLiteralHeaderWithoutIndexing(buffer, index) {
493
- return this.decodeLiteralHeaderWithIndexing(buffer, index);
509
+ // RFC 7541 §6.2.2 / §6.2.3: 4-bit prefix, do NOT add to dynamic table
510
+ const [nameIndex, nextIndex] = this.decodeInteger(buffer, index, 4);
511
+ index = nextIndex;
512
+ let name;
513
+ if (nameIndex > 0) {
514
+ const headerField = this.getIndexedHeader(nameIndex);
515
+ name = headerField ? headerField[0] : '';
516
+ }
517
+ else {
518
+ const [decodedName, newIndex] = this.decodeLiteralString(buffer, index);
519
+ name = decodedName;
520
+ index = newIndex;
521
+ }
522
+ const [value, finalIndex] = this.decodeLiteralString(buffer, index);
523
+ return [name, value, finalIndex];
494
524
  }
495
525
  // 直接转换为字符串的方法
496
526
  huffmanDecodeToString(bytes) {
@@ -578,9 +608,11 @@ const SETTINGS_PARAMETERS = {
578
608
  };
579
609
  const defaultSettings = {
580
610
  [SETTINGS_PARAMETERS.HEADER_TABLE_SIZE]: 4096,
581
- [SETTINGS_PARAMETERS.ENABLE_PUSH]: 1,
611
+ // gRPC 客户端不使用 Server Push,禁用以避免无效的 PUSH_PROMISE 处理
612
+ [SETTINGS_PARAMETERS.ENABLE_PUSH]: 0,
582
613
  [SETTINGS_PARAMETERS.MAX_CONCURRENT_STREAMS]: 100,
583
- [SETTINGS_PARAMETERS.INITIAL_WINDOW_SIZE]: 16 << 10, // 16k
614
+ // 匹配 parser 的实际接收缓冲区大小(4MB),避免服务端在单流上过早被限速
615
+ [SETTINGS_PARAMETERS.INITIAL_WINDOW_SIZE]: 4 << 20, // 4MB
584
616
  [SETTINGS_PARAMETERS.MAX_FRAME_SIZE]: 16 << 10, // 16k
585
617
  [SETTINGS_PARAMETERS.MAX_HEADER_LIST_SIZE]: 8192
586
618
  };
@@ -641,8 +673,8 @@ class Http2Frame {
641
673
  // Message-Data
642
674
  grpcMessage.set(data, 5);
643
675
  // 然后将完整的 gRPC 消息分割成多个 HTTP/2 DATA 帧
644
- // HTTP/2 帧头为 9 字节
645
- const maxDataPerFrame = maxFrameSize - 9;
676
+ // maxFrameSize 是 payload 上限(RFC 7540 §6.5.2 MAX_FRAME_SIZE),不含 9 字节帧头
677
+ const maxDataPerFrame = maxFrameSize;
646
678
  for (let offset = 0; offset < grpcMessage.length; offset += maxDataPerFrame) {
647
679
  const remaining = grpcMessage.length - offset;
648
680
  const chunkSize = Math.min(maxDataPerFrame, remaining);
@@ -674,13 +706,13 @@ class Http2Frame {
674
706
  const flags = endStream ? 0x01 : 0x0; // END_STREAM flag
675
707
  return Http2Frame.createFrame(0x0, flags, streamId, framedData);
676
708
  }
677
- static createHeadersFrame(streamId, path, endHeaders = true, token) {
709
+ static createHeadersFrame(streamId, path, endHeaders = true, token, authority = 'localhost') {
678
710
  // gRPC-Web 需要的标准 headers
679
711
  const headersList = {
680
712
  ':path': path,
681
713
  ':method': 'POST',
682
714
  ':scheme': 'http',
683
- ':authority': 'localhost',
715
+ ':authority': authority,
684
716
  'content-type': 'application/grpc+proto',
685
717
  'user-agent': 'grpc-web-client/0.1',
686
718
  'accept': 'application/grpc+proto',
@@ -807,8 +839,15 @@ function _createPayload(settings) {
807
839
 
808
840
  const HTTP2_PREFACE = new TextEncoder().encode("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
809
841
  class HTTP2Parser {
842
+ /** 兼容旧代码读取 buffer —— 仅在必须全量访问时调用 _flattenBuffer() */
843
+ get buffer() { return this._flattenBuffer(); }
844
+ set buffer(v) { this.bufferChunks = v.length ? [v] : []; this.bufferTotalLength = v.length; }
810
845
  constructor(writer, options) {
811
- this.buffer = new Uint8Array(0);
846
+ /** 分段缓冲:避免每次 chunk 到达时 O(n) 全量拷贝 */
847
+ this.bufferChunks = [];
848
+ this.bufferTotalLength = 0;
849
+ this.bufferChunks = [];
850
+ this.bufferTotalLength = 0;
812
851
  this.settingsAckReceived = false;
813
852
  this.peerSettingsReceived = false;
814
853
  // 初始化连接级别的流控制窗口大小(默认值:65,535)
@@ -822,11 +861,38 @@ class HTTP2Parser {
822
861
  this.sendStreamWindows = new Map();
823
862
  this.peerInitialStreamWindow = 65535;
824
863
  this.sendWindowWaiters = [];
864
+ this.settingsAckWaiters = [];
865
+ this.peerSettingsWaiters = [];
866
+ this.endOfStreamWaiters = [];
825
867
  // 结束标志
826
868
  this.endFlag = false;
827
869
  this.writer = writer;
828
870
  this.compatibilityMode = options?.compatibilityMode ?? false;
829
871
  }
872
+ /** 将所有分段合并为一个连续 Uint8Array(仅在必要时调用)*/
873
+ _flattenBuffer() {
874
+ if (this.bufferChunks.length === 0)
875
+ return new Uint8Array(0);
876
+ if (this.bufferChunks.length === 1)
877
+ return this.bufferChunks[0];
878
+ const out = new Uint8Array(this.bufferTotalLength);
879
+ let off = 0;
880
+ for (const c of this.bufferChunks) {
881
+ out.set(c, off);
882
+ off += c.length;
883
+ }
884
+ return out;
885
+ }
886
+ /** 唤醒所有发送窗口等待者 */
887
+ _wakeWindowWaiters() {
888
+ const ws = this.sendWindowWaiters.splice(0);
889
+ for (const w of ws) {
890
+ try {
891
+ w.resolve();
892
+ }
893
+ catch { /* ignore */ }
894
+ }
895
+ }
830
896
  // 持续处理流数据
831
897
  async processStream(stream) {
832
898
  try {
@@ -836,7 +902,6 @@ class HTTP2Parser {
836
902
  }
837
903
  // Stream 结束后的清理工作
838
904
  if (!this.compatibilityMode && !this.endFlag) {
839
- this.endFlag = true;
840
905
  try {
841
906
  this.onEnd?.();
842
907
  }
@@ -844,51 +909,84 @@ class HTTP2Parser {
844
909
  console.error("Error during onEnd callback:", err);
845
910
  }
846
911
  }
912
+ // 无论何种模式,stream 结束时都通知 waitForEndOfStream 等待者,
913
+ // 防止 compatibilityMode=true(server-streaming)时 waitForEndOfStream(0) 永久挂死
914
+ if (!this.endFlag) {
915
+ this._notifyEndOfStream();
916
+ }
847
917
  }
848
918
  catch (error) {
849
- console.error("Error processing stream:", error);
919
+ // abort() 触发的清理错误(如 'Call cleanup' / 'unaryCall cleanup')属于预期行为,降级为 debug 日志
920
+ const errMsg = error instanceof Error ? error.message : String(error);
921
+ const isAbortCleanup = /cleanup/i.test(errMsg) || /aborted/i.test(errMsg);
922
+ if (isAbortCleanup) {
923
+ console.debug("[processStream] stream aborted (expected):", errMsg);
924
+ }
925
+ else {
926
+ console.error("Error processing stream:", error);
927
+ }
928
+ // 确保 waitForEndOfStream 等待者得到通知,防止 operationPromise 后台挂死
929
+ if (!this.endFlag) {
930
+ this._notifyEndOfStream();
931
+ }
850
932
  throw error;
851
933
  }
852
934
  }
853
- // 处理单个数据块
935
+ // 处理单个数据块 — 分段列表追加,避免每次 O(n) 全量拷贝
854
936
  _processChunk(chunk) {
855
937
  // chunk 是 Uint8ArrayList 或 Uint8Array
856
938
  const newData = 'subarray' in chunk && typeof chunk.subarray === 'function'
857
939
  ? chunk.subarray()
858
940
  : chunk;
859
- // 原作者之前的 O(N) 内存拷贝优化被保留,去掉了存在 onEnd 竞态的 setTimeout
860
- const newBuffer = new Uint8Array(this.buffer.length + newData.length);
861
- newBuffer.set(this.buffer);
862
- newBuffer.set(newData, this.buffer.length);
863
- this.buffer = newBuffer;
941
+ // 追加到分段列表,O(1),不拷贝历史数据
942
+ if (newData.length > 0) {
943
+ this.bufferChunks.push(newData);
944
+ this.bufferTotalLength += newData.length;
945
+ }
946
+ // 将所有分段合并为一块后处理帧(只合并一次,后续 slice 替换)
947
+ // 仅在确实有完整帧时才触发合并,碎片仅 push 不合并
948
+ if (this.bufferTotalLength < 9)
949
+ return;
950
+ // 合并一次
951
+ const flat = this._flattenBuffer();
952
+ this.bufferChunks = [flat];
953
+ // bufferTotalLength 保持不变
864
954
  // 持续处理所有完整的帧
865
955
  let readOffset = 0;
866
- while (this.buffer.length - readOffset >= 9) {
956
+ while (flat.length - readOffset >= 9) {
867
957
  // 判断是否有HTTP/2前导
868
- if (this.buffer.length - readOffset >= 24 && this.isHttp2Preface(this.buffer.subarray(readOffset))) {
958
+ if (flat.length - readOffset >= 24 && this.isHttp2Preface(flat.subarray(readOffset))) {
869
959
  readOffset += 24;
870
960
  // 发送SETTINGS帧
871
961
  const settingFrame = Http2Frame.createSettingsFrame();
872
962
  this.writer.write(settingFrame);
873
963
  continue;
874
964
  }
875
- const frameHeader = this._parseFrameHeader(this.buffer.subarray(readOffset));
965
+ const frameHeader = this._parseFrameHeader(flat.subarray(readOffset));
876
966
  const totalFrameLength = 9 + frameHeader.length;
877
967
  // 检查是否有完整的帧
878
- if (this.buffer.length - readOffset < totalFrameLength) {
968
+ if (flat.length - readOffset < totalFrameLength) {
879
969
  break;
880
970
  }
881
- // 获取完整帧数据
882
- const frameData = this.buffer.subarray(readOffset, readOffset + totalFrameLength);
971
+ // 获取完整帧数据(subarray 视图,零拷贝)
972
+ const frameData = flat.subarray(readOffset, readOffset + totalFrameLength);
883
973
  // 处理不同类型的帧
884
974
  this._handleFrame(frameHeader, frameData).catch((err) => {
885
975
  console.error("Error handling frame:", err);
886
976
  });
887
- // 移动偏移量
888
977
  readOffset += totalFrameLength;
889
978
  }
979
+ // 保留未消费的尾部字节(slice 一次,后续仍分段追加)
890
980
  if (readOffset > 0) {
891
- this.buffer = this.buffer.slice(readOffset);
981
+ if (readOffset >= flat.length) {
982
+ this.bufferChunks = [];
983
+ this.bufferTotalLength = 0;
984
+ }
985
+ else {
986
+ const remaining = flat.slice(readOffset);
987
+ this.bufferChunks = [remaining];
988
+ this.bufferTotalLength = remaining.length;
989
+ }
892
990
  }
893
991
  }
894
992
  isHttp2Preface(buffer) {
@@ -900,50 +998,67 @@ class HTTP2Parser {
900
998
  }
901
999
  return true;
902
1000
  }
903
- // 移除之前的 for await 循环代码
904
- _oldProcessStream_removed() {
905
- // 这个方法已被上面的事件驱动实现替代
906
- }
907
- // 等待SETTINGS ACK
1001
+ // 等待SETTINGS ACK 事件驱动,无轮询
908
1002
  waitForSettingsAck() {
909
1003
  return new Promise((resolve, reject) => {
910
1004
  if (this.settingsAckReceived) {
911
1005
  resolve();
912
1006
  return;
913
1007
  }
914
- const interval = setInterval(() => {
915
- if (this.settingsAckReceived) {
916
- clearInterval(interval);
917
- clearTimeout(timeout);
918
- resolve();
919
- }
920
- }, 100);
1008
+ const waiter = { resolve, reject };
1009
+ this.settingsAckWaiters.push(waiter);
921
1010
  const timeout = setTimeout(() => {
922
- clearInterval(interval);
1011
+ const idx = this.settingsAckWaiters.indexOf(waiter);
1012
+ if (idx >= 0)
1013
+ this.settingsAckWaiters.splice(idx, 1);
923
1014
  reject(new Error("Settings ACK timeout"));
924
1015
  }, 30000);
1016
+ // 覆盖 resolve 以便超时前自动清理定时器
1017
+ waiter.resolve = () => { clearTimeout(timeout); resolve(); };
1018
+ waiter.reject = (e) => { clearTimeout(timeout); reject(e); };
925
1019
  });
926
1020
  }
927
- // 等待接收来自对端的 SETTINGS(非 ACK
1021
+ /** 内部调用:SETTINGS ACK 收到时唤醒所有等待者 */
1022
+ _notifySettingsAck() {
1023
+ this.settingsAckReceived = true;
1024
+ const ws = this.settingsAckWaiters.splice(0);
1025
+ for (const w of ws) {
1026
+ try {
1027
+ w.resolve();
1028
+ }
1029
+ catch { /* ignore */ }
1030
+ }
1031
+ }
1032
+ // 等待接收来自对端的 SETTINGS(非 ACK)— 事件驱动,无轮询
928
1033
  waitForPeerSettings(timeoutMs = 30000) {
929
1034
  return new Promise((resolve, reject) => {
930
1035
  if (this.peerSettingsReceived) {
931
1036
  resolve();
932
1037
  return;
933
1038
  }
934
- const interval = setInterval(() => {
935
- if (this.peerSettingsReceived) {
936
- clearInterval(interval);
937
- clearTimeout(timeout);
938
- resolve();
939
- }
940
- }, 100);
1039
+ const waiter = { resolve, reject };
1040
+ this.peerSettingsWaiters.push(waiter);
941
1041
  const timeout = setTimeout(() => {
942
- clearInterval(interval);
1042
+ const idx = this.peerSettingsWaiters.indexOf(waiter);
1043
+ if (idx >= 0)
1044
+ this.peerSettingsWaiters.splice(idx, 1);
943
1045
  reject(new Error("Peer SETTINGS timeout"));
944
1046
  }, timeoutMs);
1047
+ waiter.resolve = () => { clearTimeout(timeout); resolve(); };
1048
+ waiter.reject = (e) => { clearTimeout(timeout); reject(e); };
945
1049
  });
946
1050
  }
1051
+ /** 内部调用:收到对端 SETTINGS(非 ACK)时唤醒等待者 */
1052
+ _notifyPeerSettings() {
1053
+ this.peerSettingsReceived = true;
1054
+ const ws = this.peerSettingsWaiters.splice(0);
1055
+ for (const w of ws) {
1056
+ try {
1057
+ w.resolve();
1058
+ }
1059
+ catch { /* ignore */ }
1060
+ }
1061
+ }
947
1062
  // 注册我们要发送数据的出站流(用于初始化该流的对端窗口)
948
1063
  registerOutboundStream(streamId) {
949
1064
  if (!this.sendStreamWindows.has(streamId)) {
@@ -970,54 +1085,51 @@ class HTTP2Parser {
970
1085
  this.sendConnWindow = Math.min(0x7fffffff, this.sendConnWindow + bytes);
971
1086
  const cur = this.sendStreamWindows.get(streamId) ?? 0;
972
1087
  this.sendStreamWindows.set(streamId, Math.min(0x7fffffff, cur + bytes));
1088
+ // 窗口增大,唤醒等待者
1089
+ this._wakeWindowWaiters();
973
1090
  }
974
- // 等待可用发送窗口(两个窗口都需要 >0)
975
- async waitForSendWindow(streamId, minBytes = 1, timeoutMs = 30000) {
976
- const start = Date.now();
1091
+ // 等待可用发送窗口 — 事件驱动,WINDOW_UPDATE/SETTINGS 收到时直接唤醒
1092
+ waitForSendWindow(streamId, minBytes = 1, timeoutMs = 30000) {
1093
+ const { conn, stream } = this.getSendWindows(streamId);
1094
+ if (conn >= minBytes && stream >= minBytes)
1095
+ return Promise.resolve();
977
1096
  return new Promise((resolve, reject) => {
978
- let interval = null;
979
1097
  let settled = false;
980
- const check = () => {
981
- const { conn, stream } = this.getSendWindows(streamId);
982
- if (conn >= minBytes && stream >= minBytes) {
983
- if (!settled) {
984
- settled = true;
985
- if (interval) {
986
- clearInterval(interval);
987
- interval = null;
988
- }
989
- resolve();
990
- }
991
- return true;
1098
+ const timeout = timeoutMs > 0
1099
+ ? setTimeout(() => {
1100
+ if (settled)
1101
+ return;
1102
+ settled = true;
1103
+ const idx = this.sendWindowWaiters.findIndex(w => w.resolve === resolveWrap);
1104
+ if (idx >= 0)
1105
+ this.sendWindowWaiters.splice(idx, 1);
1106
+ reject(new Error('Send window wait timeout'));
1107
+ }, timeoutMs)
1108
+ : undefined;
1109
+ const resolveWrap = () => {
1110
+ if (settled)
1111
+ return;
1112
+ const { conn: c2, stream: s2 } = this.getSendWindows(streamId);
1113
+ if (c2 >= minBytes && s2 >= minBytes) {
1114
+ settled = true;
1115
+ if (timeout)
1116
+ clearTimeout(timeout);
1117
+ resolve();
992
1118
  }
993
- if (Date.now() - start > timeoutMs) {
994
- if (!settled) {
995
- settled = true;
996
- if (interval) {
997
- clearInterval(interval);
998
- interval = null;
999
- }
1000
- reject(new Error('Send window wait timeout'));
1001
- }
1002
- return true;
1119
+ else {
1120
+ // 窗口仍不够,重新入队等待下一次更新
1121
+ this.sendWindowWaiters.push({ resolve: resolveWrap, reject: rejectWrap });
1003
1122
  }
1004
- return false;
1005
1123
  };
1006
- if (check())
1007
- return;
1008
- const tick = () => {
1009
- if (!check()) ;
1124
+ const rejectWrap = (e) => {
1125
+ if (settled)
1126
+ return;
1127
+ settled = true;
1128
+ if (timeout)
1129
+ clearTimeout(timeout);
1130
+ reject(e);
1010
1131
  };
1011
- const wake = () => { tick(); };
1012
- // 简单的等待模型:依赖 WINDOW_UPDATE 到达时调用 wake
1013
- this.sendWindowWaiters.push(wake);
1014
- // 同时做一个轻微的轮询,防止错过唤醒
1015
- interval = setInterval(() => {
1016
- if (check() && interval) {
1017
- clearInterval(interval);
1018
- interval = null;
1019
- }
1020
- }, 50);
1132
+ this.sendWindowWaiters.push({ resolve: resolveWrap, reject: rejectWrap });
1021
1133
  });
1022
1134
  }
1023
1135
  // 处理单个帧
@@ -1025,7 +1137,7 @@ class HTTP2Parser {
1025
1137
  switch (frameHeader.type) {
1026
1138
  case FRAME_TYPES.SETTINGS:
1027
1139
  if ((frameHeader.flags & FRAME_FLAGS.ACK) === FRAME_FLAGS.ACK) {
1028
- this.settingsAckReceived = true;
1140
+ this._notifySettingsAck();
1029
1141
  }
1030
1142
  else {
1031
1143
  //接收到Setting请求,进行解析
@@ -1035,10 +1147,12 @@ class HTTP2Parser {
1035
1147
  for (let i = 0; i < settingsPayload.length; i += 6) {
1036
1148
  // 正确解析:2字节ID + 4字节值
1037
1149
  const id = (settingsPayload[i] << 8) | settingsPayload[i + 1];
1038
- const value = (settingsPayload[i + 2] << 24) |
1150
+ // >>> 0 将结果转为无符号 32 位整数,防止高位为 1 时(如 0xffffffff)
1151
+ // 被 JS 按有符号解读为负数,导致 maxConcurrentStreams 等字段为负值
1152
+ const value = ((settingsPayload[i + 2] << 24) |
1039
1153
  (settingsPayload[i + 3] << 16) |
1040
1154
  (settingsPayload[i + 4] << 8) |
1041
- settingsPayload[i + 5];
1155
+ settingsPayload[i + 5]) >>> 0;
1042
1156
  if (id === 4) {
1043
1157
  // SETTINGS_INITIAL_WINDOW_SIZE
1044
1158
  this.defaultStreamWindowSize = value; // 我方接收窗口(入站)
@@ -1075,47 +1189,47 @@ class HTTP2Parser {
1075
1189
  if (this.onSettings) {
1076
1190
  this.onSettings(frameHeader);
1077
1191
  }
1078
- // 标记已收到对端 SETTINGS
1079
- this.peerSettingsReceived = true;
1080
- // 唤醒等待窗口(以防部分实现通过 SETTINGS 改变有效窗口)
1081
- const waiters = this.sendWindowWaiters.splice(0);
1082
- waiters.forEach(fn => { try {
1083
- fn();
1084
- }
1085
- catch (e) {
1086
- console.debug('waiter error', e);
1087
- } });
1192
+ // 标记已收到对端 SETTINGS 并唤醒等待者
1193
+ this._notifyPeerSettings();
1194
+ // 唤醒发送窗口等待者(以防部分实现通过 SETTINGS 改变有效窗口)
1195
+ this._wakeWindowWaiters();
1088
1196
  }
1089
1197
  break;
1090
- case FRAME_TYPES.DATA:
1198
+ case FRAME_TYPES.DATA: {
1091
1199
  // 处理数据帧
1092
1200
  if (this.onData) {
1093
1201
  this.onData(frameData.slice(9), frameHeader); // 跳过帧头
1094
1202
  }
1095
1203
  // 更新流窗口和连接窗口
1096
- try {
1097
- // 更新流级别的窗口
1098
- if (frameHeader.streamId !== 0) {
1099
- const streamWindowUpdate = Http2Frame.createWindowUpdateFrame(frameHeader.streamId, frameHeader.length ?? 0);
1100
- this.writer.write(streamWindowUpdate);
1204
+ // 仅在帧有实际数据时才发送 WINDOW_UPDATE:
1205
+ // RFC 7540 §6.9.1 明确禁止 increment=0 的 WINDOW_UPDATE,
1206
+ // 服务端必须以 PROTOCOL_ERROR 响应,会导致连接被强制关闭。
1207
+ // DATA 帧(如纯 END_STREAM 帧)length=0,不需要归还窗口。
1208
+ const dataLength = frameHeader.length ?? 0;
1209
+ if (dataLength > 0) {
1210
+ try {
1211
+ // 更新流级别的窗口
1212
+ if (frameHeader.streamId !== 0) {
1213
+ const streamWindowUpdate = Http2Frame.createWindowUpdateFrame(frameHeader.streamId, dataLength);
1214
+ this.writer.write(streamWindowUpdate);
1215
+ }
1216
+ // 更新连接级别的窗口
1217
+ const connWindowUpdate = Http2Frame.createWindowUpdateFrame(0, dataLength);
1218
+ this.writer.write(connWindowUpdate);
1219
+ }
1220
+ catch (err) {
1221
+ console.error("[HTTP2] Error sending window update:", err);
1101
1222
  }
1102
- // 更新连接级别的窗口
1103
- const connWindowUpdate = Http2Frame.createWindowUpdateFrame(0, frameHeader.length ?? 0);
1104
- this.writer.write(connWindowUpdate);
1105
- }
1106
- catch (err) {
1107
- console.error("[HTTP2] Error sending window update:", err);
1108
1223
  }
1109
1224
  //判断是否是最后一个帧
1110
1225
  if ((frameHeader.flags & FRAME_FLAGS.END_STREAM) ===
1111
1226
  FRAME_FLAGS.END_STREAM) {
1112
- this.endFlag = true;
1113
- if (this.onEnd) {
1114
- this.onEnd();
1115
- }
1227
+ this.onEnd?.();
1228
+ this._notifyEndOfStream();
1116
1229
  return;
1117
1230
  }
1118
1231
  break;
1232
+ }
1119
1233
  case FRAME_TYPES.HEADERS:
1120
1234
  // 处理头部帧
1121
1235
  if (this.onHeaders) {
@@ -1124,35 +1238,26 @@ class HTTP2Parser {
1124
1238
  //判断是否是最后一个帧
1125
1239
  if ((frameHeader.flags & FRAME_FLAGS.END_STREAM) ===
1126
1240
  FRAME_FLAGS.END_STREAM) {
1127
- this.endFlag = true;
1128
- if (this.onEnd) {
1129
- this.onEnd();
1130
- }
1241
+ this.onEnd?.();
1242
+ this._notifyEndOfStream();
1131
1243
  return;
1132
1244
  }
1133
1245
  break;
1134
1246
  case FRAME_TYPES.WINDOW_UPDATE:
1135
- // 处理窗口更新帧
1136
- this.handleWindowUpdateFrame(frameHeader, frameData);
1137
- // 更新发送窗口(对端接收窗口)
1247
+ // 处理窗口更新帧(同时更新接收侧诊断计数器和发送侧流控窗口,只解析一次)
1138
1248
  try {
1139
- const inc = this.parseWindowUpdateFrame(frameData, frameHeader).windowSizeIncrement;
1249
+ const result = this.handleWindowUpdateFrame(frameHeader, frameData);
1250
+ // 更新发送方向窗口(对端的接收窗口)
1140
1251
  if (frameHeader.streamId === 0) {
1141
- this.sendConnWindow += inc;
1252
+ this.sendConnWindow += result.windowSizeIncrement;
1142
1253
  }
1143
1254
  else {
1144
1255
  const cur = this.sendStreamWindows.get(frameHeader.streamId) ?? this.peerInitialStreamWindow;
1145
- this.sendStreamWindows.set(frameHeader.streamId, cur + inc);
1146
- }
1147
- const waiters = this.sendWindowWaiters.splice(0);
1148
- waiters.forEach(fn => { try {
1149
- fn();
1256
+ this.sendStreamWindows.set(frameHeader.streamId, cur + result.windowSizeIncrement);
1150
1257
  }
1151
- catch (e) {
1152
- console.debug('waiter error', e);
1153
- } });
1258
+ this._wakeWindowWaiters();
1154
1259
  }
1155
- catch { /* ignore WINDOW_UPDATE parse errors */ }
1260
+ catch { /* ignore WINDOW_UPDATE parse errors (e.g. increment=0 is RFC PROTOCOL_ERROR) */ }
1156
1261
  break;
1157
1262
  case FRAME_TYPES.PING:
1158
1263
  // 处理PING帧
@@ -1181,13 +1286,13 @@ class HTTP2Parser {
1181
1286
  catch (err) {
1182
1287
  console.error('Error during GOAWAY callback:', err);
1183
1288
  }
1184
- this.endFlag = true;
1185
1289
  try {
1186
1290
  this.onEnd?.();
1187
1291
  }
1188
1292
  catch (err) {
1189
1293
  console.error('Error during GOAWAY onEnd callback:', err);
1190
1294
  }
1295
+ this._notifyEndOfStream();
1191
1296
  break;
1192
1297
  }
1193
1298
  // case FRAME_TYPES.PUSH_PROMISE:
@@ -1195,10 +1300,8 @@ class HTTP2Parser {
1195
1300
  // this.handlePushPromiseFrame(frameHeader, frameData);
1196
1301
  // break;
1197
1302
  case FRAME_TYPES.RST_STREAM:
1198
- this.endFlag = true;
1199
- if (this.onEnd) {
1200
- this.onEnd();
1201
- }
1303
+ this.onEnd?.();
1304
+ this._notifyEndOfStream();
1202
1305
  break;
1203
1306
  default:
1204
1307
  console.debug("Unknown frame type:", frameHeader.type);
@@ -1208,7 +1311,8 @@ class HTTP2Parser {
1208
1311
  const length = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
1209
1312
  const type = buffer[3];
1210
1313
  const flags = buffer[4];
1211
- const streamId = (buffer[5] << 24) | (buffer[6] << 16) | (buffer[7] << 8) | buffer[8];
1314
+ // RFC 7540 §4.1: most significant bit is reserved and MUST be ignored on receipt
1315
+ const streamId = ((buffer[5] << 24) | (buffer[6] << 16) | (buffer[7] << 8) | buffer[8]) & 0x7fffffff;
1212
1316
  return {
1213
1317
  length,
1214
1318
  type,
@@ -1237,52 +1341,40 @@ class HTTP2Parser {
1237
1341
  throw error;
1238
1342
  }
1239
1343
  }
1240
- //等待流结束
1344
+ // 等待流结束 — 事件驱动,onEnd 触发时直接唤醒,无 setInterval 轮询
1241
1345
  waitForEndOfStream(waitTime) {
1242
1346
  return new Promise((resolve, reject) => {
1243
- // If the stream has already ended, resolve immediately
1244
1347
  if (this.endFlag) {
1245
1348
  resolve();
1246
1349
  return;
1247
1350
  }
1248
- // 如果是0 ,则不设置超时
1249
- let timeout = null;
1250
- if (waitTime > 0) {
1251
- timeout = setTimeout(() => {
1252
- clearInterval(interval);
1351
+ const waiter = { resolve, reject };
1352
+ this.endOfStreamWaiters.push(waiter);
1353
+ const timeout = waitTime > 0
1354
+ ? setTimeout(() => {
1355
+ const idx = this.endOfStreamWaiters.indexOf(waiter);
1356
+ if (idx >= 0)
1357
+ this.endOfStreamWaiters.splice(idx, 1);
1253
1358
  reject(new Error("End of stream timeout"));
1254
- }, waitTime);
1255
- }
1256
- // Check interval for real-time endFlag monitoring
1257
- const checkInterval = 100; // Check every 100 milliseconds
1258
- // Set an interval to check the endFlag regularly
1259
- const interval = setInterval(() => {
1260
- if (this.endFlag) {
1261
- if (timeout !== null) {
1262
- clearTimeout(timeout);
1263
- }
1264
- clearInterval(interval);
1265
- resolve();
1266
- }
1267
- }, checkInterval);
1268
- // If the onEnd is triggered externally, it should now be marked manually
1269
- const originalOnEnd = this.onEnd;
1270
- this.onEnd = () => {
1271
- if (!this.endFlag) {
1272
- // The external trigger may set endFlag; if not, handle here
1273
- this.endFlag = true;
1274
- }
1275
- if (timeout !== null) {
1276
- clearTimeout(timeout);
1277
- }
1278
- clearInterval(interval);
1279
- resolve();
1280
- if (originalOnEnd) {
1281
- originalOnEnd(); // Call the original onEnd function if set
1282
- }
1283
- };
1359
+ }, waitTime)
1360
+ : null;
1361
+ waiter.resolve = () => { if (timeout)
1362
+ clearTimeout(timeout); resolve(); };
1363
+ waiter.reject = (e) => { if (timeout)
1364
+ clearTimeout(timeout); reject(e); };
1284
1365
  });
1285
1366
  }
1367
+ /** 内部调用:流结束时唤醒所有 waitForEndOfStream 等待者 */
1368
+ _notifyEndOfStream() {
1369
+ this.endFlag = true;
1370
+ const ws = this.endOfStreamWaiters.splice(0);
1371
+ for (const w of ws) {
1372
+ try {
1373
+ w.resolve();
1374
+ }
1375
+ catch { /* ignore */ }
1376
+ }
1377
+ }
1286
1378
  // 解析 WINDOW_UPDATE 帧
1287
1379
  parseWindowUpdateFrame(frameBuffer, frameHeader) {
1288
1380
  // WINDOW_UPDATE帧的payload固定为4字节