@peers-app/peers-sdk 0.8.8 → 0.8.10
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.
|
@@ -132,36 +132,80 @@ function createBinaryPeerSocket(connectionId, peer, protocol = 'binary') {
|
|
|
132
132
|
const transportType = protocol === 'wrtc' ? 'wrtc' :
|
|
133
133
|
protocol === 'libp2p' ? 'libp2p' :
|
|
134
134
|
protocol === 'ws' ? 'ws' : 'unknown';
|
|
135
|
-
//
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
// WebRTC SCTP has ~256KB max message size, use 64KB to be safe
|
|
136
|
+
const WRTC_MAX_CHUNK = 64 * 1024;
|
|
137
|
+
// Backpressure thresholds
|
|
138
|
+
const HIGH_WATER_MARK = transportType === 'wrtc' ? 64 * 1024 : 4 * 1024 * 1024;
|
|
139
|
+
const LOW_WATER_MARK = transportType === 'wrtc' ? 16 * 1024 : 2 * 1024 * 1024;
|
|
140
|
+
// Helper to wait for buffer to drain
|
|
141
|
+
const waitForDrain = async () => {
|
|
142
|
+
if (!peer.getBufferedAmount)
|
|
143
|
+
return;
|
|
144
|
+
const buffered = peer.getBufferedAmount();
|
|
145
|
+
if (buffered <= HIGH_WATER_MARK)
|
|
146
|
+
return;
|
|
147
|
+
await new Promise(resolve => {
|
|
148
|
+
const checkDrain = () => {
|
|
149
|
+
const currentBuffered = peer.getBufferedAmount ? peer.getBufferedAmount() : 0;
|
|
150
|
+
if (!peer.getBufferedAmount || currentBuffered < LOW_WATER_MARK) {
|
|
151
|
+
resolve();
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
setTimeout(checkDrain, 5);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
peer.on('drain', resolve);
|
|
158
|
+
checkDrain();
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
// Helper to send a single raw bytes packet
|
|
162
|
+
const sendRawBytesPacket = async (streamIdBytes, payload) => {
|
|
163
|
+
const packet = new Uint8Array(1 + 2 + streamIdBytes.length + payload.length);
|
|
139
164
|
packet[0] = PROTOCOL_RAW_BYTES;
|
|
140
165
|
const view = new DataView(packet.buffer, packet.byteOffset, packet.byteLength);
|
|
141
166
|
view.setUint16(1, streamIdBytes.length, false);
|
|
142
167
|
packet.set(streamIdBytes, 3);
|
|
143
|
-
packet.set(
|
|
144
|
-
|
|
145
|
-
const HIGH_WATER_MARK = transportType === 'wrtc' ? 128 * 1024 : 4 * 1024 * 1024;
|
|
146
|
-
const LOW_WATER_MARK = transportType === 'wrtc' ? 32 * 1024 : 2 * 1024 * 1024;
|
|
147
|
-
if (peer.getBufferedAmount && peer.getBufferedAmount() > HIGH_WATER_MARK) {
|
|
148
|
-
await new Promise(resolve => {
|
|
149
|
-
const checkDrain = () => {
|
|
150
|
-
if (!peer.getBufferedAmount || peer.getBufferedAmount() < LOW_WATER_MARK) {
|
|
151
|
-
resolve();
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
setTimeout(checkDrain, 5);
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
peer.on('drain', resolve);
|
|
158
|
-
checkDrain();
|
|
159
|
-
});
|
|
160
|
-
}
|
|
168
|
+
packet.set(payload, 3 + streamIdBytes.length);
|
|
169
|
+
await waitForDrain();
|
|
161
170
|
stats.bytesSent += packet.length;
|
|
162
171
|
stats.messagesSent++;
|
|
163
172
|
peer.send(packet);
|
|
164
173
|
};
|
|
174
|
+
// Core sendRawBytes implementation
|
|
175
|
+
// For WebRTC: chunks large data to fit under SCTP message size limit
|
|
176
|
+
// For RPC_STREAM_ID: also prepends 4-byte length for reassembly
|
|
177
|
+
const sendRawBytesImpl = async (streamId, data) => {
|
|
178
|
+
const streamIdBytes = new TextEncoder().encode(streamId);
|
|
179
|
+
const headerOverhead = 1 + 2 + streamIdBytes.length; // PROTOCOL + streamIdLen + streamId
|
|
180
|
+
const maxPayloadSize = WRTC_MAX_CHUNK - headerOverhead;
|
|
181
|
+
// For WebRTC with large data
|
|
182
|
+
if (transportType === 'wrtc' && data.length > maxPayloadSize) {
|
|
183
|
+
// RPC_STREAM_ID needs length prefix for reassembly (receiver must reconstruct complete message)
|
|
184
|
+
if (streamId === RPC_STREAM_ID) {
|
|
185
|
+
// Prepend 4-byte total length to data
|
|
186
|
+
const withLength = new Uint8Array(4 + data.length);
|
|
187
|
+
new DataView(withLength.buffer).setUint32(0, data.length, false);
|
|
188
|
+
withLength.set(data, 4);
|
|
189
|
+
// Send in chunks
|
|
190
|
+
for (let offset = 0; offset < withLength.length; offset += maxPayloadSize) {
|
|
191
|
+
const chunk = withLength.subarray(offset, Math.min(offset + maxPayloadSize, withLength.length));
|
|
192
|
+
await sendRawBytesPacket(streamIdBytes, chunk);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
// Other streams: just chunk without length prefix
|
|
197
|
+
// Handlers receive smaller pieces but that's OK (e.g., file transfers stream to disk)
|
|
198
|
+
for (let offset = 0; offset < data.length; offset += maxPayloadSize) {
|
|
199
|
+
const chunk = data.subarray(offset, Math.min(offset + maxPayloadSize, data.length));
|
|
200
|
+
await sendRawBytesPacket(streamIdBytes, chunk);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Small data or non-WebRTC: send directly
|
|
206
|
+
await sendRawBytesPacket(streamIdBytes, data);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
165
209
|
// Helper to send RPC messages - routes large messages through raw bytes streaming
|
|
166
210
|
const sendRPCData = async (encoded) => {
|
|
167
211
|
if (encoded.length > LARGE_MESSAGE_THRESHOLD) {
|
|
@@ -178,6 +222,9 @@ function createBinaryPeerSocket(connectionId, peer, protocol = 'binary') {
|
|
|
178
222
|
peer.send(withProtocol);
|
|
179
223
|
}
|
|
180
224
|
};
|
|
225
|
+
// Buffer for RPC stream reassembly (large RPC messages sent via raw bytes are length-prefixed)
|
|
226
|
+
let rpcStreamBuffer = null;
|
|
227
|
+
let rpcStreamExpectedLength = null;
|
|
181
228
|
// Handle incoming data
|
|
182
229
|
peer.on('data', async (data) => {
|
|
183
230
|
try {
|
|
@@ -198,17 +245,52 @@ function createBinaryPeerSocket(connectionId, peer, protocol = 'binary') {
|
|
|
198
245
|
const streamIdBytes = bytes.subarray(3, 3 + streamIdLength);
|
|
199
246
|
const streamId = new TextDecoder().decode(streamIdBytes);
|
|
200
247
|
const payload = bytes.subarray(3 + streamIdLength);
|
|
201
|
-
//
|
|
248
|
+
// Handle RPC stream with reassembly (large RPC messages are length-prefixed)
|
|
202
249
|
if (streamId === RPC_STREAM_ID) {
|
|
203
|
-
|
|
250
|
+
// Append to buffer
|
|
251
|
+
if (rpcStreamBuffer === null) {
|
|
252
|
+
rpcStreamBuffer = payload;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const combined = new Uint8Array(rpcStreamBuffer.length + payload.length);
|
|
256
|
+
combined.set(rpcStreamBuffer, 0);
|
|
257
|
+
combined.set(payload, rpcStreamBuffer.length);
|
|
258
|
+
rpcStreamBuffer = combined;
|
|
259
|
+
}
|
|
260
|
+
// Process complete messages from buffer
|
|
261
|
+
while (rpcStreamBuffer && rpcStreamBuffer.length >= 4) {
|
|
262
|
+
// Read expected length if not yet known
|
|
263
|
+
if (rpcStreamExpectedLength === null) {
|
|
264
|
+
const lenView = new DataView(rpcStreamBuffer.buffer, rpcStreamBuffer.byteOffset, rpcStreamBuffer.byteLength);
|
|
265
|
+
rpcStreamExpectedLength = lenView.getUint32(0, false);
|
|
266
|
+
}
|
|
267
|
+
// Check if we have the complete message
|
|
268
|
+
if (rpcStreamBuffer.length >= 4 + rpcStreamExpectedLength) {
|
|
269
|
+
const rpcData = rpcStreamBuffer.subarray(4, 4 + rpcStreamExpectedLength);
|
|
270
|
+
handleRPCMessage(rpcData, handlers, callbacks, sendRPCData);
|
|
271
|
+
// Remove processed data from buffer
|
|
272
|
+
if (rpcStreamBuffer.length === 4 + rpcStreamExpectedLength) {
|
|
273
|
+
rpcStreamBuffer = null;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
rpcStreamBuffer = rpcStreamBuffer.subarray(4 + rpcStreamExpectedLength);
|
|
277
|
+
}
|
|
278
|
+
rpcStreamExpectedLength = null;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// Need more data
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
204
285
|
return;
|
|
205
286
|
}
|
|
287
|
+
// Other streams: pass through to handler
|
|
206
288
|
const handler = rawBytesHandlers[streamId];
|
|
207
289
|
if (handler)
|
|
208
290
|
handler(payload);
|
|
209
291
|
return;
|
|
210
292
|
}
|
|
211
|
-
// Handle RPC messages
|
|
293
|
+
// Handle RPC messages (small, sent directly)
|
|
212
294
|
if (protocolType === PROTOCOL_RPC) {
|
|
213
295
|
const rpcBytes = bytes.subarray(1);
|
|
214
296
|
handleRPCMessage(rpcBytes, handlers, callbacks, sendRPCData);
|