socket-function 0.10.8 → 0.11.1
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 +2 -2
- package/src/CallFactory.ts +141 -133
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "socket-function",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
|
|
@@ -28,6 +28,6 @@
|
|
|
28
28
|
"@types/node-forge": "^1.3.1",
|
|
29
29
|
"debugbreak": "^0.6.5",
|
|
30
30
|
"pegjs": "^0.10.0",
|
|
31
|
-
"typedev": "^0.1.
|
|
31
|
+
"typedev": "^0.1.3"
|
|
32
32
|
}
|
|
33
33
|
}
|
package/src/CallFactory.ts
CHANGED
|
@@ -296,7 +296,7 @@ export async function createCallFactory(
|
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
async function send(data: Buffer[]) {
|
|
299
|
-
sendRaw([
|
|
299
|
+
await sendRaw([
|
|
300
300
|
(data.length + BASE_LENGTH_OFFSET).toString(),
|
|
301
301
|
...data,
|
|
302
302
|
]);
|
|
@@ -324,7 +324,7 @@ export async function createCallFactory(
|
|
|
324
324
|
}
|
|
325
325
|
// if (totalResultSize > SocketFunction.MAX_MESSAGE_SIZE * 1.5) {
|
|
326
326
|
// Split up Buffer[] if they are too large
|
|
327
|
-
sendRaw([
|
|
327
|
+
await sendRaw([
|
|
328
328
|
JSON.stringify(header),
|
|
329
329
|
...data,
|
|
330
330
|
]);
|
|
@@ -346,6 +346,141 @@ export async function createCallFactory(
|
|
|
346
346
|
let pendingCall: MessageHeader & {
|
|
347
347
|
buffers: Buffer[];
|
|
348
348
|
} | undefined;
|
|
349
|
+
|
|
350
|
+
async function processPendingCall() {
|
|
351
|
+
if (!pendingCall) throw new Error(`No pending call`);
|
|
352
|
+
let currentCall = pendingCall;
|
|
353
|
+
pendingCall = undefined;
|
|
354
|
+
let currentBuffers = currentCall.buffers;
|
|
355
|
+
let call: InternalCallType | InternalReturnType;
|
|
356
|
+
let resultSize: number;
|
|
357
|
+
let time = Date.now();
|
|
358
|
+
if (currentCall.type === "Buffer" || currentCall.type === "Buffer[]") {
|
|
359
|
+
let result: Buffer | Buffer[] = currentBuffers;
|
|
360
|
+
if (currentCall.bufferLengths) {
|
|
361
|
+
let pendingBuffers = currentBuffers;
|
|
362
|
+
function takeBuffer(len: number) {
|
|
363
|
+
let lenLeft = len;
|
|
364
|
+
let buffers: Buffer[] = [];
|
|
365
|
+
while (lenLeft > 0) {
|
|
366
|
+
let buf = currentBuffers.pop();
|
|
367
|
+
if (!buf) {
|
|
368
|
+
throw new Error(`Not enough buffers received.`);
|
|
369
|
+
}
|
|
370
|
+
if (buf.length > lenLeft) {
|
|
371
|
+
buffers.push(buf.slice(0, lenLeft));
|
|
372
|
+
currentBuffers.unshift(buf.slice(lenLeft));
|
|
373
|
+
break;
|
|
374
|
+
} else {
|
|
375
|
+
buffers.push(buf);
|
|
376
|
+
lenLeft -= buf.length;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (buffers.length === 1) {
|
|
380
|
+
return buffers[0];
|
|
381
|
+
}
|
|
382
|
+
return Buffer.concat(buffers);
|
|
383
|
+
}
|
|
384
|
+
result = currentCall.bufferLengths.map(takeBuffer);
|
|
385
|
+
if (pendingBuffers.length > 0) {
|
|
386
|
+
throw new Error(`Received too many buffers.`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
resultSize = result.map(x => x.length).reduce((a, b) => a + b, 0);
|
|
390
|
+
if (currentCall.type === "Buffer") {
|
|
391
|
+
if (result.length === 1) {
|
|
392
|
+
result = result[0];
|
|
393
|
+
} else {
|
|
394
|
+
result = Buffer.concat(result);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
call = {
|
|
398
|
+
...currentCall.metadata,
|
|
399
|
+
result,
|
|
400
|
+
};
|
|
401
|
+
} else {
|
|
402
|
+
resultSize = currentBuffers.map(x => x.length).reduce((a, b) => a + b, 0);
|
|
403
|
+
call = await SocketFunction.WIRE_SERIALIZER.deserialize(currentBuffers) as InternalCallType | InternalReturnType;
|
|
404
|
+
}
|
|
405
|
+
time = Date.now() - time;
|
|
406
|
+
for (let callback of SocketFunction.trackMessageSizes.download) {
|
|
407
|
+
callback(resultSize);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (call.isReturn) {
|
|
411
|
+
let callbackObj = pendingCalls.get(call.seqNum);
|
|
412
|
+
if (time > SocketFunction.WIRE_WARN_TIME) {
|
|
413
|
+
console.log(red(`Slow parse, took ${time}ms to parse ${resultSize} bytes, for receieving result of call to ${callbackObj?.call.classGuid}.${callbackObj?.call.functionName}`));
|
|
414
|
+
}
|
|
415
|
+
if (!callbackObj) {
|
|
416
|
+
console.log(`Got return for unknown call ${call.seqNum} (created at time ${new Date(call.seqNum)})`);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
if (SocketFunction.logMessages) {
|
|
420
|
+
let call = callbackObj.call;
|
|
421
|
+
console.log(`SIZE\t${(formatNumberSuffixed(resultSize) + "B").padEnd(4, " ")}\t${call.classGuid}.${call.functionName} at ${Date.now()}`);
|
|
422
|
+
}
|
|
423
|
+
if (call.isResultCompressed) {
|
|
424
|
+
call.result = await decompressObj(call.result as Buffer);
|
|
425
|
+
call.isResultCompressed = false;
|
|
426
|
+
}
|
|
427
|
+
callbackObj.callback(call);
|
|
428
|
+
} else {
|
|
429
|
+
if (call.isArgsCompressed) {
|
|
430
|
+
call.args = await decompressObj(call.args as any as Buffer) as any;
|
|
431
|
+
call.isArgsCompressed = false;
|
|
432
|
+
}
|
|
433
|
+
if (SocketFunction.logMessages) {
|
|
434
|
+
console.log(`SIZE\t${(formatNumberSuffixed(resultSize) + "B").padEnd(4, " ")}\t${call.classGuid}.${call.functionName} at ${Date.now()}`);
|
|
435
|
+
}
|
|
436
|
+
if (time > SocketFunction.WIRE_WARN_TIME) {
|
|
437
|
+
console.log(red(`Slow parse, took ${time}ms to parse ${resultSize} bytes, for call to ${call.classGuid}.${call.functionName}`));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
let response: InternalReturnType;
|
|
441
|
+
try {
|
|
442
|
+
let result = await performLocalCall({ call, caller: callerContext });
|
|
443
|
+
response = {
|
|
444
|
+
isReturn: true,
|
|
445
|
+
result,
|
|
446
|
+
seqNum: call.seqNum,
|
|
447
|
+
};
|
|
448
|
+
if (shouldCompressCall(call)) {
|
|
449
|
+
response.result = await compressObj(response.result) as any;
|
|
450
|
+
response.isResultCompressed = true;
|
|
451
|
+
}
|
|
452
|
+
} catch (e: any) {
|
|
453
|
+
response = {
|
|
454
|
+
isReturn: true,
|
|
455
|
+
result: undefined,
|
|
456
|
+
seqNum: call.seqNum,
|
|
457
|
+
error: e.stack,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (response.result instanceof Buffer) {
|
|
462
|
+
let { result, ...remaining } = response;
|
|
463
|
+
await sendWithHeader([result], { type: "Buffer", bufferCount: 1, metadata: remaining });
|
|
464
|
+
} else if (Array.isArray(response.result) && response.result.every(x => x instanceof Buffer)) {
|
|
465
|
+
let { result, ...remaining } = response;
|
|
466
|
+
await sendWithHeader(result, { type: "Buffer[]", bufferCount: result.length, metadata: remaining });
|
|
467
|
+
} else {
|
|
468
|
+
let result: Buffer[] = await SocketFunction.WIRE_SERIALIZER.serialize(response);
|
|
469
|
+
let totalResultSize = result.map(x => x.length).reduce((a, b) => a + b, 0);
|
|
470
|
+
if (totalResultSize > SocketFunction.MAX_MESSAGE_SIZE * 1.5) {
|
|
471
|
+
response = {
|
|
472
|
+
isReturn: true,
|
|
473
|
+
result: undefined,
|
|
474
|
+
seqNum: call.seqNum,
|
|
475
|
+
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,
|
|
476
|
+
};
|
|
477
|
+
result = await SocketFunction.WIRE_SERIALIZER.serialize(response);
|
|
478
|
+
}
|
|
479
|
+
await send(result);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
349
484
|
let clientsideSerial = runInSerial(async <T>(val: Promise<T>) => val);
|
|
350
485
|
async function onMessage(message: ws.RawData | ws.MessageEvent | string) {
|
|
351
486
|
try {
|
|
@@ -386,6 +521,9 @@ export async function createCallFactory(
|
|
|
386
521
|
type: "serialized",
|
|
387
522
|
};
|
|
388
523
|
}
|
|
524
|
+
if (pendingCall?.bufferCount === 0) {
|
|
525
|
+
await processPendingCall();
|
|
526
|
+
}
|
|
389
527
|
return;
|
|
390
528
|
}
|
|
391
529
|
if (message instanceof Buffer) {
|
|
@@ -393,141 +531,11 @@ export async function createCallFactory(
|
|
|
393
531
|
throw new Error(`Received data without size`);
|
|
394
532
|
}
|
|
395
533
|
pendingCall.buffers.push(message);
|
|
396
|
-
let currentBuffers: Buffer[];
|
|
397
534
|
if (pendingCall.buffers.length !== pendingCall.bufferCount) {
|
|
398
535
|
return;
|
|
399
536
|
}
|
|
400
537
|
|
|
401
|
-
|
|
402
|
-
pendingCall = undefined;
|
|
403
|
-
currentBuffers = currentCall.buffers;
|
|
404
|
-
let call: InternalCallType | InternalReturnType;
|
|
405
|
-
let resultSize: number;
|
|
406
|
-
let time = Date.now();
|
|
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
|
-
}
|
|
454
|
-
time = Date.now() - time;
|
|
455
|
-
for (let callback of SocketFunction.trackMessageSizes.download) {
|
|
456
|
-
callback(resultSize);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
if (call.isReturn) {
|
|
460
|
-
let callbackObj = pendingCalls.get(call.seqNum);
|
|
461
|
-
if (time > SocketFunction.WIRE_WARN_TIME) {
|
|
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}`));
|
|
463
|
-
}
|
|
464
|
-
if (!callbackObj) {
|
|
465
|
-
console.log(`Got return for unknown call ${call.seqNum} (created at time ${new Date(call.seqNum)})`);
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
if (SocketFunction.logMessages) {
|
|
469
|
-
let call = callbackObj.call;
|
|
470
|
-
console.log(`SIZE\t${(formatNumberSuffixed(resultSize) + "B").padEnd(4, " ")}\t${call.classGuid}.${call.functionName} at ${Date.now()}`);
|
|
471
|
-
}
|
|
472
|
-
if (call.isResultCompressed) {
|
|
473
|
-
call.result = await decompressObj(call.result as Buffer);
|
|
474
|
-
call.isResultCompressed = false;
|
|
475
|
-
}
|
|
476
|
-
callbackObj.callback(call);
|
|
477
|
-
} else {
|
|
478
|
-
if (call.isArgsCompressed) {
|
|
479
|
-
call.args = await decompressObj(call.args as any as Buffer) as any;
|
|
480
|
-
call.isArgsCompressed = false;
|
|
481
|
-
}
|
|
482
|
-
if (SocketFunction.logMessages) {
|
|
483
|
-
console.log(`SIZE\t${(formatNumberSuffixed(resultSize) + "B").padEnd(4, " ")}\t${call.classGuid}.${call.functionName} at ${Date.now()}`);
|
|
484
|
-
}
|
|
485
|
-
if (time > SocketFunction.WIRE_WARN_TIME) {
|
|
486
|
-
console.log(red(`Slow parse, took ${time}ms to parse ${resultSize} bytes, for call to ${call.classGuid}.${call.functionName}`));
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
let response: InternalReturnType;
|
|
490
|
-
try {
|
|
491
|
-
let result = await performLocalCall({ call, caller: callerContext });
|
|
492
|
-
response = {
|
|
493
|
-
isReturn: true,
|
|
494
|
-
result,
|
|
495
|
-
seqNum: call.seqNum,
|
|
496
|
-
};
|
|
497
|
-
if (shouldCompressCall(call)) {
|
|
498
|
-
response.result = await compressObj(response.result) as any;
|
|
499
|
-
response.isResultCompressed = true;
|
|
500
|
-
}
|
|
501
|
-
} catch (e: any) {
|
|
502
|
-
response = {
|
|
503
|
-
isReturn: true,
|
|
504
|
-
result: undefined,
|
|
505
|
-
seqNum: call.seqNum,
|
|
506
|
-
error: e.stack,
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
|
|
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);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
538
|
+
await processPendingCall();
|
|
531
539
|
return;
|
|
532
540
|
}
|
|
533
541
|
throw new Error(`Unhandled data type ${typeof message}`);
|