@rivetkit/redis
Version:
_Lightweight Libraries for Backends_
148 lines (134 loc) • 4.09 kB
text/typescript
import { logger } from "../../log";
import type { GlobalState } from "../../types";
import type {
ToFollowerWebSocketClose,
ToFollowerWebSocketMessage,
ToFollowerWebSocketOpen,
} from "../protocol";
export async function handleFollowerWebSocketOpen(
globalState: GlobalState,
open: ToFollowerWebSocketOpen,
) {
logger().debug("handling follower websocket open", {
websocketId: open.wi,
hasRelayWebSockets: !!globalState.relayWebSockets,
relayWebSocketsSize: globalState.relayWebSockets?.size ?? 0,
hasFollowerWebSockets: !!globalState.followerWebSockets,
followerWebSocketsSize: globalState.followerWebSockets?.size ?? 0,
});
// Check for relay WebSocket adapter
const relayWs = globalState.relayWebSockets?.get(open.wi);
if (relayWs) {
logger().debug("calling _handleOpen on relay websocket", {
websocketId: open.wi,
});
relayWs._handleOpen();
return;
}
// Check for follower WebSocket (proxyWebSocket)
const followerWs = globalState.followerWebSockets?.get(open.wi);
if (followerWs) {
// For proxy websockets, the client WebSocket is already open
// This message confirms the actor-side WebSocket is also ready
logger().debug("follower websocket open confirmed by leader", {
websocketId: open.wi,
});
return;
}
logger().warn("received websocket open for nonexistent follower websocket", {
websocketId: open.wi,
allRelayWebSocketIds: Array.from(globalState.relayWebSockets?.keys() ?? []),
allFollowerWebSocketIds: Array.from(
globalState.followerWebSockets?.keys() ?? [],
),
});
}
export async function handleFollowerWebSocketMessage(
globalState: GlobalState,
message: ToFollowerWebSocketMessage,
) {
// Check for raw WebSocket first
const ws = globalState.rawWebSockets.get(message.wi);
if (ws) {
// Forward message - handle binary data properly
if (message.data instanceof Uint8Array) {
// Convert Uint8Array to ArrayBuffer for WebSocket
ws.send(
message.data.buffer.slice(
message.data.byteOffset,
message.data.byteOffset + message.data.byteLength,
),
);
} else {
ws.send(message.data);
}
return;
}
// Check for relay WebSocket adapter
const relayWs = globalState.relayWebSockets?.get(message.wi);
if (relayWs) {
relayWs._handleMessage(message.data, message.binary);
return;
}
// Check for follower WebSocket (proxyWebSocket)
const followerWs = globalState.followerWebSockets?.get(message.wi);
if (followerWs) {
logger().debug("forwarding message to follower websocket", {
websocketId: message.wi,
isBinary: message.binary,
dataType: typeof message.data,
dataLength:
typeof message.data === "string"
? message.data.length
: message.data.byteLength,
});
// Handle binary data properly
if (message.data instanceof Uint8Array) {
// Convert Uint8Array to ArrayBuffer for WebSocket
followerWs.ws.send(
message.data.buffer.slice(
message.data.byteOffset,
message.data.byteOffset + message.data.byteLength,
),
);
} else {
followerWs.ws.send(message.data);
}
return;
}
logger().warn(
"received websocket message for nonexistent follower websocket",
{
websocketId: message.wi,
},
);
}
export async function handleFollowerWebSocketClose(
globalState: GlobalState,
close: ToFollowerWebSocketClose,
) {
// Check for raw WebSocket first
const ws = globalState.rawWebSockets.get(close.wi);
if (ws) {
globalState.rawWebSockets.delete(close.wi);
ws.close(close.code, close.reason);
return;
}
// Check for relay WebSocket adapter
const relayWs = globalState.relayWebSockets?.get(close.wi);
if (relayWs) {
relayWs._handleClose(close.code, close.reason);
globalState.relayWebSockets.delete(close.wi);
return;
}
// Check for follower WebSocket (proxyWebSocket)
const followerWs = globalState.followerWebSockets?.get(close.wi);
if (followerWs) {
followerWs.ws.close(close.code, close.reason);
globalState.followerWebSockets.delete(close.wi);
return;
}
logger().warn("received websocket close for nonexistent follower websocket", {
websocketId: close.wi,
});
}