From 804f2d990a3698ccd63661ea464361b01e6516dc Mon Sep 17 00:00:00 2001 From: Alexander Zinn Date: Thu, 18 Sep 2025 17:49:07 -0400 Subject: [PATCH] bfcache: server - Implement WebSocket server functionality * Added WebSocketServer class with configurable options * Introduced WebSocketServerFactory for creating WebSocket relay servers * Updated index.ts to set up a server with WebSocket support and handle various signals for graceful shutdown --- Web/bfcache/server/src/index.ts | 37 ++++++++++++++++++- .../{ => net/websockets}/WebSocketServer.ts | 14 +++---- .../net/websockets/WebSocketServerFactory.ts | 37 +++++++++++++++++++ 3 files changed, 80 insertions(+), 8 deletions(-) rename Web/bfcache/server/src/{ => net/websockets}/WebSocketServer.ts (94%) create mode 100644 Web/bfcache/server/src/net/websockets/WebSocketServerFactory.ts diff --git a/Web/bfcache/server/src/index.ts b/Web/bfcache/server/src/index.ts index a6f6a0f..68e55dd 100644 --- a/Web/bfcache/server/src/index.ts +++ b/Web/bfcache/server/src/index.ts @@ -1 +1,36 @@ -console.log('hello claris'); +import {LoggerFactory} from '@techniker-me/logger'; +import WebSocketServerFactory from './net/websockets/WebSocketServerFactory'; + +LoggerFactory.setLoggingLevel('All'); + +const webSocketRelayServer = WebSocketServerFactory.createWebSocketRelayServer(); +const PORT = parseInt(process.env.PORT || '4444', 10); + +const server = Bun.serve({ + port: PORT, + fetch: (request, server) => { + if (server.upgrade(request)) { + return; + } + + return new Response('hi'); + }, + websocket: webSocketRelayServer +}); + +process.on('SIGINT', () => { + console.log('Received [SIGINT] stopping server'); + server.stop(); +}); + +process.on('SIGKILL', () => { + console.log('Received [SIGKILL] stopping server'); + server.stop(); +}); + +process.on('SIGTERM', () => { + console.log('Received [SIGTERM] stopping server'); + server.stop(); +}); + +console.log(`Server listening on [:${server.port}]`); diff --git a/Web/bfcache/server/src/WebSocketServer.ts b/Web/bfcache/server/src/net/websockets/WebSocketServer.ts similarity index 94% rename from Web/bfcache/server/src/WebSocketServer.ts rename to Web/bfcache/server/src/net/websockets/WebSocketServer.ts index 5488686..2467591 100644 --- a/Web/bfcache/server/src/WebSocketServer.ts +++ b/Web/bfcache/server/src/net/websockets/WebSocketServer.ts @@ -1,12 +1,12 @@ import type {ServerWebSocket, WebSocketCompressor} from 'bun'; -import type {Seconds, Bytes} from './types/Units'; +import type {Seconds, Bytes} from '../../types/Units'; export type PerMessageDeflate = -| boolean -| { - compress?: boolean | WebSocketCompressor; - decomporess?: boolean | WebSocketCompressor; -}; + | boolean + | { + compress?: boolean | WebSocketCompressor; + decomporess?: boolean | WebSocketCompressor; + }; export type WebSocketServerOptions = { maxPayloadLength?: Bytes; @@ -95,4 +95,4 @@ export default class WebSocketServer { get perMessageDeflate(): PerMessageDeflate { return this._perMessageDeflate; } -} \ No newline at end of file +} diff --git a/Web/bfcache/server/src/net/websockets/WebSocketServerFactory.ts b/Web/bfcache/server/src/net/websockets/WebSocketServerFactory.ts new file mode 100644 index 0000000..3b45d06 --- /dev/null +++ b/Web/bfcache/server/src/net/websockets/WebSocketServerFactory.ts @@ -0,0 +1,37 @@ +import {LoggerFactory} from '@techniker-me/logger'; +import WebSocketServer, {WebSocketServerOptions} from './WebSocketServer'; +import {ServerWebSocket} from 'bun'; + +export default class WebSocketServerFactory { + public static createWebSocketRelayServer(): WebSocketServer { + const logger = LoggerFactory.getLogger('WebSocketRelayServer'); + const clients = new Set(); + const webSocketRelayServerOptions: WebSocketServerOptions = { + onSocketError: (client, error) => logger.error(`Error: [%o] [${error.message}]`, client), + onSocketOpen: client => { + logger.debug('New WebSocketClient [%o]', client); + + clients.add(client); + }, + onSocketMessage: (fromClient, message) => { + logger.debug(`Relaying message [%o]`, message); + + for (const client of clients) { + if (client === fromClient) { + continue; + } + + client.send(message); + } + }, + onSocketClose: client => clients.delete(client), + onSocketDrain: client => logger.debug('Client drain [%o]', client), + publishToSelf: false + }; + + return new WebSocketServer(webSocketRelayServerOptions); + } + private constructor() { + throw new Error('WebSocketServerFactory is a static class that may not be instantiated'); + } +}