grpc-libp2p-client 0.0.39 → 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.
@@ -11,7 +11,12 @@ type ParserOptions = {
11
11
  const HTTP2_PREFACE = new TextEncoder().encode("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
12
12
 
13
13
  export class HTTP2Parser {
14
- buffer: Uint8Array;
14
+ /** 分段缓冲:避免每次 chunk 到达时 O(n) 全量拷贝 */
15
+ private bufferChunks: Uint8Array[] = [];
16
+ private bufferTotalLength = 0;
17
+ /** 兼容旧代码读取 buffer —— 仅在必须全量访问时调用 _flattenBuffer() */
18
+ get buffer(): Uint8Array { return this._flattenBuffer(); }
19
+ set buffer(v: Uint8Array) { this.bufferChunks = v.length ? [v] : []; this.bufferTotalLength = v.length; }
15
20
  settingsAckReceived: boolean;
16
21
  peerSettingsReceived: boolean;
17
22
  connectionWindowSize: number;
@@ -21,7 +26,11 @@ export class HTTP2Parser {
21
26
  sendConnWindow: number;
22
27
  sendStreamWindows: Map<number, number>;
23
28
  peerInitialStreamWindow: number;
24
- private sendWindowWaiters: Array<() => void>;
29
+ private sendWindowWaiters: Array<{ resolve: () => void; reject: (e: Error) => void; cleanup?: () => void }>;
30
+ // 事件驱动等待器
31
+ private settingsAckWaiters: Array<{ resolve: () => void; reject: (e: Error) => void }>;
32
+ private peerSettingsWaiters: Array<{ resolve: () => void; reject: (e: Error) => void }>;
33
+ private endOfStreamWaiters: Array<{ resolve: () => void; reject: (e: Error) => void }>;
25
34
  onSettings?: (frameHeader: Frame) => void;
26
35
  onData?: (payload: Uint8Array, frameHeader: Frame) => void;
27
36
  onEnd?: () => void;
@@ -33,7 +42,8 @@ export class HTTP2Parser {
33
42
  private readonly compatibilityMode: boolean;
34
43
 
35
44
  constructor(writer: StreamWriter, options?: ParserOptions) {
36
- this.buffer = new Uint8Array(0);
45
+ this.bufferChunks = [];
46
+ this.bufferTotalLength = 0;
37
47
  this.settingsAckReceived = false;
38
48
  this.peerSettingsReceived = false;
39
49
  // 初始化连接级别的流控制窗口大小(默认值:65,535)
@@ -47,6 +57,9 @@ export class HTTP2Parser {
47
57
  this.sendStreamWindows = new Map();
48
58
  this.peerInitialStreamWindow = 65535;
49
59
  this.sendWindowWaiters = [];
60
+ this.settingsAckWaiters = [];
61
+ this.peerSettingsWaiters = [];
62
+ this.endOfStreamWaiters = [];
50
63
  // 结束标志
51
64
  this.endFlag = false;
52
65
 
@@ -54,6 +67,22 @@ export class HTTP2Parser {
54
67
  this.compatibilityMode = options?.compatibilityMode ?? false;
55
68
  }
56
69
 
70
+ /** 将所有分段合并为一个连续 Uint8Array(仅在必要时调用)*/
71
+ private _flattenBuffer(): Uint8Array {
72
+ if (this.bufferChunks.length === 0) return new Uint8Array(0);
73
+ if (this.bufferChunks.length === 1) return this.bufferChunks[0];
74
+ const out = new Uint8Array(this.bufferTotalLength);
75
+ let off = 0;
76
+ for (const c of this.bufferChunks) { out.set(c, off); off += c.length; }
77
+ return out;
78
+ }
79
+
80
+ /** 唤醒所有发送窗口等待者 */
81
+ private _wakeWindowWaiters() {
82
+ const ws = this.sendWindowWaiters.splice(0);
83
+ for (const w of ws) { try { w.resolve(); } catch { /* ignore */ } }
84
+ }
85
+
57
86
  // 持续处理流数据
58
87
  async processStream(stream: Stream) {
59
88
  try {
@@ -61,68 +90,95 @@ export class HTTP2Parser {
61
90
  for await (const chunk of stream) {
62
91
  this._processChunk(chunk);
63
92
  }
64
-
93
+
65
94
  // Stream 结束后的清理工作
66
95
  if (!this.compatibilityMode && !this.endFlag) {
67
- this.endFlag = true;
68
- try {
69
- this.onEnd?.();
70
- } catch (err) {
71
- console.error("Error during onEnd callback:", err);
72
- }
96
+ try { this.onEnd?.(); } catch (err) { console.error("Error during onEnd callback:", err); }
97
+ }
98
+ // 无论何种模式,stream 结束时都通知 waitForEndOfStream 等待者,
99
+ // 防止 compatibilityMode=true(server-streaming)时 waitForEndOfStream(0) 永久挂死
100
+ if (!this.endFlag) {
101
+ this._notifyEndOfStream();
73
102
  }
74
103
  } catch (error) {
75
- console.error("Error processing stream:", error);
104
+ // abort() 触发的清理错误(如 'Call cleanup' / 'unaryCall cleanup')属于预期行为,降级为 debug 日志
105
+ const errMsg = error instanceof Error ? error.message : String(error);
106
+ const isAbortCleanup = /cleanup/i.test(errMsg) || /aborted/i.test(errMsg);
107
+ if (isAbortCleanup) {
108
+ console.debug("[processStream] stream aborted (expected):", errMsg);
109
+ } else {
110
+ console.error("Error processing stream:", error);
111
+ }
112
+ // 确保 waitForEndOfStream 等待者得到通知,防止 operationPromise 后台挂死
113
+ if (!this.endFlag) {
114
+ this._notifyEndOfStream();
115
+ }
76
116
  throw error;
77
117
  }
78
118
  }
79
119
 
80
- // 处理单个数据块
120
+ // 处理单个数据块 — 分段列表追加,避免每次 O(n) 全量拷贝
81
121
  private _processChunk(chunk: Uint8Array | { subarray(): Uint8Array }): void {
82
122
  // chunk 是 Uint8ArrayList 或 Uint8Array
83
123
  const newData: Uint8Array = 'subarray' in chunk && typeof chunk.subarray === 'function'
84
124
  ? chunk.subarray()
85
125
  : (chunk as Uint8Array);
86
-
87
- // 原作者之前的 O(N) 内存拷贝优化被保留,去掉了存在 onEnd 竞态的 setTimeout
88
- const newBuffer = new Uint8Array(this.buffer.length + newData.length);
89
- newBuffer.set(this.buffer);
90
- newBuffer.set(newData, this.buffer.length);
91
- this.buffer = newBuffer;
126
+
127
+ // 追加到分段列表,O(1),不拷贝历史数据
128
+ if (newData.length > 0) {
129
+ this.bufferChunks.push(newData);
130
+ this.bufferTotalLength += newData.length;
131
+ }
132
+
133
+ // 将所有分段合并为一块后处理帧(只合并一次,后续 slice 替换)
134
+ // 仅在确实有完整帧时才触发合并,碎片仅 push 不合并
135
+ if (this.bufferTotalLength < 9) return;
136
+
137
+ // 合并一次
138
+ const flat = this._flattenBuffer();
139
+ this.bufferChunks = [flat];
140
+ // bufferTotalLength 保持不变
92
141
 
93
142
  // 持续处理所有完整的帧
94
143
  let readOffset = 0;
95
- while (this.buffer.length - readOffset >= 9) {
144
+ while (flat.length - readOffset >= 9) {
96
145
  // 判断是否有HTTP/2前导
97
- if (this.buffer.length - readOffset >= 24 && this.isHttp2Preface(this.buffer.subarray(readOffset))) {
146
+ if (flat.length - readOffset >= 24 && this.isHttp2Preface(flat.subarray(readOffset))) {
98
147
  readOffset += 24;
99
148
  // 发送SETTINGS帧
100
149
  const settingFrame = Http2Frame.createSettingsFrame();
101
150
  this.writer.write(settingFrame);
102
151
  continue;
103
152
  }
104
-
105
- const frameHeader = this._parseFrameHeader(this.buffer.subarray(readOffset));
153
+
154
+ const frameHeader = this._parseFrameHeader(flat.subarray(readOffset));
106
155
  const totalFrameLength = 9 + frameHeader.length;
107
156
 
108
157
  // 检查是否有完整的帧
109
- if (this.buffer.length - readOffset < totalFrameLength) {
158
+ if (flat.length - readOffset < totalFrameLength) {
110
159
  break;
111
160
  }
112
- // 获取完整帧数据
113
- const frameData = this.buffer.subarray(readOffset, readOffset + totalFrameLength);
161
+ // 获取完整帧数据(subarray 视图,零拷贝)
162
+ const frameData = flat.subarray(readOffset, readOffset + totalFrameLength);
114
163
 
115
164
  // 处理不同类型的帧
116
165
  this._handleFrame(frameHeader, frameData).catch((err) => {
117
166
  console.error("Error handling frame:", err);
118
167
  });
119
168
 
120
- // 移动偏移量
121
169
  readOffset += totalFrameLength;
122
170
  }
123
-
171
+
172
+ // 保留未消费的尾部字节(slice 一次,后续仍分段追加)
124
173
  if (readOffset > 0) {
125
- this.buffer = this.buffer.slice(readOffset);
174
+ if (readOffset >= flat.length) {
175
+ this.bufferChunks = [];
176
+ this.bufferTotalLength = 0;
177
+ } else {
178
+ const remaining = flat.slice(readOffset);
179
+ this.bufferChunks = [remaining];
180
+ this.bufferTotalLength = remaining.length;
181
+ }
126
182
  }
127
183
  }
128
184
 
@@ -134,55 +190,53 @@ export class HTTP2Parser {
134
190
  return true;
135
191
  }
136
192
 
137
- // 移除之前的 for await 循环代码
138
- private _oldProcessStream_removed() {
139
- // 这个方法已被上面的事件驱动实现替代
140
- }
141
-
142
- // 等待SETTINGS ACK
193
+ // 等待SETTINGS ACK 事件驱动,无轮询
143
194
  waitForSettingsAck(): Promise<void> {
144
195
  return new Promise((resolve, reject) => {
145
- if (this.settingsAckReceived) {
146
- resolve();
147
- return;
148
- }
149
- const interval = setInterval(() => {
150
- if (this.settingsAckReceived) {
151
- clearInterval(interval);
152
- clearTimeout(timeout);
153
- resolve();
154
- }
155
- }, 100);
156
-
196
+ if (this.settingsAckReceived) { resolve(); return; }
197
+ const waiter = { resolve, reject };
198
+ this.settingsAckWaiters.push(waiter);
157
199
  const timeout = setTimeout(() => {
158
- clearInterval(interval);
200
+ const idx = this.settingsAckWaiters.indexOf(waiter);
201
+ if (idx >= 0) this.settingsAckWaiters.splice(idx, 1);
159
202
  reject(new Error("Settings ACK timeout"));
160
203
  }, 30000);
204
+ // 覆盖 resolve 以便超时前自动清理定时器
205
+ waiter.resolve = () => { clearTimeout(timeout); resolve(); };
206
+ waiter.reject = (e: Error) => { clearTimeout(timeout); reject(e); };
161
207
  });
162
208
  }
163
209
 
164
- // 等待接收来自对端的 SETTINGS(非 ACK
210
+ /** 内部调用:SETTINGS ACK 收到时唤醒所有等待者 */
211
+ private _notifySettingsAck() {
212
+ this.settingsAckReceived = true;
213
+ const ws = this.settingsAckWaiters.splice(0);
214
+ for (const w of ws) { try { w.resolve(); } catch { /* ignore */ } }
215
+ }
216
+
217
+ // 等待接收来自对端的 SETTINGS(非 ACK)— 事件驱动,无轮询
165
218
  waitForPeerSettings(timeoutMs: number = 30000): Promise<void> {
166
219
  return new Promise((resolve, reject) => {
167
- if (this.peerSettingsReceived) {
168
- resolve();
169
- return;
170
- }
171
- const interval = setInterval(() => {
172
- if (this.peerSettingsReceived) {
173
- clearInterval(interval);
174
- clearTimeout(timeout);
175
- resolve();
176
- }
177
- }, 100);
178
-
220
+ if (this.peerSettingsReceived) { resolve(); return; }
221
+ const waiter = { resolve, reject };
222
+ this.peerSettingsWaiters.push(waiter);
179
223
  const timeout = setTimeout(() => {
180
- clearInterval(interval);
224
+ const idx = this.peerSettingsWaiters.indexOf(waiter);
225
+ if (idx >= 0) this.peerSettingsWaiters.splice(idx, 1);
181
226
  reject(new Error("Peer SETTINGS timeout"));
182
227
  }, timeoutMs);
228
+ waiter.resolve = () => { clearTimeout(timeout); resolve(); };
229
+ waiter.reject = (e: Error) => { clearTimeout(timeout); reject(e); };
183
230
  });
184
231
  }
185
232
 
233
+ /** 内部调用:收到对端 SETTINGS(非 ACK)时唤醒等待者 */
234
+ private _notifyPeerSettings() {
235
+ this.peerSettingsReceived = true;
236
+ const ws = this.peerSettingsWaiters.splice(0);
237
+ for (const w of ws) { try { w.resolve(); } catch { /* ignore */ } }
238
+ }
239
+
186
240
  // 注册我们要发送数据的出站流(用于初始化该流的对端窗口)
187
241
  registerOutboundStream(streamId: number) {
188
242
  if (!this.sendStreamWindows.has(streamId)) {
@@ -210,56 +264,46 @@ export class HTTP2Parser {
210
264
  this.sendConnWindow = Math.min(0x7fffffff, this.sendConnWindow + bytes);
211
265
  const cur = this.sendStreamWindows.get(streamId) ?? 0;
212
266
  this.sendStreamWindows.set(streamId, Math.min(0x7fffffff, cur + bytes));
267
+ // 窗口增大,唤醒等待者
268
+ this._wakeWindowWaiters();
213
269
  }
214
270
 
215
- // 等待可用发送窗口(两个窗口都需要 >0)
216
- async waitForSendWindow(streamId: number, minBytes: number = 1, timeoutMs: number = 30000): Promise<void> {
217
- const start = Date.now();
271
+ // 等待可用发送窗口 — 事件驱动,WINDOW_UPDATE/SETTINGS 收到时直接唤醒
272
+ waitForSendWindow(streamId: number, minBytes: number = 1, timeoutMs: number = 30000): Promise<void> {
273
+ const { conn, stream } = this.getSendWindows(streamId);
274
+ if (conn >= minBytes && stream >= minBytes) return Promise.resolve();
275
+
218
276
  return new Promise((resolve, reject) => {
219
- let interval: ReturnType<typeof setInterval> | null = null;
220
277
  let settled = false;
221
- const check = () => {
222
- const { conn, stream } = this.getSendWindows(streamId);
223
- if (conn >= minBytes && stream >= minBytes) {
224
- if (!settled) {
225
- settled = true;
226
- if (interval) {
227
- clearInterval(interval);
228
- interval = null;
229
- }
230
- resolve();
231
- }
232
- return true;
233
- }
234
- if (Date.now() - start > timeoutMs) {
235
- if (!settled) {
278
+ const timeout = timeoutMs > 0
279
+ ? setTimeout(() => {
280
+ if (settled) return;
236
281
  settled = true;
237
- if (interval) {
238
- clearInterval(interval);
239
- interval = null;
240
- }
282
+ const idx = this.sendWindowWaiters.findIndex(w => w.resolve === resolveWrap);
283
+ if (idx >= 0) this.sendWindowWaiters.splice(idx, 1);
241
284
  reject(new Error('Send window wait timeout'));
242
- }
243
- return true;
285
+ }, timeoutMs)
286
+ : undefined;
287
+
288
+ const resolveWrap = () => {
289
+ if (settled) return;
290
+ const { conn: c2, stream: s2 } = this.getSendWindows(streamId);
291
+ if (c2 >= minBytes && s2 >= minBytes) {
292
+ settled = true;
293
+ if (timeout) clearTimeout(timeout);
294
+ resolve();
295
+ } else {
296
+ // 窗口仍不够,重新入队等待下一次更新
297
+ this.sendWindowWaiters.push({ resolve: resolveWrap, reject: rejectWrap });
244
298
  }
245
- return false;
246
299
  };
247
- if (check()) return;
248
- const tick = () => {
249
- if (!check()) {
250
- // 继续等待
251
- }
300
+ const rejectWrap = (e: Error) => {
301
+ if (settled) return;
302
+ settled = true;
303
+ if (timeout) clearTimeout(timeout);
304
+ reject(e);
252
305
  };
253
- const wake = () => { tick(); };
254
- // 简单的等待模型:依赖 WINDOW_UPDATE 到达时调用 wake
255
- this.sendWindowWaiters.push(wake);
256
- // 同时做一个轻微的轮询,防止错过唤醒
257
- interval = setInterval(() => {
258
- if (check() && interval) {
259
- clearInterval(interval);
260
- interval = null;
261
- }
262
- }, 50);
306
+ this.sendWindowWaiters.push({ resolve: resolveWrap, reject: rejectWrap });
263
307
  });
264
308
  }
265
309
 
@@ -268,7 +312,7 @@ export class HTTP2Parser {
268
312
  switch (frameHeader.type) {
269
313
  case FRAME_TYPES.SETTINGS:
270
314
  if ((frameHeader.flags & FRAME_FLAGS.ACK) === FRAME_FLAGS.ACK) {
271
- this.settingsAckReceived = true;
315
+ this._notifySettingsAck();
272
316
  } else {
273
317
  //接收到Setting请求,进行解析
274
318
  const settingsPayload = frameData.slice(9);
@@ -278,11 +322,14 @@ export class HTTP2Parser {
278
322
  for (let i = 0; i < settingsPayload.length; i += 6) {
279
323
  // 正确解析:2字节ID + 4字节值
280
324
  const id = (settingsPayload[i] << 8) | settingsPayload[i + 1];
281
- const value =
325
+ // >>> 0 将结果转为无符号 32 位整数,防止高位为 1 时(如 0xffffffff)
326
+ // 被 JS 按有符号解读为负数,导致 maxConcurrentStreams 等字段为负值
327
+ const value = (
282
328
  (settingsPayload[i + 2] << 24) |
283
329
  (settingsPayload[i + 3] << 16) |
284
330
  (settingsPayload[i + 4] << 8) |
285
- settingsPayload[i + 5];
331
+ settingsPayload[i + 5]
332
+ ) >>> 0;
286
333
 
287
334
  settings[id] = value;
288
335
  if (id === 4) {
@@ -322,51 +369,56 @@ export class HTTP2Parser {
322
369
  if (this.onSettings) {
323
370
  this.onSettings(frameHeader);
324
371
  }
325
- // 标记已收到对端 SETTINGS
326
- this.peerSettingsReceived = true;
327
- // 唤醒等待窗口(以防部分实现通过 SETTINGS 改变有效窗口)
328
- const waiters = this.sendWindowWaiters.splice(0);
329
- waiters.forEach(fn => { try { fn(); } catch (e) { console.debug('waiter error', e); } });
372
+ // 标记已收到对端 SETTINGS 并唤醒等待者
373
+ this._notifyPeerSettings();
374
+ // 唤醒发送窗口等待者(以防部分实现通过 SETTINGS 改变有效窗口)
375
+ this._wakeWindowWaiters();
330
376
  }
331
377
  break;
332
378
 
333
- case FRAME_TYPES.DATA:
379
+ case FRAME_TYPES.DATA: {
334
380
  // 处理数据帧
335
381
  if (this.onData) {
336
382
  this.onData(frameData.slice(9), frameHeader); // 跳过帧头
337
383
  }
338
384
  // 更新流窗口和连接窗口
339
- try {
340
- // 更新流级别的窗口
341
- if (frameHeader.streamId !== 0) {
342
- const streamWindowUpdate = Http2Frame.createWindowUpdateFrame(
343
- frameHeader.streamId,
344
- frameHeader.length ?? 0
385
+ // 仅在帧有实际数据时才发送 WINDOW_UPDATE:
386
+ // RFC 7540 §6.9.1 明确禁止 increment=0 的 WINDOW_UPDATE,
387
+ // 服务端必须以 PROTOCOL_ERROR 响应,会导致连接被强制关闭。
388
+ // DATA 帧(如纯 END_STREAM 帧)length=0,不需要归还窗口。
389
+ const dataLength = frameHeader.length ?? 0;
390
+ if (dataLength > 0) {
391
+ try {
392
+ // 更新流级别的窗口
393
+ if (frameHeader.streamId !== 0) {
394
+ const streamWindowUpdate = Http2Frame.createWindowUpdateFrame(
395
+ frameHeader.streamId,
396
+ dataLength
397
+ );
398
+ this.writer.write(streamWindowUpdate);
399
+ }
400
+
401
+ // 更新连接级别的窗口
402
+ const connWindowUpdate = Http2Frame.createWindowUpdateFrame(
403
+ 0,
404
+ dataLength
345
405
  );
346
- this.writer.write(streamWindowUpdate);
406
+ this.writer.write(connWindowUpdate);
407
+ } catch (err) {
408
+ console.error("[HTTP2] Error sending window update:", err);
347
409
  }
348
-
349
- // 更新连接级别的窗口
350
- const connWindowUpdate = Http2Frame.createWindowUpdateFrame(
351
- 0,
352
- frameHeader.length ?? 0
353
- );
354
- this.writer.write(connWindowUpdate);
355
- } catch (err) {
356
- console.error("[HTTP2] Error sending window update:", err);
357
410
  }
358
411
  //判断是否是最后一个帧
359
412
  if (
360
413
  (frameHeader.flags & FRAME_FLAGS.END_STREAM) ===
361
414
  FRAME_FLAGS.END_STREAM
362
415
  ) {
363
- this.endFlag = true;
364
- if (this.onEnd) {
365
- this.onEnd();
366
- }
416
+ this.onEnd?.();
417
+ this._notifyEndOfStream();
367
418
  return;
368
419
  }
369
420
  break;
421
+ }
370
422
  case FRAME_TYPES.HEADERS:
371
423
  // 处理头部帧
372
424
  if (this.onHeaders) {
@@ -377,31 +429,24 @@ export class HTTP2Parser {
377
429
  (frameHeader.flags & FRAME_FLAGS.END_STREAM) ===
378
430
  FRAME_FLAGS.END_STREAM
379
431
  ) {
380
- this.endFlag = true;
381
- if (this.onEnd) {
382
- this.onEnd();
383
- }
432
+ this.onEnd?.();
433
+ this._notifyEndOfStream();
384
434
  return;
385
435
  }
386
436
  break;
387
437
  case FRAME_TYPES.WINDOW_UPDATE:
388
- // 处理窗口更新帧
389
- this.handleWindowUpdateFrame(
390
- frameHeader,
391
- frameData
392
- );
393
- // 更新发送窗口(对端接收窗口)
438
+ // 处理窗口更新帧(同时更新接收侧诊断计数器和发送侧流控窗口,只解析一次)
394
439
  try {
395
- const inc = this.parseWindowUpdateFrame(frameData, frameHeader).windowSizeIncrement;
440
+ const result = this.handleWindowUpdateFrame(frameHeader, frameData);
441
+ // 更新发送方向窗口(对端的接收窗口)
396
442
  if (frameHeader.streamId === 0) {
397
- this.sendConnWindow += inc;
443
+ this.sendConnWindow += result.windowSizeIncrement;
398
444
  } else {
399
445
  const cur = this.sendStreamWindows.get(frameHeader.streamId) ?? this.peerInitialStreamWindow;
400
- this.sendStreamWindows.set(frameHeader.streamId, cur + inc);
446
+ this.sendStreamWindows.set(frameHeader.streamId, cur + result.windowSizeIncrement);
401
447
  }
402
- const waiters = this.sendWindowWaiters.splice(0);
403
- waiters.forEach(fn => { try { fn(); } catch (e) { console.debug('waiter error', e); } });
404
- } catch { /* ignore WINDOW_UPDATE parse errors */ }
448
+ this._wakeWindowWaiters();
449
+ } catch { /* ignore WINDOW_UPDATE parse errors (e.g. increment=0 is RFC PROTOCOL_ERROR) */ }
405
450
  break;
406
451
  case FRAME_TYPES.PING:
407
452
  // 处理PING帧
@@ -427,12 +472,12 @@ export class HTTP2Parser {
427
472
  } catch (err) {
428
473
  console.error('Error during GOAWAY callback:', err);
429
474
  }
430
- this.endFlag = true;
431
475
  try {
432
476
  this.onEnd?.();
433
477
  } catch (err) {
434
478
  console.error('Error during GOAWAY onEnd callback:', err);
435
479
  }
480
+ this._notifyEndOfStream();
436
481
  break;
437
482
  }
438
483
 
@@ -441,10 +486,8 @@ export class HTTP2Parser {
441
486
  // this.handlePushPromiseFrame(frameHeader, frameData);
442
487
  // break;
443
488
  case FRAME_TYPES.RST_STREAM:
444
- this.endFlag = true;
445
- if (this.onEnd) {
446
- this.onEnd();
447
- }
489
+ this.onEnd?.();
490
+ this._notifyEndOfStream();
448
491
  break;
449
492
  default:
450
493
  console.debug("Unknown frame type:", frameHeader.type);
@@ -455,8 +498,9 @@ export class HTTP2Parser {
455
498
  const length = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
456
499
  const type = buffer[3];
457
500
  const flags = buffer[4];
501
+ // RFC 7540 §4.1: most significant bit is reserved and MUST be ignored on receipt
458
502
  const streamId =
459
- (buffer[5] << 24) | (buffer[6] << 16) | (buffer[7] << 8) | buffer[8];
503
+ ((buffer[5] << 24) | (buffer[6] << 16) | (buffer[7] << 8) | buffer[8]) & 0x7fffffff;
460
504
 
461
505
  return {
462
506
  length,
@@ -486,55 +530,34 @@ export class HTTP2Parser {
486
530
  }
487
531
  }
488
532
 
489
- //等待流结束
533
+ // 等待流结束 — 事件驱动,onEnd 触发时直接唤醒,无 setInterval 轮询
490
534
  waitForEndOfStream(waitTime: number): Promise<void> {
491
535
  return new Promise((resolve, reject) => {
492
- // If the stream has already ended, resolve immediately
493
- if (this.endFlag) {
494
- resolve();
495
- return;
496
- }
497
- // 如果是0 ,则不设置超时
498
- let timeout: ReturnType<typeof setTimeout> | null = null;
499
- if (waitTime > 0) {
500
- timeout = setTimeout(() => {
501
- clearInterval(interval);
502
- reject(new Error("End of stream timeout"));
503
- }, waitTime);
504
- }
536
+ if (this.endFlag) { resolve(); return; }
505
537
 
506
- // Check interval for real-time endFlag monitoring
507
- const checkInterval = 100; // Check every 100 milliseconds
508
- // Set an interval to check the endFlag regularly
509
- const interval = setInterval(() => {
510
- if (this.endFlag) {
511
- if (timeout !== null) {
512
- clearTimeout(timeout);
513
- }
514
- clearInterval(interval);
515
- resolve();
516
- }
517
- }, checkInterval);
518
-
519
- // If the onEnd is triggered externally, it should now be marked manually
520
- const originalOnEnd = this.onEnd;
521
- this.onEnd = () => {
522
- if (!this.endFlag) {
523
- // The external trigger may set endFlag; if not, handle here
524
- this.endFlag = true;
525
- }
526
- if (timeout !== null) {
527
- clearTimeout(timeout);
528
- }
529
- clearInterval(interval);
530
- resolve();
531
- if (originalOnEnd) {
532
- originalOnEnd(); // Call the original onEnd function if set
533
- }
534
- };
538
+ const waiter = { resolve, reject };
539
+ this.endOfStreamWaiters.push(waiter);
540
+
541
+ const timeout = waitTime > 0
542
+ ? setTimeout(() => {
543
+ const idx = this.endOfStreamWaiters.indexOf(waiter);
544
+ if (idx >= 0) this.endOfStreamWaiters.splice(idx, 1);
545
+ reject(new Error("End of stream timeout"));
546
+ }, waitTime)
547
+ : null;
548
+
549
+ waiter.resolve = () => { if (timeout) clearTimeout(timeout); resolve(); };
550
+ waiter.reject = (e: Error) => { if (timeout) clearTimeout(timeout); reject(e); };
535
551
  });
536
552
  }
537
553
 
554
+ /** 内部调用:流结束时唤醒所有 waitForEndOfStream 等待者 */
555
+ private _notifyEndOfStream() {
556
+ this.endFlag = true;
557
+ const ws = this.endOfStreamWaiters.splice(0);
558
+ for (const w of ws) { try { w.resolve(); } catch { /* ignore */ } }
559
+ }
560
+
538
561
  // 解析 WINDOW_UPDATE 帧
539
562
  parseWindowUpdateFrame(frameBuffer: Uint8Array, frameHeader: Frame) {
540
563
  // WINDOW_UPDATE帧的payload固定为4字节