@aegis-fluxion/core 0.4.0 → 0.7.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/README.md +230 -101
- package/dist/index.cjs +303 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +303 -58
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import WebSocket, { WebSocketServer } from 'ws';
|
|
|
4
4
|
// src/index.ts
|
|
5
5
|
var DEFAULT_CLOSE_CODE = 1e3;
|
|
6
6
|
var DEFAULT_CLOSE_REASON = "";
|
|
7
|
+
var POLICY_VIOLATION_CLOSE_CODE = 1008;
|
|
8
|
+
var POLICY_VIOLATION_CLOSE_REASON = "Connection rejected by middleware.";
|
|
7
9
|
var INTERNAL_HANDSHAKE_EVENT = "__handshake";
|
|
8
10
|
var INTERNAL_RPC_REQUEST_EVENT = "__rpc:req";
|
|
9
11
|
var INTERNAL_RPC_RESPONSE_EVENT = "__rpc:res";
|
|
@@ -15,6 +17,8 @@ var GCM_AUTH_TAG_LENGTH = 16;
|
|
|
15
17
|
var ENCRYPTION_KEY_LENGTH = 32;
|
|
16
18
|
var ENCRYPTED_PACKET_VERSION = 1;
|
|
17
19
|
var ENCRYPTED_PACKET_PREFIX_LENGTH = 1 + GCM_IV_LENGTH + GCM_AUTH_TAG_LENGTH;
|
|
20
|
+
var BINARY_PAYLOAD_MARKER = "__afxBinaryPayload";
|
|
21
|
+
var BINARY_PAYLOAD_VERSION = 1;
|
|
18
22
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
19
23
|
var DEFAULT_HEARTBEAT_TIMEOUT_MS = 15e3;
|
|
20
24
|
var DEFAULT_RECONNECT_INITIAL_DELAY_MS = 250;
|
|
@@ -55,7 +59,103 @@ function rawDataToBuffer(rawData) {
|
|
|
55
59
|
}
|
|
56
60
|
return Buffer.from(rawData);
|
|
57
61
|
}
|
|
58
|
-
function
|
|
62
|
+
function isBlobValue(value) {
|
|
63
|
+
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
64
|
+
}
|
|
65
|
+
function isPlainObject(value) {
|
|
66
|
+
if (typeof value !== "object" || value === null) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const prototype = Object.getPrototypeOf(value);
|
|
70
|
+
return prototype === Object.prototype || prototype === null;
|
|
71
|
+
}
|
|
72
|
+
function encodeBinaryPayload(kind, payloadBuffer, mimeType) {
|
|
73
|
+
const encodedPayload = {
|
|
74
|
+
[BINARY_PAYLOAD_MARKER]: BINARY_PAYLOAD_VERSION,
|
|
75
|
+
kind,
|
|
76
|
+
base64: payloadBuffer.toString("base64")
|
|
77
|
+
};
|
|
78
|
+
if (mimeType !== void 0 && mimeType.length > 0) {
|
|
79
|
+
encodedPayload.mimeType = mimeType;
|
|
80
|
+
}
|
|
81
|
+
return encodedPayload;
|
|
82
|
+
}
|
|
83
|
+
async function encodeEnvelopeData(value) {
|
|
84
|
+
if (Buffer.isBuffer(value)) {
|
|
85
|
+
return encodeBinaryPayload("buffer", value);
|
|
86
|
+
}
|
|
87
|
+
if (value instanceof Uint8Array) {
|
|
88
|
+
const typedArrayBuffer = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
89
|
+
return encodeBinaryPayload("uint8array", typedArrayBuffer);
|
|
90
|
+
}
|
|
91
|
+
if (isBlobValue(value)) {
|
|
92
|
+
const blobBuffer = Buffer.from(await value.arrayBuffer());
|
|
93
|
+
return encodeBinaryPayload("blob", blobBuffer, value.type);
|
|
94
|
+
}
|
|
95
|
+
if (Array.isArray(value)) {
|
|
96
|
+
return Promise.all(value.map((item) => encodeEnvelopeData(item)));
|
|
97
|
+
}
|
|
98
|
+
if (isPlainObject(value)) {
|
|
99
|
+
const encodedEntries = await Promise.all(
|
|
100
|
+
Object.entries(value).map(async ([key, entryValue]) => {
|
|
101
|
+
return [key, await encodeEnvelopeData(entryValue)];
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
return Object.fromEntries(encodedEntries);
|
|
105
|
+
}
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
function isEncodedBinaryPayload(value) {
|
|
109
|
+
if (!isPlainObject(value)) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
if (value[BINARY_PAYLOAD_MARKER] !== BINARY_PAYLOAD_VERSION) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
if (value.kind !== "buffer" && value.kind !== "uint8array" && value.kind !== "blob") {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (typeof value.base64 !== "string") {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (value.mimeType !== void 0 && typeof value.mimeType !== "string") {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
function decodeEnvelopeData(value) {
|
|
127
|
+
if (Array.isArray(value)) {
|
|
128
|
+
return value.map((item) => decodeEnvelopeData(item));
|
|
129
|
+
}
|
|
130
|
+
if (isEncodedBinaryPayload(value)) {
|
|
131
|
+
const binaryBuffer = Buffer.from(value.base64, "base64");
|
|
132
|
+
if (value.kind === "buffer") {
|
|
133
|
+
return binaryBuffer;
|
|
134
|
+
}
|
|
135
|
+
if (value.kind === "uint8array") {
|
|
136
|
+
return Uint8Array.from(binaryBuffer);
|
|
137
|
+
}
|
|
138
|
+
if (typeof Blob === "undefined") {
|
|
139
|
+
return binaryBuffer;
|
|
140
|
+
}
|
|
141
|
+
return new Blob([binaryBuffer], {
|
|
142
|
+
type: value.mimeType ?? ""
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (isPlainObject(value)) {
|
|
146
|
+
const decodedEntries = Object.entries(value).map(([key, entryValue]) => {
|
|
147
|
+
return [key, decodeEnvelopeData(entryValue)];
|
|
148
|
+
});
|
|
149
|
+
return Object.fromEntries(decodedEntries);
|
|
150
|
+
}
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
async function serializeEnvelope(event, data) {
|
|
154
|
+
const encodedData = await encodeEnvelopeData(data);
|
|
155
|
+
const envelope = { event, data: encodedData };
|
|
156
|
+
return JSON.stringify(envelope);
|
|
157
|
+
}
|
|
158
|
+
function serializePlainEnvelope(event, data) {
|
|
59
159
|
const envelope = { event, data };
|
|
60
160
|
return JSON.stringify(envelope);
|
|
61
161
|
}
|
|
@@ -67,7 +167,7 @@ function parseEnvelope(rawData) {
|
|
|
67
167
|
}
|
|
68
168
|
return {
|
|
69
169
|
event: parsed.event,
|
|
70
|
-
data: parsed.data
|
|
170
|
+
data: decodeEnvelopeData(parsed.data)
|
|
71
171
|
};
|
|
72
172
|
}
|
|
73
173
|
function parseEnvelopeFromText(decodedPayload) {
|
|
@@ -77,7 +177,7 @@ function parseEnvelopeFromText(decodedPayload) {
|
|
|
77
177
|
}
|
|
78
178
|
return {
|
|
79
179
|
event: parsed.event,
|
|
80
|
-
data: parsed.data
|
|
180
|
+
data: decodeEnvelopeData(parsed.data)
|
|
81
181
|
};
|
|
82
182
|
}
|
|
83
183
|
function decodeCloseReason(reason) {
|
|
@@ -259,7 +359,9 @@ var SecureServer = class {
|
|
|
259
359
|
disconnectHandlers = /* @__PURE__ */ new Set();
|
|
260
360
|
readyHandlers = /* @__PURE__ */ new Set();
|
|
261
361
|
errorHandlers = /* @__PURE__ */ new Set();
|
|
362
|
+
middlewareHandlers = [];
|
|
262
363
|
handshakeStateBySocket = /* @__PURE__ */ new WeakMap();
|
|
364
|
+
middlewareMetadataBySocket = /* @__PURE__ */ new WeakMap();
|
|
263
365
|
sharedSecretBySocket = /* @__PURE__ */ new WeakMap();
|
|
264
366
|
encryptionKeyBySocket = /* @__PURE__ */ new WeakMap();
|
|
265
367
|
pendingPayloadsBySocket = /* @__PURE__ */ new WeakMap();
|
|
@@ -348,6 +450,19 @@ var SecureServer = class {
|
|
|
348
450
|
}
|
|
349
451
|
return this;
|
|
350
452
|
}
|
|
453
|
+
use(middleware) {
|
|
454
|
+
try {
|
|
455
|
+
if (typeof middleware !== "function") {
|
|
456
|
+
throw new Error("Server middleware must be a function.");
|
|
457
|
+
}
|
|
458
|
+
this.middlewareHandlers.push(middleware);
|
|
459
|
+
} catch (error) {
|
|
460
|
+
this.notifyError(
|
|
461
|
+
normalizeToError(error, "Failed to register server middleware.")
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
return this;
|
|
465
|
+
}
|
|
351
466
|
emit(event, data) {
|
|
352
467
|
try {
|
|
353
468
|
if (isReservedEmitEvent(event)) {
|
|
@@ -355,7 +470,9 @@ var SecureServer = class {
|
|
|
355
470
|
}
|
|
356
471
|
const envelope = { event, data };
|
|
357
472
|
for (const client of this.clientsById.values()) {
|
|
358
|
-
this.sendOrQueuePayload(client.socket, envelope)
|
|
473
|
+
void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
|
|
474
|
+
return void 0;
|
|
475
|
+
});
|
|
359
476
|
}
|
|
360
477
|
} catch (error) {
|
|
361
478
|
this.notifyError(normalizeToError(error, "Failed to emit server event."));
|
|
@@ -373,7 +490,9 @@ var SecureServer = class {
|
|
|
373
490
|
throw new Error(`Client with id ${clientId} was not found.`);
|
|
374
491
|
}
|
|
375
492
|
if (!ackArgs.expectsAck) {
|
|
376
|
-
this.sendOrQueuePayload(client.socket, { event, data })
|
|
493
|
+
void this.sendOrQueuePayload(client.socket, { event, data }).catch(() => {
|
|
494
|
+
return void 0;
|
|
495
|
+
});
|
|
377
496
|
return true;
|
|
378
497
|
}
|
|
379
498
|
const ackPromise = this.sendRpcRequest(
|
|
@@ -429,6 +548,7 @@ var SecureServer = class {
|
|
|
429
548
|
client.socket,
|
|
430
549
|
new Error("Server closed before ACK response was received.")
|
|
431
550
|
);
|
|
551
|
+
this.middlewareMetadataBySocket.delete(client.socket);
|
|
432
552
|
if (client.socket.readyState === WebSocket.OPEN || client.socket.readyState === WebSocket.CONNECTING) {
|
|
433
553
|
client.socket.close(code, reason);
|
|
434
554
|
}
|
|
@@ -491,6 +611,7 @@ var SecureServer = class {
|
|
|
491
611
|
this.pendingRpcRequestsBySocket.delete(socket);
|
|
492
612
|
this.handshakeStateBySocket.delete(socket);
|
|
493
613
|
this.heartbeatStateBySocket.delete(socket);
|
|
614
|
+
this.middlewareMetadataBySocket.delete(socket);
|
|
494
615
|
socket.terminate();
|
|
495
616
|
continue;
|
|
496
617
|
}
|
|
@@ -520,17 +641,46 @@ var SecureServer = class {
|
|
|
520
641
|
}
|
|
521
642
|
bindSocketServerEvents() {
|
|
522
643
|
this.socketServer.on("connection", (socket, request) => {
|
|
523
|
-
this.handleConnection(socket, request);
|
|
644
|
+
void this.handleConnection(socket, request);
|
|
524
645
|
});
|
|
525
646
|
this.socketServer.on("error", (error) => {
|
|
526
647
|
this.notifyError(normalizeToError(error, "WebSocket server encountered an error."));
|
|
527
648
|
});
|
|
528
649
|
}
|
|
529
|
-
handleConnection(socket, request) {
|
|
650
|
+
async handleConnection(socket, request) {
|
|
651
|
+
const connectionMetadata = /* @__PURE__ */ new Map();
|
|
652
|
+
this.middlewareMetadataBySocket.set(socket, connectionMetadata);
|
|
653
|
+
try {
|
|
654
|
+
await this.executeServerMiddleware({
|
|
655
|
+
phase: "connection",
|
|
656
|
+
socket,
|
|
657
|
+
request,
|
|
658
|
+
metadata: connectionMetadata
|
|
659
|
+
});
|
|
660
|
+
} catch (error) {
|
|
661
|
+
const normalizedError = normalizeToError(
|
|
662
|
+
error,
|
|
663
|
+
"Connection middleware rejected the incoming socket."
|
|
664
|
+
);
|
|
665
|
+
this.notifyError(normalizedError);
|
|
666
|
+
this.middlewareMetadataBySocket.delete(socket);
|
|
667
|
+
if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
|
|
668
|
+
socket.close(
|
|
669
|
+
POLICY_VIOLATION_CLOSE_CODE,
|
|
670
|
+
normalizedError.message || POLICY_VIOLATION_CLOSE_REASON
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
530
675
|
try {
|
|
531
676
|
const clientId = randomUUID();
|
|
532
677
|
const handshakeState = this.createServerHandshakeState();
|
|
533
|
-
const client = this.createSecureServerClient(
|
|
678
|
+
const client = this.createSecureServerClient(
|
|
679
|
+
clientId,
|
|
680
|
+
socket,
|
|
681
|
+
request,
|
|
682
|
+
connectionMetadata
|
|
683
|
+
);
|
|
534
684
|
this.clientsById.set(clientId, client);
|
|
535
685
|
this.clientIdBySocket.set(socket, clientId);
|
|
536
686
|
this.handshakeStateBySocket.set(socket, handshakeState);
|
|
@@ -542,7 +692,7 @@ var SecureServer = class {
|
|
|
542
692
|
});
|
|
543
693
|
this.roomNamesByClientId.set(clientId, /* @__PURE__ */ new Set());
|
|
544
694
|
socket.on("message", (rawData) => {
|
|
545
|
-
this.handleIncomingMessage(client, rawData);
|
|
695
|
+
void this.handleIncomingMessage(client, rawData);
|
|
546
696
|
});
|
|
547
697
|
socket.on("close", (code, reason) => {
|
|
548
698
|
this.handleDisconnection(client, code, reason);
|
|
@@ -562,9 +712,10 @@ var SecureServer = class {
|
|
|
562
712
|
this.notifyConnection(client);
|
|
563
713
|
} catch (error) {
|
|
564
714
|
this.notifyError(normalizeToError(error, "Failed to handle client connection."));
|
|
715
|
+
this.middlewareMetadataBySocket.delete(socket);
|
|
565
716
|
}
|
|
566
717
|
}
|
|
567
|
-
handleIncomingMessage(client, rawData) {
|
|
718
|
+
async handleIncomingMessage(client, rawData) {
|
|
568
719
|
try {
|
|
569
720
|
let envelope = null;
|
|
570
721
|
try {
|
|
@@ -608,10 +759,16 @@ var SecureServer = class {
|
|
|
608
759
|
return;
|
|
609
760
|
}
|
|
610
761
|
if (decryptedEnvelope.event === INTERNAL_RPC_REQUEST_EVENT) {
|
|
611
|
-
|
|
762
|
+
await this.handleRpcRequest(client, decryptedEnvelope.data);
|
|
612
763
|
return;
|
|
613
764
|
}
|
|
614
|
-
this.
|
|
765
|
+
const interceptedData = await this.applyMessageMiddleware(
|
|
766
|
+
"incoming",
|
|
767
|
+
client,
|
|
768
|
+
decryptedEnvelope.event,
|
|
769
|
+
decryptedEnvelope.data
|
|
770
|
+
);
|
|
771
|
+
this.dispatchCustomEvent(decryptedEnvelope.event, interceptedData, client);
|
|
615
772
|
} catch (error) {
|
|
616
773
|
this.notifyError(normalizeToError(error, "Failed to process incoming server message."));
|
|
617
774
|
}
|
|
@@ -631,6 +788,7 @@ var SecureServer = class {
|
|
|
631
788
|
);
|
|
632
789
|
this.pendingRpcRequestsBySocket.delete(client.socket);
|
|
633
790
|
this.heartbeatStateBySocket.delete(client.socket);
|
|
791
|
+
this.middlewareMetadataBySocket.delete(client.socket);
|
|
634
792
|
const decodedReason = decodeCloseReason(reason);
|
|
635
793
|
for (const handler of this.disconnectHandlers) {
|
|
636
794
|
try {
|
|
@@ -676,6 +834,48 @@ var SecureServer = class {
|
|
|
676
834
|
}
|
|
677
835
|
}
|
|
678
836
|
}
|
|
837
|
+
async executeServerMiddleware(context) {
|
|
838
|
+
if (this.middlewareHandlers.length === 0) {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
let currentIndex = -1;
|
|
842
|
+
const dispatch = async (index) => {
|
|
843
|
+
if (index <= currentIndex) {
|
|
844
|
+
throw new Error("Server middleware next() was called multiple times.");
|
|
845
|
+
}
|
|
846
|
+
currentIndex = index;
|
|
847
|
+
const middleware = this.middlewareHandlers[index];
|
|
848
|
+
if (!middleware) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
await Promise.resolve(
|
|
852
|
+
middleware(context, async () => {
|
|
853
|
+
await dispatch(index + 1);
|
|
854
|
+
})
|
|
855
|
+
);
|
|
856
|
+
};
|
|
857
|
+
await dispatch(0);
|
|
858
|
+
}
|
|
859
|
+
async applyMessageMiddleware(phase, client, event, data) {
|
|
860
|
+
const metadata = this.middlewareMetadataBySocket.get(client.socket) ?? /* @__PURE__ */ new Map();
|
|
861
|
+
this.middlewareMetadataBySocket.set(client.socket, metadata);
|
|
862
|
+
const middlewareContext = {
|
|
863
|
+
phase,
|
|
864
|
+
client,
|
|
865
|
+
event,
|
|
866
|
+
data,
|
|
867
|
+
metadata
|
|
868
|
+
};
|
|
869
|
+
await this.executeServerMiddleware(middlewareContext);
|
|
870
|
+
return middlewareContext.data;
|
|
871
|
+
}
|
|
872
|
+
resolveClientBySocket(socket) {
|
|
873
|
+
const clientId = this.clientIdBySocket.get(socket);
|
|
874
|
+
if (!clientId) {
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
return this.clientsById.get(clientId) ?? null;
|
|
878
|
+
}
|
|
679
879
|
sendRaw(socket, payload) {
|
|
680
880
|
try {
|
|
681
881
|
if (socket.readyState !== WebSocket.OPEN) {
|
|
@@ -686,22 +886,24 @@ var SecureServer = class {
|
|
|
686
886
|
this.notifyError(normalizeToError(error, "Failed to send server payload."));
|
|
687
887
|
}
|
|
688
888
|
}
|
|
689
|
-
sendEncryptedEnvelope(socket, envelope) {
|
|
889
|
+
async sendEncryptedEnvelope(socket, envelope) {
|
|
890
|
+
if (socket.readyState !== WebSocket.OPEN) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const encryptionKey = this.encryptionKeyBySocket.get(socket);
|
|
894
|
+
if (!encryptionKey) {
|
|
895
|
+
const missingKeyError = new Error("Missing encryption key for connected socket.");
|
|
896
|
+
this.notifyError(missingKeyError);
|
|
897
|
+
throw missingKeyError;
|
|
898
|
+
}
|
|
690
899
|
try {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
}
|
|
694
|
-
const encryptionKey = this.encryptionKeyBySocket.get(socket);
|
|
695
|
-
if (!encryptionKey) {
|
|
696
|
-
throw new Error("Missing encryption key for connected socket.");
|
|
697
|
-
}
|
|
698
|
-
const encryptedPayload = encryptSerializedEnvelope(
|
|
699
|
-
serializeEnvelope(envelope.event, envelope.data),
|
|
700
|
-
encryptionKey
|
|
701
|
-
);
|
|
900
|
+
const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
|
|
901
|
+
const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
|
|
702
902
|
socket.send(encryptedPayload);
|
|
703
903
|
} catch (error) {
|
|
704
|
-
|
|
904
|
+
const normalizedError = normalizeToError(error, "Failed to send encrypted server payload.");
|
|
905
|
+
this.notifyError(normalizedError);
|
|
906
|
+
throw normalizedError;
|
|
705
907
|
}
|
|
706
908
|
}
|
|
707
909
|
sendRpcRequest(socket, event, data, timeoutMs) {
|
|
@@ -722,13 +924,19 @@ var SecureServer = class {
|
|
|
722
924
|
reject,
|
|
723
925
|
timeoutHandle
|
|
724
926
|
});
|
|
725
|
-
this.sendOrQueuePayload(socket, {
|
|
927
|
+
void this.sendOrQueuePayload(socket, {
|
|
726
928
|
event: INTERNAL_RPC_REQUEST_EVENT,
|
|
727
929
|
data: {
|
|
728
930
|
id: requestId,
|
|
729
931
|
event,
|
|
730
932
|
data
|
|
731
933
|
}
|
|
934
|
+
}).catch((error) => {
|
|
935
|
+
clearTimeout(timeoutHandle);
|
|
936
|
+
pendingRequests.delete(requestId);
|
|
937
|
+
reject(
|
|
938
|
+
normalizeToError(error, `Failed to dispatch ACK request for event "${event}".`)
|
|
939
|
+
);
|
|
732
940
|
});
|
|
733
941
|
});
|
|
734
942
|
}
|
|
@@ -765,12 +973,18 @@ var SecureServer = class {
|
|
|
765
973
|
return;
|
|
766
974
|
}
|
|
767
975
|
try {
|
|
976
|
+
const interceptedData = await this.applyMessageMiddleware(
|
|
977
|
+
"incoming",
|
|
978
|
+
client,
|
|
979
|
+
rpcRequestPayload.event,
|
|
980
|
+
rpcRequestPayload.data
|
|
981
|
+
);
|
|
768
982
|
const ackResponse = await this.executeRpcRequestHandler(
|
|
769
983
|
rpcRequestPayload.event,
|
|
770
|
-
|
|
984
|
+
interceptedData,
|
|
771
985
|
client
|
|
772
986
|
);
|
|
773
|
-
this.sendEncryptedEnvelope(client.socket, {
|
|
987
|
+
await this.sendEncryptedEnvelope(client.socket, {
|
|
774
988
|
event: INTERNAL_RPC_RESPONSE_EVENT,
|
|
775
989
|
data: {
|
|
776
990
|
id: rpcRequestPayload.id,
|
|
@@ -780,7 +994,7 @@ var SecureServer = class {
|
|
|
780
994
|
});
|
|
781
995
|
} catch (error) {
|
|
782
996
|
const normalizedError = normalizeToError(error, "Server ACK request handler failed.");
|
|
783
|
-
this.sendEncryptedEnvelope(client.socket, {
|
|
997
|
+
await this.sendEncryptedEnvelope(client.socket, {
|
|
784
998
|
event: INTERNAL_RPC_RESPONSE_EVENT,
|
|
785
999
|
data: {
|
|
786
1000
|
id: rpcRequestPayload.id,
|
|
@@ -854,7 +1068,7 @@ var SecureServer = class {
|
|
|
854
1068
|
sendInternalHandshake(socket, localPublicKey) {
|
|
855
1069
|
this.sendRaw(
|
|
856
1070
|
socket,
|
|
857
|
-
|
|
1071
|
+
serializePlainEnvelope(INTERNAL_HANDSHAKE_EVENT, {
|
|
858
1072
|
publicKey: localPublicKey
|
|
859
1073
|
})
|
|
860
1074
|
);
|
|
@@ -884,33 +1098,50 @@ var SecureServer = class {
|
|
|
884
1098
|
isClientHandshakeReady(socket) {
|
|
885
1099
|
return this.handshakeStateBySocket.get(socket)?.isReady ?? false;
|
|
886
1100
|
}
|
|
887
|
-
sendOrQueuePayload(socket, envelope) {
|
|
1101
|
+
async sendOrQueuePayload(socket, envelope) {
|
|
1102
|
+
let interceptedEnvelope = envelope;
|
|
1103
|
+
if (!isReservedEmitEvent(envelope.event)) {
|
|
1104
|
+
const targetClient = this.resolveClientBySocket(socket);
|
|
1105
|
+
if (targetClient) {
|
|
1106
|
+
const interceptedData = await this.applyMessageMiddleware(
|
|
1107
|
+
"outgoing",
|
|
1108
|
+
targetClient,
|
|
1109
|
+
envelope.event,
|
|
1110
|
+
envelope.data
|
|
1111
|
+
);
|
|
1112
|
+
interceptedEnvelope = {
|
|
1113
|
+
event: envelope.event,
|
|
1114
|
+
data: interceptedData
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
888
1118
|
if (!this.isClientHandshakeReady(socket)) {
|
|
889
|
-
this.queuePayload(socket,
|
|
1119
|
+
this.queuePayload(socket, interceptedEnvelope);
|
|
890
1120
|
return;
|
|
891
1121
|
}
|
|
892
|
-
this.sendEncryptedEnvelope(socket,
|
|
1122
|
+
await this.sendEncryptedEnvelope(socket, interceptedEnvelope);
|
|
893
1123
|
}
|
|
894
1124
|
queuePayload(socket, envelope) {
|
|
895
1125
|
const pendingPayloads = this.pendingPayloadsBySocket.get(socket) ?? [];
|
|
896
1126
|
pendingPayloads.push(envelope);
|
|
897
1127
|
this.pendingPayloadsBySocket.set(socket, pendingPayloads);
|
|
898
1128
|
}
|
|
899
|
-
flushQueuedPayloads(socket) {
|
|
1129
|
+
async flushQueuedPayloads(socket) {
|
|
900
1130
|
const pendingPayloads = this.pendingPayloadsBySocket.get(socket);
|
|
901
1131
|
if (!pendingPayloads || pendingPayloads.length === 0) {
|
|
902
1132
|
return;
|
|
903
1133
|
}
|
|
904
1134
|
this.pendingPayloadsBySocket.delete(socket);
|
|
905
1135
|
for (const envelope of pendingPayloads) {
|
|
906
|
-
this.sendEncryptedEnvelope(socket, envelope);
|
|
1136
|
+
await this.sendEncryptedEnvelope(socket, envelope);
|
|
907
1137
|
}
|
|
908
1138
|
}
|
|
909
|
-
createSecureServerClient(clientId, socket, request) {
|
|
1139
|
+
createSecureServerClient(clientId, socket, request, metadata) {
|
|
910
1140
|
return {
|
|
911
1141
|
id: clientId,
|
|
912
1142
|
socket,
|
|
913
1143
|
request,
|
|
1144
|
+
metadata,
|
|
914
1145
|
emit: (event, data, callbackOrOptions, maybeCallback) => {
|
|
915
1146
|
if (callbackOrOptions === void 0 && maybeCallback === void 0) {
|
|
916
1147
|
return this.emitTo(clientId, event, data);
|
|
@@ -1013,7 +1244,9 @@ var SecureServer = class {
|
|
|
1013
1244
|
if (!client) {
|
|
1014
1245
|
continue;
|
|
1015
1246
|
}
|
|
1016
|
-
this.sendOrQueuePayload(client.socket, envelope)
|
|
1247
|
+
void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
|
|
1248
|
+
return void 0;
|
|
1249
|
+
});
|
|
1017
1250
|
}
|
|
1018
1251
|
}
|
|
1019
1252
|
};
|
|
@@ -1177,7 +1410,9 @@ var SecureClient = class {
|
|
|
1177
1410
|
this.pendingPayloadQueue.push(envelope);
|
|
1178
1411
|
return true;
|
|
1179
1412
|
}
|
|
1180
|
-
this.sendEncryptedEnvelope(envelope)
|
|
1413
|
+
void this.sendEncryptedEnvelope(envelope).catch(() => {
|
|
1414
|
+
return void 0;
|
|
1415
|
+
});
|
|
1181
1416
|
return true;
|
|
1182
1417
|
} catch (error) {
|
|
1183
1418
|
const normalizedError = normalizeToError(error, "Failed to emit client event.");
|
|
@@ -1423,22 +1658,26 @@ var SecureClient = class {
|
|
|
1423
1658
|
}
|
|
1424
1659
|
}
|
|
1425
1660
|
}
|
|
1426
|
-
sendEncryptedEnvelope(envelope) {
|
|
1661
|
+
async sendEncryptedEnvelope(envelope) {
|
|
1662
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
1663
|
+
const socketStateError = new Error("Client socket is not connected.");
|
|
1664
|
+
this.notifyError(socketStateError);
|
|
1665
|
+
throw socketStateError;
|
|
1666
|
+
}
|
|
1667
|
+
const encryptionKey = this.handshakeState?.encryptionKey;
|
|
1668
|
+
if (!encryptionKey) {
|
|
1669
|
+
const missingKeyError = new Error("Missing encryption key for client payload encryption.");
|
|
1670
|
+
this.notifyError(missingKeyError);
|
|
1671
|
+
throw missingKeyError;
|
|
1672
|
+
}
|
|
1427
1673
|
try {
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
}
|
|
1431
|
-
const encryptionKey = this.handshakeState?.encryptionKey;
|
|
1432
|
-
if (!encryptionKey) {
|
|
1433
|
-
throw new Error("Missing encryption key for client payload encryption.");
|
|
1434
|
-
}
|
|
1435
|
-
const encryptedPayload = encryptSerializedEnvelope(
|
|
1436
|
-
serializeEnvelope(envelope.event, envelope.data),
|
|
1437
|
-
encryptionKey
|
|
1438
|
-
);
|
|
1674
|
+
const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
|
|
1675
|
+
const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
|
|
1439
1676
|
this.socket.send(encryptedPayload);
|
|
1440
1677
|
} catch (error) {
|
|
1441
|
-
|
|
1678
|
+
const normalizedError = normalizeToError(error, "Failed to send encrypted client payload.");
|
|
1679
|
+
this.notifyError(normalizedError);
|
|
1680
|
+
throw normalizedError;
|
|
1442
1681
|
}
|
|
1443
1682
|
}
|
|
1444
1683
|
sendRpcRequest(event, data, timeoutMs) {
|
|
@@ -1469,7 +1708,13 @@ var SecureClient = class {
|
|
|
1469
1708
|
this.pendingPayloadQueue.push(rpcRequestEnvelope);
|
|
1470
1709
|
return;
|
|
1471
1710
|
}
|
|
1472
|
-
this.sendEncryptedEnvelope(rpcRequestEnvelope)
|
|
1711
|
+
void this.sendEncryptedEnvelope(rpcRequestEnvelope).catch((error) => {
|
|
1712
|
+
clearTimeout(timeoutHandle);
|
|
1713
|
+
this.pendingRpcRequests.delete(requestId);
|
|
1714
|
+
reject(
|
|
1715
|
+
normalizeToError(error, `Failed to dispatch ACK request for event "${event}".`)
|
|
1716
|
+
);
|
|
1717
|
+
});
|
|
1473
1718
|
});
|
|
1474
1719
|
}
|
|
1475
1720
|
handleRpcResponse(data) {
|
|
@@ -1505,7 +1750,7 @@ var SecureClient = class {
|
|
|
1505
1750
|
rpcRequestPayload.event,
|
|
1506
1751
|
rpcRequestPayload.data
|
|
1507
1752
|
);
|
|
1508
|
-
this.sendEncryptedEnvelope({
|
|
1753
|
+
await this.sendEncryptedEnvelope({
|
|
1509
1754
|
event: INTERNAL_RPC_RESPONSE_EVENT,
|
|
1510
1755
|
data: {
|
|
1511
1756
|
id: rpcRequestPayload.id,
|
|
@@ -1515,7 +1760,7 @@ var SecureClient = class {
|
|
|
1515
1760
|
});
|
|
1516
1761
|
} catch (error) {
|
|
1517
1762
|
const normalizedError = normalizeToError(error, "Client ACK request handler failed.");
|
|
1518
|
-
this.sendEncryptedEnvelope({
|
|
1763
|
+
await this.sendEncryptedEnvelope({
|
|
1519
1764
|
event: INTERNAL_RPC_RESPONSE_EVENT,
|
|
1520
1765
|
data: {
|
|
1521
1766
|
id: rpcRequestPayload.id,
|
|
@@ -1560,7 +1805,7 @@ var SecureClient = class {
|
|
|
1560
1805
|
throw new Error("Missing client handshake state.");
|
|
1561
1806
|
}
|
|
1562
1807
|
this.socket.send(
|
|
1563
|
-
|
|
1808
|
+
serializePlainEnvelope(INTERNAL_HANDSHAKE_EVENT, {
|
|
1564
1809
|
publicKey: this.handshakeState.localPublicKey
|
|
1565
1810
|
})
|
|
1566
1811
|
);
|
|
@@ -1582,7 +1827,7 @@ var SecureClient = class {
|
|
|
1582
1827
|
this.handshakeState.sharedSecret = sharedSecret;
|
|
1583
1828
|
this.handshakeState.encryptionKey = deriveEncryptionKey(sharedSecret);
|
|
1584
1829
|
this.handshakeState.isReady = true;
|
|
1585
|
-
this.flushPendingPayloadQueue();
|
|
1830
|
+
void this.flushPendingPayloadQueue();
|
|
1586
1831
|
this.notifyReady();
|
|
1587
1832
|
} catch (error) {
|
|
1588
1833
|
this.notifyError(normalizeToError(error, "Failed to complete client handshake."));
|
|
@@ -1591,14 +1836,14 @@ var SecureClient = class {
|
|
|
1591
1836
|
isHandshakeReady() {
|
|
1592
1837
|
return this.handshakeState?.isReady ?? false;
|
|
1593
1838
|
}
|
|
1594
|
-
flushPendingPayloadQueue() {
|
|
1839
|
+
async flushPendingPayloadQueue() {
|
|
1595
1840
|
if (!this.socket || this.socket.readyState !== WebSocket.OPEN || !this.isHandshakeReady()) {
|
|
1596
1841
|
return;
|
|
1597
1842
|
}
|
|
1598
1843
|
const pendingPayloads = this.pendingPayloadQueue;
|
|
1599
1844
|
this.pendingPayloadQueue = [];
|
|
1600
1845
|
for (const envelope of pendingPayloads) {
|
|
1601
|
-
this.sendEncryptedEnvelope(envelope);
|
|
1846
|
+
await this.sendEncryptedEnvelope(envelope);
|
|
1602
1847
|
}
|
|
1603
1848
|
}
|
|
1604
1849
|
};
|