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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grpc-libp2p-client",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "description": "grpc-libp2p-client",
6
6
  "main": "./dist/index.cjs.js",
@@ -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
- const frames: Uint8Array[] = [];
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
- while (offset < data.length) {
54
- const chunkSize = Math.min(maxFrameSize, data.length - offset);
55
- const chunk = data.slice(offset, offset + chunkSize);
56
- let isEndStream = shouldEnd && (offset + chunkSize >= data.length); // 最后一帧设置 END_STREAM
57
- const frame = Http2Frame.createDataFrame(streamId, chunk, isEndStream);
58
- frames.push(frame);
59
- offset += chunkSize;
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
- return frames;
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)
@@ -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
- const buffer = new Uint8Array(frameBuffer);
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
- const grpcData = payload.subarray(5) // 移除 gRPC 长度前缀
75
- responseBuffer.push(grpcData)
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)