@aegis-fluxion/core 0.9.0 → 0.10.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 +118 -1
- package/dist/index.cjs +134 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +134 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Low-level encrypted WebSocket primitives for the `aegis-fluxion` ecosystem.
|
|
4
4
|
|
|
5
|
-
Version: **0.
|
|
5
|
+
Version: **0.10.0**
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -10,6 +10,7 @@ Version: **0.9.0**
|
|
|
10
10
|
|
|
11
11
|
- Ephemeral ECDH handshake (`prime256v1`)
|
|
12
12
|
- AES-256-GCM encrypted envelopes
|
|
13
|
+
- Built-in telemetry via `getMetrics()` and `getMetricsPrometheus()`
|
|
13
14
|
- ACK request/response (Promise + callback styles)
|
|
14
15
|
- Encrypted chunked streaming for large `Buffer`/`Readable` payloads
|
|
15
16
|
- Secure room routing (`join`, `leave`, `leaveAll`, `to(room).emit(...)`)
|
|
@@ -28,6 +29,122 @@ npm install @aegis-fluxion/core ws
|
|
|
28
29
|
|
|
29
30
|
---
|
|
30
31
|
|
|
32
|
+
## Observability & telemetry (new in 0.10.0)
|
|
33
|
+
|
|
34
|
+
`SecureServer` exposes real-time metrics for operational visibility:
|
|
35
|
+
|
|
36
|
+
- active secure connections
|
|
37
|
+
- successful/failed handshakes (including resume attempts)
|
|
38
|
+
- encrypted message and byte throughput (ingress/egress)
|
|
39
|
+
- DDoS/rate-limit counters (blocked, throttled, disconnected)
|
|
40
|
+
|
|
41
|
+
### JSON metrics snapshot
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { SecureServer } from "@aegis-fluxion/core";
|
|
45
|
+
|
|
46
|
+
const server = new SecureServer({ host: "127.0.0.1", port: 8080 });
|
|
47
|
+
|
|
48
|
+
const snapshot = server.getMetrics();
|
|
49
|
+
console.log(snapshot.activeConnections);
|
|
50
|
+
console.log(snapshot.encryptedMessagesReceivedTotal);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Prometheus endpoint integration
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { createServer } from "node:http";
|
|
57
|
+
import { SecureServer } from "@aegis-fluxion/core";
|
|
58
|
+
|
|
59
|
+
const secureServer = new SecureServer({ host: "127.0.0.1", port: 8080 });
|
|
60
|
+
|
|
61
|
+
createServer((request, response) => {
|
|
62
|
+
if (request.url === "/metrics") {
|
|
63
|
+
response.setHeader("Content-Type", "text/plain; version=0.0.4; charset=utf-8");
|
|
64
|
+
response.end(secureServer.getMetricsPrometheus());
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
response.statusCode = 404;
|
|
69
|
+
response.end("Not Found");
|
|
70
|
+
}).listen(9100, "127.0.0.1");
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Frontend integration (React)
|
|
76
|
+
|
|
77
|
+
`@aegis-fluxion/core` is server/runtime focused. For browser clients, pair it with
|
|
78
|
+
`@aegis-fluxion/browser-client`.
|
|
79
|
+
|
|
80
|
+
### Node backend (`SecureServer`)
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { SecureServer } from "@aegis-fluxion/core";
|
|
84
|
+
|
|
85
|
+
const server = new SecureServer({ host: "127.0.0.1", port: 8080 });
|
|
86
|
+
|
|
87
|
+
server.on("feed:publish", async (payload) => {
|
|
88
|
+
server.emit("feed:message", payload);
|
|
89
|
+
return { ok: true };
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### React frontend (`BrowserSecureClient`)
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { useEffect, useMemo, useState } from "react";
|
|
97
|
+
import { BrowserSecureClient } from "@aegis-fluxion/browser-client";
|
|
98
|
+
|
|
99
|
+
export function SecureFeedPanel() {
|
|
100
|
+
const [status, setStatus] = useState("connecting");
|
|
101
|
+
const [messages, setMessages] = useState<string[]>([]);
|
|
102
|
+
|
|
103
|
+
const client = useMemo(() => {
|
|
104
|
+
return new BrowserSecureClient("ws://127.0.0.1:8080", {
|
|
105
|
+
autoConnect: false,
|
|
106
|
+
reconnect: true
|
|
107
|
+
});
|
|
108
|
+
}, []);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
const onReady = () => setStatus("ready");
|
|
112
|
+
const onDisconnect = () => setStatus("disconnected");
|
|
113
|
+
const onFeedMessage = (payload: unknown) => {
|
|
114
|
+
const data = payload as { text?: string };
|
|
115
|
+
if (typeof data.text === "string") {
|
|
116
|
+
setMessages((prev) => [data.text, ...prev]);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
client.on("ready", onReady);
|
|
121
|
+
client.on("disconnect", onDisconnect);
|
|
122
|
+
client.on("feed:message", onFeedMessage);
|
|
123
|
+
client.connect();
|
|
124
|
+
|
|
125
|
+
return () => {
|
|
126
|
+
client.off("ready", onReady);
|
|
127
|
+
client.off("disconnect", onDisconnect);
|
|
128
|
+
client.off("feed:message", onFeedMessage);
|
|
129
|
+
client.disconnect();
|
|
130
|
+
};
|
|
131
|
+
}, [client]);
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<section>
|
|
135
|
+
<p>Status: {status}</p>
|
|
136
|
+
<ul>
|
|
137
|
+
{messages.map((message) => (
|
|
138
|
+
<li key={message}>{message}</li>
|
|
139
|
+
))}
|
|
140
|
+
</ul>
|
|
141
|
+
</section>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
31
148
|
## Chunked streaming (new in 0.9.0)
|
|
32
149
|
|
|
33
150
|
`@aegis-fluxion/core@0.9.0` adds secure chunked stream transport for large payloads.
|
package/dist/index.cjs
CHANGED
|
@@ -259,6 +259,9 @@ function parseEnvelopeFromText(decodedPayload) {
|
|
|
259
259
|
function decodeCloseReason(reason) {
|
|
260
260
|
return reason.toString("utf8");
|
|
261
261
|
}
|
|
262
|
+
function escapePrometheusLabelValue(value) {
|
|
263
|
+
return value.replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, '\\"');
|
|
264
|
+
}
|
|
262
265
|
function isReservedEmitEvent(event) {
|
|
263
266
|
return event === INTERNAL_HANDSHAKE_EVENT || event === INTERNAL_SESSION_TICKET_EVENT || event === INTERNAL_RPC_REQUEST_EVENT || event === INTERNAL_RPC_RESPONSE_EVENT || event === INTERNAL_STREAM_FRAME_EVENT || event === READY_EVENT;
|
|
264
267
|
}
|
|
@@ -793,6 +796,7 @@ function decryptSerializedEnvelope(rawData, encryptionKey) {
|
|
|
793
796
|
}
|
|
794
797
|
var SecureServer = class {
|
|
795
798
|
instanceId = crypto.randomUUID();
|
|
799
|
+
startedAtMs = Date.now();
|
|
796
800
|
socketServer;
|
|
797
801
|
adapter = null;
|
|
798
802
|
heartbeatConfig;
|
|
@@ -822,6 +826,20 @@ var SecureServer = class {
|
|
|
822
826
|
rateLimitBucketsByClientId = /* @__PURE__ */ new Map();
|
|
823
827
|
rateLimitBucketsByIp = /* @__PURE__ */ new Map();
|
|
824
828
|
sessionTicketStore = /* @__PURE__ */ new Map();
|
|
829
|
+
telemetryCounters = {
|
|
830
|
+
totalConnections: 0,
|
|
831
|
+
handshakeSuccessTotal: 0,
|
|
832
|
+
handshakeFailureTotal: 0,
|
|
833
|
+
resumeHandshakeSuccessTotal: 0,
|
|
834
|
+
resumeHandshakeFailureTotal: 0,
|
|
835
|
+
encryptedMessagesSentTotal: 0,
|
|
836
|
+
encryptedMessagesReceivedTotal: 0,
|
|
837
|
+
encryptedBytesSentTotal: 0,
|
|
838
|
+
encryptedBytesReceivedTotal: 0,
|
|
839
|
+
ddosBlockedTotal: 0,
|
|
840
|
+
ddosThrottledTotal: 0,
|
|
841
|
+
ddosDisconnectedTotal: 0
|
|
842
|
+
};
|
|
825
843
|
constructor(options) {
|
|
826
844
|
const { heartbeat, rateLimit, sessionResumption, adapter, ...socketServerOptions } = options;
|
|
827
845
|
this.heartbeatConfig = this.resolveHeartbeatConfig(heartbeat);
|
|
@@ -845,6 +863,79 @@ var SecureServer = class {
|
|
|
845
863
|
get clients() {
|
|
846
864
|
return this.clientsById;
|
|
847
865
|
}
|
|
866
|
+
getMetrics() {
|
|
867
|
+
const now = Date.now();
|
|
868
|
+
const uptimeSeconds = Math.max(0, (now - this.startedAtMs) / 1e3);
|
|
869
|
+
return {
|
|
870
|
+
serverId: this.instanceId,
|
|
871
|
+
timestampMs: now,
|
|
872
|
+
uptimeSeconds,
|
|
873
|
+
activeConnections: this.clientCount,
|
|
874
|
+
totalConnections: this.telemetryCounters.totalConnections,
|
|
875
|
+
handshakeSuccessTotal: this.telemetryCounters.handshakeSuccessTotal,
|
|
876
|
+
handshakeFailureTotal: this.telemetryCounters.handshakeFailureTotal,
|
|
877
|
+
resumeHandshakeSuccessTotal: this.telemetryCounters.resumeHandshakeSuccessTotal,
|
|
878
|
+
resumeHandshakeFailureTotal: this.telemetryCounters.resumeHandshakeFailureTotal,
|
|
879
|
+
encryptedMessagesSentTotal: this.telemetryCounters.encryptedMessagesSentTotal,
|
|
880
|
+
encryptedMessagesReceivedTotal: this.telemetryCounters.encryptedMessagesReceivedTotal,
|
|
881
|
+
encryptedBytesSentTotal: this.telemetryCounters.encryptedBytesSentTotal,
|
|
882
|
+
encryptedBytesReceivedTotal: this.telemetryCounters.encryptedBytesReceivedTotal,
|
|
883
|
+
ddosBlockedTotal: this.telemetryCounters.ddosBlockedTotal,
|
|
884
|
+
ddosThrottledTotal: this.telemetryCounters.ddosThrottledTotal,
|
|
885
|
+
ddosDisconnectedTotal: this.telemetryCounters.ddosDisconnectedTotal
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
getMetricsPrometheus() {
|
|
889
|
+
const metrics = this.getMetrics();
|
|
890
|
+
const labelValue = escapePrometheusLabelValue(metrics.serverId);
|
|
891
|
+
const labels = `{server_id="${labelValue}"}`;
|
|
892
|
+
const lines = [
|
|
893
|
+
"# HELP aegis_fluxion_server_active_connections Number of currently active secure connections.",
|
|
894
|
+
"# TYPE aegis_fluxion_server_active_connections gauge",
|
|
895
|
+
`aegis_fluxion_server_active_connections${labels} ${metrics.activeConnections}`,
|
|
896
|
+
"# HELP aegis_fluxion_server_total_connections_total Total number of accepted secure connections since process start.",
|
|
897
|
+
"# TYPE aegis_fluxion_server_total_connections_total counter",
|
|
898
|
+
`aegis_fluxion_server_total_connections_total${labels} ${metrics.totalConnections}`,
|
|
899
|
+
"# HELP aegis_fluxion_server_uptime_seconds Process uptime in seconds.",
|
|
900
|
+
"# TYPE aegis_fluxion_server_uptime_seconds gauge",
|
|
901
|
+
`aegis_fluxion_server_uptime_seconds${labels} ${metrics.uptimeSeconds}`,
|
|
902
|
+
"# HELP aegis_fluxion_server_handshake_success_total Total successful secure handshakes.",
|
|
903
|
+
"# TYPE aegis_fluxion_server_handshake_success_total counter",
|
|
904
|
+
`aegis_fluxion_server_handshake_success_total${labels} ${metrics.handshakeSuccessTotal}`,
|
|
905
|
+
"# HELP aegis_fluxion_server_handshake_failure_total Total failed secure handshake attempts.",
|
|
906
|
+
"# TYPE aegis_fluxion_server_handshake_failure_total counter",
|
|
907
|
+
`aegis_fluxion_server_handshake_failure_total${labels} ${metrics.handshakeFailureTotal}`,
|
|
908
|
+
"# HELP aegis_fluxion_server_resume_handshake_success_total Total successful session-resume handshakes.",
|
|
909
|
+
"# TYPE aegis_fluxion_server_resume_handshake_success_total counter",
|
|
910
|
+
`aegis_fluxion_server_resume_handshake_success_total${labels} ${metrics.resumeHandshakeSuccessTotal}`,
|
|
911
|
+
"# HELP aegis_fluxion_server_resume_handshake_failure_total Total failed session-resume handshakes.",
|
|
912
|
+
"# TYPE aegis_fluxion_server_resume_handshake_failure_total counter",
|
|
913
|
+
`aegis_fluxion_server_resume_handshake_failure_total${labels} ${metrics.resumeHandshakeFailureTotal}`,
|
|
914
|
+
"# HELP aegis_fluxion_server_encrypted_messages_sent_total Total encrypted messages sent by the server.",
|
|
915
|
+
"# TYPE aegis_fluxion_server_encrypted_messages_sent_total counter",
|
|
916
|
+
`aegis_fluxion_server_encrypted_messages_sent_total${labels} ${metrics.encryptedMessagesSentTotal}`,
|
|
917
|
+
"# HELP aegis_fluxion_server_encrypted_messages_received_total Total encrypted messages received by the server.",
|
|
918
|
+
"# TYPE aegis_fluxion_server_encrypted_messages_received_total counter",
|
|
919
|
+
`aegis_fluxion_server_encrypted_messages_received_total${labels} ${metrics.encryptedMessagesReceivedTotal}`,
|
|
920
|
+
"# HELP aegis_fluxion_server_encrypted_bytes_sent_total Total encrypted bytes sent by the server.",
|
|
921
|
+
"# TYPE aegis_fluxion_server_encrypted_bytes_sent_total counter",
|
|
922
|
+
`aegis_fluxion_server_encrypted_bytes_sent_total${labels} ${metrics.encryptedBytesSentTotal}`,
|
|
923
|
+
"# HELP aegis_fluxion_server_encrypted_bytes_received_total Total encrypted bytes received by the server.",
|
|
924
|
+
"# TYPE aegis_fluxion_server_encrypted_bytes_received_total counter",
|
|
925
|
+
`aegis_fluxion_server_encrypted_bytes_received_total${labels} ${metrics.encryptedBytesReceivedTotal}`,
|
|
926
|
+
"# HELP aegis_fluxion_server_ddos_blocked_total Total DDoS/flood attempts blocked by rate limiting.",
|
|
927
|
+
"# TYPE aegis_fluxion_server_ddos_blocked_total counter",
|
|
928
|
+
`aegis_fluxion_server_ddos_blocked_total${labels} ${metrics.ddosBlockedTotal}`,
|
|
929
|
+
"# HELP aegis_fluxion_server_ddos_throttled_total Total requests slowed down by adaptive throttling.",
|
|
930
|
+
"# TYPE aegis_fluxion_server_ddos_throttled_total counter",
|
|
931
|
+
`aegis_fluxion_server_ddos_throttled_total${labels} ${metrics.ddosThrottledTotal}`,
|
|
932
|
+
"# HELP aegis_fluxion_server_ddos_disconnected_total Total sockets disconnected due to severe rate limit violations.",
|
|
933
|
+
"# TYPE aegis_fluxion_server_ddos_disconnected_total counter",
|
|
934
|
+
`aegis_fluxion_server_ddos_disconnected_total${labels} ${metrics.ddosDisconnectedTotal}`
|
|
935
|
+
];
|
|
936
|
+
return `${lines.join("\n")}
|
|
937
|
+
`;
|
|
938
|
+
}
|
|
848
939
|
async setAdapter(adapter) {
|
|
849
940
|
const previousAdapter = this.adapter;
|
|
850
941
|
if (previousAdapter === adapter) {
|
|
@@ -1153,6 +1244,35 @@ var SecureServer = class {
|
|
|
1153
1244
|
this.notifyError(normalizeToError(error, "Failed to close server."));
|
|
1154
1245
|
}
|
|
1155
1246
|
}
|
|
1247
|
+
recordHandshakeSuccess(resumed) {
|
|
1248
|
+
this.telemetryCounters.handshakeSuccessTotal += 1;
|
|
1249
|
+
if (resumed) {
|
|
1250
|
+
this.telemetryCounters.resumeHandshakeSuccessTotal += 1;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
recordHandshakeFailure(resumed) {
|
|
1254
|
+
this.telemetryCounters.handshakeFailureTotal += 1;
|
|
1255
|
+
if (resumed) {
|
|
1256
|
+
this.telemetryCounters.resumeHandshakeFailureTotal += 1;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
recordEncryptedMessageSent(byteLength) {
|
|
1260
|
+
this.telemetryCounters.encryptedMessagesSentTotal += 1;
|
|
1261
|
+
this.telemetryCounters.encryptedBytesSentTotal += Math.max(0, byteLength);
|
|
1262
|
+
}
|
|
1263
|
+
recordEncryptedMessageReceived(byteLength) {
|
|
1264
|
+
this.telemetryCounters.encryptedMessagesReceivedTotal += 1;
|
|
1265
|
+
this.telemetryCounters.encryptedBytesReceivedTotal += Math.max(0, byteLength);
|
|
1266
|
+
}
|
|
1267
|
+
recordDdosBlocked(disconnected) {
|
|
1268
|
+
this.telemetryCounters.ddosBlockedTotal += 1;
|
|
1269
|
+
if (disconnected) {
|
|
1270
|
+
this.telemetryCounters.ddosDisconnectedTotal += 1;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
recordDdosThrottled() {
|
|
1274
|
+
this.telemetryCounters.ddosThrottledTotal += 1;
|
|
1275
|
+
}
|
|
1156
1276
|
resolveHeartbeatConfig(heartbeatOptions) {
|
|
1157
1277
|
const intervalMs = heartbeatOptions?.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
1158
1278
|
const timeoutMs = heartbeatOptions?.timeoutMs ?? DEFAULT_HEARTBEAT_TIMEOUT_MS;
|
|
@@ -1582,6 +1702,7 @@ var SecureServer = class {
|
|
|
1582
1702
|
lastPingAt: 0
|
|
1583
1703
|
});
|
|
1584
1704
|
this.roomNamesByClientId.set(clientId, /* @__PURE__ */ new Set());
|
|
1705
|
+
this.telemetryCounters.totalConnections += 1;
|
|
1585
1706
|
socket.on("message", (rawData) => {
|
|
1586
1707
|
void this.handleIncomingMessage(client, rawData);
|
|
1587
1708
|
});
|
|
@@ -1610,6 +1731,7 @@ var SecureServer = class {
|
|
|
1610
1731
|
try {
|
|
1611
1732
|
const rateLimitDecision = this.evaluateIncomingRateLimit(client);
|
|
1612
1733
|
if (rateLimitDecision.shouldDisconnect) {
|
|
1734
|
+
this.recordDdosBlocked(true);
|
|
1613
1735
|
this.notifyError(
|
|
1614
1736
|
new Error(
|
|
1615
1737
|
`Rate limit disconnect triggered for client ${client.id}.`
|
|
@@ -1624,9 +1746,11 @@ var SecureServer = class {
|
|
|
1624
1746
|
return;
|
|
1625
1747
|
}
|
|
1626
1748
|
if (rateLimitDecision.shouldDrop) {
|
|
1749
|
+
this.recordDdosBlocked(false);
|
|
1627
1750
|
return;
|
|
1628
1751
|
}
|
|
1629
1752
|
if (rateLimitDecision.throttleDelayMs > 0) {
|
|
1753
|
+
this.recordDdosThrottled();
|
|
1630
1754
|
this.notifyError(
|
|
1631
1755
|
new Error(
|
|
1632
1756
|
`Rate limit throttle applied to client ${client.id} for ${rateLimitDecision.throttleDelayMs}ms.`
|
|
@@ -1667,6 +1791,7 @@ var SecureServer = class {
|
|
|
1667
1791
|
return;
|
|
1668
1792
|
}
|
|
1669
1793
|
let decryptedPayload;
|
|
1794
|
+
const encryptedPayloadByteLength = rawDataToBuffer(rawData).length;
|
|
1670
1795
|
try {
|
|
1671
1796
|
decryptedPayload = decryptSerializedEnvelope(rawData, encryptionKey);
|
|
1672
1797
|
} catch {
|
|
@@ -1674,6 +1799,7 @@ var SecureServer = class {
|
|
|
1674
1799
|
return;
|
|
1675
1800
|
}
|
|
1676
1801
|
const decryptedEnvelope = parseEnvelopeFromText(decryptedPayload);
|
|
1802
|
+
this.recordEncryptedMessageReceived(encryptedPayloadByteLength);
|
|
1677
1803
|
if (decryptedEnvelope.event === INTERNAL_RPC_RESPONSE_EVENT) {
|
|
1678
1804
|
this.handleRpcResponse(client.socket, decryptedEnvelope.data);
|
|
1679
1805
|
return;
|
|
@@ -2032,6 +2158,7 @@ var SecureServer = class {
|
|
|
2032
2158
|
try {
|
|
2033
2159
|
const serializedEnvelope = await serializeEnvelope(envelope.event, envelope.data);
|
|
2034
2160
|
const encryptedPayload = encryptSerializedEnvelope(serializedEnvelope, encryptionKey);
|
|
2161
|
+
this.recordEncryptedMessageSent(encryptedPayload.length);
|
|
2035
2162
|
socket.send(encryptedPayload);
|
|
2036
2163
|
} catch (error) {
|
|
2037
2164
|
const normalizedError = normalizeToError(error, "Failed to send encrypted server payload.");
|
|
@@ -2230,6 +2357,7 @@ var SecureServer = class {
|
|
|
2230
2357
|
}
|
|
2231
2358
|
handleResumeHandshake(client, payload) {
|
|
2232
2359
|
if (!this.sessionResumptionConfig.enabled) {
|
|
2360
|
+
this.recordHandshakeFailure(true);
|
|
2233
2361
|
this.sendResumeAck(client.socket, {
|
|
2234
2362
|
ok: false,
|
|
2235
2363
|
reason: "Session resumption is disabled."
|
|
@@ -2238,6 +2366,7 @@ var SecureServer = class {
|
|
|
2238
2366
|
}
|
|
2239
2367
|
const ticketRecord = this.getSessionTicket(payload.sessionId);
|
|
2240
2368
|
if (!ticketRecord) {
|
|
2369
|
+
this.recordHandshakeFailure(true);
|
|
2241
2370
|
this.sendResumeAck(client.socket, {
|
|
2242
2371
|
ok: false,
|
|
2243
2372
|
reason: "Session ticket is unknown or expired."
|
|
@@ -2264,6 +2393,7 @@ var SecureServer = class {
|
|
|
2264
2393
|
clientNonce
|
|
2265
2394
|
);
|
|
2266
2395
|
if (!equalsConstantTime(receivedProof, expectedProof)) {
|
|
2396
|
+
this.recordHandshakeFailure(true);
|
|
2267
2397
|
this.sendResumeAck(client.socket, {
|
|
2268
2398
|
ok: false,
|
|
2269
2399
|
reason: "Session resumption proof validation failed."
|
|
@@ -2284,6 +2414,7 @@ var SecureServer = class {
|
|
|
2284
2414
|
this.sharedSecretBySocket.set(client.socket, resumedKey);
|
|
2285
2415
|
this.encryptionKeyBySocket.set(client.socket, resumedKey);
|
|
2286
2416
|
handshakeState.isReady = true;
|
|
2417
|
+
this.recordHandshakeSuccess(true);
|
|
2287
2418
|
this.sendResumeAck(client.socket, {
|
|
2288
2419
|
ok: true,
|
|
2289
2420
|
sessionId: ticketRecord.sessionId,
|
|
@@ -2293,6 +2424,7 @@ var SecureServer = class {
|
|
|
2293
2424
|
this.notifyReady(client);
|
|
2294
2425
|
this.issueSessionTicket(client.socket, resumedKey);
|
|
2295
2426
|
} catch (error) {
|
|
2427
|
+
this.recordHandshakeFailure(true);
|
|
2296
2428
|
this.sendResumeAck(client.socket, {
|
|
2297
2429
|
ok: false,
|
|
2298
2430
|
reason: "Session resumption payload was invalid."
|
|
@@ -2323,10 +2455,12 @@ var SecureServer = class {
|
|
|
2323
2455
|
this.sharedSecretBySocket.set(client.socket, sharedSecret);
|
|
2324
2456
|
this.encryptionKeyBySocket.set(client.socket, encryptionKey);
|
|
2325
2457
|
handshakeState.isReady = true;
|
|
2458
|
+
this.recordHandshakeSuccess(false);
|
|
2326
2459
|
void this.flushQueuedPayloads(client.socket);
|
|
2327
2460
|
this.notifyReady(client);
|
|
2328
2461
|
this.issueSessionTicket(client.socket, encryptionKey);
|
|
2329
2462
|
} catch (error) {
|
|
2463
|
+
this.recordHandshakeFailure(false);
|
|
2330
2464
|
this.notifyError(normalizeToError(error, "Failed to complete server handshake."));
|
|
2331
2465
|
}
|
|
2332
2466
|
}
|