@replit/river 0.207.1 → 0.207.3
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/dist/adapter-f2b6e211.d.ts +46 -0
- package/dist/{chunk-7LMZNSVC.js → chunk-B7REV3ZV.js} +6 -5
- package/dist/{chunk-7LMZNSVC.js.map → chunk-B7REV3ZV.js.map} +1 -1
- package/dist/{chunk-QMAVXV4Z.js → chunk-BO7MFCO6.js} +1136 -132
- package/dist/chunk-BO7MFCO6.js.map +1 -0
- package/dist/{chunk-BCCZA7SX.js → chunk-QGPYCXV4.js} +2 -2
- package/dist/{chunk-BCCZA7SX.js.map → chunk-QGPYCXV4.js.map} +1 -1
- package/dist/codec/index.cjs +157 -23
- package/dist/codec/index.cjs.map +1 -1
- package/dist/codec/index.d.cts +5 -1
- package/dist/codec/index.d.ts +5 -1
- package/dist/codec/index.js +6 -20
- package/dist/codec/index.js.map +1 -1
- package/dist/{connection-933c87b2.d.ts → connection-06d72f2e.d.ts} +3 -2
- package/dist/index-02554794.d.ts +37 -0
- package/dist/logging/index.d.cts +2 -1
- package/dist/logging/index.d.ts +2 -1
- package/dist/{message-ffacb98a.d.ts → message-01c3e85a.d.ts} +1 -35
- package/dist/router/index.cjs +1 -1
- package/dist/router/index.cjs.map +1 -1
- package/dist/router/index.d.cts +6 -5
- package/dist/router/index.d.ts +6 -5
- package/dist/router/index.js +1 -1
- package/dist/{services-4cd29829.d.ts → services-87887bc5.d.ts} +16 -11
- package/dist/testUtil/index.cjs +992 -829
- package/dist/testUtil/index.cjs.map +1 -1
- package/dist/testUtil/index.d.cts +4 -3
- package/dist/testUtil/index.d.ts +4 -3
- package/dist/testUtil/index.js +18 -13
- package/dist/testUtil/index.js.map +1 -1
- package/dist/transport/impls/ws/client.cjs +293 -193
- package/dist/transport/impls/ws/client.cjs.map +1 -1
- package/dist/transport/impls/ws/client.d.cts +5 -4
- package/dist/transport/impls/ws/client.d.ts +5 -4
- package/dist/transport/impls/ws/client.js +5 -7
- package/dist/transport/impls/ws/client.js.map +1 -1
- package/dist/transport/impls/ws/server.cjs +230 -117
- package/dist/transport/impls/ws/server.cjs.map +1 -1
- package/dist/transport/impls/ws/server.d.cts +5 -4
- package/dist/transport/impls/ws/server.d.ts +5 -4
- package/dist/transport/impls/ws/server.js +5 -7
- package/dist/transport/impls/ws/server.js.map +1 -1
- package/dist/transport/index.cjs +408 -259
- package/dist/transport/index.cjs.map +1 -1
- package/dist/transport/index.d.cts +7 -6
- package/dist/transport/index.d.ts +7 -6
- package/dist/transport/index.js +4 -9
- package/package.json +1 -1
- package/dist/chunk-AJGIY2UB.js +0 -56
- package/dist/chunk-AJGIY2UB.js.map +0 -1
- package/dist/chunk-CRD3HDVN.js +0 -438
- package/dist/chunk-CRD3HDVN.js.map +0 -1
- package/dist/chunk-I27WBSMZ.js +0 -377
- package/dist/chunk-I27WBSMZ.js.map +0 -1
- package/dist/chunk-QMAVXV4Z.js.map +0 -1
- package/dist/types-3e5768ec.d.ts +0 -20
package/dist/testUtil/index.cjs
CHANGED
|
@@ -235,23 +235,20 @@ var NaiveJsonCodec = {
|
|
|
235
235
|
);
|
|
236
236
|
},
|
|
237
237
|
fromBuffer: (buff) => {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return val;
|
|
246
|
-
}
|
|
238
|
+
const parsed = JSON.parse(
|
|
239
|
+
decoder.decode(buff),
|
|
240
|
+
function reviver(_key, val) {
|
|
241
|
+
if (val?.$t) {
|
|
242
|
+
return base64ToUint8Array(val.$t);
|
|
243
|
+
} else {
|
|
244
|
+
return val;
|
|
247
245
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
} catch {
|
|
253
|
-
return null;
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
249
|
+
throw new Error("unpacked msg is not an object");
|
|
254
250
|
}
|
|
251
|
+
return parsed;
|
|
255
252
|
}
|
|
256
253
|
};
|
|
257
254
|
|
|
@@ -281,7 +278,6 @@ var defaultServerTransportOptions = {
|
|
|
281
278
|
};
|
|
282
279
|
|
|
283
280
|
// transport/sessionStateMachine/common.ts
|
|
284
|
-
var import_value = require("@sinclair/typebox/value");
|
|
285
281
|
var ERR_CONSUMED = `session state has been consumed and is no longer valid`;
|
|
286
282
|
var StateMachineState = class {
|
|
287
283
|
/*
|
|
@@ -341,34 +337,16 @@ var StateMachineState = class {
|
|
|
341
337
|
var CommonSession = class extends StateMachineState {
|
|
342
338
|
from;
|
|
343
339
|
options;
|
|
340
|
+
codec;
|
|
344
341
|
tracer;
|
|
345
342
|
log;
|
|
346
|
-
constructor({ from, options, log, tracer }) {
|
|
343
|
+
constructor({ from, options, log, tracer, codec }) {
|
|
347
344
|
super();
|
|
348
345
|
this.from = from;
|
|
349
346
|
this.options = options;
|
|
350
347
|
this.log = log;
|
|
351
348
|
this.tracer = tracer;
|
|
352
|
-
|
|
353
|
-
parseMsg(msg) {
|
|
354
|
-
const parsedMsg = this.options.codec.fromBuffer(msg);
|
|
355
|
-
if (parsedMsg === null) {
|
|
356
|
-
this.log?.error(
|
|
357
|
-
`received malformed msg: ${Buffer.from(msg).toString("base64")}`,
|
|
358
|
-
this.loggingMetadata
|
|
359
|
-
);
|
|
360
|
-
return null;
|
|
361
|
-
}
|
|
362
|
-
if (!import_value.Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {
|
|
363
|
-
this.log?.error(`received invalid msg: ${JSON.stringify(parsedMsg)}`, {
|
|
364
|
-
...this.loggingMetadata,
|
|
365
|
-
validationErrors: [
|
|
366
|
-
...import_value.Value.Errors(OpaqueTransportMessageSchema, parsedMsg)
|
|
367
|
-
]
|
|
368
|
-
});
|
|
369
|
-
return null;
|
|
370
|
-
}
|
|
371
|
-
return parsedMsg;
|
|
349
|
+
this.codec = codec;
|
|
372
350
|
}
|
|
373
351
|
};
|
|
374
352
|
var IdentifiedSession = class extends CommonSession {
|
|
@@ -428,9 +406,6 @@ var IdentifiedSession = class extends CommonSession {
|
|
|
428
406
|
return metadata;
|
|
429
407
|
}
|
|
430
408
|
constructMsg(partialMsg) {
|
|
431
|
-
if (this._isConsumed) {
|
|
432
|
-
throw new Error(ERR_CONSUMED);
|
|
433
|
-
}
|
|
434
409
|
const msg = {
|
|
435
410
|
...partialMsg,
|
|
436
411
|
id: generateId(),
|
|
@@ -448,7 +423,10 @@ var IdentifiedSession = class extends CommonSession {
|
|
|
448
423
|
send(msg) {
|
|
449
424
|
const constructedMsg = this.constructMsg(msg);
|
|
450
425
|
this.sendBuffer.push(constructedMsg);
|
|
451
|
-
return
|
|
426
|
+
return {
|
|
427
|
+
ok: true,
|
|
428
|
+
value: constructedMsg.id
|
|
429
|
+
};
|
|
452
430
|
}
|
|
453
431
|
_handleStateExit() {
|
|
454
432
|
}
|
|
@@ -480,6 +458,23 @@ var IdentifiedSessionWithGracePeriod = class extends IdentifiedSession {
|
|
|
480
458
|
super._handleClose();
|
|
481
459
|
}
|
|
482
460
|
};
|
|
461
|
+
function sendMessage(conn, codec, msg) {
|
|
462
|
+
const buff = codec.toBuffer(msg);
|
|
463
|
+
if (!buff.ok) {
|
|
464
|
+
return buff;
|
|
465
|
+
}
|
|
466
|
+
const sent = conn.send(buff.value);
|
|
467
|
+
if (!sent) {
|
|
468
|
+
return {
|
|
469
|
+
ok: false,
|
|
470
|
+
reason: "failed to send message"
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
ok: true,
|
|
475
|
+
value: msg.id
|
|
476
|
+
};
|
|
477
|
+
}
|
|
483
478
|
|
|
484
479
|
// transport/sessionStateMachine/SessionConnecting.ts
|
|
485
480
|
var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
|
|
@@ -532,8 +527,8 @@ var SessionConnecting = class extends IdentifiedSessionWithGracePeriod {
|
|
|
532
527
|
}
|
|
533
528
|
}
|
|
534
529
|
_handleClose() {
|
|
535
|
-
this.bestEffortClose();
|
|
536
530
|
super._handleClose();
|
|
531
|
+
this.bestEffortClose();
|
|
537
532
|
}
|
|
538
533
|
};
|
|
539
534
|
|
|
@@ -560,7 +555,7 @@ function coerceErrorString(err) {
|
|
|
560
555
|
}
|
|
561
556
|
|
|
562
557
|
// package.json
|
|
563
|
-
var version = "0.207.
|
|
558
|
+
var version = "0.207.3";
|
|
564
559
|
|
|
565
560
|
// tracing/index.ts
|
|
566
561
|
function getPropagationContext(ctx) {
|
|
@@ -632,18 +627,18 @@ var SessionWaitingForHandshake = class extends CommonSession {
|
|
|
632
627
|
};
|
|
633
628
|
}
|
|
634
629
|
onHandshakeData = (msg) => {
|
|
635
|
-
const
|
|
636
|
-
if (
|
|
630
|
+
const parsedMsgRes = this.codec.fromBuffer(msg);
|
|
631
|
+
if (!parsedMsgRes.ok) {
|
|
637
632
|
this.listeners.onInvalidHandshake(
|
|
638
|
-
|
|
633
|
+
`could not parse handshake message: ${parsedMsgRes.reason}`,
|
|
639
634
|
"MALFORMED_HANDSHAKE"
|
|
640
635
|
);
|
|
641
636
|
return;
|
|
642
637
|
}
|
|
643
|
-
this.listeners.onHandshake(
|
|
638
|
+
this.listeners.onHandshake(parsedMsgRes.value);
|
|
644
639
|
};
|
|
645
640
|
sendHandshake(msg) {
|
|
646
|
-
return this.conn
|
|
641
|
+
return sendMessage(this.conn, this.codec, msg);
|
|
647
642
|
}
|
|
648
643
|
_handleStateExit() {
|
|
649
644
|
this.conn.removeDataListener(this.onHandshakeData);
|
|
@@ -681,18 +676,18 @@ var SessionHandshaking = class extends IdentifiedSessionWithGracePeriod {
|
|
|
681
676
|
};
|
|
682
677
|
}
|
|
683
678
|
onHandshakeData = (msg) => {
|
|
684
|
-
const
|
|
685
|
-
if (
|
|
679
|
+
const parsedMsgRes = this.codec.fromBuffer(msg);
|
|
680
|
+
if (!parsedMsgRes.ok) {
|
|
686
681
|
this.listeners.onInvalidHandshake(
|
|
687
|
-
|
|
682
|
+
`could not parse handshake message: ${parsedMsgRes.reason}`,
|
|
688
683
|
"MALFORMED_HANDSHAKE"
|
|
689
684
|
);
|
|
690
685
|
return;
|
|
691
686
|
}
|
|
692
|
-
this.listeners.onHandshake(
|
|
687
|
+
this.listeners.onHandshake(parsedMsgRes.value);
|
|
693
688
|
};
|
|
694
689
|
sendHandshake(msg) {
|
|
695
|
-
return this.conn
|
|
690
|
+
return sendMessage(this.conn, this.codec, msg);
|
|
696
691
|
}
|
|
697
692
|
_handleStateExit() {
|
|
698
693
|
super._handleStateExit();
|
|
@@ -717,12 +712,15 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
717
712
|
conn;
|
|
718
713
|
listeners;
|
|
719
714
|
heartbeatHandle;
|
|
720
|
-
|
|
721
|
-
isActivelyHeartbeating;
|
|
715
|
+
heartbeatMissTimeout;
|
|
716
|
+
isActivelyHeartbeating = false;
|
|
722
717
|
updateBookkeeping(ack, seq) {
|
|
723
718
|
this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
|
|
724
719
|
this.ack = seq + 1;
|
|
725
|
-
this.
|
|
720
|
+
if (this.heartbeatMissTimeout) {
|
|
721
|
+
clearTimeout(this.heartbeatMissTimeout);
|
|
722
|
+
}
|
|
723
|
+
this.startMissingHeartbeatTimeout();
|
|
726
724
|
}
|
|
727
725
|
assertSendOrdering(constructedMsg) {
|
|
728
726
|
if (constructedMsg.seq > this.seqSent + 1) {
|
|
@@ -739,9 +737,13 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
739
737
|
const constructedMsg = this.constructMsg(msg);
|
|
740
738
|
this.assertSendOrdering(constructedMsg);
|
|
741
739
|
this.sendBuffer.push(constructedMsg);
|
|
742
|
-
this.conn
|
|
740
|
+
const res = sendMessage(this.conn, this.codec, constructedMsg);
|
|
741
|
+
if (!res.ok) {
|
|
742
|
+
this.listeners.onMessageSendFailure(constructedMsg, res.reason);
|
|
743
|
+
return res;
|
|
744
|
+
}
|
|
743
745
|
this.seqSent = constructedMsg.seq;
|
|
744
|
-
return
|
|
746
|
+
return res;
|
|
745
747
|
}
|
|
746
748
|
constructor(props) {
|
|
747
749
|
super(props);
|
|
@@ -750,6 +752,8 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
750
752
|
this.conn.addDataListener(this.onMessageData);
|
|
751
753
|
this.conn.addCloseListener(this.listeners.onConnectionClosed);
|
|
752
754
|
this.conn.addErrorListener(this.listeners.onConnectionErrored);
|
|
755
|
+
}
|
|
756
|
+
sendBufferedMessages() {
|
|
753
757
|
if (this.sendBuffer.length > 0) {
|
|
754
758
|
this.log?.info(
|
|
755
759
|
`sending ${this.sendBuffer.length} buffered messages, starting at seq ${this.nextSeq()}`,
|
|
@@ -757,30 +761,15 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
757
761
|
);
|
|
758
762
|
for (const msg of this.sendBuffer) {
|
|
759
763
|
this.assertSendOrdering(msg);
|
|
760
|
-
this.conn
|
|
764
|
+
const res = sendMessage(this.conn, this.codec, msg);
|
|
765
|
+
if (!res.ok) {
|
|
766
|
+
this.listeners.onMessageSendFailure(msg, res.reason);
|
|
767
|
+
return res;
|
|
768
|
+
}
|
|
761
769
|
this.seqSent = msg.seq;
|
|
762
770
|
}
|
|
763
771
|
}
|
|
764
|
-
|
|
765
|
-
this.heartbeatHandle = setInterval(() => {
|
|
766
|
-
const misses = this.heartbeatMisses;
|
|
767
|
-
const missDuration = misses * this.options.heartbeatIntervalMs;
|
|
768
|
-
if (misses >= this.options.heartbeatsUntilDead) {
|
|
769
|
-
this.log?.info(
|
|
770
|
-
`closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
|
|
771
|
-
this.loggingMetadata
|
|
772
|
-
);
|
|
773
|
-
this.telemetry.span.addEvent("closing connection due to inactivity");
|
|
774
|
-
this.conn.close();
|
|
775
|
-
clearInterval(this.heartbeatHandle);
|
|
776
|
-
this.heartbeatHandle = void 0;
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
if (this.isActivelyHeartbeating) {
|
|
780
|
-
this.sendHeartbeat();
|
|
781
|
-
}
|
|
782
|
-
this.heartbeatMisses++;
|
|
783
|
-
}, this.options.heartbeatIntervalMs);
|
|
772
|
+
return { ok: true, value: void 0 };
|
|
784
773
|
}
|
|
785
774
|
get loggingMetadata() {
|
|
786
775
|
return {
|
|
@@ -788,31 +777,46 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
788
777
|
...this.conn.loggingMetadata
|
|
789
778
|
};
|
|
790
779
|
}
|
|
780
|
+
startMissingHeartbeatTimeout() {
|
|
781
|
+
const maxMisses = this.options.heartbeatsUntilDead;
|
|
782
|
+
const missDuration = maxMisses * this.options.heartbeatIntervalMs;
|
|
783
|
+
this.heartbeatMissTimeout = setTimeout(() => {
|
|
784
|
+
this.log?.info(
|
|
785
|
+
`closing connection to ${this.to} due to inactivity (missed ${maxMisses} heartbeats which is ${missDuration}ms)`,
|
|
786
|
+
this.loggingMetadata
|
|
787
|
+
);
|
|
788
|
+
this.telemetry.span.addEvent(
|
|
789
|
+
"closing connection due to missing heartbeat"
|
|
790
|
+
);
|
|
791
|
+
this.conn.close();
|
|
792
|
+
}, missDuration);
|
|
793
|
+
}
|
|
791
794
|
startActiveHeartbeat() {
|
|
792
795
|
this.isActivelyHeartbeating = true;
|
|
796
|
+
this.heartbeatHandle = setInterval(() => {
|
|
797
|
+
this.sendHeartbeat();
|
|
798
|
+
}, this.options.heartbeatIntervalMs);
|
|
793
799
|
}
|
|
794
800
|
sendHeartbeat() {
|
|
795
801
|
this.log?.debug("sending heartbeat", this.loggingMetadata);
|
|
796
|
-
|
|
802
|
+
const heartbeat = {
|
|
797
803
|
streamId: "heartbeat",
|
|
798
804
|
controlFlags: 1 /* AckBit */,
|
|
799
805
|
payload: {
|
|
800
806
|
type: "ACK"
|
|
801
807
|
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
closeConnection() {
|
|
805
|
-
this.conn.removeDataListener(this.onMessageData);
|
|
806
|
-
this.conn.removeCloseListener(this.listeners.onConnectionClosed);
|
|
807
|
-
this.conn.removeErrorListener(this.listeners.onConnectionErrored);
|
|
808
|
-
this.conn.close();
|
|
808
|
+
};
|
|
809
|
+
this.send(heartbeat);
|
|
809
810
|
}
|
|
810
811
|
onMessageData = (msg) => {
|
|
811
|
-
const
|
|
812
|
-
if (
|
|
813
|
-
this.listeners.onInvalidMessage(
|
|
812
|
+
const parsedMsgRes = this.codec.fromBuffer(msg);
|
|
813
|
+
if (!parsedMsgRes.ok) {
|
|
814
|
+
this.listeners.onInvalidMessage(
|
|
815
|
+
`could not parse message: ${parsedMsgRes.reason}`
|
|
816
|
+
);
|
|
814
817
|
return;
|
|
815
818
|
}
|
|
819
|
+
const parsedMsg = parsedMsgRes.value;
|
|
816
820
|
if (parsedMsg.seq !== this.ack) {
|
|
817
821
|
if (parsedMsg.seq < this.ack) {
|
|
818
822
|
this.log?.debug(
|
|
@@ -833,7 +837,7 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
833
837
|
code: import_api2.SpanStatusCode.ERROR,
|
|
834
838
|
message: reason
|
|
835
839
|
});
|
|
836
|
-
this.
|
|
840
|
+
this.conn.close();
|
|
837
841
|
}
|
|
838
842
|
return;
|
|
839
843
|
}
|
|
@@ -851,9 +855,7 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
851
855
|
transportMessage: parsedMsg
|
|
852
856
|
});
|
|
853
857
|
if (!this.isActivelyHeartbeating) {
|
|
854
|
-
|
|
855
|
-
this.sendHeartbeat();
|
|
856
|
-
});
|
|
858
|
+
this.sendHeartbeat();
|
|
857
859
|
}
|
|
858
860
|
};
|
|
859
861
|
_handleStateExit() {
|
|
@@ -865,6 +867,10 @@ var SessionConnected = class extends IdentifiedSession {
|
|
|
865
867
|
clearInterval(this.heartbeatHandle);
|
|
866
868
|
this.heartbeatHandle = void 0;
|
|
867
869
|
}
|
|
870
|
+
if (this.heartbeatMissTimeout) {
|
|
871
|
+
clearTimeout(this.heartbeatMissTimeout);
|
|
872
|
+
this.heartbeatMissTimeout = void 0;
|
|
873
|
+
}
|
|
868
874
|
}
|
|
869
875
|
_handleClose() {
|
|
870
876
|
super._handleClose();
|
|
@@ -896,485 +902,108 @@ var SessionBackingOff = class extends IdentifiedSessionWithGracePeriod {
|
|
|
896
902
|
}
|
|
897
903
|
};
|
|
898
904
|
|
|
899
|
-
//
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
905
|
+
// codec/adapter.ts
|
|
906
|
+
var import_value3 = require("@sinclair/typebox/value");
|
|
907
|
+
|
|
908
|
+
// logging/log.ts
|
|
909
|
+
var import_api3 = require("@opentelemetry/api");
|
|
910
|
+
var LoggingLevels = {
|
|
911
|
+
debug: -1,
|
|
912
|
+
info: 0,
|
|
913
|
+
warn: 1,
|
|
914
|
+
error: 2
|
|
915
|
+
};
|
|
916
|
+
var cleanedLogFn = (log) => {
|
|
917
|
+
return (msg, metadata) => {
|
|
918
|
+
if (metadata && !metadata.telemetry) {
|
|
919
|
+
const span = import_api3.trace.getSpan(import_api3.context.active());
|
|
920
|
+
if (span) {
|
|
921
|
+
metadata.telemetry = {
|
|
922
|
+
traceId: span.spanContext().traceId,
|
|
923
|
+
spanId: span.spanContext().spanId
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (!metadata?.transportMessage) {
|
|
928
|
+
log(msg, metadata);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
const { payload, ...rest } = metadata.transportMessage;
|
|
932
|
+
metadata.transportMessage = rest;
|
|
933
|
+
log(msg, metadata);
|
|
920
934
|
};
|
|
921
|
-
}
|
|
922
|
-
var
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
to,
|
|
933
|
-
seq: 0,
|
|
934
|
-
ack: 0,
|
|
935
|
-
seqSent: 0,
|
|
936
|
-
graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,
|
|
937
|
-
sendBuffer,
|
|
938
|
-
telemetry,
|
|
939
|
-
options,
|
|
940
|
-
protocolVersion,
|
|
941
|
-
tracer,
|
|
942
|
-
log
|
|
943
|
-
});
|
|
944
|
-
session.log?.info(`session ${session.id} created in NoConnection state`, {
|
|
945
|
-
...session.loggingMetadata,
|
|
946
|
-
tags: ["state-transition"]
|
|
947
|
-
});
|
|
948
|
-
return session;
|
|
949
|
-
},
|
|
950
|
-
WaitingForHandshake: (from, conn, listeners, options, tracer, log) => {
|
|
951
|
-
const session = new SessionWaitingForHandshake({
|
|
952
|
-
conn,
|
|
953
|
-
listeners,
|
|
954
|
-
from,
|
|
955
|
-
options,
|
|
956
|
-
tracer,
|
|
957
|
-
log
|
|
958
|
-
});
|
|
959
|
-
session.log?.info(`session created in WaitingForHandshake state`, {
|
|
960
|
-
...session.loggingMetadata,
|
|
961
|
-
tags: ["state-transition"]
|
|
962
|
-
});
|
|
963
|
-
return session;
|
|
935
|
+
};
|
|
936
|
+
var BaseLogger = class {
|
|
937
|
+
minLevel;
|
|
938
|
+
output;
|
|
939
|
+
constructor(output, minLevel = "info") {
|
|
940
|
+
this.minLevel = minLevel;
|
|
941
|
+
this.output = output;
|
|
942
|
+
}
|
|
943
|
+
debug(msg, metadata) {
|
|
944
|
+
if (LoggingLevels[this.minLevel] <= LoggingLevels.debug) {
|
|
945
|
+
this.output(msg, metadata ?? {}, "debug");
|
|
964
946
|
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
// happy path transitions
|
|
970
|
-
NoConnectionToBackingOff: (oldSession, backoffMs, listeners) => {
|
|
971
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
972
|
-
oldSession._handleStateExit();
|
|
973
|
-
const session = new SessionBackingOff({
|
|
974
|
-
backoffMs,
|
|
975
|
-
listeners,
|
|
976
|
-
...carriedState
|
|
977
|
-
});
|
|
978
|
-
session.log?.info(
|
|
979
|
-
`session ${session.id} transition from NoConnection to BackingOff`,
|
|
980
|
-
{
|
|
981
|
-
...session.loggingMetadata,
|
|
982
|
-
tags: ["state-transition"]
|
|
983
|
-
}
|
|
984
|
-
);
|
|
985
|
-
return session;
|
|
986
|
-
},
|
|
987
|
-
BackingOffToConnecting: (oldSession, connPromise, listeners) => {
|
|
988
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
989
|
-
oldSession._handleStateExit();
|
|
990
|
-
const session = new SessionConnecting({
|
|
991
|
-
connPromise,
|
|
992
|
-
listeners,
|
|
993
|
-
...carriedState
|
|
994
|
-
});
|
|
995
|
-
session.log?.info(
|
|
996
|
-
`session ${session.id} transition from BackingOff to Connecting`,
|
|
997
|
-
{
|
|
998
|
-
...session.loggingMetadata,
|
|
999
|
-
tags: ["state-transition"]
|
|
1000
|
-
}
|
|
1001
|
-
);
|
|
1002
|
-
return session;
|
|
1003
|
-
},
|
|
1004
|
-
ConnectingToHandshaking: (oldSession, conn, listeners) => {
|
|
1005
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
1006
|
-
oldSession._handleStateExit();
|
|
1007
|
-
const session = new SessionHandshaking({
|
|
1008
|
-
conn,
|
|
1009
|
-
listeners,
|
|
1010
|
-
...carriedState
|
|
1011
|
-
});
|
|
1012
|
-
conn.telemetry = createConnectionTelemetryInfo(
|
|
1013
|
-
session.tracer,
|
|
1014
|
-
conn,
|
|
1015
|
-
session.telemetry
|
|
1016
|
-
);
|
|
1017
|
-
session.log?.info(
|
|
1018
|
-
`session ${session.id} transition from Connecting to Handshaking`,
|
|
1019
|
-
{
|
|
1020
|
-
...session.loggingMetadata,
|
|
1021
|
-
tags: ["state-transition"]
|
|
1022
|
-
}
|
|
1023
|
-
);
|
|
1024
|
-
return session;
|
|
1025
|
-
},
|
|
1026
|
-
HandshakingToConnected: (oldSession, listeners) => {
|
|
1027
|
-
const carriedState = inheritSharedSession(oldSession);
|
|
1028
|
-
const conn = oldSession.conn;
|
|
1029
|
-
oldSession._handleStateExit();
|
|
1030
|
-
const session = new SessionConnected({
|
|
1031
|
-
conn,
|
|
1032
|
-
listeners,
|
|
1033
|
-
...carriedState
|
|
1034
|
-
});
|
|
1035
|
-
session.log?.info(
|
|
1036
|
-
`session ${session.id} transition from Handshaking to Connected`,
|
|
1037
|
-
{
|
|
1038
|
-
...session.loggingMetadata,
|
|
1039
|
-
tags: ["state-transition"]
|
|
1040
|
-
}
|
|
1041
|
-
);
|
|
1042
|
-
return session;
|
|
1043
|
-
},
|
|
1044
|
-
WaitingForHandshakeToConnected: (pendingSession, oldSession, sessionId, to, propagationCtx, listeners, protocolVersion) => {
|
|
1045
|
-
const conn = pendingSession.conn;
|
|
1046
|
-
const { from, options } = pendingSession;
|
|
1047
|
-
const carriedState = oldSession ? (
|
|
1048
|
-
// old session exists, inherit state
|
|
1049
|
-
inheritSharedSession(oldSession)
|
|
1050
|
-
) : (
|
|
1051
|
-
// old session does not exist, create new state
|
|
1052
|
-
{
|
|
1053
|
-
id: sessionId,
|
|
1054
|
-
from,
|
|
1055
|
-
to,
|
|
1056
|
-
seq: 0,
|
|
1057
|
-
ack: 0,
|
|
1058
|
-
seqSent: 0,
|
|
1059
|
-
sendBuffer: [],
|
|
1060
|
-
telemetry: createSessionTelemetryInfo(
|
|
1061
|
-
pendingSession.tracer,
|
|
1062
|
-
sessionId,
|
|
1063
|
-
to,
|
|
1064
|
-
from,
|
|
1065
|
-
propagationCtx
|
|
1066
|
-
),
|
|
1067
|
-
options,
|
|
1068
|
-
tracer: pendingSession.tracer,
|
|
1069
|
-
log: pendingSession.log,
|
|
1070
|
-
protocolVersion
|
|
1071
|
-
}
|
|
1072
|
-
);
|
|
1073
|
-
pendingSession._handleStateExit();
|
|
1074
|
-
oldSession?._handleStateExit();
|
|
1075
|
-
const session = new SessionConnected({
|
|
1076
|
-
conn,
|
|
1077
|
-
listeners,
|
|
1078
|
-
...carriedState
|
|
1079
|
-
});
|
|
1080
|
-
conn.telemetry = createConnectionTelemetryInfo(
|
|
1081
|
-
session.tracer,
|
|
1082
|
-
conn,
|
|
1083
|
-
session.telemetry
|
|
1084
|
-
);
|
|
1085
|
-
session.log?.info(
|
|
1086
|
-
`session ${session.id} transition from WaitingForHandshake to Connected`,
|
|
1087
|
-
{
|
|
1088
|
-
...session.loggingMetadata,
|
|
1089
|
-
tags: ["state-transition"]
|
|
1090
|
-
}
|
|
1091
|
-
);
|
|
1092
|
-
return session;
|
|
1093
|
-
},
|
|
1094
|
-
// disconnect paths
|
|
1095
|
-
BackingOffToNoConnection: (oldSession, listeners) => {
|
|
1096
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
1097
|
-
oldSession._handleStateExit();
|
|
1098
|
-
const session = new SessionNoConnection({
|
|
1099
|
-
listeners,
|
|
1100
|
-
...carriedState
|
|
1101
|
-
});
|
|
1102
|
-
session.log?.info(
|
|
1103
|
-
`session ${session.id} transition from BackingOff to NoConnection`,
|
|
1104
|
-
{
|
|
1105
|
-
...session.loggingMetadata,
|
|
1106
|
-
tags: ["state-transition"]
|
|
1107
|
-
}
|
|
1108
|
-
);
|
|
1109
|
-
return session;
|
|
1110
|
-
},
|
|
1111
|
-
ConnectingToNoConnection: (oldSession, listeners) => {
|
|
1112
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
1113
|
-
oldSession.bestEffortClose();
|
|
1114
|
-
oldSession._handleStateExit();
|
|
1115
|
-
const session = new SessionNoConnection({
|
|
1116
|
-
listeners,
|
|
1117
|
-
...carriedState
|
|
1118
|
-
});
|
|
1119
|
-
session.log?.info(
|
|
1120
|
-
`session ${session.id} transition from Connecting to NoConnection`,
|
|
1121
|
-
{
|
|
1122
|
-
...session.loggingMetadata,
|
|
1123
|
-
tags: ["state-transition"]
|
|
1124
|
-
}
|
|
1125
|
-
);
|
|
1126
|
-
return session;
|
|
1127
|
-
},
|
|
1128
|
-
HandshakingToNoConnection: (oldSession, listeners) => {
|
|
1129
|
-
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
1130
|
-
oldSession.conn.close();
|
|
1131
|
-
oldSession._handleStateExit();
|
|
1132
|
-
const session = new SessionNoConnection({
|
|
1133
|
-
listeners,
|
|
1134
|
-
...carriedState
|
|
1135
|
-
});
|
|
1136
|
-
session.log?.info(
|
|
1137
|
-
`session ${session.id} transition from Handshaking to NoConnection`,
|
|
1138
|
-
{
|
|
1139
|
-
...session.loggingMetadata,
|
|
1140
|
-
tags: ["state-transition"]
|
|
1141
|
-
}
|
|
1142
|
-
);
|
|
1143
|
-
return session;
|
|
1144
|
-
},
|
|
1145
|
-
ConnectedToNoConnection: (oldSession, listeners) => {
|
|
1146
|
-
const carriedState = inheritSharedSession(oldSession);
|
|
1147
|
-
const graceExpiryTime = Date.now() + oldSession.options.sessionDisconnectGraceMs;
|
|
1148
|
-
oldSession.conn.close();
|
|
1149
|
-
oldSession._handleStateExit();
|
|
1150
|
-
const session = new SessionNoConnection({
|
|
1151
|
-
listeners,
|
|
1152
|
-
graceExpiryTime,
|
|
1153
|
-
...carriedState
|
|
1154
|
-
});
|
|
1155
|
-
session.log?.info(
|
|
1156
|
-
`session ${session.id} transition from Connected to NoConnection`,
|
|
1157
|
-
{
|
|
1158
|
-
...session.loggingMetadata,
|
|
1159
|
-
tags: ["state-transition"]
|
|
1160
|
-
}
|
|
1161
|
-
);
|
|
1162
|
-
return session;
|
|
947
|
+
}
|
|
948
|
+
info(msg, metadata) {
|
|
949
|
+
if (LoggingLevels[this.minLevel] <= LoggingLevels.info) {
|
|
950
|
+
this.output(msg, metadata ?? {}, "info");
|
|
1163
951
|
}
|
|
1164
952
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
transition: {
|
|
1170
|
-
// happy paths
|
|
1171
|
-
// NoConnection -> BackingOff: attempt to connect
|
|
1172
|
-
NoConnectionToBackingOff: transitions.NoConnectionToBackingOff,
|
|
1173
|
-
// BackingOff -> Connecting: backoff period elapsed, start connection
|
|
1174
|
-
BackingOffToConnecting: transitions.BackingOffToConnecting,
|
|
1175
|
-
// Connecting -> Handshaking: connection established, start handshake
|
|
1176
|
-
ConnectingToHandshaking: transitions.ConnectingToHandshaking,
|
|
1177
|
-
// Handshaking -> Connected: handshake complete, session ready
|
|
1178
|
-
HandshakingToConnected: transitions.HandshakingToConnected,
|
|
1179
|
-
// disconnect paths
|
|
1180
|
-
// BackingOff -> NoConnection: unused
|
|
1181
|
-
BackingOffToNoConnection: transitions.BackingOffToNoConnection,
|
|
1182
|
-
// Connecting -> NoConnection: connection failed or connection timeout
|
|
1183
|
-
ConnectingToNoConnection: transitions.ConnectingToNoConnection,
|
|
1184
|
-
// Handshaking -> NoConnection: connection closed or handshake timeout
|
|
1185
|
-
HandshakingToNoConnection: transitions.HandshakingToNoConnection,
|
|
1186
|
-
// Connected -> NoConnection: connection closed
|
|
1187
|
-
ConnectedToNoConnection: transitions.ConnectedToNoConnection
|
|
1188
|
-
// destroy/close paths
|
|
1189
|
-
// NoConnection -> x: grace period elapsed
|
|
1190
|
-
// BackingOff -> x: grace period elapsed
|
|
1191
|
-
// Connecting -> x: grace period elapsed
|
|
1192
|
-
// Handshaking -> x: grace period elapsed or invalid handshake message or handshake rejection
|
|
1193
|
-
// Connected -> x: grace period elapsed or invalid message
|
|
953
|
+
warn(msg, metadata) {
|
|
954
|
+
if (LoggingLevels[this.minLevel] <= LoggingLevels.warn) {
|
|
955
|
+
this.output(msg, metadata ?? {}, "warn");
|
|
956
|
+
}
|
|
1194
957
|
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
// happy paths
|
|
1200
|
-
// WaitingForHandshake -> Connected: handshake complete, session ready
|
|
1201
|
-
WaitingForHandshakeToConnected: transitions.WaitingForHandshakeToConnected,
|
|
1202
|
-
// disconnect paths
|
|
1203
|
-
// Connected -> NoConnection: connection closed
|
|
1204
|
-
ConnectedToNoConnection: transitions.ConnectedToNoConnection
|
|
1205
|
-
// destroy/close paths
|
|
1206
|
-
// WaitingForHandshake -> x: handshake timeout elapsed or invalid handshake message or handshake rejection or connection closed
|
|
958
|
+
error(msg, metadata) {
|
|
959
|
+
if (LoggingLevels[this.minLevel] <= LoggingLevels.error) {
|
|
960
|
+
this.output(msg, metadata ?? {}, "error");
|
|
961
|
+
}
|
|
1207
962
|
}
|
|
1208
963
|
};
|
|
964
|
+
var createLogProxy = (log) => ({
|
|
965
|
+
debug: cleanedLogFn(log.debug.bind(log)),
|
|
966
|
+
info: cleanedLogFn(log.info.bind(log)),
|
|
967
|
+
warn: cleanedLogFn(log.warn.bind(log)),
|
|
968
|
+
error: cleanedLogFn(log.error.bind(log))
|
|
969
|
+
});
|
|
1209
970
|
|
|
1210
|
-
// transport/
|
|
1211
|
-
var
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
getBackoffMs() {
|
|
1223
|
-
if (this.getBudgetConsumed() === 0) {
|
|
1224
|
-
return 0;
|
|
1225
|
-
}
|
|
1226
|
-
const exponent = Math.max(0, this.getBudgetConsumed() - 1);
|
|
1227
|
-
const jitter = Math.floor(Math.random() * this.options.maxJitterMs);
|
|
1228
|
-
const backoffMs = Math.min(
|
|
1229
|
-
this.options.baseIntervalMs * 2 ** exponent,
|
|
1230
|
-
this.options.maxBackoffMs
|
|
1231
|
-
);
|
|
1232
|
-
return backoffMs + jitter;
|
|
1233
|
-
}
|
|
1234
|
-
get totalBudgetRestoreTime() {
|
|
1235
|
-
return this.options.budgetRestoreIntervalMs * this.options.attemptBudgetCapacity;
|
|
1236
|
-
}
|
|
1237
|
-
consumeBudget() {
|
|
1238
|
-
this.stopLeak();
|
|
1239
|
-
this.budgetConsumed = this.getBudgetConsumed() + 1;
|
|
1240
|
-
}
|
|
1241
|
-
getBudgetConsumed() {
|
|
1242
|
-
return this.budgetConsumed;
|
|
971
|
+
// transport/events.ts
|
|
972
|
+
var ProtocolError = {
|
|
973
|
+
RetriesExceeded: "conn_retry_exceeded",
|
|
974
|
+
HandshakeFailed: "handshake_failed",
|
|
975
|
+
MessageOrderingViolated: "message_ordering_violated",
|
|
976
|
+
InvalidMessage: "invalid_message",
|
|
977
|
+
MessageSendFailure: "message_send_failure"
|
|
978
|
+
};
|
|
979
|
+
var EventDispatcher = class {
|
|
980
|
+
eventListeners = {};
|
|
981
|
+
removeAllListeners() {
|
|
982
|
+
this.eventListeners = {};
|
|
1243
983
|
}
|
|
1244
|
-
|
|
1245
|
-
return this.
|
|
984
|
+
numberOfListeners(eventType) {
|
|
985
|
+
return this.eventListeners[eventType]?.size ?? 0;
|
|
1246
986
|
}
|
|
1247
|
-
|
|
1248
|
-
if (this.
|
|
1249
|
-
|
|
987
|
+
addEventListener(eventType, handler) {
|
|
988
|
+
if (!this.eventListeners[eventType]) {
|
|
989
|
+
this.eventListeners[eventType] = /* @__PURE__ */ new Set();
|
|
1250
990
|
}
|
|
1251
|
-
|
|
1252
|
-
const currentBudget = this.budgetConsumed;
|
|
1253
|
-
if (!currentBudget) {
|
|
1254
|
-
this.stopLeak();
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
const newBudget = currentBudget - 1;
|
|
1258
|
-
if (newBudget === 0) {
|
|
1259
|
-
return;
|
|
1260
|
-
}
|
|
1261
|
-
this.budgetConsumed = newBudget;
|
|
1262
|
-
};
|
|
1263
|
-
this.intervalHandle = setInterval(
|
|
1264
|
-
restoreBudgetForUser,
|
|
1265
|
-
this.options.budgetRestoreIntervalMs
|
|
1266
|
-
);
|
|
991
|
+
this.eventListeners[eventType]?.add(handler);
|
|
1267
992
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
993
|
+
removeEventListener(eventType, handler) {
|
|
994
|
+
const handlers = this.eventListeners[eventType];
|
|
995
|
+
if (handlers) {
|
|
996
|
+
this.eventListeners[eventType]?.delete(handler);
|
|
1271
997
|
}
|
|
1272
|
-
clearInterval(this.intervalHandle);
|
|
1273
|
-
this.intervalHandle = void 0;
|
|
1274
998
|
}
|
|
1275
|
-
|
|
1276
|
-
this.
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
debug: -1,
|
|
1284
|
-
info: 0,
|
|
1285
|
-
warn: 1,
|
|
1286
|
-
error: 2
|
|
1287
|
-
};
|
|
1288
|
-
var cleanedLogFn = (log) => {
|
|
1289
|
-
return (msg, metadata) => {
|
|
1290
|
-
if (metadata && !metadata.telemetry) {
|
|
1291
|
-
const span = import_api3.trace.getSpan(import_api3.context.active());
|
|
1292
|
-
if (span) {
|
|
1293
|
-
metadata.telemetry = {
|
|
1294
|
-
traceId: span.spanContext().traceId,
|
|
1295
|
-
spanId: span.spanContext().spanId
|
|
1296
|
-
};
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
if (!metadata?.transportMessage) {
|
|
1300
|
-
log(msg, metadata);
|
|
1301
|
-
return;
|
|
1302
|
-
}
|
|
1303
|
-
const { payload, ...rest } = metadata.transportMessage;
|
|
1304
|
-
metadata.transportMessage = rest;
|
|
1305
|
-
log(msg, metadata);
|
|
1306
|
-
};
|
|
1307
|
-
};
|
|
1308
|
-
var BaseLogger = class {
|
|
1309
|
-
minLevel;
|
|
1310
|
-
output;
|
|
1311
|
-
constructor(output, minLevel = "info") {
|
|
1312
|
-
this.minLevel = minLevel;
|
|
1313
|
-
this.output = output;
|
|
1314
|
-
}
|
|
1315
|
-
debug(msg, metadata) {
|
|
1316
|
-
if (LoggingLevels[this.minLevel] <= LoggingLevels.debug) {
|
|
1317
|
-
this.output(msg, metadata ?? {}, "debug");
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
info(msg, metadata) {
|
|
1321
|
-
if (LoggingLevels[this.minLevel] <= LoggingLevels.info) {
|
|
1322
|
-
this.output(msg, metadata ?? {}, "info");
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
warn(msg, metadata) {
|
|
1326
|
-
if (LoggingLevels[this.minLevel] <= LoggingLevels.warn) {
|
|
1327
|
-
this.output(msg, metadata ?? {}, "warn");
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
error(msg, metadata) {
|
|
1331
|
-
if (LoggingLevels[this.minLevel] <= LoggingLevels.error) {
|
|
1332
|
-
this.output(msg, metadata ?? {}, "error");
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
};
|
|
1336
|
-
var createLogProxy = (log) => ({
|
|
1337
|
-
debug: cleanedLogFn(log.debug.bind(log)),
|
|
1338
|
-
info: cleanedLogFn(log.info.bind(log)),
|
|
1339
|
-
warn: cleanedLogFn(log.warn.bind(log)),
|
|
1340
|
-
error: cleanedLogFn(log.error.bind(log))
|
|
1341
|
-
});
|
|
1342
|
-
|
|
1343
|
-
// transport/events.ts
|
|
1344
|
-
var ProtocolError = {
|
|
1345
|
-
RetriesExceeded: "conn_retry_exceeded",
|
|
1346
|
-
HandshakeFailed: "handshake_failed",
|
|
1347
|
-
MessageOrderingViolated: "message_ordering_violated",
|
|
1348
|
-
InvalidMessage: "invalid_message"
|
|
1349
|
-
};
|
|
1350
|
-
var EventDispatcher = class {
|
|
1351
|
-
eventListeners = {};
|
|
1352
|
-
removeAllListeners() {
|
|
1353
|
-
this.eventListeners = {};
|
|
1354
|
-
}
|
|
1355
|
-
numberOfListeners(eventType) {
|
|
1356
|
-
return this.eventListeners[eventType]?.size ?? 0;
|
|
1357
|
-
}
|
|
1358
|
-
addEventListener(eventType, handler) {
|
|
1359
|
-
if (!this.eventListeners[eventType]) {
|
|
1360
|
-
this.eventListeners[eventType] = /* @__PURE__ */ new Set();
|
|
1361
|
-
}
|
|
1362
|
-
this.eventListeners[eventType]?.add(handler);
|
|
1363
|
-
}
|
|
1364
|
-
removeEventListener(eventType, handler) {
|
|
1365
|
-
const handlers = this.eventListeners[eventType];
|
|
1366
|
-
if (handlers) {
|
|
1367
|
-
this.eventListeners[eventType]?.delete(handler);
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
dispatchEvent(eventType, event) {
|
|
1371
|
-
const handlers = this.eventListeners[eventType];
|
|
1372
|
-
if (handlers) {
|
|
1373
|
-
const copy = [...handlers];
|
|
1374
|
-
for (const handler of copy) {
|
|
1375
|
-
handler(event);
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
999
|
+
dispatchEvent(eventType, event) {
|
|
1000
|
+
const handlers = this.eventListeners[eventType];
|
|
1001
|
+
if (handlers) {
|
|
1002
|
+
const copy = [...handlers];
|
|
1003
|
+
for (const handler of copy) {
|
|
1004
|
+
handler(event);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1378
1007
|
}
|
|
1379
1008
|
};
|
|
1380
1009
|
|
|
@@ -1588,18 +1217,92 @@ var Transport = class {
|
|
|
1588
1217
|
);
|
|
1589
1218
|
}
|
|
1590
1219
|
const sameSession = session.id === sessionId;
|
|
1591
|
-
if (!sameSession) {
|
|
1220
|
+
if (!sameSession || session._isConsumed) {
|
|
1592
1221
|
throw new Error(
|
|
1593
1222
|
`session scope for ${sessionId} has ended (transition), can't send`
|
|
1594
1223
|
);
|
|
1595
1224
|
}
|
|
1596
|
-
|
|
1225
|
+
const res = session.send(msg);
|
|
1226
|
+
if (!res.ok) {
|
|
1227
|
+
throw new Error(res.reason);
|
|
1228
|
+
}
|
|
1229
|
+
return res.value;
|
|
1597
1230
|
};
|
|
1598
1231
|
}
|
|
1599
1232
|
};
|
|
1600
1233
|
|
|
1601
1234
|
// transport/client.ts
|
|
1602
|
-
var
|
|
1235
|
+
var import_api4 = require("@opentelemetry/api");
|
|
1236
|
+
|
|
1237
|
+
// transport/rateLimit.ts
|
|
1238
|
+
var LeakyBucketRateLimit = class {
|
|
1239
|
+
budgetConsumed;
|
|
1240
|
+
intervalHandle;
|
|
1241
|
+
options;
|
|
1242
|
+
constructor(options) {
|
|
1243
|
+
this.options = options;
|
|
1244
|
+
this.budgetConsumed = 0;
|
|
1245
|
+
}
|
|
1246
|
+
getBackoffMs() {
|
|
1247
|
+
if (this.getBudgetConsumed() === 0) {
|
|
1248
|
+
return 0;
|
|
1249
|
+
}
|
|
1250
|
+
const exponent = Math.max(0, this.getBudgetConsumed() - 1);
|
|
1251
|
+
const jitter = Math.floor(Math.random() * this.options.maxJitterMs);
|
|
1252
|
+
const backoffMs = Math.min(
|
|
1253
|
+
this.options.baseIntervalMs * 2 ** exponent,
|
|
1254
|
+
this.options.maxBackoffMs
|
|
1255
|
+
);
|
|
1256
|
+
return backoffMs + jitter;
|
|
1257
|
+
}
|
|
1258
|
+
get totalBudgetRestoreTime() {
|
|
1259
|
+
return this.options.budgetRestoreIntervalMs * this.options.attemptBudgetCapacity;
|
|
1260
|
+
}
|
|
1261
|
+
consumeBudget() {
|
|
1262
|
+
this.stopLeak();
|
|
1263
|
+
this.budgetConsumed = this.getBudgetConsumed() + 1;
|
|
1264
|
+
}
|
|
1265
|
+
getBudgetConsumed() {
|
|
1266
|
+
return this.budgetConsumed;
|
|
1267
|
+
}
|
|
1268
|
+
hasBudget() {
|
|
1269
|
+
return this.getBudgetConsumed() < this.options.attemptBudgetCapacity;
|
|
1270
|
+
}
|
|
1271
|
+
startRestoringBudget() {
|
|
1272
|
+
if (this.intervalHandle) {
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
const restoreBudgetForUser = () => {
|
|
1276
|
+
const currentBudget = this.budgetConsumed;
|
|
1277
|
+
if (!currentBudget) {
|
|
1278
|
+
this.stopLeak();
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
const newBudget = currentBudget - 1;
|
|
1282
|
+
if (newBudget === 0) {
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
this.budgetConsumed = newBudget;
|
|
1286
|
+
};
|
|
1287
|
+
this.intervalHandle = setInterval(
|
|
1288
|
+
restoreBudgetForUser,
|
|
1289
|
+
this.options.budgetRestoreIntervalMs
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
stopLeak() {
|
|
1293
|
+
if (!this.intervalHandle) {
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
clearInterval(this.intervalHandle);
|
|
1297
|
+
this.intervalHandle = void 0;
|
|
1298
|
+
}
|
|
1299
|
+
close() {
|
|
1300
|
+
this.stopLeak();
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1304
|
+
// transport/client.ts
|
|
1305
|
+
var import_value = require("@sinclair/typebox/value");
|
|
1603
1306
|
var ClientTransport = class extends Transport {
|
|
1604
1307
|
/**
|
|
1605
1308
|
* The options for this transport.
|
|
@@ -1730,19 +1433,19 @@ var ClientTransport = class extends Transport {
|
|
|
1730
1433
|
this.deleteSession(session, { unhealthy: true });
|
|
1731
1434
|
}
|
|
1732
1435
|
onHandshakeResponse(session, msg) {
|
|
1733
|
-
if (!
|
|
1436
|
+
if (!import_value.Value.Check(ControlMessageHandshakeResponseSchema, msg.payload)) {
|
|
1734
1437
|
const reason = `received invalid handshake response`;
|
|
1735
1438
|
this.rejectHandshakeResponse(session, reason, {
|
|
1736
1439
|
...session.loggingMetadata,
|
|
1737
1440
|
transportMessage: msg,
|
|
1738
1441
|
validationErrors: [
|
|
1739
|
-
...
|
|
1442
|
+
...import_value.Value.Errors(ControlMessageHandshakeResponseSchema, msg.payload)
|
|
1740
1443
|
]
|
|
1741
1444
|
});
|
|
1742
1445
|
return;
|
|
1743
1446
|
}
|
|
1744
1447
|
if (!msg.payload.status.ok) {
|
|
1745
|
-
const retriable =
|
|
1448
|
+
const retriable = import_value.Value.Check(
|
|
1746
1449
|
HandshakeErrorRetriableResponseCodes,
|
|
1747
1450
|
msg.payload.status.code
|
|
1748
1451
|
);
|
|
@@ -1794,13 +1497,41 @@ var ClientTransport = class extends Transport {
|
|
|
1794
1497
|
this.handleMsg(msg2);
|
|
1795
1498
|
},
|
|
1796
1499
|
onInvalidMessage: (reason) => {
|
|
1797
|
-
this.
|
|
1500
|
+
this.log?.error(`invalid message: ${reason}`, {
|
|
1501
|
+
...connectedSession.loggingMetadata,
|
|
1502
|
+
transportMessage: msg
|
|
1503
|
+
});
|
|
1798
1504
|
this.protocolError({
|
|
1799
1505
|
type: ProtocolError.InvalidMessage,
|
|
1800
1506
|
message: reason
|
|
1801
1507
|
});
|
|
1508
|
+
this.deleteSession(connectedSession, { unhealthy: true });
|
|
1509
|
+
},
|
|
1510
|
+
onMessageSendFailure: (msg2, reason) => {
|
|
1511
|
+
this.log?.error(`failed to send message: ${reason}`, {
|
|
1512
|
+
...connectedSession.loggingMetadata,
|
|
1513
|
+
transportMessage: msg2
|
|
1514
|
+
});
|
|
1515
|
+
this.protocolError({
|
|
1516
|
+
type: ProtocolError.MessageSendFailure,
|
|
1517
|
+
message: reason
|
|
1518
|
+
});
|
|
1519
|
+
this.deleteSession(connectedSession, { unhealthy: true });
|
|
1802
1520
|
}
|
|
1803
1521
|
});
|
|
1522
|
+
const res = connectedSession.sendBufferedMessages();
|
|
1523
|
+
if (!res.ok) {
|
|
1524
|
+
this.log?.error(`failed to send buffered messages: ${res.reason}`, {
|
|
1525
|
+
...connectedSession.loggingMetadata,
|
|
1526
|
+
transportMessage: msg
|
|
1527
|
+
});
|
|
1528
|
+
this.protocolError({
|
|
1529
|
+
type: ProtocolError.MessageSendFailure,
|
|
1530
|
+
message: res.reason
|
|
1531
|
+
});
|
|
1532
|
+
this.deleteSession(connectedSession, { unhealthy: true });
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1804
1535
|
this.updateSession(connectedSession);
|
|
1805
1536
|
this.retryBudget.startRestoringBudget();
|
|
1806
1537
|
}
|
|
@@ -1939,7 +1670,18 @@ var ClientTransport = class extends Transport {
|
|
|
1939
1670
|
...session.loggingMetadata,
|
|
1940
1671
|
transportMessage: requestMsg
|
|
1941
1672
|
});
|
|
1942
|
-
session.sendHandshake(requestMsg);
|
|
1673
|
+
const res = session.sendHandshake(requestMsg);
|
|
1674
|
+
if (!res.ok) {
|
|
1675
|
+
this.log?.error(`failed to send handshake request: ${res.reason}`, {
|
|
1676
|
+
...session.loggingMetadata,
|
|
1677
|
+
transportMessage: requestMsg
|
|
1678
|
+
});
|
|
1679
|
+
this.protocolError({
|
|
1680
|
+
type: ProtocolError.MessageSendFailure,
|
|
1681
|
+
message: res.reason
|
|
1682
|
+
});
|
|
1683
|
+
this.deleteSession(session, { unhealthy: true });
|
|
1684
|
+
}
|
|
1943
1685
|
}
|
|
1944
1686
|
close() {
|
|
1945
1687
|
this.retryBudget.close();
|
|
@@ -1947,103 +1689,16 @@ var ClientTransport = class extends Transport {
|
|
|
1947
1689
|
}
|
|
1948
1690
|
};
|
|
1949
1691
|
|
|
1950
|
-
// transport/
|
|
1951
|
-
var
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
constructor() {
|
|
1955
|
-
this.id = `conn-${generateId()}`;
|
|
1956
|
-
}
|
|
1957
|
-
get loggingMetadata() {
|
|
1958
|
-
const metadata = { connId: this.id };
|
|
1959
|
-
if (this.telemetry?.span.isRecording()) {
|
|
1960
|
-
const spanContext = this.telemetry.span.spanContext();
|
|
1961
|
-
metadata.telemetry = {
|
|
1962
|
-
traceId: spanContext.traceId,
|
|
1963
|
-
spanId: spanContext.spanId
|
|
1964
|
-
};
|
|
1965
|
-
}
|
|
1966
|
-
return metadata;
|
|
1967
|
-
}
|
|
1968
|
-
// can't use event emitter because we need this to work in both node + browser
|
|
1969
|
-
_dataListeners = /* @__PURE__ */ new Set();
|
|
1970
|
-
_closeListeners = /* @__PURE__ */ new Set();
|
|
1971
|
-
_errorListeners = /* @__PURE__ */ new Set();
|
|
1972
|
-
get dataListeners() {
|
|
1973
|
-
return [...this._dataListeners];
|
|
1974
|
-
}
|
|
1975
|
-
get closeListeners() {
|
|
1976
|
-
return [...this._closeListeners];
|
|
1977
|
-
}
|
|
1978
|
-
get errorListeners() {
|
|
1979
|
-
return [...this._errorListeners];
|
|
1980
|
-
}
|
|
1981
|
-
onData(msg) {
|
|
1982
|
-
for (const cb of this.dataListeners) {
|
|
1983
|
-
cb(msg);
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
onError(err) {
|
|
1987
|
-
for (const cb of this.errorListeners) {
|
|
1988
|
-
cb(err);
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
onClose() {
|
|
1992
|
-
for (const cb of this.closeListeners) {
|
|
1993
|
-
cb();
|
|
1994
|
-
}
|
|
1995
|
-
this.telemetry?.span.end();
|
|
1996
|
-
}
|
|
1692
|
+
// transport/server.ts
|
|
1693
|
+
var import_api5 = require("@opentelemetry/api");
|
|
1694
|
+
var import_value2 = require("@sinclair/typebox/value");
|
|
1695
|
+
var ServerTransport = class extends Transport {
|
|
1997
1696
|
/**
|
|
1998
|
-
*
|
|
1999
|
-
* @param msg The message that was received.
|
|
1697
|
+
* The options for this transport.
|
|
2000
1698
|
*/
|
|
2001
|
-
|
|
2002
|
-
this._dataListeners.add(cb);
|
|
2003
|
-
}
|
|
2004
|
-
removeDataListener(cb) {
|
|
2005
|
-
this._dataListeners.delete(cb);
|
|
2006
|
-
}
|
|
1699
|
+
options;
|
|
2007
1700
|
/**
|
|
2008
|
-
*
|
|
2009
|
-
* This should also be called if an error happens and after notifying all the error listeners.
|
|
2010
|
-
* @param cb The callback to call when the connection is closed.
|
|
2011
|
-
*/
|
|
2012
|
-
addCloseListener(cb) {
|
|
2013
|
-
this._closeListeners.add(cb);
|
|
2014
|
-
}
|
|
2015
|
-
removeCloseListener(cb) {
|
|
2016
|
-
this._closeListeners.delete(cb);
|
|
2017
|
-
}
|
|
2018
|
-
/**
|
|
2019
|
-
* Handle adding a callback for when an error is received.
|
|
2020
|
-
* This should only be used for this.logging errors, all cleanup
|
|
2021
|
-
* should be delegated to addCloseListener.
|
|
2022
|
-
*
|
|
2023
|
-
* The implementer should take care such that the implemented
|
|
2024
|
-
* connection will call both the close and error callbacks
|
|
2025
|
-
* on an error.
|
|
2026
|
-
*
|
|
2027
|
-
* @param cb The callback to call when an error is received.
|
|
2028
|
-
*/
|
|
2029
|
-
addErrorListener(cb) {
|
|
2030
|
-
this._errorListeners.add(cb);
|
|
2031
|
-
}
|
|
2032
|
-
removeErrorListener(cb) {
|
|
2033
|
-
this._errorListeners.delete(cb);
|
|
2034
|
-
}
|
|
2035
|
-
};
|
|
2036
|
-
|
|
2037
|
-
// transport/server.ts
|
|
2038
|
-
var import_api5 = require("@opentelemetry/api");
|
|
2039
|
-
var import_value3 = require("@sinclair/typebox/value");
|
|
2040
|
-
var ServerTransport = class extends Transport {
|
|
2041
|
-
/**
|
|
2042
|
-
* The options for this transport.
|
|
2043
|
-
*/
|
|
2044
|
-
options;
|
|
2045
|
-
/**
|
|
2046
|
-
* Optional handshake options for the server.
|
|
1701
|
+
* Optional handshake options for the server.
|
|
2047
1702
|
*/
|
|
2048
1703
|
handshakeExtensions;
|
|
2049
1704
|
/**
|
|
@@ -2150,17 +1805,28 @@ var ServerTransport = class extends Transport {
|
|
|
2150
1805
|
message: reason
|
|
2151
1806
|
});
|
|
2152
1807
|
this.log?.warn(reason, metadata);
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
)
|
|
1808
|
+
const responseMsg = handshakeResponseMessage({
|
|
1809
|
+
from: this.clientId,
|
|
1810
|
+
to,
|
|
1811
|
+
status: {
|
|
1812
|
+
ok: false,
|
|
1813
|
+
code,
|
|
1814
|
+
reason
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
const res = session.sendHandshake(responseMsg);
|
|
1818
|
+
if (!res.ok) {
|
|
1819
|
+
this.log?.error(`failed to send handshake response: ${res.reason}`, {
|
|
1820
|
+
...session.loggingMetadata,
|
|
1821
|
+
transportMessage: responseMsg
|
|
1822
|
+
});
|
|
1823
|
+
this.protocolError({
|
|
1824
|
+
type: ProtocolError.MessageSendFailure,
|
|
1825
|
+
message: res.reason
|
|
1826
|
+
});
|
|
1827
|
+
this.deletePendingSession(session);
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
2164
1830
|
this.protocolError({
|
|
2165
1831
|
type: ProtocolError.HandshakeFailed,
|
|
2166
1832
|
code,
|
|
@@ -2169,7 +1835,7 @@ var ServerTransport = class extends Transport {
|
|
|
2169
1835
|
this.deletePendingSession(session);
|
|
2170
1836
|
}
|
|
2171
1837
|
async onHandshakeRequest(session, msg) {
|
|
2172
|
-
if (!
|
|
1838
|
+
if (!import_value2.Value.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {
|
|
2173
1839
|
this.rejectHandshakeRequest(
|
|
2174
1840
|
session,
|
|
2175
1841
|
msg.from,
|
|
@@ -2180,7 +1846,7 @@ var ServerTransport = class extends Transport {
|
|
|
2180
1846
|
transportMessage: msg,
|
|
2181
1847
|
connectedTo: msg.from,
|
|
2182
1848
|
validationErrors: [
|
|
2183
|
-
...
|
|
1849
|
+
...import_value2.Value.Errors(ControlMessageHandshakeRequestSchema, msg.payload)
|
|
2184
1850
|
]
|
|
2185
1851
|
}
|
|
2186
1852
|
);
|
|
@@ -2203,7 +1869,7 @@ var ServerTransport = class extends Transport {
|
|
|
2203
1869
|
}
|
|
2204
1870
|
let parsedMetadata = {};
|
|
2205
1871
|
if (this.handshakeExtensions) {
|
|
2206
|
-
if (!
|
|
1872
|
+
if (!import_value2.Value.Check(this.handshakeExtensions.schema, msg.payload.metadata)) {
|
|
2207
1873
|
this.rejectHandshakeRequest(
|
|
2208
1874
|
session,
|
|
2209
1875
|
msg.from,
|
|
@@ -2213,7 +1879,7 @@ var ServerTransport = class extends Transport {
|
|
|
2213
1879
|
...session.loggingMetadata,
|
|
2214
1880
|
connectedTo: msg.from,
|
|
2215
1881
|
validationErrors: [
|
|
2216
|
-
...
|
|
1882
|
+
...import_value2.Value.Errors(
|
|
2217
1883
|
this.handshakeExtensions.schema,
|
|
2218
1884
|
msg.payload.metadata
|
|
2219
1885
|
)
|
|
@@ -2229,165 +1895,652 @@ var ServerTransport = class extends Transport {
|
|
|
2229
1895
|
msg.payload.metadata,
|
|
2230
1896
|
previousParsedMetadata
|
|
2231
1897
|
);
|
|
2232
|
-
if (session._isConsumed) {
|
|
2233
|
-
return;
|
|
2234
|
-
}
|
|
2235
|
-
if (
|
|
2236
|
-
HandshakeErrorCustomHandlerFatalResponseCodes,
|
|
2237
|
-
parsedMetadataOrFailureCode
|
|
2238
|
-
)) {
|
|
2239
|
-
this.rejectHandshakeRequest(
|
|
2240
|
-
session,
|
|
2241
|
-
msg.from,
|
|
2242
|
-
"rejected by handshake handler",
|
|
2243
|
-
parsedMetadataOrFailureCode,
|
|
2244
|
-
{
|
|
2245
|
-
...session.loggingMetadata,
|
|
2246
|
-
connectedTo: msg.from,
|
|
2247
|
-
clientId: this.clientId
|
|
2248
|
-
}
|
|
2249
|
-
);
|
|
2250
|
-
return;
|
|
2251
|
-
}
|
|
2252
|
-
parsedMetadata = parsedMetadataOrFailureCode;
|
|
2253
|
-
}
|
|
2254
|
-
let connectCase = "new session";
|
|
2255
|
-
const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
|
|
2256
|
-
const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;
|
|
2257
|
-
let oldSession = this.sessions.get(msg.from);
|
|
2258
|
-
if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
|
|
2259
|
-
connectCase = "transparent reconnection";
|
|
2260
|
-
const ourNextSeq = oldSession.nextSeq();
|
|
2261
|
-
const ourAck = oldSession.ack;
|
|
2262
|
-
if (clientNextSentSeq > ourAck) {
|
|
2263
|
-
this.rejectHandshakeRequest(
|
|
2264
|
-
session,
|
|
2265
|
-
msg.from,
|
|
2266
|
-
`client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,
|
|
2267
|
-
"SESSION_STATE_MISMATCH",
|
|
2268
|
-
{
|
|
2269
|
-
...session.loggingMetadata,
|
|
2270
|
-
connectedTo: msg.from,
|
|
2271
|
-
transportMessage: msg
|
|
2272
|
-
}
|
|
2273
|
-
);
|
|
2274
|
-
return;
|
|
2275
|
-
}
|
|
2276
|
-
if (ourNextSeq > clientNextExpectedSeq) {
|
|
2277
|
-
this.rejectHandshakeRequest(
|
|
2278
|
-
session,
|
|
2279
|
-
msg.from,
|
|
2280
|
-
`server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,
|
|
2281
|
-
"SESSION_STATE_MISMATCH",
|
|
2282
|
-
{
|
|
2283
|
-
...session.loggingMetadata,
|
|
2284
|
-
connectedTo: msg.from,
|
|
2285
|
-
transportMessage: msg
|
|
2286
|
-
}
|
|
2287
|
-
);
|
|
2288
|
-
return;
|
|
2289
|
-
}
|
|
2290
|
-
if (oldSession.state !== "NoConnection" /* NoConnection */) {
|
|
2291
|
-
const noConnectionSession = ServerSessionStateGraph.transition.ConnectedToNoConnection(
|
|
2292
|
-
oldSession,
|
|
2293
|
-
{
|
|
2294
|
-
onSessionGracePeriodElapsed: () => {
|
|
2295
|
-
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
2296
|
-
}
|
|
2297
|
-
}
|
|
2298
|
-
);
|
|
2299
|
-
oldSession = noConnectionSession;
|
|
2300
|
-
this.updateSession(oldSession);
|
|
2301
|
-
}
|
|
2302
|
-
} else if (oldSession) {
|
|
2303
|
-
connectCase = "hard reconnection";
|
|
2304
|
-
this.log?.info(
|
|
2305
|
-
`client is reconnecting to a new session (${msg.payload.sessionId}) with an old session (${oldSession.id}) already existing, closing old session`,
|
|
1898
|
+
if (session._isConsumed) {
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
if (import_value2.Value.Check(
|
|
1902
|
+
HandshakeErrorCustomHandlerFatalResponseCodes,
|
|
1903
|
+
parsedMetadataOrFailureCode
|
|
1904
|
+
)) {
|
|
1905
|
+
this.rejectHandshakeRequest(
|
|
1906
|
+
session,
|
|
1907
|
+
msg.from,
|
|
1908
|
+
"rejected by handshake handler",
|
|
1909
|
+
parsedMetadataOrFailureCode,
|
|
1910
|
+
{
|
|
1911
|
+
...session.loggingMetadata,
|
|
1912
|
+
connectedTo: msg.from,
|
|
1913
|
+
clientId: this.clientId
|
|
1914
|
+
}
|
|
1915
|
+
);
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1918
|
+
parsedMetadata = parsedMetadataOrFailureCode;
|
|
1919
|
+
}
|
|
1920
|
+
let connectCase = "new session";
|
|
1921
|
+
const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
|
|
1922
|
+
const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq;
|
|
1923
|
+
let oldSession = this.sessions.get(msg.from);
|
|
1924
|
+
if (this.options.enableTransparentSessionReconnects && oldSession && oldSession.id === msg.payload.sessionId) {
|
|
1925
|
+
connectCase = "transparent reconnection";
|
|
1926
|
+
const ourNextSeq = oldSession.nextSeq();
|
|
1927
|
+
const ourAck = oldSession.ack;
|
|
1928
|
+
if (clientNextSentSeq > ourAck) {
|
|
1929
|
+
this.rejectHandshakeRequest(
|
|
1930
|
+
session,
|
|
1931
|
+
msg.from,
|
|
1932
|
+
`client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,
|
|
1933
|
+
"SESSION_STATE_MISMATCH",
|
|
1934
|
+
{
|
|
1935
|
+
...session.loggingMetadata,
|
|
1936
|
+
connectedTo: msg.from,
|
|
1937
|
+
transportMessage: msg
|
|
1938
|
+
}
|
|
1939
|
+
);
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
if (ourNextSeq > clientNextExpectedSeq) {
|
|
1943
|
+
this.rejectHandshakeRequest(
|
|
1944
|
+
session,
|
|
1945
|
+
msg.from,
|
|
1946
|
+
`server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,
|
|
1947
|
+
"SESSION_STATE_MISMATCH",
|
|
1948
|
+
{
|
|
1949
|
+
...session.loggingMetadata,
|
|
1950
|
+
connectedTo: msg.from,
|
|
1951
|
+
transportMessage: msg
|
|
1952
|
+
}
|
|
1953
|
+
);
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1956
|
+
if (oldSession.state !== "NoConnection" /* NoConnection */) {
|
|
1957
|
+
const noConnectionSession = ServerSessionStateGraph.transition.ConnectedToNoConnection(
|
|
1958
|
+
oldSession,
|
|
1959
|
+
{
|
|
1960
|
+
onSessionGracePeriodElapsed: () => {
|
|
1961
|
+
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
);
|
|
1965
|
+
oldSession = noConnectionSession;
|
|
1966
|
+
this.updateSession(oldSession);
|
|
1967
|
+
}
|
|
1968
|
+
} else if (oldSession) {
|
|
1969
|
+
connectCase = "hard reconnection";
|
|
1970
|
+
this.log?.info(
|
|
1971
|
+
`client is reconnecting to a new session (${msg.payload.sessionId}) with an old session (${oldSession.id}) already existing, closing old session`,
|
|
1972
|
+
{
|
|
1973
|
+
...session.loggingMetadata,
|
|
1974
|
+
connectedTo: msg.from,
|
|
1975
|
+
sessionId: msg.payload.sessionId
|
|
1976
|
+
}
|
|
1977
|
+
);
|
|
1978
|
+
this.deleteSession(oldSession);
|
|
1979
|
+
oldSession = void 0;
|
|
1980
|
+
}
|
|
1981
|
+
if (!oldSession && (clientNextSentSeq > 0 || clientNextExpectedSeq > 0)) {
|
|
1982
|
+
connectCase = "unknown session";
|
|
1983
|
+
const rejectionMessage = this.options.enableTransparentSessionReconnects ? `client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}` : `client is attempting a transparent reconnect to a session but the server does not support it: ${msg.payload.sessionId}`;
|
|
1984
|
+
this.rejectHandshakeRequest(
|
|
1985
|
+
session,
|
|
1986
|
+
msg.from,
|
|
1987
|
+
rejectionMessage,
|
|
1988
|
+
"SESSION_STATE_MISMATCH",
|
|
1989
|
+
{
|
|
1990
|
+
...session.loggingMetadata,
|
|
1991
|
+
connectedTo: msg.from,
|
|
1992
|
+
transportMessage: msg
|
|
1993
|
+
}
|
|
1994
|
+
);
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
const sessionId = msg.payload.sessionId;
|
|
1998
|
+
this.log?.info(
|
|
1999
|
+
`handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,
|
|
2000
|
+
{
|
|
2001
|
+
...session.loggingMetadata,
|
|
2002
|
+
connectedTo: msg.from
|
|
2003
|
+
}
|
|
2004
|
+
);
|
|
2005
|
+
const responseMsg = handshakeResponseMessage({
|
|
2006
|
+
from: this.clientId,
|
|
2007
|
+
to: msg.from,
|
|
2008
|
+
status: {
|
|
2009
|
+
ok: true,
|
|
2010
|
+
sessionId
|
|
2011
|
+
}
|
|
2012
|
+
});
|
|
2013
|
+
const res = session.sendHandshake(responseMsg);
|
|
2014
|
+
if (!res.ok) {
|
|
2015
|
+
this.log?.error(`failed to send handshake response: ${res.reason}`, {
|
|
2016
|
+
...session.loggingMetadata,
|
|
2017
|
+
transportMessage: responseMsg
|
|
2018
|
+
});
|
|
2019
|
+
this.protocolError({
|
|
2020
|
+
type: ProtocolError.MessageSendFailure,
|
|
2021
|
+
message: res.reason
|
|
2022
|
+
});
|
|
2023
|
+
this.deletePendingSession(session);
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
this.pendingSessions.delete(session);
|
|
2027
|
+
const connectedSession = ServerSessionStateGraph.transition.WaitingForHandshakeToConnected(
|
|
2028
|
+
session,
|
|
2029
|
+
// by this point oldSession is either no connection or we dont have an old session
|
|
2030
|
+
oldSession,
|
|
2031
|
+
sessionId,
|
|
2032
|
+
msg.from,
|
|
2033
|
+
msg.tracing,
|
|
2034
|
+
{
|
|
2035
|
+
onConnectionErrored: (err) => {
|
|
2036
|
+
const errStr = coerceErrorString(err);
|
|
2037
|
+
this.log?.warn(
|
|
2038
|
+
`connection to ${connectedSession.to} errored: ${errStr}`,
|
|
2039
|
+
connectedSession.loggingMetadata
|
|
2040
|
+
);
|
|
2041
|
+
},
|
|
2042
|
+
onConnectionClosed: () => {
|
|
2043
|
+
this.log?.info(
|
|
2044
|
+
`connection to ${connectedSession.to} closed`,
|
|
2045
|
+
connectedSession.loggingMetadata
|
|
2046
|
+
);
|
|
2047
|
+
this.onConnClosed(connectedSession);
|
|
2048
|
+
},
|
|
2049
|
+
onMessage: (msg2) => {
|
|
2050
|
+
this.handleMsg(msg2);
|
|
2051
|
+
},
|
|
2052
|
+
onInvalidMessage: (reason) => {
|
|
2053
|
+
this.log?.error(`invalid message: ${reason}`, {
|
|
2054
|
+
...connectedSession.loggingMetadata,
|
|
2055
|
+
transportMessage: msg
|
|
2056
|
+
});
|
|
2057
|
+
this.protocolError({
|
|
2058
|
+
type: ProtocolError.InvalidMessage,
|
|
2059
|
+
message: reason
|
|
2060
|
+
});
|
|
2061
|
+
this.deleteSession(connectedSession, { unhealthy: true });
|
|
2062
|
+
},
|
|
2063
|
+
onMessageSendFailure: (msg2, reason) => {
|
|
2064
|
+
this.log?.error(`failed to send message: ${reason}`, {
|
|
2065
|
+
...connectedSession.loggingMetadata,
|
|
2066
|
+
transportMessage: msg2
|
|
2067
|
+
});
|
|
2068
|
+
this.protocolError({
|
|
2069
|
+
type: ProtocolError.MessageSendFailure,
|
|
2070
|
+
message: reason
|
|
2071
|
+
});
|
|
2072
|
+
this.deleteSession(connectedSession, { unhealthy: true });
|
|
2073
|
+
}
|
|
2074
|
+
},
|
|
2075
|
+
gotVersion
|
|
2076
|
+
);
|
|
2077
|
+
const bufferSendRes = connectedSession.sendBufferedMessages();
|
|
2078
|
+
if (!bufferSendRes.ok) {
|
|
2079
|
+
this.log?.error(
|
|
2080
|
+
`failed to send buffered messages: ${bufferSendRes.reason}`,
|
|
2081
|
+
{
|
|
2082
|
+
...connectedSession.loggingMetadata,
|
|
2083
|
+
transportMessage: msg
|
|
2084
|
+
}
|
|
2085
|
+
);
|
|
2086
|
+
this.protocolError({
|
|
2087
|
+
type: ProtocolError.MessageSendFailure,
|
|
2088
|
+
message: bufferSendRes.reason
|
|
2089
|
+
});
|
|
2090
|
+
this.deleteSession(connectedSession, { unhealthy: true });
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
|
|
2094
|
+
if (oldSession) {
|
|
2095
|
+
this.updateSession(connectedSession);
|
|
2096
|
+
} else {
|
|
2097
|
+
this.createSession(connectedSession);
|
|
2098
|
+
}
|
|
2099
|
+
connectedSession.startActiveHeartbeat();
|
|
2100
|
+
}
|
|
2101
|
+
};
|
|
2102
|
+
|
|
2103
|
+
// transport/connection.ts
|
|
2104
|
+
var Connection = class {
|
|
2105
|
+
id;
|
|
2106
|
+
telemetry;
|
|
2107
|
+
constructor() {
|
|
2108
|
+
this.id = `conn-${generateId()}`;
|
|
2109
|
+
}
|
|
2110
|
+
get loggingMetadata() {
|
|
2111
|
+
const metadata = { connId: this.id };
|
|
2112
|
+
if (this.telemetry?.span.isRecording()) {
|
|
2113
|
+
const spanContext = this.telemetry.span.spanContext();
|
|
2114
|
+
metadata.telemetry = {
|
|
2115
|
+
traceId: spanContext.traceId,
|
|
2116
|
+
spanId: spanContext.spanId
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
return metadata;
|
|
2120
|
+
}
|
|
2121
|
+
// can't use event emitter because we need this to work in both node + browser
|
|
2122
|
+
_dataListeners = /* @__PURE__ */ new Set();
|
|
2123
|
+
_closeListeners = /* @__PURE__ */ new Set();
|
|
2124
|
+
_errorListeners = /* @__PURE__ */ new Set();
|
|
2125
|
+
get dataListeners() {
|
|
2126
|
+
return [...this._dataListeners];
|
|
2127
|
+
}
|
|
2128
|
+
get closeListeners() {
|
|
2129
|
+
return [...this._closeListeners];
|
|
2130
|
+
}
|
|
2131
|
+
get errorListeners() {
|
|
2132
|
+
return [...this._errorListeners];
|
|
2133
|
+
}
|
|
2134
|
+
onData(msg) {
|
|
2135
|
+
for (const cb of this.dataListeners) {
|
|
2136
|
+
cb(msg);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
onError(err) {
|
|
2140
|
+
for (const cb of this.errorListeners) {
|
|
2141
|
+
cb(err);
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
onClose() {
|
|
2145
|
+
for (const cb of this.closeListeners) {
|
|
2146
|
+
cb();
|
|
2147
|
+
}
|
|
2148
|
+
this.telemetry?.span.end();
|
|
2149
|
+
}
|
|
2150
|
+
/**
|
|
2151
|
+
* Handle adding a callback for when a message is received.
|
|
2152
|
+
* @param msg The message that was received.
|
|
2153
|
+
*/
|
|
2154
|
+
addDataListener(cb) {
|
|
2155
|
+
this._dataListeners.add(cb);
|
|
2156
|
+
}
|
|
2157
|
+
removeDataListener(cb) {
|
|
2158
|
+
this._dataListeners.delete(cb);
|
|
2159
|
+
}
|
|
2160
|
+
/**
|
|
2161
|
+
* Handle adding a callback for when the connection is closed.
|
|
2162
|
+
* This should also be called if an error happens and after notifying all the error listeners.
|
|
2163
|
+
* @param cb The callback to call when the connection is closed.
|
|
2164
|
+
*/
|
|
2165
|
+
addCloseListener(cb) {
|
|
2166
|
+
this._closeListeners.add(cb);
|
|
2167
|
+
}
|
|
2168
|
+
removeCloseListener(cb) {
|
|
2169
|
+
this._closeListeners.delete(cb);
|
|
2170
|
+
}
|
|
2171
|
+
/**
|
|
2172
|
+
* Handle adding a callback for when an error is received.
|
|
2173
|
+
* This should only be used for this.logging errors, all cleanup
|
|
2174
|
+
* should be delegated to addCloseListener.
|
|
2175
|
+
*
|
|
2176
|
+
* The implementer should take care such that the implemented
|
|
2177
|
+
* connection will call both the close and error callbacks
|
|
2178
|
+
* on an error.
|
|
2179
|
+
*
|
|
2180
|
+
* @param cb The callback to call when an error is received.
|
|
2181
|
+
*/
|
|
2182
|
+
addErrorListener(cb) {
|
|
2183
|
+
this._errorListeners.add(cb);
|
|
2184
|
+
}
|
|
2185
|
+
removeErrorListener(cb) {
|
|
2186
|
+
this._errorListeners.delete(cb);
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
|
|
2190
|
+
// codec/adapter.ts
|
|
2191
|
+
var CodecMessageAdapter = class {
|
|
2192
|
+
constructor(codec) {
|
|
2193
|
+
this.codec = codec;
|
|
2194
|
+
}
|
|
2195
|
+
toBuffer(msg) {
|
|
2196
|
+
try {
|
|
2197
|
+
return {
|
|
2198
|
+
ok: true,
|
|
2199
|
+
value: this.codec.toBuffer(msg)
|
|
2200
|
+
};
|
|
2201
|
+
} catch (e) {
|
|
2202
|
+
return {
|
|
2203
|
+
ok: false,
|
|
2204
|
+
reason: coerceErrorString(e)
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
fromBuffer(buf) {
|
|
2209
|
+
try {
|
|
2210
|
+
const parsedMsg = this.codec.fromBuffer(buf);
|
|
2211
|
+
if (!import_value3.Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {
|
|
2212
|
+
return {
|
|
2213
|
+
ok: false,
|
|
2214
|
+
reason: "transport message schema mismatch"
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
return {
|
|
2218
|
+
ok: true,
|
|
2219
|
+
value: parsedMsg
|
|
2220
|
+
};
|
|
2221
|
+
} catch (e) {
|
|
2222
|
+
return {
|
|
2223
|
+
ok: false,
|
|
2224
|
+
reason: coerceErrorString(e)
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
};
|
|
2229
|
+
|
|
2230
|
+
// transport/sessionStateMachine/transitions.ts
|
|
2231
|
+
function inheritSharedSession(session) {
|
|
2232
|
+
return {
|
|
2233
|
+
id: session.id,
|
|
2234
|
+
from: session.from,
|
|
2235
|
+
to: session.to,
|
|
2236
|
+
seq: session.seq,
|
|
2237
|
+
ack: session.ack,
|
|
2238
|
+
seqSent: session.seqSent,
|
|
2239
|
+
sendBuffer: session.sendBuffer,
|
|
2240
|
+
telemetry: session.telemetry,
|
|
2241
|
+
options: session.options,
|
|
2242
|
+
log: session.log,
|
|
2243
|
+
tracer: session.tracer,
|
|
2244
|
+
protocolVersion: session.protocolVersion,
|
|
2245
|
+
codec: session.codec
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
function inheritSharedSessionWithGrace(session) {
|
|
2249
|
+
return {
|
|
2250
|
+
...inheritSharedSession(session),
|
|
2251
|
+
graceExpiryTime: session.graceExpiryTime
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
var SessionStateGraph = {
|
|
2255
|
+
entrypoints: {
|
|
2256
|
+
NoConnection: (to, from, listeners, options, protocolVersion, tracer, log) => {
|
|
2257
|
+
const id = `session-${generateId()}`;
|
|
2258
|
+
const telemetry = createSessionTelemetryInfo(tracer, id, to, from);
|
|
2259
|
+
const sendBuffer = [];
|
|
2260
|
+
const session = new SessionNoConnection({
|
|
2261
|
+
listeners,
|
|
2262
|
+
id,
|
|
2263
|
+
from,
|
|
2264
|
+
to,
|
|
2265
|
+
seq: 0,
|
|
2266
|
+
ack: 0,
|
|
2267
|
+
seqSent: 0,
|
|
2268
|
+
graceExpiryTime: Date.now() + options.sessionDisconnectGraceMs,
|
|
2269
|
+
sendBuffer,
|
|
2270
|
+
telemetry,
|
|
2271
|
+
options,
|
|
2272
|
+
protocolVersion,
|
|
2273
|
+
tracer,
|
|
2274
|
+
log,
|
|
2275
|
+
codec: new CodecMessageAdapter(options.codec)
|
|
2276
|
+
});
|
|
2277
|
+
session.log?.info(`session ${session.id} created in NoConnection state`, {
|
|
2278
|
+
...session.loggingMetadata,
|
|
2279
|
+
tags: ["state-transition"]
|
|
2280
|
+
});
|
|
2281
|
+
return session;
|
|
2282
|
+
},
|
|
2283
|
+
WaitingForHandshake: (from, conn, listeners, options, tracer, log) => {
|
|
2284
|
+
const session = new SessionWaitingForHandshake({
|
|
2285
|
+
conn,
|
|
2286
|
+
listeners,
|
|
2287
|
+
from,
|
|
2288
|
+
options,
|
|
2289
|
+
tracer,
|
|
2290
|
+
log,
|
|
2291
|
+
codec: new CodecMessageAdapter(options.codec)
|
|
2292
|
+
});
|
|
2293
|
+
session.log?.info(`session created in WaitingForHandshake state`, {
|
|
2294
|
+
...session.loggingMetadata,
|
|
2295
|
+
tags: ["state-transition"]
|
|
2296
|
+
});
|
|
2297
|
+
return session;
|
|
2298
|
+
}
|
|
2299
|
+
},
|
|
2300
|
+
// All of the transitions 'move'/'consume' the old session and return a new one.
|
|
2301
|
+
// After a session is transitioned, any usage of the old session will throw.
|
|
2302
|
+
transition: {
|
|
2303
|
+
// happy path transitions
|
|
2304
|
+
NoConnectionToBackingOff: (oldSession, backoffMs, listeners) => {
|
|
2305
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2306
|
+
oldSession._handleStateExit();
|
|
2307
|
+
const session = new SessionBackingOff({
|
|
2308
|
+
backoffMs,
|
|
2309
|
+
listeners,
|
|
2310
|
+
...carriedState
|
|
2311
|
+
});
|
|
2312
|
+
session.log?.info(
|
|
2313
|
+
`session ${session.id} transition from NoConnection to BackingOff`,
|
|
2314
|
+
{
|
|
2315
|
+
...session.loggingMetadata,
|
|
2316
|
+
tags: ["state-transition"]
|
|
2317
|
+
}
|
|
2318
|
+
);
|
|
2319
|
+
return session;
|
|
2320
|
+
},
|
|
2321
|
+
BackingOffToConnecting: (oldSession, connPromise, listeners) => {
|
|
2322
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2323
|
+
oldSession._handleStateExit();
|
|
2324
|
+
const session = new SessionConnecting({
|
|
2325
|
+
connPromise,
|
|
2326
|
+
listeners,
|
|
2327
|
+
...carriedState
|
|
2328
|
+
});
|
|
2329
|
+
session.log?.info(
|
|
2330
|
+
`session ${session.id} transition from BackingOff to Connecting`,
|
|
2331
|
+
{
|
|
2332
|
+
...session.loggingMetadata,
|
|
2333
|
+
tags: ["state-transition"]
|
|
2334
|
+
}
|
|
2335
|
+
);
|
|
2336
|
+
return session;
|
|
2337
|
+
},
|
|
2338
|
+
ConnectingToHandshaking: (oldSession, conn, listeners) => {
|
|
2339
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2340
|
+
oldSession._handleStateExit();
|
|
2341
|
+
const session = new SessionHandshaking({
|
|
2342
|
+
conn,
|
|
2343
|
+
listeners,
|
|
2344
|
+
...carriedState
|
|
2345
|
+
});
|
|
2346
|
+
conn.telemetry = createConnectionTelemetryInfo(
|
|
2347
|
+
session.tracer,
|
|
2348
|
+
conn,
|
|
2349
|
+
session.telemetry
|
|
2350
|
+
);
|
|
2351
|
+
session.log?.info(
|
|
2352
|
+
`session ${session.id} transition from Connecting to Handshaking`,
|
|
2353
|
+
{
|
|
2354
|
+
...session.loggingMetadata,
|
|
2355
|
+
tags: ["state-transition"]
|
|
2356
|
+
}
|
|
2357
|
+
);
|
|
2358
|
+
return session;
|
|
2359
|
+
},
|
|
2360
|
+
HandshakingToConnected: (oldSession, listeners) => {
|
|
2361
|
+
const carriedState = inheritSharedSession(oldSession);
|
|
2362
|
+
const conn = oldSession.conn;
|
|
2363
|
+
oldSession._handleStateExit();
|
|
2364
|
+
const session = new SessionConnected({
|
|
2365
|
+
conn,
|
|
2366
|
+
listeners,
|
|
2367
|
+
...carriedState
|
|
2368
|
+
});
|
|
2369
|
+
session.startMissingHeartbeatTimeout();
|
|
2370
|
+
session.log?.info(
|
|
2371
|
+
`session ${session.id} transition from Handshaking to Connected`,
|
|
2372
|
+
{
|
|
2373
|
+
...session.loggingMetadata,
|
|
2374
|
+
tags: ["state-transition"]
|
|
2375
|
+
}
|
|
2376
|
+
);
|
|
2377
|
+
return session;
|
|
2378
|
+
},
|
|
2379
|
+
WaitingForHandshakeToConnected: (pendingSession, oldSession, sessionId, to, propagationCtx, listeners, protocolVersion) => {
|
|
2380
|
+
const conn = pendingSession.conn;
|
|
2381
|
+
const { from, options } = pendingSession;
|
|
2382
|
+
const carriedState = oldSession ? (
|
|
2383
|
+
// old session exists, inherit state
|
|
2384
|
+
inheritSharedSession(oldSession)
|
|
2385
|
+
) : (
|
|
2386
|
+
// old session does not exist, create new state
|
|
2387
|
+
{
|
|
2388
|
+
id: sessionId,
|
|
2389
|
+
from,
|
|
2390
|
+
to,
|
|
2391
|
+
seq: 0,
|
|
2392
|
+
ack: 0,
|
|
2393
|
+
seqSent: 0,
|
|
2394
|
+
sendBuffer: [],
|
|
2395
|
+
telemetry: createSessionTelemetryInfo(
|
|
2396
|
+
pendingSession.tracer,
|
|
2397
|
+
sessionId,
|
|
2398
|
+
to,
|
|
2399
|
+
from,
|
|
2400
|
+
propagationCtx
|
|
2401
|
+
),
|
|
2402
|
+
options,
|
|
2403
|
+
tracer: pendingSession.tracer,
|
|
2404
|
+
log: pendingSession.log,
|
|
2405
|
+
protocolVersion,
|
|
2406
|
+
codec: new CodecMessageAdapter(options.codec)
|
|
2407
|
+
}
|
|
2408
|
+
);
|
|
2409
|
+
pendingSession._handleStateExit();
|
|
2410
|
+
oldSession?._handleStateExit();
|
|
2411
|
+
const session = new SessionConnected({
|
|
2412
|
+
conn,
|
|
2413
|
+
listeners,
|
|
2414
|
+
...carriedState
|
|
2415
|
+
});
|
|
2416
|
+
session.startMissingHeartbeatTimeout();
|
|
2417
|
+
conn.telemetry = createConnectionTelemetryInfo(
|
|
2418
|
+
session.tracer,
|
|
2419
|
+
conn,
|
|
2420
|
+
session.telemetry
|
|
2421
|
+
);
|
|
2422
|
+
session.log?.info(
|
|
2423
|
+
`session ${session.id} transition from WaitingForHandshake to Connected`,
|
|
2424
|
+
{
|
|
2425
|
+
...session.loggingMetadata,
|
|
2426
|
+
tags: ["state-transition"]
|
|
2427
|
+
}
|
|
2428
|
+
);
|
|
2429
|
+
return session;
|
|
2430
|
+
},
|
|
2431
|
+
// disconnect paths
|
|
2432
|
+
BackingOffToNoConnection: (oldSession, listeners) => {
|
|
2433
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2434
|
+
oldSession._handleStateExit();
|
|
2435
|
+
const session = new SessionNoConnection({
|
|
2436
|
+
listeners,
|
|
2437
|
+
...carriedState
|
|
2438
|
+
});
|
|
2439
|
+
session.log?.info(
|
|
2440
|
+
`session ${session.id} transition from BackingOff to NoConnection`,
|
|
2441
|
+
{
|
|
2442
|
+
...session.loggingMetadata,
|
|
2443
|
+
tags: ["state-transition"]
|
|
2444
|
+
}
|
|
2445
|
+
);
|
|
2446
|
+
return session;
|
|
2447
|
+
},
|
|
2448
|
+
ConnectingToNoConnection: (oldSession, listeners) => {
|
|
2449
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2450
|
+
oldSession.bestEffortClose();
|
|
2451
|
+
oldSession._handleStateExit();
|
|
2452
|
+
const session = new SessionNoConnection({
|
|
2453
|
+
listeners,
|
|
2454
|
+
...carriedState
|
|
2455
|
+
});
|
|
2456
|
+
session.log?.info(
|
|
2457
|
+
`session ${session.id} transition from Connecting to NoConnection`,
|
|
2306
2458
|
{
|
|
2307
2459
|
...session.loggingMetadata,
|
|
2308
|
-
|
|
2309
|
-
sessionId: msg.payload.sessionId
|
|
2460
|
+
tags: ["state-transition"]
|
|
2310
2461
|
}
|
|
2311
2462
|
);
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2463
|
+
return session;
|
|
2464
|
+
},
|
|
2465
|
+
HandshakingToNoConnection: (oldSession, listeners) => {
|
|
2466
|
+
const carriedState = inheritSharedSessionWithGrace(oldSession);
|
|
2467
|
+
oldSession.conn.close();
|
|
2468
|
+
oldSession._handleStateExit();
|
|
2469
|
+
const session = new SessionNoConnection({
|
|
2470
|
+
listeners,
|
|
2471
|
+
...carriedState
|
|
2472
|
+
});
|
|
2473
|
+
session.log?.info(
|
|
2474
|
+
`session ${session.id} transition from Handshaking to NoConnection`,
|
|
2323
2475
|
{
|
|
2324
2476
|
...session.loggingMetadata,
|
|
2325
|
-
|
|
2326
|
-
transportMessage: msg
|
|
2477
|
+
tags: ["state-transition"]
|
|
2327
2478
|
}
|
|
2328
2479
|
);
|
|
2329
|
-
return;
|
|
2330
|
-
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
});
|
|
2347
|
-
session.sendHandshake(responseMsg);
|
|
2348
|
-
const connectedSession = ServerSessionStateGraph.transition.WaitingForHandshakeToConnected(
|
|
2349
|
-
session,
|
|
2350
|
-
// by this point oldSession is either no connection or we dont have an old session
|
|
2351
|
-
oldSession,
|
|
2352
|
-
sessionId,
|
|
2353
|
-
msg.from,
|
|
2354
|
-
msg.tracing,
|
|
2355
|
-
{
|
|
2356
|
-
onConnectionErrored: (err) => {
|
|
2357
|
-
const errStr = coerceErrorString(err);
|
|
2358
|
-
this.log?.warn(
|
|
2359
|
-
`connection to ${connectedSession.to} errored: ${errStr}`,
|
|
2360
|
-
connectedSession.loggingMetadata
|
|
2361
|
-
);
|
|
2362
|
-
},
|
|
2363
|
-
onConnectionClosed: () => {
|
|
2364
|
-
this.log?.info(
|
|
2365
|
-
`connection to ${connectedSession.to} closed`,
|
|
2366
|
-
connectedSession.loggingMetadata
|
|
2367
|
-
);
|
|
2368
|
-
this.onConnClosed(connectedSession);
|
|
2369
|
-
},
|
|
2370
|
-
onMessage: (msg2) => {
|
|
2371
|
-
this.handleMsg(msg2);
|
|
2372
|
-
},
|
|
2373
|
-
onInvalidMessage: (reason) => {
|
|
2374
|
-
this.protocolError({
|
|
2375
|
-
type: ProtocolError.InvalidMessage,
|
|
2376
|
-
message: reason
|
|
2377
|
-
});
|
|
2378
|
-
this.deleteSession(connectedSession, { unhealthy: true });
|
|
2480
|
+
return session;
|
|
2481
|
+
},
|
|
2482
|
+
ConnectedToNoConnection: (oldSession, listeners) => {
|
|
2483
|
+
const carriedState = inheritSharedSession(oldSession);
|
|
2484
|
+
const graceExpiryTime = Date.now() + oldSession.options.sessionDisconnectGraceMs;
|
|
2485
|
+
oldSession.conn.close();
|
|
2486
|
+
oldSession._handleStateExit();
|
|
2487
|
+
const session = new SessionNoConnection({
|
|
2488
|
+
listeners,
|
|
2489
|
+
graceExpiryTime,
|
|
2490
|
+
...carriedState
|
|
2491
|
+
});
|
|
2492
|
+
session.log?.info(
|
|
2493
|
+
`session ${session.id} transition from Connected to NoConnection`,
|
|
2494
|
+
{
|
|
2495
|
+
...session.loggingMetadata,
|
|
2496
|
+
tags: ["state-transition"]
|
|
2379
2497
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
);
|
|
2383
|
-
this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
|
|
2384
|
-
if (oldSession) {
|
|
2385
|
-
this.updateSession(connectedSession);
|
|
2386
|
-
} else {
|
|
2387
|
-
this.createSession(connectedSession);
|
|
2498
|
+
);
|
|
2499
|
+
return session;
|
|
2388
2500
|
}
|
|
2389
|
-
|
|
2390
|
-
|
|
2501
|
+
}
|
|
2502
|
+
};
|
|
2503
|
+
var transitions = SessionStateGraph.transition;
|
|
2504
|
+
var ClientSessionStateGraph = {
|
|
2505
|
+
entrypoint: SessionStateGraph.entrypoints.NoConnection,
|
|
2506
|
+
transition: {
|
|
2507
|
+
// happy paths
|
|
2508
|
+
// NoConnection -> BackingOff: attempt to connect
|
|
2509
|
+
NoConnectionToBackingOff: transitions.NoConnectionToBackingOff,
|
|
2510
|
+
// BackingOff -> Connecting: backoff period elapsed, start connection
|
|
2511
|
+
BackingOffToConnecting: transitions.BackingOffToConnecting,
|
|
2512
|
+
// Connecting -> Handshaking: connection established, start handshake
|
|
2513
|
+
ConnectingToHandshaking: transitions.ConnectingToHandshaking,
|
|
2514
|
+
// Handshaking -> Connected: handshake complete, session ready
|
|
2515
|
+
HandshakingToConnected: transitions.HandshakingToConnected,
|
|
2516
|
+
// disconnect paths
|
|
2517
|
+
// BackingOff -> NoConnection: unused
|
|
2518
|
+
BackingOffToNoConnection: transitions.BackingOffToNoConnection,
|
|
2519
|
+
// Connecting -> NoConnection: connection failed or connection timeout
|
|
2520
|
+
ConnectingToNoConnection: transitions.ConnectingToNoConnection,
|
|
2521
|
+
// Handshaking -> NoConnection: connection closed or handshake timeout
|
|
2522
|
+
HandshakingToNoConnection: transitions.HandshakingToNoConnection,
|
|
2523
|
+
// Connected -> NoConnection: connection closed
|
|
2524
|
+
ConnectedToNoConnection: transitions.ConnectedToNoConnection
|
|
2525
|
+
// destroy/close paths
|
|
2526
|
+
// NoConnection -> x: grace period elapsed
|
|
2527
|
+
// BackingOff -> x: grace period elapsed
|
|
2528
|
+
// Connecting -> x: grace period elapsed
|
|
2529
|
+
// Handshaking -> x: grace period elapsed or invalid handshake message or handshake rejection
|
|
2530
|
+
// Connected -> x: grace period elapsed or invalid message
|
|
2531
|
+
}
|
|
2532
|
+
};
|
|
2533
|
+
var ServerSessionStateGraph = {
|
|
2534
|
+
entrypoint: SessionStateGraph.entrypoints.WaitingForHandshake,
|
|
2535
|
+
transition: {
|
|
2536
|
+
// happy paths
|
|
2537
|
+
// WaitingForHandshake -> Connected: handshake complete, session ready
|
|
2538
|
+
WaitingForHandshakeToConnected: transitions.WaitingForHandshakeToConnected,
|
|
2539
|
+
// disconnect paths
|
|
2540
|
+
// Connected -> NoConnection: connection closed
|
|
2541
|
+
ConnectedToNoConnection: transitions.ConnectedToNoConnection
|
|
2542
|
+
// destroy/close paths
|
|
2543
|
+
// WaitingForHandshake -> x: handshake timeout elapsed or invalid handshake message or handshake rejection or connection closed
|
|
2391
2544
|
}
|
|
2392
2545
|
};
|
|
2393
2546
|
|
|
@@ -2477,6 +2630,16 @@ function duplexPair() {
|
|
|
2477
2630
|
const side1 = new DuplexSide();
|
|
2478
2631
|
side0[kInitOtherSide](side1);
|
|
2479
2632
|
side1[kInitOtherSide](side0);
|
|
2633
|
+
side0.on("close", () => {
|
|
2634
|
+
setImmediate(() => {
|
|
2635
|
+
side1.destroy();
|
|
2636
|
+
});
|
|
2637
|
+
});
|
|
2638
|
+
side1.on("close", () => {
|
|
2639
|
+
setImmediate(() => {
|
|
2640
|
+
side0.destroy();
|
|
2641
|
+
});
|
|
2642
|
+
});
|
|
2480
2643
|
return [side0, side1];
|
|
2481
2644
|
}
|
|
2482
2645
|
|
|
@@ -2589,14 +2752,14 @@ function createMockTransportNetwork(opts) {
|
|
|
2589
2752
|
transport.close();
|
|
2590
2753
|
}
|
|
2591
2754
|
for (const conn of Object.values(connections.get())) {
|
|
2592
|
-
conn.serverToClient.
|
|
2593
|
-
conn.clientToServer.
|
|
2755
|
+
conn.serverToClient.destroy();
|
|
2756
|
+
conn.clientToServer.destroy();
|
|
2594
2757
|
}
|
|
2595
2758
|
},
|
|
2596
2759
|
cleanup() {
|
|
2597
2760
|
for (const conn of Object.values(connections.get())) {
|
|
2598
|
-
conn.serverToClient.
|
|
2599
|
-
conn.clientToServer.
|
|
2761
|
+
conn.serverToClient.destroy();
|
|
2762
|
+
conn.clientToServer.destroy();
|
|
2600
2763
|
}
|
|
2601
2764
|
}
|
|
2602
2765
|
};
|