@replit/river 0.23.16 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -20
- package/dist/{chunk-UDXM64QK.js → chunk-AASMR3CQ.js} +24 -18
- package/dist/chunk-AASMR3CQ.js.map +1 -0
- package/dist/chunk-JA57I7MG.js +653 -0
- package/dist/chunk-JA57I7MG.js.map +1 -0
- package/dist/chunk-KX5PQRVN.js +382 -0
- package/dist/chunk-KX5PQRVN.js.map +1 -0
- package/dist/{chunk-LTSLICON.js → chunk-KYYB4DUR.js} +68 -519
- package/dist/chunk-KYYB4DUR.js.map +1 -0
- package/dist/chunk-NLQPPDOT.js +399 -0
- package/dist/chunk-NLQPPDOT.js.map +1 -0
- package/dist/{chunk-TXSQRTZB.js → chunk-PJGGC3LV.js} +55 -41
- package/dist/chunk-PJGGC3LV.js.map +1 -0
- package/dist/chunk-RXJLI2OP.js +50 -0
- package/dist/chunk-RXJLI2OP.js.map +1 -0
- package/dist/{chunk-6LCL2ZZF.js → chunk-TAH2GVTJ.js} +1 -1
- package/dist/chunk-TAH2GVTJ.js.map +1 -0
- package/dist/chunk-ZAT3R4CU.js +277 -0
- package/dist/chunk-ZAT3R4CU.js.map +1 -0
- package/dist/{client-0926d3d6.d.ts → client-ba0d3315.d.ts} +12 -15
- package/dist/{connection-99a67d3e.d.ts → connection-c3a96d09.d.ts} +1 -5
- package/dist/connection-d33e3246.d.ts +11 -0
- package/dist/{handshake-75d0124f.d.ts → handshake-cdead82a.d.ts} +149 -180
- package/dist/logging/index.cjs.map +1 -1
- package/dist/logging/index.d.cts +1 -1
- package/dist/logging/index.d.ts +1 -1
- package/dist/logging/index.js +1 -1
- package/dist/{index-ea74cdbb.d.ts → message-e6c560fd.d.ts} +2 -2
- package/dist/router/index.cjs +107 -530
- package/dist/router/index.cjs.map +1 -1
- package/dist/router/index.d.cts +12 -50
- package/dist/router/index.d.ts +12 -50
- package/dist/router/index.js +2 -4
- package/dist/server-2ef5e6ec.d.ts +42 -0
- package/dist/{services-75e84a9f.d.ts → services-e1417b33.d.ts} +7 -7
- package/dist/transport/impls/uds/client.cjs +1242 -1223
- package/dist/transport/impls/uds/client.cjs.map +1 -1
- package/dist/transport/impls/uds/client.d.cts +4 -4
- package/dist/transport/impls/uds/client.d.ts +4 -4
- package/dist/transport/impls/uds/client.js +7 -13
- package/dist/transport/impls/uds/client.js.map +1 -1
- package/dist/transport/impls/uds/server.cjs +1301 -1151
- package/dist/transport/impls/uds/server.cjs.map +1 -1
- package/dist/transport/impls/uds/server.d.cts +4 -4
- package/dist/transport/impls/uds/server.d.ts +4 -4
- package/dist/transport/impls/uds/server.js +6 -6
- package/dist/transport/impls/ws/client.cjs +980 -969
- package/dist/transport/impls/ws/client.cjs.map +1 -1
- package/dist/transport/impls/ws/client.d.cts +4 -4
- package/dist/transport/impls/ws/client.d.ts +4 -4
- package/dist/transport/impls/ws/client.js +6 -7
- package/dist/transport/impls/ws/client.js.map +1 -1
- package/dist/transport/impls/ws/server.cjs +1182 -1047
- package/dist/transport/impls/ws/server.cjs.map +1 -1
- package/dist/transport/impls/ws/server.d.cts +4 -4
- package/dist/transport/impls/ws/server.d.ts +4 -4
- package/dist/transport/impls/ws/server.js +6 -6
- package/dist/transport/index.cjs +1434 -1360
- package/dist/transport/index.cjs.map +1 -1
- package/dist/transport/index.d.cts +4 -4
- package/dist/transport/index.d.ts +4 -4
- package/dist/transport/index.js +9 -9
- package/dist/util/testHelpers.cjs +743 -309
- package/dist/util/testHelpers.cjs.map +1 -1
- package/dist/util/testHelpers.d.cts +10 -7
- package/dist/util/testHelpers.d.ts +10 -7
- package/dist/util/testHelpers.js +33 -10
- package/dist/util/testHelpers.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6LCL2ZZF.js.map +0 -1
- package/dist/chunk-JA7XGTAL.js +0 -476
- package/dist/chunk-JA7XGTAL.js.map +0 -1
- package/dist/chunk-LTSLICON.js.map +0 -1
- package/dist/chunk-MQCGG6KL.js +0 -335
- package/dist/chunk-MQCGG6KL.js.map +0 -1
- package/dist/chunk-R47IZD67.js +0 -59
- package/dist/chunk-R47IZD67.js.map +0 -1
- package/dist/chunk-TXSQRTZB.js.map +0 -1
- package/dist/chunk-UDXM64QK.js.map +0 -1
- package/dist/chunk-WN77AT67.js +0 -476
- package/dist/chunk-WN77AT67.js.map +0 -1
- package/dist/chunk-YXDAOVP7.js +0 -347
- package/dist/chunk-YXDAOVP7.js.map +0 -1
- package/dist/connection-d738cc08.d.ts +0 -17
- package/dist/server-3740c5d9.d.ts +0 -24
package/dist/transport/index.cjs
CHANGED
|
@@ -25,18 +25,189 @@ __export(transport_exports, {
|
|
|
25
25
|
OpaqueTransportMessageSchema: () => OpaqueTransportMessageSchema,
|
|
26
26
|
ProtocolError: () => ProtocolError,
|
|
27
27
|
ServerTransport: () => ServerTransport,
|
|
28
|
-
|
|
28
|
+
SessionState: () => SessionState,
|
|
29
29
|
Transport: () => Transport,
|
|
30
30
|
TransportMessageSchema: () => TransportMessageSchema
|
|
31
31
|
});
|
|
32
32
|
module.exports = __toCommonJS(transport_exports);
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
var
|
|
34
|
+
// logging/log.ts
|
|
35
|
+
var LoggingLevels = {
|
|
36
|
+
debug: -1,
|
|
37
|
+
info: 0,
|
|
38
|
+
warn: 1,
|
|
39
|
+
error: 2
|
|
40
|
+
};
|
|
41
|
+
var cleanedLogFn = (log) => {
|
|
42
|
+
return (msg, metadata) => {
|
|
43
|
+
if (!metadata?.transportMessage) {
|
|
44
|
+
log(msg, metadata);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const { payload, ...rest } = metadata.transportMessage;
|
|
48
|
+
metadata.transportMessage = rest;
|
|
49
|
+
log(msg, metadata);
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
var BaseLogger = class {
|
|
53
|
+
minLevel;
|
|
54
|
+
output;
|
|
55
|
+
constructor(output, minLevel = "info") {
|
|
56
|
+
this.minLevel = minLevel;
|
|
57
|
+
this.output = output;
|
|
58
|
+
}
|
|
59
|
+
debug(msg, metadata) {
|
|
60
|
+
if (LoggingLevels[this.minLevel] <= LoggingLevels.debug) {
|
|
61
|
+
this.output(msg, metadata ?? {}, "debug");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
info(msg, metadata) {
|
|
65
|
+
if (LoggingLevels[this.minLevel] <= LoggingLevels.info) {
|
|
66
|
+
this.output(msg, metadata ?? {}, "info");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
warn(msg, metadata) {
|
|
70
|
+
if (LoggingLevels[this.minLevel] <= LoggingLevels.warn) {
|
|
71
|
+
this.output(msg, metadata ?? {}, "warn");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
error(msg, metadata) {
|
|
75
|
+
if (LoggingLevels[this.minLevel] <= LoggingLevels.error) {
|
|
76
|
+
this.output(msg, metadata ?? {}, "error");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var createLogProxy = (log) => ({
|
|
81
|
+
debug: cleanedLogFn(log.debug.bind(log)),
|
|
82
|
+
info: cleanedLogFn(log.info.bind(log)),
|
|
83
|
+
warn: cleanedLogFn(log.warn.bind(log)),
|
|
84
|
+
error: cleanedLogFn(log.error.bind(log))
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// transport/events.ts
|
|
88
|
+
var ProtocolError = {
|
|
89
|
+
RetriesExceeded: "conn_retry_exceeded",
|
|
90
|
+
HandshakeFailed: "handshake_failed",
|
|
91
|
+
MessageOrderingViolated: "message_ordering_violated"
|
|
92
|
+
};
|
|
93
|
+
var EventDispatcher = class {
|
|
94
|
+
eventListeners = {};
|
|
95
|
+
removeAllListeners() {
|
|
96
|
+
this.eventListeners = {};
|
|
97
|
+
}
|
|
98
|
+
numberOfListeners(eventType) {
|
|
99
|
+
return this.eventListeners[eventType]?.size ?? 0;
|
|
100
|
+
}
|
|
101
|
+
addEventListener(eventType, handler) {
|
|
102
|
+
if (!this.eventListeners[eventType]) {
|
|
103
|
+
this.eventListeners[eventType] = /* @__PURE__ */ new Set();
|
|
104
|
+
}
|
|
105
|
+
this.eventListeners[eventType]?.add(handler);
|
|
106
|
+
}
|
|
107
|
+
removeEventListener(eventType, handler) {
|
|
108
|
+
const handlers = this.eventListeners[eventType];
|
|
109
|
+
if (handlers) {
|
|
110
|
+
this.eventListeners[eventType]?.delete(handler);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
dispatchEvent(eventType, event) {
|
|
114
|
+
const handlers = this.eventListeners[eventType];
|
|
115
|
+
if (handlers) {
|
|
116
|
+
const copy = [...handlers];
|
|
117
|
+
for (const handler of copy) {
|
|
118
|
+
handler(event);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// codec/json.ts
|
|
125
|
+
var encoder = new TextEncoder();
|
|
126
|
+
var decoder = new TextDecoder();
|
|
127
|
+
function uint8ArrayToBase64(uint8Array) {
|
|
128
|
+
let binary = "";
|
|
129
|
+
uint8Array.forEach((byte) => {
|
|
130
|
+
binary += String.fromCharCode(byte);
|
|
131
|
+
});
|
|
132
|
+
return btoa(binary);
|
|
133
|
+
}
|
|
134
|
+
function base64ToUint8Array(base64) {
|
|
135
|
+
const binaryString = atob(base64);
|
|
136
|
+
const uint8Array = new Uint8Array(binaryString.length);
|
|
137
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
138
|
+
uint8Array[i] = binaryString.charCodeAt(i);
|
|
139
|
+
}
|
|
140
|
+
return uint8Array;
|
|
141
|
+
}
|
|
142
|
+
var NaiveJsonCodec = {
|
|
143
|
+
toBuffer: (obj) => {
|
|
144
|
+
return encoder.encode(
|
|
145
|
+
JSON.stringify(obj, function replacer(key) {
|
|
146
|
+
const val = this[key];
|
|
147
|
+
if (val instanceof Uint8Array) {
|
|
148
|
+
return { $t: uint8ArrayToBase64(val) };
|
|
149
|
+
} else {
|
|
150
|
+
return val;
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
},
|
|
155
|
+
fromBuffer: (buff) => {
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(
|
|
158
|
+
decoder.decode(buff),
|
|
159
|
+
function reviver(_key, val) {
|
|
160
|
+
if (val?.$t) {
|
|
161
|
+
return base64ToUint8Array(val.$t);
|
|
162
|
+
} else {
|
|
163
|
+
return val;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
if (typeof parsed === "object")
|
|
168
|
+
return parsed;
|
|
169
|
+
return null;
|
|
170
|
+
} catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// transport/options.ts
|
|
177
|
+
var defaultTransportOptions = {
|
|
178
|
+
heartbeatIntervalMs: 1e3,
|
|
179
|
+
heartbeatsUntilDead: 2,
|
|
180
|
+
sessionDisconnectGraceMs: 5e3,
|
|
181
|
+
connectionTimeoutMs: 2e3,
|
|
182
|
+
handshakeTimeoutMs: 1e3,
|
|
183
|
+
codec: NaiveJsonCodec
|
|
184
|
+
};
|
|
185
|
+
var defaultConnectionRetryOptions = {
|
|
186
|
+
baseIntervalMs: 250,
|
|
187
|
+
maxJitterMs: 200,
|
|
188
|
+
maxBackoffMs: 32e3,
|
|
189
|
+
attemptBudgetCapacity: 5,
|
|
190
|
+
budgetRestoreIntervalMs: 200
|
|
191
|
+
};
|
|
192
|
+
var defaultClientTransportOptions = {
|
|
193
|
+
...defaultTransportOptions,
|
|
194
|
+
...defaultConnectionRetryOptions
|
|
195
|
+
};
|
|
196
|
+
var defaultServerTransportOptions = {
|
|
197
|
+
...defaultTransportOptions
|
|
198
|
+
};
|
|
36
199
|
|
|
37
200
|
// transport/message.ts
|
|
38
201
|
var import_typebox = require("@sinclair/typebox");
|
|
202
|
+
|
|
203
|
+
// transport/id.ts
|
|
39
204
|
var import_nanoid = require("nanoid");
|
|
205
|
+
var alphabet = (0, import_nanoid.customAlphabet)(
|
|
206
|
+
"1234567890abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ"
|
|
207
|
+
);
|
|
208
|
+
var generateId = () => alphabet(12);
|
|
209
|
+
|
|
210
|
+
// transport/message.ts
|
|
40
211
|
var TransportMessageSchema = (t) => import_typebox.Type.Object({
|
|
41
212
|
id: import_typebox.Type.String(),
|
|
42
213
|
from: import_typebox.Type.String(),
|
|
@@ -71,18 +242,29 @@ var ControlMessageHandshakeRequestSchema = import_typebox.Type.Object({
|
|
|
71
242
|
* used by the server to know whether this is a new or a reestablished connection, and whether it
|
|
72
243
|
* is compatible with what it already has.
|
|
73
244
|
*/
|
|
74
|
-
expectedSessionState: import_typebox.Type.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
})
|
|
83
|
-
),
|
|
245
|
+
expectedSessionState: import_typebox.Type.Object({
|
|
246
|
+
// what the client expects the server to send next
|
|
247
|
+
nextExpectedSeq: import_typebox.Type.Integer(),
|
|
248
|
+
// TODO: remove optional once we know all servers
|
|
249
|
+
// are nextSentSeq here
|
|
250
|
+
// what the server expects the client to send next
|
|
251
|
+
nextSentSeq: import_typebox.Type.Optional(import_typebox.Type.Integer())
|
|
252
|
+
}),
|
|
84
253
|
metadata: import_typebox.Type.Optional(import_typebox.Type.Unknown())
|
|
85
254
|
});
|
|
255
|
+
var HandshakeErrorRetriableResponseCodes = import_typebox.Type.Union([
|
|
256
|
+
import_typebox.Type.Literal("SESSION_STATE_MISMATCH")
|
|
257
|
+
]);
|
|
258
|
+
var HandshakeErrorFatalResponseCodes = import_typebox.Type.Union([
|
|
259
|
+
import_typebox.Type.Literal("MALFORMED_HANDSHAKE_META"),
|
|
260
|
+
import_typebox.Type.Literal("MALFORMED_HANDSHAKE"),
|
|
261
|
+
import_typebox.Type.Literal("PROTOCOL_VERSION_MISMATCH"),
|
|
262
|
+
import_typebox.Type.Literal("REJECTED_BY_CUSTOM_HANDLER")
|
|
263
|
+
]);
|
|
264
|
+
var HandshakeErrorResponseCodes = import_typebox.Type.Union([
|
|
265
|
+
HandshakeErrorRetriableResponseCodes,
|
|
266
|
+
HandshakeErrorFatalResponseCodes
|
|
267
|
+
]);
|
|
86
268
|
var ControlMessageHandshakeResponseSchema = import_typebox.Type.Object({
|
|
87
269
|
type: import_typebox.Type.Literal("HANDSHAKE_RESP"),
|
|
88
270
|
status: import_typebox.Type.Union([
|
|
@@ -92,7 +274,10 @@ var ControlMessageHandshakeResponseSchema = import_typebox.Type.Object({
|
|
|
92
274
|
}),
|
|
93
275
|
import_typebox.Type.Object({
|
|
94
276
|
ok: import_typebox.Type.Literal(false),
|
|
95
|
-
reason: import_typebox.Type.String()
|
|
277
|
+
reason: import_typebox.Type.String(),
|
|
278
|
+
// TODO: remove optional once we know all servers
|
|
279
|
+
// are sending code here
|
|
280
|
+
code: import_typebox.Type.Optional(HandshakeErrorResponseCodes)
|
|
96
281
|
})
|
|
97
282
|
])
|
|
98
283
|
});
|
|
@@ -114,12 +299,12 @@ function handshakeRequestMessage({
|
|
|
114
299
|
tracing
|
|
115
300
|
}) {
|
|
116
301
|
return {
|
|
117
|
-
id: (
|
|
302
|
+
id: generateId(),
|
|
118
303
|
from,
|
|
119
304
|
to,
|
|
120
305
|
seq: 0,
|
|
121
306
|
ack: 0,
|
|
122
|
-
streamId: (
|
|
307
|
+
streamId: generateId(),
|
|
123
308
|
controlFlags: 0,
|
|
124
309
|
tracing,
|
|
125
310
|
payload: {
|
|
@@ -131,19 +316,18 @@ function handshakeRequestMessage({
|
|
|
131
316
|
}
|
|
132
317
|
};
|
|
133
318
|
}
|
|
134
|
-
var SESSION_STATE_MISMATCH = "session state mismatch";
|
|
135
319
|
function handshakeResponseMessage({
|
|
136
320
|
from,
|
|
137
321
|
to,
|
|
138
322
|
status
|
|
139
323
|
}) {
|
|
140
324
|
return {
|
|
141
|
-
id: (
|
|
325
|
+
id: generateId(),
|
|
142
326
|
from,
|
|
143
327
|
to,
|
|
144
328
|
seq: 0,
|
|
145
329
|
ack: 0,
|
|
146
|
-
streamId: (
|
|
330
|
+
streamId: generateId(),
|
|
147
331
|
controlFlags: 0,
|
|
148
332
|
payload: {
|
|
149
333
|
type: "HANDSHAKE_RESP",
|
|
@@ -155,104 +339,230 @@ function isAck(controlFlag) {
|
|
|
155
339
|
return (controlFlag & 1 /* AckBit */) === 1 /* AckBit */;
|
|
156
340
|
}
|
|
157
341
|
|
|
158
|
-
//
|
|
159
|
-
var
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
minLevel;
|
|
178
|
-
output;
|
|
179
|
-
constructor(output, minLevel = "info") {
|
|
180
|
-
this.minLevel = minLevel;
|
|
181
|
-
this.output = output;
|
|
342
|
+
// transport/sessionStateMachine/common.ts
|
|
343
|
+
var import_value = require("@sinclair/typebox/value");
|
|
344
|
+
var SessionState = /* @__PURE__ */ ((SessionState2) => {
|
|
345
|
+
SessionState2["NoConnection"] = "NoConnection";
|
|
346
|
+
SessionState2["Connecting"] = "Connecting";
|
|
347
|
+
SessionState2["Handshaking"] = "Handshaking";
|
|
348
|
+
SessionState2["Connected"] = "Connected";
|
|
349
|
+
SessionState2["WaitingForHandshake"] = "WaitingForHandshake";
|
|
350
|
+
return SessionState2;
|
|
351
|
+
})(SessionState || {});
|
|
352
|
+
var ERR_CONSUMED = `session state has been consumed and is no longer valid`;
|
|
353
|
+
var StateMachineState = class {
|
|
354
|
+
/*
|
|
355
|
+
* Whether this state has been consumed
|
|
356
|
+
* and we've moved on to another state
|
|
357
|
+
*/
|
|
358
|
+
_isConsumed;
|
|
359
|
+
close() {
|
|
360
|
+
this._handleClose();
|
|
182
361
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
362
|
+
constructor() {
|
|
363
|
+
this._isConsumed = false;
|
|
364
|
+
return new Proxy(this, {
|
|
365
|
+
get(target, prop) {
|
|
366
|
+
if (prop === "_isConsumed" || prop === "id" || prop === "state") {
|
|
367
|
+
return Reflect.get(target, prop);
|
|
368
|
+
}
|
|
369
|
+
if (prop === "_handleStateExit") {
|
|
370
|
+
return () => {
|
|
371
|
+
target._isConsumed = true;
|
|
372
|
+
target._handleStateExit();
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
if (prop === "_handleClose") {
|
|
376
|
+
return () => {
|
|
377
|
+
target._handleStateExit();
|
|
378
|
+
target._handleClose();
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
if (target._isConsumed) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`${ERR_CONSUMED}: getting ${prop.toString()} on consumed state`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
return Reflect.get(target, prop);
|
|
387
|
+
},
|
|
388
|
+
set(target, prop, value) {
|
|
389
|
+
if (target._isConsumed) {
|
|
390
|
+
throw new Error(
|
|
391
|
+
`${ERR_CONSUMED}: setting ${prop.toString()} on consumed state`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
return Reflect.set(target, prop, value);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
187
397
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
398
|
+
};
|
|
399
|
+
var CommonSession = class extends StateMachineState {
|
|
400
|
+
from;
|
|
401
|
+
options;
|
|
402
|
+
log;
|
|
403
|
+
constructor(from, options, log) {
|
|
404
|
+
super();
|
|
405
|
+
this.from = from;
|
|
406
|
+
this.options = options;
|
|
407
|
+
this.log = log;
|
|
192
408
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
409
|
+
parseMsg(msg) {
|
|
410
|
+
const parsedMsg = this.options.codec.fromBuffer(msg);
|
|
411
|
+
if (parsedMsg === null) {
|
|
412
|
+
const decodedBuffer = new TextDecoder().decode(Buffer.from(msg));
|
|
413
|
+
this.log?.error(
|
|
414
|
+
`received malformed msg: ${decodedBuffer}`,
|
|
415
|
+
this.loggingMetadata
|
|
416
|
+
);
|
|
417
|
+
return null;
|
|
196
418
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
419
|
+
if (!import_value.Value.Check(OpaqueTransportMessageSchema, parsedMsg)) {
|
|
420
|
+
this.log?.error(`received invalid msg: ${JSON.stringify(parsedMsg)}`, {
|
|
421
|
+
...this.loggingMetadata,
|
|
422
|
+
validationErrors: [
|
|
423
|
+
...import_value.Value.Errors(OpaqueTransportMessageSchema, parsedMsg)
|
|
424
|
+
]
|
|
425
|
+
});
|
|
426
|
+
return null;
|
|
201
427
|
}
|
|
428
|
+
return parsedMsg;
|
|
202
429
|
}
|
|
203
430
|
};
|
|
204
|
-
var
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
this.
|
|
431
|
+
var IdentifiedSession = class extends CommonSession {
|
|
432
|
+
id;
|
|
433
|
+
telemetry;
|
|
434
|
+
to;
|
|
435
|
+
/**
|
|
436
|
+
* Index of the message we will send next (excluding handshake)
|
|
437
|
+
*/
|
|
438
|
+
seq;
|
|
439
|
+
/**
|
|
440
|
+
* Number of unique messages we've received this session (excluding handshake)
|
|
441
|
+
*/
|
|
442
|
+
ack;
|
|
443
|
+
sendBuffer;
|
|
444
|
+
constructor(id, from, to, seq, ack, sendBuffer, telemetry, options, log) {
|
|
445
|
+
super(from, options, log);
|
|
446
|
+
this.id = id;
|
|
447
|
+
this.to = to;
|
|
448
|
+
this.seq = seq;
|
|
449
|
+
this.ack = ack;
|
|
450
|
+
this.sendBuffer = sendBuffer;
|
|
451
|
+
this.telemetry = telemetry;
|
|
452
|
+
this.log = log;
|
|
221
453
|
}
|
|
222
|
-
|
|
223
|
-
|
|
454
|
+
get loggingMetadata() {
|
|
455
|
+
const spanContext = this.telemetry.span.spanContext();
|
|
456
|
+
return {
|
|
457
|
+
clientId: this.from,
|
|
458
|
+
connectedTo: this.to,
|
|
459
|
+
sessionId: this.id,
|
|
460
|
+
telemetry: {
|
|
461
|
+
traceId: spanContext.traceId,
|
|
462
|
+
spanId: spanContext.spanId
|
|
463
|
+
}
|
|
464
|
+
};
|
|
224
465
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
466
|
+
constructMsg(partialMsg) {
|
|
467
|
+
const msg = {
|
|
468
|
+
...partialMsg,
|
|
469
|
+
id: generateId(),
|
|
470
|
+
to: this.to,
|
|
471
|
+
from: this.from,
|
|
472
|
+
seq: this.seq,
|
|
473
|
+
ack: this.ack
|
|
474
|
+
};
|
|
475
|
+
this.seq++;
|
|
476
|
+
return msg;
|
|
230
477
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if (handlers) {
|
|
234
|
-
this.eventListeners[eventType]?.delete(handler);
|
|
235
|
-
}
|
|
478
|
+
nextSeq() {
|
|
479
|
+
return this.sendBuffer.length > 0 ? this.sendBuffer[0].seq : this.seq;
|
|
236
480
|
}
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
481
|
+
send(msg) {
|
|
482
|
+
const constructedMsg = this.constructMsg(msg);
|
|
483
|
+
this.sendBuffer.push(constructedMsg);
|
|
484
|
+
return constructedMsg.id;
|
|
485
|
+
}
|
|
486
|
+
_handleStateExit() {
|
|
487
|
+
}
|
|
488
|
+
_handleClose() {
|
|
489
|
+
this.sendBuffer.length = 0;
|
|
490
|
+
this.telemetry.span.end();
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
// transport/sessionStateMachine/SessionConnecting.ts
|
|
495
|
+
var SessionConnecting = class extends IdentifiedSession {
|
|
496
|
+
state = "Connecting" /* Connecting */;
|
|
497
|
+
connPromise;
|
|
498
|
+
listeners;
|
|
499
|
+
connectionTimeout;
|
|
500
|
+
constructor(connPromise, listeners, ...args) {
|
|
501
|
+
super(...args);
|
|
502
|
+
this.connPromise = connPromise;
|
|
503
|
+
this.listeners = listeners;
|
|
504
|
+
this.connectionTimeout = setTimeout(() => {
|
|
505
|
+
listeners.onConnectionTimeout();
|
|
506
|
+
}, this.options.connectionTimeoutMs);
|
|
507
|
+
connPromise.then(
|
|
508
|
+
(conn) => {
|
|
509
|
+
if (this._isConsumed)
|
|
510
|
+
return;
|
|
511
|
+
listeners.onConnectionEstablished(conn);
|
|
512
|
+
},
|
|
513
|
+
(err) => {
|
|
514
|
+
if (this._isConsumed)
|
|
515
|
+
return;
|
|
516
|
+
listeners.onConnectionFailed(err);
|
|
243
517
|
}
|
|
244
|
-
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
// close a pending connection if it resolves, ignore errors if the promise
|
|
521
|
+
// ends up rejected anyways
|
|
522
|
+
bestEffortClose() {
|
|
523
|
+
void this.connPromise.then((conn) => conn.close()).catch(() => {
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
_handleStateExit() {
|
|
527
|
+
super._handleStateExit();
|
|
528
|
+
clearTimeout(this.connectionTimeout);
|
|
529
|
+
this.connectionTimeout = void 0;
|
|
530
|
+
}
|
|
531
|
+
_handleClose() {
|
|
532
|
+
this.bestEffortClose();
|
|
533
|
+
super._handleClose();
|
|
245
534
|
}
|
|
246
535
|
};
|
|
247
536
|
|
|
248
|
-
// transport/
|
|
249
|
-
var
|
|
537
|
+
// transport/sessionStateMachine/SessionNoConnection.ts
|
|
538
|
+
var SessionNoConnection = class extends IdentifiedSession {
|
|
539
|
+
state = "NoConnection" /* NoConnection */;
|
|
540
|
+
listeners;
|
|
541
|
+
gracePeriodTimeout;
|
|
542
|
+
constructor(listeners, ...args) {
|
|
543
|
+
super(...args);
|
|
544
|
+
this.listeners = listeners;
|
|
545
|
+
this.gracePeriodTimeout = setTimeout(() => {
|
|
546
|
+
this.listeners.onSessionGracePeriodElapsed();
|
|
547
|
+
}, this.options.sessionDisconnectGraceMs);
|
|
548
|
+
}
|
|
549
|
+
_handleClose() {
|
|
550
|
+
super._handleClose();
|
|
551
|
+
}
|
|
552
|
+
_handleStateExit() {
|
|
553
|
+
super._handleStateExit();
|
|
554
|
+
if (this.gracePeriodTimeout) {
|
|
555
|
+
clearTimeout(this.gracePeriodTimeout);
|
|
556
|
+
this.gracePeriodTimeout = void 0;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
250
560
|
|
|
251
561
|
// tracing/index.ts
|
|
252
562
|
var import_api = require("@opentelemetry/api");
|
|
253
563
|
|
|
254
564
|
// package.json
|
|
255
|
-
var version = "0.
|
|
565
|
+
var version = "0.24.0";
|
|
256
566
|
|
|
257
567
|
// tracing/index.ts
|
|
258
568
|
function getPropagationContext(ctx) {
|
|
@@ -263,16 +573,16 @@ function getPropagationContext(ctx) {
|
|
|
263
573
|
import_api.propagation.inject(ctx, tracing);
|
|
264
574
|
return tracing;
|
|
265
575
|
}
|
|
266
|
-
function createSessionTelemetryInfo(
|
|
576
|
+
function createSessionTelemetryInfo(sessionId, to, from, propagationCtx) {
|
|
267
577
|
const parentCtx = propagationCtx ? import_api.propagation.extract(import_api.context.active(), propagationCtx) : import_api.context.active();
|
|
268
578
|
const span = tracer.startSpan(
|
|
269
|
-
`session ${
|
|
579
|
+
`session ${sessionId}`,
|
|
270
580
|
{
|
|
271
581
|
attributes: {
|
|
272
582
|
component: "river",
|
|
273
|
-
"river.session.id":
|
|
274
|
-
"river.session.to":
|
|
275
|
-
"river.session.from":
|
|
583
|
+
"river.session.id": sessionId,
|
|
584
|
+
"river.session.to": to,
|
|
585
|
+
"river.session.from": from
|
|
276
586
|
}
|
|
277
587
|
},
|
|
278
588
|
parentCtx
|
|
@@ -280,173 +590,155 @@ function createSessionTelemetryInfo(session, propagationCtx) {
|
|
|
280
590
|
const ctx = import_api.trace.setSpan(parentCtx, span);
|
|
281
591
|
return { span, ctx };
|
|
282
592
|
}
|
|
283
|
-
function createConnectionTelemetryInfo(connection, info) {
|
|
284
|
-
const span = tracer.startSpan(
|
|
285
|
-
`connection ${connection.id}`,
|
|
286
|
-
{
|
|
287
|
-
attributes: {
|
|
288
|
-
component: "river",
|
|
289
|
-
"river.connection.id": connection.id
|
|
290
|
-
},
|
|
291
|
-
links: [{ context: info.span.spanContext() }]
|
|
292
|
-
},
|
|
293
|
-
info.ctx
|
|
294
|
-
);
|
|
295
|
-
const ctx = import_api.trace.setSpan(info.ctx, span);
|
|
296
|
-
return { span, ctx };
|
|
297
|
-
}
|
|
298
593
|
var tracer = import_api.trace.getTracer("river", version);
|
|
299
594
|
var tracing_default = tracer;
|
|
300
595
|
|
|
301
|
-
// transport/
|
|
302
|
-
var
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
this.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
596
|
+
// transport/sessionStateMachine/SessionWaitingForHandshake.ts
|
|
597
|
+
var SessionWaitingForHandshake = class extends CommonSession {
|
|
598
|
+
state = "WaitingForHandshake" /* WaitingForHandshake */;
|
|
599
|
+
conn;
|
|
600
|
+
listeners;
|
|
601
|
+
handshakeTimeout;
|
|
602
|
+
constructor(conn, listeners, ...args) {
|
|
603
|
+
super(...args);
|
|
604
|
+
this.conn = conn;
|
|
605
|
+
this.listeners = listeners;
|
|
606
|
+
this.handshakeTimeout = setTimeout(() => {
|
|
607
|
+
listeners.onHandshakeTimeout();
|
|
608
|
+
}, this.options.handshakeTimeoutMs);
|
|
609
|
+
this.conn.addDataListener(this.onHandshakeData);
|
|
610
|
+
this.conn.addErrorListener(listeners.onConnectionErrored);
|
|
611
|
+
this.conn.addCloseListener(listeners.onConnectionClosed);
|
|
612
|
+
}
|
|
613
|
+
onHandshakeData = (msg) => {
|
|
614
|
+
const parsedMsg = this.parseMsg(msg);
|
|
615
|
+
if (parsedMsg === null) {
|
|
616
|
+
this.listeners.onInvalidHandshake("could not parse message");
|
|
617
|
+
return;
|
|
319
618
|
}
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
var Session = class {
|
|
324
|
-
codec;
|
|
325
|
-
options;
|
|
326
|
-
telemetry;
|
|
327
|
-
/**
|
|
328
|
-
* The buffer of messages that have been sent but not yet acknowledged.
|
|
329
|
-
*/
|
|
330
|
-
sendBuffer = [];
|
|
331
|
-
/**
|
|
332
|
-
* The active connection associated with this session
|
|
333
|
-
*/
|
|
334
|
-
connection;
|
|
335
|
-
/**
|
|
336
|
-
* A connection that is currently undergoing handshaking. Used to distinguish between the active
|
|
337
|
-
* connection, but still be able to close it if needed.
|
|
338
|
-
*/
|
|
339
|
-
handshakingConnection;
|
|
340
|
-
from;
|
|
341
|
-
to;
|
|
342
|
-
/**
|
|
343
|
-
* The unique ID of this session.
|
|
344
|
-
*/
|
|
345
|
-
id;
|
|
346
|
-
/**
|
|
347
|
-
* What the other side advertised as their session ID
|
|
348
|
-
* for this session.
|
|
349
|
-
*/
|
|
350
|
-
advertisedSessionId;
|
|
351
|
-
/**
|
|
352
|
-
* Number of messages we've sent along this session (excluding handshake and acks)
|
|
353
|
-
*/
|
|
354
|
-
seq = 0;
|
|
355
|
-
/**
|
|
356
|
-
* Number of unique messages we've received this session (excluding handshake and acks)
|
|
357
|
-
*/
|
|
358
|
-
ack = 0;
|
|
359
|
-
/**
|
|
360
|
-
* The grace period between when the inner connection is disconnected
|
|
361
|
-
* and when we should consider the entire session disconnected.
|
|
362
|
-
*/
|
|
363
|
-
disconnectionGrace;
|
|
364
|
-
/**
|
|
365
|
-
* Number of heartbeats we've sent without a response.
|
|
366
|
-
*/
|
|
367
|
-
heartbeatMisses;
|
|
368
|
-
/**
|
|
369
|
-
* The interval for sending heartbeats.
|
|
370
|
-
*/
|
|
371
|
-
heartbeat;
|
|
372
|
-
log;
|
|
373
|
-
constructor(conn, from, to, options, propagationCtx) {
|
|
374
|
-
this.id = `session-${nanoid2(12)}`;
|
|
375
|
-
this.options = options;
|
|
376
|
-
this.from = from;
|
|
377
|
-
this.to = to;
|
|
378
|
-
this.connection = conn;
|
|
379
|
-
this.codec = options.codec;
|
|
380
|
-
this.heartbeatMisses = 0;
|
|
381
|
-
this.heartbeat = setInterval(
|
|
382
|
-
() => this.sendHeartbeat(),
|
|
383
|
-
options.heartbeatIntervalMs
|
|
384
|
-
);
|
|
385
|
-
this.telemetry = createSessionTelemetryInfo(this, propagationCtx);
|
|
386
|
-
}
|
|
387
|
-
bindLogger(log) {
|
|
388
|
-
this.log = log;
|
|
389
|
-
}
|
|
619
|
+
this.listeners.onHandshake(parsedMsg);
|
|
620
|
+
};
|
|
390
621
|
get loggingMetadata() {
|
|
391
|
-
const spanContext = this.telemetry.span.spanContext();
|
|
392
622
|
return {
|
|
393
623
|
clientId: this.from,
|
|
394
|
-
|
|
395
|
-
sessionId: this.id,
|
|
396
|
-
connId: this.connection?.id,
|
|
397
|
-
telemetry: {
|
|
398
|
-
traceId: spanContext.traceId,
|
|
399
|
-
spanId: spanContext.spanId
|
|
400
|
-
}
|
|
624
|
+
connId: this.conn.id
|
|
401
625
|
};
|
|
402
626
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
627
|
+
sendHandshake(msg) {
|
|
628
|
+
return this.conn.send(this.options.codec.toBuffer(msg));
|
|
629
|
+
}
|
|
630
|
+
_handleStateExit() {
|
|
631
|
+
this.conn.removeDataListener(this.onHandshakeData);
|
|
632
|
+
this.conn.removeErrorListener(this.listeners.onConnectionErrored);
|
|
633
|
+
this.conn.removeCloseListener(this.listeners.onConnectionClosed);
|
|
634
|
+
clearTimeout(this.handshakeTimeout);
|
|
635
|
+
this.handshakeTimeout = void 0;
|
|
636
|
+
}
|
|
637
|
+
_handleClose() {
|
|
638
|
+
this.conn.close();
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
// transport/sessionStateMachine/SessionHandshaking.ts
|
|
643
|
+
var SessionHandshaking = class extends IdentifiedSession {
|
|
644
|
+
state = "Handshaking" /* Handshaking */;
|
|
645
|
+
conn;
|
|
646
|
+
listeners;
|
|
647
|
+
handshakeTimeout;
|
|
648
|
+
constructor(conn, listeners, ...args) {
|
|
649
|
+
super(...args);
|
|
650
|
+
this.conn = conn;
|
|
651
|
+
this.listeners = listeners;
|
|
652
|
+
this.handshakeTimeout = setTimeout(() => {
|
|
653
|
+
listeners.onHandshakeTimeout();
|
|
654
|
+
}, this.options.handshakeTimeoutMs);
|
|
655
|
+
this.conn.addDataListener(this.onHandshakeData);
|
|
656
|
+
this.conn.addErrorListener(listeners.onConnectionErrored);
|
|
657
|
+
this.conn.addCloseListener(listeners.onConnectionClosed);
|
|
658
|
+
}
|
|
659
|
+
onHandshakeData = (msg) => {
|
|
660
|
+
const parsedMsg = this.parseMsg(msg);
|
|
661
|
+
if (parsedMsg === null) {
|
|
662
|
+
this.listeners.onInvalidHandshake("could not parse message");
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
this.listeners.onHandshake(parsedMsg);
|
|
666
|
+
};
|
|
667
|
+
sendHandshake(msg) {
|
|
668
|
+
return this.conn.send(this.options.codec.toBuffer(msg));
|
|
669
|
+
}
|
|
670
|
+
_handleStateExit() {
|
|
671
|
+
super._handleStateExit();
|
|
672
|
+
this.conn.removeDataListener(this.onHandshakeData);
|
|
673
|
+
this.conn.removeErrorListener(this.listeners.onConnectionErrored);
|
|
674
|
+
this.conn.removeCloseListener(this.listeners.onConnectionClosed);
|
|
675
|
+
clearTimeout(this.handshakeTimeout);
|
|
676
|
+
}
|
|
677
|
+
_handleClose() {
|
|
678
|
+
super._handleClose();
|
|
679
|
+
this.conn.close();
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
// transport/sessionStateMachine/SessionConnected.ts
|
|
684
|
+
var import_api2 = require("@opentelemetry/api");
|
|
685
|
+
var SessionConnected = class extends IdentifiedSession {
|
|
686
|
+
state = "Connected" /* Connected */;
|
|
687
|
+
conn;
|
|
688
|
+
listeners;
|
|
689
|
+
heartbeatHandle;
|
|
690
|
+
heartbeatMisses = 0;
|
|
691
|
+
get isActivelyHeartbeating() {
|
|
692
|
+
return this.heartbeatHandle !== void 0;
|
|
693
|
+
}
|
|
694
|
+
updateBookkeeping(ack, seq) {
|
|
695
|
+
this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq >= ack);
|
|
696
|
+
this.ack = seq + 1;
|
|
697
|
+
this.heartbeatMisses = 0;
|
|
698
|
+
}
|
|
411
699
|
send(msg) {
|
|
412
|
-
const
|
|
413
|
-
this.
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
transportMessage: fullMsg
|
|
426
|
-
}
|
|
427
|
-
);
|
|
428
|
-
} else {
|
|
700
|
+
const constructedMsg = this.constructMsg(msg);
|
|
701
|
+
this.sendBuffer.push(constructedMsg);
|
|
702
|
+
this.conn.send(this.options.codec.toBuffer(constructedMsg));
|
|
703
|
+
return constructedMsg.id;
|
|
704
|
+
}
|
|
705
|
+
constructor(conn, listeners, ...args) {
|
|
706
|
+
super(...args);
|
|
707
|
+
this.conn = conn;
|
|
708
|
+
this.listeners = listeners;
|
|
709
|
+
this.conn.addDataListener(this.onMessageData);
|
|
710
|
+
this.conn.addCloseListener(listeners.onConnectionClosed);
|
|
711
|
+
this.conn.addErrorListener(listeners.onConnectionErrored);
|
|
712
|
+
if (this.sendBuffer.length > 0) {
|
|
429
713
|
this.log?.debug(
|
|
430
|
-
`
|
|
431
|
-
|
|
714
|
+
`sending ${this.sendBuffer.length} buffered messages`,
|
|
715
|
+
this.loggingMetadata
|
|
432
716
|
);
|
|
433
717
|
}
|
|
434
|
-
|
|
718
|
+
for (const msg of this.sendBuffer) {
|
|
719
|
+
conn.send(this.options.codec.toBuffer(msg));
|
|
720
|
+
}
|
|
435
721
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (this.
|
|
722
|
+
startActiveHeartbeat() {
|
|
723
|
+
this.heartbeatHandle = setInterval(() => {
|
|
724
|
+
const misses = this.heartbeatMisses;
|
|
725
|
+
const missDuration = misses * this.options.heartbeatIntervalMs;
|
|
726
|
+
if (misses >= this.options.heartbeatsUntilDead) {
|
|
441
727
|
this.log?.info(
|
|
442
728
|
`closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
|
|
443
729
|
this.loggingMetadata
|
|
444
730
|
);
|
|
445
731
|
this.telemetry.span.addEvent("closing connection due to inactivity");
|
|
446
|
-
this.
|
|
732
|
+
this.conn.close();
|
|
733
|
+
clearInterval(this.heartbeatHandle);
|
|
734
|
+
this.heartbeatHandle = void 0;
|
|
735
|
+
return;
|
|
447
736
|
}
|
|
448
|
-
|
|
449
|
-
|
|
737
|
+
this.sendHeartbeat();
|
|
738
|
+
this.heartbeatMisses++;
|
|
739
|
+
}, this.options.heartbeatIntervalMs);
|
|
740
|
+
}
|
|
741
|
+
sendHeartbeat() {
|
|
450
742
|
this.send({
|
|
451
743
|
streamId: "heartbeat",
|
|
452
744
|
controlFlags: 1 /* AckBit */,
|
|
@@ -454,585 +746,293 @@ var Session = class {
|
|
|
454
746
|
type: "ACK"
|
|
455
747
|
}
|
|
456
748
|
});
|
|
457
|
-
this.heartbeatMisses++;
|
|
458
749
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const ok = conn.send(this.codec.toBuffer(msg));
|
|
476
|
-
if (!ok) {
|
|
477
|
-
const errMsg = `failed to send buffered message to ${this.to} (sus, this is a fresh connection)`;
|
|
478
|
-
conn.telemetry?.span.setStatus({
|
|
479
|
-
code: import_api2.SpanStatusCode.ERROR,
|
|
480
|
-
message: errMsg
|
|
481
|
-
});
|
|
482
|
-
this.log?.error(errMsg, {
|
|
750
|
+
onMessageData = (msg) => {
|
|
751
|
+
const parsedMsg = this.parseMsg(msg);
|
|
752
|
+
if (parsedMsg === null)
|
|
753
|
+
return;
|
|
754
|
+
if (parsedMsg.seq !== this.ack) {
|
|
755
|
+
if (parsedMsg.seq < this.ack) {
|
|
756
|
+
this.log?.debug(
|
|
757
|
+
`received duplicate msg (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack}), discarding`,
|
|
758
|
+
{
|
|
759
|
+
...this.loggingMetadata,
|
|
760
|
+
transportMessage: parsedMsg
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
} else {
|
|
764
|
+
const reason = `received out-of-order msg (got seq: ${parsedMsg.seq}, wanted seq: ${this.ack})`;
|
|
765
|
+
this.log?.error(reason, {
|
|
483
766
|
...this.loggingMetadata,
|
|
484
|
-
transportMessage:
|
|
485
|
-
connId: conn.id,
|
|
767
|
+
transportMessage: parsedMsg,
|
|
486
768
|
tags: ["invariant-violation"]
|
|
487
769
|
});
|
|
488
|
-
|
|
489
|
-
|
|
770
|
+
this.telemetry.span.setStatus({
|
|
771
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
772
|
+
message: reason
|
|
773
|
+
});
|
|
774
|
+
this.listeners.onInvalidMessage(reason);
|
|
490
775
|
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
updateBookkeeping(ack, seq) {
|
|
494
|
-
if (seq + 1 < this.ack) {
|
|
495
|
-
this.log?.error(`received stale seq ${seq} + 1 < ${this.ack}`, {
|
|
496
|
-
...this.loggingMetadata,
|
|
497
|
-
tags: ["invariant-violation"]
|
|
498
|
-
});
|
|
499
776
|
return;
|
|
500
777
|
}
|
|
501
|
-
this.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
`closing old inner connection from session to ${this.to}`,
|
|
509
|
-
this.loggingMetadata
|
|
510
|
-
);
|
|
511
|
-
this.connection.close();
|
|
512
|
-
this.connection = void 0;
|
|
513
|
-
}
|
|
514
|
-
replaceWithNewConnection(newConn, isTransparentReconnect) {
|
|
515
|
-
this.closeStaleConnection(newConn);
|
|
516
|
-
this.cancelGrace();
|
|
517
|
-
if (isTransparentReconnect) {
|
|
518
|
-
this.sendBufferedMessages(newConn);
|
|
519
|
-
}
|
|
520
|
-
this.connection = newConn;
|
|
521
|
-
this.handshakingConnection = void 0;
|
|
522
|
-
}
|
|
523
|
-
replaceWithNewHandshakingConnection(newConn) {
|
|
524
|
-
this.handshakingConnection = newConn;
|
|
525
|
-
}
|
|
526
|
-
beginGrace(cb) {
|
|
527
|
-
this.log?.info(
|
|
528
|
-
`starting ${this.options.sessionDisconnectGraceMs}ms grace period until session to ${this.to} is closed`,
|
|
529
|
-
this.loggingMetadata
|
|
530
|
-
);
|
|
531
|
-
this.cancelGrace();
|
|
532
|
-
this.disconnectionGrace = setTimeout(() => {
|
|
533
|
-
this.log?.info(
|
|
534
|
-
`grace period for ${this.to} elapsed`,
|
|
535
|
-
this.loggingMetadata
|
|
536
|
-
);
|
|
537
|
-
cb();
|
|
538
|
-
}, this.options.sessionDisconnectGraceMs);
|
|
539
|
-
}
|
|
540
|
-
// called on reconnect of the underlying session
|
|
541
|
-
cancelGrace() {
|
|
542
|
-
this.heartbeatMisses = 0;
|
|
543
|
-
clearTimeout(this.disconnectionGrace);
|
|
544
|
-
this.disconnectionGrace = void 0;
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* Used to close the handshaking connection, if set.
|
|
548
|
-
*/
|
|
549
|
-
closeHandshakingConnection(expectedHandshakingConn) {
|
|
550
|
-
if (this.handshakingConnection === void 0)
|
|
551
|
-
return;
|
|
552
|
-
if (expectedHandshakingConn !== void 0 && this.handshakingConnection === expectedHandshakingConn) {
|
|
778
|
+
this.log?.debug(`received msg`, {
|
|
779
|
+
...this.loggingMetadata,
|
|
780
|
+
transportMessage: parsedMsg
|
|
781
|
+
});
|
|
782
|
+
this.updateBookkeeping(parsedMsg.ack, parsedMsg.seq);
|
|
783
|
+
if (!isAck(parsedMsg.controlFlags)) {
|
|
784
|
+
this.listeners.onMessage(parsedMsg);
|
|
553
785
|
return;
|
|
554
786
|
}
|
|
555
|
-
this.
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
this.closeStaleConnection();
|
|
562
|
-
this.cancelGrace();
|
|
563
|
-
this.resetBufferedMessages();
|
|
564
|
-
clearInterval(this.heartbeat);
|
|
565
|
-
}
|
|
566
|
-
get connected() {
|
|
567
|
-
return this.connection !== void 0;
|
|
568
|
-
}
|
|
569
|
-
get nextExpectedAck() {
|
|
570
|
-
return this.seq;
|
|
571
|
-
}
|
|
572
|
-
get nextExpectedSeq() {
|
|
573
|
-
return this.ack;
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Check that the peer's next expected seq number matches something that is in our send buffer
|
|
577
|
-
* _or_ matches our actual next seq.
|
|
578
|
-
*/
|
|
579
|
-
nextExpectedSeqInRange(nextExpectedSeq) {
|
|
580
|
-
for (const msg of this.sendBuffer) {
|
|
581
|
-
if (nextExpectedSeq === msg.seq) {
|
|
582
|
-
return true;
|
|
583
|
-
}
|
|
787
|
+
this.log?.debug(`discarding msg (ack bit set)`, {
|
|
788
|
+
...this.loggingMetadata,
|
|
789
|
+
transportMessage: parsedMsg
|
|
790
|
+
});
|
|
791
|
+
if (!this.isActivelyHeartbeating) {
|
|
792
|
+
this.sendHeartbeat();
|
|
584
793
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
this.
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
from: this.from,
|
|
598
|
-
seq: this.seq,
|
|
599
|
-
ack: this.ack
|
|
600
|
-
};
|
|
601
|
-
this.seq++;
|
|
602
|
-
this.sendBuffer.push(msg);
|
|
603
|
-
return msg;
|
|
604
|
-
}
|
|
605
|
-
inspectSendBuffer() {
|
|
606
|
-
return this.sendBuffer;
|
|
794
|
+
};
|
|
795
|
+
_handleStateExit() {
|
|
796
|
+
super._handleStateExit();
|
|
797
|
+
this.conn.removeDataListener(this.onMessageData);
|
|
798
|
+
this.conn.removeCloseListener(this.listeners.onConnectionClosed);
|
|
799
|
+
this.conn.removeErrorListener(this.listeners.onConnectionErrored);
|
|
800
|
+
clearInterval(this.heartbeatHandle);
|
|
801
|
+
this.heartbeatHandle = void 0;
|
|
802
|
+
}
|
|
803
|
+
_handleClose() {
|
|
804
|
+
super._handleClose();
|
|
805
|
+
this.conn.close();
|
|
607
806
|
}
|
|
608
807
|
};
|
|
609
808
|
|
|
610
|
-
// transport/
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
function base64ToUint8Array(base64) {
|
|
624
|
-
const binaryString = atob(base64);
|
|
625
|
-
const uint8Array = new Uint8Array(binaryString.length);
|
|
626
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
627
|
-
uint8Array[i] = binaryString.charCodeAt(i);
|
|
628
|
-
}
|
|
629
|
-
return uint8Array;
|
|
809
|
+
// transport/sessionStateMachine/transitions.ts
|
|
810
|
+
function inheritSharedSession(session) {
|
|
811
|
+
return [
|
|
812
|
+
session.id,
|
|
813
|
+
session.from,
|
|
814
|
+
session.to,
|
|
815
|
+
session.seq,
|
|
816
|
+
session.ack,
|
|
817
|
+
session.sendBuffer,
|
|
818
|
+
session.telemetry,
|
|
819
|
+
session.options,
|
|
820
|
+
session.log
|
|
821
|
+
];
|
|
630
822
|
}
|
|
631
|
-
var
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
823
|
+
var SessionStateGraph = {
|
|
824
|
+
entrypoints: {
|
|
825
|
+
NoConnection(to, from, listeners, options, log) {
|
|
826
|
+
const id = `session-${generateId()}`;
|
|
827
|
+
const telemetry = createSessionTelemetryInfo(id, to, from);
|
|
828
|
+
const sendBuffer = [];
|
|
829
|
+
const session = new SessionNoConnection(
|
|
830
|
+
listeners,
|
|
831
|
+
id,
|
|
832
|
+
from,
|
|
833
|
+
to,
|
|
834
|
+
0,
|
|
835
|
+
0,
|
|
836
|
+
sendBuffer,
|
|
837
|
+
telemetry,
|
|
838
|
+
options,
|
|
839
|
+
log
|
|
840
|
+
);
|
|
841
|
+
session.log?.info(`session ${session.id} created in NoConnection state`, {
|
|
842
|
+
...session.loggingMetadata,
|
|
843
|
+
tags: ["state-transition"]
|
|
844
|
+
});
|
|
845
|
+
return session;
|
|
846
|
+
},
|
|
847
|
+
WaitingForHandshake(from, conn, listeners, options, log) {
|
|
848
|
+
const session = new SessionWaitingForHandshake(
|
|
849
|
+
conn,
|
|
850
|
+
listeners,
|
|
851
|
+
from,
|
|
852
|
+
options,
|
|
853
|
+
log
|
|
854
|
+
);
|
|
855
|
+
session.log?.info(`session created in WaitingForHandshake state`, {
|
|
856
|
+
...session.loggingMetadata,
|
|
857
|
+
tags: ["state-transition"]
|
|
858
|
+
});
|
|
859
|
+
return session;
|
|
860
|
+
}
|
|
643
861
|
},
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
862
|
+
// All of the transitions 'move'/'consume' the old session and return a new one.
|
|
863
|
+
// After a session is transitioned, any usage of the old session will throw.
|
|
864
|
+
transition: {
|
|
865
|
+
// happy path transitions
|
|
866
|
+
NoConnectionToConnecting(oldSession, connPromise, listeners) {
|
|
867
|
+
const carriedState = inheritSharedSession(oldSession);
|
|
868
|
+
oldSession._handleStateExit();
|
|
869
|
+
const session = new SessionConnecting(
|
|
870
|
+
connPromise,
|
|
871
|
+
listeners,
|
|
872
|
+
...carriedState
|
|
873
|
+
);
|
|
874
|
+
session.log?.info(
|
|
875
|
+
`session ${session.id} transition from NoConnection to Connecting`,
|
|
876
|
+
{
|
|
877
|
+
...session.loggingMetadata,
|
|
878
|
+
tags: ["state-transition"]
|
|
654
879
|
}
|
|
655
880
|
);
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// transport/options.ts
|
|
666
|
-
var defaultTransportOptions = {
|
|
667
|
-
heartbeatIntervalMs: 1e3,
|
|
668
|
-
heartbeatsUntilDead: 2,
|
|
669
|
-
sessionDisconnectGraceMs: 5e3,
|
|
670
|
-
codec: NaiveJsonCodec
|
|
671
|
-
};
|
|
672
|
-
var defaultConnectionRetryOptions = {
|
|
673
|
-
baseIntervalMs: 250,
|
|
674
|
-
maxJitterMs: 200,
|
|
675
|
-
maxBackoffMs: 32e3,
|
|
676
|
-
attemptBudgetCapacity: 5,
|
|
677
|
-
budgetRestoreIntervalMs: 200
|
|
678
|
-
};
|
|
679
|
-
var defaultClientTransportOptions = {
|
|
680
|
-
...defaultTransportOptions,
|
|
681
|
-
...defaultConnectionRetryOptions
|
|
682
|
-
};
|
|
683
|
-
var defaultServerTransportOptions = {
|
|
684
|
-
...defaultTransportOptions
|
|
685
|
-
};
|
|
686
|
-
|
|
687
|
-
// transport/transport.ts
|
|
688
|
-
var Transport = class {
|
|
689
|
-
/**
|
|
690
|
-
* The status of the transport.
|
|
691
|
-
*/
|
|
692
|
-
status;
|
|
693
|
-
/**
|
|
694
|
-
* The {@link Codec} used to encode and decode messages.
|
|
695
|
-
*/
|
|
696
|
-
codec;
|
|
697
|
-
/**
|
|
698
|
-
* The client ID of this transport.
|
|
699
|
-
*/
|
|
700
|
-
clientId;
|
|
701
|
-
/**
|
|
702
|
-
* The map of {@link Session}s managed by this transport.
|
|
703
|
-
*/
|
|
704
|
-
sessions;
|
|
705
|
-
/**
|
|
706
|
-
* The map of {@link Connection}s managed by this transport.
|
|
707
|
-
*/
|
|
708
|
-
get connections() {
|
|
709
|
-
return new Map(
|
|
710
|
-
[...this.sessions].map(([client, session]) => [client, session.connection]).filter((entry) => entry[1] !== void 0)
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
/**
|
|
714
|
-
* The event dispatcher for handling events of type EventTypes.
|
|
715
|
-
*/
|
|
716
|
-
eventDispatcher;
|
|
717
|
-
/**
|
|
718
|
-
* The options for this transport.
|
|
719
|
-
*/
|
|
720
|
-
options;
|
|
721
|
-
log;
|
|
722
|
-
/**
|
|
723
|
-
* Creates a new Transport instance.
|
|
724
|
-
* This should also set up {@link onConnect}, and {@link onDisconnect} listeners.
|
|
725
|
-
* @param codec The codec used to encode and decode messages.
|
|
726
|
-
* @param clientId The client ID of this transport.
|
|
727
|
-
*/
|
|
728
|
-
constructor(clientId, providedOptions) {
|
|
729
|
-
this.options = { ...defaultTransportOptions, ...providedOptions };
|
|
730
|
-
this.eventDispatcher = new EventDispatcher();
|
|
731
|
-
this.sessions = /* @__PURE__ */ new Map();
|
|
732
|
-
this.codec = this.options.codec;
|
|
733
|
-
this.clientId = clientId;
|
|
734
|
-
this.status = "open";
|
|
735
|
-
}
|
|
736
|
-
bindLogger(fn, level) {
|
|
737
|
-
if (typeof fn === "function") {
|
|
738
|
-
this.log = createLogProxy(new BaseLogger(fn, level));
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
this.log = createLogProxy(fn);
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Called when a new connection is established
|
|
745
|
-
* and we know the identity of the connected client.
|
|
746
|
-
* @param conn The connection object.
|
|
747
|
-
*/
|
|
748
|
-
onConnect(conn, session, isTransparentReconnect) {
|
|
749
|
-
this.eventDispatcher.dispatchEvent("connectionStatus", {
|
|
750
|
-
status: "connect",
|
|
751
|
-
conn
|
|
752
|
-
});
|
|
753
|
-
conn.telemetry = createConnectionTelemetryInfo(conn, session.telemetry);
|
|
754
|
-
session.replaceWithNewConnection(conn, isTransparentReconnect);
|
|
755
|
-
this.log?.info(`connected to ${session.to}`, {
|
|
756
|
-
...conn.loggingMetadata,
|
|
757
|
-
...session.loggingMetadata
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
createSession(to, conn, propagationCtx) {
|
|
761
|
-
const session = new Session(
|
|
762
|
-
conn,
|
|
763
|
-
this.clientId,
|
|
764
|
-
to,
|
|
765
|
-
this.options,
|
|
766
|
-
propagationCtx
|
|
767
|
-
);
|
|
768
|
-
if (this.log) {
|
|
769
|
-
session.bindLogger(this.log);
|
|
770
|
-
}
|
|
771
|
-
const currentSession = this.sessions.get(session.to);
|
|
772
|
-
if (currentSession) {
|
|
773
|
-
this.log?.warn(
|
|
774
|
-
`session ${session.id} from ${session.to} surreptitiously replacing ${currentSession.id}`,
|
|
881
|
+
return session;
|
|
882
|
+
},
|
|
883
|
+
ConnectingToHandshaking(oldSession, conn, listeners) {
|
|
884
|
+
const carriedState = inheritSharedSession(oldSession);
|
|
885
|
+
oldSession._handleStateExit();
|
|
886
|
+
const session = new SessionHandshaking(conn, listeners, ...carriedState);
|
|
887
|
+
session.log?.info(
|
|
888
|
+
`session ${session.id} transition from Connecting to Handshaking`,
|
|
775
889
|
{
|
|
776
|
-
...
|
|
777
|
-
tags: ["
|
|
890
|
+
...session.loggingMetadata,
|
|
891
|
+
tags: ["state-transition"]
|
|
778
892
|
}
|
|
779
893
|
);
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
to,
|
|
794
|
-
conn,
|
|
795
|
-
sessionId,
|
|
796
|
-
propagationCtx
|
|
797
|
-
}) {
|
|
798
|
-
let session = this.sessions.get(to);
|
|
799
|
-
if (session !== void 0) {
|
|
800
|
-
this.log?.info(
|
|
801
|
-
`session for ${to} already exists, replacing it with a new session as requested`,
|
|
802
|
-
session.loggingMetadata
|
|
894
|
+
return session;
|
|
895
|
+
},
|
|
896
|
+
HandshakingToConnected(oldSession, listeners) {
|
|
897
|
+
const carriedState = inheritSharedSession(oldSession);
|
|
898
|
+
const conn = oldSession.conn;
|
|
899
|
+
oldSession._handleStateExit();
|
|
900
|
+
const session = new SessionConnected(conn, listeners, ...carriedState);
|
|
901
|
+
session.log?.info(
|
|
902
|
+
`session ${session.id} transition from Handshaking to Connected`,
|
|
903
|
+
{
|
|
904
|
+
...session.loggingMetadata,
|
|
905
|
+
tags: ["state-transition"]
|
|
906
|
+
}
|
|
803
907
|
);
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
session.advertisedSessionId !== sessionId
|
|
826
|
-
) {
|
|
827
|
-
return false;
|
|
828
|
-
}
|
|
829
|
-
this.log?.info(
|
|
830
|
-
`reused existing session for ${to}`,
|
|
831
|
-
session.loggingMetadata
|
|
832
|
-
);
|
|
833
|
-
return session;
|
|
834
|
-
}
|
|
835
|
-
getOrCreateSession({
|
|
836
|
-
to,
|
|
837
|
-
conn,
|
|
838
|
-
handshakingConn,
|
|
839
|
-
sessionId,
|
|
840
|
-
propagationCtx
|
|
841
|
-
}) {
|
|
842
|
-
let session = this.sessions.get(to);
|
|
843
|
-
const isReconnect = session !== void 0;
|
|
844
|
-
let isTransparentReconnect = isReconnect;
|
|
845
|
-
if (session?.advertisedSessionId !== void 0 && sessionId !== void 0 && session.advertisedSessionId !== sessionId) {
|
|
846
|
-
this.log?.info(
|
|
847
|
-
`session for ${to} already exists but has a different session id (expected: ${session.advertisedSessionId}, got: ${sessionId}), creating a new one`,
|
|
848
|
-
session.loggingMetadata
|
|
908
|
+
return session;
|
|
909
|
+
},
|
|
910
|
+
WaitingForHandshakeToConnected(pendingSession, oldSession, sessionId, to, propagationCtx, listeners) {
|
|
911
|
+
const conn = pendingSession.conn;
|
|
912
|
+
const { from, options } = pendingSession;
|
|
913
|
+
const carriedState = oldSession ? (
|
|
914
|
+
// old session exists, inherit state
|
|
915
|
+
inheritSharedSession(oldSession)
|
|
916
|
+
) : (
|
|
917
|
+
// old session does not exist, create new state
|
|
918
|
+
[
|
|
919
|
+
sessionId,
|
|
920
|
+
from,
|
|
921
|
+
to,
|
|
922
|
+
0,
|
|
923
|
+
0,
|
|
924
|
+
[],
|
|
925
|
+
createSessionTelemetryInfo(sessionId, to, from, propagationCtx),
|
|
926
|
+
options,
|
|
927
|
+
pendingSession.log
|
|
928
|
+
]
|
|
849
929
|
);
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
930
|
+
pendingSession._handleStateExit();
|
|
931
|
+
oldSession?._handleStateExit();
|
|
932
|
+
const session = new SessionConnected(conn, listeners, ...carriedState);
|
|
933
|
+
session.log?.info(
|
|
934
|
+
`session ${session.id} transition from WaitingForHandshake to Connected`,
|
|
935
|
+
{
|
|
936
|
+
...session.loggingMetadata,
|
|
937
|
+
tags: ["state-transition"]
|
|
938
|
+
}
|
|
939
|
+
);
|
|
940
|
+
return session;
|
|
941
|
+
},
|
|
942
|
+
// disconnect paths
|
|
943
|
+
ConnectingToNoConnection(oldSession, listeners) {
|
|
944
|
+
const carriedState = inheritSharedSession(oldSession);
|
|
945
|
+
oldSession.bestEffortClose();
|
|
946
|
+
oldSession._handleStateExit();
|
|
947
|
+
const session = new SessionNoConnection(listeners, ...carriedState);
|
|
948
|
+
session.log?.info(
|
|
949
|
+
`session ${session.id} transition from Connecting to NoConnection`,
|
|
950
|
+
{
|
|
951
|
+
...session.loggingMetadata,
|
|
952
|
+
tags: ["state-transition"]
|
|
953
|
+
}
|
|
954
|
+
);
|
|
955
|
+
return session;
|
|
956
|
+
},
|
|
957
|
+
HandshakingToNoConnection(oldSession, listeners) {
|
|
958
|
+
const carriedState = inheritSharedSession(oldSession);
|
|
959
|
+
oldSession.conn.close();
|
|
960
|
+
oldSession._handleStateExit();
|
|
961
|
+
const session = new SessionNoConnection(listeners, ...carriedState);
|
|
962
|
+
session.log?.info(
|
|
963
|
+
`session ${session.id} transition from Handshaking to NoConnection`,
|
|
964
|
+
{
|
|
965
|
+
...session.loggingMetadata,
|
|
966
|
+
tags: ["state-transition"]
|
|
967
|
+
}
|
|
863
968
|
);
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
deleteSession({
|
|
874
|
-
session,
|
|
875
|
-
closeHandshakingConnection,
|
|
876
|
-
handshakingConn
|
|
877
|
-
}) {
|
|
878
|
-
if (closeHandshakingConnection) {
|
|
879
|
-
session.closeHandshakingConnection(handshakingConn);
|
|
880
|
-
}
|
|
881
|
-
session.close();
|
|
882
|
-
session.telemetry.span.end();
|
|
883
|
-
const currentSession = this.sessions.get(session.to);
|
|
884
|
-
if (currentSession && currentSession.id !== session.id) {
|
|
885
|
-
this.log?.warn(
|
|
886
|
-
`session ${session.id} disconnect from ${session.to}, mismatch with ${currentSession.id}`,
|
|
969
|
+
return session;
|
|
970
|
+
},
|
|
971
|
+
ConnectedToNoConnection(oldSession, listeners) {
|
|
972
|
+
const carriedState = inheritSharedSession(oldSession);
|
|
973
|
+
oldSession.conn.close();
|
|
974
|
+
oldSession._handleStateExit();
|
|
975
|
+
const session = new SessionNoConnection(listeners, ...carriedState);
|
|
976
|
+
session.log?.info(
|
|
977
|
+
`session ${session.id} transition from Connected to NoConnection`,
|
|
887
978
|
{
|
|
888
979
|
...session.loggingMetadata,
|
|
889
|
-
tags: ["
|
|
980
|
+
tags: ["state-transition"]
|
|
890
981
|
}
|
|
891
982
|
);
|
|
892
|
-
return;
|
|
983
|
+
return session;
|
|
893
984
|
}
|
|
894
|
-
this.sessions.delete(session.to);
|
|
895
|
-
this.log?.info(
|
|
896
|
-
`session ${session.id} disconnect from ${session.to}`,
|
|
897
|
-
session.loggingMetadata
|
|
898
|
-
);
|
|
899
|
-
this.eventDispatcher.dispatchEvent("sessionStatus", {
|
|
900
|
-
status: "disconnect",
|
|
901
|
-
session
|
|
902
|
-
});
|
|
903
985
|
}
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
// transport/transport.ts
|
|
989
|
+
var Transport = class {
|
|
904
990
|
/**
|
|
905
|
-
* The
|
|
906
|
-
* @param conn The connection object.
|
|
907
|
-
* @param connectedTo The peer we are connected to.
|
|
991
|
+
* The status of the transport.
|
|
908
992
|
*/
|
|
909
|
-
|
|
910
|
-
if (session.connection !== void 0 && session.connection.id !== conn.id) {
|
|
911
|
-
session.telemetry.span.addEvent("onDisconnect race");
|
|
912
|
-
this.log?.warn("onDisconnect race", {
|
|
913
|
-
clientId: this.clientId,
|
|
914
|
-
...session.loggingMetadata,
|
|
915
|
-
...conn.loggingMetadata,
|
|
916
|
-
tags: ["invariant-violation"]
|
|
917
|
-
});
|
|
918
|
-
return;
|
|
919
|
-
}
|
|
920
|
-
conn.telemetry?.span.end();
|
|
921
|
-
this.eventDispatcher.dispatchEvent("connectionStatus", {
|
|
922
|
-
status: "disconnect",
|
|
923
|
-
conn
|
|
924
|
-
});
|
|
925
|
-
session.connection = void 0;
|
|
926
|
-
session.beginGrace(() => {
|
|
927
|
-
if (session.connection !== void 0) {
|
|
928
|
-
session.telemetry.span.addEvent("session grace period race");
|
|
929
|
-
this.log?.warn("session grace period race", {
|
|
930
|
-
clientId: this.clientId,
|
|
931
|
-
...session.loggingMetadata,
|
|
932
|
-
...conn.loggingMetadata,
|
|
933
|
-
tags: ["invariant-violation"]
|
|
934
|
-
});
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
session.telemetry.span.addEvent("session grace period expired");
|
|
938
|
-
this.deleteSession({
|
|
939
|
-
session,
|
|
940
|
-
closeHandshakingConnection: true,
|
|
941
|
-
handshakingConn: conn
|
|
942
|
-
});
|
|
943
|
-
});
|
|
944
|
-
}
|
|
993
|
+
status;
|
|
945
994
|
/**
|
|
946
|
-
*
|
|
947
|
-
* @param msg The message to parse.
|
|
948
|
-
* @returns The parsed message, or null if the message is malformed or invalid.
|
|
995
|
+
* The client ID of this transport.
|
|
949
996
|
*/
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
997
|
+
clientId;
|
|
998
|
+
/**
|
|
999
|
+
* The event dispatcher for handling events of type EventTypes.
|
|
1000
|
+
*/
|
|
1001
|
+
eventDispatcher;
|
|
1002
|
+
/**
|
|
1003
|
+
* The options for this transport.
|
|
1004
|
+
*/
|
|
1005
|
+
options;
|
|
1006
|
+
log;
|
|
1007
|
+
sessions;
|
|
1008
|
+
/**
|
|
1009
|
+
* Creates a new Transport instance.
|
|
1010
|
+
* @param codec The codec used to encode and decode messages.
|
|
1011
|
+
* @param clientId The client ID of this transport.
|
|
1012
|
+
*/
|
|
1013
|
+
constructor(clientId, providedOptions) {
|
|
1014
|
+
this.options = { ...defaultTransportOptions, ...providedOptions };
|
|
1015
|
+
this.eventDispatcher = new EventDispatcher();
|
|
1016
|
+
this.clientId = clientId;
|
|
1017
|
+
this.status = "open";
|
|
1018
|
+
this.sessions = /* @__PURE__ */ new Map();
|
|
1019
|
+
}
|
|
1020
|
+
bindLogger(fn, level) {
|
|
1021
|
+
if (typeof fn === "function") {
|
|
1022
|
+
this.log = createLogProxy(new BaseLogger(fn, level));
|
|
1023
|
+
return;
|
|
972
1024
|
}
|
|
973
|
-
|
|
1025
|
+
this.log = createLogProxy(fn);
|
|
974
1026
|
}
|
|
975
1027
|
/**
|
|
976
1028
|
* Called when a message is received by this transport.
|
|
977
1029
|
* You generally shouldn't need to override this in downstream transport implementations.
|
|
978
1030
|
* @param msg The received message.
|
|
979
1031
|
*/
|
|
980
|
-
handleMsg(msg
|
|
1032
|
+
handleMsg(msg) {
|
|
981
1033
|
if (this.getStatus() !== "open")
|
|
982
1034
|
return;
|
|
983
|
-
|
|
984
|
-
if (!session) {
|
|
985
|
-
this.log?.error(`received message for unknown session from ${msg.from}`, {
|
|
986
|
-
clientId: this.clientId,
|
|
987
|
-
transportMessage: msg,
|
|
988
|
-
...conn.loggingMetadata,
|
|
989
|
-
tags: ["invariant-violation"]
|
|
990
|
-
});
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
session.cancelGrace();
|
|
994
|
-
this.log?.debug(`received msg`, {
|
|
995
|
-
clientId: this.clientId,
|
|
996
|
-
transportMessage: msg,
|
|
997
|
-
...conn.loggingMetadata
|
|
998
|
-
});
|
|
999
|
-
if (msg.seq !== session.nextExpectedSeq) {
|
|
1000
|
-
if (msg.seq < session.nextExpectedSeq) {
|
|
1001
|
-
this.log?.debug(
|
|
1002
|
-
`received duplicate msg (got seq: ${msg.seq}, wanted seq: ${session.nextExpectedSeq}), discarding`,
|
|
1003
|
-
{
|
|
1004
|
-
clientId: this.clientId,
|
|
1005
|
-
transportMessage: msg,
|
|
1006
|
-
...conn.loggingMetadata
|
|
1007
|
-
}
|
|
1008
|
-
);
|
|
1009
|
-
} else {
|
|
1010
|
-
const errMsg = `received out-of-order msg (got seq: ${msg.seq}, wanted seq: ${session.nextExpectedSeq})`;
|
|
1011
|
-
this.log?.error(`${errMsg}, marking connection as dead`, {
|
|
1012
|
-
clientId: this.clientId,
|
|
1013
|
-
transportMessage: msg,
|
|
1014
|
-
...conn.loggingMetadata,
|
|
1015
|
-
tags: ["invariant-violation"]
|
|
1016
|
-
});
|
|
1017
|
-
this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
|
|
1018
|
-
session.telemetry.span.setStatus({
|
|
1019
|
-
code: import_api3.SpanStatusCode.ERROR,
|
|
1020
|
-
message: "message order violated"
|
|
1021
|
-
});
|
|
1022
|
-
this.deleteSession({ session, closeHandshakingConnection: true });
|
|
1023
|
-
}
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
1026
|
-
session.updateBookkeeping(msg.ack, msg.seq);
|
|
1027
|
-
if (!isAck(msg.controlFlags)) {
|
|
1028
|
-
this.eventDispatcher.dispatchEvent("message", msg);
|
|
1029
|
-
} else {
|
|
1030
|
-
this.log?.debug(`discarding msg (ack bit set)`, {
|
|
1031
|
-
clientId: this.clientId,
|
|
1032
|
-
transportMessage: msg,
|
|
1033
|
-
...conn.loggingMetadata
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1035
|
+
this.eventDispatcher.dispatchEvent("message", msg);
|
|
1036
1036
|
}
|
|
1037
1037
|
/**
|
|
1038
1038
|
* Adds a listener to this transport.
|
|
@@ -1050,34 +1050,6 @@ var Transport = class {
|
|
|
1050
1050
|
removeEventListener(type, handler) {
|
|
1051
1051
|
this.eventDispatcher.removeEventListener(type, handler);
|
|
1052
1052
|
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Sends a message over this transport, delegating to the appropriate connection to actually
|
|
1055
|
-
* send the message.
|
|
1056
|
-
* @param msg The message to send.
|
|
1057
|
-
* @returns The ID of the sent message or undefined if it wasn't sent
|
|
1058
|
-
*/
|
|
1059
|
-
send(to, msg) {
|
|
1060
|
-
if (this.getStatus() === "closed") {
|
|
1061
|
-
const err = "transport is closed, cant send";
|
|
1062
|
-
this.log?.error(err, {
|
|
1063
|
-
clientId: this.clientId,
|
|
1064
|
-
transportMessage: msg,
|
|
1065
|
-
tags: ["invariant-violation"]
|
|
1066
|
-
});
|
|
1067
|
-
throw new Error(err);
|
|
1068
|
-
}
|
|
1069
|
-
return this.getOrCreateSession({ to }).session.send(msg);
|
|
1070
|
-
}
|
|
1071
|
-
// control helpers
|
|
1072
|
-
sendCloseStream(to, streamId) {
|
|
1073
|
-
return this.send(to, {
|
|
1074
|
-
streamId,
|
|
1075
|
-
controlFlags: 4 /* StreamClosedBit */,
|
|
1076
|
-
payload: {
|
|
1077
|
-
type: "CLOSE"
|
|
1078
|
-
}
|
|
1079
|
-
});
|
|
1080
|
-
}
|
|
1081
1053
|
protocolError(type, message) {
|
|
1082
1054
|
this.eventDispatcher.dispatchEvent("protocolError", { type, message });
|
|
1083
1055
|
}
|
|
@@ -1089,7 +1061,7 @@ var Transport = class {
|
|
|
1089
1061
|
close() {
|
|
1090
1062
|
this.status = "closed";
|
|
1091
1063
|
for (const session of this.sessions.values()) {
|
|
1092
|
-
this.deleteSession(
|
|
1064
|
+
this.deleteSession(session);
|
|
1093
1065
|
}
|
|
1094
1066
|
this.eventDispatcher.dispatchEvent("transportStatus", {
|
|
1095
1067
|
status: this.status
|
|
@@ -1100,10 +1072,72 @@ var Transport = class {
|
|
|
1100
1072
|
getStatus() {
|
|
1101
1073
|
return this.status;
|
|
1102
1074
|
}
|
|
1075
|
+
updateSession(session) {
|
|
1076
|
+
const activeSession = this.sessions.get(session.to);
|
|
1077
|
+
if (activeSession && activeSession.id !== session.id) {
|
|
1078
|
+
const msg = `attempt to transition active session for ${session.to} but active session (${activeSession.id}) is different from handle (${session.id})`;
|
|
1079
|
+
throw new Error(msg);
|
|
1080
|
+
}
|
|
1081
|
+
this.sessions.set(session.to, session);
|
|
1082
|
+
if (!activeSession) {
|
|
1083
|
+
this.eventDispatcher.dispatchEvent("sessionStatus", {
|
|
1084
|
+
status: "connect",
|
|
1085
|
+
session
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
this.eventDispatcher.dispatchEvent("sessionTransition", {
|
|
1089
|
+
state: session.state,
|
|
1090
|
+
session
|
|
1091
|
+
});
|
|
1092
|
+
return session;
|
|
1093
|
+
}
|
|
1094
|
+
// state transitions
|
|
1095
|
+
deleteSession(session) {
|
|
1096
|
+
session.log?.info(`closing session ${session.id}`, session.loggingMetadata);
|
|
1097
|
+
this.eventDispatcher.dispatchEvent("sessionStatus", {
|
|
1098
|
+
status: "disconnect",
|
|
1099
|
+
session
|
|
1100
|
+
});
|
|
1101
|
+
session.close();
|
|
1102
|
+
this.sessions.delete(session.to);
|
|
1103
|
+
}
|
|
1104
|
+
// common listeners
|
|
1105
|
+
onSessionGracePeriodElapsed(session) {
|
|
1106
|
+
this.log?.warn(
|
|
1107
|
+
`session to ${session.to} grace period elapsed, closing`,
|
|
1108
|
+
session.loggingMetadata
|
|
1109
|
+
);
|
|
1110
|
+
this.deleteSession(session);
|
|
1111
|
+
}
|
|
1112
|
+
onConnectingFailed(session) {
|
|
1113
|
+
const noConnectionSession = SessionStateGraph.transition.ConnectingToNoConnection(session, {
|
|
1114
|
+
onSessionGracePeriodElapsed: () => {
|
|
1115
|
+
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
return this.updateSession(noConnectionSession);
|
|
1119
|
+
}
|
|
1120
|
+
onConnClosed(session) {
|
|
1121
|
+
let noConnectionSession;
|
|
1122
|
+
if (session.state === "Handshaking" /* Handshaking */) {
|
|
1123
|
+
noConnectionSession = SessionStateGraph.transition.HandshakingToNoConnection(session, {
|
|
1124
|
+
onSessionGracePeriodElapsed: () => {
|
|
1125
|
+
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
} else {
|
|
1129
|
+
noConnectionSession = SessionStateGraph.transition.ConnectedToNoConnection(session, {
|
|
1130
|
+
onSessionGracePeriodElapsed: () => {
|
|
1131
|
+
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
return this.updateSession(noConnectionSession);
|
|
1136
|
+
}
|
|
1103
1137
|
};
|
|
1104
1138
|
|
|
1105
1139
|
// transport/client.ts
|
|
1106
|
-
var
|
|
1140
|
+
var import_api3 = require("@opentelemetry/api");
|
|
1107
1141
|
|
|
1108
1142
|
// transport/rateLimit.ts
|
|
1109
1143
|
var LeakyBucketRateLimit = class {
|
|
@@ -1191,10 +1225,6 @@ var ClientTransport = class extends Transport {
|
|
|
1191
1225
|
* The options for this transport.
|
|
1192
1226
|
*/
|
|
1193
1227
|
options;
|
|
1194
|
-
/**
|
|
1195
|
-
* The map of reconnect promises for each client ID.
|
|
1196
|
-
*/
|
|
1197
|
-
inflightConnectionPromises;
|
|
1198
1228
|
retryBudget;
|
|
1199
1229
|
/**
|
|
1200
1230
|
* A flag indicating whether the transport should automatically reconnect
|
|
@@ -1213,352 +1243,278 @@ var ClientTransport = class extends Transport {
|
|
|
1213
1243
|
...defaultClientTransportOptions,
|
|
1214
1244
|
...providedOptions
|
|
1215
1245
|
};
|
|
1216
|
-
this.inflightConnectionPromises = /* @__PURE__ */ new Map();
|
|
1217
1246
|
this.retryBudget = new LeakyBucketRateLimit(this.options);
|
|
1218
1247
|
}
|
|
1219
1248
|
extendHandshake(options) {
|
|
1220
1249
|
this.handshakeExtensions = options;
|
|
1221
1250
|
}
|
|
1222
|
-
|
|
1223
|
-
if (this.getStatus()
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
}, this.options.sessionDisconnectGraceMs);
|
|
1235
|
-
const handshakeHandler = (data) => {
|
|
1236
|
-
const maybeSession = this.receiveHandshakeResponseMessage(data, conn);
|
|
1237
|
-
clearTimeout(handshakeTimeout);
|
|
1238
|
-
if (!maybeSession) {
|
|
1239
|
-
conn.close();
|
|
1240
|
-
return;
|
|
1241
|
-
} else {
|
|
1242
|
-
session = maybeSession;
|
|
1243
|
-
}
|
|
1244
|
-
conn.removeDataListener(handshakeHandler);
|
|
1245
|
-
conn.addDataListener((data2) => {
|
|
1246
|
-
const parsed = this.parseMsg(data2, conn);
|
|
1247
|
-
if (!parsed) {
|
|
1248
|
-
conn.telemetry?.span.setStatus({
|
|
1249
|
-
code: import_api4.SpanStatusCode.ERROR,
|
|
1250
|
-
message: "message parse failure"
|
|
1251
|
-
});
|
|
1252
|
-
conn.close();
|
|
1253
|
-
return;
|
|
1254
|
-
}
|
|
1255
|
-
this.handleMsg(parsed, conn);
|
|
1251
|
+
tryReconnecting(to) {
|
|
1252
|
+
if (this.reconnectOnConnectionDrop && this.getStatus() === "open") {
|
|
1253
|
+
this.connect(to);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
send(to, msg) {
|
|
1257
|
+
if (this.getStatus() === "closed") {
|
|
1258
|
+
const err = "transport is closed, cant send";
|
|
1259
|
+
this.log?.error(err, {
|
|
1260
|
+
clientId: this.clientId,
|
|
1261
|
+
transportMessage: msg,
|
|
1262
|
+
tags: ["invariant-violation"]
|
|
1256
1263
|
});
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1264
|
+
throw new Error(err);
|
|
1265
|
+
}
|
|
1266
|
+
let session = this.sessions.get(to);
|
|
1267
|
+
if (!session) {
|
|
1268
|
+
session = this.createUnconnectedSession(to);
|
|
1269
|
+
}
|
|
1270
|
+
return session.send(msg);
|
|
1271
|
+
}
|
|
1272
|
+
createUnconnectedSession(to) {
|
|
1273
|
+
const session = SessionStateGraph.entrypoints.NoConnection(
|
|
1274
|
+
to,
|
|
1275
|
+
this.clientId,
|
|
1276
|
+
{
|
|
1277
|
+
onSessionGracePeriodElapsed: () => {
|
|
1278
|
+
this.onSessionGracePeriodElapsed(session);
|
|
1271
1279
|
}
|
|
1272
|
-
|
|
1273
|
-
this.
|
|
1274
|
-
|
|
1275
|
-
|
|
1280
|
+
},
|
|
1281
|
+
this.options,
|
|
1282
|
+
this.log
|
|
1283
|
+
);
|
|
1284
|
+
this.updateSession(session);
|
|
1285
|
+
return session;
|
|
1286
|
+
}
|
|
1287
|
+
// listeners
|
|
1288
|
+
onConnectingFailed(session) {
|
|
1289
|
+
const noConnectionSession = super.onConnectingFailed(session);
|
|
1290
|
+
this.tryReconnecting(noConnectionSession.to);
|
|
1291
|
+
return noConnectionSession;
|
|
1292
|
+
}
|
|
1293
|
+
onConnClosed(session) {
|
|
1294
|
+
const noConnectionSession = super.onConnClosed(session);
|
|
1295
|
+
this.tryReconnecting(noConnectionSession.to);
|
|
1296
|
+
return noConnectionSession;
|
|
1297
|
+
}
|
|
1298
|
+
onConnectionEstablished(session, conn) {
|
|
1299
|
+
const handshakingSession = SessionStateGraph.transition.ConnectingToHandshaking(session, conn, {
|
|
1300
|
+
onConnectionErrored: (err) => {
|
|
1301
|
+
const errStr = coerceErrorString(err);
|
|
1302
|
+
this.log?.error(
|
|
1303
|
+
`connection to ${handshakingSession.to} errored during handshake: ${errStr}`,
|
|
1304
|
+
handshakingSession.loggingMetadata
|
|
1305
|
+
);
|
|
1306
|
+
},
|
|
1307
|
+
onConnectionClosed: () => {
|
|
1308
|
+
this.log?.warn(
|
|
1309
|
+
`connection to ${handshakingSession.to} closed during handshake`,
|
|
1310
|
+
handshakingSession.loggingMetadata
|
|
1311
|
+
);
|
|
1312
|
+
this.onConnClosed(handshakingSession);
|
|
1313
|
+
},
|
|
1314
|
+
onHandshake: (msg) => {
|
|
1315
|
+
this.onHandshakeResponse(handshakingSession, msg);
|
|
1316
|
+
},
|
|
1317
|
+
onInvalidHandshake: (reason) => {
|
|
1318
|
+
this.log?.error(
|
|
1319
|
+
`invalid handshake: ${reason}`,
|
|
1320
|
+
handshakingSession.loggingMetadata
|
|
1321
|
+
);
|
|
1322
|
+
this.deleteSession(session);
|
|
1323
|
+
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1324
|
+
},
|
|
1325
|
+
onHandshakeTimeout: () => {
|
|
1326
|
+
this.log?.error(
|
|
1327
|
+
`connection to ${handshakingSession.to} timed out during handshake`,
|
|
1328
|
+
handshakingSession.loggingMetadata
|
|
1329
|
+
);
|
|
1330
|
+
this.onConnClosed(handshakingSession);
|
|
1276
1331
|
}
|
|
1277
1332
|
});
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
...conn.loggingMetadata,
|
|
1287
|
-
...session?.loggingMetadata,
|
|
1288
|
-
clientId: this.clientId,
|
|
1289
|
-
connectedTo: to
|
|
1290
|
-
}
|
|
1291
|
-
);
|
|
1333
|
+
this.updateSession(handshakingSession);
|
|
1334
|
+
void this.sendHandshake(handshakingSession);
|
|
1335
|
+
return handshakingSession;
|
|
1336
|
+
}
|
|
1337
|
+
rejectHandshakeResponse(session, reason, metadata) {
|
|
1338
|
+
session.conn.telemetry?.span.setStatus({
|
|
1339
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1340
|
+
message: reason
|
|
1292
1341
|
});
|
|
1342
|
+
this.log?.warn(reason, metadata);
|
|
1343
|
+
this.deleteSession(session);
|
|
1293
1344
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
});
|
|
1301
|
-
this.protocolError(
|
|
1302
|
-
ProtocolError.HandshakeFailed,
|
|
1303
|
-
"received non-transport message"
|
|
1304
|
-
);
|
|
1305
|
-
return false;
|
|
1306
|
-
}
|
|
1307
|
-
if (!import_value2.Value.Check(ControlMessageHandshakeResponseSchema, parsed.payload)) {
|
|
1308
|
-
conn.telemetry?.span.setStatus({
|
|
1309
|
-
code: import_api4.SpanStatusCode.ERROR,
|
|
1310
|
-
message: "invalid handshake response"
|
|
1311
|
-
});
|
|
1312
|
-
this.log?.warn(`received invalid handshake resp`, {
|
|
1313
|
-
...conn.loggingMetadata,
|
|
1314
|
-
clientId: this.clientId,
|
|
1315
|
-
connectedTo: parsed.from,
|
|
1316
|
-
transportMessage: parsed,
|
|
1345
|
+
onHandshakeResponse(session, msg) {
|
|
1346
|
+
if (!import_value2.Value.Check(ControlMessageHandshakeResponseSchema, msg.payload)) {
|
|
1347
|
+
const reason = `received invalid handshake response`;
|
|
1348
|
+
this.rejectHandshakeResponse(session, reason, {
|
|
1349
|
+
...session.loggingMetadata,
|
|
1350
|
+
transportMessage: msg,
|
|
1317
1351
|
validationErrors: [
|
|
1318
|
-
...import_value2.Value.Errors(
|
|
1319
|
-
ControlMessageHandshakeResponseSchema,
|
|
1320
|
-
parsed.payload
|
|
1321
|
-
)
|
|
1352
|
+
...import_value2.Value.Errors(ControlMessageHandshakeResponseSchema, msg.payload)
|
|
1322
1353
|
]
|
|
1323
1354
|
});
|
|
1324
|
-
|
|
1325
|
-
ProtocolError.HandshakeFailed,
|
|
1326
|
-
"invalid handshake resp"
|
|
1327
|
-
);
|
|
1328
|
-
return false;
|
|
1355
|
+
return;
|
|
1329
1356
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
});
|
|
1357
|
+
if (!msg.payload.status.ok) {
|
|
1358
|
+
const retriable = msg.payload.status.code ? import_value2.Value.Check(
|
|
1359
|
+
HandshakeErrorRetriableResponseCodes,
|
|
1360
|
+
msg.payload.status.code
|
|
1361
|
+
) : false;
|
|
1362
|
+
const reason = `handshake failed: ${msg.payload.status.reason}`;
|
|
1363
|
+
this.rejectHandshakeResponse(session, reason, {
|
|
1364
|
+
...session.loggingMetadata,
|
|
1365
|
+
transportMessage: msg
|
|
1366
|
+
});
|
|
1367
|
+
if (retriable) {
|
|
1368
|
+
this.tryReconnecting(session.to);
|
|
1343
1369
|
} else {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
message: "handshake rejected"
|
|
1347
|
-
});
|
|
1370
|
+
this.deleteSession(session);
|
|
1371
|
+
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1348
1372
|
}
|
|
1349
|
-
|
|
1350
|
-
`received handshake rejection: ${parsed.payload.status.reason}`,
|
|
1351
|
-
{
|
|
1352
|
-
...conn.loggingMetadata,
|
|
1353
|
-
clientId: this.clientId,
|
|
1354
|
-
connectedTo: parsed.from,
|
|
1355
|
-
transportMessage: parsed
|
|
1356
|
-
}
|
|
1357
|
-
);
|
|
1358
|
-
this.protocolError(
|
|
1359
|
-
ProtocolError.HandshakeFailed,
|
|
1360
|
-
parsed.payload.status.reason
|
|
1361
|
-
);
|
|
1362
|
-
return false;
|
|
1373
|
+
return;
|
|
1363
1374
|
}
|
|
1364
|
-
if (
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
conn.telemetry?.span.setStatus({
|
|
1370
|
-
code: import_api4.SpanStatusCode.ERROR,
|
|
1371
|
-
message: "session id mismatch"
|
|
1372
|
-
});
|
|
1373
|
-
this.log?.warn(`handshake from ${parsed.from} session id mismatch`, {
|
|
1374
|
-
...conn.loggingMetadata,
|
|
1375
|
-
clientId: this.clientId,
|
|
1376
|
-
connectedTo: parsed.from,
|
|
1377
|
-
transportMessage: parsed
|
|
1375
|
+
if (msg.payload.status.sessionId !== session.id) {
|
|
1376
|
+
const reason = `session id mismatch: expected ${session.id}, got ${msg.payload.status.sessionId}`;
|
|
1377
|
+
this.rejectHandshakeResponse(session, reason, {
|
|
1378
|
+
...session.loggingMetadata,
|
|
1379
|
+
transportMessage: msg
|
|
1378
1380
|
});
|
|
1379
|
-
|
|
1380
|
-
return false;
|
|
1381
|
+
return;
|
|
1381
1382
|
}
|
|
1382
|
-
this.log?.
|
|
1383
|
-
...
|
|
1384
|
-
|
|
1385
|
-
connectedTo: parsed.from,
|
|
1386
|
-
transportMessage: parsed
|
|
1383
|
+
this.log?.info(`handshake from ${msg.from} ok`, {
|
|
1384
|
+
...session.loggingMetadata,
|
|
1385
|
+
transportMessage: msg
|
|
1387
1386
|
});
|
|
1388
|
-
const
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1387
|
+
const connectedSession = SessionStateGraph.transition.HandshakingToConnected(session, {
|
|
1388
|
+
onConnectionErrored: (err) => {
|
|
1389
|
+
const errStr = coerceErrorString(err);
|
|
1390
|
+
this.log?.warn(
|
|
1391
|
+
`connection to ${connectedSession.to} errored: ${errStr}`,
|
|
1392
|
+
connectedSession.loggingMetadata
|
|
1393
|
+
);
|
|
1394
|
+
},
|
|
1395
|
+
onConnectionClosed: () => {
|
|
1396
|
+
this.log?.info(
|
|
1397
|
+
`connection to ${connectedSession.to} closed`,
|
|
1398
|
+
connectedSession.loggingMetadata
|
|
1399
|
+
);
|
|
1400
|
+
this.onConnClosed(connectedSession);
|
|
1401
|
+
},
|
|
1402
|
+
onMessage: (msg2) => this.handleMsg(msg2),
|
|
1403
|
+
onInvalidMessage: (reason) => {
|
|
1404
|
+
this.deleteSession(connectedSession);
|
|
1405
|
+
this.protocolError(ProtocolError.MessageOrderingViolated, reason);
|
|
1406
|
+
}
|
|
1392
1407
|
});
|
|
1393
|
-
this.
|
|
1394
|
-
this.retryBudget.startRestoringBudget(
|
|
1395
|
-
return session;
|
|
1408
|
+
this.updateSession(connectedSession);
|
|
1409
|
+
this.retryBudget.startRestoringBudget(connectedSession.to);
|
|
1396
1410
|
}
|
|
1397
1411
|
/**
|
|
1398
1412
|
* Manually attempts to connect to a client.
|
|
1399
1413
|
* @param to The client ID of the node to connect to.
|
|
1400
1414
|
*/
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1415
|
+
connect(to) {
|
|
1416
|
+
let session = this.sessions.get(to);
|
|
1417
|
+
session ??= this.createUnconnectedSession(to);
|
|
1418
|
+
if (session.state !== "NoConnection" /* NoConnection */) {
|
|
1419
|
+
this.log?.debug(
|
|
1420
|
+
`session to ${to} has state ${session.state}, skipping connect attempt`,
|
|
1421
|
+
session.loggingMetadata
|
|
1422
|
+
);
|
|
1407
1423
|
return;
|
|
1408
1424
|
}
|
|
1409
|
-
|
|
1410
|
-
if (!canProceedWithConnection()) {
|
|
1425
|
+
if (this.getStatus() !== "open") {
|
|
1411
1426
|
this.log?.info(
|
|
1412
1427
|
`transport state is no longer open, cancelling attempt to connect to ${to}`,
|
|
1413
|
-
|
|
1428
|
+
session.loggingMetadata
|
|
1414
1429
|
);
|
|
1415
1430
|
return;
|
|
1416
1431
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
this.protocolError(ProtocolError.RetriesExceeded, errMsg);
|
|
1424
|
-
return;
|
|
1425
|
-
}
|
|
1426
|
-
let sleep = Promise.resolve();
|
|
1427
|
-
const backoffMs = this.retryBudget.getBackoffMs(to);
|
|
1428
|
-
if (backoffMs > 0) {
|
|
1429
|
-
sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
1430
|
-
}
|
|
1431
|
-
this.log?.info(
|
|
1432
|
-
`attempting connection to ${to} (${backoffMs}ms backoff)`,
|
|
1433
|
-
{
|
|
1434
|
-
clientId: this.clientId,
|
|
1435
|
-
connectedTo: to
|
|
1436
|
-
}
|
|
1437
|
-
);
|
|
1438
|
-
this.retryBudget.consumeBudget(to);
|
|
1439
|
-
reconnectPromise = tracing_default.startActiveSpan("connect", async (span) => {
|
|
1440
|
-
try {
|
|
1441
|
-
span.addEvent("backoff", { backoffMs });
|
|
1442
|
-
await sleep;
|
|
1443
|
-
if (!canProceedWithConnection()) {
|
|
1444
|
-
throw new Error("transport state is no longer open");
|
|
1445
|
-
}
|
|
1446
|
-
span.addEvent("connecting");
|
|
1447
|
-
const conn = await this.createNewOutgoingConnection(to);
|
|
1448
|
-
if (!canProceedWithConnection()) {
|
|
1449
|
-
this.log?.info(
|
|
1450
|
-
`transport state is no longer open, closing pre-handshake connection to ${to}`,
|
|
1451
|
-
{
|
|
1452
|
-
...conn.loggingMetadata,
|
|
1453
|
-
clientId: this.clientId,
|
|
1454
|
-
connectedTo: to
|
|
1455
|
-
}
|
|
1456
|
-
);
|
|
1457
|
-
conn.close();
|
|
1458
|
-
throw new Error("transport state is no longer open");
|
|
1459
|
-
}
|
|
1460
|
-
span.addEvent("sending handshake");
|
|
1461
|
-
const ok = await this.sendHandshake(to, conn);
|
|
1462
|
-
if (!ok) {
|
|
1463
|
-
conn.close();
|
|
1464
|
-
throw new Error("failed to send handshake");
|
|
1465
|
-
}
|
|
1466
|
-
return conn;
|
|
1467
|
-
} catch (err) {
|
|
1468
|
-
const errStr = coerceErrorString(err);
|
|
1469
|
-
span.recordException(errStr);
|
|
1470
|
-
span.setStatus({ code: import_api4.SpanStatusCode.ERROR });
|
|
1471
|
-
throw err;
|
|
1472
|
-
} finally {
|
|
1473
|
-
span.end();
|
|
1474
|
-
}
|
|
1475
|
-
});
|
|
1476
|
-
this.inflightConnectionPromises.set(to, reconnectPromise);
|
|
1477
|
-
} else {
|
|
1478
|
-
this.log?.info(
|
|
1479
|
-
`attempting connection to ${to} (reusing previous attempt)`,
|
|
1480
|
-
{
|
|
1481
|
-
clientId: this.clientId,
|
|
1482
|
-
connectedTo: to
|
|
1483
|
-
}
|
|
1484
|
-
);
|
|
1432
|
+
if (!this.retryBudget.hasBudget(to)) {
|
|
1433
|
+
const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
|
|
1434
|
+
const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
|
|
1435
|
+
this.log?.error(errMsg, session.loggingMetadata);
|
|
1436
|
+
this.protocolError(ProtocolError.RetriesExceeded, errMsg);
|
|
1437
|
+
return;
|
|
1485
1438
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
const errStr = coerceErrorString(error);
|
|
1491
|
-
if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
|
|
1492
|
-
this.log?.warn(`connection to ${to} failed (${errStr})`, {
|
|
1493
|
-
clientId: this.clientId,
|
|
1494
|
-
connectedTo: to
|
|
1495
|
-
});
|
|
1496
|
-
} else {
|
|
1497
|
-
this.log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
|
|
1498
|
-
clientId: this.clientId,
|
|
1499
|
-
connectedTo: to
|
|
1500
|
-
});
|
|
1501
|
-
await this.connect(to);
|
|
1502
|
-
}
|
|
1439
|
+
let sleep = Promise.resolve();
|
|
1440
|
+
const backoffMs = this.retryBudget.getBackoffMs(to);
|
|
1441
|
+
if (backoffMs > 0) {
|
|
1442
|
+
sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
1503
1443
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1444
|
+
this.log?.info(
|
|
1445
|
+
`attempting connection to ${to} (${backoffMs}ms backoff)`,
|
|
1446
|
+
session.loggingMetadata
|
|
1447
|
+
);
|
|
1448
|
+
this.retryBudget.consumeBudget(to);
|
|
1449
|
+
const reconnectPromise = tracing_default.startActiveSpan("connect", async (span) => {
|
|
1450
|
+
try {
|
|
1451
|
+
span.addEvent("backoff", { backoffMs });
|
|
1452
|
+
await sleep;
|
|
1453
|
+
if (this.getStatus() !== "open") {
|
|
1454
|
+
throw new Error("transport state is no longer open");
|
|
1455
|
+
}
|
|
1456
|
+
span.addEvent("connecting");
|
|
1457
|
+
return await this.createNewOutgoingConnection(to);
|
|
1458
|
+
} catch (err) {
|
|
1459
|
+
const errStr = coerceErrorString(err);
|
|
1460
|
+
span.recordException(errStr);
|
|
1461
|
+
span.setStatus({ code: import_api3.SpanStatusCode.ERROR });
|
|
1462
|
+
throw err;
|
|
1463
|
+
} finally {
|
|
1464
|
+
span.end();
|
|
1465
|
+
}
|
|
1515
1466
|
});
|
|
1467
|
+
const connectingSession = SessionStateGraph.transition.NoConnectionToConnecting(
|
|
1468
|
+
session,
|
|
1469
|
+
reconnectPromise,
|
|
1470
|
+
{
|
|
1471
|
+
onConnectionEstablished: (conn) => {
|
|
1472
|
+
this.log?.debug(
|
|
1473
|
+
`connection to ${connectingSession.to} established`,
|
|
1474
|
+
connectingSession.loggingMetadata
|
|
1475
|
+
);
|
|
1476
|
+
this.onConnectionEstablished(connectingSession, conn);
|
|
1477
|
+
},
|
|
1478
|
+
onConnectionFailed: (error) => {
|
|
1479
|
+
const errStr = coerceErrorString(error);
|
|
1480
|
+
this.log?.error(
|
|
1481
|
+
`error connecting to ${connectingSession.to}: ${errStr}`,
|
|
1482
|
+
connectingSession.loggingMetadata
|
|
1483
|
+
);
|
|
1484
|
+
this.onConnectingFailed(connectingSession);
|
|
1485
|
+
},
|
|
1486
|
+
onConnectionTimeout: () => {
|
|
1487
|
+
this.log?.error(
|
|
1488
|
+
`connection to ${connectingSession.to} timed out`,
|
|
1489
|
+
connectingSession.loggingMetadata
|
|
1490
|
+
);
|
|
1491
|
+
this.onConnectingFailed(connectingSession);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
);
|
|
1495
|
+
this.updateSession(connectingSession);
|
|
1516
1496
|
}
|
|
1517
|
-
async sendHandshake(
|
|
1497
|
+
async sendHandshake(session) {
|
|
1518
1498
|
let metadata = void 0;
|
|
1519
1499
|
if (this.handshakeExtensions) {
|
|
1520
1500
|
metadata = await this.handshakeExtensions.construct();
|
|
1521
|
-
if (!import_value2.Value.Check(this.handshakeExtensions.schema, metadata)) {
|
|
1522
|
-
this.log?.error(`constructed handshake metadata did not match schema`, {
|
|
1523
|
-
...conn.loggingMetadata,
|
|
1524
|
-
clientId: this.clientId,
|
|
1525
|
-
connectedTo: to,
|
|
1526
|
-
validationErrors: [
|
|
1527
|
-
...import_value2.Value.Errors(this.handshakeExtensions.schema, metadata)
|
|
1528
|
-
],
|
|
1529
|
-
tags: ["invariant-violation"]
|
|
1530
|
-
});
|
|
1531
|
-
this.protocolError(
|
|
1532
|
-
ProtocolError.HandshakeFailed,
|
|
1533
|
-
"handshake metadata did not match schema"
|
|
1534
|
-
);
|
|
1535
|
-
conn.telemetry?.span.setStatus({
|
|
1536
|
-
code: import_api4.SpanStatusCode.ERROR,
|
|
1537
|
-
message: "handshake meta mismatch"
|
|
1538
|
-
});
|
|
1539
|
-
return false;
|
|
1540
|
-
}
|
|
1541
1501
|
}
|
|
1542
|
-
const { session } = this.getOrCreateSession({ to, handshakingConn: conn });
|
|
1543
1502
|
const requestMsg = handshakeRequestMessage({
|
|
1544
1503
|
from: this.clientId,
|
|
1545
|
-
to,
|
|
1504
|
+
to: session.to,
|
|
1546
1505
|
sessionId: session.id,
|
|
1547
1506
|
expectedSessionState: {
|
|
1548
|
-
|
|
1549
|
-
|
|
1507
|
+
nextExpectedSeq: session.ack,
|
|
1508
|
+
nextSentSeq: session.nextSeq()
|
|
1550
1509
|
},
|
|
1551
1510
|
metadata,
|
|
1552
1511
|
tracing: getPropagationContext(session.telemetry.ctx)
|
|
1553
1512
|
});
|
|
1554
|
-
this.log?.debug(`sending handshake request to ${to}`, {
|
|
1555
|
-
...
|
|
1556
|
-
clientId: this.clientId,
|
|
1557
|
-
connectedTo: to,
|
|
1513
|
+
this.log?.debug(`sending handshake request to ${session.to}`, {
|
|
1514
|
+
...session.loggingMetadata,
|
|
1558
1515
|
transportMessage: requestMsg
|
|
1559
1516
|
});
|
|
1560
|
-
|
|
1561
|
-
return true;
|
|
1517
|
+
session.sendHandshake(requestMsg);
|
|
1562
1518
|
}
|
|
1563
1519
|
close() {
|
|
1564
1520
|
this.retryBudget.close();
|
|
@@ -1567,7 +1523,7 @@ var ClientTransport = class extends Transport {
|
|
|
1567
1523
|
};
|
|
1568
1524
|
|
|
1569
1525
|
// transport/server.ts
|
|
1570
|
-
var
|
|
1526
|
+
var import_api4 = require("@opentelemetry/api");
|
|
1571
1527
|
var import_value3 = require("@sinclair/typebox/value");
|
|
1572
1528
|
var ServerTransport = class extends Transport {
|
|
1573
1529
|
/**
|
|
@@ -1581,14 +1537,14 @@ var ServerTransport = class extends Transport {
|
|
|
1581
1537
|
/**
|
|
1582
1538
|
* A map of session handshake data for each session.
|
|
1583
1539
|
*/
|
|
1584
|
-
sessionHandshakeMetadata;
|
|
1540
|
+
sessionHandshakeMetadata = /* @__PURE__ */ new Map();
|
|
1541
|
+
pendingSessions = /* @__PURE__ */ new Set();
|
|
1585
1542
|
constructor(clientId, providedOptions) {
|
|
1586
1543
|
super(clientId, providedOptions);
|
|
1587
1544
|
this.options = {
|
|
1588
1545
|
...defaultServerTransportOptions,
|
|
1589
1546
|
...providedOptions
|
|
1590
1547
|
};
|
|
1591
|
-
this.sessionHandshakeMetadata = /* @__PURE__ */ new WeakMap();
|
|
1592
1548
|
this.log?.info(`initiated server transport`, {
|
|
1593
1549
|
clientId: this.clientId,
|
|
1594
1550
|
protocolVersion: PROTOCOL_VERSION
|
|
@@ -1597,6 +1553,36 @@ var ServerTransport = class extends Transport {
|
|
|
1597
1553
|
extendHandshake(options) {
|
|
1598
1554
|
this.handshakeExtensions = options;
|
|
1599
1555
|
}
|
|
1556
|
+
send(to, msg) {
|
|
1557
|
+
if (this.getStatus() === "closed") {
|
|
1558
|
+
const err = "transport is closed, cant send";
|
|
1559
|
+
this.log?.error(err, {
|
|
1560
|
+
clientId: this.clientId,
|
|
1561
|
+
transportMessage: msg,
|
|
1562
|
+
tags: ["invariant-violation"]
|
|
1563
|
+
});
|
|
1564
|
+
throw new Error(err);
|
|
1565
|
+
}
|
|
1566
|
+
const session = this.sessions.get(to);
|
|
1567
|
+
if (!session) {
|
|
1568
|
+
const err = `session to ${to} does not exist`;
|
|
1569
|
+
this.log?.error(err, {
|
|
1570
|
+
clientId: this.clientId,
|
|
1571
|
+
transportMessage: msg,
|
|
1572
|
+
tags: ["invariant-violation"]
|
|
1573
|
+
});
|
|
1574
|
+
throw new Error(err);
|
|
1575
|
+
}
|
|
1576
|
+
return session.send(msg);
|
|
1577
|
+
}
|
|
1578
|
+
deletePendingSession(pendingSession) {
|
|
1579
|
+
pendingSession.close();
|
|
1580
|
+
this.pendingSessions.delete(pendingSession);
|
|
1581
|
+
}
|
|
1582
|
+
deleteSession(session) {
|
|
1583
|
+
this.sessionHandshakeMetadata.delete(session.to);
|
|
1584
|
+
super.deleteSession(session);
|
|
1585
|
+
}
|
|
1600
1586
|
handleConnection(conn) {
|
|
1601
1587
|
if (this.getStatus() !== "open")
|
|
1602
1588
|
return;
|
|
@@ -1604,281 +1590,369 @@ var ServerTransport = class extends Transport {
|
|
|
1604
1590
|
...conn.loggingMetadata,
|
|
1605
1591
|
clientId: this.clientId
|
|
1606
1592
|
});
|
|
1607
|
-
let
|
|
1608
|
-
const
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1593
|
+
let receivedHandshake = false;
|
|
1594
|
+
const pendingSession = SessionStateGraph.entrypoints.WaitingForHandshake(
|
|
1595
|
+
this.clientId,
|
|
1596
|
+
conn,
|
|
1597
|
+
{
|
|
1598
|
+
onConnectionClosed: () => {
|
|
1599
|
+
this.log?.warn(
|
|
1600
|
+
`connection from unknown closed before handshake finished`,
|
|
1601
|
+
pendingSession.loggingMetadata
|
|
1602
|
+
);
|
|
1603
|
+
this.deletePendingSession(pendingSession);
|
|
1604
|
+
},
|
|
1605
|
+
onConnectionErrored: (err) => {
|
|
1606
|
+
const errorString = coerceErrorString(err);
|
|
1607
|
+
this.log?.warn(
|
|
1608
|
+
`connection from unknown errored before handshake finished: ${errorString}`,
|
|
1609
|
+
pendingSession.loggingMetadata
|
|
1610
|
+
);
|
|
1611
|
+
this.deletePendingSession(pendingSession);
|
|
1612
|
+
},
|
|
1613
|
+
onHandshakeTimeout: () => {
|
|
1614
|
+
this.log?.warn(
|
|
1615
|
+
`connection from unknown timed out before handshake finished`,
|
|
1616
|
+
pendingSession.loggingMetadata
|
|
1617
|
+
);
|
|
1618
|
+
this.deletePendingSession(pendingSession);
|
|
1619
|
+
},
|
|
1620
|
+
onHandshake: (msg) => {
|
|
1621
|
+
if (receivedHandshake) {
|
|
1622
|
+
this.log?.error(
|
|
1623
|
+
`received multiple handshake messages from pending session`,
|
|
1624
|
+
{
|
|
1625
|
+
...pendingSession.loggingMetadata,
|
|
1626
|
+
connectedTo: msg.from,
|
|
1627
|
+
transportMessage: msg
|
|
1628
|
+
}
|
|
1629
|
+
);
|
|
1630
|
+
this.deletePendingSession(pendingSession);
|
|
1639
1631
|
return;
|
|
1640
1632
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
dataHandler(data2);
|
|
1652
|
-
}
|
|
1653
|
-
conn.removeDataListener(handshakeHandler);
|
|
1654
|
-
conn.addDataListener(dataHandler);
|
|
1655
|
-
buffer.length = 0;
|
|
1633
|
+
receivedHandshake = true;
|
|
1634
|
+
void this.onHandshakeRequest(pendingSession, msg);
|
|
1635
|
+
},
|
|
1636
|
+
onInvalidHandshake: (reason) => {
|
|
1637
|
+
this.log?.error(
|
|
1638
|
+
`invalid handshake: ${reason}`,
|
|
1639
|
+
pendingSession.loggingMetadata
|
|
1640
|
+
);
|
|
1641
|
+
this.deletePendingSession(pendingSession);
|
|
1642
|
+
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1656
1643
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
return;
|
|
1663
|
-
this.log?.info(`connection to ${client()} disconnected`, {
|
|
1664
|
-
...conn.loggingMetadata,
|
|
1665
|
-
clientId: this.clientId
|
|
1666
|
-
});
|
|
1667
|
-
this.onDisconnect(conn, session);
|
|
1668
|
-
});
|
|
1669
|
-
conn.addErrorListener((err) => {
|
|
1670
|
-
conn.telemetry?.span.setStatus({
|
|
1671
|
-
code: import_api5.SpanStatusCode.ERROR,
|
|
1672
|
-
message: "connection error"
|
|
1673
|
-
});
|
|
1674
|
-
if (!session)
|
|
1675
|
-
return;
|
|
1676
|
-
this.log?.warn(
|
|
1677
|
-
`connection to ${client()} got an error: ${coerceErrorString(err)}`,
|
|
1678
|
-
{ ...conn.loggingMetadata, clientId: this.clientId }
|
|
1679
|
-
);
|
|
1680
|
-
});
|
|
1681
|
-
}
|
|
1682
|
-
async validateHandshakeMetadata(conn, session, rawMetadata, from) {
|
|
1683
|
-
let parsedMetadata = {};
|
|
1684
|
-
if (this.handshakeExtensions) {
|
|
1685
|
-
if (!import_value3.Value.Check(this.handshakeExtensions.schema, rawMetadata)) {
|
|
1686
|
-
conn.telemetry?.span.setStatus({
|
|
1687
|
-
code: import_api5.SpanStatusCode.ERROR,
|
|
1688
|
-
message: "malformed handshake meta"
|
|
1689
|
-
});
|
|
1690
|
-
const reason = "received malformed handshake metadata";
|
|
1691
|
-
const responseMsg = handshakeResponseMessage({
|
|
1692
|
-
from: this.clientId,
|
|
1693
|
-
to: from,
|
|
1694
|
-
status: {
|
|
1695
|
-
ok: false,
|
|
1696
|
-
reason
|
|
1697
|
-
}
|
|
1698
|
-
});
|
|
1699
|
-
conn.send(this.codec.toBuffer(responseMsg));
|
|
1700
|
-
this.log?.warn(`received malformed handshake metadata from ${from}`, {
|
|
1701
|
-
...conn.loggingMetadata,
|
|
1702
|
-
clientId: this.clientId,
|
|
1703
|
-
validationErrors: [
|
|
1704
|
-
...import_value3.Value.Errors(this.handshakeExtensions.schema, rawMetadata)
|
|
1705
|
-
]
|
|
1706
|
-
});
|
|
1707
|
-
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1708
|
-
return false;
|
|
1709
|
-
}
|
|
1710
|
-
const previousParsedMetadata = session ? this.sessionHandshakeMetadata.get(session) : void 0;
|
|
1711
|
-
parsedMetadata = await this.handshakeExtensions.validate(
|
|
1712
|
-
rawMetadata,
|
|
1713
|
-
previousParsedMetadata
|
|
1714
|
-
);
|
|
1715
|
-
if (parsedMetadata === false) {
|
|
1716
|
-
const reason = "rejected by handshake handler";
|
|
1717
|
-
conn.telemetry?.span.setStatus({
|
|
1718
|
-
code: import_api5.SpanStatusCode.ERROR,
|
|
1719
|
-
message: reason
|
|
1720
|
-
});
|
|
1721
|
-
const responseMsg = handshakeResponseMessage({
|
|
1722
|
-
from: this.clientId,
|
|
1723
|
-
to: from,
|
|
1724
|
-
status: {
|
|
1725
|
-
ok: false,
|
|
1726
|
-
reason
|
|
1727
|
-
}
|
|
1728
|
-
});
|
|
1729
|
-
conn.send(this.codec.toBuffer(responseMsg));
|
|
1730
|
-
this.log?.warn(`rejected handshake from ${from}`, {
|
|
1731
|
-
...conn.loggingMetadata,
|
|
1732
|
-
clientId: this.clientId
|
|
1733
|
-
});
|
|
1734
|
-
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1735
|
-
return false;
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
return parsedMetadata;
|
|
1644
|
+
},
|
|
1645
|
+
this.options,
|
|
1646
|
+
this.log
|
|
1647
|
+
);
|
|
1648
|
+
this.pendingSessions.add(pendingSession);
|
|
1739
1649
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
ProtocolError.HandshakeFailed,
|
|
1749
|
-
"received non-transport message"
|
|
1750
|
-
);
|
|
1751
|
-
return false;
|
|
1752
|
-
}
|
|
1753
|
-
if (!import_value3.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
|
|
1754
|
-
conn.telemetry?.span.setStatus({
|
|
1755
|
-
code: import_api5.SpanStatusCode.ERROR,
|
|
1756
|
-
message: "invalid handshake request"
|
|
1757
|
-
});
|
|
1758
|
-
const reason = "received invalid handshake msg";
|
|
1759
|
-
const responseMsg2 = handshakeResponseMessage({
|
|
1650
|
+
rejectHandshakeRequest(session, to, reason, code, metadata) {
|
|
1651
|
+
session.conn.telemetry?.span.setStatus({
|
|
1652
|
+
code: import_api4.SpanStatusCode.ERROR,
|
|
1653
|
+
message: reason
|
|
1654
|
+
});
|
|
1655
|
+
this.log?.warn(reason, metadata);
|
|
1656
|
+
session.sendHandshake(
|
|
1657
|
+
handshakeResponseMessage({
|
|
1760
1658
|
from: this.clientId,
|
|
1761
|
-
to
|
|
1659
|
+
to,
|
|
1762
1660
|
status: {
|
|
1763
1661
|
ok: false,
|
|
1662
|
+
code,
|
|
1764
1663
|
reason
|
|
1765
1664
|
}
|
|
1766
|
-
})
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1665
|
+
})
|
|
1666
|
+
);
|
|
1667
|
+
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1668
|
+
this.deletePendingSession(session);
|
|
1669
|
+
}
|
|
1670
|
+
async onHandshakeRequest(session, msg) {
|
|
1671
|
+
if (!import_value3.Value.Check(ControlMessageHandshakeRequestSchema, msg.payload)) {
|
|
1672
|
+
this.rejectHandshakeRequest(
|
|
1673
|
+
session,
|
|
1674
|
+
msg.from,
|
|
1675
|
+
"received invalid handshake request",
|
|
1676
|
+
"MALFORMED_HANDSHAKE",
|
|
1677
|
+
{
|
|
1678
|
+
...session.loggingMetadata,
|
|
1679
|
+
transportMessage: msg,
|
|
1680
|
+
connectedTo: msg.from,
|
|
1681
|
+
validationErrors: [
|
|
1682
|
+
...import_value3.Value.Errors(ControlMessageHandshakeRequestSchema, msg.payload)
|
|
1683
|
+
]
|
|
1684
|
+
}
|
|
1781
1685
|
);
|
|
1782
|
-
return
|
|
1686
|
+
return;
|
|
1783
1687
|
}
|
|
1784
|
-
const gotVersion =
|
|
1688
|
+
const gotVersion = msg.payload.protocolVersion;
|
|
1785
1689
|
if (gotVersion !== PROTOCOL_VERSION) {
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
ok: false,
|
|
1796
|
-
reason
|
|
1690
|
+
this.rejectHandshakeRequest(
|
|
1691
|
+
session,
|
|
1692
|
+
msg.from,
|
|
1693
|
+
`expected protocol version ${PROTOCOL_VERSION}, got ${gotVersion}`,
|
|
1694
|
+
"PROTOCOL_VERSION_MISMATCH",
|
|
1695
|
+
{
|
|
1696
|
+
...session.loggingMetadata,
|
|
1697
|
+
connectedTo: msg.from,
|
|
1698
|
+
transportMessage: msg
|
|
1797
1699
|
}
|
|
1798
|
-
});
|
|
1799
|
-
conn.send(this.codec.toBuffer(responseMsg2));
|
|
1800
|
-
this.log?.warn(
|
|
1801
|
-
`received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
|
|
1802
|
-
{ ...conn.loggingMetadata, clientId: this.clientId }
|
|
1803
1700
|
);
|
|
1804
|
-
|
|
1805
|
-
return false;
|
|
1701
|
+
return;
|
|
1806
1702
|
}
|
|
1807
|
-
|
|
1703
|
+
let oldSession = this.sessions.get(msg.from);
|
|
1808
1704
|
const parsedMetadata = await this.validateHandshakeMetadata(
|
|
1809
|
-
|
|
1705
|
+
session,
|
|
1810
1706
|
oldSession,
|
|
1811
|
-
|
|
1812
|
-
|
|
1707
|
+
msg.payload.metadata,
|
|
1708
|
+
msg.from
|
|
1813
1709
|
);
|
|
1814
1710
|
if (parsedMetadata === false) {
|
|
1815
|
-
return
|
|
1711
|
+
return;
|
|
1816
1712
|
}
|
|
1817
|
-
let session;
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1713
|
+
let connectCase = "new session";
|
|
1714
|
+
if (oldSession && oldSession.id === msg.payload.sessionId) {
|
|
1715
|
+
connectCase = "transparent reconnection";
|
|
1716
|
+
const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
|
|
1717
|
+
const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq ?? 0;
|
|
1718
|
+
const ourNextSeq = oldSession.nextSeq();
|
|
1719
|
+
const ourAck = oldSession.ack;
|
|
1720
|
+
if (clientNextSentSeq > ourAck) {
|
|
1721
|
+
this.rejectHandshakeRequest(
|
|
1722
|
+
session,
|
|
1723
|
+
msg.from,
|
|
1724
|
+
`client is in the future: server wanted next message to be ${ourAck} but client would have sent ${clientNextSentSeq}`,
|
|
1725
|
+
"SESSION_STATE_MISMATCH",
|
|
1726
|
+
{
|
|
1727
|
+
...session.loggingMetadata,
|
|
1728
|
+
connectedTo: msg.from,
|
|
1729
|
+
transportMessage: msg
|
|
1730
|
+
}
|
|
1731
|
+
);
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
if (ourNextSeq > clientNextExpectedSeq) {
|
|
1735
|
+
this.rejectHandshakeRequest(
|
|
1736
|
+
session,
|
|
1737
|
+
msg.from,
|
|
1738
|
+
`server is in the future: client wanted next message to be ${clientNextExpectedSeq} but server would have sent ${ourNextSeq}`,
|
|
1739
|
+
"SESSION_STATE_MISMATCH",
|
|
1740
|
+
{
|
|
1741
|
+
...session.loggingMetadata,
|
|
1742
|
+
connectedTo: msg.from,
|
|
1743
|
+
transportMessage: msg
|
|
1744
|
+
}
|
|
1745
|
+
);
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
if (oldSession.state === "Connected" /* Connected */) {
|
|
1749
|
+
const noConnectionSession = SessionStateGraph.transition.ConnectedToNoConnection(oldSession, {
|
|
1750
|
+
onSessionGracePeriodElapsed: () => {
|
|
1751
|
+
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1752
|
+
}
|
|
1836
1753
|
});
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
ok: false,
|
|
1843
|
-
reason
|
|
1754
|
+
oldSession = noConnectionSession;
|
|
1755
|
+
} else if (oldSession.state === "Handshaking" /* Handshaking */) {
|
|
1756
|
+
const noConnectionSession = SessionStateGraph.transition.HandshakingToNoConnection(oldSession, {
|
|
1757
|
+
onSessionGracePeriodElapsed: () => {
|
|
1758
|
+
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1844
1759
|
}
|
|
1845
1760
|
});
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1761
|
+
oldSession = noConnectionSession;
|
|
1762
|
+
} else if (oldSession.state === "Connecting" /* Connecting */) {
|
|
1763
|
+
const noConnectionSession = SessionStateGraph.transition.ConnectingToNoConnection(oldSession, {
|
|
1764
|
+
onSessionGracePeriodElapsed: () => {
|
|
1765
|
+
this.onSessionGracePeriodElapsed(noConnectionSession);
|
|
1766
|
+
}
|
|
1767
|
+
});
|
|
1768
|
+
oldSession = noConnectionSession;
|
|
1853
1769
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1770
|
+
this.updateSession(oldSession);
|
|
1771
|
+
} else if (oldSession) {
|
|
1772
|
+
connectCase = "hard reconnection";
|
|
1773
|
+
this.deleteSession(oldSession);
|
|
1774
|
+
oldSession = void 0;
|
|
1856
1775
|
} else {
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1776
|
+
connectCase = "unknown session";
|
|
1777
|
+
const clientNextExpectedSeq = msg.payload.expectedSessionState.nextExpectedSeq;
|
|
1778
|
+
const clientNextSentSeq = msg.payload.expectedSessionState.nextSentSeq ?? 0;
|
|
1779
|
+
if (clientNextSentSeq > 0 || clientNextExpectedSeq > 0) {
|
|
1780
|
+
this.rejectHandshakeRequest(
|
|
1781
|
+
session,
|
|
1782
|
+
msg.from,
|
|
1783
|
+
`client is trying to reconnect to a session the server don't know about: ${msg.payload.sessionId}`,
|
|
1784
|
+
"SESSION_STATE_MISMATCH",
|
|
1785
|
+
{
|
|
1786
|
+
...session.loggingMetadata,
|
|
1787
|
+
connectedTo: msg.from,
|
|
1788
|
+
transportMessage: msg
|
|
1789
|
+
}
|
|
1790
|
+
);
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1865
1793
|
}
|
|
1866
|
-
|
|
1867
|
-
this.log?.
|
|
1868
|
-
`handshake from ${
|
|
1869
|
-
|
|
1794
|
+
const sessionId = msg.payload.sessionId;
|
|
1795
|
+
this.log?.info(
|
|
1796
|
+
`handshake from ${msg.from} ok (${connectCase}), responding with handshake success`,
|
|
1797
|
+
{
|
|
1798
|
+
...session.loggingMetadata,
|
|
1799
|
+
connectedTo: msg.from
|
|
1800
|
+
}
|
|
1870
1801
|
);
|
|
1871
1802
|
const responseMsg = handshakeResponseMessage({
|
|
1872
1803
|
from: this.clientId,
|
|
1873
|
-
to:
|
|
1804
|
+
to: msg.from,
|
|
1874
1805
|
status: {
|
|
1875
1806
|
ok: true,
|
|
1876
|
-
sessionId
|
|
1807
|
+
sessionId
|
|
1877
1808
|
}
|
|
1878
1809
|
});
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1810
|
+
session.sendHandshake(responseMsg);
|
|
1811
|
+
const connectedSession = SessionStateGraph.transition.WaitingForHandshakeToConnected(
|
|
1812
|
+
session,
|
|
1813
|
+
// by this point oldSession is either no connection or we dont have an old session
|
|
1814
|
+
oldSession,
|
|
1815
|
+
sessionId,
|
|
1816
|
+
msg.from,
|
|
1817
|
+
msg.tracing,
|
|
1818
|
+
{
|
|
1819
|
+
onConnectionErrored: (err) => {
|
|
1820
|
+
const errStr = coerceErrorString(err);
|
|
1821
|
+
this.log?.warn(
|
|
1822
|
+
`connection to ${connectedSession.to} errored: ${errStr}`,
|
|
1823
|
+
connectedSession.loggingMetadata
|
|
1824
|
+
);
|
|
1825
|
+
},
|
|
1826
|
+
onConnectionClosed: () => {
|
|
1827
|
+
this.log?.info(
|
|
1828
|
+
`connection to ${connectedSession.to} closed`,
|
|
1829
|
+
connectedSession.loggingMetadata
|
|
1830
|
+
);
|
|
1831
|
+
this.onConnClosed(connectedSession);
|
|
1832
|
+
},
|
|
1833
|
+
onMessage: (msg2) => this.handleMsg(msg2),
|
|
1834
|
+
onInvalidMessage: (reason) => {
|
|
1835
|
+
this.protocolError(ProtocolError.MessageOrderingViolated, reason);
|
|
1836
|
+
this.deleteSession(connectedSession);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
);
|
|
1840
|
+
this.sessionHandshakeMetadata.set(connectedSession.to, parsedMetadata);
|
|
1841
|
+
this.updateSession(connectedSession);
|
|
1842
|
+
this.pendingSessions.delete(session);
|
|
1843
|
+
connectedSession.startActiveHeartbeat();
|
|
1844
|
+
}
|
|
1845
|
+
async validateHandshakeMetadata(handshakingSession, existingSession, rawMetadata, from) {
|
|
1846
|
+
let parsedMetadata = {};
|
|
1847
|
+
if (this.handshakeExtensions) {
|
|
1848
|
+
if (!import_value3.Value.Check(this.handshakeExtensions.schema, rawMetadata)) {
|
|
1849
|
+
this.rejectHandshakeRequest(
|
|
1850
|
+
handshakingSession,
|
|
1851
|
+
from,
|
|
1852
|
+
"received malformed handshake metadata",
|
|
1853
|
+
"MALFORMED_HANDSHAKE_META",
|
|
1854
|
+
{
|
|
1855
|
+
...handshakingSession.loggingMetadata,
|
|
1856
|
+
connectedTo: from,
|
|
1857
|
+
validationErrors: [
|
|
1858
|
+
...import_value3.Value.Errors(this.handshakeExtensions.schema, rawMetadata)
|
|
1859
|
+
]
|
|
1860
|
+
}
|
|
1861
|
+
);
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
const previousParsedMetadata = existingSession ? this.sessionHandshakeMetadata.get(existingSession.to) : void 0;
|
|
1865
|
+
parsedMetadata = await this.handshakeExtensions.validate(
|
|
1866
|
+
rawMetadata,
|
|
1867
|
+
previousParsedMetadata
|
|
1868
|
+
);
|
|
1869
|
+
if (parsedMetadata === false) {
|
|
1870
|
+
this.rejectHandshakeRequest(
|
|
1871
|
+
handshakingSession,
|
|
1872
|
+
from,
|
|
1873
|
+
"rejected by handshake handler",
|
|
1874
|
+
"REJECTED_BY_CUSTOM_HANDLER",
|
|
1875
|
+
{
|
|
1876
|
+
...handshakingSession.loggingMetadata,
|
|
1877
|
+
connectedTo: from,
|
|
1878
|
+
clientId: this.clientId
|
|
1879
|
+
}
|
|
1880
|
+
);
|
|
1881
|
+
return false;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
return parsedMetadata;
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
|
|
1888
|
+
// transport/connection.ts
|
|
1889
|
+
var Connection = class {
|
|
1890
|
+
id;
|
|
1891
|
+
telemetry;
|
|
1892
|
+
constructor() {
|
|
1893
|
+
this.id = `conn-${generateId()}`;
|
|
1894
|
+
}
|
|
1895
|
+
get loggingMetadata() {
|
|
1896
|
+
const metadata = { connId: this.id };
|
|
1897
|
+
const spanContext = this.telemetry?.span.spanContext();
|
|
1898
|
+
if (this.telemetry?.span.isRecording() && spanContext) {
|
|
1899
|
+
metadata.telemetry = {
|
|
1900
|
+
traceId: spanContext.traceId,
|
|
1901
|
+
spanId: spanContext.spanId
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
return metadata;
|
|
1905
|
+
}
|
|
1906
|
+
// can't use event emitter because we need this to work in both node + browser
|
|
1907
|
+
_dataListeners = /* @__PURE__ */ new Set();
|
|
1908
|
+
_closeListeners = /* @__PURE__ */ new Set();
|
|
1909
|
+
_errorListeners = /* @__PURE__ */ new Set();
|
|
1910
|
+
get dataListeners() {
|
|
1911
|
+
return [...this._dataListeners];
|
|
1912
|
+
}
|
|
1913
|
+
get closeListeners() {
|
|
1914
|
+
return [...this._closeListeners];
|
|
1915
|
+
}
|
|
1916
|
+
get errorListeners() {
|
|
1917
|
+
return [...this._errorListeners];
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* Handle adding a callback for when a message is received.
|
|
1921
|
+
* @param msg The message that was received.
|
|
1922
|
+
*/
|
|
1923
|
+
addDataListener(cb) {
|
|
1924
|
+
this._dataListeners.add(cb);
|
|
1925
|
+
}
|
|
1926
|
+
removeDataListener(cb) {
|
|
1927
|
+
this._dataListeners.delete(cb);
|
|
1928
|
+
}
|
|
1929
|
+
/**
|
|
1930
|
+
* Handle adding a callback for when the connection is closed.
|
|
1931
|
+
* This should also be called if an error happens and after notifying all the error listeners.
|
|
1932
|
+
* @param cb The callback to call when the connection is closed.
|
|
1933
|
+
*/
|
|
1934
|
+
addCloseListener(cb) {
|
|
1935
|
+
this._closeListeners.add(cb);
|
|
1936
|
+
}
|
|
1937
|
+
removeCloseListener(cb) {
|
|
1938
|
+
this._closeListeners.delete(cb);
|
|
1939
|
+
}
|
|
1940
|
+
/**
|
|
1941
|
+
* Handle adding a callback for when an error is received.
|
|
1942
|
+
* This should only be used for this.logging errors, all cleanup
|
|
1943
|
+
* should be delegated to addCloseListener.
|
|
1944
|
+
*
|
|
1945
|
+
* The implementer should take care such that the implemented
|
|
1946
|
+
* connection will call both the close and error callbacks
|
|
1947
|
+
* on an error.
|
|
1948
|
+
*
|
|
1949
|
+
* @param cb The callback to call when an error is received.
|
|
1950
|
+
*/
|
|
1951
|
+
addErrorListener(cb) {
|
|
1952
|
+
this._errorListeners.add(cb);
|
|
1953
|
+
}
|
|
1954
|
+
removeErrorListener(cb) {
|
|
1955
|
+
this._errorListeners.delete(cb);
|
|
1882
1956
|
}
|
|
1883
1957
|
};
|
|
1884
1958
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1888,7 +1962,7 @@ var ServerTransport = class extends Transport {
|
|
|
1888
1962
|
OpaqueTransportMessageSchema,
|
|
1889
1963
|
ProtocolError,
|
|
1890
1964
|
ServerTransport,
|
|
1891
|
-
|
|
1965
|
+
SessionState,
|
|
1892
1966
|
Transport,
|
|
1893
1967
|
TransportMessageSchema
|
|
1894
1968
|
});
|