rivetkit 2.0.6 → 2.0.7-rc.1
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/v1.ts +0 -6
- package/dist/tsup/actor-router-consts-B3Lu87yJ.d.cts +28 -0
- package/dist/tsup/actor-router-consts-B3Lu87yJ.d.ts +28 -0
- package/dist/tsup/{chunk-MRRT2CZD.cjs → chunk-3MBP4WNC.cjs} +7 -7
- package/dist/tsup/{chunk-MRRT2CZD.cjs.map → chunk-3MBP4WNC.cjs.map} +1 -1
- package/dist/tsup/{chunk-TWGATZ3X.cjs → chunk-3Y45CIF4.cjs} +922 -872
- package/dist/tsup/chunk-3Y45CIF4.cjs.map +1 -0
- package/dist/tsup/chunk-4GP7BZSR.js +102 -0
- package/dist/tsup/chunk-4GP7BZSR.js.map +1 -0
- package/dist/tsup/{chunk-UFWAK3X2.cjs → chunk-5ZOHIKWG.cjs} +660 -385
- package/dist/tsup/chunk-5ZOHIKWG.cjs.map +1 -0
- package/dist/tsup/{chunk-5JBFVV4C.cjs → chunk-6EUWRXLT.cjs} +21 -7
- package/dist/tsup/chunk-6EUWRXLT.cjs.map +1 -0
- package/dist/tsup/{chunk-UTI5NCES.cjs → chunk-6OVKCDSH.cjs} +6 -6
- package/dist/tsup/{chunk-UTI5NCES.cjs.map → chunk-6OVKCDSH.cjs.map} +1 -1
- package/dist/tsup/{chunk-VPV4MWXR.js → chunk-7N56ZUC7.js} +3 -3
- package/dist/tsup/{chunk-DIAYNQTE.cjs → chunk-B3TLRM4Q.cjs} +12 -12
- package/dist/tsup/{chunk-DIAYNQTE.cjs.map → chunk-B3TLRM4Q.cjs.map} +1 -1
- package/dist/tsup/{chunk-4CKHQRXG.js → chunk-BW5DPM6Z.js} +515 -240
- package/dist/tsup/chunk-BW5DPM6Z.js.map +1 -0
- package/dist/tsup/{chunk-NTCUGYSD.cjs → chunk-DFS77KAA.cjs} +34 -31
- package/dist/tsup/chunk-DFS77KAA.cjs.map +1 -0
- package/dist/tsup/{chunk-VCEHU56K.js → chunk-E4UVJKSV.js} +2 -2
- package/dist/tsup/chunk-G4ABMAQY.cjs +102 -0
- package/dist/tsup/chunk-G4ABMAQY.cjs.map +1 -0
- package/dist/tsup/{chunk-ZYLTS2EM.js → chunk-GZVBFXBI.js} +2 -2
- package/dist/tsup/{chunk-W6LN7AF5.js → chunk-HPT3I7UU.js} +866 -816
- package/dist/tsup/chunk-HPT3I7UU.js.map +1 -0
- package/dist/tsup/{chunk-7OUKNSTU.js → chunk-JD54PXWP.js} +17 -14
- package/dist/tsup/chunk-JD54PXWP.js.map +1 -0
- package/dist/tsup/{chunk-KG3C7MKR.cjs → chunk-K4ENQCC4.cjs} +3 -3
- package/dist/tsup/{chunk-KG3C7MKR.cjs.map → chunk-K4ENQCC4.cjs.map} +1 -1
- package/dist/tsup/{chunk-WC2PSJWN.js → chunk-PUSQNDJG.js} +2 -2
- package/dist/tsup/{chunk-RGQR2J7S.js → chunk-RVP5RUSC.js} +20 -6
- package/dist/tsup/chunk-RVP5RUSC.js.map +1 -0
- package/dist/tsup/{chunk-TCUI5JFE.cjs → chunk-SAZCNSVY.cjs} +45 -18
- package/dist/tsup/chunk-SAZCNSVY.cjs.map +1 -0
- package/dist/tsup/{chunk-G75SVQON.js → chunk-SBKRVQS2.js} +9 -5
- package/dist/tsup/chunk-SBKRVQS2.js.map +1 -0
- package/dist/tsup/{chunk-6P6RA47N.cjs → chunk-TZGUSEIJ.cjs} +14 -10
- package/dist/tsup/chunk-TZGUSEIJ.cjs.map +1 -0
- package/dist/tsup/{chunk-2K3JMDAN.js → chunk-YQ4XQYPM.js} +40 -13
- package/dist/tsup/chunk-YQ4XQYPM.js.map +1 -0
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.d.cts +7 -8
- package/dist/tsup/client/mod.d.ts +7 -8
- 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/{connection-BLemxi4f.d.ts → conn-DCSQgIlw.d.ts} +1605 -1353
- package/dist/tsup/{connection-CpDIydXf.d.cts → conn-DdzHTm2E.d.cts} +1605 -1353
- package/dist/tsup/driver-helpers/mod.cjs +31 -5
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
- package/dist/tsup/driver-helpers/mod.d.cts +7 -8
- package/dist/tsup/driver-helpers/mod.d.ts +7 -8
- package/dist/tsup/driver-helpers/mod.js +33 -7
- package/dist/tsup/driver-test-suite/mod.cjs +317 -222
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +7 -7
- package/dist/tsup/driver-test-suite/mod.d.ts +7 -7
- package/dist/tsup/driver-test-suite/mod.js +582 -487
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/inspector/mod.cjs +16 -6
- package/dist/tsup/inspector/mod.cjs.map +1 -1
- package/dist/tsup/inspector/mod.d.cts +34 -7
- package/dist/tsup/inspector/mod.d.ts +34 -7
- package/dist/tsup/inspector/mod.js +17 -7
- package/dist/tsup/mod.cjs +10 -20
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +9 -7
- package/dist/tsup/mod.d.ts +9 -7
- package/dist/tsup/mod.js +9 -19
- package/dist/tsup/test/mod.cjs +11 -11
- package/dist/tsup/test/mod.d.cts +6 -7
- package/dist/tsup/test/mod.d.ts +6 -7
- package/dist/tsup/test/mod.js +10 -10
- package/dist/tsup/utils.cjs +4 -2
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.d.cts +11 -1
- package/dist/tsup/utils.d.ts +11 -1
- package/dist/tsup/utils.js +3 -1
- package/package.json +8 -4
- package/src/actor/action.ts +1 -1
- package/src/actor/config.ts +1 -1
- package/src/actor/conn-drivers.ts +205 -0
- package/src/actor/conn-socket.ts +6 -0
- package/src/actor/{connection.ts → conn.ts} +78 -84
- package/src/actor/context.ts +1 -1
- package/src/actor/driver.ts +4 -43
- package/src/actor/instance.ts +162 -86
- package/src/actor/mod.ts +1 -11
- package/src/actor/persisted.ts +2 -5
- package/src/actor/protocol/old.ts +1 -1
- package/src/actor/router-endpoints.ts +142 -106
- package/src/actor/router.ts +81 -45
- package/src/actor/utils.ts +5 -1
- package/src/client/actor-conn.ts +154 -23
- package/src/client/client.ts +1 -1
- package/src/client/config.ts +7 -0
- package/src/common/actor-router-consts.ts +29 -8
- package/src/common/router.ts +2 -1
- package/src/common/versioned-data.ts +5 -5
- package/src/driver-helpers/mod.ts +14 -1
- package/src/driver-test-suite/mod.ts +11 -2
- package/src/driver-test-suite/test-inline-client-driver.ts +36 -18
- package/src/driver-test-suite/tests/actor-conn-state.ts +66 -22
- package/src/driver-test-suite/tests/actor-conn.ts +65 -126
- package/src/driver-test-suite/tests/actor-reconnect.ts +160 -0
- package/src/driver-test-suite/tests/actor-sleep.ts +0 -1
- package/src/driver-test-suite/tests/raw-websocket.ts +0 -35
- package/src/driver-test-suite/utils.ts +3 -3
- package/src/drivers/default.ts +8 -7
- package/src/drivers/engine/actor-driver.ts +53 -31
- package/src/drivers/engine/config.ts +4 -0
- package/src/drivers/file-system/actor.ts +0 -6
- package/src/drivers/file-system/global-state.ts +3 -14
- package/src/drivers/file-system/manager.ts +12 -8
- package/src/inspector/actor.ts +4 -3
- package/src/inspector/config.ts +10 -1
- package/src/inspector/mod.ts +1 -0
- package/src/inspector/utils.ts +23 -4
- package/src/manager/driver.ts +11 -1
- package/src/manager/gateway.ts +407 -0
- package/src/manager/router.ts +269 -468
- package/src/manager-api/actors.ts +61 -0
- package/src/manager-api/common.ts +4 -0
- package/src/mod.ts +1 -1
- package/src/registry/mod.ts +119 -10
- package/src/remote-manager-driver/actor-http-client.ts +30 -19
- package/src/remote-manager-driver/actor-websocket-client.ts +43 -16
- package/src/remote-manager-driver/api-endpoints.ts +19 -21
- package/src/remote-manager-driver/api-utils.ts +10 -1
- package/src/remote-manager-driver/mod.ts +51 -48
- package/src/remote-manager-driver/ws-proxy.ts +2 -9
- package/src/test/mod.ts +6 -2
- package/src/utils.ts +21 -2
- package/dist/tsup/actor-router-consts-BK6arfy8.d.cts +0 -17
- package/dist/tsup/actor-router-consts-BK6arfy8.d.ts +0 -17
- package/dist/tsup/chunk-2K3JMDAN.js.map +0 -1
- package/dist/tsup/chunk-42I3OZ3Q.js +0 -15
- package/dist/tsup/chunk-42I3OZ3Q.js.map +0 -1
- package/dist/tsup/chunk-4CKHQRXG.js.map +0 -1
- package/dist/tsup/chunk-5JBFVV4C.cjs.map +0 -1
- package/dist/tsup/chunk-6P6RA47N.cjs.map +0 -1
- package/dist/tsup/chunk-7OUKNSTU.js.map +0 -1
- package/dist/tsup/chunk-G75SVQON.js.map +0 -1
- package/dist/tsup/chunk-KUPQZYUQ.cjs +0 -15
- package/dist/tsup/chunk-KUPQZYUQ.cjs.map +0 -1
- package/dist/tsup/chunk-NTCUGYSD.cjs.map +0 -1
- package/dist/tsup/chunk-RGQR2J7S.js.map +0 -1
- package/dist/tsup/chunk-TCUI5JFE.cjs.map +0 -1
- package/dist/tsup/chunk-TWGATZ3X.cjs.map +0 -1
- package/dist/tsup/chunk-UFWAK3X2.cjs.map +0 -1
- package/dist/tsup/chunk-W6LN7AF5.js.map +0 -1
- package/dist/tsup/common-CXCe7s6i.d.cts +0 -218
- package/dist/tsup/common-CXCe7s6i.d.ts +0 -218
- package/src/actor/generic-conn-driver.ts +0 -246
- package/src/common/fake-event-source.ts +0 -267
- package/src/manager-api/routes/actors-create.ts +0 -16
- package/src/manager-api/routes/actors-delete.ts +0 -4
- package/src/manager-api/routes/actors-get-by-id.ts +0 -7
- package/src/manager-api/routes/actors-get-or-create-by-id.ts +0 -29
- package/src/manager-api/routes/actors-get.ts +0 -7
- package/src/manager-api/routes/common.ts +0 -18
- /package/dist/tsup/{chunk-VPV4MWXR.js.map → chunk-7N56ZUC7.js.map} +0 -0
- /package/dist/tsup/{chunk-VCEHU56K.js.map → chunk-E4UVJKSV.js.map} +0 -0
- /package/dist/tsup/{chunk-ZYLTS2EM.js.map → chunk-GZVBFXBI.js.map} +0 -0
- /package/dist/tsup/{chunk-WC2PSJWN.js.map → chunk-PUSQNDJG.js.map} +0 -0
|
@@ -2,22 +2,24 @@ import * as cbor from "cbor-x";
|
|
|
2
2
|
import type { Context as HonoContext, HonoRequest } from "hono";
|
|
3
3
|
import { type SSEStreamingApi, streamSSE } from "hono/streaming";
|
|
4
4
|
import type { WSContext } from "hono/ws";
|
|
5
|
+
import invariant from "invariant";
|
|
5
6
|
import { ActionContext } from "@/actor/action";
|
|
6
|
-
import type { AnyConn } from "@/actor/
|
|
7
|
+
import type { AnyConn } from "@/actor/conn";
|
|
7
8
|
import {
|
|
8
|
-
CONNECTION_DRIVER_HTTP,
|
|
9
|
-
CONNECTION_DRIVER_SSE,
|
|
10
|
-
CONNECTION_DRIVER_WEBSOCKET,
|
|
11
9
|
generateConnId,
|
|
10
|
+
generateConnSocketId,
|
|
12
11
|
generateConnToken,
|
|
13
|
-
} from "@/actor/
|
|
12
|
+
} from "@/actor/conn";
|
|
13
|
+
import { ConnDriverKind } from "@/actor/conn-drivers";
|
|
14
14
|
import * as errors from "@/actor/errors";
|
|
15
15
|
import type { AnyActorInstance } from "@/actor/instance";
|
|
16
16
|
import type { InputData } from "@/actor/protocol/serde";
|
|
17
17
|
import { type Encoding, EncodingSchema } from "@/actor/protocol/serde";
|
|
18
18
|
import {
|
|
19
19
|
HEADER_ACTOR_QUERY,
|
|
20
|
+
HEADER_CONN_ID,
|
|
20
21
|
HEADER_CONN_PARAMS,
|
|
22
|
+
HEADER_CONN_TOKEN,
|
|
21
23
|
HEADER_ENCODING,
|
|
22
24
|
} from "@/common/actor-router-consts";
|
|
23
25
|
import type { UpgradeWebSocketArgs } from "@/common/inline-websocket-adapter2";
|
|
@@ -36,22 +38,18 @@ import {
|
|
|
36
38
|
deserializeWithEncoding,
|
|
37
39
|
serializeWithEncoding,
|
|
38
40
|
} from "@/serde";
|
|
39
|
-
import { bufferToArrayBuffer } from "@/utils";
|
|
41
|
+
import { bufferToArrayBuffer, promiseWithResolvers } from "@/utils";
|
|
40
42
|
import type { ActorDriver } from "./driver";
|
|
41
|
-
import type {
|
|
42
|
-
GenericHttpDriverState,
|
|
43
|
-
GenericSseDriverState,
|
|
44
|
-
GenericWebSocketDriverState,
|
|
45
|
-
} from "./generic-conn-driver";
|
|
46
43
|
import { loggerWithoutContext } from "./log";
|
|
47
44
|
import { parseMessage } from "./protocol/old";
|
|
48
45
|
|
|
46
|
+
export const SSE_PING_INTERVAL = 1000;
|
|
47
|
+
|
|
49
48
|
export interface ConnectWebSocketOpts {
|
|
50
49
|
req?: HonoRequest;
|
|
51
50
|
encoding: Encoding;
|
|
52
51
|
actorId: string;
|
|
53
52
|
params: unknown;
|
|
54
|
-
authData: unknown;
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
export interface ConnectWebSocketOutput {
|
|
@@ -65,7 +63,6 @@ export interface ConnectSseOpts {
|
|
|
65
63
|
encoding: Encoding;
|
|
66
64
|
params: unknown;
|
|
67
65
|
actorId: string;
|
|
68
|
-
authData: unknown;
|
|
69
66
|
}
|
|
70
67
|
|
|
71
68
|
export interface ConnectSseOutput {
|
|
@@ -79,7 +76,6 @@ export interface ActionOpts {
|
|
|
79
76
|
actionName: string;
|
|
80
77
|
actionArgs: unknown[];
|
|
81
78
|
actorId: string;
|
|
82
|
-
authData: unknown;
|
|
83
79
|
}
|
|
84
80
|
|
|
85
81
|
export interface ActionOutput {
|
|
@@ -97,14 +93,12 @@ export interface ConnsMessageOpts {
|
|
|
97
93
|
export interface FetchOpts {
|
|
98
94
|
request: Request;
|
|
99
95
|
actorId: string;
|
|
100
|
-
authData: unknown;
|
|
101
96
|
}
|
|
102
97
|
|
|
103
98
|
export interface WebSocketOpts {
|
|
104
99
|
request: Request;
|
|
105
100
|
websocket: UniversalWebSocket;
|
|
106
101
|
actorId: string;
|
|
107
|
-
authData: unknown;
|
|
108
102
|
}
|
|
109
103
|
|
|
110
104
|
/**
|
|
@@ -117,7 +111,8 @@ export async function handleWebSocketConnect(
|
|
|
117
111
|
actorId: string,
|
|
118
112
|
encoding: Encoding,
|
|
119
113
|
parameters: unknown,
|
|
120
|
-
|
|
114
|
+
connId?: string,
|
|
115
|
+
connToken?: string,
|
|
121
116
|
): Promise<UpgradeWebSocketArgs> {
|
|
122
117
|
const exposeInternalError = req ? getRequestExposeInternalError(req) : false;
|
|
123
118
|
|
|
@@ -126,7 +121,7 @@ export async function handleWebSocketConnect(
|
|
|
126
121
|
promise: handlersPromise,
|
|
127
122
|
resolve: handlersResolve,
|
|
128
123
|
reject: handlersReject,
|
|
129
|
-
} =
|
|
124
|
+
} = promiseWithResolvers<{
|
|
130
125
|
conn: AnyConn;
|
|
131
126
|
actor: AnyActorInstance;
|
|
132
127
|
connId: string;
|
|
@@ -158,40 +153,47 @@ export async function handleWebSocketConnect(
|
|
|
158
153
|
};
|
|
159
154
|
}
|
|
160
155
|
|
|
156
|
+
// Promise used to wait for the websocket close in `disconnect`
|
|
157
|
+
const closePromise = promiseWithResolvers<void>();
|
|
158
|
+
const socketId = generateConnSocketId();
|
|
159
|
+
|
|
161
160
|
return {
|
|
162
161
|
onOpen: (_evt: any, ws: WSContext) => {
|
|
163
|
-
actor.rLog.debug("websocket open");
|
|
162
|
+
actor.rLog.debug("actor websocket open");
|
|
164
163
|
|
|
165
164
|
// Run async operations in background
|
|
166
165
|
(async () => {
|
|
167
166
|
try {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
// Save socket
|
|
173
|
-
const connGlobalState =
|
|
174
|
-
actorDriver.getGenericConnGlobalState(actorId);
|
|
175
|
-
connGlobalState.websockets.set(connId, ws);
|
|
167
|
+
let conn: AnyConn;
|
|
168
|
+
|
|
169
|
+
// Create or reconnect connection
|
|
176
170
|
actor.rLog.debug({
|
|
177
|
-
msg:
|
|
171
|
+
msg: connId
|
|
172
|
+
? "websocket reconnection attempt"
|
|
173
|
+
: "new websocket connection",
|
|
174
|
+
connId,
|
|
178
175
|
actorId,
|
|
179
|
-
totalCount: connGlobalState.websockets.size,
|
|
180
176
|
});
|
|
181
177
|
|
|
182
|
-
|
|
183
|
-
|
|
178
|
+
conn = await actor.createConn(
|
|
179
|
+
{
|
|
180
|
+
socketId,
|
|
181
|
+
driverState: {
|
|
182
|
+
[ConnDriverKind.WEBSOCKET]: {
|
|
183
|
+
encoding,
|
|
184
|
+
websocket: ws,
|
|
185
|
+
closePromise,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
parameters,
|
|
190
|
+
req,
|
|
184
191
|
connId,
|
|
185
192
|
connToken,
|
|
186
|
-
parameters,
|
|
187
|
-
connState,
|
|
188
|
-
CONNECTION_DRIVER_WEBSOCKET,
|
|
189
|
-
{ encoding } satisfies GenericWebSocketDriverState,
|
|
190
|
-
authData,
|
|
191
193
|
);
|
|
192
194
|
|
|
193
195
|
// Unblock other handlers
|
|
194
|
-
handlersResolve({ conn, actor, connId });
|
|
196
|
+
handlersResolve({ conn, actor, connId: conn.id });
|
|
195
197
|
} catch (error) {
|
|
196
198
|
handlersReject(error);
|
|
197
199
|
|
|
@@ -263,6 +265,10 @@ export async function handleWebSocketConnect(
|
|
|
263
265
|
},
|
|
264
266
|
ws: WSContext,
|
|
265
267
|
) => {
|
|
268
|
+
handlersReject(`WebSocket closed (${event.code}): ${event.reason}`);
|
|
269
|
+
|
|
270
|
+
closePromise.resolve();
|
|
271
|
+
|
|
266
272
|
if (event.wasClean) {
|
|
267
273
|
actor.rLog.info({
|
|
268
274
|
msg: "websocket closed",
|
|
@@ -285,24 +291,8 @@ export async function handleWebSocketConnect(
|
|
|
285
291
|
|
|
286
292
|
// Handle cleanup asynchronously
|
|
287
293
|
handlersPromise
|
|
288
|
-
.then(({ conn, actor
|
|
289
|
-
|
|
290
|
-
actorDriver.getGenericConnGlobalState(actorId);
|
|
291
|
-
const didDelete = connGlobalState.websockets.delete(connId);
|
|
292
|
-
if (didDelete) {
|
|
293
|
-
actor.rLog.info({
|
|
294
|
-
msg: "removing websocket for conn",
|
|
295
|
-
totalCount: connGlobalState.websockets.size,
|
|
296
|
-
});
|
|
297
|
-
} else {
|
|
298
|
-
actor.rLog.warn({
|
|
299
|
-
msg: "websocket does not exist for conn",
|
|
300
|
-
actorId,
|
|
301
|
-
totalCount: connGlobalState.websockets.size,
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
actor.__removeConn(conn);
|
|
294
|
+
.then(({ conn, actor }) => {
|
|
295
|
+
actor.__connDisconnected(conn, event.wasClean, socketId);
|
|
306
296
|
})
|
|
307
297
|
.catch((error) => {
|
|
308
298
|
deconstructError(
|
|
@@ -337,46 +327,50 @@ export async function handleSseConnect(
|
|
|
337
327
|
_runConfig: RunConfig,
|
|
338
328
|
actorDriver: ActorDriver,
|
|
339
329
|
actorId: string,
|
|
340
|
-
authData: unknown,
|
|
341
330
|
) {
|
|
331
|
+
c.header("Content-Encoding", "Identity");
|
|
332
|
+
|
|
342
333
|
const encoding = getRequestEncoding(c.req);
|
|
343
334
|
const parameters = getRequestConnParams(c.req);
|
|
335
|
+
const socketId = generateConnSocketId();
|
|
336
|
+
|
|
337
|
+
// Check for reconnection parameters
|
|
338
|
+
const connId = c.req.header(HEADER_CONN_ID);
|
|
339
|
+
const connToken = c.req.header(HEADER_CONN_TOKEN);
|
|
344
340
|
|
|
345
341
|
// Return the main handler with all async work inside
|
|
346
342
|
return streamSSE(c, async (stream) => {
|
|
347
343
|
let actor: AnyActorInstance | undefined;
|
|
348
|
-
let connId: string | undefined;
|
|
349
|
-
let connToken: string | undefined;
|
|
350
|
-
let connState: unknown;
|
|
351
344
|
let conn: AnyConn | undefined;
|
|
352
345
|
|
|
353
346
|
try {
|
|
354
347
|
// Do all async work inside the handler
|
|
355
348
|
actor = await actorDriver.loadActor(actorId);
|
|
356
|
-
connId = generateConnId();
|
|
357
|
-
connToken = generateConnToken();
|
|
358
|
-
connState = await actor.prepareConn(parameters, c.req.raw);
|
|
359
349
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
.sseStreams.set(connId, stream);
|
|
350
|
+
// Create or reconnect connection
|
|
351
|
+
actor.rLog.debug({
|
|
352
|
+
msg: connId ? "sse reconnection attempt" : "sse open",
|
|
353
|
+
connId,
|
|
354
|
+
});
|
|
366
355
|
|
|
367
|
-
// Create connection
|
|
368
356
|
conn = await actor.createConn(
|
|
357
|
+
{
|
|
358
|
+
socketId,
|
|
359
|
+
driverState: {
|
|
360
|
+
[ConnDriverKind.SSE]: {
|
|
361
|
+
encoding,
|
|
362
|
+
stream: stream,
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
parameters,
|
|
367
|
+
c.req.raw,
|
|
369
368
|
connId,
|
|
370
369
|
connToken,
|
|
371
|
-
parameters,
|
|
372
|
-
connState,
|
|
373
|
-
CONNECTION_DRIVER_SSE,
|
|
374
|
-
{ encoding } satisfies GenericSseDriverState,
|
|
375
|
-
authData,
|
|
376
370
|
);
|
|
377
371
|
|
|
378
372
|
// Wait for close
|
|
379
|
-
const abortResolver =
|
|
373
|
+
const abortResolver = promiseWithResolvers();
|
|
380
374
|
|
|
381
375
|
// HACK: This is required so the abort handler below works
|
|
382
376
|
//
|
|
@@ -385,18 +379,14 @@ export async function handleSseConnect(
|
|
|
385
379
|
|
|
386
380
|
// Handle stream abort (when client closes the connection)
|
|
387
381
|
c.req.raw.signal.addEventListener("abort", async () => {
|
|
388
|
-
|
|
382
|
+
invariant(actor, "actor should exist");
|
|
383
|
+
const rLog = actor.rLog ?? loggerWithoutContext();
|
|
389
384
|
try {
|
|
390
385
|
rLog.debug("sse stream aborted");
|
|
391
386
|
|
|
392
387
|
// Cleanup
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
.getGenericConnGlobalState(actorId)
|
|
396
|
-
.sseStreams.delete(connId);
|
|
397
|
-
}
|
|
398
|
-
if (conn && actor) {
|
|
399
|
-
actor.__removeConn(conn);
|
|
388
|
+
if (conn) {
|
|
389
|
+
actor.__connDisconnected(conn, false, socketId);
|
|
400
390
|
}
|
|
401
391
|
|
|
402
392
|
abortResolver.resolve(undefined);
|
|
@@ -406,24 +396,33 @@ export async function handleSseConnect(
|
|
|
406
396
|
}
|
|
407
397
|
});
|
|
408
398
|
|
|
409
|
-
// HACK: Will throw if not configured
|
|
410
|
-
try {
|
|
411
|
-
|
|
412
|
-
} catch {}
|
|
399
|
+
// // HACK: Will throw if not configured
|
|
400
|
+
// try {
|
|
401
|
+
// c.executionCtx.waitUntil(abortResolver.promise);
|
|
402
|
+
// } catch {}
|
|
403
|
+
|
|
404
|
+
// Send ping every second to keep the connection alive
|
|
405
|
+
//
|
|
406
|
+
// NOTE: This is required on Cloudflare Workers in order to detect when the connection is closed
|
|
407
|
+
while (true) {
|
|
408
|
+
if (stream.closed || stream.aborted) {
|
|
409
|
+
actor?.rLog.debug({
|
|
410
|
+
msg: "sse stream closed",
|
|
411
|
+
closed: stream.closed,
|
|
412
|
+
aborted: stream.aborted,
|
|
413
|
+
});
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
413
416
|
|
|
414
|
-
|
|
415
|
-
|
|
417
|
+
await stream.writeSSE({ event: "ping", data: "" });
|
|
418
|
+
await stream.sleep(SSE_PING_INTERVAL);
|
|
419
|
+
}
|
|
416
420
|
} catch (error) {
|
|
417
421
|
loggerWithoutContext().error({ msg: "error in sse connection", error });
|
|
418
422
|
|
|
419
423
|
// Cleanup on error
|
|
420
|
-
if (connId !== undefined) {
|
|
421
|
-
actorDriver
|
|
422
|
-
.getGenericConnGlobalState(actorId)
|
|
423
|
-
.sseStreams.delete(connId);
|
|
424
|
-
}
|
|
425
424
|
if (conn && actor !== undefined) {
|
|
426
|
-
actor.
|
|
425
|
+
actor.__connDisconnected(conn, false, socketId);
|
|
427
426
|
}
|
|
428
427
|
|
|
429
428
|
// Close the stream on error
|
|
@@ -441,7 +440,6 @@ export async function handleAction(
|
|
|
441
440
|
actorDriver: ActorDriver,
|
|
442
441
|
actionName: string,
|
|
443
442
|
actorId: string,
|
|
444
|
-
authData: unknown,
|
|
445
443
|
) {
|
|
446
444
|
const encoding = getRequestEncoding(c.req);
|
|
447
445
|
const parameters = getRequestConnParams(c.req);
|
|
@@ -454,6 +452,7 @@ export async function handleAction(
|
|
|
454
452
|
HTTP_ACTION_REQUEST_VERSIONED,
|
|
455
453
|
);
|
|
456
454
|
const actionArgs = cbor.decode(new Uint8Array(request.args));
|
|
455
|
+
const socketId = generateConnSocketId();
|
|
457
456
|
|
|
458
457
|
// Invoke the action
|
|
459
458
|
let actor: AnyActorInstance | undefined;
|
|
@@ -465,15 +464,13 @@ export async function handleAction(
|
|
|
465
464
|
actor.rLog.debug({ msg: "handling action", actionName, encoding });
|
|
466
465
|
|
|
467
466
|
// Create conn
|
|
468
|
-
const connState = await actor.prepareConn(parameters, c.req.raw);
|
|
469
467
|
conn = await actor.createConn(
|
|
470
|
-
|
|
471
|
-
|
|
468
|
+
{
|
|
469
|
+
socketId,
|
|
470
|
+
driverState: { [ConnDriverKind.HTTP]: {} },
|
|
471
|
+
},
|
|
472
472
|
parameters,
|
|
473
|
-
|
|
474
|
-
CONNECTION_DRIVER_HTTP,
|
|
475
|
-
{} satisfies GenericHttpDriverState,
|
|
476
|
-
authData,
|
|
473
|
+
c.req.raw,
|
|
477
474
|
);
|
|
478
475
|
|
|
479
476
|
// Call action
|
|
@@ -481,7 +478,8 @@ export async function handleAction(
|
|
|
481
478
|
output = await actor.executeAction(ctx, actionName, actionArgs);
|
|
482
479
|
} finally {
|
|
483
480
|
if (conn) {
|
|
484
|
-
|
|
481
|
+
// HTTP connections don't have persistent sockets, so no socket ID needed
|
|
482
|
+
actor?.__connDisconnected(conn, true, socketId);
|
|
485
483
|
}
|
|
486
484
|
}
|
|
487
485
|
|
|
@@ -494,7 +492,9 @@ export async function handleAction(
|
|
|
494
492
|
responseData,
|
|
495
493
|
HTTP_ACTION_RESPONSE_VERSIONED,
|
|
496
494
|
);
|
|
497
|
-
|
|
495
|
+
|
|
496
|
+
// TODO: Remvoe any, Hono is being a dumbass
|
|
497
|
+
return c.body(serialized as Uint8Array as any, 200, {
|
|
498
498
|
"Content-Type": contentTypeForEncoding(encoding),
|
|
499
499
|
});
|
|
500
500
|
}
|
|
@@ -539,12 +539,48 @@ export async function handleConnectionMessage(
|
|
|
539
539
|
return c.json({});
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
+
export async function handleConnectionClose(
|
|
543
|
+
c: HonoContext,
|
|
544
|
+
_runConfig: RunConfig,
|
|
545
|
+
actorDriver: ActorDriver,
|
|
546
|
+
connId: string,
|
|
547
|
+
connToken: string,
|
|
548
|
+
actorId: string,
|
|
549
|
+
) {
|
|
550
|
+
const actor = await actorDriver.loadActor(actorId);
|
|
551
|
+
|
|
552
|
+
// Find connection
|
|
553
|
+
const conn = actor.conns.get(connId);
|
|
554
|
+
if (!conn) {
|
|
555
|
+
throw new errors.ConnNotFound(connId);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Authenticate connection
|
|
559
|
+
if (conn._token !== connToken) {
|
|
560
|
+
throw new errors.IncorrectConnToken();
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Check if this is an SSE connection
|
|
564
|
+
if (
|
|
565
|
+
!conn.__socket?.driverState ||
|
|
566
|
+
!(ConnDriverKind.SSE in conn.__socket.driverState)
|
|
567
|
+
) {
|
|
568
|
+
throw new errors.UserError(
|
|
569
|
+
"Connection close is only supported for SSE connections",
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Close the SSE connection
|
|
574
|
+
await conn.disconnect("Connection closed by client request");
|
|
575
|
+
|
|
576
|
+
return c.json({});
|
|
577
|
+
}
|
|
578
|
+
|
|
542
579
|
export async function handleRawWebSocketHandler(
|
|
543
580
|
req: Request | undefined,
|
|
544
581
|
path: string,
|
|
545
582
|
actorDriver: ActorDriver,
|
|
546
583
|
actorId: string,
|
|
547
|
-
authData: unknown,
|
|
548
584
|
): Promise<UpgradeWebSocketArgs> {
|
|
549
585
|
const actor = await actorDriver.loadActor(actorId);
|
|
550
586
|
|
package/src/actor/router.ts
CHANGED
|
@@ -11,19 +11,24 @@ import {
|
|
|
11
11
|
type ConnectWebSocketOutput,
|
|
12
12
|
type ConnsMessageOpts,
|
|
13
13
|
handleAction,
|
|
14
|
+
handleConnectionClose,
|
|
14
15
|
handleConnectionMessage,
|
|
15
16
|
handleRawWebSocketHandler,
|
|
16
17
|
handleSseConnect,
|
|
17
18
|
handleWebSocketConnect,
|
|
18
19
|
} from "@/actor/router-endpoints";
|
|
19
20
|
import {
|
|
20
|
-
HEADER_AUTH_DATA,
|
|
21
21
|
HEADER_CONN_ID,
|
|
22
22
|
HEADER_CONN_PARAMS,
|
|
23
23
|
HEADER_CONN_TOKEN,
|
|
24
24
|
HEADER_ENCODING,
|
|
25
25
|
PATH_CONNECT_WEBSOCKET,
|
|
26
26
|
PATH_RAW_WEBSOCKET_PREFIX,
|
|
27
|
+
WS_PROTOCOL_CONN_ID,
|
|
28
|
+
WS_PROTOCOL_CONN_PARAMS,
|
|
29
|
+
WS_PROTOCOL_CONN_TOKEN,
|
|
30
|
+
WS_PROTOCOL_ENCODING,
|
|
31
|
+
WS_PROTOCOL_TOKEN,
|
|
27
32
|
} from "@/common/actor-router-consts";
|
|
28
33
|
import {
|
|
29
34
|
handleRouteError,
|
|
@@ -35,8 +40,9 @@ import {
|
|
|
35
40
|
type ActorInspectorRouterEnv,
|
|
36
41
|
createActorInspectorRouter,
|
|
37
42
|
} from "@/inspector/actor";
|
|
38
|
-
import { secureInspector } from "@/inspector/utils";
|
|
43
|
+
import { isInspectorEnabled, secureInspector } from "@/inspector/utils";
|
|
39
44
|
import type { RunConfig } from "@/registry/run-config";
|
|
45
|
+
import { ConnDriverKind } from "./conn-drivers";
|
|
40
46
|
import type { ActorDriver } from "./driver";
|
|
41
47
|
import { InternalError } from "./errors";
|
|
42
48
|
import { loggerWithoutContext } from "./log";
|
|
@@ -78,19 +84,70 @@ export function createActorRouter(
|
|
|
78
84
|
return c.text("ok");
|
|
79
85
|
});
|
|
80
86
|
|
|
87
|
+
// Test endpoint to force disconnect a connection non-cleanly
|
|
88
|
+
router.post("/.test/force-disconnect", async (c) => {
|
|
89
|
+
const connId = c.req.query("conn");
|
|
90
|
+
|
|
91
|
+
if (!connId) {
|
|
92
|
+
return c.text("Missing conn query parameter", 400);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const actor = await actorDriver.loadActor(c.env.actorId);
|
|
96
|
+
const conn = actor.__getConnForId(connId);
|
|
97
|
+
|
|
98
|
+
if (!conn) {
|
|
99
|
+
return c.text(`Connection not found: ${connId}`, 404);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Force close the websocket/SSE connection without clean shutdown
|
|
103
|
+
const driverState = conn.__driverState;
|
|
104
|
+
if (driverState && ConnDriverKind.WEBSOCKET in driverState) {
|
|
105
|
+
const ws = driverState[ConnDriverKind.WEBSOCKET].websocket;
|
|
106
|
+
|
|
107
|
+
// Force close without sending close frame
|
|
108
|
+
(ws.raw as any).terminate();
|
|
109
|
+
} else if (driverState && ConnDriverKind.SSE in driverState) {
|
|
110
|
+
const stream = driverState[ConnDriverKind.SSE].stream;
|
|
111
|
+
|
|
112
|
+
// Force close the SSE stream
|
|
113
|
+
stream.abort();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return c.json({ success: true });
|
|
117
|
+
});
|
|
118
|
+
|
|
81
119
|
router.get(PATH_CONNECT_WEBSOCKET, async (c) => {
|
|
82
120
|
const upgradeWebSocket = runConfig.getUpgradeWebSocket?.();
|
|
83
121
|
if (upgradeWebSocket) {
|
|
84
122
|
return upgradeWebSocket(async (c) => {
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
123
|
+
// Parse configuration from Sec-WebSocket-Protocol header
|
|
124
|
+
const protocols = c.req.header("sec-websocket-protocol");
|
|
125
|
+
let encodingRaw: string | undefined;
|
|
126
|
+
let connParamsRaw: string | undefined;
|
|
127
|
+
let connIdRaw: string | undefined;
|
|
128
|
+
let connTokenRaw: string | undefined;
|
|
129
|
+
|
|
130
|
+
if (protocols) {
|
|
131
|
+
const protocolList = protocols.split(",").map((p) => p.trim());
|
|
132
|
+
for (const protocol of protocolList) {
|
|
133
|
+
if (protocol.startsWith(WS_PROTOCOL_ENCODING)) {
|
|
134
|
+
encodingRaw = protocol.substring(WS_PROTOCOL_ENCODING.length);
|
|
135
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_PARAMS)) {
|
|
136
|
+
connParamsRaw = decodeURIComponent(
|
|
137
|
+
protocol.substring(WS_PROTOCOL_CONN_PARAMS.length),
|
|
138
|
+
);
|
|
139
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_ID)) {
|
|
140
|
+
connIdRaw = protocol.substring(WS_PROTOCOL_CONN_ID.length);
|
|
141
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_TOKEN)) {
|
|
142
|
+
connTokenRaw = protocol.substring(WS_PROTOCOL_CONN_TOKEN.length);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
88
146
|
|
|
89
147
|
const encoding = EncodingSchema.parse(encodingRaw);
|
|
90
148
|
const connParams = connParamsRaw
|
|
91
149
|
? JSON.parse(connParamsRaw)
|
|
92
150
|
: undefined;
|
|
93
|
-
const authData = authDataRaw ? JSON.parse(authDataRaw) : undefined;
|
|
94
151
|
|
|
95
152
|
return await handleWebSocketConnect(
|
|
96
153
|
c.req.raw,
|
|
@@ -99,7 +156,8 @@ export function createActorRouter(
|
|
|
99
156
|
c.env.actorId,
|
|
100
157
|
encoding,
|
|
101
158
|
connParams,
|
|
102
|
-
|
|
159
|
+
connIdRaw,
|
|
160
|
+
connTokenRaw,
|
|
103
161
|
);
|
|
104
162
|
})(c, noopNext());
|
|
105
163
|
} else {
|
|
@@ -111,41 +169,38 @@ export function createActorRouter(
|
|
|
111
169
|
});
|
|
112
170
|
|
|
113
171
|
router.get("/connect/sse", async (c) => {
|
|
114
|
-
|
|
115
|
-
let authData: unknown;
|
|
116
|
-
if (authDataRaw) {
|
|
117
|
-
authData = JSON.parse(authDataRaw);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return handleSseConnect(c, runConfig, actorDriver, c.env.actorId, authData);
|
|
172
|
+
return handleSseConnect(c, runConfig, actorDriver, c.env.actorId);
|
|
121
173
|
});
|
|
122
174
|
|
|
123
175
|
router.post("/action/:action", async (c) => {
|
|
124
176
|
const actionName = c.req.param("action");
|
|
125
177
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (authDataRaw) {
|
|
129
|
-
authData = JSON.parse(authDataRaw);
|
|
130
|
-
}
|
|
178
|
+
return handleAction(c, runConfig, actorDriver, actionName, c.env.actorId);
|
|
179
|
+
});
|
|
131
180
|
|
|
132
|
-
|
|
181
|
+
router.post("/connections/message", async (c) => {
|
|
182
|
+
const connId = c.req.header(HEADER_CONN_ID);
|
|
183
|
+
const connToken = c.req.header(HEADER_CONN_TOKEN);
|
|
184
|
+
if (!connId || !connToken) {
|
|
185
|
+
throw new Error("Missing required parameters");
|
|
186
|
+
}
|
|
187
|
+
return handleConnectionMessage(
|
|
133
188
|
c,
|
|
134
189
|
runConfig,
|
|
135
190
|
actorDriver,
|
|
136
|
-
|
|
191
|
+
connId,
|
|
192
|
+
connToken,
|
|
137
193
|
c.env.actorId,
|
|
138
|
-
authData,
|
|
139
194
|
);
|
|
140
195
|
});
|
|
141
196
|
|
|
142
|
-
router.post("/connections/
|
|
197
|
+
router.post("/connections/close", async (c) => {
|
|
143
198
|
const connId = c.req.header(HEADER_CONN_ID);
|
|
144
199
|
const connToken = c.req.header(HEADER_CONN_TOKEN);
|
|
145
200
|
if (!connId || !connToken) {
|
|
146
201
|
throw new Error("Missing required parameters");
|
|
147
202
|
}
|
|
148
|
-
return
|
|
203
|
+
return handleConnectionClose(
|
|
149
204
|
c,
|
|
150
205
|
runConfig,
|
|
151
206
|
actorDriver,
|
|
@@ -157,12 +212,6 @@ export function createActorRouter(
|
|
|
157
212
|
|
|
158
213
|
// Raw HTTP endpoints - /http/*
|
|
159
214
|
router.all("/raw/http/*", async (c) => {
|
|
160
|
-
const authDataRaw = c.req.header(HEADER_AUTH_DATA);
|
|
161
|
-
let authData: unknown;
|
|
162
|
-
if (authDataRaw) {
|
|
163
|
-
authData = JSON.parse(authDataRaw);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
215
|
const actor = await actorDriver.loadActor(c.env.actorId);
|
|
167
216
|
|
|
168
217
|
// TODO: This is not a clean way of doing this since `/http/` might exist mid-path
|
|
@@ -186,9 +235,7 @@ export function createActorRouter(
|
|
|
186
235
|
});
|
|
187
236
|
|
|
188
237
|
// Call the actor's onFetch handler - it will throw appropriate errors
|
|
189
|
-
const response = await actor.handleFetch(correctedRequest, {
|
|
190
|
-
auth: authData,
|
|
191
|
-
});
|
|
238
|
+
const response = await actor.handleFetch(correctedRequest, {});
|
|
192
239
|
|
|
193
240
|
// This should never happen now since handleFetch throws errors
|
|
194
241
|
if (!response) {
|
|
@@ -203,16 +250,6 @@ export function createActorRouter(
|
|
|
203
250
|
const upgradeWebSocket = runConfig.getUpgradeWebSocket?.();
|
|
204
251
|
if (upgradeWebSocket) {
|
|
205
252
|
return upgradeWebSocket(async (c) => {
|
|
206
|
-
const encodingRaw = c.req.header(HEADER_ENCODING);
|
|
207
|
-
const connParamsRaw = c.req.header(HEADER_CONN_PARAMS);
|
|
208
|
-
const authDataRaw = c.req.header(HEADER_AUTH_DATA);
|
|
209
|
-
|
|
210
|
-
const encoding = EncodingSchema.parse(encodingRaw);
|
|
211
|
-
const connParams = connParamsRaw
|
|
212
|
-
? JSON.parse(connParamsRaw)
|
|
213
|
-
: undefined;
|
|
214
|
-
const authData = authDataRaw ? JSON.parse(authDataRaw) : undefined;
|
|
215
|
-
|
|
216
253
|
const url = new URL(c.req.url);
|
|
217
254
|
const pathWithQuery = c.req.path + url.search;
|
|
218
255
|
|
|
@@ -229,7 +266,6 @@ export function createActorRouter(
|
|
|
229
266
|
pathWithQuery,
|
|
230
267
|
actorDriver,
|
|
231
268
|
c.env.actorId,
|
|
232
|
-
authData,
|
|
233
269
|
);
|
|
234
270
|
})(c, noopNext());
|
|
235
271
|
} else {
|
|
@@ -240,7 +276,7 @@ export function createActorRouter(
|
|
|
240
276
|
}
|
|
241
277
|
});
|
|
242
278
|
|
|
243
|
-
if (runConfig
|
|
279
|
+
if (isInspectorEnabled(runConfig, "actor")) {
|
|
244
280
|
router.route(
|
|
245
281
|
"/inspect",
|
|
246
282
|
new Hono<ActorInspectorRouterEnv & { Bindings: ActorRouterBindings }>()
|
package/src/actor/utils.ts
CHANGED
|
@@ -87,7 +87,11 @@ export class Lock<T> {
|
|
|
87
87
|
export function generateSecureToken(length = 32) {
|
|
88
88
|
const array = new Uint8Array(length);
|
|
89
89
|
crypto.getRandomValues(array);
|
|
90
|
-
|
|
90
|
+
// Replace base64 chars that are not URL safe with URL-safe chars and strip padding
|
|
91
|
+
return btoa(String.fromCharCode(...array))
|
|
92
|
+
.replace(/\+/g, "-")
|
|
93
|
+
.replace(/\//g, "_")
|
|
94
|
+
.replace(/=/g, "");
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
export function generateRandomString(length = 32) {
|