| import { createHash, randomUUID } from "node:crypto"; | |
| import { STATUS_CODES } from "node:http"; | |
| export const WS_PUBLIC_PATHS = new Set(["/v1/ws", "/api/v1/ws"]); | |
| export const WS_ALLOWED_ENDPOINTS = new Set([ | |
| "/v1/chat/completions", | |
| "/api/v1/chat/completions", | |
| "/v1/messages", | |
| "/api/v1/messages", | |
| "/v1/responses", | |
| "/api/v1/responses", | |
| "/v1/completions", | |
| "/api/v1/completions", | |
| ]); | |
| const HANDSHAKE_PATH = "/api/v1/ws"; | |
| const WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | |
| const WS_QUERY_TOKEN_KEYS = ["api_key", "token", "access_token"]; | |
| const textEncoder = new TextEncoder(); | |
| const textDecoder = new TextDecoder(); | |
| function isText(value) { | |
| return typeof value === "string" && value.length > 0; | |
| } | |
| function jsonStringifySafe(value) { | |
| try { | |
| return JSON.stringify(value); | |
| } catch { | |
| return JSON.stringify({ | |
| type: "protocol.error", | |
| code: "serialization_failed", | |
| message: "Failed to serialize WebSocket payload", | |
| }); | |
| } | |
| } | |
| function encodeWsFrame(opcode, payload = Buffer.alloc(0)) { | |
| const payloadBuffer = Buffer.isBuffer(payload) ? payload : Buffer.from(payload); | |
| const length = payloadBuffer.length; | |
| let header; | |
| if (length < 126) { | |
| header = Buffer.allocUnsafe(2); | |
| header[1] = length; | |
| } else if (length <= 0xffff) { | |
| header = Buffer.allocUnsafe(4); | |
| header[1] = 126; | |
| header.writeUInt16BE(length, 2); | |
| } else { | |
| header = Buffer.allocUnsafe(10); | |
| header[1] = 127; | |
| header.writeBigUInt64BE(BigInt(length), 2); | |
| } | |
| header[0] = 0x80 | (opcode & 0x0f); | |
| return Buffer.concat([header, payloadBuffer]); | |
| } | |
| function decodeClientFrames(buffer) { | |
| const frames = []; | |
| let offset = 0; | |
| while (buffer.length - offset >= 2) { | |
| const byte1 = buffer[offset]; | |
| const byte2 = buffer[offset + 1]; | |
| const fin = (byte1 & 0x80) !== 0; | |
| const opcode = byte1 & 0x0f; | |
| const masked = (byte2 & 0x80) !== 0; | |
| let payloadLength = byte2 & 0x7f; | |
| let headerLength = 2; | |
| if (!masked) { | |
| throw new Error("Client WebSocket frames must be masked"); | |
| } | |
| if (payloadLength === 126) { | |
| if (buffer.length - offset < 4) break; | |
| payloadLength = buffer.readUInt16BE(offset + 2); | |
| headerLength = 4; | |
| } else if (payloadLength === 127) { | |
| if (buffer.length - offset < 10) break; | |
| const bigLength = buffer.readBigUInt64BE(offset + 2); | |
| if (bigLength > BigInt(Number.MAX_SAFE_INTEGER)) { | |
| throw new Error("WebSocket payload too large"); | |
| } | |
| payloadLength = Number(bigLength); | |
| headerLength = 10; | |
| } | |
| const totalLength = headerLength + 4 + payloadLength; | |
| if (buffer.length - offset < totalLength) break; | |
| const mask = buffer.subarray(offset + headerLength, offset + headerLength + 4); | |
| const payload = Buffer.from(buffer.subarray(offset + headerLength + 4, offset + totalLength)); | |
| for (let index = 0; index < payload.length; index += 1) { | |
| payload[index] ^= mask[index % 4]; | |
| } | |
| frames.push({ fin, opcode, payload }); | |
| offset += totalLength; | |
| } | |
| return { | |
| frames, | |
| remaining: buffer.subarray(offset), | |
| }; | |
| } | |
| function writeHttpError(socket, status, body, headers = {}) { | |
| if (!socket.writable || socket.destroyed) return; | |
| const bodyBuffer = Buffer.from(body || "", "utf8"); | |
| const statusText = STATUS_CODES[status] || "Error"; | |
| const responseHeaders = { | |
| Connection: "close", | |
| "Content-Length": String(bodyBuffer.length), | |
| "Content-Type": "application/json; charset=utf-8", | |
| ...headers, | |
| }; | |
| const head = [ | |
| `HTTP/1.1 ${status} ${statusText}`, | |
| ...Object.entries(responseHeaders).map(([name, value]) => `${name}: ${value}`), | |
| "", | |
| "", | |
| ].join("\r\n"); | |
| socket.write(head); | |
| socket.end(bodyBuffer); | |
| } | |
| function isWsPath(pathname) { | |
| return WS_PUBLIC_PATHS.has(pathname); | |
| } | |
| function normalizeEndpoint(rawEndpoint) { | |
| const endpoint = isText(rawEndpoint) ? rawEndpoint : "/v1/chat/completions"; | |
| let parsed; | |
| try { | |
| parsed = new URL(endpoint, "http://omniroute.local"); | |
| } catch { | |
| return null; | |
| } | |
| if (parsed.origin !== "http://omniroute.local") { | |
| return null; | |
| } | |
| if (!WS_ALLOWED_ENDPOINTS.has(parsed.pathname)) { | |
| return null; | |
| } | |
| return `${parsed.pathname}${parsed.search}`; | |
| } | |
| function getForwardHeaders(requestUrl, requestHeaders) { | |
| const headers = { | |
| accept: "text/event-stream", | |
| "content-type": "application/json", | |
| }; | |
| const authorization = requestHeaders.authorization; | |
| if (isText(authorization)) { | |
| headers.authorization = authorization; | |
| } else { | |
| const url = new URL(requestUrl, "http://omniroute.local"); | |
| for (const key of WS_QUERY_TOKEN_KEYS) { | |
| const value = url.searchParams.get(key); | |
| if (isText(value)) { | |
| headers.authorization = `Bearer ${value.trim()}`; | |
| break; | |
| } | |
| } | |
| } | |
| const cookie = requestHeaders.cookie; | |
| if (isText(cookie)) { | |
| headers.cookie = cookie; | |
| } | |
| const origin = requestHeaders.origin; | |
| if (isText(origin)) { | |
| headers.origin = origin; | |
| } | |
| return headers; | |
| } | |
| async function performHandshake(fetchImpl, baseUrl, requestUrl, requestHeaders) { | |
| const incomingUrl = new URL(requestUrl, baseUrl); | |
| const handshakeUrl = new URL(HANDSHAKE_PATH, baseUrl); | |
| for (const [key, value] of incomingUrl.searchParams.entries()) { | |
| handshakeUrl.searchParams.set(key, value); | |
| } | |
| handshakeUrl.searchParams.set("handshake", "1"); | |
| const response = await fetchImpl(handshakeUrl, { | |
| method: "GET", | |
| headers: { | |
| authorization: requestHeaders.authorization || "", | |
| cookie: requestHeaders.cookie || "", | |
| origin: requestHeaders.origin || "", | |
| "x-forwarded-for": requestHeaders["x-forwarded-for"] || "", | |
| }, | |
| }); | |
| const bodyText = await response.text(); | |
| let bodyJson = null; | |
| try { | |
| bodyJson = bodyText ? JSON.parse(bodyText) : null; | |
| } catch { | |
| bodyJson = null; | |
| } | |
| return { | |
| status: response.status, | |
| headers: Object.fromEntries(response.headers.entries()), | |
| bodyText, | |
| bodyJson, | |
| ok: response.ok, | |
| }; | |
| } | |
| class WebSocketSession { | |
| constructor(options) { | |
| this.baseUrl = options.baseUrl; | |
| this.fetchImpl = options.fetchImpl; | |
| this.idleTimeoutMs = options.idleTimeoutMs; | |
| this.pingIntervalMs = options.pingIntervalMs; | |
| this.socket = options.socket; | |
| this.requestHeaders = options.requestHeaders; | |
| this.requestUrl = options.requestUrl; | |
| this.sessionId = randomUUID(); | |
| this.closed = false; | |
| this.buffer = Buffer.alloc(0); | |
| this.fragmentOpcode = null; | |
| this.fragmentParts = []; | |
| this.activeRequests = new Map(); | |
| this.lastSeenAt = Date.now(); | |
| this.pingTimer = setInterval(() => { | |
| if (this.closed) return; | |
| const idleForMs = Date.now() - this.lastSeenAt; | |
| if (idleForMs >= this.idleTimeoutMs) { | |
| this.close(1001, "idle_timeout"); | |
| return; | |
| } | |
| this.sendFrame(0x9); | |
| }, this.pingIntervalMs); | |
| this.socket.setNoDelay(true); | |
| this.socket.on("data", (chunk) => { | |
| this.onData(chunk).catch((error) => { | |
| this.sendProtocolError( | |
| "frame_decode_failed", | |
| error instanceof Error ? error.message : String(error) | |
| ); | |
| }); | |
| }); | |
| this.socket.on("close", () => this.dispose()); | |
| this.socket.on("end", () => this.dispose()); | |
| this.socket.on("error", () => this.dispose()); | |
| } | |
| sendFrame(opcode, payload) { | |
| if (this.closed || this.socket.destroyed) return; | |
| this.socket.write(encodeWsFrame(opcode, payload)); | |
| } | |
| sendJson(payload) { | |
| this.sendFrame(0x1, Buffer.from(jsonStringifySafe(payload), "utf8")); | |
| } | |
| sendProtocolError(code, message, id = null) { | |
| this.sendJson({ | |
| type: "protocol.error", | |
| code, | |
| id, | |
| message, | |
| }); | |
| } | |
| async onData(chunk) { | |
| this.lastSeenAt = Date.now(); | |
| this.buffer = Buffer.concat([this.buffer, chunk]); | |
| const parsed = decodeClientFrames(this.buffer); | |
| this.buffer = parsed.remaining; | |
| for (const frame of parsed.frames) { | |
| await this.handleFrame(frame); | |
| } | |
| } | |
| async handleFrame(frame) { | |
| switch (frame.opcode) { | |
| case 0x0: | |
| if (this.fragmentOpcode === null) { | |
| this.sendProtocolError("unexpected_continuation", "Unexpected continuation frame"); | |
| return; | |
| } | |
| this.fragmentParts.push(frame.payload); | |
| if (frame.fin) { | |
| const payload = Buffer.concat(this.fragmentParts); | |
| const opcode = this.fragmentOpcode; | |
| this.fragmentOpcode = null; | |
| this.fragmentParts = []; | |
| await this.handleDataFrame(opcode, payload); | |
| } | |
| return; | |
| case 0x1: | |
| case 0x2: | |
| if (!frame.fin) { | |
| this.fragmentOpcode = frame.opcode; | |
| this.fragmentParts = [frame.payload]; | |
| return; | |
| } | |
| await this.handleDataFrame(frame.opcode, frame.payload); | |
| return; | |
| case 0x8: | |
| this.close(); | |
| return; | |
| case 0x9: | |
| this.sendFrame(0xa, frame.payload); | |
| return; | |
| case 0xa: | |
| this.lastSeenAt = Date.now(); | |
| return; | |
| default: | |
| this.sendProtocolError("unsupported_opcode", `Unsupported opcode ${frame.opcode}`); | |
| } | |
| } | |
| async handleDataFrame(opcode, payload) { | |
| if (opcode !== 0x1) { | |
| this.sendProtocolError("unsupported_payload", "Only UTF-8 text messages are supported"); | |
| return; | |
| } | |
| const raw = textDecoder.decode(payload); | |
| let message; | |
| try { | |
| message = JSON.parse(raw); | |
| } catch { | |
| this.sendProtocolError("invalid_json", "WebSocket message must be valid JSON"); | |
| return; | |
| } | |
| await this.handleMessage(message); | |
| } | |
| async handleMessage(message) { | |
| if (!message || typeof message !== "object" || Array.isArray(message)) { | |
| this.sendProtocolError("invalid_envelope", "WebSocket message must be an object"); | |
| return; | |
| } | |
| if (message.type === "ping") { | |
| this.sendJson({ type: "pong", sessionId: this.sessionId }); | |
| return; | |
| } | |
| if (message.type === "cancel") { | |
| const requestId = isText(message.id) ? message.id : null; | |
| if (!requestId) { | |
| this.sendProtocolError("invalid_cancel", "cancel envelopes require a string id"); | |
| return; | |
| } | |
| const active = this.activeRequests.get(requestId); | |
| if (!active) { | |
| this.sendProtocolError( | |
| "unknown_request", | |
| "No active request matches the provided id", | |
| requestId | |
| ); | |
| return; | |
| } | |
| active.abortController.abort(); | |
| return; | |
| } | |
| if (message.type !== "request") { | |
| this.sendProtocolError( | |
| "unsupported_type", | |
| "Supported message types are request, cancel, and ping" | |
| ); | |
| return; | |
| } | |
| const requestId = isText(message.id) ? message.id : null; | |
| if (!requestId) { | |
| this.sendProtocolError("invalid_request_id", "request envelopes require a non-empty id"); | |
| return; | |
| } | |
| if (this.activeRequests.has(requestId)) { | |
| this.sendProtocolError( | |
| "duplicate_request", | |
| "A request with this id is already in flight", | |
| requestId | |
| ); | |
| return; | |
| } | |
| if (!message.payload || typeof message.payload !== "object" || Array.isArray(message.payload)) { | |
| this.sendProtocolError( | |
| "invalid_payload", | |
| "request envelopes require an object payload", | |
| requestId | |
| ); | |
| return; | |
| } | |
| const endpoint = normalizeEndpoint(message.endpoint); | |
| if (!endpoint) { | |
| this.sendProtocolError( | |
| "invalid_endpoint", | |
| "Endpoint must target a supported /v1 chat surface", | |
| requestId | |
| ); | |
| return; | |
| } | |
| const requestPayload = { | |
| ...message.payload, | |
| stream: message.payload.stream === undefined ? true : message.payload.stream, | |
| }; | |
| const abortController = new AbortController(); | |
| this.activeRequests.set(requestId, { abortController }); | |
| this.executeRequest(requestId, endpoint, requestPayload, abortController).catch((error) => { | |
| this.sendJson({ | |
| type: abortController.signal.aborted ? "response.cancelled" : "response.error", | |
| id: requestId, | |
| code: abortController.signal.aborted ? "client_cancelled" : "request_failed", | |
| message: error instanceof Error ? error.message : String(error), | |
| }); | |
| this.activeRequests.delete(requestId); | |
| }); | |
| } | |
| async executeRequest(requestId, endpoint, payload, abortController) { | |
| const headers = { | |
| ...this.requestHeaders, | |
| accept: payload.stream === false ? "application/json" : "text/event-stream", | |
| "content-type": "application/json", | |
| "x-omniroute-ws-session-id": this.sessionId, | |
| "x-omniroute-ws-request-id": requestId, | |
| }; | |
| const response = await this.fetchImpl(new URL(endpoint, this.baseUrl), { | |
| method: "POST", | |
| headers, | |
| body: JSON.stringify(payload), | |
| signal: abortController.signal, | |
| }); | |
| const contentType = response.headers.get("content-type") || ""; | |
| this.sendJson({ | |
| type: "response.start", | |
| id: requestId, | |
| status: response.status, | |
| ok: response.ok, | |
| contentType, | |
| endpoint, | |
| }); | |
| if (contentType.includes("text/event-stream") && response.body) { | |
| const reader = response.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| try { | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| const chunk = decoder.decode(value, { stream: true }); | |
| if (chunk) { | |
| this.sendJson({ | |
| type: "response.chunk", | |
| id: requestId, | |
| chunk, | |
| }); | |
| } | |
| } | |
| const tail = decoder.decode(); | |
| if (tail) { | |
| this.sendJson({ | |
| type: "response.chunk", | |
| id: requestId, | |
| chunk: tail, | |
| }); | |
| } | |
| } finally { | |
| this.activeRequests.delete(requestId); | |
| } | |
| this.sendJson({ | |
| type: response.ok ? "response.completed" : "response.error", | |
| id: requestId, | |
| status: response.status, | |
| ok: response.ok, | |
| }); | |
| return; | |
| } | |
| const bodyText = await response.text(); | |
| let body = bodyText; | |
| try { | |
| body = bodyText ? JSON.parse(bodyText) : null; | |
| } catch { | |
| body = bodyText; | |
| } | |
| this.activeRequests.delete(requestId); | |
| this.sendJson({ | |
| type: response.ok ? "response.output" : "response.error", | |
| id: requestId, | |
| status: response.status, | |
| ok: response.ok, | |
| body, | |
| }); | |
| this.sendJson({ | |
| type: "response.completed", | |
| id: requestId, | |
| status: response.status, | |
| ok: response.ok, | |
| }); | |
| } | |
| close(code = 1000, reason = "normal_closure") { | |
| if (this.closed) return; | |
| this.closed = true; | |
| clearInterval(this.pingTimer); | |
| for (const active of this.activeRequests.values()) { | |
| active.abortController.abort(); | |
| } | |
| this.activeRequests.clear(); | |
| const reasonBuffer = Buffer.from(reason, "utf8"); | |
| const payload = Buffer.allocUnsafe(2 + reasonBuffer.length); | |
| payload.writeUInt16BE(code, 0); | |
| reasonBuffer.copy(payload, 2); | |
| this.sendFrame(0x8, payload); | |
| this.socket.end(); | |
| setTimeout(() => { | |
| if (!this.socket.destroyed) { | |
| this.socket.destroy(); | |
| } | |
| }, 50).unref?.(); | |
| } | |
| dispose() { | |
| if (this.closed) return; | |
| this.closed = true; | |
| clearInterval(this.pingTimer); | |
| for (const active of this.activeRequests.values()) { | |
| active.abortController.abort(); | |
| } | |
| this.activeRequests.clear(); | |
| } | |
| } | |
| export function createOmnirouteWsBridge({ | |
| baseUrl, | |
| fetchImpl = fetch, | |
| pingIntervalMs = 25000, | |
| idleTimeoutMs = 90000, | |
| } = {}) { | |
| if (!isText(baseUrl)) { | |
| throw new Error("createOmnirouteWsBridge requires a baseUrl"); | |
| } | |
| return { | |
| isWsPath, | |
| async handleUpgrade(req, socket, head) { | |
| const pathname = new URL(req.url || "/", baseUrl).pathname; | |
| if (!isWsPath(pathname)) { | |
| return false; | |
| } | |
| const upgradeHeader = String(req.headers.upgrade || "").toLowerCase(); | |
| if (upgradeHeader !== "websocket") { | |
| writeHttpError( | |
| socket, | |
| 426, | |
| JSON.stringify({ | |
| error: { | |
| message: "Upgrade Required", | |
| code: "upgrade_required", | |
| }, | |
| }), | |
| { Upgrade: "websocket" } | |
| ); | |
| return true; | |
| } | |
| try { | |
| const handshake = await performHandshake(fetchImpl, baseUrl, req.url || "/", req.headers); | |
| if (!handshake.ok) { | |
| writeHttpError(socket, handshake.status, handshake.bodyText || "{}", handshake.headers); | |
| return true; | |
| } | |
| const wsKey = req.headers["sec-websocket-key"]; | |
| if (!isText(wsKey)) { | |
| writeHttpError( | |
| socket, | |
| 400, | |
| JSON.stringify({ | |
| error: { | |
| message: "Missing sec-websocket-key header", | |
| code: "bad_websocket_handshake", | |
| }, | |
| }) | |
| ); | |
| return true; | |
| } | |
| const acceptKey = createHash("sha1").update(`${wsKey}${WS_GUID}`).digest("base64"); | |
| const headers = [ | |
| "HTTP/1.1 101 Switching Protocols", | |
| "Upgrade: websocket", | |
| "Connection: Upgrade", | |
| `Sec-WebSocket-Accept: ${acceptKey}`, | |
| "", | |
| "", | |
| ].join("\r\n"); | |
| socket.write(headers); | |
| if (head && head.length > 0) { | |
| socket.unshift(head); | |
| } | |
| const session = new WebSocketSession({ | |
| baseUrl, | |
| fetchImpl, | |
| idleTimeoutMs, | |
| pingIntervalMs, | |
| socket, | |
| requestUrl: req.url || pathname, | |
| requestHeaders: getForwardHeaders(req.url || pathname, req.headers), | |
| }); | |
| session.sendJson({ | |
| type: "session.ready", | |
| sessionId: session.sessionId, | |
| path: handshake.bodyJson?.path || pathname, | |
| wsAuth: handshake.bodyJson?.wsAuth === true, | |
| authenticated: handshake.bodyJson?.authenticated === true, | |
| authType: handshake.bodyJson?.authType || "none", | |
| }); | |
| return true; | |
| } catch (error) { | |
| writeHttpError( | |
| socket, | |
| 500, | |
| JSON.stringify({ | |
| error: { | |
| message: error instanceof Error ? error.message : String(error), | |
| code: "websocket_bridge_failed", | |
| }, | |
| }) | |
| ); | |
| return true; | |
| } | |
| }, | |
| }; | |
| } | |