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
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import type { Context as HonoContext, Next } from "hono";
|
|
2
|
+
import type { WSContext } from "hono/ws";
|
|
3
|
+
import { MissingActorHeader, WebSocketsNotEnabled } from "@/actor/errors";
|
|
4
|
+
import type { Encoding, Transport } from "@/client/mod";
|
|
5
|
+
import {
|
|
6
|
+
HEADER_RIVET_ACTOR,
|
|
7
|
+
HEADER_RIVET_TARGET,
|
|
8
|
+
WS_PROTOCOL_ACTOR,
|
|
9
|
+
WS_PROTOCOL_CONN_ID,
|
|
10
|
+
WS_PROTOCOL_CONN_PARAMS,
|
|
11
|
+
WS_PROTOCOL_CONN_TOKEN,
|
|
12
|
+
WS_PROTOCOL_ENCODING,
|
|
13
|
+
WS_PROTOCOL_TARGET,
|
|
14
|
+
} from "@/common/actor-router-consts";
|
|
15
|
+
import { deconstructError, noopNext } from "@/common/utils";
|
|
16
|
+
import type { UniversalWebSocket, UpgradeWebSocketArgs } from "@/mod";
|
|
17
|
+
import type { RunConfig } from "@/registry/run-config";
|
|
18
|
+
import { promiseWithResolvers, stringifyError } from "@/utils";
|
|
19
|
+
import type { ManagerDriver } from "./driver";
|
|
20
|
+
import { logger } from "./log";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Provides an endpoint to connect to individual actors.
|
|
24
|
+
*
|
|
25
|
+
* Routes requests based on the Upgrade header:
|
|
26
|
+
* - WebSocket requests: Uses sec-websocket-protocol for routing (target.actor, actor.{id})
|
|
27
|
+
* - HTTP requests: Uses x-rivet-target and x-rivet-actor headers for routing
|
|
28
|
+
*/
|
|
29
|
+
export async function actorGateway(
|
|
30
|
+
runConfig: RunConfig,
|
|
31
|
+
managerDriver: ManagerDriver,
|
|
32
|
+
c: HonoContext,
|
|
33
|
+
next: Next,
|
|
34
|
+
) {
|
|
35
|
+
// Skip test routes - let them be handled by their specific handlers
|
|
36
|
+
if (c.req.path.startsWith("/.test/")) {
|
|
37
|
+
return next();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if this is a WebSocket upgrade request
|
|
41
|
+
if (c.req.header("upgrade") === "websocket") {
|
|
42
|
+
return await handleWebSocketGateway(runConfig, managerDriver, c);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Handle regular HTTP requests
|
|
46
|
+
return await handleHttpGateway(managerDriver, c, next);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Handle WebSocket requests using sec-websocket-protocol for routing
|
|
51
|
+
*/
|
|
52
|
+
async function handleWebSocketGateway(
|
|
53
|
+
runConfig: RunConfig,
|
|
54
|
+
managerDriver: ManagerDriver,
|
|
55
|
+
c: HonoContext,
|
|
56
|
+
) {
|
|
57
|
+
const upgradeWebSocket = runConfig.getUpgradeWebSocket?.();
|
|
58
|
+
if (!upgradeWebSocket) {
|
|
59
|
+
throw new WebSocketsNotEnabled();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Parse configuration from Sec-WebSocket-Protocol header
|
|
63
|
+
const protocols = c.req.header("sec-websocket-protocol");
|
|
64
|
+
let target: string | undefined;
|
|
65
|
+
let actorId: string | undefined;
|
|
66
|
+
let encodingRaw: string | undefined;
|
|
67
|
+
let connParamsRaw: string | undefined;
|
|
68
|
+
let connIdRaw: string | undefined;
|
|
69
|
+
let connTokenRaw: string | undefined;
|
|
70
|
+
|
|
71
|
+
if (protocols) {
|
|
72
|
+
const protocolList = protocols.split(",").map((p) => p.trim());
|
|
73
|
+
for (const protocol of protocolList) {
|
|
74
|
+
if (protocol.startsWith(WS_PROTOCOL_TARGET)) {
|
|
75
|
+
target = protocol.substring(WS_PROTOCOL_TARGET.length);
|
|
76
|
+
} else if (protocol.startsWith(WS_PROTOCOL_ACTOR)) {
|
|
77
|
+
actorId = protocol.substring(WS_PROTOCOL_ACTOR.length);
|
|
78
|
+
} else if (protocol.startsWith(WS_PROTOCOL_ENCODING)) {
|
|
79
|
+
encodingRaw = protocol.substring(WS_PROTOCOL_ENCODING.length);
|
|
80
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_PARAMS)) {
|
|
81
|
+
connParamsRaw = decodeURIComponent(
|
|
82
|
+
protocol.substring(WS_PROTOCOL_CONN_PARAMS.length),
|
|
83
|
+
);
|
|
84
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_ID)) {
|
|
85
|
+
connIdRaw = protocol.substring(WS_PROTOCOL_CONN_ID.length);
|
|
86
|
+
} else if (protocol.startsWith(WS_PROTOCOL_CONN_TOKEN)) {
|
|
87
|
+
connTokenRaw = protocol.substring(WS_PROTOCOL_CONN_TOKEN.length);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (target !== "actor") {
|
|
93
|
+
return c.text("WebSocket upgrade requires target.actor protocol", 400);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!actorId) {
|
|
97
|
+
throw new MissingActorHeader();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
logger().debug({
|
|
101
|
+
msg: "proxying websocket to actor",
|
|
102
|
+
actorId,
|
|
103
|
+
path: c.req.path,
|
|
104
|
+
encoding: encodingRaw,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const encoding = encodingRaw || "json";
|
|
108
|
+
const connParams = connParamsRaw ? JSON.parse(connParamsRaw) : undefined;
|
|
109
|
+
|
|
110
|
+
// Include query string if present
|
|
111
|
+
const pathWithQuery = c.req.url.includes("?")
|
|
112
|
+
? c.req.path + c.req.url.substring(c.req.url.indexOf("?"))
|
|
113
|
+
: c.req.path;
|
|
114
|
+
|
|
115
|
+
return await managerDriver.proxyWebSocket(
|
|
116
|
+
c,
|
|
117
|
+
pathWithQuery,
|
|
118
|
+
actorId,
|
|
119
|
+
encoding as any, // Will be validated by driver
|
|
120
|
+
connParams,
|
|
121
|
+
connIdRaw,
|
|
122
|
+
connTokenRaw,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Handle HTTP requests using x-rivet headers for routing
|
|
128
|
+
*/
|
|
129
|
+
async function handleHttpGateway(
|
|
130
|
+
managerDriver: ManagerDriver,
|
|
131
|
+
c: HonoContext,
|
|
132
|
+
next: Next,
|
|
133
|
+
) {
|
|
134
|
+
const target = c.req.header(HEADER_RIVET_TARGET);
|
|
135
|
+
const actorId = c.req.header(HEADER_RIVET_ACTOR);
|
|
136
|
+
|
|
137
|
+
if (target !== "actor") {
|
|
138
|
+
return next();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!actorId) {
|
|
142
|
+
throw new MissingActorHeader();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
logger().debug({
|
|
146
|
+
msg: "proxying request to actor",
|
|
147
|
+
actorId,
|
|
148
|
+
path: c.req.path,
|
|
149
|
+
method: c.req.method,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Preserve all headers except the routing headers
|
|
153
|
+
const proxyHeaders = new Headers(c.req.raw.headers);
|
|
154
|
+
proxyHeaders.delete(HEADER_RIVET_TARGET);
|
|
155
|
+
proxyHeaders.delete(HEADER_RIVET_ACTOR);
|
|
156
|
+
|
|
157
|
+
// Build the proxy request with the actor URL format
|
|
158
|
+
const url = new URL(c.req.url);
|
|
159
|
+
const proxyUrl = new URL(`http://actor${url.pathname}${url.search}`);
|
|
160
|
+
|
|
161
|
+
const proxyRequest = new Request(proxyUrl, {
|
|
162
|
+
method: c.req.raw.method,
|
|
163
|
+
headers: proxyHeaders,
|
|
164
|
+
body: c.req.raw.body,
|
|
165
|
+
signal: c.req.raw.signal,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return await managerDriver.proxyRequest(c, proxyRequest, actorId);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Creates a WebSocket proxy for test endpoints that forwards messages between server and client WebSockets
|
|
173
|
+
*/
|
|
174
|
+
export async function createTestWebSocketProxy(
|
|
175
|
+
clientWsPromise: Promise<UniversalWebSocket>,
|
|
176
|
+
): Promise<UpgradeWebSocketArgs> {
|
|
177
|
+
// Store a reference to the resolved WebSocket
|
|
178
|
+
let clientWs: UniversalWebSocket | null = null;
|
|
179
|
+
const {
|
|
180
|
+
promise: serverWsPromise,
|
|
181
|
+
resolve: serverWsResolve,
|
|
182
|
+
reject: serverWsReject,
|
|
183
|
+
} = promiseWithResolvers<WSContext>();
|
|
184
|
+
try {
|
|
185
|
+
// Resolve the client WebSocket promise
|
|
186
|
+
logger().debug({ msg: "awaiting client websocket promise" });
|
|
187
|
+
const ws = await clientWsPromise;
|
|
188
|
+
clientWs = ws;
|
|
189
|
+
logger().debug({
|
|
190
|
+
msg: "client websocket promise resolved",
|
|
191
|
+
constructor: ws?.constructor.name,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Wait for ws to open
|
|
195
|
+
await new Promise<void>((resolve, reject) => {
|
|
196
|
+
const onOpen = () => {
|
|
197
|
+
logger().debug({ msg: "test websocket connection to actor opened" });
|
|
198
|
+
resolve();
|
|
199
|
+
};
|
|
200
|
+
const onError = (error: any) => {
|
|
201
|
+
logger().error({ msg: "test websocket connection failed", error });
|
|
202
|
+
reject(
|
|
203
|
+
new Error(`Failed to open WebSocket: ${error.message || error}`),
|
|
204
|
+
);
|
|
205
|
+
serverWsReject();
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
ws.addEventListener("open", onOpen);
|
|
209
|
+
|
|
210
|
+
ws.addEventListener("error", onError);
|
|
211
|
+
|
|
212
|
+
ws.addEventListener("message", async (clientEvt: MessageEvent) => {
|
|
213
|
+
const serverWs = await serverWsPromise;
|
|
214
|
+
|
|
215
|
+
logger().debug({
|
|
216
|
+
msg: `test websocket connection message from client`,
|
|
217
|
+
dataType: typeof clientEvt.data,
|
|
218
|
+
isBlob: clientEvt.data instanceof Blob,
|
|
219
|
+
isArrayBuffer: clientEvt.data instanceof ArrayBuffer,
|
|
220
|
+
dataConstructor: clientEvt.data?.constructor?.name,
|
|
221
|
+
dataStr:
|
|
222
|
+
typeof clientEvt.data === "string"
|
|
223
|
+
? clientEvt.data.substring(0, 100)
|
|
224
|
+
: undefined,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (serverWs.readyState === 1) {
|
|
228
|
+
// OPEN
|
|
229
|
+
// Handle Blob data
|
|
230
|
+
if (clientEvt.data instanceof Blob) {
|
|
231
|
+
clientEvt.data
|
|
232
|
+
.arrayBuffer()
|
|
233
|
+
.then((buffer) => {
|
|
234
|
+
logger().debug({
|
|
235
|
+
msg: "converted client blob to arraybuffer, sending to server",
|
|
236
|
+
bufferSize: buffer.byteLength,
|
|
237
|
+
});
|
|
238
|
+
serverWs.send(buffer as any);
|
|
239
|
+
})
|
|
240
|
+
.catch((error) => {
|
|
241
|
+
logger().error({
|
|
242
|
+
msg: "failed to convert blob to arraybuffer",
|
|
243
|
+
error,
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
} else {
|
|
247
|
+
logger().debug({
|
|
248
|
+
msg: "sending client data directly to server",
|
|
249
|
+
dataType: typeof clientEvt.data,
|
|
250
|
+
dataLength:
|
|
251
|
+
typeof clientEvt.data === "string"
|
|
252
|
+
? clientEvt.data.length
|
|
253
|
+
: undefined,
|
|
254
|
+
});
|
|
255
|
+
serverWs.send(clientEvt.data as any);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
ws.addEventListener("close", async (clientEvt: any) => {
|
|
261
|
+
const serverWs = await serverWsPromise;
|
|
262
|
+
|
|
263
|
+
logger().debug({
|
|
264
|
+
msg: `test websocket connection closed`,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
if (serverWs.readyState !== 3) {
|
|
268
|
+
// Not CLOSED
|
|
269
|
+
serverWs.close(clientEvt.code, clientEvt.reason);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
ws.addEventListener("error", async () => {
|
|
274
|
+
const serverWs = await serverWsPromise;
|
|
275
|
+
|
|
276
|
+
logger().debug({
|
|
277
|
+
msg: `test websocket connection error`,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (serverWs.readyState !== 3) {
|
|
281
|
+
// Not CLOSED
|
|
282
|
+
serverWs.close(1011, "Error in client websocket");
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
} catch (error) {
|
|
287
|
+
logger().error({
|
|
288
|
+
msg: `failed to establish client websocket connection`,
|
|
289
|
+
error,
|
|
290
|
+
});
|
|
291
|
+
return {
|
|
292
|
+
onOpen: (_evt, serverWs) => {
|
|
293
|
+
serverWs.close(1011, "Failed to establish connection");
|
|
294
|
+
},
|
|
295
|
+
onMessage: () => {},
|
|
296
|
+
onError: () => {},
|
|
297
|
+
onClose: () => {},
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Create WebSocket proxy handlers to relay messages between client and server
|
|
302
|
+
return {
|
|
303
|
+
onOpen: (_evt: any, serverWs: WSContext) => {
|
|
304
|
+
logger().debug({
|
|
305
|
+
msg: `test websocket connection from client opened`,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Check WebSocket type
|
|
309
|
+
logger().debug({
|
|
310
|
+
msg: "clientWs info",
|
|
311
|
+
constructor: clientWs.constructor.name,
|
|
312
|
+
hasAddEventListener: typeof clientWs.addEventListener === "function",
|
|
313
|
+
readyState: clientWs.readyState,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
serverWsResolve(serverWs);
|
|
317
|
+
},
|
|
318
|
+
onMessage: (evt: { data: any }) => {
|
|
319
|
+
logger().debug({
|
|
320
|
+
msg: "received message from server",
|
|
321
|
+
dataType: typeof evt.data,
|
|
322
|
+
isBlob: evt.data instanceof Blob,
|
|
323
|
+
isArrayBuffer: evt.data instanceof ArrayBuffer,
|
|
324
|
+
dataConstructor: evt.data?.constructor?.name,
|
|
325
|
+
dataStr:
|
|
326
|
+
typeof evt.data === "string" ? evt.data.substring(0, 100) : undefined,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Forward messages from server websocket to client websocket
|
|
330
|
+
if (clientWs.readyState === 1) {
|
|
331
|
+
// OPEN
|
|
332
|
+
// Handle Blob data
|
|
333
|
+
if (evt.data instanceof Blob) {
|
|
334
|
+
evt.data
|
|
335
|
+
.arrayBuffer()
|
|
336
|
+
.then((buffer) => {
|
|
337
|
+
logger().debug({
|
|
338
|
+
msg: "converted blob to arraybuffer, sending",
|
|
339
|
+
bufferSize: buffer.byteLength,
|
|
340
|
+
});
|
|
341
|
+
clientWs.send(buffer);
|
|
342
|
+
})
|
|
343
|
+
.catch((error) => {
|
|
344
|
+
logger().error({
|
|
345
|
+
msg: "failed to convert blob to arraybuffer",
|
|
346
|
+
error,
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
} else {
|
|
350
|
+
logger().debug({
|
|
351
|
+
msg: "sending data directly",
|
|
352
|
+
dataType: typeof evt.data,
|
|
353
|
+
dataLength:
|
|
354
|
+
typeof evt.data === "string" ? evt.data.length : undefined,
|
|
355
|
+
});
|
|
356
|
+
clientWs.send(evt.data);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
onClose: (
|
|
361
|
+
event: {
|
|
362
|
+
wasClean: boolean;
|
|
363
|
+
code: number;
|
|
364
|
+
reason: string;
|
|
365
|
+
},
|
|
366
|
+
serverWs: WSContext,
|
|
367
|
+
) => {
|
|
368
|
+
logger().debug({
|
|
369
|
+
msg: `server websocket closed`,
|
|
370
|
+
wasClean: event.wasClean,
|
|
371
|
+
code: event.code,
|
|
372
|
+
reason: event.reason,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// HACK: Close socket in order to fix bug with Cloudflare leaving WS in closing state
|
|
376
|
+
// https://github.com/cloudflare/workerd/issues/2569
|
|
377
|
+
serverWs.close(1000, "hack_force_close");
|
|
378
|
+
|
|
379
|
+
// Close the client websocket when the server websocket closes
|
|
380
|
+
if (
|
|
381
|
+
clientWs &&
|
|
382
|
+
clientWs.readyState !== clientWs.CLOSED &&
|
|
383
|
+
clientWs.readyState !== clientWs.CLOSING
|
|
384
|
+
) {
|
|
385
|
+
// Don't pass code/message since this may affect how close events are triggered
|
|
386
|
+
clientWs.close(1000, event.reason);
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
onError: (error: unknown) => {
|
|
390
|
+
logger().error({
|
|
391
|
+
msg: `error in server websocket`,
|
|
392
|
+
error,
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Close the client websocket on error
|
|
396
|
+
if (
|
|
397
|
+
clientWs &&
|
|
398
|
+
clientWs.readyState !== clientWs.CLOSED &&
|
|
399
|
+
clientWs.readyState !== clientWs.CLOSING
|
|
400
|
+
) {
|
|
401
|
+
clientWs.close(1011, "Error in server websocket");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
serverWsReject();
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
}
|