grpc-libp2p-client 0.0.6 → 0.0.7
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/package.json +1 -1
- package/src/dc-http2/frame.ts +68 -14
- package/src/dc-http2/parser.ts +19 -5
- package/src/index.ts +63 -11
- package/dist/dc-http2/frame.cjs.js +0 -1468
- package/dist/dc-http2/frame.cjs.js.map +0 -1
- package/dist/dc-http2/frame.d.ts +0 -34
- package/dist/dc-http2/frame.esm.js +0 -1466
- package/dist/dc-http2/frame.esm.js.map +0 -1
- package/dist/dc-http2/hpack.cjs.js +0 -1201
- package/dist/dc-http2/hpack.cjs.js.map +0 -1
- package/dist/dc-http2/hpack.d.ts +0 -45
- package/dist/dc-http2/hpack.esm.js +0 -1199
- package/dist/dc-http2/hpack.esm.js.map +0 -1
- package/dist/dc-http2/parser.cjs.js +0 -1755
- package/dist/dc-http2/parser.cjs.js.map +0 -1
- package/dist/dc-http2/parser.d.ts +0 -87
- package/dist/dc-http2/parser.esm.js +0 -1753
- package/dist/dc-http2/parser.esm.js.map +0 -1
- package/dist/dc-http2/stream.cjs.js +0 -250
- package/dist/dc-http2/stream.cjs.js.map +0 -1
- package/dist/dc-http2/stream.d.ts +0 -42
- package/dist/dc-http2/stream.esm.js +0 -248
- package/dist/dc-http2/stream.esm.js.map +0 -1
- package/dist/grpc.js +0 -3377
- package/dist/grpc.js.map +0 -1
- package/dist/grpc.min.js +0 -1
- package/dist/grpc.min.js.map +0 -1
- package/dist/index.cjs.js +0 -2221
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.d.ts +0 -21
- package/dist/index.esm.js +0 -2219
- package/dist/index.esm.js.map +0 -1
package/package.json
CHANGED
package/src/dc-http2/frame.ts
CHANGED
|
@@ -45,22 +45,78 @@ export class Http2Frame {
|
|
|
45
45
|
return FrameEncoder.encodeSettingsAckFrame();
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* 创建 WINDOW_UPDATE 帧
|
|
50
|
+
* @param streamId 流ID,0表示连接级别的窗口更新
|
|
51
|
+
* @param increment 窗口大小增量(必须为正数)
|
|
52
|
+
* @returns 编码好的 WINDOW_UPDATE 帧
|
|
53
|
+
*/
|
|
54
|
+
static createWindowUpdateFrame(streamId: number, increment: number): Uint8Array {
|
|
55
|
+
// 验证窗口增量
|
|
56
|
+
if (increment <= 0 || increment > 2147483647) { // 2^31 - 1
|
|
57
|
+
throw new Error('Window size increment must be between 1 and 2^31-1');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 创建4字节的payload
|
|
61
|
+
const payload = new Uint8Array(4);
|
|
62
|
+
|
|
63
|
+
// 写入窗口大小增量 (31位无符号整数,最高位保留为0)
|
|
64
|
+
payload[0] = (increment >> 24) & 0x7F; // 只用低7位,确保最高位为0
|
|
65
|
+
payload[1] = (increment >> 16) & 0xFF;
|
|
66
|
+
payload[2] = (increment >> 8) & 0xFF;
|
|
67
|
+
payload[3] = increment & 0xFF;
|
|
68
|
+
|
|
69
|
+
// 创建帧,类型0x8表示WINDOW_UPDATE
|
|
70
|
+
return Http2Frame.createFrame(0x8, 0x0, streamId, payload);
|
|
71
|
+
}
|
|
72
|
+
|
|
48
73
|
|
|
49
|
-
static createDataFrames(streamId: number, data: Uint8Array,shouldEnd: boolean = false, maxFrameSize: number = 16384): Uint8Array[] {
|
|
50
|
-
|
|
51
|
-
let offset = 0;
|
|
74
|
+
static createDataFrames(streamId: number, data: Uint8Array, shouldEnd: boolean = false, maxFrameSize: number = 16384): Uint8Array[] {
|
|
75
|
+
const frames: Uint8Array[] = [];
|
|
52
76
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
77
|
+
// 首先构建完整的 gRPC 消息
|
|
78
|
+
// 格式: Compressed-Flag(1) + Message-Length(4) + Message-Data
|
|
79
|
+
const messageLength = data.length;
|
|
80
|
+
const grpcMessage = new Uint8Array(5 + messageLength);
|
|
81
|
+
|
|
82
|
+
// Compressed-Flag (0 = 不压缩)
|
|
83
|
+
grpcMessage[0] = 0;
|
|
61
84
|
|
|
62
|
-
|
|
85
|
+
// Message-Length (4 bytes) - 整个消息的长度
|
|
86
|
+
grpcMessage[1] = (messageLength >> 24) & 0xFF;
|
|
87
|
+
grpcMessage[2] = (messageLength >> 16) & 0xFF;
|
|
88
|
+
grpcMessage[3] = (messageLength >> 8) & 0xFF;
|
|
89
|
+
grpcMessage[4] = messageLength & 0xFF;
|
|
90
|
+
|
|
91
|
+
// Message-Data
|
|
92
|
+
grpcMessage.set(data, 5);
|
|
93
|
+
|
|
94
|
+
// 然后将完整的 gRPC 消息分割成多个 HTTP/2 DATA 帧
|
|
95
|
+
// HTTP/2 帧头为 9 字节
|
|
96
|
+
const maxDataPerFrame = maxFrameSize - 9;
|
|
97
|
+
|
|
98
|
+
for (let offset = 0; offset < grpcMessage.length; offset += maxDataPerFrame) {
|
|
99
|
+
const remaining = grpcMessage.length - offset;
|
|
100
|
+
const chunkSize = Math.min(maxDataPerFrame, remaining);
|
|
101
|
+
const chunk = grpcMessage.slice(offset, offset + chunkSize);
|
|
102
|
+
|
|
103
|
+
// 判断是否是最后一个数据块
|
|
104
|
+
const isLastChunk = (offset + chunkSize) >= grpcMessage.length;
|
|
105
|
+
const endStream = isLastChunk && shouldEnd;
|
|
106
|
+
|
|
107
|
+
// 创建 HTTP/2 DATA 帧
|
|
108
|
+
const frame = Http2Frame.createFrame(
|
|
109
|
+
0x0, // DATA 帧类型
|
|
110
|
+
endStream ? 0x01 : 0x0, // 只在最后一块且需要结束流时设置 END_STREAM
|
|
111
|
+
streamId,
|
|
112
|
+
chunk
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
frames.push(frame);
|
|
63
116
|
}
|
|
117
|
+
|
|
118
|
+
return frames;
|
|
119
|
+
}
|
|
64
120
|
|
|
65
121
|
static createDataFrame( streamId:number,data:Uint8Array,endStream:boolean = true):Uint8Array {
|
|
66
122
|
// gRPC 消息格式: 压缩标志(1字节) + 消息长度(4字节) + 消息内容
|
|
@@ -101,7 +157,6 @@ export class Http2Frame {
|
|
|
101
157
|
// 将 headers 编码为 HPACK 格式
|
|
102
158
|
const hpack = new HPACK();
|
|
103
159
|
const encodedHeaders = hpack.encode(headersList);
|
|
104
|
-
console.log('Encoded:', encodedHeaders);
|
|
105
160
|
// HEADERS frame flags: END_HEADERS | END_STREAM
|
|
106
161
|
const flags = endHeaders ? 0x04 : 0x00
|
|
107
162
|
return Http2Frame.createFrame(0x01, flags, streamId, encodedHeaders)
|
|
@@ -111,7 +166,6 @@ export class Http2Frame {
|
|
|
111
166
|
// 将 headers 编码为 HPACK 格式
|
|
112
167
|
const hpack = new HPACK();
|
|
113
168
|
const encodedHeaders = hpack.encode(headersList);
|
|
114
|
-
console.log('Encoded:', encodedHeaders);
|
|
115
169
|
// HEADERS frame flags: END_HEADERS | END_STREAM
|
|
116
170
|
const flags = endHeaders ? 0x04 : 0x00
|
|
117
171
|
return Http2Frame.createFrame(0x01, flags, streamId, encodedHeaders)
|
package/src/dc-http2/parser.ts
CHANGED
|
@@ -47,7 +47,6 @@ export class HTTP2Parser {
|
|
|
47
47
|
while (this.buffer.length >= 9) {
|
|
48
48
|
// 判断是否有HTTP/2前导
|
|
49
49
|
if (this.buffer.length >= 24 && this.isHttp2Preface(this.buffer)) {
|
|
50
|
-
console.log("HTTP/2 preface detected");
|
|
51
50
|
this.buffer = this.buffer.slice(24);
|
|
52
51
|
// 发送SETTINGS帧
|
|
53
52
|
const settingFrme = Http2Frame.createSettingsFrame();
|
|
@@ -114,7 +113,6 @@ export class HTTP2Parser {
|
|
|
114
113
|
case FRAME_TYPES.SETTINGS:
|
|
115
114
|
if ((frameHeader.flags & FRAME_FLAGS.ACK) === FRAME_FLAGS.ACK) {
|
|
116
115
|
this.settingsAckReceived = true;
|
|
117
|
-
console.log("Received SETTINGS ACK");
|
|
118
116
|
} else {
|
|
119
117
|
//接收到Setting请求,进行解析
|
|
120
118
|
const settingsPayload = frameData.slice(9);
|
|
@@ -127,7 +125,6 @@ export class HTTP2Parser {
|
|
|
127
125
|
settingsPayload[i + 3];
|
|
128
126
|
settings[id] = value;
|
|
129
127
|
}
|
|
130
|
-
console.log("Received SETTINGS:", settings);
|
|
131
128
|
|
|
132
129
|
//发送ACK
|
|
133
130
|
if (this.onSettings) {
|
|
@@ -141,6 +138,23 @@ export class HTTP2Parser {
|
|
|
141
138
|
if (this.onData) {
|
|
142
139
|
this.onData(frameData.slice(9), frameHeader); // 跳过帧头
|
|
143
140
|
}
|
|
141
|
+
// 更新流窗口和连接窗口
|
|
142
|
+
try {
|
|
143
|
+
// 更新流级别的窗口
|
|
144
|
+
if (frameHeader.streamId !== 0) {
|
|
145
|
+
const streamWindowUpdate = Http2Frame.createWindowUpdateFrame(
|
|
146
|
+
frameHeader.streamId,
|
|
147
|
+
frameHeader.length
|
|
148
|
+
);
|
|
149
|
+
this.writer.write(streamWindowUpdate);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 更新连接级别的窗口
|
|
153
|
+
const connWindowUpdate = Http2Frame.createWindowUpdateFrame(0, frameHeader.length);
|
|
154
|
+
this.writer.write(connWindowUpdate);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
console.error("[HTTP2] Error sending window update:", err);
|
|
157
|
+
}
|
|
144
158
|
//判断是否是最后一个帧
|
|
145
159
|
if (
|
|
146
160
|
(frameHeader.flags & FRAME_FLAGS.END_STREAM) ===
|
|
@@ -286,7 +300,8 @@ export class HTTP2Parser {
|
|
|
286
300
|
}
|
|
287
301
|
|
|
288
302
|
// 确保frameBuffer是Uint8Array类型
|
|
289
|
-
|
|
303
|
+
// const buffer = new Uint8Array(frameBuffer);
|
|
304
|
+
const buffer = new Uint8Array(frameBuffer.slice(9));
|
|
290
305
|
|
|
291
306
|
// 读取window size increment (4字节,大端序)
|
|
292
307
|
// 手动计算32位无符号整数,确保最高位为0
|
|
@@ -316,7 +331,6 @@ export class HTTP2Parser {
|
|
|
316
331
|
const windowUpdate = this.parseWindowUpdateFrame(payload, frameHeader);
|
|
317
332
|
|
|
318
333
|
this.connectionWindowSize += windowUpdate.windowSizeIncrement;
|
|
319
|
-
console.log(`Connection window size increased by ${windowUpdate.windowSizeIncrement}`);
|
|
320
334
|
|
|
321
335
|
return windowUpdate;
|
|
322
336
|
} catch (error) {
|
package/src/index.ts
CHANGED
|
@@ -55,6 +55,7 @@ export class Libp2pGrpcClient {
|
|
|
55
55
|
let stream:Stream|null = null
|
|
56
56
|
let responseData: Uint8Array | null = null
|
|
57
57
|
let responseBuffer: Uint8Array[] = [] // 添加缓冲区来累积数据
|
|
58
|
+
let responseDataExpectedLength = -1 // 当前响应的期望长度
|
|
58
59
|
const hpack = new HPACK()
|
|
59
60
|
let exitFlag = false
|
|
60
61
|
let errMsg = ''
|
|
@@ -69,13 +70,52 @@ export class Libp2pGrpcClient {
|
|
|
69
70
|
const streamId = this.steamManager.getNextAppLevelStreamId()
|
|
70
71
|
const writer = new StreamWriter(stream.sink)
|
|
71
72
|
const parser = new HTTP2Parser(writer);
|
|
72
|
-
|
|
73
|
+
responseDataExpectedLength = -1 // 重置期望长度
|
|
74
|
+
responseBuffer = [] // 重置缓冲区
|
|
73
75
|
parser.onData = (payload,frameHeader) => {//接收数据
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
if (responseDataExpectedLength === -1) {//grpc消息头部未读取
|
|
77
|
+
//提取gRPC消息头部
|
|
78
|
+
if (payload.length < 5) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
const compressionFlag = payload[0] // 压缩标志
|
|
82
|
+
const lengthBytes = payload.slice(1, 5) // 消息长度的4字节
|
|
83
|
+
responseDataExpectedLength = new DataView(lengthBytes.buffer, lengthBytes.byteOffset).getUint32(0, false) // big-endian
|
|
84
|
+
if (responseDataExpectedLength < 0) {
|
|
85
|
+
throw new Error('Invalid gRPC message length')
|
|
86
|
+
}
|
|
87
|
+
if (responseDataExpectedLength + 5 > payload.length) {
|
|
88
|
+
// 如果当前 payload 不足以包含完整的 gRPC 消息,缓存数据
|
|
89
|
+
const grpcData = payload.subarray(5)
|
|
90
|
+
responseBuffer.push(grpcData)
|
|
91
|
+
responseDataExpectedLength -= grpcData.length // 更新期望长度
|
|
92
|
+
return
|
|
93
|
+
}else {
|
|
94
|
+
// 如果当前 payload 足以包含完整的 gRPC 消息,重置缓冲区
|
|
95
|
+
const grpcData = payload.subarray(5) // 提取完整的 gRPC 消息
|
|
96
|
+
responseBuffer.push(grpcData)
|
|
97
|
+
responseData = grpcData
|
|
98
|
+
isResponseComplete = true
|
|
99
|
+
responseDataExpectedLength = -1 // 重置期望长度
|
|
100
|
+
}
|
|
101
|
+
}else if (responseDataExpectedLength > 0) {//grpc消息头部已读取
|
|
102
|
+
responseBuffer.push(payload) // 将数据添加到缓冲区
|
|
103
|
+
responseDataExpectedLength -= payload.length // 更新期望长度
|
|
104
|
+
if (responseDataExpectedLength <= 0) {
|
|
105
|
+
// 如果缓冲区中的数据已经完全处理,重置缓冲区
|
|
106
|
+
responseData = new Uint8Array(responseBuffer.reduce((sum, chunk) => sum + chunk.length, 0))
|
|
107
|
+
let offset = 0
|
|
108
|
+
for (const chunk of responseBuffer) {
|
|
109
|
+
responseData.set(chunk, offset)
|
|
110
|
+
offset += chunk.length
|
|
111
|
+
|
|
112
|
+
}
|
|
113
|
+
responseDataExpectedLength = -1
|
|
114
|
+
isResponseComplete = true // 设置响应完成标志
|
|
115
|
+
}
|
|
116
|
+
}
|
|
77
117
|
// 检查是否是流的最后一个帧(END_STREAM 标志)
|
|
78
|
-
if (frameHeader && (frameHeader.flags & 0x1)) { // END_STREAM flag
|
|
118
|
+
if (frameHeader && (frameHeader.flags & 0x1) && !isResponseComplete) { // END_STREAM flag
|
|
79
119
|
// 合并所有缓冲的数据
|
|
80
120
|
const totalLength = responseBuffer.reduce((sum, chunk) => sum + chunk.length, 0)
|
|
81
121
|
responseData = new Uint8Array(totalLength)
|
|
@@ -87,16 +127,31 @@ export class Libp2pGrpcClient {
|
|
|
87
127
|
isResponseComplete = true
|
|
88
128
|
}
|
|
89
129
|
}
|
|
130
|
+
parser.onEnd = () => {//接收结束
|
|
131
|
+
if (!isResponseComplete) {
|
|
132
|
+
isResponseComplete = true // 设置响应完成标志
|
|
133
|
+
if (responseBuffer.length === 0) {
|
|
134
|
+
responseData = new Uint8Array() // 如果没有数据,返回空数组
|
|
135
|
+
}else{
|
|
136
|
+
// 合并所有缓冲的数据
|
|
137
|
+
const totalLength = responseBuffer.reduce((sum, chunk) => sum + chunk.length, 0)
|
|
138
|
+
responseData = new Uint8Array(totalLength)
|
|
139
|
+
let offset = 0
|
|
140
|
+
for (const chunk of responseBuffer) {
|
|
141
|
+
responseData.set(chunk, offset)
|
|
142
|
+
offset += chunk.length
|
|
143
|
+
}
|
|
144
|
+
isResponseComplete = true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
90
148
|
parser.onSettings = () => {//接收settings,反馈ack
|
|
91
|
-
console.log('Settings received')
|
|
92
149
|
const ackSettingFrame = Http2Frame.createSettingsAckFrame()
|
|
93
150
|
writer.write(ackSettingFrame)
|
|
94
151
|
}
|
|
95
152
|
parser.onHeaders = (headers,header) => {
|
|
96
153
|
const plainHeaders = hpack.decodeHeaderFields(headers)
|
|
97
|
-
console.log('Received headers:', plainHeaders);
|
|
98
154
|
if (plainHeaders.get('grpc-status') === '0') {
|
|
99
|
-
console.log('gRPC call success')
|
|
100
155
|
} else if (plainHeaders.get('grpc-status') !== undefined) {
|
|
101
156
|
exitFlag = true
|
|
102
157
|
errMsg = plainHeaders.get('grpc-message') || 'gRPC call failed'
|
|
@@ -225,15 +280,12 @@ export class Libp2pGrpcClient {
|
|
|
225
280
|
};
|
|
226
281
|
|
|
227
282
|
parser.onSettings = () => {//接收settings,反馈ack
|
|
228
|
-
console.log('Settings received')
|
|
229
283
|
const ackSettingFrame = Http2Frame.createSettingsAckFrame()
|
|
230
284
|
writer.write(ackSettingFrame)
|
|
231
285
|
}
|
|
232
286
|
parser.onHeaders = (headers,header) => {
|
|
233
287
|
const plainHeaders = hpack.decodeHeaderFields(headers)
|
|
234
|
-
console.log('Received headers:', plainHeaders);
|
|
235
288
|
if (plainHeaders.get('grpc-status') === '0') {
|
|
236
|
-
console.log('gRPC call success')
|
|
237
289
|
} else if (plainHeaders.get('grpc-status') !== undefined) {
|
|
238
290
|
const errMsg = plainHeaders.get('grpc-message') || 'gRPC call failed'
|
|
239
291
|
const err = new Error(errMsg)
|