rivetkit 2.0.24-rc.1 → 2.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/schemas/actor-persist/v2.ts +3 -3
- package/dist/schemas/actor-persist/v3.ts +274 -0
- package/dist/schemas/client-protocol/v2.ts +432 -0
- package/dist/schemas/file-system-driver/v2.ts +136 -0
- package/dist/tsup/actor/errors.cjs +2 -4
- package/dist/tsup/actor/errors.cjs.map +1 -1
- package/dist/tsup/actor/errors.d.cts +7 -10
- package/dist/tsup/actor/errors.d.ts +7 -10
- package/dist/tsup/actor/errors.js +9 -11
- package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.cts → actor-router-consts-DzI2szci.d.cts} +5 -9
- package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.ts → actor-router-consts-DzI2szci.d.ts} +5 -9
- package/dist/tsup/{chunk-HHFKKVLR.cjs → chunk-3543NCSN.cjs} +45 -57
- package/dist/tsup/chunk-3543NCSN.cjs.map +1 -0
- package/dist/tsup/chunk-4SHILYS5.cjs +5694 -0
- package/dist/tsup/chunk-4SHILYS5.cjs.map +1 -0
- package/dist/tsup/{chunk-ZTH3KYFH.cjs → chunk-5BZO5XPS.cjs} +3 -3
- package/dist/tsup/{chunk-ZTH3KYFH.cjs.map → chunk-5BZO5XPS.cjs.map} +1 -1
- package/dist/tsup/{chunk-PLUN2NQT.js → chunk-BAIGSF64.js} +189 -187
- package/dist/tsup/chunk-BAIGSF64.js.map +1 -0
- package/dist/tsup/{chunk-SHVX2QUR.cjs → chunk-CHLZBSI2.cjs} +17 -17
- package/dist/tsup/chunk-CHLZBSI2.cjs.map +1 -0
- package/dist/tsup/chunk-D3SLADUD.cjs +512 -0
- package/dist/tsup/chunk-D3SLADUD.cjs.map +1 -0
- package/dist/tsup/{chunk-KSRXX3Z4.cjs → chunk-D6762AOA.cjs} +20 -25
- package/dist/tsup/chunk-D6762AOA.cjs.map +1 -0
- package/dist/tsup/{chunk-7L65NNWP.cjs → chunk-DLK5YCTN.cjs} +187 -185
- package/dist/tsup/chunk-DLK5YCTN.cjs.map +1 -0
- package/dist/tsup/{chunk-YBG6R7LX.js → chunk-DUJQWGYD.js} +3 -7
- package/dist/tsup/chunk-DUJQWGYD.js.map +1 -0
- package/dist/tsup/{chunk-CD33GT6Z.js → chunk-EIPANQMF.js} +2 -2
- package/dist/tsup/{chunk-2JYPS5YM.cjs → chunk-ESMTDP7G.cjs} +6 -6
- package/dist/tsup/chunk-ESMTDP7G.cjs.map +1 -0
- package/dist/tsup/{chunk-VHGY7PU5.cjs → chunk-FVAKREFB.cjs} +1900 -1737
- package/dist/tsup/chunk-FVAKREFB.cjs.map +1 -0
- package/dist/tsup/{chunk-BLK27ES3.js → chunk-I3XT7WOF.js} +44 -56
- package/dist/tsup/chunk-I3XT7WOF.js.map +1 -0
- package/dist/tsup/{chunk-YBHYXIP6.js → chunk-IMDS5T42.js} +3 -3
- package/dist/tsup/chunk-IMDS5T42.js.map +1 -0
- package/dist/tsup/{chunk-INNFK746.cjs → chunk-J3HZJF2P.cjs} +10 -14
- package/dist/tsup/chunk-J3HZJF2P.cjs.map +1 -0
- package/dist/tsup/{chunk-BYMKMOBS.js → chunk-MBBJUHSP.js} +1844 -1681
- package/dist/tsup/chunk-MBBJUHSP.js.map +1 -0
- package/dist/tsup/{chunk-BOMZS2TJ.js → chunk-MO5CB6MD.js} +9 -9
- package/dist/tsup/chunk-MO5CB6MD.js.map +1 -0
- package/dist/tsup/chunk-OFOTPKAH.js +512 -0
- package/dist/tsup/chunk-OFOTPKAH.js.map +1 -0
- package/dist/tsup/{chunk-G64QUEDJ.js → chunk-W6RDS6NW.js} +23 -28
- package/dist/tsup/chunk-W6RDS6NW.js.map +1 -0
- package/dist/tsup/{chunk-36JJ4IQB.cjs → chunk-YC5DUHPM.cjs} +4 -8
- package/dist/tsup/chunk-YC5DUHPM.cjs.map +1 -0
- package/dist/tsup/{chunk-FX7TWFQR.js → chunk-YC7YPM2T.js} +2 -6
- package/dist/tsup/chunk-YC7YPM2T.js.map +1 -0
- package/dist/tsup/{chunk-227FEWMB.js → chunk-ZSPU5R4C.js} +3322 -2251
- package/dist/tsup/chunk-ZSPU5R4C.js.map +1 -0
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.d.cts +5 -7
- package/dist/tsup/client/mod.d.ts +5 -7
- package/dist/tsup/client/mod.js +8 -8
- package/dist/tsup/common/log.cjs +3 -3
- package/dist/tsup/common/log.js +2 -2
- package/dist/tsup/common/websocket.cjs +4 -4
- package/dist/tsup/common/websocket.js +3 -3
- package/dist/tsup/{conn-B3Vhbgnd.d.ts → config-BRDYDraU.d.cts} +1119 -1047
- package/dist/tsup/{conn-DJWL3nGx.d.cts → config-Bo-blHpJ.d.ts} +1119 -1047
- package/dist/tsup/driver-helpers/mod.cjs +5 -13
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
- package/dist/tsup/driver-helpers/mod.d.cts +11 -9
- package/dist/tsup/driver-helpers/mod.d.ts +11 -9
- package/dist/tsup/driver-helpers/mod.js +14 -22
- package/dist/tsup/driver-test-suite/mod.cjs +474 -303
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +6 -9
- package/dist/tsup/driver-test-suite/mod.d.ts +6 -9
- package/dist/tsup/driver-test-suite/mod.js +1085 -914
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.d.cts +5 -7
- package/dist/tsup/inspector/mod.d.ts +5 -7
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/mod.cjs +10 -16
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +23 -25
- package/dist/tsup/mod.d.ts +23 -25
- package/dist/tsup/mod.js +17 -23
- package/dist/tsup/test/mod.cjs +11 -11
- package/dist/tsup/test/mod.d.cts +4 -6
- package/dist/tsup/test/mod.d.ts +4 -6
- package/dist/tsup/test/mod.js +10 -10
- package/dist/tsup/utils.cjs +3 -5
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.d.cts +1 -2
- package/dist/tsup/utils.d.ts +1 -2
- package/dist/tsup/utils.js +2 -4
- package/package.json +13 -6
- package/src/actor/config.ts +56 -44
- package/src/actor/conn/driver.ts +61 -0
- package/src/actor/conn/drivers/http.ts +17 -0
- package/src/actor/conn/drivers/raw-request.ts +24 -0
- package/src/actor/conn/drivers/raw-websocket.ts +65 -0
- package/src/actor/conn/drivers/websocket.ts +129 -0
- package/src/actor/conn/mod.ts +232 -0
- package/src/actor/conn/persisted.ts +81 -0
- package/src/actor/conn/state-manager.ts +196 -0
- package/src/actor/contexts/action.ts +23 -0
- package/src/actor/{context.ts → contexts/actor.ts} +19 -8
- package/src/actor/contexts/conn-init.ts +31 -0
- package/src/actor/contexts/conn.ts +48 -0
- package/src/actor/contexts/create-conn-state.ts +13 -0
- package/src/actor/contexts/on-before-connect.ts +13 -0
- package/src/actor/contexts/on-connect.ts +22 -0
- package/src/actor/contexts/request.ts +48 -0
- package/src/actor/contexts/websocket.ts +48 -0
- package/src/actor/definition.ts +3 -3
- package/src/actor/driver.ts +36 -5
- package/src/actor/errors.ts +19 -24
- package/src/actor/instance/connection-manager.ts +465 -0
- package/src/actor/instance/event-manager.ts +292 -0
- package/src/actor/instance/kv.ts +15 -0
- package/src/actor/instance/mod.ts +1107 -0
- package/src/actor/instance/persisted.ts +67 -0
- package/src/actor/instance/schedule-manager.ts +349 -0
- package/src/actor/instance/state-manager.ts +502 -0
- package/src/actor/mod.ts +13 -16
- package/src/actor/protocol/old.ts +131 -43
- package/src/actor/protocol/serde.ts +19 -4
- package/src/actor/router-endpoints.ts +61 -586
- package/src/actor/router-websocket-endpoints.ts +408 -0
- package/src/actor/router.ts +63 -197
- package/src/actor/schedule.ts +1 -1
- package/src/client/actor-conn.ts +183 -249
- package/src/client/actor-handle.ts +29 -6
- package/src/client/client.ts +0 -4
- package/src/client/config.ts +1 -4
- package/src/client/mod.ts +0 -1
- package/src/client/raw-utils.ts +3 -3
- package/src/client/utils.ts +85 -39
- package/src/common/actor-router-consts.ts +5 -12
- package/src/common/{inline-websocket-adapter2.ts → inline-websocket-adapter.ts} +26 -48
- package/src/common/log.ts +1 -1
- package/src/common/router.ts +28 -17
- package/src/common/utils.ts +2 -0
- package/src/driver-helpers/mod.ts +7 -10
- package/src/driver-helpers/utils.ts +18 -9
- package/src/driver-test-suite/mod.ts +26 -50
- package/src/driver-test-suite/test-inline-client-driver.ts +27 -51
- package/src/driver-test-suite/tests/actor-conn-hibernation.ts +150 -0
- package/src/driver-test-suite/tests/actor-conn-state.ts +1 -4
- package/src/driver-test-suite/tests/actor-conn.ts +5 -9
- package/src/driver-test-suite/tests/actor-destroy.ts +294 -0
- package/src/driver-test-suite/tests/actor-driver.ts +0 -7
- package/src/driver-test-suite/tests/actor-handle.ts +12 -12
- package/src/driver-test-suite/tests/actor-metadata.ts +1 -1
- package/src/driver-test-suite/tests/manager-driver.ts +1 -1
- package/src/driver-test-suite/tests/raw-http-direct-registry.ts +8 -8
- package/src/driver-test-suite/tests/raw-http-request-properties.ts +6 -5
- package/src/driver-test-suite/tests/raw-http.ts +5 -5
- package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +7 -7
- package/src/driver-test-suite/tests/request-access.ts +4 -4
- package/src/driver-test-suite/utils.ts +6 -10
- package/src/drivers/engine/actor-driver.ts +614 -424
- package/src/drivers/engine/mod.ts +0 -1
- package/src/drivers/file-system/actor.ts +24 -12
- package/src/drivers/file-system/global-state.ts +427 -37
- package/src/drivers/file-system/manager.ts +71 -83
- package/src/drivers/file-system/mod.ts +3 -0
- package/src/drivers/file-system/utils.ts +18 -8
- package/src/engine-process/mod.ts +38 -38
- package/src/inspector/utils.ts +7 -5
- package/src/manager/driver.ts +11 -4
- package/src/manager/gateway.ts +4 -29
- package/src/manager/protocol/mod.ts +0 -2
- package/src/manager/protocol/query.ts +0 -4
- package/src/manager/router.ts +67 -64
- package/src/manager-api/actors.ts +13 -0
- package/src/mod.ts +1 -3
- package/src/registry/mod.ts +20 -20
- package/src/registry/serve.ts +9 -14
- package/src/remote-manager-driver/actor-websocket-client.ts +1 -16
- package/src/remote-manager-driver/api-endpoints.ts +13 -1
- package/src/remote-manager-driver/api-utils.ts +8 -0
- package/src/remote-manager-driver/metadata.ts +58 -0
- package/src/remote-manager-driver/mod.ts +47 -62
- package/src/remote-manager-driver/ws-proxy.ts +1 -1
- package/src/schemas/actor-persist/mod.ts +1 -1
- package/src/schemas/actor-persist/versioned.ts +56 -31
- package/src/schemas/client-protocol/mod.ts +1 -1
- package/src/schemas/client-protocol/versioned.ts +41 -21
- package/src/schemas/client-protocol-zod/mod.ts +103 -0
- package/src/schemas/file-system-driver/mod.ts +1 -1
- package/src/schemas/file-system-driver/versioned.ts +42 -19
- package/src/serde.ts +33 -11
- package/src/test/mod.ts +7 -3
- package/src/utils/node.ts +173 -0
- package/src/utils.ts +0 -4
- package/dist/tsup/chunk-227FEWMB.js.map +0 -1
- package/dist/tsup/chunk-2JYPS5YM.cjs.map +0 -1
- package/dist/tsup/chunk-36JJ4IQB.cjs.map +0 -1
- package/dist/tsup/chunk-7L65NNWP.cjs.map +0 -1
- package/dist/tsup/chunk-BLK27ES3.js.map +0 -1
- package/dist/tsup/chunk-BOMZS2TJ.js.map +0 -1
- package/dist/tsup/chunk-BYMKMOBS.js.map +0 -1
- package/dist/tsup/chunk-FX7TWFQR.js.map +0 -1
- package/dist/tsup/chunk-G64QUEDJ.js.map +0 -1
- package/dist/tsup/chunk-HHFKKVLR.cjs.map +0 -1
- package/dist/tsup/chunk-INNFK746.cjs.map +0 -1
- package/dist/tsup/chunk-KSRXX3Z4.cjs.map +0 -1
- package/dist/tsup/chunk-O44LFKSB.cjs +0 -4623
- package/dist/tsup/chunk-O44LFKSB.cjs.map +0 -1
- package/dist/tsup/chunk-PLUN2NQT.js.map +0 -1
- package/dist/tsup/chunk-S4UJG7ZE.js +0 -1119
- package/dist/tsup/chunk-S4UJG7ZE.js.map +0 -1
- package/dist/tsup/chunk-SHVX2QUR.cjs.map +0 -1
- package/dist/tsup/chunk-VFB23BYZ.cjs +0 -1119
- package/dist/tsup/chunk-VFB23BYZ.cjs.map +0 -1
- package/dist/tsup/chunk-VHGY7PU5.cjs.map +0 -1
- package/dist/tsup/chunk-YBG6R7LX.js.map +0 -1
- package/dist/tsup/chunk-YBHYXIP6.js.map +0 -1
- package/src/actor/action.ts +0 -178
- package/src/actor/conn-drivers.ts +0 -216
- package/src/actor/conn-socket.ts +0 -8
- package/src/actor/conn.ts +0 -272
- package/src/actor/instance.ts +0 -2336
- package/src/actor/persisted.ts +0 -49
- package/src/actor/unstable-react.ts +0 -110
- package/src/driver-test-suite/tests/actor-reconnect.ts +0 -170
- package/src/drivers/engine/kv.ts +0 -3
- package/src/manager/hono-websocket-adapter.ts +0 -393
- /package/dist/tsup/{chunk-CD33GT6Z.js.map → chunk-EIPANQMF.js.map} +0 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
import type { WSContext } from "hono/ws";
|
|
2
|
+
import invariant from "invariant";
|
|
3
|
+
import type { AnyConn } from "@/actor/conn/mod";
|
|
4
|
+
import type { AnyActorInstance } from "@/actor/instance/mod";
|
|
5
|
+
import type { InputData } from "@/actor/protocol/serde";
|
|
6
|
+
import { type Encoding, EncodingSchema } from "@/actor/protocol/serde";
|
|
7
|
+
import {
|
|
8
|
+
PATH_CONNECT,
|
|
9
|
+
PATH_INSPECTOR_CONNECT,
|
|
10
|
+
PATH_WEBSOCKET_BASE,
|
|
11
|
+
PATH_WEBSOCKET_PREFIX,
|
|
12
|
+
WS_PROTOCOL_CONN_PARAMS,
|
|
13
|
+
WS_PROTOCOL_ENCODING,
|
|
14
|
+
} from "@/common/actor-router-consts";
|
|
15
|
+
import { deconstructError } from "@/common/utils";
|
|
16
|
+
import type {
|
|
17
|
+
RivetMessageEvent,
|
|
18
|
+
UniversalWebSocket,
|
|
19
|
+
} from "@/common/websocket-interface";
|
|
20
|
+
import type { RunnerConfig } from "@/registry/run-config";
|
|
21
|
+
import { promiseWithResolvers } from "@/utils";
|
|
22
|
+
import type { ConnDriver } from "./conn/driver";
|
|
23
|
+
import { createRawWebSocketDriver } from "./conn/drivers/raw-websocket";
|
|
24
|
+
import { createWebSocketDriver } from "./conn/drivers/websocket";
|
|
25
|
+
import type { ActorDriver } from "./driver";
|
|
26
|
+
import { loggerWithoutContext } from "./log";
|
|
27
|
+
import { parseMessage } from "./protocol/old";
|
|
28
|
+
import { getRequestExposeInternalError } from "./router-endpoints";
|
|
29
|
+
|
|
30
|
+
// TODO: Merge with ConnectWebSocketOutput interface
|
|
31
|
+
export interface UpgradeWebSocketArgs {
|
|
32
|
+
conn?: AnyConn;
|
|
33
|
+
actor?: AnyActorInstance;
|
|
34
|
+
onRestore?: (ws: WSContext) => void;
|
|
35
|
+
onOpen: (event: any, ws: WSContext) => void;
|
|
36
|
+
onMessage: (event: any, ws: WSContext) => void;
|
|
37
|
+
onClose: (event: any, ws: WSContext) => void;
|
|
38
|
+
onError: (error: any, ws: WSContext) => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface WebSocketHandlerOpts {
|
|
42
|
+
runConfig: RunnerConfig;
|
|
43
|
+
request: Request | undefined;
|
|
44
|
+
encoding: Encoding;
|
|
45
|
+
actor: AnyActorInstance;
|
|
46
|
+
closePromiseResolvers: ReturnType<typeof promiseWithResolvers<void>>;
|
|
47
|
+
conn: AnyConn;
|
|
48
|
+
exposeInternalError: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Handler for a specific WebSocket route. Used in routeWebSocket. */
|
|
52
|
+
type WebSocketHandler = (
|
|
53
|
+
opts: WebSocketHandlerOpts,
|
|
54
|
+
) => Promise<UpgradeWebSocketArgs>;
|
|
55
|
+
|
|
56
|
+
export async function routeWebSocket(
|
|
57
|
+
request: Request | undefined,
|
|
58
|
+
requestPath: string,
|
|
59
|
+
requestHeaders: Record<string, string>,
|
|
60
|
+
runConfig: RunnerConfig,
|
|
61
|
+
actorDriver: ActorDriver,
|
|
62
|
+
actorId: string,
|
|
63
|
+
encoding: Encoding,
|
|
64
|
+
parameters: unknown,
|
|
65
|
+
gatewayId: ArrayBuffer | undefined,
|
|
66
|
+
requestId: ArrayBuffer | undefined,
|
|
67
|
+
isHibernatable: boolean,
|
|
68
|
+
isRestoringHibernatable: boolean,
|
|
69
|
+
): Promise<UpgradeWebSocketArgs> {
|
|
70
|
+
const exposeInternalError = request
|
|
71
|
+
? getRequestExposeInternalError(request)
|
|
72
|
+
: false;
|
|
73
|
+
|
|
74
|
+
let createdConn: AnyConn | undefined;
|
|
75
|
+
try {
|
|
76
|
+
const actor = await actorDriver.loadActor(actorId);
|
|
77
|
+
|
|
78
|
+
actor.rLog.debug({
|
|
79
|
+
msg: "new websocket connection",
|
|
80
|
+
actorId,
|
|
81
|
+
requestPath,
|
|
82
|
+
isHibernatable,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Promise used to wait for the websocket close in `disconnect`
|
|
86
|
+
const closePromiseResolvers = promiseWithResolvers<void>();
|
|
87
|
+
|
|
88
|
+
// Route WebSocket & create driver
|
|
89
|
+
let handler: WebSocketHandler;
|
|
90
|
+
let connDriver: ConnDriver;
|
|
91
|
+
if (requestPath === PATH_CONNECT) {
|
|
92
|
+
const { driver, setWebSocket } = createWebSocketDriver(
|
|
93
|
+
isHibernatable
|
|
94
|
+
? { gatewayId: gatewayId!, requestId: requestId! }
|
|
95
|
+
: undefined,
|
|
96
|
+
encoding,
|
|
97
|
+
closePromiseResolvers.promise,
|
|
98
|
+
);
|
|
99
|
+
handler = handleWebSocketConnect.bind(undefined, setWebSocket);
|
|
100
|
+
connDriver = driver;
|
|
101
|
+
} else if (
|
|
102
|
+
requestPath === PATH_WEBSOCKET_BASE ||
|
|
103
|
+
requestPath.startsWith(PATH_WEBSOCKET_PREFIX)
|
|
104
|
+
) {
|
|
105
|
+
const { driver, setWebSocket } = createRawWebSocketDriver(
|
|
106
|
+
isHibernatable
|
|
107
|
+
? { gatewayId: gatewayId!, requestId: requestId! }
|
|
108
|
+
: undefined,
|
|
109
|
+
closePromiseResolvers.promise,
|
|
110
|
+
);
|
|
111
|
+
handler = handleRawWebSocket.bind(undefined, setWebSocket);
|
|
112
|
+
connDriver = driver;
|
|
113
|
+
} else if (requestPath === PATH_INSPECTOR_CONNECT) {
|
|
114
|
+
// This returns raw UpgradeWebSocketArgs instead of accepting a
|
|
115
|
+
// Conn since this does not need a Conn
|
|
116
|
+
return await handleWebSocketInspectorConnect();
|
|
117
|
+
} else {
|
|
118
|
+
throw `WebSocket Path Not Found: ${requestPath}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Prepare connection
|
|
122
|
+
const conn = await actor.connectionManager.prepareConn(
|
|
123
|
+
connDriver,
|
|
124
|
+
parameters,
|
|
125
|
+
request,
|
|
126
|
+
requestPath,
|
|
127
|
+
requestHeaders,
|
|
128
|
+
isHibernatable,
|
|
129
|
+
isRestoringHibernatable,
|
|
130
|
+
);
|
|
131
|
+
createdConn = conn;
|
|
132
|
+
|
|
133
|
+
// Create handler
|
|
134
|
+
//
|
|
135
|
+
// This must call actor.connectionManager.connectConn in onOpen.
|
|
136
|
+
return await handler({
|
|
137
|
+
runConfig,
|
|
138
|
+
request,
|
|
139
|
+
encoding,
|
|
140
|
+
actor,
|
|
141
|
+
closePromiseResolvers,
|
|
142
|
+
conn,
|
|
143
|
+
exposeInternalError,
|
|
144
|
+
});
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const { group, code } = deconstructError(
|
|
147
|
+
error,
|
|
148
|
+
loggerWithoutContext(),
|
|
149
|
+
{},
|
|
150
|
+
exposeInternalError,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Clean up connection
|
|
154
|
+
if (createdConn) {
|
|
155
|
+
createdConn.disconnect(`${group}.${code}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Return handler that immediately closes with error
|
|
159
|
+
// Note: createdConn should always exist here, but we use a type assertion for safety
|
|
160
|
+
return {
|
|
161
|
+
conn: createdConn!,
|
|
162
|
+
onOpen: (_evt: any, ws: WSContext) => {
|
|
163
|
+
ws.close(1011, code);
|
|
164
|
+
},
|
|
165
|
+
onMessage: (_evt: { data: any }, ws: WSContext) => {
|
|
166
|
+
ws.close(1011, "actor.not_loaded");
|
|
167
|
+
},
|
|
168
|
+
onClose: (_event: any, _ws: WSContext) => {},
|
|
169
|
+
onError: (_error: unknown) => {},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Creates a WebSocket connection handler
|
|
176
|
+
*/
|
|
177
|
+
export async function handleWebSocketConnect(
|
|
178
|
+
setWebSocket: (ws: WSContext) => void,
|
|
179
|
+
{
|
|
180
|
+
runConfig,
|
|
181
|
+
encoding,
|
|
182
|
+
actor,
|
|
183
|
+
closePromiseResolvers,
|
|
184
|
+
conn,
|
|
185
|
+
exposeInternalError,
|
|
186
|
+
}: WebSocketHandlerOpts,
|
|
187
|
+
): Promise<UpgradeWebSocketArgs> {
|
|
188
|
+
return {
|
|
189
|
+
conn,
|
|
190
|
+
actor,
|
|
191
|
+
onRestore: (ws: WSContext) => {
|
|
192
|
+
setWebSocket(ws);
|
|
193
|
+
},
|
|
194
|
+
// NOTE: onOpen cannot be async since this messes up the open event listener order
|
|
195
|
+
onOpen: (_evt: any, ws: WSContext) => {
|
|
196
|
+
actor.rLog.debug("actor websocket open");
|
|
197
|
+
|
|
198
|
+
setWebSocket(ws);
|
|
199
|
+
|
|
200
|
+
// This will not be called by restoring hibernatable
|
|
201
|
+
// connections. All restoration is done in prepareConn.
|
|
202
|
+
actor.connectionManager.connectConn(conn);
|
|
203
|
+
},
|
|
204
|
+
onMessage: (evt: RivetMessageEvent, ws: WSContext) => {
|
|
205
|
+
// Handle message asynchronously
|
|
206
|
+
actor.rLog.debug({ msg: "received message" });
|
|
207
|
+
|
|
208
|
+
const value = evt.data.valueOf() as InputData;
|
|
209
|
+
parseMessage(value, {
|
|
210
|
+
encoding: encoding,
|
|
211
|
+
maxIncomingMessageSize: runConfig.maxIncomingMessageSize,
|
|
212
|
+
})
|
|
213
|
+
.then((message) => {
|
|
214
|
+
actor.processMessage(message, conn).catch((error) => {
|
|
215
|
+
const { code } = deconstructError(
|
|
216
|
+
error,
|
|
217
|
+
actor.rLog,
|
|
218
|
+
{
|
|
219
|
+
wsEvent: "message",
|
|
220
|
+
},
|
|
221
|
+
exposeInternalError,
|
|
222
|
+
);
|
|
223
|
+
ws.close(1011, code);
|
|
224
|
+
});
|
|
225
|
+
})
|
|
226
|
+
.catch((error) => {
|
|
227
|
+
const { code } = deconstructError(
|
|
228
|
+
error,
|
|
229
|
+
actor.rLog,
|
|
230
|
+
{
|
|
231
|
+
wsEvent: "message",
|
|
232
|
+
},
|
|
233
|
+
exposeInternalError,
|
|
234
|
+
);
|
|
235
|
+
ws.close(1011, code);
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
onClose: (
|
|
239
|
+
event: {
|
|
240
|
+
wasClean: boolean;
|
|
241
|
+
code: number;
|
|
242
|
+
reason: string;
|
|
243
|
+
},
|
|
244
|
+
ws: WSContext,
|
|
245
|
+
) => {
|
|
246
|
+
closePromiseResolvers.resolve();
|
|
247
|
+
|
|
248
|
+
if (event.wasClean) {
|
|
249
|
+
actor.rLog.info({
|
|
250
|
+
msg: "websocket closed",
|
|
251
|
+
code: event.code,
|
|
252
|
+
reason: event.reason,
|
|
253
|
+
wasClean: event.wasClean,
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
actor.rLog.warn({
|
|
257
|
+
msg: "websocket closed",
|
|
258
|
+
code: event.code,
|
|
259
|
+
reason: event.reason,
|
|
260
|
+
wasClean: event.wasClean,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// HACK: Close socket in order to fix bug with Cloudflare leaving WS in closing state
|
|
265
|
+
// https://github.com/cloudflare/workerd/issues/2569
|
|
266
|
+
ws.close(1000, "hack_force_close");
|
|
267
|
+
|
|
268
|
+
// Wait for actor.createConn to finish before removing the connection
|
|
269
|
+
conn.disconnect(event?.reason);
|
|
270
|
+
},
|
|
271
|
+
onError: (_error: unknown) => {
|
|
272
|
+
try {
|
|
273
|
+
// Actors don't need to know about this, since it's abstracted away
|
|
274
|
+
actor.rLog.warn({ msg: "websocket error" });
|
|
275
|
+
} catch (error) {
|
|
276
|
+
deconstructError(
|
|
277
|
+
error,
|
|
278
|
+
actor.rLog,
|
|
279
|
+
{ wsEvent: "error" },
|
|
280
|
+
exposeInternalError,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export async function handleRawWebSocket(
|
|
288
|
+
setWebSocket: (ws: UniversalWebSocket) => void,
|
|
289
|
+
{ request, actor, closePromiseResolvers, conn }: WebSocketHandlerOpts,
|
|
290
|
+
): Promise<UpgradeWebSocketArgs> {
|
|
291
|
+
return {
|
|
292
|
+
conn,
|
|
293
|
+
actor,
|
|
294
|
+
onRestore: (wsContext: WSContext) => {
|
|
295
|
+
const ws = wsContext.raw as UniversalWebSocket;
|
|
296
|
+
invariant(ws, "missing wsContext.raw");
|
|
297
|
+
|
|
298
|
+
setWebSocket(ws);
|
|
299
|
+
},
|
|
300
|
+
// NOTE: onOpen cannot be async since this will cause the client's open
|
|
301
|
+
// event to be called before this completes. Do all async work in
|
|
302
|
+
// handleRawWebSocket root.
|
|
303
|
+
onOpen: (_evt: any, wsContext: WSContext) => {
|
|
304
|
+
const ws = wsContext.raw as UniversalWebSocket;
|
|
305
|
+
invariant(ws, "missing wsContext.raw");
|
|
306
|
+
|
|
307
|
+
setWebSocket(ws);
|
|
308
|
+
|
|
309
|
+
// This will not be called by restoring hibernatable
|
|
310
|
+
// connections. All restoration is done in prepareConn.
|
|
311
|
+
actor.connectionManager.connectConn(conn);
|
|
312
|
+
|
|
313
|
+
// Call the actor's onWebSocket handler with the adapted WebSocket
|
|
314
|
+
//
|
|
315
|
+
// NOTE: onWebSocket is called inside this function. Make sure
|
|
316
|
+
// this is called synchronously within onOpen.
|
|
317
|
+
actor.handleRawWebSocket(conn, ws, request);
|
|
318
|
+
},
|
|
319
|
+
onMessage: (event: any, ws: any) => {
|
|
320
|
+
// Find the adapter for this WebSocket
|
|
321
|
+
const adapter = (ws as any).__adapter;
|
|
322
|
+
if (adapter) {
|
|
323
|
+
adapter._handleMessage(event);
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
onClose: (evt: any, ws: any) => {
|
|
327
|
+
// Resolve the close promise
|
|
328
|
+
closePromiseResolvers.resolve();
|
|
329
|
+
|
|
330
|
+
// Clean up the connection
|
|
331
|
+
conn.disconnect(evt?.reason);
|
|
332
|
+
},
|
|
333
|
+
onError: (error: any, ws: any) => {},
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export async function handleWebSocketInspectorConnect(): Promise<UpgradeWebSocketArgs> {
|
|
338
|
+
return {
|
|
339
|
+
// NOTE: onOpen cannot be async since this messes up the open event listener order
|
|
340
|
+
onOpen: (_evt: any, ws: WSContext) => {
|
|
341
|
+
ws.send("Hello world");
|
|
342
|
+
},
|
|
343
|
+
onMessage: (evt: RivetMessageEvent, ws: WSContext) => {
|
|
344
|
+
ws.send("Pong");
|
|
345
|
+
},
|
|
346
|
+
onClose: (
|
|
347
|
+
event: {
|
|
348
|
+
wasClean: boolean;
|
|
349
|
+
code: number;
|
|
350
|
+
reason: string;
|
|
351
|
+
},
|
|
352
|
+
ws: WSContext,
|
|
353
|
+
) => {
|
|
354
|
+
// TODO:
|
|
355
|
+
},
|
|
356
|
+
onError: (_error: unknown) => {
|
|
357
|
+
// TODO:
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Parse encoding and connection parameters from WebSocket Sec-WebSocket-Protocol header
|
|
364
|
+
*/
|
|
365
|
+
export function parseWebSocketProtocols(protocols: string | null | undefined): {
|
|
366
|
+
encoding: Encoding;
|
|
367
|
+
connParams: unknown;
|
|
368
|
+
} {
|
|
369
|
+
let encodingRaw: string | undefined;
|
|
370
|
+
let connParamsRaw: string | undefined;
|
|
371
|
+
|
|
372
|
+
if (protocols) {
|
|
373
|
+
const protocolList = protocols.split(",").map((p) => p.trim());
|
|
374
|
+
for (const protocol of protocolList) {
|
|
375
|
+
if (protocol.startsWith(WS_PROTOCOL_ENCODING)) {
|
|
376
|
+
encodingRaw = protocol.substring(WS_PROTOCOL_ENCODING.length);
|
|
377
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_PARAMS)) {
|
|
378
|
+
connParamsRaw = decodeURIComponent(
|
|
379
|
+
protocol.substring(WS_PROTOCOL_CONN_PARAMS.length),
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const encoding = EncodingSchema.parse(encodingRaw);
|
|
386
|
+
const connParams = connParamsRaw ? JSON.parse(connParamsRaw) : undefined;
|
|
387
|
+
|
|
388
|
+
return { encoding, connParams };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Truncase the PATH_WEBSOCKET_PREFIX path prefix in order to pass a clean
|
|
393
|
+
* path to the onWebSocket handler.
|
|
394
|
+
*
|
|
395
|
+
* Example:
|
|
396
|
+
* - `/websocket/foo` -> `/foo`
|
|
397
|
+
* - `/websocket` -> `/`
|
|
398
|
+
*/
|
|
399
|
+
export function truncateRawWebSocketPathPrefix(path: string): string {
|
|
400
|
+
// Extract the path after prefix and preserve query parameters
|
|
401
|
+
// Use URL API for cleaner parsing
|
|
402
|
+
const url = new URL(path, "http://actor");
|
|
403
|
+
const pathname = url.pathname.replace(/^\/websocket\/?/, "") || "/";
|
|
404
|
+
const normalizedPath =
|
|
405
|
+
(pathname.startsWith("/") ? pathname : `/${pathname}`) + url.search;
|
|
406
|
+
|
|
407
|
+
return normalizedPath;
|
|
408
|
+
}
|