socket-function 0.10.7 → 0.11.0
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/CallFactory.ts +142 -41
package/package.json
CHANGED
package/src/CallFactory.ts
CHANGED
|
@@ -29,7 +29,6 @@ type InternalReturnType = {
|
|
|
29
29
|
result: unknown;
|
|
30
30
|
error?: string;
|
|
31
31
|
seqNum: number;
|
|
32
|
-
resultSize: number;
|
|
33
32
|
isResultCompressed?: boolean;
|
|
34
33
|
};
|
|
35
34
|
|
|
@@ -228,7 +227,6 @@ export async function createCallFactory(
|
|
|
228
227
|
result: undefined,
|
|
229
228
|
error: error,
|
|
230
229
|
seqNum: call.call.seqNum,
|
|
231
|
-
resultSize: 0,
|
|
232
230
|
});
|
|
233
231
|
}
|
|
234
232
|
|
|
@@ -275,7 +273,16 @@ export async function createCallFactory(
|
|
|
275
273
|
}
|
|
276
274
|
|
|
277
275
|
const BASE_LENGTH_OFFSET = 324_432_461_592_612;
|
|
278
|
-
|
|
276
|
+
type MessageHeader = {
|
|
277
|
+
type: "serialized";
|
|
278
|
+
bufferCount: number;
|
|
279
|
+
} | {
|
|
280
|
+
type: "Buffer[]" | "Buffer";
|
|
281
|
+
bufferCount: number;
|
|
282
|
+
bufferLengths?: number[];
|
|
283
|
+
metadata: Omit<InternalReturnType, "result">;
|
|
284
|
+
};
|
|
285
|
+
async function sendRaw(data: (string | Buffer)[]) {
|
|
279
286
|
if (!webSocketPromise) {
|
|
280
287
|
if (canReconnect) {
|
|
281
288
|
webSocketPromise = tryToReconnect();
|
|
@@ -284,11 +291,44 @@ export async function createCallFactory(
|
|
|
284
291
|
}
|
|
285
292
|
}
|
|
286
293
|
let webSocket = await webSocketPromise;
|
|
287
|
-
webSocket.send((data.length + BASE_LENGTH_OFFSET).toString());
|
|
288
294
|
for (let d of data) {
|
|
289
295
|
webSocket.send(d);
|
|
290
296
|
}
|
|
291
297
|
}
|
|
298
|
+
async function send(data: Buffer[]) {
|
|
299
|
+
sendRaw([
|
|
300
|
+
(data.length + BASE_LENGTH_OFFSET).toString(),
|
|
301
|
+
...data,
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
async function sendWithHeader(data: Buffer[], header: MessageHeader) {
|
|
305
|
+
if (data.some(x => x.length > SocketFunction.MAX_MESSAGE_SIZE * 1.5)) {
|
|
306
|
+
if (header.type === "Buffer" || header.type === "Buffer[]") {
|
|
307
|
+
header.bufferLengths = data.map(x => x.length);
|
|
308
|
+
let fitBuffers: Buffer[] = [];
|
|
309
|
+
for (let buf of data) {
|
|
310
|
+
if (buf.length > SocketFunction.MAX_MESSAGE_SIZE) {
|
|
311
|
+
let offset = 0;
|
|
312
|
+
while (offset < buf.length) {
|
|
313
|
+
fitBuffers.push(buf.slice(offset, offset + SocketFunction.MAX_MESSAGE_SIZE));
|
|
314
|
+
offset += SocketFunction.MAX_MESSAGE_SIZE;
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
fitBuffers.push(buf);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
data = fitBuffers;
|
|
321
|
+
} else {
|
|
322
|
+
throw new Error(`Cannot send large amounts of data unless we are returning Buffer or Buffer[]`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// if (totalResultSize > SocketFunction.MAX_MESSAGE_SIZE * 1.5) {
|
|
326
|
+
// Split up Buffer[] if they are too large
|
|
327
|
+
sendRaw([
|
|
328
|
+
JSON.stringify(header),
|
|
329
|
+
...data,
|
|
330
|
+
]);
|
|
331
|
+
}
|
|
292
332
|
async function tryToReconnect(): Promise<SenderInterface> {
|
|
293
333
|
// Don't try to reconnect too often!
|
|
294
334
|
let timeSinceLastAttempt = Date.now() - lastConnectionAttempt;
|
|
@@ -303,7 +343,9 @@ export async function createCallFactory(
|
|
|
303
343
|
return newWebSocket;
|
|
304
344
|
}
|
|
305
345
|
|
|
306
|
-
let pendingCall:
|
|
346
|
+
let pendingCall: MessageHeader & {
|
|
347
|
+
buffers: Buffer[];
|
|
348
|
+
} | undefined;
|
|
307
349
|
let clientsideSerial = runInSerial(async <T>(val: Promise<T>) => val);
|
|
308
350
|
async function onMessage(message: ws.RawData | ws.MessageEvent | string) {
|
|
309
351
|
try {
|
|
@@ -320,21 +362,30 @@ export async function createCallFactory(
|
|
|
320
362
|
}
|
|
321
363
|
}
|
|
322
364
|
if (typeof message === "string") {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
365
|
+
if (message.startsWith("{")) {
|
|
366
|
+
let obj = JSON.parse(message);
|
|
367
|
+
pendingCall = {
|
|
368
|
+
...obj,
|
|
369
|
+
buffers: [],
|
|
370
|
+
};
|
|
371
|
+
} else {
|
|
372
|
+
let count = parseInt(message);
|
|
373
|
+
if (isNaN(count)) {
|
|
374
|
+
throw new Error(`Invalid message count ${message}`);
|
|
375
|
+
}
|
|
376
|
+
if (count < BASE_LENGTH_OFFSET) {
|
|
377
|
+
throw new Error(`Invalid message count ${message}`);
|
|
378
|
+
}
|
|
379
|
+
count -= BASE_LENGTH_OFFSET;
|
|
380
|
+
if (count > 1000 * 1000) {
|
|
381
|
+
throw new Error(`Invalid message count ${count}`);
|
|
382
|
+
}
|
|
383
|
+
pendingCall = {
|
|
384
|
+
buffers: [],
|
|
385
|
+
bufferCount: count,
|
|
386
|
+
type: "serialized",
|
|
387
|
+
};
|
|
333
388
|
}
|
|
334
|
-
pendingCall = {
|
|
335
|
-
buffers: [],
|
|
336
|
-
targetCount: size,
|
|
337
|
-
};
|
|
338
389
|
return;
|
|
339
390
|
}
|
|
340
391
|
if (message instanceof Buffer) {
|
|
@@ -343,17 +394,63 @@ export async function createCallFactory(
|
|
|
343
394
|
}
|
|
344
395
|
pendingCall.buffers.push(message);
|
|
345
396
|
let currentBuffers: Buffer[];
|
|
346
|
-
if (pendingCall.buffers.length !== pendingCall.
|
|
397
|
+
if (pendingCall.buffers.length !== pendingCall.bufferCount) {
|
|
347
398
|
return;
|
|
348
399
|
}
|
|
349
400
|
|
|
350
|
-
|
|
401
|
+
let currentCall = pendingCall;
|
|
351
402
|
pendingCall = undefined;
|
|
352
|
-
|
|
353
|
-
let
|
|
354
|
-
|
|
403
|
+
currentBuffers = currentCall.buffers;
|
|
404
|
+
let call: InternalCallType | InternalReturnType;
|
|
405
|
+
let resultSize: number;
|
|
355
406
|
let time = Date.now();
|
|
356
|
-
|
|
407
|
+
if (currentCall.type === "Buffer" || currentCall.type === "Buffer[]") {
|
|
408
|
+
let result: Buffer | Buffer[] = currentBuffers;
|
|
409
|
+
if (currentCall.bufferLengths) {
|
|
410
|
+
let pendingBuffers = currentBuffers;
|
|
411
|
+
function takeBuffer(len: number) {
|
|
412
|
+
let lenLeft = len;
|
|
413
|
+
let buffers: Buffer[] = [];
|
|
414
|
+
while (lenLeft > 0) {
|
|
415
|
+
let buf = currentBuffers.pop();
|
|
416
|
+
if (!buf) {
|
|
417
|
+
throw new Error(`Not enough buffers received.`);
|
|
418
|
+
}
|
|
419
|
+
if (buf.length > lenLeft) {
|
|
420
|
+
buffers.push(buf.slice(0, lenLeft));
|
|
421
|
+
currentBuffers.unshift(buf.slice(lenLeft));
|
|
422
|
+
break;
|
|
423
|
+
} else {
|
|
424
|
+
buffers.push(buf);
|
|
425
|
+
lenLeft -= buf.length;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (buffers.length === 1) {
|
|
429
|
+
return buffers[0];
|
|
430
|
+
}
|
|
431
|
+
return Buffer.concat(buffers);
|
|
432
|
+
}
|
|
433
|
+
result = currentCall.bufferLengths.map(takeBuffer);
|
|
434
|
+
if (pendingBuffers.length > 0) {
|
|
435
|
+
throw new Error(`Received too many buffers.`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
resultSize = result.map(x => x.length).reduce((a, b) => a + b, 0);
|
|
439
|
+
if (currentCall.type === "Buffer") {
|
|
440
|
+
if (result.length === 1) {
|
|
441
|
+
result = result[0];
|
|
442
|
+
} else {
|
|
443
|
+
result = Buffer.concat(result);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
call = {
|
|
447
|
+
...currentCall.metadata,
|
|
448
|
+
result,
|
|
449
|
+
};
|
|
450
|
+
} else {
|
|
451
|
+
resultSize = currentBuffers.map(x => x.length).reduce((a, b) => a + b, 0);
|
|
452
|
+
call = await SocketFunction.WIRE_SERIALIZER.deserialize(currentBuffers) as InternalCallType | InternalReturnType;
|
|
453
|
+
}
|
|
357
454
|
time = Date.now() - time;
|
|
358
455
|
for (let callback of SocketFunction.trackMessageSizes.download) {
|
|
359
456
|
callback(resultSize);
|
|
@@ -365,7 +462,7 @@ export async function createCallFactory(
|
|
|
365
462
|
console.log(red(`Slow parse, took ${time}ms to parse ${resultSize} bytes, for receieving result of call to ${callbackObj?.call.classGuid}.${callbackObj?.call.functionName}`));
|
|
366
463
|
}
|
|
367
464
|
if (!callbackObj) {
|
|
368
|
-
console.log(`Got return for unknown call ${call.seqNum}`);
|
|
465
|
+
console.log(`Got return for unknown call ${call.seqNum} (created at time ${new Date(call.seqNum)})`);
|
|
369
466
|
return;
|
|
370
467
|
}
|
|
371
468
|
if (SocketFunction.logMessages) {
|
|
@@ -376,7 +473,6 @@ export async function createCallFactory(
|
|
|
376
473
|
call.result = await decompressObj(call.result as Buffer);
|
|
377
474
|
call.isResultCompressed = false;
|
|
378
475
|
}
|
|
379
|
-
call.resultSize = resultSize;
|
|
380
476
|
callbackObj.callback(call);
|
|
381
477
|
} else {
|
|
382
478
|
if (call.isArgsCompressed) {
|
|
@@ -397,7 +493,6 @@ export async function createCallFactory(
|
|
|
397
493
|
isReturn: true,
|
|
398
494
|
result,
|
|
399
495
|
seqNum: call.seqNum,
|
|
400
|
-
resultSize: resultSize,
|
|
401
496
|
};
|
|
402
497
|
if (shouldCompressCall(call)) {
|
|
403
498
|
response.result = await compressObj(response.result) as any;
|
|
@@ -409,23 +504,29 @@ export async function createCallFactory(
|
|
|
409
504
|
result: undefined,
|
|
410
505
|
seqNum: call.seqNum,
|
|
411
506
|
error: e.stack,
|
|
412
|
-
resultSize: resultSize,
|
|
413
507
|
};
|
|
414
508
|
}
|
|
415
509
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
510
|
+
if (response.result instanceof Buffer) {
|
|
511
|
+
let { result, ...remaining } = response;
|
|
512
|
+
await sendWithHeader([result], { type: "Buffer", bufferCount: 1, metadata: remaining });
|
|
513
|
+
} else if (Array.isArray(response.result) && response.result.every(x => x instanceof Buffer)) {
|
|
514
|
+
let { result, ...remaining } = response;
|
|
515
|
+
await sendWithHeader(result, { type: "Buffer[]", bufferCount: result.length, metadata: remaining });
|
|
516
|
+
} else {
|
|
517
|
+
let result: Buffer[] = await SocketFunction.WIRE_SERIALIZER.serialize(response);
|
|
518
|
+
let totalResultSize = result.map(x => x.length).reduce((a, b) => a + b, 0);
|
|
519
|
+
if (totalResultSize > SocketFunction.MAX_MESSAGE_SIZE * 1.5) {
|
|
520
|
+
response = {
|
|
521
|
+
isReturn: true,
|
|
522
|
+
result: undefined,
|
|
523
|
+
seqNum: call.seqNum,
|
|
524
|
+
error: new Error(`Response too large to send (${call.classGuid}.${call.functionName}, size: ${formatNumber(totalResultSize)} > ${formatNumber(SocketFunction.MAX_MESSAGE_SIZE)}). If you need to handle very large static data use some external service, such as Backblaze B2 or AWS S3. Or consider fragmenting data at an application level, because sending large data will cause large lag spikes for other clients using this server. Or, if absolutely required, set SocketFunction.MAX_MESSAGE_SIZE to a higher value.`).stack,
|
|
525
|
+
};
|
|
526
|
+
result = await SocketFunction.WIRE_SERIALIZER.serialize(response);
|
|
527
|
+
}
|
|
528
|
+
await send(result);
|
|
427
529
|
}
|
|
428
|
-
await send(result);
|
|
429
530
|
}
|
|
430
531
|
return;
|
|
431
532
|
}
|