@replit/river 0.16.1 → 0.17.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 +25 -11
- package/dist/{chunk-Q6WPGM3K.js → chunk-7IQO434V.js} +11 -3
- package/dist/{chunk-7SPCAA6Q.js → chunk-LQMPJI3S.js} +101 -97
- package/dist/{chunk-GFRAOY75.js → chunk-VH3NGOXQ.js} +8 -17
- package/dist/{chunk-L65XWBX2.js → chunk-VJRLJ3JU.js} +1 -1
- package/dist/{chunk-XLJGKNV2.js → chunk-Y6DLSCKU.js} +1 -1
- package/dist/{connection-94896f3b.d.ts → connection-0767dc6b.d.ts} +1 -1
- package/dist/{connection-99346822.d.ts → connection-f31edbcd.d.ts} +1 -1
- package/dist/{index-2e402bb8.d.ts → index-8df0bdfb.d.ts} +27 -31
- package/dist/{procedures-f0226890.d.ts → procedures-b5ddb54d.d.ts} +1 -1
- package/dist/router/index.cjs +12 -4
- package/dist/router/index.d.cts +8 -4
- package/dist/router/index.d.ts +8 -4
- package/dist/router/index.js +2 -2
- package/dist/transport/impls/uds/client.cjs +80 -69
- package/dist/transport/impls/uds/client.d.cts +2 -2
- package/dist/transport/impls/uds/client.d.ts +2 -2
- package/dist/transport/impls/uds/client.js +3 -3
- package/dist/transport/impls/uds/server.cjs +82 -94
- package/dist/transport/impls/uds/server.d.cts +2 -2
- package/dist/transport/impls/uds/server.d.ts +2 -2
- package/dist/transport/impls/uds/server.js +3 -3
- package/dist/transport/impls/ws/client.cjs +80 -69
- package/dist/transport/impls/ws/client.d.cts +2 -2
- package/dist/transport/impls/ws/client.d.ts +2 -2
- package/dist/transport/impls/ws/client.js +3 -3
- package/dist/transport/impls/ws/server.cjs +82 -94
- package/dist/transport/impls/ws/server.d.cts +2 -2
- package/dist/transport/impls/ws/server.d.ts +2 -2
- package/dist/transport/impls/ws/server.js +3 -3
- package/dist/transport/index.cjs +107 -115
- package/dist/transport/index.d.cts +1 -1
- package/dist/transport/index.d.ts +1 -1
- package/dist/transport/index.js +2 -2
- package/dist/util/testHelpers.cjs +45 -18
- package/dist/util/testHelpers.d.cts +4 -9
- package/dist/util/testHelpers.d.ts +4 -9
- package/dist/util/testHelpers.js +8 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,32 +22,46 @@ See [PROTOCOL.md](./PROTOCOL.md) for more information on the protocol.
|
|
|
22
22
|
|
|
23
23
|
Before proceeding, ensure you have TypeScript 5 installed and configured appropriately:
|
|
24
24
|
|
|
25
|
-
1. **Ensure
|
|
25
|
+
1. **Ensure your `tsconfig.json` is configured correctly**:
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
You must verify that:
|
|
28
|
+
|
|
29
|
+
- `compilerOptions.moduleResolution` is set to `"bundler"`
|
|
30
|
+
- `compilerOptions.strictFunctionTypes` is set to `true`
|
|
31
|
+
- `compilerOptions.strictNullChecks` is set to `true`
|
|
32
|
+
|
|
33
|
+
or, preferably, that:
|
|
34
|
+
|
|
35
|
+
- `compilerOptions.moduleResolution` is set to `"bundler"`
|
|
36
|
+
- `compilerOptions.strict` is set to `true`
|
|
37
|
+
|
|
38
|
+
Like so:
|
|
39
|
+
|
|
40
|
+
```jsonc
|
|
28
41
|
{
|
|
29
42
|
"compilerOptions": {
|
|
30
|
-
"moduleResolution": "bundler"
|
|
43
|
+
"moduleResolution": "bundler",
|
|
44
|
+
"strict": true
|
|
31
45
|
// Other compiler options...
|
|
32
46
|
}
|
|
33
47
|
}
|
|
34
48
|
```
|
|
35
49
|
|
|
36
|
-
If
|
|
50
|
+
If these options already exist in your `tsconfig.json` and don't match what is shown above, modify them. River is designed for `"strict": true`, but technically only `strictFunctionTypes` and `strictNullChecks` being set to `true` is required. Failing to set these will cause unresolvable type errors when defining services.
|
|
37
51
|
|
|
38
52
|
2. Install River and Dependencies:
|
|
39
53
|
|
|
40
|
-
To use River, install the required packages using npm:
|
|
54
|
+
To use River, install the required packages using npm:
|
|
41
55
|
|
|
42
|
-
```bash
|
|
43
|
-
|
|
44
|
-
```
|
|
56
|
+
```bash
|
|
57
|
+
npm i @replit/river @sinclair/typebox
|
|
58
|
+
```
|
|
45
59
|
|
|
46
60
|
3. If you plan on using WebSocket for the underlying transport, also install
|
|
47
61
|
|
|
48
|
-
```bash
|
|
49
|
-
npm i ws isomorphic-ws
|
|
50
|
-
```
|
|
62
|
+
```bash
|
|
63
|
+
npm i ws isomorphic-ws
|
|
64
|
+
```
|
|
51
65
|
|
|
52
66
|
## Writing services
|
|
53
67
|
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
coerceErrorString,
|
|
4
4
|
isStreamClose,
|
|
5
5
|
isStreamOpen
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VH3NGOXQ.js";
|
|
7
7
|
import {
|
|
8
8
|
log
|
|
9
9
|
} from "./chunk-H4BYJELI.js";
|
|
@@ -570,8 +570,13 @@ function _createRecursiveProxy(callback, path) {
|
|
|
570
570
|
});
|
|
571
571
|
return proxy;
|
|
572
572
|
}
|
|
573
|
-
var
|
|
574
|
-
|
|
573
|
+
var defaultClientOptions = {
|
|
574
|
+
connectOnInvoke: true,
|
|
575
|
+
eagerlyConnect: true
|
|
576
|
+
};
|
|
577
|
+
var createClient = (transport, serverId, providedClientOptions = {}) => {
|
|
578
|
+
const options = { ...defaultClientOptions, ...providedClientOptions };
|
|
579
|
+
if (options.eagerlyConnect) {
|
|
575
580
|
void transport.connect(serverId);
|
|
576
581
|
}
|
|
577
582
|
return _createRecursiveProxy(async (opts) => {
|
|
@@ -587,6 +592,9 @@ var createClient = (transport, serverId, eagerlyConnect = true) => {
|
|
|
587
592
|
input
|
|
588
593
|
)}`
|
|
589
594
|
);
|
|
595
|
+
if (options.connectOnInvoke && !transport.connections.has(serverId)) {
|
|
596
|
+
void transport.connect(serverId);
|
|
597
|
+
}
|
|
590
598
|
if (procType === "rpc") {
|
|
591
599
|
return handleRpc(
|
|
592
600
|
transport,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
handshakeRequestMessage,
|
|
8
8
|
handshakeResponseMessage,
|
|
9
9
|
isAck
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-VH3NGOXQ.js";
|
|
11
11
|
import {
|
|
12
12
|
log
|
|
13
13
|
} from "./chunk-H4BYJELI.js";
|
|
@@ -19,7 +19,8 @@ import {
|
|
|
19
19
|
var ProtocolError = {
|
|
20
20
|
RetriesExceeded: "conn_retry_exceeded",
|
|
21
21
|
HandshakeFailed: "handshake_failed",
|
|
22
|
-
UseAfterDestroy: "use_after_destroy"
|
|
22
|
+
UseAfterDestroy: "use_after_destroy",
|
|
23
|
+
MessageOrderingViolated: "message_ordering_violated"
|
|
23
24
|
};
|
|
24
25
|
var EventDispatcher = class {
|
|
25
26
|
eventListeners = {};
|
|
@@ -74,7 +75,12 @@ var Session = class {
|
|
|
74
75
|
/**
|
|
75
76
|
* The unique ID of this session.
|
|
76
77
|
*/
|
|
77
|
-
|
|
78
|
+
id;
|
|
79
|
+
/**
|
|
80
|
+
* What the other side advertised as their session ID
|
|
81
|
+
* for this session.
|
|
82
|
+
*/
|
|
83
|
+
advertisedSessionId;
|
|
78
84
|
/**
|
|
79
85
|
* Number of messages we've sent along this session (excluding handshake and acks)
|
|
80
86
|
*/
|
|
@@ -96,11 +102,11 @@ var Session = class {
|
|
|
96
102
|
* The interval for sending heartbeats.
|
|
97
103
|
*/
|
|
98
104
|
heartbeat;
|
|
99
|
-
constructor(
|
|
105
|
+
constructor(conn, from, to, options) {
|
|
106
|
+
this.id = `session-${nanoid(12)}`;
|
|
100
107
|
this.options = options;
|
|
101
|
-
this.debugId = `sess-${unsafeId()}`;
|
|
102
108
|
this.from = from;
|
|
103
|
-
this.to =
|
|
109
|
+
this.to = to;
|
|
104
110
|
this.connection = conn;
|
|
105
111
|
this.codec = options.codec;
|
|
106
112
|
this.heartbeatMisses = 0;
|
|
@@ -140,7 +146,7 @@ var Session = class {
|
|
|
140
146
|
log?.info(
|
|
141
147
|
`${this.from} -- closing connection (id: ${this.connection.debugId}) to ${this.to} due to inactivity`
|
|
142
148
|
);
|
|
143
|
-
this.closeStaleConnection(
|
|
149
|
+
this.closeStaleConnection();
|
|
144
150
|
}
|
|
145
151
|
return;
|
|
146
152
|
}
|
|
@@ -171,33 +177,37 @@ var Session = class {
|
|
|
171
177
|
log?.debug(`${this.from} -- resending ${msg.id} (seq: ${msg.seq})`);
|
|
172
178
|
const ok = this.connection.send(this.codec.toBuffer(msg));
|
|
173
179
|
if (!ok) {
|
|
174
|
-
const msg2 = `${this.from} -- failed to send buffered message to ${this.to} in session (id: ${this.
|
|
180
|
+
const msg2 = `${this.from} -- failed to send buffered message to ${this.to} in session (id: ${this.id}) (if you hit this code path something is seriously wrong)`;
|
|
175
181
|
log?.error(msg2);
|
|
176
182
|
throw new Error(msg2);
|
|
177
183
|
}
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
186
|
updateBookkeeping(ack, seq) {
|
|
187
|
+
if (seq + 1 < this.ack) {
|
|
188
|
+
log?.error(`${this.from} -- received stale seq ${seq} + 1 < ${this.ack}`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
181
191
|
this.sendBuffer = this.sendBuffer.filter((unacked) => unacked.seq > ack);
|
|
182
192
|
this.ack = seq + 1;
|
|
183
193
|
}
|
|
184
194
|
closeStaleConnection(conn) {
|
|
185
|
-
if (
|
|
195
|
+
if (this.connection === void 0 || this.connection === conn)
|
|
186
196
|
return;
|
|
187
197
|
log?.info(
|
|
188
|
-
`${this.from} -- closing old inner connection (id: ${this.connection.debugId}) from session (id: ${this.
|
|
198
|
+
`${this.from} -- closing old inner connection (id: ${this.connection.debugId}) from session (id: ${this.id}) to ${this.to}`
|
|
189
199
|
);
|
|
190
200
|
this.connection.close();
|
|
191
201
|
this.connection = void 0;
|
|
192
202
|
}
|
|
193
203
|
replaceWithNewConnection(newConn) {
|
|
194
|
-
this.closeStaleConnection(
|
|
204
|
+
this.closeStaleConnection(newConn);
|
|
195
205
|
this.cancelGrace();
|
|
196
206
|
this.connection = newConn;
|
|
197
207
|
}
|
|
198
208
|
beginGrace(cb) {
|
|
199
209
|
log?.info(
|
|
200
|
-
`${this.from} -- starting ${this.options.sessionDisconnectGraceMs}ms grace period until session (id: ${this.
|
|
210
|
+
`${this.from} -- starting ${this.options.sessionDisconnectGraceMs}ms grace period until session (id: ${this.id}) to ${this.to} is closed`
|
|
201
211
|
);
|
|
202
212
|
this.disconnectionGrace = setTimeout(() => {
|
|
203
213
|
this.close();
|
|
@@ -208,11 +218,12 @@ var Session = class {
|
|
|
208
218
|
cancelGrace() {
|
|
209
219
|
this.heartbeatMisses = 0;
|
|
210
220
|
clearTimeout(this.disconnectionGrace);
|
|
221
|
+
this.disconnectionGrace = void 0;
|
|
211
222
|
}
|
|
212
223
|
// closed when we want to discard the whole session
|
|
213
224
|
// (i.e. shutdown or session disconnect)
|
|
214
225
|
close() {
|
|
215
|
-
this.closeStaleConnection(
|
|
226
|
+
this.closeStaleConnection();
|
|
216
227
|
this.cancelGrace();
|
|
217
228
|
this.resetBufferedMessages();
|
|
218
229
|
clearInterval(this.heartbeat);
|
|
@@ -243,7 +254,6 @@ var Session = class {
|
|
|
243
254
|
|
|
244
255
|
// transport/transport.ts
|
|
245
256
|
import { Value } from "@sinclair/typebox/value";
|
|
246
|
-
import { nanoid as nanoid2 } from "nanoid";
|
|
247
257
|
|
|
248
258
|
// transport/rateLimit.ts
|
|
249
259
|
var LeakyBucketRateLimit = class {
|
|
@@ -331,17 +341,10 @@ var defaultConnectionRetryOptions = {
|
|
|
331
341
|
budgetRestoreIntervalMs: 200
|
|
332
342
|
};
|
|
333
343
|
var defaultClientTransportOptions = {
|
|
334
|
-
|
|
335
|
-
...
|
|
344
|
+
...defaultTransportOptions,
|
|
345
|
+
...defaultConnectionRetryOptions
|
|
336
346
|
};
|
|
337
347
|
var Transport = class {
|
|
338
|
-
/**
|
|
339
|
-
* Unique per instance of the transport.
|
|
340
|
-
* This allows us to distinguish reconnects to different
|
|
341
|
-
* transports.
|
|
342
|
-
*/
|
|
343
|
-
instanceId = nanoid2();
|
|
344
|
-
connectedInstanceIds = /* @__PURE__ */ new Map();
|
|
345
348
|
/**
|
|
346
349
|
* A flag indicating whether the transport has been destroyed.
|
|
347
350
|
* A destroyed transport will not attempt to reconnect and cannot be used again.
|
|
@@ -394,41 +397,41 @@ var Transport = class {
|
|
|
394
397
|
* and we know the identity of the connected client.
|
|
395
398
|
* @param conn The connection object.
|
|
396
399
|
*/
|
|
397
|
-
onConnect(conn, connectedTo,
|
|
400
|
+
onConnect(conn, connectedTo, advertisedSessionId) {
|
|
398
401
|
this.eventDispatcher.dispatchEvent("connectionStatus", {
|
|
399
402
|
status: "connect",
|
|
400
403
|
conn
|
|
401
404
|
});
|
|
402
405
|
let oldSession = this.sessions.get(connectedTo);
|
|
403
|
-
|
|
404
|
-
if (oldSession && lastInstanceId !== void 0 && lastInstanceId !== instanceId) {
|
|
406
|
+
if (oldSession?.advertisedSessionId && oldSession.advertisedSessionId !== advertisedSessionId) {
|
|
405
407
|
log?.warn(
|
|
406
|
-
`${this.clientId} -- connection from ${connectedTo} is a different
|
|
408
|
+
`${this.clientId} -- connection from ${connectedTo} is a different session (id: ${advertisedSessionId}, last connected to: ${oldSession.advertisedSessionId}), starting a new session`
|
|
407
409
|
);
|
|
408
410
|
oldSession.close();
|
|
409
411
|
this.deleteSession(oldSession);
|
|
410
412
|
oldSession = void 0;
|
|
411
413
|
}
|
|
412
|
-
this.connectedInstanceIds.set(connectedTo, instanceId);
|
|
413
414
|
if (oldSession === void 0) {
|
|
414
415
|
const newSession = this.createSession(connectedTo, conn);
|
|
416
|
+
newSession.advertisedSessionId = advertisedSessionId;
|
|
415
417
|
log?.info(
|
|
416
|
-
`${this.clientId} -- new connection (id: ${conn.debugId}) for new session (id: ${newSession.
|
|
418
|
+
`${this.clientId} -- new connection (id: ${conn.debugId}) for new session (id: ${newSession.id}) to ${connectedTo}`
|
|
417
419
|
);
|
|
418
420
|
return newSession;
|
|
419
421
|
}
|
|
420
422
|
log?.info(
|
|
421
|
-
`${this.clientId} -- new connection (id: ${conn.debugId}) for existing session (id: ${oldSession.
|
|
423
|
+
`${this.clientId} -- new connection (id: ${conn.debugId}) for existing session (id: ${oldSession.id}) to ${connectedTo}`
|
|
422
424
|
);
|
|
423
425
|
oldSession.replaceWithNewConnection(conn);
|
|
424
426
|
oldSession.sendBufferedMessages();
|
|
427
|
+
oldSession.advertisedSessionId = advertisedSessionId;
|
|
425
428
|
return oldSession;
|
|
426
429
|
}
|
|
427
|
-
createSession(
|
|
430
|
+
createSession(to, conn) {
|
|
428
431
|
const session = new Session(
|
|
429
|
-
this.clientId,
|
|
430
|
-
connectedTo,
|
|
431
432
|
conn,
|
|
433
|
+
this.clientId,
|
|
434
|
+
to,
|
|
432
435
|
this.options
|
|
433
436
|
);
|
|
434
437
|
this.sessions.set(session.to, session);
|
|
@@ -438,10 +441,20 @@ var Transport = class {
|
|
|
438
441
|
});
|
|
439
442
|
return session;
|
|
440
443
|
}
|
|
444
|
+
getOrCreateSession(to, conn) {
|
|
445
|
+
let session = this.sessions.get(to);
|
|
446
|
+
if (!session) {
|
|
447
|
+
session = this.createSession(to, conn);
|
|
448
|
+
log?.info(
|
|
449
|
+
`${this.clientId} -- no session for ${to}, created a new one (id: ${session.id})`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
return session;
|
|
453
|
+
}
|
|
441
454
|
deleteSession(session) {
|
|
442
455
|
this.sessions.delete(session.to);
|
|
443
456
|
log?.info(
|
|
444
|
-
`${this.clientId} -- session ${session.
|
|
457
|
+
`${this.clientId} -- session ${session.id} disconnect from ${session.to}`
|
|
445
458
|
);
|
|
446
459
|
this.eventDispatcher.dispatchEvent("sessionStatus", {
|
|
447
460
|
status: "disconnect",
|
|
@@ -509,11 +522,14 @@ var Transport = class {
|
|
|
509
522
|
)}`
|
|
510
523
|
);
|
|
511
524
|
} else {
|
|
525
|
+
const errMsg = `received out-of-order msg (got seq: ${msg.seq}, wanted seq: ${session.nextExpectedSeq})`;
|
|
512
526
|
log?.error(
|
|
513
|
-
`${this.clientId} --
|
|
527
|
+
`${this.clientId} -- fatal: ${errMsg}, marking connection as dead: ${JSON.stringify(
|
|
528
|
+
msg
|
|
529
|
+
)}`
|
|
514
530
|
);
|
|
531
|
+
this.protocolError(ProtocolError.MessageOrderingViolated, errMsg);
|
|
515
532
|
session.close();
|
|
516
|
-
this.deleteSession(session);
|
|
517
533
|
}
|
|
518
534
|
return;
|
|
519
535
|
}
|
|
@@ -560,14 +576,7 @@ var Transport = class {
|
|
|
560
576
|
);
|
|
561
577
|
return void 0;
|
|
562
578
|
}
|
|
563
|
-
|
|
564
|
-
if (!session) {
|
|
565
|
-
session = this.createSession(to, void 0);
|
|
566
|
-
log?.info(
|
|
567
|
-
`${this.clientId} -- no session for ${to}, created a new one (id: ${session.debugId})`
|
|
568
|
-
);
|
|
569
|
-
}
|
|
570
|
-
return session.send(msg);
|
|
579
|
+
return this.getOrCreateSession(to).send(msg);
|
|
571
580
|
}
|
|
572
581
|
// control helpers
|
|
573
582
|
sendCloseStream(to, streamId) {
|
|
@@ -619,7 +628,13 @@ var ClientTransport = class extends Transport {
|
|
|
619
628
|
*/
|
|
620
629
|
inflightConnectionPromises;
|
|
621
630
|
retryBudget;
|
|
622
|
-
|
|
631
|
+
/**
|
|
632
|
+
* A flag indicating whether the transport should automatically reconnect
|
|
633
|
+
* when a connection is dropped.
|
|
634
|
+
* Realistically, this should always be true for clients unless you are writing
|
|
635
|
+
* tests or a special case where you don't want to reconnect.
|
|
636
|
+
*/
|
|
637
|
+
reconnectOnConnectionDrop = true;
|
|
623
638
|
constructor(clientId, providedOptions) {
|
|
624
639
|
super(clientId, providedOptions);
|
|
625
640
|
this.options = {
|
|
@@ -627,21 +642,20 @@ var ClientTransport = class extends Transport {
|
|
|
627
642
|
...providedOptions
|
|
628
643
|
};
|
|
629
644
|
this.inflightConnectionPromises = /* @__PURE__ */ new Map();
|
|
630
|
-
this.retryBudget = new LeakyBucketRateLimit(
|
|
631
|
-
this.options.connectionRetryOptions
|
|
632
|
-
);
|
|
645
|
+
this.retryBudget = new LeakyBucketRateLimit(this.options);
|
|
633
646
|
}
|
|
634
647
|
handleConnection(conn, to) {
|
|
635
648
|
if (this.state !== "open")
|
|
636
649
|
return;
|
|
637
650
|
let session = void 0;
|
|
638
651
|
const handshakeHandler = (data) => {
|
|
639
|
-
const
|
|
640
|
-
if (!
|
|
652
|
+
const maybeSession = this.receiveHandshakeResponseMessage(data, conn);
|
|
653
|
+
if (!maybeSession) {
|
|
641
654
|
conn.close();
|
|
642
655
|
return;
|
|
656
|
+
} else {
|
|
657
|
+
session = maybeSession;
|
|
643
658
|
}
|
|
644
|
-
session = this.onConnect(conn, handshake.from, handshake.instanceId);
|
|
645
659
|
conn.removeDataListener(handshakeHandler);
|
|
646
660
|
conn.addDataListener((data2) => {
|
|
647
661
|
const parsed = this.parseMsg(data2);
|
|
@@ -661,7 +675,7 @@ var ClientTransport = class extends Transport {
|
|
|
661
675
|
`${this.clientId} -- connection (id: ${conn.debugId}) to ${to} disconnected`
|
|
662
676
|
);
|
|
663
677
|
this.inflightConnectionPromises.delete(to);
|
|
664
|
-
if (this.
|
|
678
|
+
if (this.reconnectOnConnectionDrop) {
|
|
665
679
|
void this.connect(to);
|
|
666
680
|
}
|
|
667
681
|
});
|
|
@@ -671,7 +685,7 @@ var ClientTransport = class extends Transport {
|
|
|
671
685
|
);
|
|
672
686
|
});
|
|
673
687
|
}
|
|
674
|
-
receiveHandshakeResponseMessage(data) {
|
|
688
|
+
receiveHandshakeResponseMessage(data, conn) {
|
|
675
689
|
const parsed = this.parseMsg(data);
|
|
676
690
|
if (!parsed) {
|
|
677
691
|
this.protocolError(
|
|
@@ -704,12 +718,14 @@ var ClientTransport = class extends Transport {
|
|
|
704
718
|
);
|
|
705
719
|
return false;
|
|
706
720
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
721
|
+
log?.debug(`${this.clientId} -- handshake from ${parsed.from} ok`);
|
|
722
|
+
const session = this.onConnect(
|
|
723
|
+
conn,
|
|
724
|
+
parsed.from,
|
|
725
|
+
parsed.payload.status.sessionId
|
|
710
726
|
);
|
|
711
727
|
this.retryBudget.startRestoringBudget(parsed.from);
|
|
712
|
-
return
|
|
728
|
+
return session;
|
|
713
729
|
}
|
|
714
730
|
/**
|
|
715
731
|
* Manually attempts to connect to a client.
|
|
@@ -767,7 +783,7 @@ var ClientTransport = class extends Transport {
|
|
|
767
783
|
} catch (error) {
|
|
768
784
|
this.inflightConnectionPromises.delete(to);
|
|
769
785
|
const errStr = coerceErrorString(error);
|
|
770
|
-
if (!this.
|
|
786
|
+
if (!this.reconnectOnConnectionDrop || !canProceedWithConnection()) {
|
|
771
787
|
log?.warn(`${this.clientId} -- connection to ${to} failed (${errStr})`);
|
|
772
788
|
} else {
|
|
773
789
|
log?.warn(
|
|
@@ -778,11 +794,8 @@ var ClientTransport = class extends Transport {
|
|
|
778
794
|
}
|
|
779
795
|
}
|
|
780
796
|
sendHandshake(to, conn) {
|
|
781
|
-
const
|
|
782
|
-
|
|
783
|
-
to,
|
|
784
|
-
this.instanceId
|
|
785
|
-
);
|
|
797
|
+
const session = this.getOrCreateSession(to, conn);
|
|
798
|
+
const requestMsg = handshakeRequestMessage(this.clientId, to, session.id);
|
|
786
799
|
log?.debug(`${this.clientId} -- sending handshake request to ${to}`);
|
|
787
800
|
conn.send(this.codec.toBuffer(requestMsg));
|
|
788
801
|
}
|
|
@@ -795,7 +808,7 @@ var ServerTransport = class extends Transport {
|
|
|
795
808
|
constructor(clientId, providedOptions) {
|
|
796
809
|
super(clientId, providedOptions);
|
|
797
810
|
log?.info(
|
|
798
|
-
`${this.clientId} -- initiated server transport (
|
|
811
|
+
`${this.clientId} -- initiated server transport (protocol: ${PROTOCOL_VERSION})`
|
|
799
812
|
);
|
|
800
813
|
}
|
|
801
814
|
handleConnection(conn) {
|
|
@@ -807,12 +820,13 @@ var ServerTransport = class extends Transport {
|
|
|
807
820
|
let session = void 0;
|
|
808
821
|
const client = () => session?.to ?? "unknown";
|
|
809
822
|
const handshakeHandler = (data) => {
|
|
810
|
-
const
|
|
811
|
-
if (!
|
|
823
|
+
const maybeSession = this.receiveHandshakeRequestMessage(data, conn);
|
|
824
|
+
if (!maybeSession) {
|
|
812
825
|
conn.close();
|
|
813
826
|
return;
|
|
827
|
+
} else {
|
|
828
|
+
session = maybeSession;
|
|
814
829
|
}
|
|
815
|
-
session = this.onConnect(conn, handshake.from, handshake.instanceId);
|
|
816
830
|
conn.removeDataListener(handshakeHandler);
|
|
817
831
|
conn.addDataListener((data2) => {
|
|
818
832
|
const parsed = this.parseMsg(data2);
|
|
@@ -850,18 +864,13 @@ var ServerTransport = class extends Transport {
|
|
|
850
864
|
return false;
|
|
851
865
|
}
|
|
852
866
|
if (!Value.Check(ControlMessageHandshakeRequestSchema, parsed.payload)) {
|
|
853
|
-
const
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
);
|
|
867
|
+
const reason = "received invalid handshake msg";
|
|
868
|
+
const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
|
|
869
|
+
ok: false,
|
|
870
|
+
reason
|
|
871
|
+
});
|
|
859
872
|
conn.send(this.codec.toBuffer(responseMsg2));
|
|
860
|
-
log?.warn(
|
|
861
|
-
`${this.clientId} -- received invalid handshake msg: ${JSON.stringify(
|
|
862
|
-
parsed
|
|
863
|
-
)}`
|
|
864
|
-
);
|
|
873
|
+
log?.warn(`${this.clientId} -- ${reason}: ${JSON.stringify(parsed)}`);
|
|
865
874
|
this.protocolError(
|
|
866
875
|
ProtocolError.HandshakeFailed,
|
|
867
876
|
"invalid handshake request"
|
|
@@ -870,34 +879,28 @@ var ServerTransport = class extends Transport {
|
|
|
870
879
|
}
|
|
871
880
|
const gotVersion = parsed.payload.protocolVersion;
|
|
872
881
|
if (gotVersion !== PROTOCOL_VERSION) {
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
);
|
|
882
|
+
const reason = `incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`;
|
|
883
|
+
const responseMsg2 = handshakeResponseMessage(this.clientId, parsed.from, {
|
|
884
|
+
ok: false,
|
|
885
|
+
reason
|
|
886
|
+
});
|
|
879
887
|
conn.send(this.codec.toBuffer(responseMsg2));
|
|
880
888
|
log?.warn(
|
|
881
889
|
`${this.clientId} -- received handshake msg with incompatible protocol version (got: ${gotVersion}, expected: ${PROTOCOL_VERSION})`
|
|
882
890
|
);
|
|
883
|
-
this.protocolError(
|
|
884
|
-
ProtocolError.HandshakeFailed,
|
|
885
|
-
`incorrect version (got: ${gotVersion} wanted ${PROTOCOL_VERSION})`
|
|
886
|
-
);
|
|
891
|
+
this.protocolError(ProtocolError.HandshakeFailed, reason);
|
|
887
892
|
return false;
|
|
888
893
|
}
|
|
889
|
-
const
|
|
894
|
+
const session = this.getOrCreateSession(parsed.from, conn);
|
|
890
895
|
log?.debug(
|
|
891
|
-
`${this.clientId} -- handshake from ${parsed.from} ok
|
|
892
|
-
);
|
|
893
|
-
const responseMsg = handshakeResponseMessage(
|
|
894
|
-
this.clientId,
|
|
895
|
-
this.instanceId,
|
|
896
|
-
parsed.from,
|
|
897
|
-
true
|
|
896
|
+
`${this.clientId} -- handshake from ${parsed.from} ok, responding with handshake success`
|
|
898
897
|
);
|
|
898
|
+
const responseMsg = handshakeResponseMessage(this.clientId, parsed.from, {
|
|
899
|
+
ok: true,
|
|
900
|
+
sessionId: session.id
|
|
901
|
+
});
|
|
899
902
|
conn.send(this.codec.toBuffer(responseMsg));
|
|
900
|
-
return
|
|
903
|
+
return this.onConnect(conn, parsed.from, parsed.payload.sessionId);
|
|
901
904
|
}
|
|
902
905
|
};
|
|
903
906
|
|
|
@@ -905,6 +908,7 @@ export {
|
|
|
905
908
|
ProtocolError,
|
|
906
909
|
Connection,
|
|
907
910
|
Session,
|
|
911
|
+
defaultTransportOptions,
|
|
908
912
|
Transport,
|
|
909
913
|
ClientTransport,
|
|
910
914
|
ServerTransport
|
|
@@ -19,18 +19,18 @@ var ControlMessageAckSchema = Type.Object({
|
|
|
19
19
|
var ControlMessageCloseSchema = Type.Object({
|
|
20
20
|
type: Type.Literal("CLOSE")
|
|
21
21
|
});
|
|
22
|
-
var PROTOCOL_VERSION = "v1";
|
|
22
|
+
var PROTOCOL_VERSION = "v1.1";
|
|
23
23
|
var ControlMessageHandshakeRequestSchema = Type.Object({
|
|
24
24
|
type: Type.Literal("HANDSHAKE_REQ"),
|
|
25
25
|
protocolVersion: Type.String(),
|
|
26
|
-
|
|
26
|
+
sessionId: Type.String()
|
|
27
27
|
});
|
|
28
28
|
var ControlMessageHandshakeResponseSchema = Type.Object({
|
|
29
29
|
type: Type.Literal("HANDSHAKE_RESP"),
|
|
30
30
|
status: Type.Union([
|
|
31
31
|
Type.Object({
|
|
32
32
|
ok: Type.Literal(true),
|
|
33
|
-
|
|
33
|
+
sessionId: Type.String()
|
|
34
34
|
}),
|
|
35
35
|
Type.Object({
|
|
36
36
|
ok: Type.Literal(false),
|
|
@@ -47,7 +47,7 @@ var ControlMessagePayloadSchema = Type.Union([
|
|
|
47
47
|
var OpaqueTransportMessageSchema = TransportMessageSchema(
|
|
48
48
|
Type.Unknown()
|
|
49
49
|
);
|
|
50
|
-
function handshakeRequestMessage(from, to,
|
|
50
|
+
function handshakeRequestMessage(from, to, sessionId) {
|
|
51
51
|
return {
|
|
52
52
|
id: nanoid(),
|
|
53
53
|
from,
|
|
@@ -59,11 +59,11 @@ function handshakeRequestMessage(from, to, instanceId) {
|
|
|
59
59
|
payload: {
|
|
60
60
|
type: "HANDSHAKE_REQ",
|
|
61
61
|
protocolVersion: PROTOCOL_VERSION,
|
|
62
|
-
|
|
62
|
+
sessionId
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
-
function handshakeResponseMessage(from,
|
|
66
|
+
function handshakeResponseMessage(from, to, status) {
|
|
67
67
|
return {
|
|
68
68
|
id: nanoid(),
|
|
69
69
|
from,
|
|
@@ -72,18 +72,9 @@ function handshakeResponseMessage(from, instanceId, to, ok, reason) {
|
|
|
72
72
|
ack: 0,
|
|
73
73
|
streamId: nanoid(),
|
|
74
74
|
controlFlags: 0,
|
|
75
|
-
payload:
|
|
76
|
-
type: "HANDSHAKE_RESP",
|
|
77
|
-
status: {
|
|
78
|
-
ok: true,
|
|
79
|
-
instanceId
|
|
80
|
-
}
|
|
81
|
-
} : {
|
|
75
|
+
payload: {
|
|
82
76
|
type: "HANDSHAKE_RESP",
|
|
83
|
-
status
|
|
84
|
-
ok: false,
|
|
85
|
-
reason: reason ?? "Unknown reason"
|
|
86
|
-
}
|
|
77
|
+
status
|
|
87
78
|
}
|
|
88
79
|
};
|
|
89
80
|
}
|