diff --git a/backend/index.js b/backend/index.js index 8a66e5f..247cfca 100644 --- a/backend/index.js +++ b/backend/index.js @@ -28,9 +28,52 @@ const get_siteroot_html = function ({ page_title }) {return ` `.trim()} const start_server = function ({ context_meta, config, jsbuild_app_frontend }) { + const make_session_object = (ws) => { + const map_obj = { + ws, + closed_by_server: false, + id_interval_ping: null, + id_timeout_checkpong: null, + } + const func_check_pong = () => { + //console.debug('CHECK PONG') + // if this check/timeout was not cancelled yet, indicates it's been "too long" (so, terminate ws) + const terminate_session = () => { + const code = 1008 + const reason = 'Policy Violation' + map_obj.closed_by_server = true + map_obj.ws.close(code, reason) + // cleanup here + context_meta.ws_map.delete(ws.data.uid) + return + } + terminate_session() + // reset this for future ref + map_obj.id_timeout_checkpong = null + return + } + const func_periodic_ping = () => { + //console.debug('PING') + ws.ping() + if (config.pong_timeout !== null) { + if (map_obj.id_timeout_checkpong === null) { + map_obj.id_timeout_checkpong = setTimeout(func_check_pong, config.pong_timeout) + } + else { + // avoid double-schedule of this check + console.warn('Unexpected: setTimeout for func_check_pong already scheduled') + } + } + return + } + if (config.ping_interval !== null) { + map_obj.id_interval_ping = setInterval(func_periodic_ping, config.ping_interval) + } + return map_obj + } const server = Bun.serve({ - hostname: config.host || '127.0.0.1', - port : config.port || 8800, + hostname: config.hostname, + port : config.port, async fetch (req, server) { const url = new URL(req.url) let resp @@ -70,10 +113,8 @@ const start_server = function ({ context_meta, config, jsbuild_app_frontend }) { }, websocket: { open: function (ws) { - const map_obj = { - ws, - closed_by_server: false, - } + console.info(['ws open', ws.data.uid]) + const map_obj = make_session_object(ws) context_meta.ws_map.set(ws.data.uid, map_obj) return }, @@ -83,6 +124,14 @@ const start_server = function ({ context_meta, config, jsbuild_app_frontend }) { console.warn(['Unexpected: ws_map missing uid', ws.data.uid]) } else { + // stop ping-ing + if (map_obj.id_interval_ping !== null) { + clearInterval(map_obj.id_interval_ping) + } + // stop pong-check-ing + if (map_obj.id_timeout_checkpong !== null) { + clearTimeout(map_obj.id_timeout_checkpong) + } if (map_obj.closed_by_server === true) { console.info(['ws closed by server', ws.data.uid]) // cleanup will happen in outer context @@ -96,6 +145,22 @@ const start_server = function ({ context_meta, config, jsbuild_app_frontend }) { return }, message: function (ws, message) {}, + pong: function (ws, data) { + //console.debug(['ws pong', ws.data.uid]) + const map_obj = context_meta.ws_map.get(ws.data.uid) + if (map_obj === undefined) { + console.warn(['Unexpected: ws_map missing uid', ws.data.uid]) + } + else { + // cancel pending check + if (map_obj.id_timeout_checkpong !== null) { + clearTimeout(map_obj.id_timeout_checkpong) + // reset this for future ref + map_obj.id_timeout_checkpong = null + } + } + return + }, }, }) return server @@ -120,10 +185,19 @@ const async_run = async function ({ config, jsbuild_app_frontend, }) { + const active_config = { + // defaults + hostname: '127.0.0.1', + port: 8800, + ping_interval: 1000 * 15, + pong_timeout : 1000 * 5, + // overrides + ...config, + } const context_meta = { ws_map: new Map(), } - const server = start_server({ context_meta, config, jsbuild_app_frontend }) + const server = start_server({ context_meta, config: active_config, jsbuild_app_frontend }) console.info(server) process.on('SIGINT', async () => { console.log('SIGINT intercepted')