@replit/river 0.21.1 → 0.23.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 +1 -1
- package/dist/{chunk-FDLAPYCK.js → chunk-DZOATC6M.js} +2 -2
- package/dist/{chunk-JMXO5L2X.js → chunk-MJUFKPBT.js} +354 -398
- package/dist/chunk-MJUFKPBT.js.map +1 -0
- package/dist/{chunk-5WFL722S.js → chunk-PCKHBAVP.js} +94 -3
- package/dist/chunk-PCKHBAVP.js.map +1 -0
- package/dist/{chunk-NCXUFDVL.js → chunk-VOJVLWVX.js} +360 -516
- package/dist/chunk-VOJVLWVX.js.map +1 -0
- package/dist/chunk-ZF2UFTNN.js +60 -0
- package/dist/chunk-ZF2UFTNN.js.map +1 -0
- package/dist/{connection-76c5ed01.d.ts → connection-5685d817.d.ts} +6 -4
- package/dist/{connection-975b25c9.d.ts → connection-7582fb92.d.ts} +1 -1
- package/dist/{index-dfad460e.d.ts → index-a6fe0edd.d.ts} +55 -59
- package/dist/logging/index.d.cts +2 -1
- package/dist/logging/index.d.ts +2 -1
- package/dist/router/index.cjs +405 -502
- package/dist/router/index.cjs.map +1 -1
- package/dist/router/index.d.cts +12 -6
- package/dist/router/index.d.ts +12 -6
- package/dist/router/index.js +4 -3
- package/dist/{services-9c496c6e.d.ts → services-be91b485.d.ts} +21 -52
- package/dist/{services-7b716dcf.d.ts → services-eb9326a1.d.ts} +21 -52
- package/dist/transport/impls/uds/client.cjs +197 -155
- package/dist/transport/impls/uds/client.cjs.map +1 -1
- package/dist/transport/impls/uds/client.d.cts +3 -2
- package/dist/transport/impls/uds/client.d.ts +3 -2
- package/dist/transport/impls/uds/client.js +3 -3
- package/dist/transport/impls/uds/server.cjs +280 -266
- package/dist/transport/impls/uds/server.cjs.map +1 -1
- package/dist/transport/impls/uds/server.d.cts +3 -2
- package/dist/transport/impls/uds/server.d.ts +3 -2
- package/dist/transport/impls/uds/server.js +3 -3
- package/dist/transport/impls/ws/client.cjs +251 -214
- package/dist/transport/impls/ws/client.cjs.map +1 -1
- package/dist/transport/impls/ws/client.d.cts +6 -6
- package/dist/transport/impls/ws/client.d.ts +6 -6
- package/dist/transport/impls/ws/client.js +33 -48
- package/dist/transport/impls/ws/client.js.map +1 -1
- package/dist/transport/impls/ws/server.cjs +302 -280
- package/dist/transport/impls/ws/server.cjs.map +1 -1
- package/dist/transport/impls/ws/server.d.cts +5 -4
- package/dist/transport/impls/ws/server.d.ts +5 -4
- package/dist/transport/impls/ws/server.js +3 -3
- package/dist/transport/impls/ws/server.js.map +1 -1
- package/dist/transport/index.cjs +400 -396
- package/dist/transport/index.cjs.map +1 -1
- package/dist/transport/index.d.cts +25 -14
- package/dist/transport/index.d.ts +25 -14
- package/dist/transport/index.js +2 -2
- package/dist/util/testHelpers.cjs +59 -14
- package/dist/util/testHelpers.cjs.map +1 -1
- package/dist/util/testHelpers.d.cts +14 -5
- package/dist/util/testHelpers.d.ts +14 -5
- package/dist/util/testHelpers.js +12 -5
- package/dist/util/testHelpers.js.map +1 -1
- package/dist/wslike-e0b32dd5.d.ts +40 -0
- package/package.json +4 -5
- package/dist/chunk-3Y7AB5EB.js +0 -42
- package/dist/chunk-3Y7AB5EB.js.map +0 -1
- package/dist/chunk-5WFL722S.js.map +0 -1
- package/dist/chunk-JMXO5L2X.js.map +0 -1
- package/dist/chunk-NCXUFDVL.js.map +0 -1
- /package/dist/{chunk-FDLAPYCK.js.map → chunk-DZOATC6M.js.map} +0 -0
package/dist/transport/index.cjs
CHANGED
|
@@ -33,7 +33,6 @@ module.exports = __toCommonJS(transport_exports);
|
|
|
33
33
|
|
|
34
34
|
// transport/transport.ts
|
|
35
35
|
var import_value = require("@sinclair/typebox/value");
|
|
36
|
-
var import_api2 = require("@opentelemetry/api");
|
|
37
36
|
|
|
38
37
|
// transport/message.ts
|
|
39
38
|
var import_typebox = require("@sinclair/typebox");
|
|
@@ -167,17 +166,71 @@ var EventDispatcher = class {
|
|
|
167
166
|
|
|
168
167
|
// transport/session.ts
|
|
169
168
|
var import_nanoid2 = require("nanoid");
|
|
169
|
+
|
|
170
|
+
// tracing/index.ts
|
|
171
|
+
var import_api = require("@opentelemetry/api");
|
|
172
|
+
|
|
173
|
+
// package.json
|
|
174
|
+
var version = "0.23.0";
|
|
175
|
+
|
|
176
|
+
// tracing/index.ts
|
|
177
|
+
function getPropagationContext(ctx) {
|
|
178
|
+
const tracing = {
|
|
179
|
+
traceparent: "",
|
|
180
|
+
tracestate: ""
|
|
181
|
+
};
|
|
182
|
+
import_api.propagation.inject(ctx, tracing);
|
|
183
|
+
return tracing;
|
|
184
|
+
}
|
|
185
|
+
function createSessionTelemetryInfo(session, propagationCtx) {
|
|
186
|
+
const ctx = propagationCtx ? import_api.propagation.extract(import_api.context.active(), propagationCtx) : import_api.context.active();
|
|
187
|
+
const span = tracer.startSpan(
|
|
188
|
+
`session ${session.id}`,
|
|
189
|
+
{
|
|
190
|
+
attributes: {
|
|
191
|
+
component: "river",
|
|
192
|
+
"river.session.id": session.id,
|
|
193
|
+
"river.session.to": session.to,
|
|
194
|
+
"river.session.from": session.from
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
ctx
|
|
198
|
+
);
|
|
199
|
+
return { span, ctx };
|
|
200
|
+
}
|
|
201
|
+
function createConnectionTelemetryInfo(connection, sessionSpan) {
|
|
202
|
+
const ctx = import_api.trace.setSpan(import_api.context.active(), sessionSpan);
|
|
203
|
+
const span = tracer.startSpan(
|
|
204
|
+
`connection ${connection.id}`,
|
|
205
|
+
{
|
|
206
|
+
attributes: {
|
|
207
|
+
component: "river",
|
|
208
|
+
"river.connection.id": connection.id
|
|
209
|
+
},
|
|
210
|
+
links: [{ context: sessionSpan.spanContext() }]
|
|
211
|
+
},
|
|
212
|
+
ctx
|
|
213
|
+
);
|
|
214
|
+
return { span, ctx };
|
|
215
|
+
}
|
|
216
|
+
var tracer = import_api.trace.getTracer("river", version);
|
|
217
|
+
var tracing_default = tracer;
|
|
218
|
+
|
|
219
|
+
// transport/session.ts
|
|
220
|
+
var import_api2 = require("@opentelemetry/api");
|
|
170
221
|
var nanoid2 = (0, import_nanoid2.customAlphabet)("1234567890abcdefghijklmnopqrstuvxyz", 6);
|
|
171
222
|
var unsafeId = () => nanoid2();
|
|
172
223
|
var Connection = class {
|
|
173
|
-
|
|
224
|
+
id;
|
|
225
|
+
telemetry;
|
|
174
226
|
constructor() {
|
|
175
|
-
this.
|
|
227
|
+
this.id = `conn-${nanoid2(12)}`;
|
|
176
228
|
}
|
|
177
229
|
};
|
|
178
230
|
var Session = class {
|
|
179
231
|
codec;
|
|
180
232
|
options;
|
|
233
|
+
telemetry;
|
|
181
234
|
/**
|
|
182
235
|
* The buffer of messages that have been sent but not yet acknowledged.
|
|
183
236
|
*/
|
|
@@ -197,12 +250,6 @@ var Session = class {
|
|
|
197
250
|
* for this session.
|
|
198
251
|
*/
|
|
199
252
|
advertisedSessionId;
|
|
200
|
-
/**
|
|
201
|
-
* The metadata for this session, as parsed from the handshake.
|
|
202
|
-
*
|
|
203
|
-
* Will only ever be populated on the server side.
|
|
204
|
-
*/
|
|
205
|
-
metadata;
|
|
206
253
|
/**
|
|
207
254
|
* Number of messages we've sent along this session (excluding handshake and acks)
|
|
208
255
|
*/
|
|
@@ -224,7 +271,7 @@ var Session = class {
|
|
|
224
271
|
* The interval for sending heartbeats.
|
|
225
272
|
*/
|
|
226
273
|
heartbeat;
|
|
227
|
-
constructor(conn, from, to, options) {
|
|
274
|
+
constructor(conn, from, to, options, propagationCtx) {
|
|
228
275
|
this.id = `session-${nanoid2(12)}`;
|
|
229
276
|
this.options = options;
|
|
230
277
|
this.from = from;
|
|
@@ -236,13 +283,14 @@ var Session = class {
|
|
|
236
283
|
() => this.sendHeartbeat(),
|
|
237
284
|
options.heartbeatIntervalMs
|
|
238
285
|
);
|
|
286
|
+
this.telemetry = createSessionTelemetryInfo(this, propagationCtx);
|
|
239
287
|
}
|
|
240
288
|
get loggingMetadata() {
|
|
241
289
|
return {
|
|
242
290
|
clientId: this.from,
|
|
243
291
|
connectedTo: this.to,
|
|
244
292
|
sessionId: this.id,
|
|
245
|
-
connId: this.connection?.
|
|
293
|
+
connId: this.connection?.id
|
|
246
294
|
};
|
|
247
295
|
}
|
|
248
296
|
/**
|
|
@@ -287,6 +335,7 @@ var Session = class {
|
|
|
287
335
|
`closing connection to ${this.to} due to inactivity (missed ${misses} heartbeats which is ${missDuration}ms)`,
|
|
288
336
|
this.loggingMetadata
|
|
289
337
|
);
|
|
338
|
+
this.telemetry.span.addEvent("closing connection due to inactivity");
|
|
290
339
|
this.closeStaleConnection();
|
|
291
340
|
}
|
|
292
341
|
return;
|
|
@@ -308,21 +357,25 @@ var Session = class {
|
|
|
308
357
|
sendBufferedMessages(conn) {
|
|
309
358
|
log?.info(`resending ${this.sendBuffer.length} buffered messages`, {
|
|
310
359
|
...this.loggingMetadata,
|
|
311
|
-
connId: conn.
|
|
360
|
+
connId: conn.id
|
|
312
361
|
});
|
|
313
362
|
for (const msg of this.sendBuffer) {
|
|
314
363
|
log?.debug(`resending msg`, {
|
|
315
364
|
...this.loggingMetadata,
|
|
316
365
|
fullTransportMessage: msg,
|
|
317
|
-
connId: conn.
|
|
366
|
+
connId: conn.id
|
|
318
367
|
});
|
|
319
368
|
const ok = conn.send(this.codec.toBuffer(msg));
|
|
320
369
|
if (!ok) {
|
|
321
370
|
const errMsg = `failed to send buffered message to ${this.to} (sus, this is a fresh connection)`;
|
|
371
|
+
conn.telemetry?.span.setStatus({
|
|
372
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
373
|
+
message: errMsg
|
|
374
|
+
});
|
|
322
375
|
log?.error(errMsg, {
|
|
323
376
|
...this.loggingMetadata,
|
|
324
377
|
fullTransportMessage: msg,
|
|
325
|
-
connId: conn.
|
|
378
|
+
connId: conn.id,
|
|
326
379
|
tags: ["invariant-violation"]
|
|
327
380
|
});
|
|
328
381
|
conn.close();
|
|
@@ -405,11 +458,6 @@ var Session = class {
|
|
|
405
458
|
}
|
|
406
459
|
};
|
|
407
460
|
|
|
408
|
-
// tracing/index.ts
|
|
409
|
-
var import_api = require("@opentelemetry/api");
|
|
410
|
-
var tracer = import_api.trace.getTracer("river");
|
|
411
|
-
var tracing_default = tracer;
|
|
412
|
-
|
|
413
461
|
// util/stringify.ts
|
|
414
462
|
function coerceErrorString(err) {
|
|
415
463
|
if (err instanceof Error) {
|
|
@@ -542,6 +590,7 @@ var NaiveJsonCodec = {
|
|
|
542
590
|
};
|
|
543
591
|
|
|
544
592
|
// transport/transport.ts
|
|
593
|
+
var import_api3 = require("@opentelemetry/api");
|
|
545
594
|
var defaultTransportOptions = {
|
|
546
595
|
heartbeatIntervalMs: 1e3,
|
|
547
596
|
heartbeatsUntilDead: 2,
|
|
@@ -620,17 +669,22 @@ var Transport = class {
|
|
|
620
669
|
status: "connect",
|
|
621
670
|
conn
|
|
622
671
|
});
|
|
672
|
+
conn.telemetry = createConnectionTelemetryInfo(
|
|
673
|
+
conn,
|
|
674
|
+
session.telemetry.span
|
|
675
|
+
);
|
|
623
676
|
if (isReconnect) {
|
|
624
677
|
session.replaceWithNewConnection(conn);
|
|
625
678
|
log?.info(`reconnected to ${connectedTo}`, session.loggingMetadata);
|
|
626
679
|
}
|
|
627
680
|
}
|
|
628
|
-
createSession(to, conn) {
|
|
681
|
+
createSession(to, conn, propagationCtx) {
|
|
629
682
|
const session = new Session(
|
|
630
683
|
conn,
|
|
631
684
|
this.clientId,
|
|
632
685
|
to,
|
|
633
|
-
this.options
|
|
686
|
+
this.options,
|
|
687
|
+
propagationCtx
|
|
634
688
|
);
|
|
635
689
|
this.sessions.set(session.to, session);
|
|
636
690
|
this.eventDispatcher.dispatchEvent("sessionStatus", {
|
|
@@ -639,11 +693,11 @@ var Transport = class {
|
|
|
639
693
|
});
|
|
640
694
|
return session;
|
|
641
695
|
}
|
|
642
|
-
getOrCreateSession(to, conn, sessionId) {
|
|
696
|
+
getOrCreateSession(to, conn, sessionId, propagationCtx) {
|
|
643
697
|
let session = this.sessions.get(to);
|
|
644
698
|
let isReconnect = session !== void 0;
|
|
645
699
|
if (session?.advertisedSessionId !== void 0 && sessionId !== void 0 && session.advertisedSessionId !== sessionId) {
|
|
646
|
-
log?.
|
|
700
|
+
log?.info(
|
|
647
701
|
`session for ${to} already exists but has a different session id (expected: ${session.advertisedSessionId}, got: ${sessionId}), creating a new one`,
|
|
648
702
|
session.loggingMetadata
|
|
649
703
|
);
|
|
@@ -652,7 +706,7 @@ var Transport = class {
|
|
|
652
706
|
session = void 0;
|
|
653
707
|
}
|
|
654
708
|
if (!session) {
|
|
655
|
-
session = this.createSession(to, conn);
|
|
709
|
+
session = this.createSession(to, conn, propagationCtx);
|
|
656
710
|
log?.info(
|
|
657
711
|
`no session for ${to}, created a new one`,
|
|
658
712
|
session.loggingMetadata
|
|
@@ -665,6 +719,7 @@ var Transport = class {
|
|
|
665
719
|
}
|
|
666
720
|
deleteSession(session) {
|
|
667
721
|
session.close();
|
|
722
|
+
session.telemetry.span.end();
|
|
668
723
|
this.sessions.delete(session.to);
|
|
669
724
|
log?.info(
|
|
670
725
|
`session ${session.id} disconnect from ${session.to}`,
|
|
@@ -681,12 +736,16 @@ var Transport = class {
|
|
|
681
736
|
* @param connectedTo The peer we are connected to.
|
|
682
737
|
*/
|
|
683
738
|
onDisconnect(conn, session) {
|
|
739
|
+
conn.telemetry?.span.end();
|
|
684
740
|
this.eventDispatcher.dispatchEvent("connectionStatus", {
|
|
685
741
|
status: "disconnect",
|
|
686
742
|
conn
|
|
687
743
|
});
|
|
688
744
|
session.connection = void 0;
|
|
689
|
-
session.beginGrace(() =>
|
|
745
|
+
session.beginGrace(() => {
|
|
746
|
+
session.telemetry.span.addEvent("session grace period expired");
|
|
747
|
+
this.deleteSession(session);
|
|
748
|
+
});
|
|
690
749
|
}
|
|
691
750
|
/**
|
|
692
751
|
* Parses a message from a Uint8Array into a {@link OpaqueTransportMessage}.
|
|
@@ -746,6 +805,10 @@ var Transport = class {
|
|
|
746
805
|
tags: ["invariant-violation"]
|
|
747
806
|
});
|
|
748
807
|
this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
|
|
808
|
+
session.telemetry.span.setStatus({
|
|
809
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
810
|
+
message: "message order violated"
|
|
811
|
+
});
|
|
749
812
|
session.close();
|
|
750
813
|
}
|
|
751
814
|
return;
|
|
@@ -856,6 +919,10 @@ var ClientTransport = class extends Transport {
|
|
|
856
919
|
* tests or a special case where you don't want to reconnect.
|
|
857
920
|
*/
|
|
858
921
|
reconnectOnConnectionDrop = true;
|
|
922
|
+
/**
|
|
923
|
+
* Optional handshake options for this client.
|
|
924
|
+
*/
|
|
925
|
+
handshakeExtensions;
|
|
859
926
|
constructor(clientId, providedOptions) {
|
|
860
927
|
super(clientId, providedOptions);
|
|
861
928
|
this.options = {
|
|
@@ -865,6 +932,9 @@ var ClientTransport = class extends Transport {
|
|
|
865
932
|
this.inflightConnectionPromises = /* @__PURE__ */ new Map();
|
|
866
933
|
this.retryBudget = new LeakyBucketRateLimit(this.options);
|
|
867
934
|
}
|
|
935
|
+
extendHandshake(options) {
|
|
936
|
+
this.handshakeExtensions = options;
|
|
937
|
+
}
|
|
868
938
|
handleConnection(conn, to) {
|
|
869
939
|
if (this.state !== "open")
|
|
870
940
|
return;
|
|
@@ -873,7 +943,7 @@ var ClientTransport = class extends Transport {
|
|
|
873
943
|
if (!session) {
|
|
874
944
|
log?.warn(
|
|
875
945
|
`connection to ${to} timed out waiting for handshake, closing`,
|
|
876
|
-
{ clientId: this.clientId, connectedTo: to, connId: conn.
|
|
946
|
+
{ clientId: this.clientId, connectedTo: to, connId: conn.id }
|
|
877
947
|
);
|
|
878
948
|
conn.close();
|
|
879
949
|
}
|
|
@@ -891,6 +961,10 @@ var ClientTransport = class extends Transport {
|
|
|
891
961
|
conn.addDataListener((data2) => {
|
|
892
962
|
const parsed = this.parseMsg(data2);
|
|
893
963
|
if (!parsed) {
|
|
964
|
+
conn.telemetry?.span.setStatus({
|
|
965
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
966
|
+
message: "message parse failure"
|
|
967
|
+
});
|
|
894
968
|
conn.close();
|
|
895
969
|
return;
|
|
896
970
|
}
|
|
@@ -913,6 +987,10 @@ var ClientTransport = class extends Transport {
|
|
|
913
987
|
}
|
|
914
988
|
});
|
|
915
989
|
conn.addErrorListener((err) => {
|
|
990
|
+
conn.telemetry?.span.setStatus({
|
|
991
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
992
|
+
message: "connection error"
|
|
993
|
+
});
|
|
916
994
|
log?.warn(`error in connection to ${to}: ${coerceErrorString(err)}`, {
|
|
917
995
|
...session?.loggingMetadata,
|
|
918
996
|
clientId: this.clientId,
|
|
@@ -923,6 +1001,10 @@ var ClientTransport = class extends Transport {
|
|
|
923
1001
|
receiveHandshakeResponseMessage(data, conn) {
|
|
924
1002
|
const parsed = this.parseMsg(data);
|
|
925
1003
|
if (!parsed) {
|
|
1004
|
+
conn.telemetry?.span.setStatus({
|
|
1005
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1006
|
+
message: "non-transport message"
|
|
1007
|
+
});
|
|
926
1008
|
this.protocolError(
|
|
927
1009
|
ProtocolError.HandshakeFailed,
|
|
928
1010
|
"received non-transport message"
|
|
@@ -930,6 +1012,10 @@ var ClientTransport = class extends Transport {
|
|
|
930
1012
|
return false;
|
|
931
1013
|
}
|
|
932
1014
|
if (!import_value.Value.Check(ControlMessageHandshakeResponseSchema, parsed.payload)) {
|
|
1015
|
+
conn.telemetry?.span.setStatus({
|
|
1016
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1017
|
+
message: "invalid handshake response"
|
|
1018
|
+
});
|
|
933
1019
|
log?.warn(`received invalid handshake resp`, {
|
|
934
1020
|
clientId: this.clientId,
|
|
935
1021
|
connectedTo: parsed.from,
|
|
@@ -942,7 +1028,11 @@ var ClientTransport = class extends Transport {
|
|
|
942
1028
|
return false;
|
|
943
1029
|
}
|
|
944
1030
|
if (!parsed.payload.status.ok) {
|
|
945
|
-
|
|
1031
|
+
conn.telemetry?.span.setStatus({
|
|
1032
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1033
|
+
message: "handshake rejected"
|
|
1034
|
+
});
|
|
1035
|
+
log?.warn(`received handshake rejection`, {
|
|
946
1036
|
clientId: this.clientId,
|
|
947
1037
|
connectedTo: parsed.from,
|
|
948
1038
|
fullTransportMessage: parsed
|
|
@@ -972,142 +1062,94 @@ var ClientTransport = class extends Transport {
|
|
|
972
1062
|
* @param to The client ID of the node to connect to.
|
|
973
1063
|
*/
|
|
974
1064
|
async connect(to) {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
span.recordException(coerceErrorString(e));
|
|
992
|
-
}
|
|
993
|
-
span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
|
|
994
|
-
} finally {
|
|
995
|
-
span.end();
|
|
996
|
-
}
|
|
1065
|
+
const canProceedWithConnection = () => this.state === "open";
|
|
1066
|
+
if (!canProceedWithConnection()) {
|
|
1067
|
+
log?.info(
|
|
1068
|
+
`transport state is no longer open, cancelling attempt to connect to ${to}`,
|
|
1069
|
+
{ clientId: this.clientId, connectedTo: to }
|
|
1070
|
+
);
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
let reconnectPromise = this.inflightConnectionPromises.get(to);
|
|
1074
|
+
if (!reconnectPromise) {
|
|
1075
|
+
const budgetConsumed = this.retryBudget.getBudgetConsumed(to);
|
|
1076
|
+
if (!this.retryBudget.hasBudget(to)) {
|
|
1077
|
+
const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
|
|
1078
|
+
log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
|
|
1079
|
+
this.protocolError(ProtocolError.RetriesExceeded, errMsg);
|
|
1080
|
+
return;
|
|
997
1081
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
{
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
kind: import_api2.SpanKind.CLIENT
|
|
1010
|
-
},
|
|
1011
|
-
async (span) => {
|
|
1082
|
+
let sleep = Promise.resolve();
|
|
1083
|
+
const backoffMs = this.retryBudget.getBackoffMs(to);
|
|
1084
|
+
if (backoffMs > 0) {
|
|
1085
|
+
sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
1086
|
+
}
|
|
1087
|
+
log?.info(`attempting connection to ${to} (${backoffMs}ms backoff)`, {
|
|
1088
|
+
clientId: this.clientId,
|
|
1089
|
+
connectedTo: to
|
|
1090
|
+
});
|
|
1091
|
+
this.retryBudget.consumeBudget(to);
|
|
1092
|
+
reconnectPromise = tracing_default.startActiveSpan("connect", async (span) => {
|
|
1012
1093
|
try {
|
|
1013
|
-
|
|
1094
|
+
span.addEvent("backoff", { backoffMs });
|
|
1095
|
+
await sleep;
|
|
1014
1096
|
if (!canProceedWithConnection()) {
|
|
1015
|
-
|
|
1016
|
-
`transport state is no longer open, cancelling attempt to connect to ${to}`,
|
|
1017
|
-
{ clientId: this.clientId, connectedTo: to }
|
|
1018
|
-
);
|
|
1019
|
-
return false;
|
|
1097
|
+
throw new Error("transport state is no longer open");
|
|
1020
1098
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
if (!this.retryBudget.hasBudget(to)) {
|
|
1025
|
-
const errMsg = `tried to connect to ${to} but retry budget exceeded (more than ${budgetConsumed} attempts in the last ${this.retryBudget.totalBudgetRestoreTime}ms)`;
|
|
1026
|
-
log?.warn(errMsg, { clientId: this.clientId, connectedTo: to });
|
|
1027
|
-
this.protocolError(ProtocolError.RetriesExceeded, errMsg);
|
|
1028
|
-
return false;
|
|
1029
|
-
}
|
|
1030
|
-
let sleep = Promise.resolve();
|
|
1031
|
-
const backoffMs = this.retryBudget.getBackoffMs(to);
|
|
1032
|
-
if (backoffMs > 0) {
|
|
1033
|
-
sleep = new Promise((resolve) => setTimeout(resolve, backoffMs));
|
|
1034
|
-
}
|
|
1035
|
-
log?.info(
|
|
1036
|
-
`attempting connection to ${to} (${backoffMs}ms backoff)`,
|
|
1037
|
-
{
|
|
1038
|
-
clientId: this.clientId,
|
|
1039
|
-
connectedTo: to
|
|
1040
|
-
}
|
|
1041
|
-
);
|
|
1042
|
-
this.retryBudget.consumeBudget(to);
|
|
1043
|
-
reconnectPromise = sleep.then(() => {
|
|
1044
|
-
if (!canProceedWithConnection()) {
|
|
1045
|
-
throw new Error("transport state is no longer open");
|
|
1046
|
-
}
|
|
1047
|
-
}).then(() => this.createNewOutgoingConnection(to)).then((conn) => {
|
|
1048
|
-
if (!canProceedWithConnection()) {
|
|
1049
|
-
log?.info(
|
|
1050
|
-
`transport state is no longer open, closing pre-handshake connection to ${to}`,
|
|
1051
|
-
{
|
|
1052
|
-
clientId: this.clientId,
|
|
1053
|
-
connectedTo: to,
|
|
1054
|
-
connId: conn.debugId
|
|
1055
|
-
}
|
|
1056
|
-
);
|
|
1057
|
-
conn.close();
|
|
1058
|
-
throw new Error("transport state is no longer open");
|
|
1059
|
-
}
|
|
1060
|
-
return this.sendHandshake(to, conn).then((ok) => {
|
|
1061
|
-
if (!ok) {
|
|
1062
|
-
conn.close();
|
|
1063
|
-
throw new Error("failed to send handshake");
|
|
1064
|
-
}
|
|
1065
|
-
return conn;
|
|
1066
|
-
});
|
|
1067
|
-
});
|
|
1068
|
-
this.inflightConnectionPromises.set(to, reconnectPromise);
|
|
1069
|
-
} else {
|
|
1099
|
+
span.addEvent("connecting");
|
|
1100
|
+
const conn = await this.createNewOutgoingConnection(to);
|
|
1101
|
+
if (!canProceedWithConnection()) {
|
|
1070
1102
|
log?.info(
|
|
1071
|
-
`
|
|
1103
|
+
`transport state is no longer open, closing pre-handshake connection to ${to}`,
|
|
1072
1104
|
{
|
|
1073
1105
|
clientId: this.clientId,
|
|
1074
|
-
connectedTo: to
|
|
1106
|
+
connectedTo: to,
|
|
1107
|
+
connId: conn.id
|
|
1075
1108
|
}
|
|
1076
1109
|
);
|
|
1110
|
+
conn.close();
|
|
1111
|
+
throw new Error("transport state is no longer open");
|
|
1077
1112
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
|
|
1084
|
-
log?.warn(`connection to ${to} failed (${errStr})`, {
|
|
1085
|
-
clientId: this.clientId,
|
|
1086
|
-
connectedTo: to
|
|
1087
|
-
});
|
|
1088
|
-
} else {
|
|
1089
|
-
log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
|
|
1090
|
-
clientId: this.clientId,
|
|
1091
|
-
connectedTo: to
|
|
1092
|
-
});
|
|
1093
|
-
return true;
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
} catch (e) {
|
|
1097
|
-
if (e instanceof Error) {
|
|
1098
|
-
span.recordException(e);
|
|
1099
|
-
} else {
|
|
1100
|
-
span.recordException(coerceErrorString(e));
|
|
1113
|
+
span.addEvent("sending handshake");
|
|
1114
|
+
const ok = await this.sendHandshake(to, conn);
|
|
1115
|
+
if (!ok) {
|
|
1116
|
+
conn.close();
|
|
1117
|
+
throw new Error("failed to send handshake");
|
|
1101
1118
|
}
|
|
1102
|
-
|
|
1119
|
+
return conn;
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
const errStr = coerceErrorString(err);
|
|
1122
|
+
span.recordException(errStr);
|
|
1123
|
+
span.setStatus({ code: import_api3.SpanStatusCode.ERROR });
|
|
1124
|
+
throw err;
|
|
1103
1125
|
} finally {
|
|
1104
1126
|
span.end();
|
|
1105
1127
|
}
|
|
1106
|
-
|
|
1128
|
+
});
|
|
1129
|
+
this.inflightConnectionPromises.set(to, reconnectPromise);
|
|
1130
|
+
} else {
|
|
1131
|
+
log?.info(`attempting connection to ${to} (reusing previous attempt)`, {
|
|
1132
|
+
clientId: this.clientId,
|
|
1133
|
+
connectedTo: to
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
try {
|
|
1137
|
+
await reconnectPromise;
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
this.inflightConnectionPromises.delete(to);
|
|
1140
|
+
const errStr = coerceErrorString(error);
|
|
1141
|
+
if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
|
|
1142
|
+
log?.warn(`connection to ${to} failed (${errStr})`, {
|
|
1143
|
+
clientId: this.clientId,
|
|
1144
|
+
connectedTo: to
|
|
1145
|
+
});
|
|
1146
|
+
} else {
|
|
1147
|
+
log?.warn(`connection to ${to} failed (${errStr}), retrying`, {
|
|
1148
|
+
clientId: this.clientId,
|
|
1149
|
+
connectedTo: to
|
|
1150
|
+
});
|
|
1151
|
+
return this.connect(to);
|
|
1107
1152
|
}
|
|
1108
|
-
);
|
|
1109
|
-
if (retry) {
|
|
1110
|
-
return this.connectAttempt(to, attempt + 1);
|
|
1111
1153
|
}
|
|
1112
1154
|
}
|
|
1113
1155
|
deleteSession(session) {
|
|
@@ -1115,13 +1157,11 @@ var ClientTransport = class extends Transport {
|
|
|
1115
1157
|
super.deleteSession(session);
|
|
1116
1158
|
}
|
|
1117
1159
|
async sendHandshake(to, conn) {
|
|
1118
|
-
const tracing = { traceparent: "", tracestate: "" };
|
|
1119
|
-
import_api2.propagation.inject(import_api2.context.active(), tracing);
|
|
1120
1160
|
let metadata;
|
|
1121
|
-
if (this.
|
|
1122
|
-
metadata = await this.
|
|
1123
|
-
if (!import_value.Value.Check(this.
|
|
1124
|
-
log?.error(`handshake metadata did not match schema`, {
|
|
1161
|
+
if (this.handshakeExtensions) {
|
|
1162
|
+
metadata = await this.handshakeExtensions.construct();
|
|
1163
|
+
if (!import_value.Value.Check(this.handshakeExtensions.schema, metadata)) {
|
|
1164
|
+
log?.error(`constructed handshake metadata did not match schema`, {
|
|
1125
1165
|
clientId: this.clientId,
|
|
1126
1166
|
connectedTo: to,
|
|
1127
1167
|
tags: ["invariant-violation"]
|
|
@@ -1130,6 +1170,10 @@ var ClientTransport = class extends Transport {
|
|
|
1130
1170
|
ProtocolError.HandshakeFailed,
|
|
1131
1171
|
"handshake metadata did not match schema"
|
|
1132
1172
|
);
|
|
1173
|
+
conn.telemetry?.span.setStatus({
|
|
1174
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1175
|
+
message: "handshake meta mismatch"
|
|
1176
|
+
});
|
|
1133
1177
|
return false;
|
|
1134
1178
|
}
|
|
1135
1179
|
}
|
|
@@ -1139,7 +1183,7 @@ var ClientTransport = class extends Transport {
|
|
|
1139
1183
|
to,
|
|
1140
1184
|
session.id,
|
|
1141
1185
|
metadata,
|
|
1142
|
-
|
|
1186
|
+
getPropagationContext(session.telemetry.ctx)
|
|
1143
1187
|
);
|
|
1144
1188
|
log?.debug(`sending handshake request to ${to}`, {
|
|
1145
1189
|
clientId: this.clientId,
|
|
@@ -1158,280 +1202,240 @@ var ServerTransport = class extends Transport {
|
|
|
1158
1202
|
* The options for this transport.
|
|
1159
1203
|
*/
|
|
1160
1204
|
options;
|
|
1205
|
+
/**
|
|
1206
|
+
* Optional handshake options for the server.
|
|
1207
|
+
*/
|
|
1208
|
+
handshakeExtensions;
|
|
1209
|
+
/**
|
|
1210
|
+
* A map of session handshake data for each session.
|
|
1211
|
+
*/
|
|
1212
|
+
sessionHandshakeMetadata;
|
|
1161
1213
|
constructor(clientId, providedOptions) {
|
|
1162
1214
|
super(clientId, providedOptions);
|
|
1163
1215
|
this.options = {
|
|
1164
1216
|
...defaultServerTransportOptions,
|
|
1165
1217
|
...providedOptions
|
|
1166
1218
|
};
|
|
1219
|
+
this.sessionHandshakeMetadata = /* @__PURE__ */ new WeakMap();
|
|
1167
1220
|
log?.info(`initiated server transport`, {
|
|
1168
1221
|
clientId: this.clientId,
|
|
1169
1222
|
protocolVersion: PROTOCOL_VERSION
|
|
1170
1223
|
});
|
|
1171
1224
|
}
|
|
1225
|
+
extendHandshake(options) {
|
|
1226
|
+
this.handshakeExtensions = options;
|
|
1227
|
+
}
|
|
1172
1228
|
handleConnection(conn) {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
(
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1229
|
+
if (this.state !== "open")
|
|
1230
|
+
return;
|
|
1231
|
+
log?.info(`new incoming connection`, {
|
|
1232
|
+
clientId: this.clientId,
|
|
1233
|
+
connId: conn.id
|
|
1234
|
+
});
|
|
1235
|
+
let session = void 0;
|
|
1236
|
+
const client = () => session?.to ?? "unknown";
|
|
1237
|
+
const handshakeTimeout = setTimeout(() => {
|
|
1238
|
+
if (!session) {
|
|
1239
|
+
log?.warn(
|
|
1240
|
+
`connection to ${client()} timed out waiting for handshake, closing`,
|
|
1241
|
+
{
|
|
1242
|
+
clientId: this.clientId,
|
|
1243
|
+
connectedTo: client(),
|
|
1244
|
+
connId: conn.id
|
|
1245
|
+
}
|
|
1246
|
+
);
|
|
1247
|
+
conn.telemetry?.span.setStatus({
|
|
1248
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1249
|
+
message: "handshake timeout"
|
|
1188
1250
|
});
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1251
|
+
conn.close();
|
|
1252
|
+
}
|
|
1253
|
+
}, this.options.sessionDisconnectGraceMs);
|
|
1254
|
+
const buffer = [];
|
|
1255
|
+
let receivedHandshakeMessage = false;
|
|
1256
|
+
const handshakeHandler = (data) => {
|
|
1257
|
+
if (receivedHandshakeMessage) {
|
|
1258
|
+
buffer.push(data);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
receivedHandshakeMessage = true;
|
|
1262
|
+
clearTimeout(handshakeTimeout);
|
|
1263
|
+
void this.receiveHandshakeRequestMessage(data, conn).then(
|
|
1264
|
+
(maybeSession) => {
|
|
1265
|
+
if (!maybeSession) {
|
|
1203
1266
|
conn.close();
|
|
1204
|
-
}
|
|
1205
|
-
}, this.options.sessionDisconnectGraceMs);
|
|
1206
|
-
const buffer = [];
|
|
1207
|
-
let receivedHandshakeMessage = false;
|
|
1208
|
-
const handshakeHandler = (data) => {
|
|
1209
|
-
if (receivedHandshakeMessage) {
|
|
1210
|
-
buffer.push(data);
|
|
1211
1267
|
return;
|
|
1212
1268
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
(
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
span.end();
|
|
1220
|
-
conn.close();
|
|
1221
|
-
return;
|
|
1222
|
-
}
|
|
1223
|
-
session = maybeSession;
|
|
1224
|
-
const dataHandler = (data2) => {
|
|
1225
|
-
const parsed = this.parseMsg(data2);
|
|
1226
|
-
if (!parsed) {
|
|
1227
|
-
conn.close();
|
|
1228
|
-
return;
|
|
1229
|
-
}
|
|
1230
|
-
this.handleMsg(parsed);
|
|
1231
|
-
};
|
|
1232
|
-
conn.removeDataListener(handshakeHandler);
|
|
1233
|
-
conn.addDataListener(dataHandler);
|
|
1234
|
-
for (const data2 of buffer) {
|
|
1235
|
-
dataHandler(data2);
|
|
1236
|
-
}
|
|
1237
|
-
buffer.length = 0;
|
|
1269
|
+
session = maybeSession;
|
|
1270
|
+
const dataHandler = (data2) => {
|
|
1271
|
+
const parsed = this.parseMsg(data2);
|
|
1272
|
+
if (!parsed) {
|
|
1273
|
+
conn.close();
|
|
1274
|
+
return;
|
|
1238
1275
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
if (session) {
|
|
1244
|
-
log?.info(`connection to ${client()} disconnected`, {
|
|
1245
|
-
clientId: this.clientId,
|
|
1246
|
-
connId: conn.debugId
|
|
1247
|
-
});
|
|
1248
|
-
this.onDisconnect(conn, session);
|
|
1276
|
+
this.handleMsg(parsed);
|
|
1277
|
+
};
|
|
1278
|
+
for (const data2 of buffer) {
|
|
1279
|
+
dataHandler(data2);
|
|
1249
1280
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1281
|
+
conn.removeDataListener(handshakeHandler);
|
|
1282
|
+
conn.addDataListener(dataHandler);
|
|
1283
|
+
buffer.length = 0;
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
};
|
|
1287
|
+
conn.addDataListener(handshakeHandler);
|
|
1288
|
+
conn.addCloseListener(() => {
|
|
1289
|
+
if (!session)
|
|
1290
|
+
return;
|
|
1291
|
+
log?.info(`connection to ${client()} disconnected`, {
|
|
1292
|
+
clientId: this.clientId,
|
|
1293
|
+
connId: conn.id
|
|
1294
|
+
});
|
|
1295
|
+
this.onDisconnect(conn, session);
|
|
1296
|
+
});
|
|
1297
|
+
conn.addErrorListener((err) => {
|
|
1298
|
+
conn.telemetry?.span.setStatus({
|
|
1299
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1300
|
+
message: "connection error"
|
|
1301
|
+
});
|
|
1302
|
+
if (!session)
|
|
1303
|
+
return;
|
|
1304
|
+
log?.warn(
|
|
1305
|
+
`connection to ${client()} got an error: ${coerceErrorString(err)}`,
|
|
1306
|
+
{ clientId: this.clientId, connId: conn.id }
|
|
1307
|
+
);
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
async validateHandshakeMetadata(conn, session, rawMetadata, from) {
|
|
1311
|
+
let parsedMetadata = {};
|
|
1312
|
+
if (this.handshakeExtensions) {
|
|
1313
|
+
if (!import_value.Value.Check(this.handshakeExtensions.schema, rawMetadata)) {
|
|
1314
|
+
conn.telemetry?.span.setStatus({
|
|
1315
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1316
|
+
message: "malformed handshake meta"
|
|
1252
1317
|
});
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
err
|
|
1258
|
-
)}`,
|
|
1259
|
-
{ clientId: this.clientId, connId: conn.debugId }
|
|
1260
|
-
);
|
|
1261
|
-
}
|
|
1262
|
-
span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
|
|
1263
|
-
span.end();
|
|
1318
|
+
const reason = "received malformed handshake metadata";
|
|
1319
|
+
const responseMsg = handshakeResponseMessage(this.clientId, from, {
|
|
1320
|
+
ok: false,
|
|
1321
|
+
reason
|
|
1264
1322
|
});
|
|
1323
|
+
conn.send(this.codec.toBuffer(responseMsg));
|
|
1324
|
+
log?.warn(`received malformed handshake metadata from ${from}`, {
|
|
1325
|
+
clientId: this.clientId,
|
|
1326
|
+
connId: conn.id
|
|
1327
|
+
});
|
|
1328
|
+
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1329
|
+
return false;
|
|
1265
1330
|
}
|
|
1266
|
-
|
|
1331
|
+
parsedMetadata = await this.handshakeExtensions.validate(
|
|
1332
|
+
rawMetadata,
|
|
1333
|
+
session
|
|
1334
|
+
);
|
|
1335
|
+
if (parsedMetadata === false) {
|
|
1336
|
+
const reason = "rejected by handshake handler";
|
|
1337
|
+
conn.telemetry?.span.setStatus({
|
|
1338
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1339
|
+
message: reason
|
|
1340
|
+
});
|
|
1341
|
+
const responseMsg = handshakeResponseMessage(this.clientId, from, {
|
|
1342
|
+
ok: false,
|
|
1343
|
+
reason
|
|
1344
|
+
});
|
|
1345
|
+
conn.send(this.codec.toBuffer(responseMsg));
|
|
1346
|
+
log?.warn(`rejected handshake from ${from}`, {
|
|
1347
|
+
clientId: this.clientId,
|
|
1348
|
+
connId: conn.id
|
|
1349
|
+
});
|
|
1350
|
+
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1351
|
+
return false;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
return parsedMetadata;
|
|
1267
1355
|
}
|
|
1268
1356
|
async receiveHandshakeRequestMessage(data, conn) {
|
|
1269
1357
|
const parsed = this.parseMsg(data);
|
|
1270
1358
|
if (!parsed) {
|
|
1359
|
+
conn.telemetry?.span.setStatus({
|
|
1360
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1361
|
+
message: "non-transport message"
|
|
1362
|
+
});
|
|
1271
1363
|
this.protocolError(
|
|
1272
1364
|
ProtocolError.HandshakeFailed,
|
|
1273
1365
|
"received non-transport message"
|
|
1274
1366
|
);
|
|
1275
1367
|
return false;
|
|
1276
1368
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1369
|
+
if (!import_value.Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
|
|
1370
|
+
conn.telemetry?.span.setStatus({
|
|
1371
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1372
|
+
message: "invalid handshake request"
|
|
1373
|
+
});
|
|
1374
|
+
const reason = "received invalid handshake msg";
|
|
1375
|
+
const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
|
|
1376
|
+
ok: false,
|
|
1377
|
+
reason
|
|
1378
|
+
});
|
|
1379
|
+
conn.send(this.codec.toBuffer(responseMsg2));
|
|
1380
|
+
const logData = { ...parsed.payload ?? {}, metadata: "redacted" };
|
|
1381
|
+
log?.warn(reason, {
|
|
1382
|
+
clientId: this.clientId,
|
|
1383
|
+
connId: conn.id,
|
|
1384
|
+
partialTransportMessage: { ...parsed, payload: logData }
|
|
1385
|
+
});
|
|
1386
|
+
this.protocolError(
|
|
1387
|
+
ProtocolError.HandshakeFailed,
|
|
1388
|
+
"invalid handshake request"
|
|
1389
|
+
);
|
|
1390
|
+
return false;
|
|
1280
1391
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
{
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
} : { ...parsed };
|
|
1307
|
-
log?.warn(`${reason}: ${JSON.stringify(logData)}`, {
|
|
1308
|
-
clientId: this.clientId,
|
|
1309
|
-
connId: conn.debugId
|
|
1310
|
-
});
|
|
1311
|
-
this.protocolError(
|
|
1312
|
-
ProtocolError.HandshakeFailed,
|
|
1313
|
-
"invalid handshake request"
|
|
1314
|
-
);
|
|
1315
|
-
span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
|
|
1316
|
-
span.end();
|
|
1317
|
-
return false;
|
|
1318
|
-
}
|
|
1319
|
-
const gotVersion = parsed.payload.protocolVersion;
|
|
1320
|
-
if (gotVersion !== PROTOCOL_VERSION) {
|
|
1321
|
-
const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
|
|
1322
|
-
const responseMsg2 = handshakeResponseMessage(
|
|
1323
|
-
this.clientId,
|
|
1324
|
-
parsed.from,
|
|
1325
|
-
{
|
|
1326
|
-
ok: false,
|
|
1327
|
-
reason
|
|
1328
|
-
}
|
|
1329
|
-
);
|
|
1330
|
-
conn.send(this.codec.toBuffer(responseMsg2));
|
|
1331
|
-
log?.warn(
|
|
1332
|
-
`received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
|
|
1333
|
-
{ clientId: this.clientId, connId: conn.debugId }
|
|
1334
|
-
);
|
|
1335
|
-
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1336
|
-
span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
|
|
1337
|
-
span.end();
|
|
1338
|
-
return false;
|
|
1339
|
-
}
|
|
1340
|
-
const { session, isReconnect } = this.getOrCreateSession(
|
|
1341
|
-
parsed.from,
|
|
1342
|
-
conn,
|
|
1343
|
-
parsed.payload.sessionId
|
|
1344
|
-
);
|
|
1345
|
-
let handshakeMetadata;
|
|
1346
|
-
if (this.options.handshake) {
|
|
1347
|
-
if (!import_value.Value.Check(
|
|
1348
|
-
this.options.handshake.requestSchema,
|
|
1349
|
-
parsed.payload.metadata
|
|
1350
|
-
)) {
|
|
1351
|
-
const reason = "received malformed handshake metadata";
|
|
1352
|
-
const responseMsg2 = handshakeResponseMessage(
|
|
1353
|
-
this.clientId,
|
|
1354
|
-
parsed.from,
|
|
1355
|
-
{ ok: false, reason }
|
|
1356
|
-
);
|
|
1357
|
-
conn.send(this.codec.toBuffer(responseMsg2));
|
|
1358
|
-
log?.warn(
|
|
1359
|
-
`received malformed handshake metadata from ${parsed.from}`,
|
|
1360
|
-
{
|
|
1361
|
-
clientId: this.clientId,
|
|
1362
|
-
connId: conn.debugId
|
|
1363
|
-
}
|
|
1364
|
-
);
|
|
1365
|
-
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1366
|
-
this.deleteSession(session);
|
|
1367
|
-
span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
|
|
1368
|
-
span.end();
|
|
1369
|
-
return false;
|
|
1370
|
-
}
|
|
1371
|
-
const parsedMetadata = await this.options.handshake.parse(
|
|
1372
|
-
parsed.payload.metadata,
|
|
1373
|
-
session,
|
|
1374
|
-
isReconnect
|
|
1375
|
-
);
|
|
1376
|
-
if (parsedMetadata === false) {
|
|
1377
|
-
const reason = "rejected by server";
|
|
1378
|
-
const responseMsg2 = handshakeResponseMessage(
|
|
1379
|
-
this.clientId,
|
|
1380
|
-
parsed.from,
|
|
1381
|
-
{ ok: false, reason }
|
|
1382
|
-
);
|
|
1383
|
-
conn.send(this.codec.toBuffer(responseMsg2));
|
|
1384
|
-
log?.warn(`rejected handshake from ${parsed.from}`, {
|
|
1385
|
-
clientId: this.clientId,
|
|
1386
|
-
connId: conn.debugId
|
|
1387
|
-
});
|
|
1388
|
-
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1389
|
-
this.deleteSession(session);
|
|
1390
|
-
span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
|
|
1391
|
-
span.end();
|
|
1392
|
-
return false;
|
|
1393
|
-
}
|
|
1394
|
-
if (!import_value.Value.Check(this.options.handshake.parsedSchema, parsedMetadata)) {
|
|
1395
|
-
const reason = "failed to parse handshake metadata";
|
|
1396
|
-
const responseMsg2 = handshakeResponseMessage(
|
|
1397
|
-
this.clientId,
|
|
1398
|
-
parsed.from,
|
|
1399
|
-
{ ok: false, reason }
|
|
1400
|
-
);
|
|
1401
|
-
conn.send(this.codec.toBuffer(responseMsg2));
|
|
1402
|
-
log?.error(`failed to parse handshake metadata`, {
|
|
1403
|
-
clientId: this.clientId,
|
|
1404
|
-
connId: conn.debugId,
|
|
1405
|
-
tags: ["invariant-violation"]
|
|
1406
|
-
});
|
|
1407
|
-
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1408
|
-
this.deleteSession(session);
|
|
1409
|
-
span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
|
|
1410
|
-
span.end();
|
|
1411
|
-
return false;
|
|
1412
|
-
}
|
|
1413
|
-
handshakeMetadata = parsedMetadata;
|
|
1414
|
-
}
|
|
1415
|
-
handshakeMetadata ??= {};
|
|
1416
|
-
session.metadata = handshakeMetadata;
|
|
1417
|
-
log?.debug(
|
|
1418
|
-
`handshake from ${parsed.from} ok, responding with handshake success`,
|
|
1419
|
-
{ clientId: this.clientId, connId: conn.debugId }
|
|
1420
|
-
);
|
|
1421
|
-
const responseMsg = handshakeResponseMessage(
|
|
1422
|
-
this.clientId,
|
|
1423
|
-
parsed.from,
|
|
1424
|
-
{
|
|
1425
|
-
ok: true,
|
|
1426
|
-
sessionId: session.id
|
|
1427
|
-
}
|
|
1428
|
-
);
|
|
1429
|
-
conn.send(this.codec.toBuffer(responseMsg));
|
|
1430
|
-
this.onConnect(conn, parsed.from, session, isReconnect);
|
|
1431
|
-
span.end();
|
|
1432
|
-
return session;
|
|
1433
|
-
}
|
|
1392
|
+
const gotVersion = parsed.payload.protocolVersion;
|
|
1393
|
+
if (gotVersion !== PROTOCOL_VERSION) {
|
|
1394
|
+
conn.telemetry?.span.setStatus({
|
|
1395
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1396
|
+
message: "incorrect protocol version"
|
|
1397
|
+
});
|
|
1398
|
+
const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
|
|
1399
|
+
const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
|
|
1400
|
+
ok: false,
|
|
1401
|
+
reason
|
|
1402
|
+
});
|
|
1403
|
+
conn.send(this.codec.toBuffer(responseMsg2));
|
|
1404
|
+
log?.warn(
|
|
1405
|
+
`received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`,
|
|
1406
|
+
{ clientId: this.clientId, connId: conn.id }
|
|
1407
|
+
);
|
|
1408
|
+
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
const oldSession = this.sessions.get(parsed.from);
|
|
1412
|
+
const parsedMetadata = await this.validateHandshakeMetadata(
|
|
1413
|
+
conn,
|
|
1414
|
+
oldSession,
|
|
1415
|
+
parsed.payload.metadata,
|
|
1416
|
+
parsed.from
|
|
1434
1417
|
);
|
|
1418
|
+
if (parsedMetadata === false) {
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
const { session, isReconnect } = this.getOrCreateSession(
|
|
1422
|
+
parsed.from,
|
|
1423
|
+
conn,
|
|
1424
|
+
parsed.payload.sessionId,
|
|
1425
|
+
parsed.tracing
|
|
1426
|
+
);
|
|
1427
|
+
this.sessionHandshakeMetadata.set(session, parsedMetadata);
|
|
1428
|
+
log?.debug(
|
|
1429
|
+
`handshake from ${parsed.from} ok, responding with handshake success`,
|
|
1430
|
+
{ clientId: this.clientId, connId: conn.id }
|
|
1431
|
+
);
|
|
1432
|
+
const responseMsg = handshakeResponseMessage(this.clientId, parsed.from, {
|
|
1433
|
+
ok: true,
|
|
1434
|
+
sessionId: session.id
|
|
1435
|
+
});
|
|
1436
|
+
conn.send(this.codec.toBuffer(responseMsg));
|
|
1437
|
+
this.onConnect(conn, parsed.from, session, isReconnect);
|
|
1438
|
+
return session;
|
|
1435
1439
|
}
|
|
1436
1440
|
};
|
|
1437
1441
|
// Annotate the CommonJS export names for ESM import in node:
|