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