From e9fa7e19ee15abf2cc31147ced8ce18e10e6bd9b Mon Sep 17 00:00:00 2001 From: Yonle Date: Wed, 22 Nov 2023 00:20:33 +0700 Subject: [PATCH] introduce EOSE Timeout Signed-off-by: Yonle --- bouncer.js | 41 +++++++++++++++++++++++++++++++++++++++-- config.js.example | 4 ++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/bouncer.js b/bouncer.js index bdb5fc4..2186fcf 100644 --- a/bouncer.js +++ b/bouncer.js @@ -3,7 +3,7 @@ const { validateEvent, nip19 } = require("nostr-tools"); const auth = require("./auth.js"); const nip42 = require("./nip42.js"); -let { relays, tmp_store, log_about_relays, authorized_keys, private_keys, reconnect_time, wait_eose, pause_on_limit } = require("./config"); +let { relays, tmp_store, log_about_relays, authorized_keys, private_keys, reconnect_time, wait_eose, pause_on_limit, eose_timeout } = require("./config"); const socks = new Set(); const csess = new Map(); @@ -21,6 +21,7 @@ module.exports = (ws, req) => { ws.events = new Map(); // only to prevent the retransmit of the same event. per subID ws.my_events = new Set(); // for event retransmitting. ws.pendingEOSE = new Map(); // each contain subID + ws.EOSETimeout = new Map(); // per subID if (authorized_keys?.length) { authKey = Date.now() + Math.random().toString(36); @@ -75,6 +76,7 @@ module.exports = (ws, req) => { ws.events.delete(data[1]); ws.pendingEOSE.delete(data[1]); ws.pause_subs.delete(data[1]); + cancel_EOSETimeout(ws.id, data[1]); bc(data, ws.id); break; case "AUTH": @@ -97,6 +99,10 @@ module.exports = (ws, req) => { console.log(process.pid, "---", "Sock", ws.id, "has disconnected."); csess.delete(ws.id); + for (i of ws.EOSETimeout) { + clearTimeout(i[1]); + } + if (!authorized) return; terminate_subs(ws.id); }); @@ -105,6 +111,34 @@ module.exports = (ws, req) => { if (authorized) relays.forEach(_ => newConn(_, ws.id)); } +// CL - Set up EOSE timeout +function timeoutEOSE(id, subid) { + const c = csess.get(id); + if (!c) return; + + clearTimeout(c.EOSETimeout.get(subid)); + c.EOSETimeout.set(subid, setTimeout(_ => timed_out_eose(id, subid), eose_timeout || 2300)); +} + +// CL - Handle timed out EOSE +function timed_out_eose(id, subid) { + const c = csess.get(id); + if (!c) return; + c.EOSETimeout.delete(subid); + if (!c.pendingEOSE.has(subid)) return; + + c.pendingEOSE.delete(subid); + if (c.pause_subs.has(subid)) return c.pause_subs.delete(subid); + c.send(JSON.stringify(["EOSE", subid])); +} + +function cancel_EOSETimeout(id, subid) { + const c = csess.get(id); + if (!c) return; + clearTimeout(c.EOSETimeout.get(subid)); + c.EOSETimeout.delete(subid); +} + // WS - Broadcast message to every existing sockets function bc(msg, id) { for (sock of socks) { @@ -157,7 +191,9 @@ function newConn(addr, id) { switch (data[0]) { case "EVENT": { if (data.length < 3 || typeof(data[1]) !== "string" || typeof(data[2]) !== "object") return; - if (!client.subs.has(data[1]) || client.pause_subs.has(data[1])) return; + if (!client.subs.has(data[1])) return; + timeoutEOSE(id, data[1]); + if (client.pause_subs.has(data[1])) return; // if filter.since > receivedEvent.created_at, skip // if receivedEvent.created_at > filter.until, skip @@ -192,6 +228,7 @@ function newConn(addr, id) { client.pendingEOSE.set(data[1], client.pendingEOSE.get(data[1]) + 1); if (wait_eose && (client.pendingEOSE.get(data[1]) < Array.from(socks).filter(sock => sock.id === id).length)) return; client.pendingEOSE.delete(data[1]); + cancel_EOSETimeout(data[1]); if (client.pause_subs.has(data[1])) return client.pause_subs.delete(data[1]); client.send(JSON.stringify(data)); break; diff --git a/config.js.example b/config.js.example index 6c1d128..d4d6a6e 100644 --- a/config.js.example +++ b/config.js.example @@ -21,6 +21,7 @@ module.exports = { // // Depending on your configured relays, // It may could cause loading problems in client due to slow EOSE from bouncer. + // You could try fix this by changing value. wait_eose: false, // Pause an subscription from receiving further events after reached to @@ -29,6 +30,9 @@ module.exports = { // Depending on settings, It could either miss some events. pause_on_limit: true, + // EOSE timeout in miliseconds + eose_timeout: 2300, + // A whitelist of users public keys who could use this bouncer. // Leaving this empty will allows everyone to use this bouncer. // NOTE: - Require NIP-42 compatible nostr client