@replit/river 0.200.0-rc.9 → 0.200.2
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 +8 -8
- package/dist/{chunk-42Z2FQIU.js → chunk-6BH2CXVE.js} +21 -13
- package/dist/chunk-6BH2CXVE.js.map +1 -0
- package/dist/{chunk-4HT6P2ZG.js → chunk-A4JKES5A.js} +22 -30
- package/dist/chunk-A4JKES5A.js.map +1 -0
- package/dist/{chunk-4PVU7J25.js → chunk-AJGIY2UB.js} +1 -1
- package/dist/chunk-AJGIY2UB.js.map +1 -0
- package/dist/{chunk-EETL2L77.js → chunk-GJUUVID2.js} +14 -32
- package/dist/chunk-GJUUVID2.js.map +1 -0
- package/dist/{chunk-GR3AQKHL.js → chunk-HRKM7BIE.js} +14 -4
- package/dist/chunk-HRKM7BIE.js.map +1 -0
- package/dist/{chunk-ZXZE253M.js → chunk-PJB2Y2AV.js} +24 -37
- package/dist/chunk-PJB2Y2AV.js.map +1 -0
- package/dist/{chunk-I75XYO5W.js → chunk-QIDEN5PP.js} +82 -20
- package/dist/chunk-QIDEN5PP.js.map +1 -0
- package/dist/{chunk-VXYHC666.js → chunk-YTMS7OP6.js} +1 -1
- package/dist/chunk-YTMS7OP6.js.map +1 -0
- package/dist/chunk-Z4PX66JO.js +307 -0
- package/dist/chunk-Z4PX66JO.js.map +1 -0
- package/dist/{client-22a47343.d.ts → client-9292552a.d.ts} +3 -4
- package/dist/codec/index.cjs.map +1 -1
- package/dist/codec/index.js +1 -1
- package/dist/connection-94dea547.d.ts +32 -0
- package/dist/{context-b4aff18f.d.ts → context-69f37ac1.d.ts} +48 -43
- 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/{message-7d135e38.d.ts → message-57bb8187.d.ts} +5 -3
- package/dist/router/index.cjs +649 -709
- package/dist/router/index.cjs.map +1 -1
- package/dist/router/index.d.cts +22 -12
- package/dist/router/index.d.ts +22 -12
- package/dist/router/index.js +502 -404
- package/dist/router/index.js.map +1 -1
- package/dist/{server-dd6a9853.d.ts → server-8fdd7fb2.d.ts} +5 -5
- package/dist/{services-1b5ac5bc.d.ts → services-259f39a3.d.ts} +191 -194
- package/dist/transport/impls/ws/client.cjs +129 -62
- 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 +7 -7
- package/dist/transport/impls/ws/client.js.map +1 -1
- package/dist/transport/impls/ws/server.cjs +146 -70
- package/dist/transport/impls/ws/server.cjs.map +1 -1
- package/dist/transport/impls/ws/server.d.cts +6 -5
- package/dist/transport/impls/ws/server.d.ts +6 -5
- package/dist/transport/impls/ws/server.js +21 -9
- package/dist/transport/impls/ws/server.js.map +1 -1
- package/dist/transport/index.cjs +138 -92
- 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 +7 -7
- package/dist/util/testHelpers.cjs +265 -327
- package/dist/util/testHelpers.cjs.map +1 -1
- package/dist/util/testHelpers.d.cts +36 -31
- package/dist/util/testHelpers.d.ts +36 -31
- package/dist/util/testHelpers.js +82 -52
- package/dist/util/testHelpers.js.map +1 -1
- package/package.json +4 -3
- package/dist/chunk-42Z2FQIU.js.map +0 -1
- package/dist/chunk-4HT6P2ZG.js.map +0 -1
- package/dist/chunk-4PVU7J25.js.map +0 -1
- package/dist/chunk-EETL2L77.js.map +0 -1
- package/dist/chunk-GR3AQKHL.js.map +0 -1
- package/dist/chunk-I75XYO5W.js.map +0 -1
- package/dist/chunk-MQ6ANR3H.js +0 -451
- package/dist/chunk-MQ6ANR3H.js.map +0 -1
- package/dist/chunk-VXYHC666.js.map +0 -1
- package/dist/chunk-ZXZE253M.js.map +0 -1
- package/dist/connection-260e45a8.d.ts +0 -11
package/dist/router/index.js
CHANGED
|
@@ -1,39 +1,49 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ABORT_CODE,
|
|
3
2
|
AnyResultSchema,
|
|
3
|
+
CANCEL_CODE,
|
|
4
4
|
Err,
|
|
5
5
|
ErrResultSchema,
|
|
6
|
-
INTERNAL_RIVER_ERROR_CODE,
|
|
7
6
|
INVALID_REQUEST_CODE,
|
|
8
7
|
Ok,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
RequestReaderErrorSchema,
|
|
12
|
-
ResponseReaderErrorSchema,
|
|
8
|
+
ReadableImpl,
|
|
9
|
+
ReaderErrorSchema,
|
|
13
10
|
UNCAUGHT_ERROR_CODE,
|
|
14
11
|
UNEXPECTED_DISCONNECT_CODE,
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
WritableImpl,
|
|
13
|
+
flattenErrorType,
|
|
14
|
+
unwrapOrThrow
|
|
15
|
+
} from "../chunk-Z4PX66JO.js";
|
|
17
16
|
import {
|
|
18
17
|
ControlMessageCloseSchema,
|
|
19
18
|
ControlMessagePayloadSchema,
|
|
20
|
-
|
|
19
|
+
cancelMessage,
|
|
21
20
|
closeStreamMessage,
|
|
22
21
|
coerceErrorString,
|
|
23
22
|
createHandlerSpan,
|
|
24
23
|
createProcTelemetryInfo,
|
|
25
24
|
generateId,
|
|
26
25
|
getPropagationContext,
|
|
27
|
-
|
|
26
|
+
isStreamCancel,
|
|
28
27
|
isStreamClose,
|
|
29
|
-
isStreamCloseRequest,
|
|
30
28
|
isStreamOpen,
|
|
31
|
-
requestCloseStreamMessage,
|
|
32
29
|
version
|
|
33
|
-
} from "../chunk-
|
|
30
|
+
} from "../chunk-GJUUVID2.js";
|
|
34
31
|
|
|
35
32
|
// router/services.ts
|
|
36
|
-
import { Type } from "@sinclair/typebox";
|
|
33
|
+
import { Type, Kind } from "@sinclair/typebox";
|
|
34
|
+
function serializeSchemaV1Compat(services, handshakeSchema) {
|
|
35
|
+
const serializedServiceObject = Object.entries(services).reduce((acc, [name, value]) => {
|
|
36
|
+
acc[name] = value.serializeV1Compat();
|
|
37
|
+
return acc;
|
|
38
|
+
}, {});
|
|
39
|
+
const schema = {
|
|
40
|
+
services: serializedServiceObject
|
|
41
|
+
};
|
|
42
|
+
if (handshakeSchema) {
|
|
43
|
+
schema.handshakeSchema = Type.Strict(handshakeSchema);
|
|
44
|
+
}
|
|
45
|
+
return schema;
|
|
46
|
+
}
|
|
37
47
|
function serializeSchema(services, handshakeSchema) {
|
|
38
48
|
const serializedServiceObject = Object.entries(services).reduce((acc, [name, value]) => {
|
|
39
49
|
acc[name] = value.serialize();
|
|
@@ -148,12 +158,9 @@ var ServiceSchema = class _ServiceSchema {
|
|
|
148
158
|
{
|
|
149
159
|
init: Type.Strict(procDef.requestInit),
|
|
150
160
|
output: Type.Strict(procDef.responseData),
|
|
161
|
+
errors: getSerializedProcErrors(procDef),
|
|
151
162
|
// Only add `description` field if the type declares it.
|
|
152
163
|
..."description" in procDef ? { description: procDef.description } : {},
|
|
153
|
-
// Only add the `errors` field if the type declares it.
|
|
154
|
-
..."responseError" in procDef ? {
|
|
155
|
-
errors: Type.Strict(procDef.responseError)
|
|
156
|
-
} : {},
|
|
157
164
|
type: procDef.type,
|
|
158
165
|
// Only add the `input` field if the type declares it.
|
|
159
166
|
..."requestData" in procDef ? {
|
|
@@ -170,7 +177,7 @@ var ServiceSchema = class _ServiceSchema {
|
|
|
170
177
|
* protocol v1. This is useful to be able to continue to generate schemas for older
|
|
171
178
|
* clients as they are still supported.
|
|
172
179
|
*/
|
|
173
|
-
|
|
180
|
+
serializeV1Compat() {
|
|
174
181
|
return {
|
|
175
182
|
procedures: Object.fromEntries(
|
|
176
183
|
Object.entries(this.procedures).map(
|
|
@@ -183,12 +190,9 @@ var ServiceSchema = class _ServiceSchema {
|
|
|
183
190
|
// this is the only change needed to make it compatible.
|
|
184
191
|
input: Type.Strict(procDef.requestInit),
|
|
185
192
|
output: Type.Strict(procDef.responseData),
|
|
193
|
+
errors: getSerializedProcErrors(procDef),
|
|
186
194
|
// Only add `description` field if the type declares it.
|
|
187
195
|
..."description" in procDef ? { description: procDef.description } : {},
|
|
188
|
-
// Only add the `errors` field if the type declares it.
|
|
189
|
-
..."responseError" in procDef ? {
|
|
190
|
-
errors: Type.Strict(procDef.responseError)
|
|
191
|
-
} : {},
|
|
192
196
|
type: procDef.type
|
|
193
197
|
}
|
|
194
198
|
];
|
|
@@ -198,12 +202,9 @@ var ServiceSchema = class _ServiceSchema {
|
|
|
198
202
|
{
|
|
199
203
|
init: Type.Strict(procDef.requestInit),
|
|
200
204
|
output: Type.Strict(procDef.responseData),
|
|
205
|
+
errors: getSerializedProcErrors(procDef),
|
|
201
206
|
// Only add `description` field if the type declares it.
|
|
202
207
|
..."description" in procDef ? { description: procDef.description } : {},
|
|
203
|
-
// Only add the `errors` field if the type declares it.
|
|
204
|
-
..."responseError" in procDef ? {
|
|
205
|
-
errors: Type.Strict(procDef.responseError)
|
|
206
|
-
} : {},
|
|
207
208
|
type: procDef.type,
|
|
208
209
|
input: Type.Strict(procDef.requestData)
|
|
209
210
|
}
|
|
@@ -226,6 +227,15 @@ var ServiceSchema = class _ServiceSchema {
|
|
|
226
227
|
});
|
|
227
228
|
}
|
|
228
229
|
};
|
|
230
|
+
function getSerializedProcErrors(procDef) {
|
|
231
|
+
if (!("responseError" in procDef) || procDef.responseError[Kind] === "Never") {
|
|
232
|
+
return Type.Strict(ReaderErrorSchema);
|
|
233
|
+
}
|
|
234
|
+
const withProtocolErrors = flattenErrorType(
|
|
235
|
+
Type.Union([procDef.responseError, ReaderErrorSchema])
|
|
236
|
+
);
|
|
237
|
+
return Type.Strict(withProtocolErrors);
|
|
238
|
+
}
|
|
229
239
|
var ServiceScaffold = class {
|
|
230
240
|
/**
|
|
231
241
|
* The configuration for this service.
|
|
@@ -283,9 +293,86 @@ var ServiceScaffold = class {
|
|
|
283
293
|
}
|
|
284
294
|
};
|
|
285
295
|
|
|
296
|
+
// router/procedures.ts
|
|
297
|
+
import { Type as Type2 } from "@sinclair/typebox";
|
|
298
|
+
function rpc({
|
|
299
|
+
requestInit,
|
|
300
|
+
responseData,
|
|
301
|
+
responseError = Type2.Never(),
|
|
302
|
+
description,
|
|
303
|
+
handler
|
|
304
|
+
}) {
|
|
305
|
+
return {
|
|
306
|
+
...description ? { description } : {},
|
|
307
|
+
type: "rpc",
|
|
308
|
+
requestInit,
|
|
309
|
+
responseData,
|
|
310
|
+
responseError,
|
|
311
|
+
handler
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function upload({
|
|
315
|
+
requestInit,
|
|
316
|
+
requestData,
|
|
317
|
+
responseData,
|
|
318
|
+
responseError = Type2.Never(),
|
|
319
|
+
description,
|
|
320
|
+
handler
|
|
321
|
+
}) {
|
|
322
|
+
return {
|
|
323
|
+
type: "upload",
|
|
324
|
+
...description ? { description } : {},
|
|
325
|
+
requestInit,
|
|
326
|
+
requestData,
|
|
327
|
+
responseData,
|
|
328
|
+
responseError,
|
|
329
|
+
handler
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function subscription({
|
|
333
|
+
requestInit,
|
|
334
|
+
responseData,
|
|
335
|
+
responseError = Type2.Never(),
|
|
336
|
+
description,
|
|
337
|
+
handler
|
|
338
|
+
}) {
|
|
339
|
+
return {
|
|
340
|
+
type: "subscription",
|
|
341
|
+
...description ? { description } : {},
|
|
342
|
+
requestInit,
|
|
343
|
+
responseData,
|
|
344
|
+
responseError,
|
|
345
|
+
handler
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function stream({
|
|
349
|
+
requestInit,
|
|
350
|
+
requestData,
|
|
351
|
+
responseData,
|
|
352
|
+
responseError = Type2.Never(),
|
|
353
|
+
description,
|
|
354
|
+
handler
|
|
355
|
+
}) {
|
|
356
|
+
return {
|
|
357
|
+
type: "stream",
|
|
358
|
+
...description ? { description } : {},
|
|
359
|
+
requestInit,
|
|
360
|
+
requestData,
|
|
361
|
+
responseData,
|
|
362
|
+
responseError,
|
|
363
|
+
handler
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
var Procedure = {
|
|
367
|
+
rpc,
|
|
368
|
+
upload,
|
|
369
|
+
subscription,
|
|
370
|
+
stream
|
|
371
|
+
};
|
|
372
|
+
|
|
286
373
|
// router/client.ts
|
|
287
374
|
import { Value } from "@sinclair/typebox/value";
|
|
288
|
-
var
|
|
375
|
+
var ReaderErrResultSchema = ErrResultSchema(ReaderErrorSchema);
|
|
289
376
|
var noop = () => {
|
|
290
377
|
};
|
|
291
378
|
function _createRecursiveProxy(callback, path) {
|
|
@@ -346,6 +433,11 @@ function createClient(transport, serverId, providedClientOptions = {}) {
|
|
|
346
433
|
}, []);
|
|
347
434
|
}
|
|
348
435
|
function handleProc(procType, transport, serverId, init, serviceName, procedureName, abortSignal) {
|
|
436
|
+
const session = transport.sessions.get(serverId) ?? transport.createUnconnectedSession(serverId);
|
|
437
|
+
const sessionScopedSend = transport.getSessionBoundSendFn(
|
|
438
|
+
serverId,
|
|
439
|
+
session.id
|
|
440
|
+
);
|
|
349
441
|
const procClosesWithInit = procType === "rpc" || procType === "subscription";
|
|
350
442
|
const streamId = generateId();
|
|
351
443
|
const { span, ctx } = createProcTelemetryInfo(
|
|
@@ -356,61 +448,61 @@ function handleProc(procType, transport, serverId, init, serviceName, procedureN
|
|
|
356
448
|
streamId
|
|
357
449
|
);
|
|
358
450
|
let cleanClose = true;
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
451
|
+
const reqWritable = new WritableImpl({
|
|
452
|
+
writeCb: (rawIn) => {
|
|
453
|
+
sessionScopedSend({
|
|
454
|
+
streamId,
|
|
455
|
+
payload: rawIn,
|
|
456
|
+
controlFlags: 0
|
|
457
|
+
});
|
|
458
|
+
},
|
|
459
|
+
// close callback
|
|
460
|
+
closeCb: () => {
|
|
461
|
+
span.addEvent("reqWritable closed");
|
|
462
|
+
if (!procClosesWithInit && cleanClose) {
|
|
463
|
+
sessionScopedSend(closeStreamMessage(streamId));
|
|
464
|
+
}
|
|
465
|
+
if (resReadable.isClosed()) {
|
|
466
|
+
cleanup();
|
|
467
|
+
}
|
|
374
468
|
}
|
|
375
469
|
});
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
if (reqWriter.isClosed()) {
|
|
470
|
+
const resReadable = new ReadableImpl();
|
|
471
|
+
const closeReadable = () => {
|
|
472
|
+
resReadable._triggerClose();
|
|
473
|
+
span.addEvent("resReadable closed");
|
|
474
|
+
if (reqWritable.isClosed()) {
|
|
382
475
|
cleanup();
|
|
383
476
|
}
|
|
384
|
-
}
|
|
477
|
+
};
|
|
385
478
|
function cleanup() {
|
|
386
479
|
transport.removeEventListener("message", onMessage);
|
|
387
480
|
transport.removeEventListener("sessionStatus", onSessionStatus);
|
|
388
|
-
abortSignal?.removeEventListener("abort",
|
|
481
|
+
abortSignal?.removeEventListener("abort", onClientCancel);
|
|
389
482
|
span.end();
|
|
390
483
|
}
|
|
391
|
-
function
|
|
392
|
-
if (
|
|
484
|
+
function onClientCancel() {
|
|
485
|
+
if (resReadable.isClosed() && reqWritable.isClosed()) {
|
|
393
486
|
return;
|
|
394
487
|
}
|
|
395
|
-
span.addEvent("sending
|
|
488
|
+
span.addEvent("sending cancel");
|
|
396
489
|
cleanClose = false;
|
|
397
|
-
if (!
|
|
398
|
-
|
|
490
|
+
if (!resReadable.isClosed()) {
|
|
491
|
+
resReadable._pushValue(
|
|
399
492
|
Err({
|
|
400
|
-
code:
|
|
401
|
-
message: "
|
|
493
|
+
code: CANCEL_CODE,
|
|
494
|
+
message: "cancelled by client"
|
|
402
495
|
})
|
|
403
496
|
);
|
|
404
|
-
|
|
497
|
+
closeReadable();
|
|
405
498
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
abortMessage(
|
|
499
|
+
reqWritable.close();
|
|
500
|
+
sessionScopedSend(
|
|
501
|
+
cancelMessage(
|
|
410
502
|
streamId,
|
|
411
503
|
Err({
|
|
412
|
-
code:
|
|
413
|
-
message: "
|
|
504
|
+
code: CANCEL_CODE,
|
|
505
|
+
message: "cancelled by client"
|
|
414
506
|
})
|
|
415
507
|
)
|
|
416
508
|
);
|
|
@@ -419,47 +511,44 @@ function handleProc(procType, transport, serverId, init, serviceName, procedureN
|
|
|
419
511
|
if (msg.streamId !== streamId)
|
|
420
512
|
return;
|
|
421
513
|
if (msg.to !== transport.clientId) {
|
|
422
|
-
transport.log?.error("
|
|
514
|
+
transport.log?.error("got stream message from unexpected client", {
|
|
423
515
|
clientId: transport.clientId,
|
|
424
516
|
transportMessage: msg
|
|
425
517
|
});
|
|
426
518
|
return;
|
|
427
519
|
}
|
|
428
|
-
if (
|
|
429
|
-
reqWriter.triggerCloseRequest();
|
|
430
|
-
}
|
|
431
|
-
if (isStreamAbort(msg.controlFlags)) {
|
|
520
|
+
if (isStreamCancel(msg.controlFlags)) {
|
|
432
521
|
cleanClose = false;
|
|
433
|
-
span.addEvent("received
|
|
434
|
-
let
|
|
435
|
-
if (Value.Check(
|
|
436
|
-
|
|
522
|
+
span.addEvent("received cancel");
|
|
523
|
+
let cancelResult;
|
|
524
|
+
if (Value.Check(ReaderErrResultSchema, msg.payload)) {
|
|
525
|
+
cancelResult = msg.payload;
|
|
437
526
|
} else {
|
|
438
|
-
|
|
439
|
-
code:
|
|
440
|
-
message: "
|
|
527
|
+
cancelResult = Err({
|
|
528
|
+
code: CANCEL_CODE,
|
|
529
|
+
message: "stream cancelled with invalid payload"
|
|
441
530
|
});
|
|
442
531
|
transport.log?.error(
|
|
443
|
-
"
|
|
532
|
+
"got stream cancel without a valid protocol error",
|
|
444
533
|
{
|
|
445
534
|
clientId: transport.clientId,
|
|
446
535
|
transportMessage: msg,
|
|
447
536
|
validationErrors: [
|
|
448
|
-
...Value.Errors(
|
|
537
|
+
...Value.Errors(ReaderErrResultSchema, msg.payload)
|
|
449
538
|
]
|
|
450
539
|
}
|
|
451
540
|
);
|
|
452
541
|
}
|
|
453
|
-
if (!
|
|
454
|
-
|
|
455
|
-
|
|
542
|
+
if (!resReadable.isClosed()) {
|
|
543
|
+
resReadable._pushValue(cancelResult);
|
|
544
|
+
closeReadable();
|
|
456
545
|
}
|
|
457
|
-
|
|
546
|
+
reqWritable.close();
|
|
458
547
|
return;
|
|
459
548
|
}
|
|
460
|
-
if (
|
|
461
|
-
span.recordException("
|
|
462
|
-
transport.log?.error("
|
|
549
|
+
if (resReadable.isClosed()) {
|
|
550
|
+
span.recordException("received message after response stream is closed");
|
|
551
|
+
transport.log?.error("received message after response stream is closed", {
|
|
463
552
|
clientId: transport.clientId,
|
|
464
553
|
transportMessage: msg
|
|
465
554
|
});
|
|
@@ -467,7 +556,7 @@ function handleProc(procType, transport, serverId, init, serviceName, procedureN
|
|
|
467
556
|
}
|
|
468
557
|
if (!Value.Check(ControlMessageCloseSchema, msg.payload)) {
|
|
469
558
|
if (Value.Check(AnyResultSchema, msg.payload)) {
|
|
470
|
-
|
|
559
|
+
resReadable._pushValue(msg.payload);
|
|
471
560
|
} else {
|
|
472
561
|
transport.log?.error(
|
|
473
562
|
"Got non-control payload, but was not a valid result",
|
|
@@ -480,33 +569,36 @@ function handleProc(procType, transport, serverId, init, serviceName, procedureN
|
|
|
480
569
|
}
|
|
481
570
|
}
|
|
482
571
|
if (isStreamClose(msg.controlFlags)) {
|
|
483
|
-
span.addEvent("received
|
|
484
|
-
|
|
572
|
+
span.addEvent("received response close");
|
|
573
|
+
if (resReadable.isClosed()) {
|
|
574
|
+
transport.log?.error(
|
|
575
|
+
"received stream close but readable was already closed"
|
|
576
|
+
);
|
|
577
|
+
} else {
|
|
578
|
+
closeReadable();
|
|
579
|
+
}
|
|
485
580
|
}
|
|
486
581
|
}
|
|
487
582
|
function onSessionStatus(evt) {
|
|
488
|
-
if (evt.status !== "disconnect") {
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
if (evt.session.to !== serverId) {
|
|
583
|
+
if (evt.status !== "disconnect" || evt.session.to !== serverId || session.id !== evt.session.id) {
|
|
492
584
|
return;
|
|
493
585
|
}
|
|
494
586
|
cleanClose = false;
|
|
495
|
-
if (!
|
|
496
|
-
|
|
587
|
+
if (!resReadable.isClosed()) {
|
|
588
|
+
resReadable._pushValue(
|
|
497
589
|
Err({
|
|
498
590
|
code: UNEXPECTED_DISCONNECT_CODE,
|
|
499
591
|
message: `${serverId} unexpectedly disconnected`
|
|
500
592
|
})
|
|
501
593
|
);
|
|
594
|
+
closeReadable();
|
|
502
595
|
}
|
|
503
|
-
|
|
504
|
-
resReader.triggerClose();
|
|
596
|
+
reqWritable.close();
|
|
505
597
|
}
|
|
506
|
-
abortSignal?.addEventListener("abort",
|
|
598
|
+
abortSignal?.addEventListener("abort", onClientCancel);
|
|
507
599
|
transport.addEventListener("message", onMessage);
|
|
508
600
|
transport.addEventListener("sessionStatus", onSessionStatus);
|
|
509
|
-
|
|
601
|
+
sessionScopedSend({
|
|
510
602
|
streamId,
|
|
511
603
|
serviceName,
|
|
512
604
|
procedureName,
|
|
@@ -515,34 +607,39 @@ function handleProc(procType, transport, serverId, init, serviceName, procedureN
|
|
|
515
607
|
controlFlags: procClosesWithInit ? 2 /* StreamOpenBit */ | 8 /* StreamClosedBit */ : 2 /* StreamOpenBit */
|
|
516
608
|
});
|
|
517
609
|
if (procClosesWithInit) {
|
|
518
|
-
|
|
610
|
+
reqWritable.close();
|
|
519
611
|
}
|
|
520
612
|
if (procType === "subscription") {
|
|
521
|
-
return {
|
|
613
|
+
return {
|
|
614
|
+
resReadable
|
|
615
|
+
};
|
|
522
616
|
}
|
|
523
617
|
if (procType === "rpc") {
|
|
524
|
-
return getSingleMessage(
|
|
618
|
+
return getSingleMessage(resReadable, transport.log);
|
|
525
619
|
}
|
|
526
620
|
if (procType === "upload") {
|
|
527
621
|
let didFinalize = false;
|
|
528
622
|
return {
|
|
529
|
-
|
|
530
|
-
|
|
623
|
+
reqWritable,
|
|
624
|
+
finalize: () => {
|
|
531
625
|
if (didFinalize) {
|
|
532
626
|
throw new Error("upload stream already finalized");
|
|
533
627
|
}
|
|
534
628
|
didFinalize = true;
|
|
535
|
-
if (!
|
|
536
|
-
|
|
629
|
+
if (!reqWritable.isClosed()) {
|
|
630
|
+
reqWritable.close();
|
|
537
631
|
}
|
|
538
|
-
return getSingleMessage(
|
|
632
|
+
return getSingleMessage(resReadable, transport.log);
|
|
539
633
|
}
|
|
540
634
|
};
|
|
541
635
|
}
|
|
542
|
-
return {
|
|
636
|
+
return {
|
|
637
|
+
resReadable,
|
|
638
|
+
reqWritable
|
|
639
|
+
};
|
|
543
640
|
}
|
|
544
|
-
async function getSingleMessage(
|
|
545
|
-
const ret = await
|
|
641
|
+
async function getSingleMessage(resReadable, log) {
|
|
642
|
+
const ret = await resReadable.collect();
|
|
546
643
|
if (ret.length > 1) {
|
|
547
644
|
log?.error("Expected single message from server, got multiple");
|
|
548
645
|
}
|
|
@@ -550,25 +647,31 @@ async function getSingleMessage(resReader, log) {
|
|
|
550
647
|
}
|
|
551
648
|
|
|
552
649
|
// router/server.ts
|
|
650
|
+
import { Type as Type3 } from "@sinclair/typebox";
|
|
553
651
|
import { Value as Value2 } from "@sinclair/typebox/value";
|
|
554
652
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
555
|
-
var
|
|
653
|
+
var CancelResultSchema = ErrResultSchema(
|
|
654
|
+
Type3.Object({
|
|
655
|
+
code: Type3.Literal(CANCEL_CODE),
|
|
656
|
+
message: Type3.String()
|
|
657
|
+
})
|
|
658
|
+
);
|
|
556
659
|
var RiverServer = class {
|
|
557
660
|
transport;
|
|
558
661
|
contextMap;
|
|
559
662
|
log;
|
|
560
663
|
/**
|
|
561
|
-
* We create a tombstones for streams
|
|
664
|
+
* We create a tombstones for streams cancelled by the server
|
|
562
665
|
* so that we don't hit errors when the client has inflight
|
|
563
|
-
* requests it sent before it saw the
|
|
564
|
-
* We track
|
|
666
|
+
* requests it sent before it saw the cancel.
|
|
667
|
+
* We track cancelled streams for every client separately, so
|
|
565
668
|
* that bad clients don't affect good clients.
|
|
566
669
|
*/
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
670
|
+
serverCancelledStreams;
|
|
671
|
+
maxCancelledStreamTombstonesPerSession;
|
|
672
|
+
streams;
|
|
570
673
|
services;
|
|
571
|
-
constructor(transport, services, handshakeOptions, extendedContext,
|
|
674
|
+
constructor(transport, services, handshakeOptions, extendedContext, maxCancelledStreamTombstonesPerSession = 200) {
|
|
572
675
|
const instances = {};
|
|
573
676
|
this.services = instances;
|
|
574
677
|
this.contextMap = /* @__PURE__ */ new Map();
|
|
@@ -584,34 +687,40 @@ var RiverServer = class {
|
|
|
584
687
|
transport.extendHandshake(handshakeOptions);
|
|
585
688
|
}
|
|
586
689
|
this.transport = transport;
|
|
587
|
-
this.
|
|
588
|
-
this.
|
|
589
|
-
this.
|
|
690
|
+
this.streams = /* @__PURE__ */ new Map();
|
|
691
|
+
this.serverCancelledStreams = /* @__PURE__ */ new Map();
|
|
692
|
+
this.maxCancelledStreamTombstonesPerSession = maxCancelledStreamTombstonesPerSession;
|
|
590
693
|
this.log = transport.log;
|
|
591
|
-
const
|
|
592
|
-
if (
|
|
694
|
+
const handleCreatingNewStreams = (message) => {
|
|
695
|
+
if (message.to !== this.transport.clientId) {
|
|
593
696
|
this.log?.info(
|
|
594
697
|
`got msg with destination that isn't this server, ignoring`,
|
|
595
698
|
{
|
|
596
699
|
clientId: this.transport.clientId,
|
|
597
|
-
transportMessage:
|
|
700
|
+
transportMessage: message
|
|
598
701
|
}
|
|
599
702
|
);
|
|
600
703
|
return;
|
|
601
704
|
}
|
|
602
|
-
|
|
705
|
+
const streamId = message.streamId;
|
|
706
|
+
const stream2 = this.streams.get(streamId);
|
|
707
|
+
if (stream2) {
|
|
708
|
+
stream2.handleMsg(message);
|
|
603
709
|
return;
|
|
604
710
|
}
|
|
605
|
-
if (this.
|
|
711
|
+
if (this.serverCancelledStreams.get(message.from)?.has(streamId)) {
|
|
606
712
|
return;
|
|
607
713
|
}
|
|
608
|
-
const
|
|
609
|
-
if (!
|
|
714
|
+
const newStreamProps = this.validateNewProcStream(message);
|
|
715
|
+
if (!newStreamProps) {
|
|
610
716
|
return;
|
|
611
717
|
}
|
|
612
|
-
this.createNewProcStream(
|
|
718
|
+
const newStream = this.createNewProcStream({
|
|
719
|
+
...newStreamProps,
|
|
720
|
+
...message
|
|
721
|
+
});
|
|
722
|
+
this.streams.set(streamId, newStream);
|
|
613
723
|
};
|
|
614
|
-
this.transport.addEventListener("message", handleMessage);
|
|
615
724
|
const handleSessionStatus = (evt) => {
|
|
616
725
|
if (evt.status !== "disconnect")
|
|
617
726
|
return;
|
|
@@ -620,256 +729,258 @@ var RiverServer = class {
|
|
|
620
729
|
`got session disconnect from ${disconnectedClientId}, cleaning up streams`,
|
|
621
730
|
evt.session.loggingMetadata
|
|
622
731
|
);
|
|
623
|
-
this.
|
|
732
|
+
for (const stream2 of this.streams.values()) {
|
|
733
|
+
if (stream2.from === disconnectedClientId) {
|
|
734
|
+
stream2.handleSessionDisconnect();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
this.serverCancelledStreams.delete(disconnectedClientId);
|
|
624
738
|
};
|
|
625
|
-
|
|
626
|
-
this.transport.addEventListener("transportStatus", (evt) => {
|
|
739
|
+
const handleTransportStatus = (evt) => {
|
|
627
740
|
if (evt.status !== "closed")
|
|
628
741
|
return;
|
|
629
|
-
this.transport.removeEventListener("message",
|
|
742
|
+
this.transport.removeEventListener("message", handleCreatingNewStreams);
|
|
630
743
|
this.transport.removeEventListener("sessionStatus", handleSessionStatus);
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
procedure,
|
|
635
|
-
procedureName,
|
|
636
|
-
service,
|
|
637
|
-
serviceName,
|
|
638
|
-
sessionMetadata,
|
|
639
|
-
loggingMetadata,
|
|
640
|
-
streamId,
|
|
641
|
-
controlFlags,
|
|
642
|
-
initPayload,
|
|
643
|
-
from,
|
|
644
|
-
sessionId,
|
|
645
|
-
tracingCtx,
|
|
646
|
-
protocolVersion,
|
|
647
|
-
passInitAsDataForBackwardsCompat
|
|
648
|
-
}) {
|
|
649
|
-
this.openStreams.add(streamId);
|
|
650
|
-
let cleanClose = true;
|
|
651
|
-
const onServerAbort = (errResult) => {
|
|
652
|
-
if (reqReader.isClosed() && resWriter.isClosed()) {
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
cleanClose = false;
|
|
656
|
-
if (!reqReader.isClosed()) {
|
|
657
|
-
reqReader.pushValue(errResult);
|
|
658
|
-
reqReader.triggerClose();
|
|
659
|
-
}
|
|
660
|
-
resWriter.close();
|
|
661
|
-
this.abortStream(from, streamId, errResult);
|
|
662
|
-
};
|
|
663
|
-
const onHandlerAbort = () => {
|
|
664
|
-
onServerAbort(
|
|
665
|
-
Err({
|
|
666
|
-
code: ABORT_CODE,
|
|
667
|
-
message: "Aborted by server procedure handler"
|
|
668
|
-
})
|
|
744
|
+
this.transport.removeEventListener(
|
|
745
|
+
"transportStatus",
|
|
746
|
+
handleTransportStatus
|
|
669
747
|
);
|
|
670
748
|
};
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
749
|
+
this.transport.addEventListener("message", handleCreatingNewStreams);
|
|
750
|
+
this.transport.addEventListener("sessionStatus", handleSessionStatus);
|
|
751
|
+
this.transport.addEventListener("transportStatus", handleTransportStatus);
|
|
752
|
+
}
|
|
753
|
+
createNewProcStream(props) {
|
|
754
|
+
const {
|
|
755
|
+
streamId,
|
|
756
|
+
initialSession,
|
|
757
|
+
procedureName,
|
|
758
|
+
serviceName,
|
|
759
|
+
procedure,
|
|
760
|
+
sessionMetadata,
|
|
761
|
+
serviceContext,
|
|
762
|
+
initPayload,
|
|
763
|
+
tracingCtx,
|
|
764
|
+
procClosesWithInit,
|
|
765
|
+
passInitAsDataForBackwardsCompat
|
|
766
|
+
} = props;
|
|
767
|
+
const {
|
|
768
|
+
to: from,
|
|
769
|
+
loggingMetadata,
|
|
770
|
+
protocolVersion,
|
|
771
|
+
id: sessionId
|
|
772
|
+
} = initialSession;
|
|
773
|
+
let cleanClose = true;
|
|
694
774
|
const onMessage = (msg) => {
|
|
695
|
-
if (streamId !== msg.streamId) {
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
775
|
if (msg.from !== from) {
|
|
699
|
-
this.log?.error("
|
|
776
|
+
this.log?.error("got stream message from unexpected client", {
|
|
700
777
|
...loggingMetadata,
|
|
701
|
-
clientId: this.transport.clientId,
|
|
702
778
|
transportMessage: msg,
|
|
703
779
|
tags: ["invariant-violation"]
|
|
704
780
|
});
|
|
705
781
|
return;
|
|
706
782
|
}
|
|
707
|
-
if (
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
let abortResult;
|
|
712
|
-
if (Value2.Check(InputErrResultSchema, msg.payload)) {
|
|
713
|
-
abortResult = msg.payload;
|
|
783
|
+
if (isStreamCancelBackwardsCompat(msg.controlFlags, protocolVersion)) {
|
|
784
|
+
let cancelResult;
|
|
785
|
+
if (Value2.Check(CancelResultSchema, msg.payload)) {
|
|
786
|
+
cancelResult = msg.payload;
|
|
714
787
|
} else {
|
|
715
|
-
|
|
716
|
-
code:
|
|
717
|
-
message: "
|
|
788
|
+
cancelResult = Err({
|
|
789
|
+
code: CANCEL_CODE,
|
|
790
|
+
message: "stream cancelled, client sent invalid payload"
|
|
718
791
|
});
|
|
719
|
-
this.log?.warn("
|
|
792
|
+
this.log?.warn("got stream cancel without a valid protocol error", {
|
|
720
793
|
...loggingMetadata,
|
|
721
|
-
clientId: this.transport.clientId,
|
|
722
794
|
transportMessage: msg,
|
|
723
795
|
validationErrors: [
|
|
724
|
-
...Value2.Errors(
|
|
796
|
+
...Value2.Errors(CancelResultSchema, msg.payload)
|
|
725
797
|
],
|
|
726
798
|
tags: ["invalid-request"]
|
|
727
799
|
});
|
|
728
800
|
}
|
|
729
|
-
if (!
|
|
730
|
-
|
|
731
|
-
|
|
801
|
+
if (!reqReadable.isClosed()) {
|
|
802
|
+
reqReadable._pushValue(cancelResult);
|
|
803
|
+
closeReadable();
|
|
732
804
|
}
|
|
733
|
-
|
|
734
|
-
clientAbortController.abort(abortResult.payload);
|
|
805
|
+
resWritable.close();
|
|
735
806
|
return;
|
|
736
807
|
}
|
|
737
|
-
if (
|
|
738
|
-
this.log?.warn("
|
|
808
|
+
if (reqReadable.isClosed()) {
|
|
809
|
+
this.log?.warn("received message after request stream is closed", {
|
|
739
810
|
...loggingMetadata,
|
|
740
|
-
clientId: this.transport.clientId,
|
|
741
811
|
transportMessage: msg,
|
|
742
812
|
tags: ["invalid-request"]
|
|
743
813
|
});
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
})
|
|
749
|
-
);
|
|
814
|
+
onServerCancel({
|
|
815
|
+
code: INVALID_REQUEST_CODE,
|
|
816
|
+
message: "received message after request stream is closed"
|
|
817
|
+
});
|
|
750
818
|
return;
|
|
751
819
|
}
|
|
752
820
|
if ("requestData" in procedure && Value2.Check(procedure.requestData, msg.payload)) {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
821
|
+
reqReadable._pushValue(Ok(msg.payload));
|
|
822
|
+
if (isStreamCloseBackwardsCompat(msg.controlFlags, protocolVersion)) {
|
|
823
|
+
closeReadable();
|
|
824
|
+
}
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
if (Value2.Check(ControlMessagePayloadSchema, msg.payload) && isStreamCloseBackwardsCompat(msg.controlFlags, protocolVersion)) {
|
|
828
|
+
closeReadable();
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
let validationErrors;
|
|
832
|
+
let errMessage;
|
|
833
|
+
if ("requestData" in procedure) {
|
|
834
|
+
errMessage = "expected requestData or control payload";
|
|
835
|
+
validationErrors = [
|
|
836
|
+
...Value2.Errors(procedure.responseData, msg.payload)
|
|
837
|
+
];
|
|
838
|
+
} else {
|
|
839
|
+
validationErrors = [
|
|
756
840
|
...Value2.Errors(ControlMessagePayloadSchema, msg.payload)
|
|
757
841
|
];
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
842
|
+
errMessage = "expected control payload";
|
|
843
|
+
}
|
|
844
|
+
this.log?.warn(errMessage, {
|
|
845
|
+
...loggingMetadata,
|
|
846
|
+
transportMessage: msg,
|
|
847
|
+
validationErrors,
|
|
848
|
+
tags: ["invalid-request"]
|
|
849
|
+
});
|
|
850
|
+
onServerCancel({
|
|
851
|
+
code: INVALID_REQUEST_CODE,
|
|
852
|
+
message: errMessage
|
|
853
|
+
});
|
|
854
|
+
};
|
|
855
|
+
const procStream = {
|
|
856
|
+
from,
|
|
857
|
+
streamId,
|
|
858
|
+
procedureName,
|
|
859
|
+
serviceName,
|
|
860
|
+
sessionMetadata,
|
|
861
|
+
procedure,
|
|
862
|
+
handleMsg: onMessage,
|
|
863
|
+
handleSessionDisconnect: () => {
|
|
864
|
+
cleanClose = false;
|
|
865
|
+
const errPayload = {
|
|
866
|
+
code: UNEXPECTED_DISCONNECT_CODE,
|
|
867
|
+
message: "client unexpectedly disconnected"
|
|
868
|
+
};
|
|
869
|
+
if (!reqReadable.isClosed()) {
|
|
870
|
+
reqReadable._pushValue(Err(errPayload));
|
|
871
|
+
closeReadable();
|
|
764
872
|
}
|
|
765
|
-
|
|
766
|
-
...loggingMetadata,
|
|
767
|
-
clientId: this.transport.clientId,
|
|
768
|
-
transportMessage: msg,
|
|
769
|
-
validationErrors,
|
|
770
|
-
tags: ["invalid-request"]
|
|
771
|
-
});
|
|
772
|
-
onServerAbort(
|
|
773
|
-
Err({
|
|
774
|
-
code: INVALID_REQUEST_CODE,
|
|
775
|
-
message: errMessage
|
|
776
|
-
})
|
|
777
|
-
);
|
|
873
|
+
resWritable.close();
|
|
778
874
|
}
|
|
779
|
-
|
|
780
|
-
|
|
875
|
+
};
|
|
876
|
+
const sessionScopedSend = this.transport.getSessionBoundSendFn(
|
|
877
|
+
from,
|
|
878
|
+
sessionId
|
|
879
|
+
);
|
|
880
|
+
const cancelStream = (streamId2, payload) => {
|
|
881
|
+
this.cancelStream(from, sessionScopedSend, streamId2, payload);
|
|
882
|
+
};
|
|
883
|
+
const onServerCancel = (e) => {
|
|
884
|
+
if (reqReadable.isClosed() && resWritable.isClosed()) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
cleanClose = false;
|
|
888
|
+
const result = Err(e);
|
|
889
|
+
if (!reqReadable.isClosed()) {
|
|
890
|
+
reqReadable._pushValue(result);
|
|
891
|
+
closeReadable();
|
|
781
892
|
}
|
|
893
|
+
resWritable.close();
|
|
894
|
+
cancelStream(streamId, result);
|
|
782
895
|
};
|
|
783
|
-
|
|
784
|
-
const onFinishedCallbacks = [];
|
|
896
|
+
const finishedController = new AbortController();
|
|
785
897
|
const cleanup = () => {
|
|
786
|
-
|
|
787
|
-
this.
|
|
788
|
-
handlerAbortController.signal.addEventListener("abort", onHandlerAbort);
|
|
789
|
-
this.openStreams.delete(streamId);
|
|
790
|
-
onFinishedCallbacks.forEach((cb) => {
|
|
791
|
-
try {
|
|
792
|
-
cb();
|
|
793
|
-
} catch {
|
|
794
|
-
}
|
|
795
|
-
});
|
|
796
|
-
onFinishedCallbacks.length = 0;
|
|
898
|
+
finishedController.abort();
|
|
899
|
+
this.streams.delete(streamId);
|
|
797
900
|
};
|
|
798
901
|
const procClosesWithResponse = procedure.type === "rpc" || procedure.type === "upload";
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
reqReader.onClose(() => {
|
|
902
|
+
const reqReadable = new ReadableImpl();
|
|
903
|
+
const closeReadable = () => {
|
|
904
|
+
reqReadable._triggerClose();
|
|
803
905
|
if (protocolVersion === "v1.1") {
|
|
804
|
-
if (!procClosesWithResponse && !
|
|
805
|
-
|
|
906
|
+
if (!procClosesWithResponse && !resWritable.isClosed()) {
|
|
907
|
+
resWritable.close();
|
|
806
908
|
}
|
|
807
909
|
}
|
|
808
|
-
if (
|
|
910
|
+
if (resWritable.isClosed()) {
|
|
809
911
|
cleanup();
|
|
810
912
|
}
|
|
811
|
-
}
|
|
913
|
+
};
|
|
812
914
|
if (passInitAsDataForBackwardsCompat) {
|
|
813
|
-
|
|
915
|
+
reqReadable._pushValue(Ok(initPayload));
|
|
814
916
|
}
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
917
|
+
const resWritable = new WritableImpl({
|
|
918
|
+
writeCb: (response) => {
|
|
919
|
+
sessionScopedSend({
|
|
920
|
+
streamId,
|
|
921
|
+
controlFlags: procClosesWithResponse ? getStreamCloseBackwardsCompat(protocolVersion) : 0,
|
|
922
|
+
payload: response
|
|
923
|
+
});
|
|
924
|
+
if (procClosesWithResponse) {
|
|
925
|
+
resWritable.close();
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
// close callback
|
|
929
|
+
closeCb: () => {
|
|
930
|
+
if (!procClosesWithResponse && cleanClose) {
|
|
931
|
+
const message = closeStreamMessage(streamId);
|
|
932
|
+
message.controlFlags = getStreamCloseBackwardsCompat(protocolVersion);
|
|
933
|
+
sessionScopedSend(message);
|
|
934
|
+
}
|
|
935
|
+
if (protocolVersion === "v1.1") {
|
|
936
|
+
if (!reqReadable.isClosed()) {
|
|
937
|
+
closeReadable();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (reqReadable.isClosed()) {
|
|
941
|
+
cleanup();
|
|
831
942
|
}
|
|
832
|
-
}
|
|
833
|
-
if (reqReader.isClosed()) {
|
|
834
|
-
cleanup();
|
|
835
943
|
}
|
|
836
944
|
});
|
|
837
945
|
const onHandlerError = (err, span) => {
|
|
838
946
|
const errorMsg = coerceErrorString(err);
|
|
839
947
|
span.recordException(err instanceof Error ? err : new Error(errorMsg));
|
|
840
948
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
949
|
+
this.log?.error(
|
|
950
|
+
`${serviceName}.${procedureName} handler threw an uncaught error`,
|
|
951
|
+
{
|
|
952
|
+
...loggingMetadata,
|
|
953
|
+
transportMessage: {
|
|
954
|
+
procedureName,
|
|
955
|
+
serviceName
|
|
956
|
+
},
|
|
957
|
+
extras: {
|
|
958
|
+
error: errorMsg
|
|
959
|
+
}
|
|
960
|
+
}
|
|
846
961
|
);
|
|
962
|
+
onServerCancel({
|
|
963
|
+
code: UNCAUGHT_ERROR_CODE,
|
|
964
|
+
message: errorMsg
|
|
965
|
+
});
|
|
847
966
|
};
|
|
848
|
-
if (
|
|
849
|
-
|
|
967
|
+
if (procClosesWithInit) {
|
|
968
|
+
closeReadable();
|
|
850
969
|
} else if (procedure.type === "rpc" || procedure.type === "subscription") {
|
|
851
|
-
this.log?.warn(
|
|
852
|
-
...loggingMetadata,
|
|
853
|
-
clientId: this.transport.clientId
|
|
854
|
-
});
|
|
970
|
+
this.log?.warn("sent an init without a stream close", loggingMetadata);
|
|
855
971
|
}
|
|
856
|
-
const
|
|
857
|
-
...
|
|
972
|
+
const handlerContext = {
|
|
973
|
+
...serviceContext,
|
|
858
974
|
from,
|
|
859
975
|
sessionId,
|
|
860
976
|
metadata: sessionMetadata,
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
}
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
onFinishedCallbacks.push(cb);
|
|
872
|
-
}
|
|
977
|
+
cancel: () => {
|
|
978
|
+
onServerCancel({
|
|
979
|
+
code: CANCEL_CODE,
|
|
980
|
+
message: "cancelled by server procedure handler"
|
|
981
|
+
});
|
|
982
|
+
},
|
|
983
|
+
signal: finishedController.signal
|
|
873
984
|
};
|
|
874
985
|
switch (procedure.type) {
|
|
875
986
|
case "rpc":
|
|
@@ -881,15 +992,14 @@ var RiverServer = class {
|
|
|
881
992
|
tracingCtx,
|
|
882
993
|
async (span) => {
|
|
883
994
|
try {
|
|
884
|
-
const
|
|
885
|
-
ctx:
|
|
995
|
+
const responsePayload = await procedure.handler({
|
|
996
|
+
ctx: handlerContext,
|
|
886
997
|
reqInit: initPayload
|
|
887
998
|
});
|
|
888
|
-
if (
|
|
999
|
+
if (resWritable.isClosed()) {
|
|
889
1000
|
return;
|
|
890
1001
|
}
|
|
891
|
-
|
|
892
|
-
resWriter.close();
|
|
1002
|
+
resWritable.write(responsePayload);
|
|
893
1003
|
} catch (err) {
|
|
894
1004
|
onHandlerError(err, span);
|
|
895
1005
|
} finally {
|
|
@@ -908,10 +1018,10 @@ var RiverServer = class {
|
|
|
908
1018
|
async (span) => {
|
|
909
1019
|
try {
|
|
910
1020
|
await procedure.handler({
|
|
911
|
-
ctx:
|
|
1021
|
+
ctx: handlerContext,
|
|
912
1022
|
reqInit: initPayload,
|
|
913
|
-
|
|
914
|
-
|
|
1023
|
+
reqReadable,
|
|
1024
|
+
resWritable
|
|
915
1025
|
});
|
|
916
1026
|
} catch (err) {
|
|
917
1027
|
onHandlerError(err, span);
|
|
@@ -931,9 +1041,9 @@ var RiverServer = class {
|
|
|
931
1041
|
async (span) => {
|
|
932
1042
|
try {
|
|
933
1043
|
await procedure.handler({
|
|
934
|
-
ctx:
|
|
935
|
-
reqInit:
|
|
936
|
-
|
|
1044
|
+
ctx: handlerContext,
|
|
1045
|
+
reqInit: initPayload,
|
|
1046
|
+
resWritable
|
|
937
1047
|
});
|
|
938
1048
|
} catch (err) {
|
|
939
1049
|
onHandlerError(err, span);
|
|
@@ -952,16 +1062,15 @@ var RiverServer = class {
|
|
|
952
1062
|
tracingCtx,
|
|
953
1063
|
async (span) => {
|
|
954
1064
|
try {
|
|
955
|
-
const
|
|
956
|
-
ctx:
|
|
957
|
-
reqInit:
|
|
958
|
-
|
|
1065
|
+
const responsePayload = await procedure.handler({
|
|
1066
|
+
ctx: handlerContext,
|
|
1067
|
+
reqInit: initPayload,
|
|
1068
|
+
reqReadable
|
|
959
1069
|
});
|
|
960
|
-
if (
|
|
1070
|
+
if (resWritable.isClosed()) {
|
|
961
1071
|
return;
|
|
962
1072
|
}
|
|
963
|
-
|
|
964
|
-
resWriter.close();
|
|
1073
|
+
resWritable.write(responsePayload);
|
|
965
1074
|
} catch (err) {
|
|
966
1075
|
onHandlerError(err, span);
|
|
967
1076
|
} finally {
|
|
@@ -970,16 +1079,8 @@ var RiverServer = class {
|
|
|
970
1079
|
}
|
|
971
1080
|
);
|
|
972
1081
|
break;
|
|
973
|
-
default:
|
|
974
|
-
this.log?.error(
|
|
975
|
-
`got request for invalid procedure type ${procedure.type} at ${serviceName}.${procedureName}`,
|
|
976
|
-
{
|
|
977
|
-
...loggingMetadata,
|
|
978
|
-
tags: ["invariant-violation"]
|
|
979
|
-
}
|
|
980
|
-
);
|
|
981
|
-
return;
|
|
982
1082
|
}
|
|
1083
|
+
return procStream;
|
|
983
1084
|
}
|
|
984
1085
|
getContext(service, serviceName) {
|
|
985
1086
|
const context = this.contextMap.get(service);
|
|
@@ -996,22 +1097,20 @@ var RiverServer = class {
|
|
|
996
1097
|
validateNewProcStream(initMessage) {
|
|
997
1098
|
const session = this.transport.sessions.get(initMessage.from);
|
|
998
1099
|
if (!session) {
|
|
999
|
-
const errMessage = `couldn't find a session for ${initMessage.from}`;
|
|
1000
1100
|
this.log?.error(`couldn't find session for ${initMessage.from}`, {
|
|
1001
1101
|
clientId: this.transport.clientId,
|
|
1002
1102
|
transportMessage: initMessage,
|
|
1003
1103
|
tags: ["invariant-violation"]
|
|
1004
1104
|
});
|
|
1005
|
-
this.abortStream(
|
|
1006
|
-
initMessage.from,
|
|
1007
|
-
initMessage.streamId,
|
|
1008
|
-
Err({
|
|
1009
|
-
code: INTERNAL_RIVER_ERROR_CODE,
|
|
1010
|
-
message: errMessage
|
|
1011
|
-
})
|
|
1012
|
-
);
|
|
1013
1105
|
return null;
|
|
1014
1106
|
}
|
|
1107
|
+
const sessionScopedSend = this.transport.getSessionBoundSendFn(
|
|
1108
|
+
initMessage.from,
|
|
1109
|
+
session.id
|
|
1110
|
+
);
|
|
1111
|
+
const cancelStream = (streamId, payload) => {
|
|
1112
|
+
this.cancelStream(initMessage.from, sessionScopedSend, streamId, payload);
|
|
1113
|
+
};
|
|
1015
1114
|
const sessionMetadata = this.transport.sessionHandshakeMetadata.get(
|
|
1016
1115
|
session.to
|
|
1017
1116
|
);
|
|
@@ -1021,11 +1120,10 @@ var RiverServer = class {
|
|
|
1021
1120
|
...session.loggingMetadata,
|
|
1022
1121
|
tags: ["invariant-violation"]
|
|
1023
1122
|
});
|
|
1024
|
-
|
|
1025
|
-
initMessage.from,
|
|
1123
|
+
cancelStream(
|
|
1026
1124
|
initMessage.streamId,
|
|
1027
1125
|
Err({
|
|
1028
|
-
code:
|
|
1126
|
+
code: UNCAUGHT_ERROR_CODE,
|
|
1029
1127
|
message: errMessage
|
|
1030
1128
|
})
|
|
1031
1129
|
);
|
|
@@ -1039,8 +1137,7 @@ var RiverServer = class {
|
|
|
1039
1137
|
transportMessage: initMessage,
|
|
1040
1138
|
tags: ["invalid-request"]
|
|
1041
1139
|
});
|
|
1042
|
-
|
|
1043
|
-
initMessage.from,
|
|
1140
|
+
cancelStream(
|
|
1044
1141
|
initMessage.streamId,
|
|
1045
1142
|
Err({
|
|
1046
1143
|
code: INVALID_REQUEST_CODE,
|
|
@@ -1053,12 +1150,10 @@ var RiverServer = class {
|
|
|
1053
1150
|
const errMessage = `missing service name in stream open message`;
|
|
1054
1151
|
this.log?.warn(errMessage, {
|
|
1055
1152
|
...session.loggingMetadata,
|
|
1056
|
-
clientId: this.transport.clientId,
|
|
1057
1153
|
transportMessage: initMessage,
|
|
1058
1154
|
tags: ["invalid-request"]
|
|
1059
1155
|
});
|
|
1060
|
-
|
|
1061
|
-
initMessage.from,
|
|
1156
|
+
cancelStream(
|
|
1062
1157
|
initMessage.streamId,
|
|
1063
1158
|
Err({
|
|
1064
1159
|
code: INVALID_REQUEST_CODE,
|
|
@@ -1071,12 +1166,10 @@ var RiverServer = class {
|
|
|
1071
1166
|
const errMessage = `missing procedure name in stream open message`;
|
|
1072
1167
|
this.log?.warn(errMessage, {
|
|
1073
1168
|
...session.loggingMetadata,
|
|
1074
|
-
clientId: this.transport.clientId,
|
|
1075
1169
|
transportMessage: initMessage,
|
|
1076
1170
|
tags: ["invalid-request"]
|
|
1077
1171
|
});
|
|
1078
|
-
|
|
1079
|
-
initMessage.from,
|
|
1172
|
+
cancelStream(
|
|
1080
1173
|
initMessage.streamId,
|
|
1081
1174
|
Err({
|
|
1082
1175
|
code: INVALID_REQUEST_CODE,
|
|
@@ -1093,8 +1186,7 @@ var RiverServer = class {
|
|
|
1093
1186
|
transportMessage: initMessage,
|
|
1094
1187
|
tags: ["invalid-request"]
|
|
1095
1188
|
});
|
|
1096
|
-
|
|
1097
|
-
initMessage.from,
|
|
1189
|
+
cancelStream(
|
|
1098
1190
|
initMessage.streamId,
|
|
1099
1191
|
Err({
|
|
1100
1192
|
code: INVALID_REQUEST_CODE,
|
|
@@ -1108,12 +1200,10 @@ var RiverServer = class {
|
|
|
1108
1200
|
const errMessage = `couldn't find a matching procedure for ${initMessage.serviceName}.${initMessage.procedureName}`;
|
|
1109
1201
|
this.log?.warn(errMessage, {
|
|
1110
1202
|
...session.loggingMetadata,
|
|
1111
|
-
clientId: this.transport.clientId,
|
|
1112
1203
|
transportMessage: initMessage,
|
|
1113
1204
|
tags: ["invalid-request"]
|
|
1114
1205
|
});
|
|
1115
|
-
|
|
1116
|
-
initMessage.from,
|
|
1206
|
+
cancelStream(
|
|
1117
1207
|
initMessage.streamId,
|
|
1118
1208
|
Err({
|
|
1119
1209
|
code: INVALID_REQUEST_CODE,
|
|
@@ -1122,7 +1212,19 @@ var RiverServer = class {
|
|
|
1122
1212
|
);
|
|
1123
1213
|
return null;
|
|
1124
1214
|
}
|
|
1215
|
+
const serviceContext = this.getContext(service, initMessage.serviceName);
|
|
1125
1216
|
const procedure = service.procedures[initMessage.procedureName];
|
|
1217
|
+
if (!["rpc", "upload", "stream", "subscription"].includes(procedure.type)) {
|
|
1218
|
+
this.log?.error(
|
|
1219
|
+
`got request for invalid procedure type ${procedure.type} at ${initMessage.serviceName}.${initMessage.procedureName}`,
|
|
1220
|
+
{
|
|
1221
|
+
...session.loggingMetadata,
|
|
1222
|
+
transportMessage: initMessage,
|
|
1223
|
+
tags: ["invariant-violation"]
|
|
1224
|
+
}
|
|
1225
|
+
);
|
|
1226
|
+
return null;
|
|
1227
|
+
}
|
|
1126
1228
|
let passInitAsDataForBackwardsCompat = false;
|
|
1127
1229
|
if (session.protocolVersion === "v1.1" && (procedure.type === "upload" || procedure.type === "stream") && Value2.Check(procedure.requestData, initMessage.payload) && Value2.Check(procedure.requestInit, {})) {
|
|
1128
1230
|
passInitAsDataForBackwardsCompat = true;
|
|
@@ -1134,8 +1236,7 @@ var RiverServer = class {
|
|
|
1134
1236
|
transportMessage: initMessage,
|
|
1135
1237
|
tags: ["invalid-request"]
|
|
1136
1238
|
});
|
|
1137
|
-
|
|
1138
|
-
initMessage.from,
|
|
1239
|
+
cancelStream(
|
|
1139
1240
|
initMessage.streamId,
|
|
1140
1241
|
Err({
|
|
1141
1242
|
code: INVALID_REQUEST_CODE,
|
|
@@ -1145,37 +1246,33 @@ var RiverServer = class {
|
|
|
1145
1246
|
return null;
|
|
1146
1247
|
}
|
|
1147
1248
|
return {
|
|
1148
|
-
|
|
1149
|
-
|
|
1249
|
+
initialSession: session,
|
|
1250
|
+
streamId: initMessage.streamId,
|
|
1150
1251
|
procedureName: initMessage.procedureName,
|
|
1151
|
-
service,
|
|
1152
1252
|
serviceName: initMessage.serviceName,
|
|
1153
|
-
loggingMetadata: {
|
|
1154
|
-
...session.loggingMetadata,
|
|
1155
|
-
transportMessage: initMessage
|
|
1156
|
-
},
|
|
1157
|
-
streamId: initMessage.streamId,
|
|
1158
|
-
controlFlags: initMessage.controlFlags,
|
|
1159
1253
|
tracingCtx: initMessage.tracing,
|
|
1160
1254
|
initPayload: initMessage.payload,
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1255
|
+
sessionMetadata,
|
|
1256
|
+
procedure,
|
|
1257
|
+
serviceContext,
|
|
1258
|
+
procClosesWithInit: isStreamCloseBackwardsCompat(
|
|
1259
|
+
initMessage.controlFlags,
|
|
1260
|
+
session.protocolVersion
|
|
1261
|
+
),
|
|
1164
1262
|
passInitAsDataForBackwardsCompat
|
|
1165
1263
|
};
|
|
1166
1264
|
}
|
|
1167
|
-
|
|
1168
|
-
let
|
|
1169
|
-
if (!
|
|
1170
|
-
|
|
1171
|
-
|
|
1265
|
+
cancelStream(to, sessionScopedSend, streamId, payload) {
|
|
1266
|
+
let cancelledStreamsInSession = this.serverCancelledStreams.get(to);
|
|
1267
|
+
if (!cancelledStreamsInSession) {
|
|
1268
|
+
cancelledStreamsInSession = new LRUSet(
|
|
1269
|
+
this.maxCancelledStreamTombstonesPerSession
|
|
1270
|
+
);
|
|
1271
|
+
this.serverCancelledStreams.set(to, cancelledStreamsInSession);
|
|
1172
1272
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
// TODO remove once clients migrate to v2
|
|
1177
|
-
this.transport.sessions.get(to)?.protocolVersion === "v1.1" ? closeStreamMessage(streamId) : abortMessage(streamId, payload)
|
|
1178
|
-
);
|
|
1273
|
+
cancelledStreamsInSession.add(streamId);
|
|
1274
|
+
const msg = cancelMessage(streamId, payload);
|
|
1275
|
+
sessionScopedSend(msg);
|
|
1179
1276
|
}
|
|
1180
1277
|
};
|
|
1181
1278
|
var LRUSet = class {
|
|
@@ -1200,21 +1297,21 @@ var LRUSet = class {
|
|
|
1200
1297
|
return this.items.has(item);
|
|
1201
1298
|
}
|
|
1202
1299
|
};
|
|
1203
|
-
function
|
|
1300
|
+
function isStreamCancelBackwardsCompat(controlFlags, protocolVersion) {
|
|
1204
1301
|
if (protocolVersion === "v1.1") {
|
|
1205
1302
|
return false;
|
|
1206
1303
|
}
|
|
1207
|
-
return
|
|
1304
|
+
return isStreamCancel(controlFlags);
|
|
1208
1305
|
}
|
|
1209
1306
|
function isStreamCloseBackwardsCompat(controlFlags, protocolVersion) {
|
|
1210
1307
|
if (protocolVersion === "v1.1") {
|
|
1211
|
-
return
|
|
1308
|
+
return isStreamCancel(controlFlags);
|
|
1212
1309
|
}
|
|
1213
1310
|
return isStreamClose(controlFlags);
|
|
1214
1311
|
}
|
|
1215
1312
|
function getStreamCloseBackwardsCompat(protocolVersion) {
|
|
1216
1313
|
if (protocolVersion === "v1.1") {
|
|
1217
|
-
return 4 /*
|
|
1314
|
+
return 4 /* StreamCancelBit */;
|
|
1218
1315
|
}
|
|
1219
1316
|
return 8 /* StreamClosedBit */;
|
|
1220
1317
|
}
|
|
@@ -1224,7 +1321,7 @@ function createServer(transport, services, providedServerOptions) {
|
|
|
1224
1321
|
services,
|
|
1225
1322
|
providedServerOptions?.handshakeOptions,
|
|
1226
1323
|
providedServerOptions?.extendedContext,
|
|
1227
|
-
providedServerOptions?.
|
|
1324
|
+
providedServerOptions?.maxCancelledStreamTombstonesPerSession
|
|
1228
1325
|
);
|
|
1229
1326
|
}
|
|
1230
1327
|
|
|
@@ -1236,15 +1333,13 @@ function createServerHandshakeOptions(schema, validate) {
|
|
|
1236
1333
|
return { schema, validate };
|
|
1237
1334
|
}
|
|
1238
1335
|
export {
|
|
1239
|
-
|
|
1336
|
+
CANCEL_CODE,
|
|
1240
1337
|
Err,
|
|
1241
|
-
INTERNAL_RIVER_ERROR_CODE,
|
|
1242
1338
|
INVALID_REQUEST_CODE,
|
|
1243
1339
|
Ok,
|
|
1244
1340
|
Procedure,
|
|
1245
1341
|
version as RIVER_VERSION,
|
|
1246
|
-
|
|
1247
|
-
ResponseReaderErrorSchema,
|
|
1342
|
+
ReaderErrorSchema,
|
|
1248
1343
|
ServiceSchema,
|
|
1249
1344
|
UNCAUGHT_ERROR_CODE,
|
|
1250
1345
|
UNEXPECTED_DISCONNECT_CODE,
|
|
@@ -1252,6 +1347,9 @@ export {
|
|
|
1252
1347
|
createClientHandshakeOptions,
|
|
1253
1348
|
createServer,
|
|
1254
1349
|
createServerHandshakeOptions,
|
|
1255
|
-
|
|
1350
|
+
flattenErrorType,
|
|
1351
|
+
serializeSchema,
|
|
1352
|
+
serializeSchemaV1Compat,
|
|
1353
|
+
unwrapOrThrow
|
|
1256
1354
|
};
|
|
1257
1355
|
//# sourceMappingURL=index.js.map
|