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/dist/index.esm.js
CHANGED
|
@@ -355,7 +355,7 @@ class HPACK {
|
|
|
355
355
|
}
|
|
356
356
|
// Huffman编码实现
|
|
357
357
|
huffmanEncode(bytes) {
|
|
358
|
-
|
|
358
|
+
const result = [];
|
|
359
359
|
let current = 0;
|
|
360
360
|
let bits = 0;
|
|
361
361
|
for (let i = 0; i < bytes.length; i++) {
|
|
@@ -448,7 +448,7 @@ class HPACK {
|
|
|
448
448
|
try {
|
|
449
449
|
result = this.decode(bytes);
|
|
450
450
|
}
|
|
451
|
-
catch
|
|
451
|
+
catch {
|
|
452
452
|
result = '';
|
|
453
453
|
}
|
|
454
454
|
}
|
|
@@ -456,7 +456,7 @@ class HPACK {
|
|
|
456
456
|
try {
|
|
457
457
|
result = new TextDecoder().decode(bytes);
|
|
458
458
|
}
|
|
459
|
-
catch
|
|
459
|
+
catch {
|
|
460
460
|
result = '';
|
|
461
461
|
}
|
|
462
462
|
}
|
|
@@ -502,7 +502,8 @@ class FrameEncoder {
|
|
|
502
502
|
// 编码SETTINGS帧
|
|
503
503
|
static encodeSettingsFrame(frame) {
|
|
504
504
|
// 计算payload总长度:每个设置项占6字节
|
|
505
|
-
const
|
|
505
|
+
const settingsPayload = frame.payload;
|
|
506
|
+
const payloadLength = settingsPayload.length * 6;
|
|
506
507
|
// 分配缓冲区:9字节头部 + payload长度
|
|
507
508
|
const buffer = new Uint8Array(9 + payloadLength);
|
|
508
509
|
// 编码帧头部(前9个字节)
|
|
@@ -513,7 +514,7 @@ class FrameEncoder {
|
|
|
513
514
|
streamId: frame.streamId
|
|
514
515
|
});
|
|
515
516
|
// 编码payload
|
|
516
|
-
_encodeSettingsPayload(buffer,
|
|
517
|
+
_encodeSettingsPayload(buffer, settingsPayload);
|
|
517
518
|
return buffer;
|
|
518
519
|
}
|
|
519
520
|
// 编码SETTINGS ACK帧
|
|
@@ -583,11 +584,11 @@ const defaultSettings = {
|
|
|
583
584
|
[SETTINGS_PARAMETERS.MAX_FRAME_SIZE]: 16 << 10, // 16k
|
|
584
585
|
[SETTINGS_PARAMETERS.MAX_HEADER_LIST_SIZE]: 8192
|
|
585
586
|
};
|
|
586
|
-
const HTTP2_PREFACE = 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n';
|
|
587
|
+
const HTTP2_PREFACE$1 = 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n';
|
|
587
588
|
class Http2Frame {
|
|
588
589
|
// 创建并编码PREFACE帧
|
|
589
590
|
static createPreface() {
|
|
590
|
-
return new TextEncoder().encode(HTTP2_PREFACE);
|
|
591
|
+
return new TextEncoder().encode(HTTP2_PREFACE$1);
|
|
591
592
|
}
|
|
592
593
|
static createPongFrame(payload) {
|
|
593
594
|
return Http2Frame.createFrame(0x6, 0x1, 0, payload);
|
|
@@ -804,6 +805,7 @@ function _createPayload(settings) {
|
|
|
804
805
|
return payload;
|
|
805
806
|
}
|
|
806
807
|
|
|
808
|
+
const HTTP2_PREFACE = new TextEncoder().encode("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
|
|
807
809
|
class HTTP2Parser {
|
|
808
810
|
constructor(writer, options) {
|
|
809
811
|
this.buffer = new Uint8Array(0);
|
|
@@ -851,44 +853,49 @@ class HTTP2Parser {
|
|
|
851
853
|
// 处理单个数据块
|
|
852
854
|
_processChunk(chunk) {
|
|
853
855
|
// chunk 是 Uint8ArrayList 或 Uint8Array
|
|
854
|
-
const newData =
|
|
855
|
-
|
|
856
|
+
const newData = 'subarray' in chunk && typeof chunk.subarray === 'function'
|
|
857
|
+
? chunk.subarray()
|
|
858
|
+
: chunk;
|
|
859
|
+
// 原作者之前的 O(N) 内存拷贝优化被保留,去掉了存在 onEnd 竞态的 setTimeout
|
|
856
860
|
const newBuffer = new Uint8Array(this.buffer.length + newData.length);
|
|
857
861
|
newBuffer.set(this.buffer);
|
|
858
862
|
newBuffer.set(newData, this.buffer.length);
|
|
859
863
|
this.buffer = newBuffer;
|
|
860
864
|
// 持续处理所有完整的帧
|
|
861
|
-
|
|
865
|
+
let readOffset = 0;
|
|
866
|
+
while (this.buffer.length - readOffset >= 9) {
|
|
862
867
|
// 判断是否有HTTP/2前导
|
|
863
|
-
if (this.buffer.length >= 24 && this.isHttp2Preface(this.buffer)) {
|
|
864
|
-
|
|
868
|
+
if (this.buffer.length - readOffset >= 24 && this.isHttp2Preface(this.buffer.subarray(readOffset))) {
|
|
869
|
+
readOffset += 24;
|
|
865
870
|
// 发送SETTINGS帧
|
|
866
871
|
const settingFrame = Http2Frame.createSettingsFrame();
|
|
867
872
|
this.writer.write(settingFrame);
|
|
868
|
-
|
|
873
|
+
continue;
|
|
869
874
|
}
|
|
870
|
-
const frameHeader = this._parseFrameHeader(this.buffer);
|
|
875
|
+
const frameHeader = this._parseFrameHeader(this.buffer.subarray(readOffset));
|
|
871
876
|
const totalFrameLength = 9 + frameHeader.length;
|
|
872
877
|
// 检查是否有完整的帧
|
|
873
|
-
if (this.buffer.length < totalFrameLength) {
|
|
878
|
+
if (this.buffer.length - readOffset < totalFrameLength) {
|
|
874
879
|
break;
|
|
875
880
|
}
|
|
876
881
|
// 获取完整帧数据
|
|
877
|
-
const frameData = this.buffer.
|
|
882
|
+
const frameData = this.buffer.subarray(readOffset, readOffset + totalFrameLength);
|
|
878
883
|
// 处理不同类型的帧
|
|
879
884
|
this._handleFrame(frameHeader, frameData).catch((err) => {
|
|
880
885
|
console.error("Error handling frame:", err);
|
|
881
886
|
});
|
|
882
|
-
//
|
|
883
|
-
|
|
887
|
+
// 移动偏移量
|
|
888
|
+
readOffset += totalFrameLength;
|
|
889
|
+
}
|
|
890
|
+
if (readOffset > 0) {
|
|
891
|
+
this.buffer = this.buffer.slice(readOffset);
|
|
884
892
|
}
|
|
885
893
|
}
|
|
886
894
|
isHttp2Preface(buffer) {
|
|
887
|
-
|
|
888
|
-
if (buffer.length < PREFACE.length)
|
|
895
|
+
if (buffer.length < HTTP2_PREFACE.length)
|
|
889
896
|
return false;
|
|
890
|
-
for (let i = 0; i <
|
|
891
|
-
if (buffer[i] !==
|
|
897
|
+
for (let i = 0; i < HTTP2_PREFACE.length; i++) {
|
|
898
|
+
if (buffer[i] !== HTTP2_PREFACE[i])
|
|
892
899
|
return false;
|
|
893
900
|
}
|
|
894
901
|
return true;
|
|
@@ -1075,7 +1082,9 @@ class HTTP2Parser {
|
|
|
1075
1082
|
waiters.forEach(fn => { try {
|
|
1076
1083
|
fn();
|
|
1077
1084
|
}
|
|
1078
|
-
catch {
|
|
1085
|
+
catch (e) {
|
|
1086
|
+
console.debug('waiter error', e);
|
|
1087
|
+
} });
|
|
1079
1088
|
}
|
|
1080
1089
|
break;
|
|
1081
1090
|
case FRAME_TYPES.DATA:
|
|
@@ -1124,7 +1133,7 @@ class HTTP2Parser {
|
|
|
1124
1133
|
break;
|
|
1125
1134
|
case FRAME_TYPES.WINDOW_UPDATE:
|
|
1126
1135
|
// 处理窗口更新帧
|
|
1127
|
-
this.handleWindowUpdateFrame(frameHeader, frameData
|
|
1136
|
+
this.handleWindowUpdateFrame(frameHeader, frameData);
|
|
1128
1137
|
// 更新发送窗口(对端接收窗口)
|
|
1129
1138
|
try {
|
|
1130
1139
|
const inc = this.parseWindowUpdateFrame(frameData, frameHeader).windowSizeIncrement;
|
|
@@ -1139,9 +1148,11 @@ class HTTP2Parser {
|
|
|
1139
1148
|
waiters.forEach(fn => { try {
|
|
1140
1149
|
fn();
|
|
1141
1150
|
}
|
|
1142
|
-
catch {
|
|
1151
|
+
catch (e) {
|
|
1152
|
+
console.debug('waiter error', e);
|
|
1153
|
+
} });
|
|
1143
1154
|
}
|
|
1144
|
-
catch
|
|
1155
|
+
catch { /* ignore WINDOW_UPDATE parse errors */ }
|
|
1145
1156
|
break;
|
|
1146
1157
|
case FRAME_TYPES.PING:
|
|
1147
1158
|
// 处理PING帧
|
|
@@ -1163,7 +1174,7 @@ class HTTP2Parser {
|
|
|
1163
1174
|
info = {};
|
|
1164
1175
|
}
|
|
1165
1176
|
}
|
|
1166
|
-
catch { }
|
|
1177
|
+
catch { /* ignore GOAWAY parse errors */ }
|
|
1167
1178
|
try {
|
|
1168
1179
|
this.onGoaway?.(info ?? {});
|
|
1169
1180
|
}
|
|
@@ -1296,7 +1307,7 @@ class HTTP2Parser {
|
|
|
1296
1307
|
};
|
|
1297
1308
|
}
|
|
1298
1309
|
// 处理 WINDOW_UPDATE 帧
|
|
1299
|
-
handleWindowUpdateFrame(frameHeader, payload
|
|
1310
|
+
handleWindowUpdateFrame(frameHeader, payload) {
|
|
1300
1311
|
try {
|
|
1301
1312
|
const windowUpdate = this.parseWindowUpdateFrame(payload, frameHeader);
|
|
1302
1313
|
this.connectionWindowSize += windowUpdate.windowSizeIncrement;
|
|
@@ -1437,8 +1448,8 @@ class StreamWriter {
|
|
|
1437
1448
|
}
|
|
1438
1449
|
catch (err) {
|
|
1439
1450
|
// Gracefully handle stream closing errors - 不要传递到 handleError
|
|
1440
|
-
const errMsg = err.message
|
|
1441
|
-
if (err.name === 'StreamStateError' ||
|
|
1451
|
+
const errMsg = (err instanceof Error ? err.message : '').toLowerCase();
|
|
1452
|
+
if ((err instanceof Error && err.name === 'StreamStateError') ||
|
|
1442
1453
|
errMsg.includes('closing') ||
|
|
1443
1454
|
errMsg.includes('closed') ||
|
|
1444
1455
|
errMsg.includes('write to a stream that is closed')) {
|
|
@@ -1450,6 +1461,7 @@ class StreamWriter {
|
|
|
1450
1461
|
}
|
|
1451
1462
|
}
|
|
1452
1463
|
createTransform() {
|
|
1464
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
1453
1465
|
const self = this;
|
|
1454
1466
|
return async function* (source) {
|
|
1455
1467
|
for await (const chunk of source) {
|
|
@@ -1459,7 +1471,7 @@ class StreamWriter {
|
|
|
1459
1471
|
// 因此这里统计的 bytesDrained 更接近实际被 sink 消费的字节数
|
|
1460
1472
|
try {
|
|
1461
1473
|
// 在下游消费后再扣减待消费队列,避免误判“next 没取”
|
|
1462
|
-
if (self.p
|
|
1474
|
+
if (self.p._queueSize != null) {
|
|
1463
1475
|
self.p._queueSize = Math.max(0, self.p._queueSize - chunk.byteLength);
|
|
1464
1476
|
}
|
|
1465
1477
|
self.bytesDrained += chunk.byteLength;
|
|
@@ -1540,6 +1552,8 @@ class StreamWriter {
|
|
|
1540
1552
|
return data.arrayBuffer();
|
|
1541
1553
|
if (typeof data === 'string')
|
|
1542
1554
|
return new TextEncoder().encode(data).buffer;
|
|
1555
|
+
if (data instanceof Uint8Array)
|
|
1556
|
+
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
1543
1557
|
return data;
|
|
1544
1558
|
}
|
|
1545
1559
|
async writeChunks(buffer) {
|
|
@@ -1682,7 +1696,7 @@ class StreamWriter {
|
|
|
1682
1696
|
}
|
|
1683
1697
|
catch (err) {
|
|
1684
1698
|
// 忽略关闭已关闭流的错误
|
|
1685
|
-
const errMsg = err.message
|
|
1699
|
+
const errMsg = (err instanceof Error ? err.message : '').toLowerCase();
|
|
1686
1700
|
if (!errMsg.includes('closed') && !errMsg.includes('closing')) {
|
|
1687
1701
|
this.log?.trace?.('Stream close error:', err);
|
|
1688
1702
|
}
|
|
@@ -1700,7 +1714,8 @@ class StreamWriter {
|
|
|
1700
1714
|
// 先检查流状态,避免在已关闭的流上调用 abort
|
|
1701
1715
|
if (this.stream && typeof this.stream.abort === 'function') {
|
|
1702
1716
|
// 检查流的状态,避免操作已关闭的流
|
|
1703
|
-
const streamState = this.stream.status ||
|
|
1717
|
+
const streamState = this.stream.status ||
|
|
1718
|
+
this.stream.state;
|
|
1704
1719
|
if (streamState !== 'closed' && streamState !== 'closing') {
|
|
1705
1720
|
this.stream.abort(new Error(reason));
|
|
1706
1721
|
}
|
|
@@ -1709,7 +1724,7 @@ class StreamWriter {
|
|
|
1709
1724
|
catch (err) {
|
|
1710
1725
|
// Stream may already be closed, ignore all stream-related errors
|
|
1711
1726
|
// 完全忽略流操作错误,避免在错误处理中再次抛出错误
|
|
1712
|
-
const errMsg = err.message
|
|
1727
|
+
const errMsg = (err instanceof Error ? err.message : '').toLowerCase();
|
|
1713
1728
|
if (!errMsg.includes('closed') && !errMsg.includes('closing') && !errMsg.includes('write')) {
|
|
1714
1729
|
this.log?.trace?.('Stream abort error:', err);
|
|
1715
1730
|
}
|
|
@@ -1731,13 +1746,13 @@ class StreamWriter {
|
|
|
1731
1746
|
}
|
|
1732
1747
|
// 立即拒绝所有待处理的写入任务,避免它们继续执行
|
|
1733
1748
|
const pendingTasks = this.writeQueue.splice(0);
|
|
1734
|
-
pendingTasks.forEach(
|
|
1749
|
+
pendingTasks.forEach(() => {
|
|
1735
1750
|
// 这些任务的 Promise 会在执行时因为检查到 aborted 而被拒绝
|
|
1736
1751
|
});
|
|
1737
1752
|
try {
|
|
1738
1753
|
this.p.end();
|
|
1739
1754
|
}
|
|
1740
|
-
catch
|
|
1755
|
+
catch {
|
|
1741
1756
|
// Ignore errors when ending pushable
|
|
1742
1757
|
}
|
|
1743
1758
|
if (this.watchdogTimer) {
|
|
@@ -1877,6 +1892,7 @@ class Libp2pGrpcClient {
|
|
|
1877
1892
|
}
|
|
1878
1893
|
await new Promise((resolve, reject) => {
|
|
1879
1894
|
let settled = false;
|
|
1895
|
+
// eslint-disable-next-line prefer-const
|
|
1880
1896
|
let timeoutId;
|
|
1881
1897
|
const cleanup = () => {
|
|
1882
1898
|
const idx = state.waiters.indexOf(waiter);
|
|
@@ -1959,7 +1975,7 @@ class Libp2pGrpcClient {
|
|
|
1959
1975
|
if (pooled) {
|
|
1960
1976
|
const conn = pooled.connection;
|
|
1961
1977
|
if (conn) {
|
|
1962
|
-
const status = conn
|
|
1978
|
+
const status = conn.status;
|
|
1963
1979
|
if (!status || status === "open") {
|
|
1964
1980
|
return conn;
|
|
1965
1981
|
}
|
|
@@ -1986,17 +2002,9 @@ class Libp2pGrpcClient {
|
|
|
1986
2002
|
}
|
|
1987
2003
|
};
|
|
1988
2004
|
try {
|
|
1989
|
-
|
|
1990
|
-
if (typeof anyConn.addEventListener === "function") {
|
|
1991
|
-
anyConn.addEventListener("close", removeFromPool, {
|
|
1992
|
-
once: true,
|
|
1993
|
-
});
|
|
1994
|
-
}
|
|
1995
|
-
else if (typeof anyConn.once === "function") {
|
|
1996
|
-
anyConn.once("close", removeFromPool);
|
|
1997
|
-
}
|
|
2005
|
+
conn.addEventListener("close", removeFromPool, { once: true });
|
|
1998
2006
|
}
|
|
1999
|
-
catch { }
|
|
2007
|
+
catch { /* ignore event listener registration errors */ }
|
|
2000
2008
|
}
|
|
2001
2009
|
}
|
|
2002
2010
|
return conn;
|
|
@@ -2066,14 +2074,14 @@ class Libp2pGrpcClient {
|
|
|
2066
2074
|
});
|
|
2067
2075
|
try {
|
|
2068
2076
|
writer.addEventListener("backpressure", (e) => {
|
|
2069
|
-
const d = e
|
|
2077
|
+
const d = e.detail || {};
|
|
2070
2078
|
console.warn(`[unary stream ${streamId}] backpressure current=${d.currentSize} avg=${d.averageSize} threshold=${d.threshold}`);
|
|
2071
2079
|
});
|
|
2072
|
-
writer.addEventListener("drain", (
|
|
2073
|
-
|
|
2080
|
+
writer.addEventListener("drain", () => {
|
|
2081
|
+
// drain event - no action needed
|
|
2074
2082
|
});
|
|
2075
2083
|
writer.addEventListener("stalled", (e) => {
|
|
2076
|
-
const d = e
|
|
2084
|
+
const d = e.detail || {};
|
|
2077
2085
|
console.warn(`[unary stream ${streamId}] stalled queue=${d.queueSize} drained=${d.drained} since=${d.sinceMs}ms — sending PING`);
|
|
2078
2086
|
try {
|
|
2079
2087
|
const payload = new Uint8Array(8);
|
|
@@ -2081,10 +2089,10 @@ class Libp2pGrpcClient {
|
|
|
2081
2089
|
const ping = Http2Frame.createFrame(0x6, 0x0, 0, payload);
|
|
2082
2090
|
writer.write(ping);
|
|
2083
2091
|
}
|
|
2084
|
-
catch { }
|
|
2092
|
+
catch { /* ignore ping write errors */ }
|
|
2085
2093
|
});
|
|
2086
2094
|
}
|
|
2087
|
-
catch { }
|
|
2095
|
+
catch { /* ignore addEventListener errors */ }
|
|
2088
2096
|
const parser = new HTTP2Parser(writer);
|
|
2089
2097
|
parser.onGoaway = (info) => {
|
|
2090
2098
|
console.warn("[unaryCall] GOAWAY received from server", info);
|
|
@@ -2095,7 +2103,7 @@ class Libp2pGrpcClient {
|
|
|
2095
2103
|
exitFlag = true;
|
|
2096
2104
|
errMsg = `GOAWAY received: code=${info.errorCode}`;
|
|
2097
2105
|
try {
|
|
2098
|
-
connection?.close
|
|
2106
|
+
connection?.close();
|
|
2099
2107
|
}
|
|
2100
2108
|
catch (err) {
|
|
2101
2109
|
console.warn("Error closing connection after GOAWAY:", err);
|
|
@@ -2120,7 +2128,6 @@ class Libp2pGrpcClient {
|
|
|
2120
2128
|
if (payload.length < 5) {
|
|
2121
2129
|
return;
|
|
2122
2130
|
}
|
|
2123
|
-
const compressionFlag = payload[0]; // 压缩标志
|
|
2124
2131
|
const lengthBytes = payload.slice(1, 5); // 消息长度的4字节
|
|
2125
2132
|
responseDataExpectedLength = new DataView(lengthBytes.buffer, lengthBytes.byteOffset).getUint32(0, false); // big-endian
|
|
2126
2133
|
if (responseDataExpectedLength < 0) {
|
|
@@ -2197,7 +2204,7 @@ class Libp2pGrpcClient {
|
|
|
2197
2204
|
const ackSettingFrame = Http2Frame.createSettingsAckFrame();
|
|
2198
2205
|
writer.write(ackSettingFrame);
|
|
2199
2206
|
};
|
|
2200
|
-
parser.onHeaders = (headers
|
|
2207
|
+
parser.onHeaders = (headers) => {
|
|
2201
2208
|
const plainHeaders = hpack.decodeHeaderFields(headers);
|
|
2202
2209
|
if (plainHeaders.get("grpc-status") === "0") {
|
|
2203
2210
|
// 成功状态
|
|
@@ -2222,21 +2229,12 @@ class Libp2pGrpcClient {
|
|
|
2222
2229
|
const settingFrme = Http2Frame.createSettingsFrame();
|
|
2223
2230
|
await writer.write(settingFrme);
|
|
2224
2231
|
// 等待对端 SETTINGS 或 ACK,择一即可,避免偶发握手竞态
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
})(),
|
|
2232
|
-
(async () => {
|
|
2233
|
-
await parser.waitForSettingsAck();
|
|
2234
|
-
progressed = true;
|
|
2235
|
-
})(),
|
|
2236
|
-
new Promise((res) => setTimeout(res, 300)),
|
|
2237
|
-
]);
|
|
2238
|
-
// 即使未等到,也继续;多数实现会随后发送
|
|
2239
|
-
}
|
|
2232
|
+
await Promise.race([
|
|
2233
|
+
parser.waitForPeerSettings(1000),
|
|
2234
|
+
parser.waitForSettingsAck(),
|
|
2235
|
+
new Promise((res) => setTimeout(res, 300)),
|
|
2236
|
+
]);
|
|
2237
|
+
// 即使未等到,也继续;多数实现会随后发送
|
|
2240
2238
|
// 创建头部帧
|
|
2241
2239
|
const headerFrame = Http2Frame.createHeadersFrame(streamId, method, true, this.token);
|
|
2242
2240
|
await writer.write(headerFrame);
|
|
@@ -2264,9 +2262,9 @@ class Libp2pGrpcClient {
|
|
|
2264
2262
|
checkResponse();
|
|
2265
2263
|
});
|
|
2266
2264
|
try {
|
|
2267
|
-
await writer.flush
|
|
2265
|
+
await writer.flush(timeout);
|
|
2268
2266
|
}
|
|
2269
|
-
catch { }
|
|
2267
|
+
catch { /* ignore flush errors */ }
|
|
2270
2268
|
await writer.end();
|
|
2271
2269
|
}
|
|
2272
2270
|
catch (err) {
|
|
@@ -2366,7 +2364,7 @@ class Libp2pGrpcClient {
|
|
|
2366
2364
|
if (options?.freshConnection) {
|
|
2367
2365
|
try {
|
|
2368
2366
|
this.connectionPool.delete(this.peerAddr.toString());
|
|
2369
|
-
await this.node
|
|
2367
|
+
await this.node.hangUp(this.peerAddr);
|
|
2370
2368
|
console.warn("[Call] hangUp existing connection before dialing due to freshConnection=true");
|
|
2371
2369
|
}
|
|
2372
2370
|
catch (err) {
|
|
@@ -2399,14 +2397,14 @@ class Libp2pGrpcClient {
|
|
|
2399
2397
|
});
|
|
2400
2398
|
try {
|
|
2401
2399
|
writer.addEventListener("backpressure", (e) => {
|
|
2402
|
-
const d = e
|
|
2400
|
+
const d = e.detail || {};
|
|
2403
2401
|
console.warn(`[stream ${streamId}] backpressure current=${d.currentSize} avg=${d.averageSize} threshold=${d.threshold}`);
|
|
2404
2402
|
});
|
|
2405
|
-
writer.addEventListener("drain", (
|
|
2406
|
-
|
|
2403
|
+
writer.addEventListener("drain", () => {
|
|
2404
|
+
// drain event - no action needed
|
|
2407
2405
|
});
|
|
2408
2406
|
writer.addEventListener("stalled", (e) => {
|
|
2409
|
-
const d = e
|
|
2407
|
+
const d = e.detail || {};
|
|
2410
2408
|
console.warn(`[stream ${streamId}] stalled queue=${d.queueSize} drained=${d.drained} since=${d.sinceMs}ms — sending PING`);
|
|
2411
2409
|
try {
|
|
2412
2410
|
const payload = new Uint8Array(8);
|
|
@@ -2414,10 +2412,10 @@ class Libp2pGrpcClient {
|
|
|
2414
2412
|
const ping = Http2Frame.createFrame(0x6, 0x0, 0, payload);
|
|
2415
2413
|
writer.write(ping);
|
|
2416
2414
|
}
|
|
2417
|
-
catch { /*
|
|
2415
|
+
catch { /* ignore ping write errors */ }
|
|
2418
2416
|
});
|
|
2419
2417
|
}
|
|
2420
|
-
catch { /*
|
|
2418
|
+
catch { /* ignore addEventListener errors */ }
|
|
2421
2419
|
const parser = new HTTP2Parser(writer, {
|
|
2422
2420
|
compatibilityMode: !useFlowControl,
|
|
2423
2421
|
});
|
|
@@ -2434,7 +2432,7 @@ class Libp2pGrpcClient {
|
|
|
2434
2432
|
}
|
|
2435
2433
|
internalController.abort();
|
|
2436
2434
|
try {
|
|
2437
|
-
connection?.close
|
|
2435
|
+
connection?.close();
|
|
2438
2436
|
}
|
|
2439
2437
|
catch (err) {
|
|
2440
2438
|
console.warn("Error closing connection after GOAWAY:", err);
|
|
@@ -2469,7 +2467,7 @@ class Libp2pGrpcClient {
|
|
|
2469
2467
|
}
|
|
2470
2468
|
};
|
|
2471
2469
|
// 在各个回调中检查是否已中止
|
|
2472
|
-
parser.onData = async (payload
|
|
2470
|
+
parser.onData = async (payload) => {
|
|
2473
2471
|
// 检查是否已中止
|
|
2474
2472
|
if (internalController.signal.aborted) {
|
|
2475
2473
|
return;
|
|
@@ -2489,7 +2487,6 @@ class Libp2pGrpcClient {
|
|
|
2489
2487
|
// 如果还没有读取消息长度,且缓冲区有足够数据
|
|
2490
2488
|
if (expectedMessageLength === -1 && messageBuffer.length >= 5) {
|
|
2491
2489
|
// 读取 gRPC 消息头:1字节压缩标志 + 4字节长度
|
|
2492
|
-
const compressionFlag = messageBuffer[0];
|
|
2493
2490
|
const lengthBytes = messageBuffer.slice(1, 5);
|
|
2494
2491
|
expectedMessageLength = new DataView(lengthBytes.buffer, lengthBytes.byteOffset).getUint32(0, false); // big-endian
|
|
2495
2492
|
}
|
|
@@ -2526,7 +2523,7 @@ class Libp2pGrpcClient {
|
|
|
2526
2523
|
const ackSettingFrame = Http2Frame.createSettingsAckFrame();
|
|
2527
2524
|
writer.write(ackSettingFrame);
|
|
2528
2525
|
};
|
|
2529
|
-
parser.onHeaders = (headers
|
|
2526
|
+
parser.onHeaders = (headers) => {
|
|
2530
2527
|
// 检查是否已中止
|
|
2531
2528
|
if (internalController.signal.aborted)
|
|
2532
2529
|
return;
|
|
@@ -2572,16 +2569,9 @@ class Libp2pGrpcClient {
|
|
|
2572
2569
|
}
|
|
2573
2570
|
// 等待对端 SETTINGS 或 ACK,择一即可,避免偶发握手竞态
|
|
2574
2571
|
{
|
|
2575
|
-
let progressed = false;
|
|
2576
2572
|
await Promise.race([
|
|
2577
|
-
(
|
|
2578
|
-
|
|
2579
|
-
progressed = true;
|
|
2580
|
-
})(),
|
|
2581
|
-
(async () => {
|
|
2582
|
-
await parser.waitForSettingsAck();
|
|
2583
|
-
progressed = true;
|
|
2584
|
-
})(),
|
|
2573
|
+
parser.waitForPeerSettings(1000),
|
|
2574
|
+
parser.waitForSettingsAck(),
|
|
2585
2575
|
new Promise((res) => setTimeout(res, 300)),
|
|
2586
2576
|
]);
|
|
2587
2577
|
// 即使未等到,也继续;多数实现会随后发送
|
|
@@ -2618,7 +2608,6 @@ class Libp2pGrpcClient {
|
|
|
2618
2608
|
}
|
|
2619
2609
|
// 动态批量处理逻辑 - 在处理过程中动态补充新数据
|
|
2620
2610
|
const batchSize = options?.batchSize || 10;
|
|
2621
|
-
const maxBatchWaitMs = options?.maxBatchWaitMs || 50;
|
|
2622
2611
|
// 动态批处理器
|
|
2623
2612
|
const processingQueue = [];
|
|
2624
2613
|
let isProcessing = false;
|
|
@@ -2749,9 +2738,9 @@ class Libp2pGrpcClient {
|
|
|
2749
2738
|
await writeFrame(finalFrame);
|
|
2750
2739
|
// 在结束前尽量冲刷内部队列,避免服务器看到部分数据 + context canceled
|
|
2751
2740
|
try {
|
|
2752
|
-
await writer.flush
|
|
2741
|
+
await writer.flush(timeout);
|
|
2753
2742
|
}
|
|
2754
|
-
catch { }
|
|
2743
|
+
catch { /* ignore flush errors */ }
|
|
2755
2744
|
await writer.end();
|
|
2756
2745
|
}
|
|
2757
2746
|
// 检查是否已中止
|
|
@@ -2798,15 +2787,16 @@ class Libp2pGrpcClient {
|
|
|
2798
2787
|
if (options?.freshConnection) {
|
|
2799
2788
|
try {
|
|
2800
2789
|
// 通过 libp2p 连接管理器关闭到该 peer 的连接
|
|
2801
|
-
|
|
2790
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2791
|
+
const conns = this.node.getConnections?.(this.peerAddr) || [];
|
|
2802
2792
|
for (const c of conns) {
|
|
2803
2793
|
try {
|
|
2804
2794
|
await c.close?.();
|
|
2805
2795
|
}
|
|
2806
|
-
catch { }
|
|
2796
|
+
catch { /* ignore close errors */ }
|
|
2807
2797
|
}
|
|
2808
2798
|
}
|
|
2809
|
-
catch { }
|
|
2799
|
+
catch { /* ignore connection cleanup errors */ }
|
|
2810
2800
|
}
|
|
2811
2801
|
if (streamSlotAcquired && state) {
|
|
2812
2802
|
state.activeStreams = Math.max(0, state.activeStreams - 1);
|