@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.cjs
CHANGED
|
@@ -10,6 +10,8 @@ var WebSocket__default = /*#__PURE__*/_interopDefault(WebSocket);
|
|
|
10
10
|
// src/index.ts
|
|
11
11
|
var DEFAULT_CLOSE_CODE = 1e3;
|
|
12
12
|
var DEFAULT_CLOSE_REASON = "";
|
|
13
|
+
var POLICY_VIOLATION_CLOSE_CODE = 1008;
|
|
14
|
+
var POLICY_VIOLATION_CLOSE_REASON = "Connection rejected by middleware.";
|
|
13
15
|
var INTERNAL_HANDSHAKE_EVENT = "__handshake";
|
|
14
16
|
var INTERNAL_RPC_REQUEST_EVENT = "__rpc:req";
|
|
15
17
|
var INTERNAL_RPC_RESPONSE_EVENT = "__rpc:res";
|
|
@@ -21,6 +23,8 @@ var GCM_AUTH_TAG_LENGTH = 16;
|
|
|
21
23
|
var ENCRYPTION_KEY_LENGTH = 32;
|
|
22
24
|
var ENCRYPTED_PACKET_VERSION = 1;
|
|
23
25
|
var ENCRYPTED_PACKET_PREFIX_LENGTH = 1 + GCM_IV_LENGTH + GCM_AUTH_TAG_LENGTH;
|
|
26
|
+
var BINARY_PAYLOAD_MARKER = "__afxBinaryPayload";
|
|
27
|
+
var BINARY_PAYLOAD_VERSION = 1;
|
|
24
28
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
25
29
|
var DEFAULT_HEARTBEAT_TIMEOUT_MS = 15e3;
|
|
26
30
|
var DEFAULT_RECONNECT_INITIAL_DELAY_MS = 250;
|
|
@@ -61,7 +65,103 @@ function rawDataToBuffer(rawData) {
|
|
|
61
65
|
}
|
|
62
66
|
return Buffer.from(rawData);
|
|
63
67
|
}
|
|
64
|
-
function
|
|
68
|
+
function isBlobValue(value) {
|
|
69
|
+
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
70
|
+
}
|
|
71
|
+
function isPlainObject(value) {
|
|
72
|
+
if (typeof value !== "object" || value === null) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const prototype = Object.getPrototypeOf(value);
|
|
76
|
+
return prototype === Object.prototype || prototype === null;
|
|
77
|
+
}
|
|
78
|
+
function encodeBinaryPayload(kind, payloadBuffer, mimeType) {
|
|
79
|
+
const encodedPayload = {
|
|
80
|
+
[BINARY_PAYLOAD_MARKER]: BINARY_PAYLOAD_VERSION,
|
|
81
|
+
kind,
|
|
82
|
+
base64: payloadBuffer.toString("base64")
|
|
83
|
+
};
|
|
84
|
+
if (mimeType !== void 0 && mimeType.length > 0) {
|
|
85
|
+
encodedPayload.mimeType = mimeType;
|
|
86
|
+
}
|
|
87
|
+
return encodedPayload;
|
|
88
|
+
}
|
|
89
|
+
async function encodeEnvelopeData(value) {
|
|
90
|
+
if (Buffer.isBuffer(value)) {
|
|
91
|
+
return encodeBinaryPayload("buffer", value);
|
|
92
|
+
}
|
|
93
|
+
if (value instanceof Uint8Array) {
|
|
94
|
+
const typedArrayBuffer = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
95
|
+
return encodeBinaryPayload("uint8array", typedArrayBuffer);
|
|
96
|
+
}
|
|
97
|
+
if (isBlobValue(value)) {
|
|
98
|
+
const blobBuffer = Buffer.from(await value.arrayBuffer());
|
|
99
|
+
return encodeBinaryPayload("blob", blobBuffer, value.type);
|
|
100
|
+
}
|
|
101
|
+
if (Array.isArray(value)) {
|
|
102
|
+
return Promise.all(value.map((item) => encodeEnvelopeData(item)));
|
|
103
|
+
}
|
|
104
|
+
if (isPlainObject(value)) {
|
|
105
|
+
const encodedEntries = await Promise.all(
|
|
106
|
+
Object.entries(value).map(async ([key, entryValue]) => {
|
|
107
|
+
return [key, await encodeEnvelopeData(entryValue)];
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
return Object.fromEntries(encodedEntries);
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
function isEncodedBinaryPayload(value) {
|
|
115
|
+
if (!isPlainObject(value)) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (value[BINARY_PAYLOAD_MARKER] !== BINARY_PAYLOAD_VERSION) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (value.kind !== "buffer" && value.kind !== "uint8array" && value.kind !== "blob") {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (typeof value.base64 !== "string") {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
if (value.mimeType !== void 0 && typeof value.mimeType !== "string") {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
function decodeEnvelopeData(value) {
|
|
133
|
+
if (Array.isArray(value)) {
|
|
134
|
+
return value.map((item) => decodeEnvelopeData(item));
|
|
135
|
+
}
|
|
136
|
+
if (isEncodedBinaryPayload(value)) {
|
|
137
|
+
const binaryBuffer = Buffer.from(value.base64, "base64");
|
|
138
|
+
if (value.kind === "buffer") {
|
|
139
|
+
return binaryBuffer;
|
|
140
|
+
}
|
|
141
|
+
if (value.kind === "uint8array") {
|
|
142
|
+
return Uint8Array.from(binaryBuffer);
|
|
143
|
+
}
|
|
144
|
+
if (typeof Blob === "undefined") {
|
|
145
|
+
return binaryBuffer;
|
|
146
|
+
}
|
|
147
|
+
return new Blob([binaryBuffer], {
|
|
148
|
+
type: value.mimeType ?? ""
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (isPlainObject(value)) {
|
|
152
|
+
const decodedEntries = Object.entries(value).map(([key, entryValue]) => {
|
|
153
|
+
return [key, decodeEnvelopeData(entryValue)];
|
|
154
|
+
});
|
|
155
|
+
return Object.fromEntries(decodedEntries);
|
|
156
|
+
}
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
async function serializeEnvelope(event, data) {
|
|
160
|
+
const encodedData = await encodeEnvelopeData(data);
|
|
161
|
+
const envelope = { event, data: encodedData };
|
|
162
|
+
return JSON.stringify(envelope);
|
|
163
|
+
}
|
|
164
|
+
function serializePlainEnvelope(event, data) {
|
|
65
165
|
const envelope = { event, data };
|
|
66
166
|
return JSON.stringify(envelope);
|
|
67
167
|
}
|
|
@@ -73,7 +173,7 @@ function parseEnvelope(rawData) {
|
|
|
73
173
|
}
|
|
74
174
|
return {
|
|
75
175
|
event: parsed.event,
|
|
76
|
-
data: parsed.data
|
|
176
|
+
data: decodeEnvelopeData(parsed.data)
|
|
77
177
|
};
|
|
78
178
|
}
|
|
79
179
|
function parseEnvelopeFromText(decodedPayload) {
|
|
@@ -83,7 +183,7 @@ function parseEnvelopeFromText(decodedPayload) {
|
|
|
83
183
|
}
|
|
84
184
|
return {
|
|
85
185
|
event: parsed.event,
|
|
86
|
-
data: parsed.data
|
|
186
|
+
data: decodeEnvelopeData(parsed.data)
|
|
87
187
|
};
|
|
88
188
|
}
|
|
89
189
|
function decodeCloseReason(reason) {
|
|
@@ -265,7 +365,9 @@ var SecureServer = class {
|
|
|
265
365
|
disconnectHandlers = /* @__PURE__ */ new Set();
|
|
266
366
|
readyHandlers = /* @__PURE__ */ new Set();
|
|
267
367
|
errorHandlers = /* @__PURE__ */ new Set();
|
|
368
|
+
middlewareHandlers = [];
|
|
268
369
|
handshakeStateBySocket = /* @__PURE__ */ new WeakMap();
|
|
370
|
+
middlewareMetadataBySocket = /* @__PURE__ */ new WeakMap();
|
|
269
371
|
sharedSecretBySocket = /* @__PURE__ */ new WeakMap();
|
|
270
372
|
encryptionKeyBySocket = /* @__PURE__ */ new WeakMap();
|
|
271
373
|
pendingPayloadsBySocket = /* @__PURE__ */ new WeakMap();
|
|
@@ -354,6 +456,19 @@ var SecureServer = class {
|
|
|
354
456
|
}
|
|
355
457
|
return this;
|
|
356
458
|
}
|
|
459
|
+
use(middleware) {
|
|
460
|
+
try {
|
|
461
|
+
if (typeof middleware !== "function") {
|
|
462
|
+
throw new Error("Server middleware must be a function.");
|
|
463
|
+
}
|
|
464
|
+
this.middlewareHandlers.push(middleware);
|
|
465
|
+
} catch (error) {
|
|
466
|
+
this.notifyError(
|
|
467
|
+
normalizeToError(error, "Failed to register server middleware.")
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
return this;
|
|
471
|
+
}
|
|
357
472
|
emit(event, data) {
|
|
358
473
|
try {
|
|
359
474
|
if (isReservedEmitEvent(event)) {
|
|
@@ -361,7 +476,9 @@ var SecureServer = class {
|
|
|
361
476
|
}
|
|
362
477
|
const envelope = { event, data };
|
|
363
478
|
for (const client of this.clientsById.values()) {
|
|
364
|
-
this.sendOrQueuePayload(client.socket, envelope)
|
|
479
|
+
void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
|
|
480
|
+
return void 0;
|
|
481
|
+
});
|
|
365
482
|
}
|
|
366
483
|
} catch (error) {
|
|
367
484
|
this.notifyError(normalizeToError(error, "Failed to emit server event."));
|
|
@@ -379,7 +496,9 @@ var SecureServer = class {
|
|
|
379
496
|
throw new Error(`Client with id ${clientId} was not found.`);
|
|
380
497
|
}
|
|
381
498
|
if (!ackArgs.expectsAck) {
|
|
382
|
-
this.sendOrQueuePayload(client.socket, { event, data })
|
|
499
|
+
void this.sendOrQueuePayload(client.socket, { event, data }).catch(() => {
|
|
500
|
+
return void 0;
|
|
501
|
+
});
|
|
383
502
|
return true;
|
|
384
503
|
}
|
|
385
504
|
const ackPromise = this.sendRpcRequest(
|
|
@@ -435,6 +554,7 @@ var SecureServer = class {
|
|
|
435
554
|
client.socket,
|
|
436
555
|
new Error("Server closed before ACK response was received.")
|
|
437
556
|
);
|
|
557
|
+
this.middlewareMetadataBySocket.delete(client.socket);
|
|
438
558
|
if (client.socket.readyState === WebSocket__default.default.OPEN || client.socket.readyState === WebSocket__default.default.CONNECTING) {
|
|
439
559
|
client.socket.close(code, reason);
|
|
440
560
|
}
|
|
@@ -497,6 +617,7 @@ var SecureServer = class {
|
|
|
497
617
|
this.pendingRpcRequestsBySocket.delete(socket);
|
|
498
618
|
this.handshakeStateBySocket.delete(socket);
|
|
499
619
|
this.heartbeatStateBySocket.delete(socket);
|
|
620
|
+
this.middlewareMetadataBySocket.delete(socket);
|
|
500
621
|
socket.terminate();
|
|
501
622
|
continue;
|
|
502
623
|
}
|
|
@@ -526,17 +647,46 @@ var SecureServer = class {
|
|
|
526
647
|
}
|
|
527
648
|
bindSocketServerEvents() {
|
|
528
649
|
this.socketServer.on("connection", (socket, request) => {
|
|
529
|
-
this.handleConnection(socket, request);
|
|
650
|
+
void this.handleConnection(socket, request);
|
|
530
651
|
});
|
|
531
652
|
this.socketServer.on("error", (error) => {
|
|
532
653
|
this.notifyError(normalizeToError(error, "WebSocket server encountered an error."));
|
|
533
654
|
});
|
|
534
655
|
}
|
|
535
|
-
handleConnection(socket, request) {
|
|
656
|
+
async handleConnection(socket, request) {
|
|
657
|
+
const connectionMetadata = /* @__PURE__ */ new Map();
|
|
658
|
+
this.middlewareMetadataBySocket.set(socket, connectionMetadata);
|
|
659
|
+
try {
|
|
660
|
+
await this.executeServerMiddleware({
|
|
661
|
+
phase: "connection",
|
|
662
|
+
socket,
|
|
663
|
+
request,
|
|
664
|
+
metadata: connectionMetadata
|
|
665
|
+
});
|
|
666
|
+
} catch (error) {
|
|
667
|
+
const normalizedError = normalizeToError(
|
|
668
|
+
error,
|
|
669
|
+
"Connection middleware rejected the incoming socket."
|
|
670
|
+
);
|
|
671
|
+
this.notifyError(normalizedError);
|
|
672
|
+
this.middlewareMetadataBySocket.delete(socket);
|
|
673
|
+
if (socket.readyState === WebSocket__default.default.OPEN || socket.readyState === WebSocket__default.default.CONNECTING) {
|
|
674
|
+
socket.close(
|
|
675
|
+
POLICY_VIOLATION_CLOSE_CODE,
|
|
676
|
+
normalizedError.message || POLICY_VIOLATION_CLOSE_REASON
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
536
681
|
try {
|
|
537
682
|
const clientId = crypto.randomUUID();
|
|
538
683
|
const handshakeState = this.createServerHandshakeState();
|
|
539
|
-
const client = this.createSecureServerClient(
|
|
684
|
+
const client = this.createSecureServerClient(
|
|
685
|
+
clientId,
|
|
686
|
+
socket,
|
|
687
|
+
request,
|
|
688
|
+
connectionMetadata
|
|
689
|
+
);
|
|
540
690
|
this.clientsById.set(clientId, client);
|
|
541
691
|
this.clientIdBySocket.set(socket, clientId);
|
|
542
692
|
this.handshakeStateBySocket.set(socket, handshakeState);
|
|
@@ -548,7 +698,7 @@ var SecureServer = class {
|
|
|
548
698
|
});
|
|
549
699
|
this.roomNamesByClientId.set(clientId, /* @__PURE__ */ new Set());
|
|
550
700
|
socket.on("message", (rawData) => {
|
|
551
|
-
this.handleIncomingMessage(client, rawData);
|
|
701
|
+
void this.handleIncomingMessage(client, rawData);
|
|
552
702
|
});
|
|
553
703
|
socket.on("close", (code, reason) => {
|
|
554
704
|
this.handleDisconnection(client, code, reason);
|
|
@@ -568,9 +718,10 @@ var SecureServer = class {
|
|
|
568
718
|
this.notifyConnection(client);
|
|
569
719
|
} catch (error) {
|
|
570
720
|
this.notifyError(normalizeToError(error, "Failed to handle client connection."));
|
|
721
|
+
this.middlewareMetadataBySocket.delete(socket);
|
|
571
722
|
}
|
|
572
723
|
}
|
|
573
|
-
handleIncomingMessage(client, rawData) {
|
|
724
|
+
async handleIncomingMessage(client, rawData) {
|
|
574
725
|
try {
|
|
575
726
|
let envelope = null;
|
|
576
727
|
try {
|
|
@@ -614,10 +765,16 @@ var SecureServer = class {
|
|
|
614
765
|
return;
|
|
615
766
|
}
|
|
616
767
|
if (decryptedEnvelope.event === INTERNAL_RPC_REQUEST_EVENT) {
|
|
617
|
-
|
|
768
|
+
await this.handleRpcRequest(client, decryptedEnvelope.data);
|
|
618
769
|
return;
|
|
619
770
|
}
|
|
620
|
-
this.
|
|
771
|
+
const interceptedData = await this.applyMessageMiddleware(
|
|
772
|
+
"incoming",
|
|
773
|
+
client,
|
|
774
|
+
decryptedEnvelope.event,
|
|
775
|
+
decryptedEnvelope.data
|
|
776
|
+
);
|
|
777
|
+
this.dispatchCustomEvent(decryptedEnvelope.event, interceptedData, client);
|
|
621
778
|
} catch (error) {
|
|
622
779
|
this.notifyError(normalizeToError(error, "Failed to process incoming server message."));
|
|
623
780
|
}
|
|
@@ -637,6 +794,7 @@ var SecureServer = class {
|
|
|
637
794
|
);
|
|
638
795
|
this.pendingRpcRequestsBySocket.delete(client.socket);
|
|
639
796
|
this.heartbeatStateBySocket.delete(client.socket);
|
|
797
|
+
this.middlewareMetadataBySocket.delete(client.socket);
|
|
640
798
|
const decodedReason = decodeCloseReason(reason);
|
|
641
799
|
for (const handler of this.disconnectHandlers) {
|
|
642
800
|
try {
|
|
@@ -682,6 +840,48 @@ var SecureServer = class {
|
|
|
682
840
|
}
|
|
683
841
|
}
|
|
684
842
|
}
|
|
843
|
+
async executeServerMiddleware(context) {
|
|
844
|
+
if (this.middlewareHandlers.length === 0) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
let currentIndex = -1;
|
|
848
|
+
const dispatch = async (index) => {
|
|
849
|
+
if (index <= currentIndex) {
|
|
850
|
+
throw new Error("Server middleware next() was called multiple times.");
|
|
851
|
+
}
|
|
852
|
+
currentIndex = index;
|
|
853
|
+
const middleware = this.middlewareHandlers[index];
|
|
854
|
+
if (!middleware) {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
await Promise.resolve(
|
|
858
|
+
middleware(context, async () => {
|
|
859
|
+
await dispatch(index + 1);
|
|
860
|
+
})
|
|
861
|
+
);
|
|
862
|
+
};
|
|
863
|
+
await dispatch(0);
|
|
864
|
+
}
|
|
865
|
+
async applyMessageMiddleware(phase, client, event, data) {
|
|
866
|
+
const metadata = this.middlewareMetadataBySocket.get(client.socket) ?? /* @__PURE__ */ new Map();
|
|
867
|
+
this.middlewareMetadataBySocket.set(client.socket, metadata);
|
|
868
|
+
const middlewareContext = {
|
|
869
|
+
phase,
|
|
870
|
+
client,
|
|
871
|
+
event,
|
|
872
|
+
data,
|
|
873
|
+
metadata
|
|
874
|
+
};
|
|
875
|
+
await this.executeServerMiddleware(middlewareContext);
|
|
876
|
+
return middlewareContext.data;
|
|
877
|
+
}
|
|
878
|
+
resolveClientBySocket(socket) {
|
|
879
|
+
const clientId = this.clientIdBySocket.get(socket);
|
|
880
|
+
if (!clientId) {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
return this.clientsById.get(clientId) ?? null;
|
|
884
|
+
}
|
|
685
885
|
sendRaw(socket, payload) {
|
|
686
886
|
try {
|
|
687
887
|
if (socket.readyState !== WebSocket__default.default.OPEN) {
|
|
@@ -692,22 +892,24 @@ var SecureServer = class {
|
|
|
692
892
|
this.notifyError(normalizeToError(error, "Failed to send server payload."));
|
|
693
893
|
}
|
|
694
894
|
}
|
|
695
|
-
sendEncryptedEnvelope(socket, envelope) {
|
|
895
|
+
async sendEncryptedEnvelope(socket, envelope) {
|
|
896
|
+
if (socket.readyState !== WebSocket__default.default.OPEN) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
const encryptionKey = this.encryptionKeyBySocket.get(socket);
|
|
900
|
+
if (!encryptionKey) {
|
|
901
|
+
const missingKeyError = new Error("Missing encryption key for connected socket.");
|
|
902
|
+
this.notifyError(missingKeyError);
|
|
903
|
+
throw missingKeyError;
|
|
904
|
+
}
|
|
696
905
|
try {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
}
|
|
700
|
-
const encryptionKey = this.encryptionKeyBySocket.get(socket);
|
|
701
|
-
if (!encryptionKey) {
|
|
702
|
-
throw new Error("Missing encryption key for connected socket.");
|
|
703
|
-
}
|
|
704
|
-
const encryptedPayload = encryptSerializedEnvelope(
|
|
705
|
-
serializeEnvelope(envelope.event, envelope.data),
|
|
706
|
-
encryptionKey
|
|
707
|
-
);
|
|
906
|
+
const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
|
|
907
|
+
const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
|
|
708
908
|
socket.send(encryptedPayload);
|
|
709
909
|
} catch (error) {
|
|
710
|
-
|
|
910
|
+
const normalizedError = normalizeToError(error, "Failed to send encrypted server payload.");
|
|
911
|
+
this.notifyError(normalizedError);
|
|
912
|
+
throw normalizedError;
|
|
711
913
|
}
|
|
712
914
|
}
|
|
713
915
|
sendRpcRequest(socket, event, data, timeoutMs) {
|
|
@@ -728,13 +930,19 @@ var SecureServer = class {
|
|
|
728
930
|
reject,
|
|
729
931
|
timeoutHandle
|
|
730
932
|
});
|
|
731
|
-
this.sendOrQueuePayload(socket, {
|
|
933
|
+
void this.sendOrQueuePayload(socket, {
|
|
732
934
|
event: INTERNAL_RPC_REQUEST_EVENT,
|
|
733
935
|
data: {
|
|
734
936
|
id: requestId,
|
|
735
937
|
event,
|
|
736
938
|
data
|
|
737
939
|
}
|
|
940
|
+
}).catch((error) => {
|
|
941
|
+
clearTimeout(timeoutHandle);
|
|
942
|
+
pendingRequests.delete(requestId);
|
|
943
|
+
reject(
|
|
944
|
+
normalizeToError(error, `Failed to dispatch ACK request for event "${event}".`)
|
|
945
|
+
);
|
|
738
946
|
});
|
|
739
947
|
});
|
|
740
948
|
}
|
|
@@ -771,12 +979,18 @@ var SecureServer = class {
|
|
|
771
979
|
return;
|
|
772
980
|
}
|
|
773
981
|
try {
|
|
982
|
+
const interceptedData = await this.applyMessageMiddleware(
|
|
983
|
+
"incoming",
|
|
984
|
+
client,
|
|
985
|
+
rpcRequestPayload.event,
|
|
986
|
+
rpcRequestPayload.data
|
|
987
|
+
);
|
|
774
988
|
const ackResponse = await this.executeRpcRequestHandler(
|
|
775
989
|
rpcRequestPayload.event,
|
|
776
|
-
|
|
990
|
+
interceptedData,
|
|
777
991
|
client
|
|
778
992
|
);
|
|
779
|
-
this.sendEncryptedEnvelope(client.socket, {
|
|
993
|
+
await this.sendEncryptedEnvelope(client.socket, {
|
|
780
994
|
event: INTERNAL_RPC_RESPONSE_EVENT,
|
|
781
995
|
data: {
|
|
782
996
|
id: rpcRequestPayload.id,
|
|
@@ -786,7 +1000,7 @@ var SecureServer = class {
|
|
|
786
1000
|
});
|
|
787
1001
|
} catch (error) {
|
|
788
1002
|
const normalizedError = normalizeToError(error, "Server ACK request handler failed.");
|
|
789
|
-
this.sendEncryptedEnvelope(client.socket, {
|
|
1003
|
+
await this.sendEncryptedEnvelope(client.socket, {
|
|
790
1004
|
event: INTERNAL_RPC_RESPONSE_EVENT,
|
|
791
1005
|
data: {
|
|
792
1006
|
id: rpcRequestPayload.id,
|
|
@@ -860,7 +1074,7 @@ var SecureServer = class {
|
|
|
860
1074
|
sendInternalHandshake(socket, localPublicKey) {
|
|
861
1075
|
this.sendRaw(
|
|
862
1076
|
socket,
|
|
863
|
-
|
|
1077
|
+
serializePlainEnvelope(INTERNAL_HANDSHAKE_EVENT, {
|
|
864
1078
|
publicKey: localPublicKey
|
|
865
1079
|
})
|
|
866
1080
|
);
|
|
@@ -890,33 +1104,50 @@ var SecureServer = class {
|
|
|
890
1104
|
isClientHandshakeReady(socket) {
|
|
891
1105
|
return this.handshakeStateBySocket.get(socket)?.isReady ?? false;
|
|
892
1106
|
}
|
|
893
|
-
sendOrQueuePayload(socket, envelope) {
|
|
1107
|
+
async sendOrQueuePayload(socket, envelope) {
|
|
1108
|
+
let interceptedEnvelope = envelope;
|
|
1109
|
+
if (!isReservedEmitEvent(envelope.event)) {
|
|
1110
|
+
const targetClient = this.resolveClientBySocket(socket);
|
|
1111
|
+
if (targetClient) {
|
|
1112
|
+
const interceptedData = await this.applyMessageMiddleware(
|
|
1113
|
+
"outgoing",
|
|
1114
|
+
targetClient,
|
|
1115
|
+
envelope.event,
|
|
1116
|
+
envelope.data
|
|
1117
|
+
);
|
|
1118
|
+
interceptedEnvelope = {
|
|
1119
|
+
event: envelope.event,
|
|
1120
|
+
data: interceptedData
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
894
1124
|
if (!this.isClientHandshakeReady(socket)) {
|
|
895
|
-
this.queuePayload(socket,
|
|
1125
|
+
this.queuePayload(socket, interceptedEnvelope);
|
|
896
1126
|
return;
|
|
897
1127
|
}
|
|
898
|
-
this.sendEncryptedEnvelope(socket,
|
|
1128
|
+
await this.sendEncryptedEnvelope(socket, interceptedEnvelope);
|
|
899
1129
|
}
|
|
900
1130
|
queuePayload(socket, envelope) {
|
|
901
1131
|
const pendingPayloads = this.pendingPayloadsBySocket.get(socket) ?? [];
|
|
902
1132
|
pendingPayloads.push(envelope);
|
|
903
1133
|
this.pendingPayloadsBySocket.set(socket, pendingPayloads);
|
|
904
1134
|
}
|
|
905
|
-
flushQueuedPayloads(socket) {
|
|
1135
|
+
async flushQueuedPayloads(socket) {
|
|
906
1136
|
const pendingPayloads = this.pendingPayloadsBySocket.get(socket);
|
|
907
1137
|
if (!pendingPayloads || pendingPayloads.length === 0) {
|
|
908
1138
|
return;
|
|
909
1139
|
}
|
|
910
1140
|
this.pendingPayloadsBySocket.delete(socket);
|
|
911
1141
|
for (const envelope of pendingPayloads) {
|
|
912
|
-
this.sendEncryptedEnvelope(socket, envelope);
|
|
1142
|
+
await this.sendEncryptedEnvelope(socket, envelope);
|
|
913
1143
|
}
|
|
914
1144
|
}
|
|
915
|
-
createSecureServerClient(clientId, socket, request) {
|
|
1145
|
+
createSecureServerClient(clientId, socket, request, metadata) {
|
|
916
1146
|
return {
|
|
917
1147
|
id: clientId,
|
|
918
1148
|
socket,
|
|
919
1149
|
request,
|
|
1150
|
+
metadata,
|
|
920
1151
|
emit: (event, data, callbackOrOptions, maybeCallback) => {
|
|
921
1152
|
if (callbackOrOptions === void 0 && maybeCallback === void 0) {
|
|
922
1153
|
return this.emitTo(clientId, event, data);
|
|
@@ -1019,7 +1250,9 @@ var SecureServer = class {
|
|
|
1019
1250
|
if (!client) {
|
|
1020
1251
|
continue;
|
|
1021
1252
|
}
|
|
1022
|
-
this.sendOrQueuePayload(client.socket, envelope)
|
|
1253
|
+
void this.sendOrQueuePayload(client.socket, envelope).catch(() => {
|
|
1254
|
+
return void 0;
|
|
1255
|
+
});
|
|
1023
1256
|
}
|
|
1024
1257
|
}
|
|
1025
1258
|
};
|
|
@@ -1183,7 +1416,9 @@ var SecureClient = class {
|
|
|
1183
1416
|
this.pendingPayloadQueue.push(envelope);
|
|
1184
1417
|
return true;
|
|
1185
1418
|
}
|
|
1186
|
-
this.sendEncryptedEnvelope(envelope)
|
|
1419
|
+
void this.sendEncryptedEnvelope(envelope).catch(() => {
|
|
1420
|
+
return void 0;
|
|
1421
|
+
});
|
|
1187
1422
|
return true;
|
|
1188
1423
|
} catch (error) {
|
|
1189
1424
|
const normalizedError = normalizeToError(error, "Failed to emit client event.");
|
|
@@ -1429,22 +1664,26 @@ var SecureClient = class {
|
|
|
1429
1664
|
}
|
|
1430
1665
|
}
|
|
1431
1666
|
}
|
|
1432
|
-
sendEncryptedEnvelope(envelope) {
|
|
1667
|
+
async sendEncryptedEnvelope(envelope) {
|
|
1668
|
+
if (!this.socket || this.socket.readyState !== WebSocket__default.default.OPEN) {
|
|
1669
|
+
const socketStateError = new Error("Client socket is not connected.");
|
|
1670
|
+
this.notifyError(socketStateError);
|
|
1671
|
+
throw socketStateError;
|
|
1672
|
+
}
|
|
1673
|
+
const encryptionKey = this.handshakeState?.encryptionKey;
|
|
1674
|
+
if (!encryptionKey) {
|
|
1675
|
+
const missingKeyError = new Error("Missing encryption key for client payload encryption.");
|
|
1676
|
+
this.notifyError(missingKeyError);
|
|
1677
|
+
throw missingKeyError;
|
|
1678
|
+
}
|
|
1433
1679
|
try {
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
}
|
|
1437
|
-
const encryptionKey = this.handshakeState?.encryptionKey;
|
|
1438
|
-
if (!encryptionKey) {
|
|
1439
|
-
throw new Error("Missing encryption key for client payload encryption.");
|
|
1440
|
-
}
|
|
1441
|
-
const encryptedPayload = encryptSerializedEnvelope(
|
|
1442
|
-
serializeEnvelope(envelope.event, envelope.data),
|
|
1443
|
-
encryptionKey
|
|
1444
|
-
);
|
|
1680
|
+
const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
|
|
1681
|
+
const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
|
|
1445
1682
|
this.socket.send(encryptedPayload);
|
|
1446
1683
|
} catch (error) {
|
|
1447
|
-
|
|
1684
|
+
const normalizedError = normalizeToError(error, "Failed to send encrypted client payload.");
|
|
1685
|
+
this.notifyError(normalizedError);
|
|
1686
|
+
throw normalizedError;
|
|
1448
1687
|
}
|
|
1449
1688
|
}
|
|
1450
1689
|
sendRpcRequest(event, data, timeoutMs) {
|
|
@@ -1475,7 +1714,13 @@ var SecureClient = class {
|
|
|
1475
1714
|
this.pendingPayloadQueue.push(rpcRequestEnvelope);
|
|
1476
1715
|
return;
|
|
1477
1716
|
}
|
|
1478
|
-
this.sendEncryptedEnvelope(rpcRequestEnvelope)
|
|
1717
|
+
void this.sendEncryptedEnvelope(rpcRequestEnvelope).catch((error) => {
|
|
1718
|
+
clearTimeout(timeoutHandle);
|
|
1719
|
+
this.pendingRpcRequests.delete(requestId);
|
|
1720
|
+
reject(
|
|
1721
|
+
normalizeToError(error, `Failed to dispatch ACK request for event "${event}".`)
|
|
1722
|
+
);
|
|
1723
|
+
});
|
|
1479
1724
|
});
|
|
1480
1725
|
}
|
|
1481
1726
|
handleRpcResponse(data) {
|
|
@@ -1511,7 +1756,7 @@ var SecureClient = class {
|
|
|
1511
1756
|
rpcRequestPayload.event,
|
|
1512
1757
|
rpcRequestPayload.data
|
|
1513
1758
|
);
|
|
1514
|
-
this.sendEncryptedEnvelope({
|
|
1759
|
+
await this.sendEncryptedEnvelope({
|
|
1515
1760
|
event: INTERNAL_RPC_RESPONSE_EVENT,
|
|
1516
1761
|
data: {
|
|
1517
1762
|
id: rpcRequestPayload.id,
|
|
@@ -1521,7 +1766,7 @@ var SecureClient = class {
|
|
|
1521
1766
|
});
|
|
1522
1767
|
} catch (error) {
|
|
1523
1768
|
const normalizedError = normalizeToError(error, "Client ACK request handler failed.");
|
|
1524
|
-
this.sendEncryptedEnvelope({
|
|
1769
|
+
await this.sendEncryptedEnvelope({
|
|
1525
1770
|
event: INTERNAL_RPC_RESPONSE_EVENT,
|
|
1526
1771
|
data: {
|
|
1527
1772
|
id: rpcRequestPayload.id,
|
|
@@ -1566,7 +1811,7 @@ var SecureClient = class {
|
|
|
1566
1811
|
throw new Error("Missing client handshake state.");
|
|
1567
1812
|
}
|
|
1568
1813
|
this.socket.send(
|
|
1569
|
-
|
|
1814
|
+
serializePlainEnvelope(INTERNAL_HANDSHAKE_EVENT, {
|
|
1570
1815
|
publicKey: this.handshakeState.localPublicKey
|
|
1571
1816
|
})
|
|
1572
1817
|
);
|
|
@@ -1588,7 +1833,7 @@ var SecureClient = class {
|
|
|
1588
1833
|
this.handshakeState.sharedSecret = sharedSecret;
|
|
1589
1834
|
this.handshakeState.encryptionKey = deriveEncryptionKey(sharedSecret);
|
|
1590
1835
|
this.handshakeState.isReady = true;
|
|
1591
|
-
this.flushPendingPayloadQueue();
|
|
1836
|
+
void this.flushPendingPayloadQueue();
|
|
1592
1837
|
this.notifyReady();
|
|
1593
1838
|
} catch (error) {
|
|
1594
1839
|
this.notifyError(normalizeToError(error, "Failed to complete client handshake."));
|
|
@@ -1597,14 +1842,14 @@ var SecureClient = class {
|
|
|
1597
1842
|
isHandshakeReady() {
|
|
1598
1843
|
return this.handshakeState?.isReady ?? false;
|
|
1599
1844
|
}
|
|
1600
|
-
flushPendingPayloadQueue() {
|
|
1845
|
+
async flushPendingPayloadQueue() {
|
|
1601
1846
|
if (!this.socket || this.socket.readyState !== WebSocket__default.default.OPEN || !this.isHandshakeReady()) {
|
|
1602
1847
|
return;
|
|
1603
1848
|
}
|
|
1604
1849
|
const pendingPayloads = this.pendingPayloadQueue;
|
|
1605
1850
|
this.pendingPayloadQueue = [];
|
|
1606
1851
|
for (const envelope of pendingPayloads) {
|
|
1607
|
-
this.sendEncryptedEnvelope(envelope);
|
|
1852
|
+
await this.sendEncryptedEnvelope(envelope);
|
|
1608
1853
|
}
|
|
1609
1854
|
}
|
|
1610
1855
|
};
|