@livedesk/hub 0.1.27 → 0.1.29
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/agents/agent-manager.js +27 -11
- package/src/agents/codex-agent-runtime.js +325 -41
- package/src/frame-packet-contract.mjs +427 -0
- package/src/live-capture-transition-retry.mjs +297 -0
- package/src/mode4-atlas-pool.js +17 -3
- package/src/mode4-atlas-worker.js +134 -21
- package/src/mode4-atlas.js +180 -16
- package/src/remote-audio-subscription-contract.mjs +109 -0
- package/src/remote-audio-subscription-contract.test.mjs +72 -0
- package/src/remote-hub.js +6405 -4236
- package/src/server.js +3234 -2206
- package/src/transport/agent-binary-ingress.js +810 -0
- package/src/transport/relay-hub-control.js +388 -57
- package/src/transport/udp-hub-transport.js +222 -46
- package/src/transport/udp-protocol.js +302 -70
|
@@ -3,15 +3,19 @@ import { lookup } from 'node:dns/promises';
|
|
|
3
3
|
import dgram from 'node:dgram';
|
|
4
4
|
import {
|
|
5
5
|
UDP_MAX_DATAGRAM_BYTES,
|
|
6
|
+
UDP_FRAME_CHUNK_PROTOCOL,
|
|
6
7
|
UDP_P2P_PROTOCOL,
|
|
7
8
|
UdpFrameReassembler,
|
|
8
9
|
UdpReplayWindow,
|
|
9
|
-
decodeRendezvousMessage,
|
|
10
|
-
decodeUdpPacket,
|
|
10
|
+
decodeRendezvousMessage,
|
|
11
|
+
decodeUdpPacket,
|
|
11
12
|
encodeRendezvousMessage,
|
|
12
13
|
encodeUdpPacket,
|
|
13
|
-
createUdpSessionKey
|
|
14
|
-
|
|
14
|
+
createUdpSessionKey,
|
|
15
|
+
isUdpBinaryDatagram,
|
|
16
|
+
peekUdpSessionRouteKey,
|
|
17
|
+
udpSessionRouteKey
|
|
18
|
+
} from './udp-protocol.js';
|
|
15
19
|
|
|
16
20
|
function isEnabled(value, fallback = false) {
|
|
17
21
|
if (value === undefined || value === null || value === '') return fallback;
|
|
@@ -69,9 +73,21 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
69
73
|
const punchTimeoutMs = numberEnv(env.LIVEDESK_UDP_PUNCH_TIMEOUT_MS, 500, 10_000, 3000);
|
|
70
74
|
const frameTimeoutMs = numberEnv(env.LIVEDESK_UDP_FRAME_TIMEOUT_MS, 20, 2000, 500);
|
|
71
75
|
const maxPendingFrames = numberEnv(env.LIVEDESK_UDP_MAX_PENDING_FRAMES, 1, 16, 2);
|
|
72
|
-
const trace = isEnabled(env.LIVEDESK_UDP_TRACE, false);
|
|
73
|
-
const socket = dgram.createSocket('udp4');
|
|
74
|
-
const sessions = new Map();
|
|
76
|
+
const trace = isEnabled(env.LIVEDESK_UDP_TRACE, false);
|
|
77
|
+
const socket = dgram.createSocket('udp4');
|
|
78
|
+
const sessions = new Map();
|
|
79
|
+
const sessionsBySessionId = new Map();
|
|
80
|
+
const sessionsByRouteKey = new Map();
|
|
81
|
+
const resource = {
|
|
82
|
+
datagramsReceived: 0,
|
|
83
|
+
binaryDatagramsReceived: 0,
|
|
84
|
+
rendezvousJsonParseAttempts: 0,
|
|
85
|
+
binaryJsonParseAttempts: 0,
|
|
86
|
+
sessionLookupCount: 0,
|
|
87
|
+
linearSessionScanCount: 0,
|
|
88
|
+
authenticatedDatagrams: 0,
|
|
89
|
+
rejectedDatagrams: 0
|
|
90
|
+
};
|
|
75
91
|
let frameHandler = () => {};
|
|
76
92
|
let eventHandler = () => {};
|
|
77
93
|
let started = false;
|
|
@@ -80,6 +96,8 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
80
96
|
let rendezvousAddresses = new Set();
|
|
81
97
|
let rendezvousRefreshTimer = null;
|
|
82
98
|
let rendezvousRefreshPromise = null;
|
|
99
|
+
let frameExpiryTimer = null;
|
|
100
|
+
const peerProbeBurstDelaysMs = [0, 120, 360];
|
|
83
101
|
|
|
84
102
|
function emit(type, event = {}) {
|
|
85
103
|
eventHandler(type, event);
|
|
@@ -172,17 +190,31 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
172
190
|
rendezvousRefreshTimer = null;
|
|
173
191
|
}
|
|
174
192
|
|
|
193
|
+
function startFrameExpiryTimer() {
|
|
194
|
+
if (frameExpiryTimer) return;
|
|
195
|
+
frameExpiryTimer = setInterval(() => {
|
|
196
|
+
const now = Date.now();
|
|
197
|
+
for (const session of sessions.values()) {
|
|
198
|
+
session.reassembler.expire(now);
|
|
199
|
+
}
|
|
200
|
+
}, Math.max(20, Math.min(250, frameTimeoutMs)));
|
|
201
|
+
frameExpiryTimer.unref?.();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function stopFrameExpiryTimer() {
|
|
205
|
+
if (frameExpiryTimer) clearInterval(frameExpiryTimer);
|
|
206
|
+
frameExpiryTimer = null;
|
|
207
|
+
}
|
|
208
|
+
|
|
175
209
|
function isTrustedRendezvousSource(rinfo) {
|
|
176
210
|
return Number(rinfo?.port) === rendezvousPort
|
|
177
211
|
&& rendezvousAddresses.has(normalizedAddress(rinfo?.address));
|
|
178
212
|
}
|
|
179
213
|
|
|
180
214
|
function sessionForPacket(packet) {
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
const sessionId = packet.subarray(27, 27 + sessionLength).toString('utf8');
|
|
185
|
-
return [...sessions.values()].find(item => item.sessionId === sessionId) || null;
|
|
215
|
+
resource.sessionLookupCount += 1;
|
|
216
|
+
const routeKey = peekUdpSessionRouteKey(packet);
|
|
217
|
+
return routeKey === null ? null : sessionsByRouteKey.get(routeKey) || null;
|
|
186
218
|
}
|
|
187
219
|
|
|
188
220
|
function bindAuthenticatedClientEndpoint(session, rinfo) {
|
|
@@ -199,8 +231,35 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
199
231
|
return true;
|
|
200
232
|
}
|
|
201
233
|
|
|
234
|
+
function clearPeerProbeTimers(session) {
|
|
235
|
+
if (!session?.peerProbeTimers) return;
|
|
236
|
+
for (const timer of session.peerProbeTimers) clearTimeout(timer);
|
|
237
|
+
session.peerProbeTimers.clear();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function schedulePeerProbeBurst(session, endpoint) {
|
|
241
|
+
clearPeerProbeTimers(session);
|
|
242
|
+
for (const delayMs of peerProbeBurstDelaysMs) {
|
|
243
|
+
const sendProbe = () => {
|
|
244
|
+
if (sessions.get(session.deviceId) !== session
|
|
245
|
+
|| !sameEndpoint(session.peerEndpoint, endpoint)) return;
|
|
246
|
+
sendPacket(session, 'probe', { punch: true }, Buffer.alloc(0), endpoint);
|
|
247
|
+
};
|
|
248
|
+
if (delayMs === 0) {
|
|
249
|
+
sendProbe();
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const timer = setTimeout(() => {
|
|
253
|
+
session.peerProbeTimers.delete(timer);
|
|
254
|
+
sendProbe();
|
|
255
|
+
}, delayMs);
|
|
256
|
+
timer.unref?.();
|
|
257
|
+
session.peerProbeTimers.add(timer);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
202
261
|
function onRendezvousPeer(message, rinfo) {
|
|
203
|
-
const session =
|
|
262
|
+
const session = sessionsBySessionId.get(safeText(message.roomId, 160));
|
|
204
263
|
if (!session || message.protocol !== UDP_P2P_PROTOCOL || !isTrustedRendezvousSource(rinfo)) return;
|
|
205
264
|
const address = safeText(message.host, 128);
|
|
206
265
|
const port = Number(message.port);
|
|
@@ -217,22 +276,49 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
217
276
|
};
|
|
218
277
|
session.hubRendezvousObservedAt = Date.now();
|
|
219
278
|
}
|
|
220
|
-
session.peerEndpoint = { address, port };
|
|
279
|
+
session.peerEndpoint = { address, port };
|
|
221
280
|
session.lastPeerAt = Date.now();
|
|
222
|
-
|
|
281
|
+
schedulePeerProbeBurst(session, session.peerEndpoint);
|
|
223
282
|
emit('UdpPeerEndpointObserved', { deviceId: session.deviceId, endpoint: `${address}:${port}` });
|
|
224
283
|
}
|
|
225
284
|
|
|
226
|
-
socket.on('message', (payload, rinfo) => {
|
|
227
|
-
|
|
228
|
-
if (
|
|
229
|
-
|
|
285
|
+
socket.on('message', (payload, rinfo) => {
|
|
286
|
+
resource.datagramsReceived += 1;
|
|
287
|
+
if (!isUdpBinaryDatagram(payload)) {
|
|
288
|
+
// Rendezvous messages are the only JSON datagrams. Binary packets
|
|
289
|
+
// never pay a speculative JSON parse/exception before routing.
|
|
290
|
+
if (payload?.[0] !== 0x7b) {
|
|
291
|
+
resource.rejectedDatagrams += 1;
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
resource.rendezvousJsonParseAttempts += 1;
|
|
295
|
+
const rendezvous = decodeRendezvousMessage(payload);
|
|
296
|
+
if (rendezvous?.type === 'rendezvous.peer') {
|
|
297
|
+
onRendezvousPeer(rendezvous, rinfo);
|
|
298
|
+
} else {
|
|
299
|
+
resource.rejectedDatagrams += 1;
|
|
300
|
+
}
|
|
230
301
|
return;
|
|
231
302
|
}
|
|
303
|
+
resource.binaryDatagramsReceived += 1;
|
|
232
304
|
const session = sessionForPacket(payload);
|
|
233
|
-
if (!session)
|
|
234
|
-
|
|
235
|
-
|
|
305
|
+
if (!session) {
|
|
306
|
+
resource.rejectedDatagrams += 1;
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
session.resource.binaryDatagramsReceived += 1;
|
|
310
|
+
session.resource.decryptAttempts += 1;
|
|
311
|
+
const packet = decodeUdpPacket(payload, session.key, {
|
|
312
|
+
id: session.sessionId,
|
|
313
|
+
bytes: session.sessionIdBytes
|
|
314
|
+
});
|
|
315
|
+
if (!packet || packet.sessionId !== session.sessionId || !session.replay.accept(packet.sequence)) {
|
|
316
|
+
session.resource.rejectedDatagrams += 1;
|
|
317
|
+
resource.rejectedDatagrams += 1;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
resource.authenticatedDatagrams += 1;
|
|
321
|
+
session.resource.authenticatedDatagrams += 1;
|
|
236
322
|
bindAuthenticatedClientEndpoint(session, rinfo);
|
|
237
323
|
session.lastPacketAt = Date.now();
|
|
238
324
|
if (packet.type === 'probe') {
|
|
@@ -240,7 +326,8 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
240
326
|
emit('UdpProbeReceived', { deviceId: session.deviceId });
|
|
241
327
|
return;
|
|
242
328
|
}
|
|
243
|
-
if (packet.type === 'ready') {
|
|
329
|
+
if (packet.type === 'ready') {
|
|
330
|
+
clearPeerProbeTimers(session);
|
|
244
331
|
session.ready = true;
|
|
245
332
|
sendPacket(session, 'pong', { state: 'ready' });
|
|
246
333
|
session.sendControl?.({
|
|
@@ -288,6 +375,7 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
288
375
|
protocol: 'livedesk.frame-transport-proof.v1',
|
|
289
376
|
streamId: safeText(complete.header?.streamId, 128),
|
|
290
377
|
commandId: safeText(complete.header?.commandId, 128),
|
|
378
|
+
streamPurpose: safeText(complete.header?.streamPurpose, 24),
|
|
291
379
|
captureGeneration: Number(complete.header?.captureGeneration || 0) || 0,
|
|
292
380
|
monitorIndex: Number(complete.header?.monitorIndex ?? -1),
|
|
293
381
|
error: proofAccepted ? '' : safeText(handled?.error || 'frame-proof-rejected', 160)
|
|
@@ -300,6 +388,7 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
300
388
|
frameSeq: Number(complete.header?.frameSeq || 0) || 0,
|
|
301
389
|
streamId: safeText(complete.header?.streamId, 128),
|
|
302
390
|
commandId: safeText(complete.header?.commandId, 128),
|
|
391
|
+
streamPurpose: safeText(complete.header?.streamPurpose, 24),
|
|
303
392
|
captureGeneration: Number(complete.header?.captureGeneration || 0) || 0,
|
|
304
393
|
monitorIndex: Number(complete.header?.monitorIndex ?? -1)
|
|
305
394
|
});
|
|
@@ -327,19 +416,36 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
327
416
|
logEvent('udp', `UDP P2P socket listening on udp://${bindHost}:${boundPort}`);
|
|
328
417
|
void refreshRendezvousRegistrations('startup');
|
|
329
418
|
startRendezvousRefreshTimer();
|
|
419
|
+
startFrameExpiryTimer();
|
|
330
420
|
return getStatus();
|
|
331
421
|
}
|
|
332
422
|
|
|
333
|
-
function registerDevice({ deviceId, sessionId: tcpSessionId, sendControl, capabilities } = {}) {
|
|
334
|
-
if (!enabled || capabilities?.udpP2p !== true || !started) return null;
|
|
335
|
-
const id = safeText(deviceId, 160);
|
|
336
|
-
if (!id) return null;
|
|
337
|
-
sessions.get(id)
|
|
338
|
-
|
|
339
|
-
|
|
423
|
+
function registerDevice({ deviceId, sessionId: tcpSessionId, sendControl, capabilities } = {}) {
|
|
424
|
+
if (!enabled || capabilities?.udpP2p !== true || !started) return null;
|
|
425
|
+
const id = safeText(deviceId, 160);
|
|
426
|
+
if (!id) return null;
|
|
427
|
+
const previous = sessions.get(id);
|
|
428
|
+
previous?.sendControl?.({ type: 'udp.session.close', reason: 'replaced' });
|
|
429
|
+
if (previous) {
|
|
430
|
+
clearPeerProbeTimers(previous);
|
|
431
|
+
sessions.delete(id);
|
|
432
|
+
sessionsBySessionId.delete(previous.sessionId);
|
|
433
|
+
sessionsByRouteKey.delete(previous.routeKey);
|
|
434
|
+
previous.reassembler.clear();
|
|
435
|
+
}
|
|
436
|
+
const key = createUdpSessionKey();
|
|
437
|
+
let udpSessionId;
|
|
438
|
+
let routeKey;
|
|
439
|
+
do {
|
|
440
|
+
udpSessionId = crypto.randomUUID();
|
|
441
|
+
routeKey = udpSessionRouteKey(udpSessionId);
|
|
442
|
+
} while (sessionsByRouteKey.has(routeKey));
|
|
443
|
+
const session = {
|
|
340
444
|
deviceId: id,
|
|
341
445
|
tcpSessionId: safeText(tcpSessionId, 160),
|
|
342
|
-
sessionId:
|
|
446
|
+
sessionId: udpSessionId,
|
|
447
|
+
sessionIdBytes: Buffer.from(udpSessionId, 'utf8'),
|
|
448
|
+
routeKey,
|
|
343
449
|
key,
|
|
344
450
|
keyBase64: key.toString('base64'),
|
|
345
451
|
rendezvousToken: crypto.randomBytes(16).toString('hex'),
|
|
@@ -349,14 +455,23 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
349
455
|
hubRendezvousEndpoint: null,
|
|
350
456
|
hubRendezvousObservedAt: 0,
|
|
351
457
|
replay: new UdpReplayWindow(),
|
|
352
|
-
reassembler: new UdpFrameReassembler({ maxPending: maxPendingFrames, timeoutMs: frameTimeoutMs }),
|
|
458
|
+
reassembler: new UdpFrameReassembler({ maxPending: maxPendingFrames, timeoutMs: frameTimeoutMs }),
|
|
459
|
+
resource: {
|
|
460
|
+
binaryDatagramsReceived: 0,
|
|
461
|
+
decryptAttempts: 0,
|
|
462
|
+
authenticatedDatagrams: 0,
|
|
463
|
+
rejectedDatagrams: 0
|
|
464
|
+
},
|
|
353
465
|
ready: false,
|
|
466
|
+
peerProbeTimers: new Set(),
|
|
354
467
|
createdAt: Date.now(),
|
|
355
468
|
lastPacketAt: 0,
|
|
356
469
|
framesReceived: 0
|
|
357
|
-
};
|
|
358
|
-
sessions.set(id, session);
|
|
359
|
-
|
|
470
|
+
};
|
|
471
|
+
sessions.set(id, session);
|
|
472
|
+
sessionsBySessionId.set(session.sessionId, session);
|
|
473
|
+
sessionsByRouteKey.set(session.routeKey, session);
|
|
474
|
+
sendControl?.({
|
|
360
475
|
type: 'udp.session.offer',
|
|
361
476
|
protocol: UDP_P2P_PROTOCOL,
|
|
362
477
|
sessionId: session.sessionId,
|
|
@@ -365,8 +480,9 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
365
480
|
port: boundPort,
|
|
366
481
|
expiresInMs: punchTimeoutMs + 15_000,
|
|
367
482
|
punchTimeoutMs,
|
|
368
|
-
maxDatagramBytes: UDP_MAX_DATAGRAM_BYTES,
|
|
369
|
-
|
|
483
|
+
maxDatagramBytes: UDP_MAX_DATAGRAM_BYTES,
|
|
484
|
+
frameChunkHeader: UDP_FRAME_CHUNK_PROTOCOL,
|
|
485
|
+
frameTimeoutMs,
|
|
370
486
|
maxPendingFrames,
|
|
371
487
|
rendezvous: rendezvousHost
|
|
372
488
|
? { host: rendezvousHost, port: rendezvousPort, roomId: session.sessionId, token: session.rendezvousToken }
|
|
@@ -377,12 +493,16 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
377
493
|
return { sessionId: session.sessionId, ready: false };
|
|
378
494
|
}
|
|
379
495
|
|
|
380
|
-
function unregisterDevice(deviceId, tcpSessionId = '') {
|
|
496
|
+
function unregisterDevice(deviceId, tcpSessionId = '') {
|
|
381
497
|
const id = safeText(deviceId, 160);
|
|
382
498
|
const session = sessions.get(id);
|
|
383
|
-
if (!session || (tcpSessionId && session.tcpSessionId !== tcpSessionId)) return;
|
|
499
|
+
if (!session || (tcpSessionId && session.tcpSessionId !== tcpSessionId)) return;
|
|
500
|
+
clearPeerProbeTimers(session);
|
|
384
501
|
sessions.delete(id);
|
|
385
|
-
|
|
502
|
+
sessionsBySessionId.delete(session.sessionId);
|
|
503
|
+
sessionsByRouteKey.delete(session.routeKey);
|
|
504
|
+
session.reassembler.clear();
|
|
505
|
+
emit('UdpSessionClosed', { deviceId: id, reason: 'tcp-session-closed' });
|
|
386
506
|
}
|
|
387
507
|
|
|
388
508
|
function getDeviceStatus(deviceId) {
|
|
@@ -396,6 +516,12 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
396
516
|
sessionId: session.sessionId,
|
|
397
517
|
lastPacketAt: session.lastPacketAt ? new Date(session.lastPacketAt).toISOString() : '',
|
|
398
518
|
framesReceived: session.framesReceived,
|
|
519
|
+
resource: {
|
|
520
|
+
...session.resource,
|
|
521
|
+
binaryJsonParseAttempts: 0,
|
|
522
|
+
sessionLookup: 'map-o1',
|
|
523
|
+
linearSessionScanCount: 0
|
|
524
|
+
},
|
|
399
525
|
...session.reassembler.getStats()
|
|
400
526
|
};
|
|
401
527
|
}
|
|
@@ -467,7 +593,14 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
467
593
|
|
|
468
594
|
async function close() {
|
|
469
595
|
stopRendezvousRefreshTimer();
|
|
596
|
+
stopFrameExpiryTimer();
|
|
597
|
+
for (const session of sessions.values()) {
|
|
598
|
+
clearPeerProbeTimers(session);
|
|
599
|
+
session.reassembler.clear();
|
|
600
|
+
}
|
|
470
601
|
sessions.clear();
|
|
602
|
+
sessionsBySessionId.clear();
|
|
603
|
+
sessionsByRouteKey.clear();
|
|
471
604
|
if (!started) return;
|
|
472
605
|
started = false;
|
|
473
606
|
await new Promise(resolve => socket.close(() => resolve()));
|
|
@@ -486,8 +619,45 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
486
619
|
setEventHandler: handler => { eventHandler = typeof handler === 'function' ? handler : () => {}; }
|
|
487
620
|
};
|
|
488
621
|
|
|
489
|
-
function getStatus() {
|
|
490
|
-
|
|
622
|
+
function getStatus() {
|
|
623
|
+
let readySessionCount = 0;
|
|
624
|
+
const reassembly = {
|
|
625
|
+
pendingFrames: 0,
|
|
626
|
+
pendingBytes: 0,
|
|
627
|
+
pendingChunkBuffers: 0,
|
|
628
|
+
pendingBytesHighWatermark: 0,
|
|
629
|
+
pendingChunkBuffersHighWatermark: 0,
|
|
630
|
+
droppedFrames: 0,
|
|
631
|
+
completedFrames: 0,
|
|
632
|
+
completedPayloadCopyCount: 0,
|
|
633
|
+
completedPayloadCopyBytes: 0,
|
|
634
|
+
fullWireDuplicateCopyCount: 0,
|
|
635
|
+
binaryChunkHeaderCount: 0,
|
|
636
|
+
legacyChunkHeaderCount: 0
|
|
637
|
+
};
|
|
638
|
+
for (const session of sessions.values()) {
|
|
639
|
+
if (session.ready) readySessionCount += 1;
|
|
640
|
+
const stats = session.reassembler.getStats();
|
|
641
|
+
reassembly.pendingFrames += stats.pendingFrames;
|
|
642
|
+
reassembly.pendingBytes += stats.pendingBytes;
|
|
643
|
+
reassembly.pendingChunkBuffers += stats.pendingChunkBuffers;
|
|
644
|
+
reassembly.pendingBytesHighWatermark = Math.max(
|
|
645
|
+
reassembly.pendingBytesHighWatermark,
|
|
646
|
+
stats.pendingBytesHighWatermark
|
|
647
|
+
);
|
|
648
|
+
reassembly.pendingChunkBuffersHighWatermark = Math.max(
|
|
649
|
+
reassembly.pendingChunkBuffersHighWatermark,
|
|
650
|
+
stats.pendingChunkBuffersHighWatermark
|
|
651
|
+
);
|
|
652
|
+
reassembly.droppedFrames += stats.droppedFrames;
|
|
653
|
+
reassembly.completedFrames += stats.completedFrames;
|
|
654
|
+
reassembly.completedPayloadCopyCount += stats.completedPayloadCopyCount;
|
|
655
|
+
reassembly.completedPayloadCopyBytes += stats.completedPayloadCopyBytes;
|
|
656
|
+
reassembly.fullWireDuplicateCopyCount += stats.fullWireDuplicateCopyCount;
|
|
657
|
+
reassembly.binaryChunkHeaderCount += stats.binaryChunkHeaderCount;
|
|
658
|
+
reassembly.legacyChunkHeaderCount += stats.legacyChunkHeaderCount;
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
491
661
|
enabled,
|
|
492
662
|
preferred: preferP2p,
|
|
493
663
|
started,
|
|
@@ -498,9 +668,15 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
498
668
|
rendezvousPort,
|
|
499
669
|
rendezvousRefreshMs,
|
|
500
670
|
rendezvousResolvedAddressCount: rendezvousAddresses.size,
|
|
501
|
-
maxDatagramBytes: UDP_MAX_DATAGRAM_BYTES,
|
|
502
|
-
sessionCount: sessions.size,
|
|
503
|
-
readySessionCount
|
|
504
|
-
|
|
671
|
+
maxDatagramBytes: UDP_MAX_DATAGRAM_BYTES,
|
|
672
|
+
sessionCount: sessions.size,
|
|
673
|
+
readySessionCount,
|
|
674
|
+
resource: {
|
|
675
|
+
...resource,
|
|
676
|
+
indexedSessionCount: sessionsBySessionId.size,
|
|
677
|
+
indexedRouteCount: sessionsByRouteKey.size,
|
|
678
|
+
...reassembly
|
|
679
|
+
}
|
|
680
|
+
};
|
|
505
681
|
}
|
|
506
682
|
}
|