@floegence/flowersec-core 0.2.0 → 0.2.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.
|
@@ -12,8 +12,6 @@ import { isTunnelAttachCloseReason } from "./tunnelAttachCloseReason.js";
|
|
|
12
12
|
export async function connectCore(args) {
|
|
13
13
|
const observer = normalizeObserver(args.opts.observer);
|
|
14
14
|
const signal = args.opts.signal;
|
|
15
|
-
const connectTimeoutMs = args.opts.connectTimeoutMs ?? 10_000;
|
|
16
|
-
const handshakeTimeoutMs = args.opts.handshakeTimeoutMs ?? 10_000;
|
|
17
15
|
const connectStart = nowSeconds();
|
|
18
16
|
const origin = args.opts.origin;
|
|
19
17
|
if (origin == null || origin === "") {
|
|
@@ -23,6 +21,41 @@ export async function connectCore(args) {
|
|
|
23
21
|
const code = args.path === "tunnel" ? "missing_tunnel_url" : "missing_ws_url";
|
|
24
22
|
throw new FlowersecError({ path: args.path, stage: "validate", code, message: "missing websocket url" });
|
|
25
23
|
}
|
|
24
|
+
const invalidOption = (message) => {
|
|
25
|
+
throw new FlowersecError({ path: args.path, stage: "validate", code: "invalid_option", message });
|
|
26
|
+
};
|
|
27
|
+
const connectTimeoutMs = args.opts.connectTimeoutMs ?? 10_000;
|
|
28
|
+
if (!Number.isFinite(connectTimeoutMs) || connectTimeoutMs < 0) {
|
|
29
|
+
invalidOption("connectTimeoutMs must be a non-negative number");
|
|
30
|
+
}
|
|
31
|
+
const handshakeTimeoutMs = args.opts.handshakeTimeoutMs ?? 10_000;
|
|
32
|
+
if (!Number.isFinite(handshakeTimeoutMs) || handshakeTimeoutMs < 0) {
|
|
33
|
+
invalidOption("handshakeTimeoutMs must be a non-negative number");
|
|
34
|
+
}
|
|
35
|
+
const keepaliveIntervalMs = args.opts.keepaliveIntervalMs ?? 0;
|
|
36
|
+
if (!Number.isFinite(keepaliveIntervalMs) || keepaliveIntervalMs < 0) {
|
|
37
|
+
invalidOption("keepaliveIntervalMs must be a non-negative number");
|
|
38
|
+
}
|
|
39
|
+
const clientFeatures = args.opts.clientFeatures ?? 0;
|
|
40
|
+
if (!Number.isSafeInteger(clientFeatures) || clientFeatures < 0 || clientFeatures > 0xffffffff) {
|
|
41
|
+
invalidOption("clientFeatures must be a uint32");
|
|
42
|
+
}
|
|
43
|
+
const maxHandshakePayload = args.opts.maxHandshakePayload ?? 0;
|
|
44
|
+
if (!Number.isSafeInteger(maxHandshakePayload) || maxHandshakePayload < 0) {
|
|
45
|
+
invalidOption("maxHandshakePayload must be a non-negative integer");
|
|
46
|
+
}
|
|
47
|
+
const maxRecordBytes = args.opts.maxRecordBytes ?? 0;
|
|
48
|
+
if (!Number.isSafeInteger(maxRecordBytes) || maxRecordBytes < 0) {
|
|
49
|
+
invalidOption("maxRecordBytes must be a non-negative integer");
|
|
50
|
+
}
|
|
51
|
+
const maxBufferedBytes = args.opts.maxBufferedBytes ?? 0;
|
|
52
|
+
if (!Number.isSafeInteger(maxBufferedBytes) || maxBufferedBytes < 0) {
|
|
53
|
+
invalidOption("maxBufferedBytes must be a non-negative integer");
|
|
54
|
+
}
|
|
55
|
+
const maxWsQueuedBytes = args.opts.maxWsQueuedBytes ?? 0;
|
|
56
|
+
if (!Number.isSafeInteger(maxWsQueuedBytes) || maxWsQueuedBytes < 0) {
|
|
57
|
+
invalidOption("maxWsQueuedBytes must be a non-negative integer");
|
|
58
|
+
}
|
|
26
59
|
let ws;
|
|
27
60
|
try {
|
|
28
61
|
ws = createWebSocket(args.wsUrl, origin, args.opts.wsFactory);
|
|
@@ -39,7 +72,7 @@ export async function connectCore(args) {
|
|
|
39
72
|
// Install close/error/message listeners before waiting for "open" to avoid a gap where a peer close
|
|
40
73
|
// (for example a tunnel attach rejection with a reason token) can be missed and misclassified as a handshake timeout.
|
|
41
74
|
const transport = new WebSocketBinaryTransport(ws, {
|
|
42
|
-
...(
|
|
75
|
+
...(maxWsQueuedBytes > 0 ? { maxQueuedBytes: maxWsQueuedBytes } : {}),
|
|
43
76
|
observer,
|
|
44
77
|
});
|
|
45
78
|
try {
|
|
@@ -116,14 +149,11 @@ export async function connectCore(args) {
|
|
|
116
149
|
const handshakeStart = nowSeconds();
|
|
117
150
|
let secure;
|
|
118
151
|
try {
|
|
119
|
-
const maxHandshakePayload = args.opts.maxHandshakePayload ?? 0;
|
|
120
|
-
const maxRecordBytes = args.opts.maxRecordBytes ?? 0;
|
|
121
|
-
const maxBufferedBytes = args.opts.maxBufferedBytes ?? 0;
|
|
122
152
|
secure = await withAbortAndTimeout(clientHandshake(transport, {
|
|
123
153
|
channelId: args.channelId,
|
|
124
154
|
suite,
|
|
125
155
|
psk,
|
|
126
|
-
clientFeatures
|
|
156
|
+
clientFeatures,
|
|
127
157
|
maxHandshakePayload: maxHandshakePayload > 0 ? maxHandshakePayload : 8 * 1024,
|
|
128
158
|
maxRecordBytes: maxRecordBytes > 0 ? maxRecordBytes : (1 << 20),
|
|
129
159
|
...(maxBufferedBytes > 0 ? { maxBufferedBytes } : {}),
|
|
@@ -209,7 +239,6 @@ export async function connectCore(args) {
|
|
|
209
239
|
throw new FlowersecError({ path: args.path, stage: "secure", code: "ping_failed", message: "ping failed", cause: e });
|
|
210
240
|
}
|
|
211
241
|
};
|
|
212
|
-
const keepaliveIntervalMs = Math.max(0, args.opts.keepaliveIntervalMs ?? 0);
|
|
213
242
|
let keepaliveTimer;
|
|
214
243
|
let keepaliveInFlight = false;
|
|
215
244
|
const stopKeepalive = () => {
|
|
@@ -218,6 +247,27 @@ export async function connectCore(args) {
|
|
|
218
247
|
clearInterval(keepaliveTimer);
|
|
219
248
|
keepaliveTimer = undefined;
|
|
220
249
|
};
|
|
250
|
+
const closeAll = () => {
|
|
251
|
+
stopKeepalive();
|
|
252
|
+
try {
|
|
253
|
+
rpc.close();
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// ignore
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
mux.close();
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// ignore
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
secure.close();
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// ignore
|
|
269
|
+
}
|
|
270
|
+
};
|
|
221
271
|
if (keepaliveIntervalMs > 0) {
|
|
222
272
|
keepaliveTimer = setInterval(() => {
|
|
223
273
|
if (keepaliveInFlight)
|
|
@@ -225,12 +275,7 @@ export async function connectCore(args) {
|
|
|
225
275
|
keepaliveInFlight = true;
|
|
226
276
|
ping()
|
|
227
277
|
.catch(() => {
|
|
228
|
-
|
|
229
|
-
ws.close();
|
|
230
|
-
}
|
|
231
|
-
catch {
|
|
232
|
-
// ignore
|
|
233
|
-
}
|
|
278
|
+
closeAll();
|
|
234
279
|
})
|
|
235
280
|
.finally(() => {
|
|
236
281
|
keepaliveInFlight = false;
|
|
@@ -343,12 +388,7 @@ export async function connectCore(args) {
|
|
|
343
388
|
}
|
|
344
389
|
return s;
|
|
345
390
|
},
|
|
346
|
-
close:
|
|
347
|
-
stopKeepalive();
|
|
348
|
-
rpc.close();
|
|
349
|
-
mux.close();
|
|
350
|
-
secure.close();
|
|
351
|
-
},
|
|
391
|
+
close: closeAll,
|
|
352
392
|
};
|
|
353
393
|
}
|
|
354
394
|
catch (e) {
|
|
@@ -105,7 +105,7 @@ export async function connectTunnel(grant, opts) {
|
|
|
105
105
|
endpoint_instance_id: endpointInstanceId
|
|
106
106
|
};
|
|
107
107
|
const attachJson = JSON.stringify(attach);
|
|
108
|
-
const keepaliveIntervalMs = opts.keepaliveIntervalMs
|
|
108
|
+
const keepaliveIntervalMs = opts.keepaliveIntervalMs ?? defaultKeepaliveIntervalMs(idleTimeoutSeconds);
|
|
109
109
|
return await connectCore({
|
|
110
110
|
path: "tunnel",
|
|
111
111
|
wsUrl: checkedGrant.tunnel_url,
|
|
@@ -119,7 +119,13 @@ export async function connectTunnel(grant, opts) {
|
|
|
119
119
|
function defaultKeepaliveIntervalMs(idleTimeoutSeconds) {
|
|
120
120
|
if (!Number.isFinite(idleTimeoutSeconds) || idleTimeoutSeconds <= 0)
|
|
121
121
|
return 0;
|
|
122
|
-
const idleMs = idleTimeoutSeconds * 1000;
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
const idleMs = Math.floor(idleTimeoutSeconds * 1000);
|
|
123
|
+
if (idleMs <= 0)
|
|
124
|
+
return 0;
|
|
125
|
+
let interval = Math.floor(idleMs / 2);
|
|
126
|
+
if (interval < 500)
|
|
127
|
+
interval = 500;
|
|
128
|
+
if (interval >= idleMs)
|
|
129
|
+
interval = Math.floor(idleMs / 2);
|
|
130
|
+
return interval;
|
|
125
131
|
}
|