grpc-libp2p-client 0.0.37 → 0.0.38
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/dc-http2/frame.cjs.js +6 -5
- package/dist/dc-http2/frame.cjs.js.map +1 -1
- package/dist/dc-http2/frame.d.ts +1 -1
- package/dist/dc-http2/frame.esm.js +6 -5
- package/dist/dc-http2/frame.esm.js.map +1 -1
- package/dist/dc-http2/hpack.cjs.js +3 -3
- package/dist/dc-http2/hpack.cjs.js.map +1 -1
- package/dist/dc-http2/hpack.esm.js +3 -3
- package/dist/dc-http2/hpack.esm.js.map +1 -1
- package/dist/dc-http2/parser.cjs.js +39 -28
- package/dist/dc-http2/parser.cjs.js.map +1 -1
- package/dist/dc-http2/parser.d.ts +7 -7
- package/dist/dc-http2/parser.esm.js +39 -28
- package/dist/dc-http2/parser.esm.js.map +1 -1
- package/dist/dc-http2/stream.cjs.js +12 -8
- package/dist/dc-http2/stream.cjs.js.map +1 -1
- package/dist/dc-http2/stream.d.ts +1 -1
- package/dist/dc-http2/stream.esm.js +12 -8
- package/dist/dc-http2/stream.esm.js.map +1 -1
- package/dist/grpc.js +231 -265
- package/dist/grpc.js.map +1 -1
- package/dist/grpc.min.js +1 -1
- package/dist/grpc.min.js.map +1 -1
- package/dist/index.cjs.js +89 -99
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +89 -99
- package/dist/index.esm.js.map +1 -1
- package/dist/stats.html +1 -1
- package/package.json +4 -4
- package/src/dc-http2/encoder.ts +3 -2
- package/src/dc-http2/hpack.ts +3 -3
- package/src/dc-http2/parser.ts +43 -38
- package/src/dc-http2/stream.ts +22 -19
- package/src/dc-http2/types.ts +1 -1
- package/src/index.ts +57 -85
package/src/dc-http2/parser.ts
CHANGED
|
@@ -8,22 +8,24 @@ type ParserOptions = {
|
|
|
8
8
|
compatibilityMode?: boolean
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
const HTTP2_PREFACE = new TextEncoder().encode("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
|
|
12
|
+
|
|
11
13
|
export class HTTP2Parser {
|
|
12
14
|
buffer: Uint8Array;
|
|
13
15
|
settingsAckReceived: boolean;
|
|
14
16
|
peerSettingsReceived: boolean;
|
|
15
17
|
connectionWindowSize: number;
|
|
16
|
-
streams: Map<number,
|
|
18
|
+
streams: Map<number, unknown>;
|
|
17
19
|
defaultStreamWindowSize: number;
|
|
18
20
|
// 发送方向(对端的接收窗口)跟踪
|
|
19
21
|
sendConnWindow: number;
|
|
20
22
|
sendStreamWindows: Map<number, number>;
|
|
21
23
|
peerInitialStreamWindow: number;
|
|
22
24
|
private sendWindowWaiters: Array<() => void>;
|
|
23
|
-
onSettings?: (frameHeader:
|
|
24
|
-
onData?: (payload: Uint8Array, frameHeader:
|
|
25
|
+
onSettings?: (frameHeader: Frame) => void;
|
|
26
|
+
onData?: (payload: Uint8Array, frameHeader: Frame) => void;
|
|
25
27
|
onEnd?: () => void;
|
|
26
|
-
onHeaders?: (headers: Uint8Array, frameHeader:
|
|
28
|
+
onHeaders?: (headers: Uint8Array, frameHeader: Frame) => void;
|
|
27
29
|
onGoaway?: (info: { lastStreamId?: number; errorCode?: number }) => void;
|
|
28
30
|
onSettingsParsed?: (settings: { maxConcurrentStreams?: number; initialWindowSize?: number }) => void;
|
|
29
31
|
endFlag: boolean;
|
|
@@ -76,53 +78,58 @@ export class HTTP2Parser {
|
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
// 处理单个数据块
|
|
79
|
-
private _processChunk(chunk:
|
|
81
|
+
private _processChunk(chunk: Uint8Array | { subarray(): Uint8Array }): void {
|
|
80
82
|
// chunk 是 Uint8ArrayList 或 Uint8Array
|
|
81
|
-
const newData =
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
const newData: Uint8Array = 'subarray' in chunk && typeof chunk.subarray === 'function'
|
|
84
|
+
? chunk.subarray()
|
|
85
|
+
: (chunk as Uint8Array);
|
|
86
|
+
|
|
87
|
+
// 原作者之前的 O(N) 内存拷贝优化被保留,去掉了存在 onEnd 竞态的 setTimeout
|
|
84
88
|
const newBuffer = new Uint8Array(this.buffer.length + newData.length);
|
|
85
89
|
newBuffer.set(this.buffer);
|
|
86
90
|
newBuffer.set(newData, this.buffer.length);
|
|
87
91
|
this.buffer = newBuffer;
|
|
88
|
-
|
|
92
|
+
|
|
89
93
|
// 持续处理所有完整的帧
|
|
90
|
-
|
|
94
|
+
let readOffset = 0;
|
|
95
|
+
while (this.buffer.length - readOffset >= 9) {
|
|
91
96
|
// 判断是否有HTTP/2前导
|
|
92
|
-
if (this.buffer.length >= 24 && this.isHttp2Preface(this.buffer)) {
|
|
93
|
-
|
|
97
|
+
if (this.buffer.length - readOffset >= 24 && this.isHttp2Preface(this.buffer.subarray(readOffset))) {
|
|
98
|
+
readOffset += 24;
|
|
94
99
|
// 发送SETTINGS帧
|
|
95
100
|
const settingFrame = Http2Frame.createSettingsFrame();
|
|
96
|
-
this.writer.write(settingFrame
|
|
97
|
-
|
|
101
|
+
this.writer.write(settingFrame);
|
|
102
|
+
continue;
|
|
98
103
|
}
|
|
99
|
-
|
|
104
|
+
|
|
105
|
+
const frameHeader = this._parseFrameHeader(this.buffer.subarray(readOffset));
|
|
100
106
|
const totalFrameLength = 9 + frameHeader.length;
|
|
101
107
|
|
|
102
108
|
// 检查是否有完整的帧
|
|
103
|
-
if (this.buffer.length < totalFrameLength) {
|
|
109
|
+
if (this.buffer.length - readOffset < totalFrameLength) {
|
|
104
110
|
break;
|
|
105
111
|
}
|
|
106
112
|
// 获取完整帧数据
|
|
107
|
-
const frameData = this.buffer.
|
|
113
|
+
const frameData = this.buffer.subarray(readOffset, readOffset + totalFrameLength);
|
|
108
114
|
|
|
109
115
|
// 处理不同类型的帧
|
|
110
116
|
this._handleFrame(frameHeader, frameData).catch((err) => {
|
|
111
117
|
console.error("Error handling frame:", err);
|
|
112
118
|
});
|
|
113
119
|
|
|
114
|
-
//
|
|
115
|
-
|
|
120
|
+
// 移动偏移量
|
|
121
|
+
readOffset += totalFrameLength;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (readOffset > 0) {
|
|
125
|
+
this.buffer = this.buffer.slice(readOffset);
|
|
116
126
|
}
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
private isHttp2Preface(buffer: Uint8Array): boolean {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (buffer.length < PREFACE.length) return false;
|
|
124
|
-
for (let i = 0; i < PREFACE.length; i++) {
|
|
125
|
-
if (buffer[i] !== PREFACE[i]) return false;
|
|
130
|
+
if (buffer.length < HTTP2_PREFACE.length) return false;
|
|
131
|
+
for (let i = 0; i < HTTP2_PREFACE.length; i++) {
|
|
132
|
+
if (buffer[i] !== HTTP2_PREFACE[i]) return false;
|
|
126
133
|
}
|
|
127
134
|
return true;
|
|
128
135
|
}
|
|
@@ -209,7 +216,7 @@ export class HTTP2Parser {
|
|
|
209
216
|
async waitForSendWindow(streamId: number, minBytes: number = 1, timeoutMs: number = 30000): Promise<void> {
|
|
210
217
|
const start = Date.now();
|
|
211
218
|
return new Promise((resolve, reject) => {
|
|
212
|
-
let interval:
|
|
219
|
+
let interval: ReturnType<typeof setInterval> | null = null;
|
|
213
220
|
let settled = false;
|
|
214
221
|
const check = () => {
|
|
215
222
|
const { conn, stream } = this.getSendWindows(streamId);
|
|
@@ -319,7 +326,7 @@ export class HTTP2Parser {
|
|
|
319
326
|
this.peerSettingsReceived = true;
|
|
320
327
|
// 唤醒等待窗口(以防部分实现通过 SETTINGS 改变有效窗口)
|
|
321
328
|
const waiters = this.sendWindowWaiters.splice(0);
|
|
322
|
-
waiters.forEach(fn => { try { fn(); } catch {} });
|
|
329
|
+
waiters.forEach(fn => { try { fn(); } catch (e) { console.debug('waiter error', e); } });
|
|
323
330
|
}
|
|
324
331
|
break;
|
|
325
332
|
|
|
@@ -336,7 +343,7 @@ export class HTTP2Parser {
|
|
|
336
343
|
frameHeader.streamId,
|
|
337
344
|
frameHeader.length ?? 0
|
|
338
345
|
);
|
|
339
|
-
this.writer.write(streamWindowUpdate
|
|
346
|
+
this.writer.write(streamWindowUpdate);
|
|
340
347
|
}
|
|
341
348
|
|
|
342
349
|
// 更新连接级别的窗口
|
|
@@ -344,7 +351,7 @@ export class HTTP2Parser {
|
|
|
344
351
|
0,
|
|
345
352
|
frameHeader.length ?? 0
|
|
346
353
|
);
|
|
347
|
-
this.writer.write(connWindowUpdate
|
|
354
|
+
this.writer.write(connWindowUpdate);
|
|
348
355
|
} catch (err) {
|
|
349
356
|
console.error("[HTTP2] Error sending window update:", err);
|
|
350
357
|
}
|
|
@@ -381,8 +388,7 @@ export class HTTP2Parser {
|
|
|
381
388
|
// 处理窗口更新帧
|
|
382
389
|
this.handleWindowUpdateFrame(
|
|
383
390
|
frameHeader,
|
|
384
|
-
frameData
|
|
385
|
-
frameHeader.streamId
|
|
391
|
+
frameData
|
|
386
392
|
);
|
|
387
393
|
// 更新发送窗口(对端接收窗口)
|
|
388
394
|
try {
|
|
@@ -394,8 +400,8 @@ export class HTTP2Parser {
|
|
|
394
400
|
this.sendStreamWindows.set(frameHeader.streamId, cur + inc);
|
|
395
401
|
}
|
|
396
402
|
const waiters = this.sendWindowWaiters.splice(0);
|
|
397
|
-
waiters.forEach(fn => { try { fn(); } catch {} });
|
|
398
|
-
} catch
|
|
403
|
+
waiters.forEach(fn => { try { fn(); } catch (e) { console.debug('waiter error', e); } });
|
|
404
|
+
} catch { /* ignore WINDOW_UPDATE parse errors */ }
|
|
399
405
|
break;
|
|
400
406
|
case FRAME_TYPES.PING:
|
|
401
407
|
// 处理PING帧
|
|
@@ -415,7 +421,7 @@ export class HTTP2Parser {
|
|
|
415
421
|
console.warn('[HTTP2] GOAWAY received');
|
|
416
422
|
info = {};
|
|
417
423
|
}
|
|
418
|
-
} catch {}
|
|
424
|
+
} catch { /* ignore GOAWAY parse errors */ }
|
|
419
425
|
try {
|
|
420
426
|
this.onGoaway?.(info ?? {});
|
|
421
427
|
} catch (err) {
|
|
@@ -473,7 +479,7 @@ export class HTTP2Parser {
|
|
|
473
479
|
// 反馈PONG帧
|
|
474
480
|
const pongFrame = Http2Frame.createPongFrame(frameData.slice(9));
|
|
475
481
|
try {
|
|
476
|
-
this.writer.write(pongFrame
|
|
482
|
+
this.writer.write(pongFrame);
|
|
477
483
|
} catch (error) {
|
|
478
484
|
console.error("Error sending PONG frame:", error);
|
|
479
485
|
throw error;
|
|
@@ -489,7 +495,7 @@ export class HTTP2Parser {
|
|
|
489
495
|
return;
|
|
490
496
|
}
|
|
491
497
|
// 如果是0 ,则不设置超时
|
|
492
|
-
let timeout:
|
|
498
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
493
499
|
if (waitTime > 0) {
|
|
494
500
|
timeout = setTimeout(() => {
|
|
495
501
|
clearInterval(interval);
|
|
@@ -561,8 +567,7 @@ export class HTTP2Parser {
|
|
|
561
567
|
// 处理 WINDOW_UPDATE 帧
|
|
562
568
|
handleWindowUpdateFrame(
|
|
563
569
|
frameHeader: Frame,
|
|
564
|
-
payload: Uint8Array
|
|
565
|
-
streamId: number
|
|
570
|
+
payload: Uint8Array
|
|
566
571
|
) {
|
|
567
572
|
try {
|
|
568
573
|
const windowUpdate = this.parseWindowUpdateFrame(payload, frameHeader);
|
package/src/dc-http2/stream.ts
CHANGED
|
@@ -41,13 +41,13 @@ export class StreamWriter {
|
|
|
41
41
|
private lastBackpressureCheck = 0 // 添加时间戳缓存
|
|
42
42
|
private bytesDrained = 0 // 统计下游实际消化的字节数
|
|
43
43
|
private lastDrainEventAt = 0
|
|
44
|
-
private watchdogTimer:
|
|
44
|
+
private watchdogTimer: ReturnType<typeof setInterval> | undefined
|
|
45
45
|
private stallStartAt = 0
|
|
46
46
|
private lastBytesDrainedSeen = 0
|
|
47
47
|
private lastBpWarnAt = 0
|
|
48
48
|
private isHandlingError = false // 防止重复错误处理
|
|
49
49
|
|
|
50
|
-
private log?: { trace?: (...args:
|
|
50
|
+
private log?: { trace?: (...args: unknown[]) => void }
|
|
51
51
|
|
|
52
52
|
constructor(
|
|
53
53
|
private stream: Stream,
|
|
@@ -58,7 +58,7 @@ export class StreamWriter {
|
|
|
58
58
|
throw new Error('StreamWriter requires a valid stream object')
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
this.log = { trace: (...args:
|
|
61
|
+
this.log = { trace: (...args: unknown[]) => console.debug('[StreamWriter]', ...args) }
|
|
62
62
|
|
|
63
63
|
if (options){
|
|
64
64
|
this.options = {
|
|
@@ -172,10 +172,10 @@ export class StreamWriter {
|
|
|
172
172
|
// 等待 drain 事件
|
|
173
173
|
await this.stream.onDrain()
|
|
174
174
|
}
|
|
175
|
-
} catch (err:
|
|
175
|
+
} catch (err: unknown) {
|
|
176
176
|
// Gracefully handle stream closing errors - 不要传递到 handleError
|
|
177
|
-
const errMsg = err.message
|
|
178
|
-
if (err.name === 'StreamStateError' ||
|
|
177
|
+
const errMsg = (err instanceof Error ? err.message : '').toLowerCase()
|
|
178
|
+
if ((err instanceof Error && err.name === 'StreamStateError') ||
|
|
179
179
|
errMsg.includes('closing') ||
|
|
180
180
|
errMsg.includes('closed') ||
|
|
181
181
|
errMsg.includes('write to a stream that is closed')) {
|
|
@@ -188,6 +188,7 @@ export class StreamWriter {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
private createTransform() {
|
|
191
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
191
192
|
const self = this;
|
|
192
193
|
return async function* (source: AsyncIterable<Uint8Array>) {
|
|
193
194
|
for await (const chunk of source) {
|
|
@@ -197,8 +198,8 @@ export class StreamWriter {
|
|
|
197
198
|
// 因此这里统计的 bytesDrained 更接近实际被 sink 消费的字节数
|
|
198
199
|
try {
|
|
199
200
|
// 在下游消费后再扣减待消费队列,避免误判“next 没取”
|
|
200
|
-
if (
|
|
201
|
-
|
|
201
|
+
if (self.p._queueSize != null) {
|
|
202
|
+
self.p._queueSize = Math.max(0, self.p._queueSize - chunk.byteLength)
|
|
202
203
|
}
|
|
203
204
|
self.bytesDrained += chunk.byteLength
|
|
204
205
|
const now = Date.now()
|
|
@@ -245,7 +246,7 @@ export class StreamWriter {
|
|
|
245
246
|
}, intervalMs)
|
|
246
247
|
}
|
|
247
248
|
|
|
248
|
-
async write(data: ArrayBuffer | Blob | string): Promise<void> {
|
|
249
|
+
async write(data: ArrayBuffer | Uint8Array | Blob | string): Promise<void> {
|
|
249
250
|
// 静默处理 aborted 状态,避免在正常的流关闭场景下抛出错误
|
|
250
251
|
if (this.abortController.signal.aborted) {
|
|
251
252
|
return Promise.resolve()
|
|
@@ -272,9 +273,10 @@ export class StreamWriter {
|
|
|
272
273
|
})
|
|
273
274
|
}
|
|
274
275
|
|
|
275
|
-
private async convertToBuffer(data: ArrayBuffer | Blob | string): Promise<ArrayBuffer> {
|
|
276
|
+
private async convertToBuffer(data: ArrayBuffer | Uint8Array | Blob | string): Promise<ArrayBuffer> {
|
|
276
277
|
if (data instanceof Blob) return data.arrayBuffer()
|
|
277
|
-
if (typeof data === 'string') return new TextEncoder().encode(data).buffer
|
|
278
|
+
if (typeof data === 'string') return new TextEncoder().encode(data).buffer as ArrayBuffer
|
|
279
|
+
if (data instanceof Uint8Array) return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer
|
|
278
280
|
return data
|
|
279
281
|
}
|
|
280
282
|
|
|
@@ -438,9 +440,9 @@ export class StreamWriter {
|
|
|
438
440
|
if (this.stream && typeof this.stream.close === 'function') {
|
|
439
441
|
try {
|
|
440
442
|
await this.stream.close()
|
|
441
|
-
} catch (err:
|
|
443
|
+
} catch (err: unknown) {
|
|
442
444
|
// 忽略关闭已关闭流的错误
|
|
443
|
-
const errMsg = err.message
|
|
445
|
+
const errMsg = (err instanceof Error ? err.message : '').toLowerCase()
|
|
444
446
|
if (!errMsg.includes('closed') && !errMsg.includes('closing')) {
|
|
445
447
|
this.log?.trace?.('Stream close error:', err)
|
|
446
448
|
}
|
|
@@ -461,15 +463,16 @@ export class StreamWriter {
|
|
|
461
463
|
// 先检查流状态,避免在已关闭的流上调用 abort
|
|
462
464
|
if (this.stream && typeof this.stream.abort === 'function') {
|
|
463
465
|
// 检查流的状态,避免操作已关闭的流
|
|
464
|
-
const streamState = (this.stream as
|
|
466
|
+
const streamState = (this.stream as { status?: string; state?: string }).status ||
|
|
467
|
+
(this.stream as { status?: string; state?: string }).state
|
|
465
468
|
if (streamState !== 'closed' && streamState !== 'closing') {
|
|
466
469
|
this.stream.abort(new Error(reason))
|
|
467
470
|
}
|
|
468
471
|
}
|
|
469
|
-
} catch (err:
|
|
472
|
+
} catch (err: unknown) {
|
|
470
473
|
// Stream may already be closed, ignore all stream-related errors
|
|
471
474
|
// 完全忽略流操作错误,避免在错误处理中再次抛出错误
|
|
472
|
-
const errMsg = err.message
|
|
475
|
+
const errMsg = (err instanceof Error ? err.message : '').toLowerCase()
|
|
473
476
|
if (!errMsg.includes('closed') && !errMsg.includes('closing') && !errMsg.includes('write')) {
|
|
474
477
|
this.log?.trace?.('Stream abort error:', err)
|
|
475
478
|
}
|
|
@@ -494,13 +497,13 @@ export class StreamWriter {
|
|
|
494
497
|
|
|
495
498
|
// 立即拒绝所有待处理的写入任务,避免它们继续执行
|
|
496
499
|
const pendingTasks = this.writeQueue.splice(0)
|
|
497
|
-
pendingTasks.forEach(
|
|
500
|
+
pendingTasks.forEach(() => {
|
|
498
501
|
// 这些任务的 Promise 会在执行时因为检查到 aborted 而被拒绝
|
|
499
502
|
})
|
|
500
503
|
|
|
501
504
|
try {
|
|
502
505
|
this.p.end()
|
|
503
|
-
} catch
|
|
506
|
+
} catch {
|
|
504
507
|
// Ignore errors when ending pushable
|
|
505
508
|
}
|
|
506
509
|
|
|
@@ -526,7 +529,7 @@ export class StreamWriter {
|
|
|
526
529
|
}
|
|
527
530
|
|
|
528
531
|
// 事件系统
|
|
529
|
-
private listeners = new Map<string,
|
|
532
|
+
private listeners = new Map<string, ((event: CustomEvent) => void)[]>()
|
|
530
533
|
|
|
531
534
|
addEventListener(type: string, callback: (event: CustomEvent) => void) {
|
|
532
535
|
const handlers = this.listeners.get(type) || []
|