Comparison

plugins/mod_smacks.lua @ 12051:c32ef09ab452

mod_smacks: Persist old counter values to storage This allows clients that try to resume a session after a server restart to at least know which of their pending outgoing stanzas were received and which need to be re-sent. This removes the limit on how many of those counters are kept, which should be fixed eventually.
author Kim Alvefur <zash@zash.se>
date Wed, 01 Dec 2021 16:41:10 +0100
parent 12050:7b87a1747eb2
child 12052:d17b8fcf11c7
comparison
equal deleted inserted replaced
12050:7b87a1747eb2 12051:c32ef09ab452
43 local s2s_resend = module:get_option_boolean("smacks_s2s_resend", false); 43 local s2s_resend = module:get_option_boolean("smacks_s2s_resend", false);
44 local max_unacked_stanzas = module:get_option_number("smacks_max_unacked_stanzas", 0); 44 local max_unacked_stanzas = module:get_option_number("smacks_max_unacked_stanzas", 0);
45 local max_inactive_unacked_stanzas = module:get_option_number("smacks_max_inactive_unacked_stanzas", 256); 45 local max_inactive_unacked_stanzas = module:get_option_number("smacks_max_inactive_unacked_stanzas", 256);
46 local delayed_ack_timeout = module:get_option_number("smacks_max_ack_delay", 30); 46 local delayed_ack_timeout = module:get_option_number("smacks_max_ack_delay", 30);
47 local max_hibernated_sessions = module:get_option_number("smacks_max_hibernated_sessions", 10); 47 local max_hibernated_sessions = module:get_option_number("smacks_max_hibernated_sessions", 10);
48 local max_old_sessions = module:get_option_number("smacks_max_old_sessions", 10);
49 48
50 assert(max_hibernated_sessions > 0, "smacks_max_hibernated_sessions must be greater than 0"); 49 assert(max_hibernated_sessions > 0, "smacks_max_hibernated_sessions must be greater than 0");
51 assert(max_old_sessions > 0, "smacks_max_old_sessions must be greater than 0");
52 50
53 local c2s_sessions = module:shared("/*/c2s/sessions"); 51 local c2s_sessions = module:shared("/*/c2s/sessions");
54 52
55 local function format_h(h) if h then return string.format("%d", h) end end 53 local function format_h(h) if h then return string.format("%d", h) end end
56 54
74 -- remove empty caches completely 72 -- remove empty caches completely
75 if stores[user]:count() == 0 then stores[user] = nil; end 73 if stores[user]:count() == 0 then stores[user] = nil; end
76 end; 74 end;
77 }; 75 };
78 end 76 end
79 local old_session_registry = init_session_cache(max_old_sessions, nil); 77 local old_session_registry = module:open_store("smacks_h", "map");
80 local session_registry = init_session_cache(max_hibernated_sessions, function(resumption_token, session) 78 local session_registry = init_session_cache(max_hibernated_sessions, function(resumption_token, session)
81 if session.destroyed then return true; end -- destroyed session can always be removed from cache 79 if session.destroyed then return true; end -- destroyed session can always be removed from cache
82 session.log("warn", "User has too much hibernated sessions, removing oldest session (token: %s)", resumption_token); 80 session.log("warn", "User has too much hibernated sessions, removing oldest session (token: %s)", resumption_token);
83 -- store old session's h values on force delete 81 -- store old session's h values on force delete
84 -- save only actual h value and username/host (for security) 82 -- save only actual h value and username/host (for security)
85 old_session_registry.set(session.username, resumption_token, { 83 old_session_registry:set(session.username, resumption_token, {
86 h = session.handled_stanza_count, 84 h = session.handled_stanza_count,
87 }); 85 });
88 return true; -- allow session to be removed from full cache to make room for new one 86 return true; -- allow session to be removed from full cache to make room for new one
89 end); 87 end);
90 88
246 244
247 module:hook("pre-session-close", function(event) 245 module:hook("pre-session-close", function(event)
248 local session = event.session; 246 local session = event.session;
249 if session.resumption_token then 247 if session.resumption_token then
250 session_registry.set(session.username, session.resumption_token, nil); 248 session_registry.set(session.username, session.resumption_token, nil);
251 old_session_registry.set(session.username, session.resumption_token, nil); 249 old_session_registry:set(session.username, session.resumption_token, nil);
252 session.resumption_token = nil; 250 session.resumption_token = nil;
253 end 251 end
254 -- send out last ack as per revision 1.5.2 of XEP-0198 252 -- send out last ack as per revision 1.5.2 of XEP-0198
255 if session.smacks and session.conn and session.handled_stanza_count then 253 if session.smacks and session.conn and session.handled_stanza_count then
256 (session.sends2s or session.send)(st.stanza("a", { 254 (session.sends2s or session.send)(st.stanza("a", {
491 return resume_timeout-(current_time-timeout_start); -- time left to wait 489 return resume_timeout-(current_time-timeout_start); -- time left to wait
492 end 490 end
493 session.log("debug", "Destroying session for hibernating too long"); 491 session.log("debug", "Destroying session for hibernating too long");
494 session_registry.set(session.username, session.resumption_token, nil); 492 session_registry.set(session.username, session.resumption_token, nil);
495 -- save only actual h value and username/host (for security) 493 -- save only actual h value and username/host (for security)
496 old_session_registry.set(session.username, session.resumption_token, { 494 old_session_registry:set(session.username, session.resumption_token, {
497 h = session.handled_stanza_count, 495 h = session.handled_stanza_count,
498 }); 496 });
499 session.resumption_token = nil; 497 session.resumption_token = nil;
500 sessionmanager.destroy_session(session, "Hibernating too long"); 498 sessionmanager.destroy_session(session, "Hibernating too long");
501 else 499 else
544 542
545 local id = stanza.attr.previd; 543 local id = stanza.attr.previd;
546 local original_session = session_registry.get(session.username, id); 544 local original_session = session_registry.get(session.username, id);
547 if not original_session then 545 if not original_session then
548 session.log("debug", "Tried to resume non-existent session with id %s", id); 546 session.log("debug", "Tried to resume non-existent session with id %s", id);
549 local old_session = old_session_registry.get(session.username, id); 547 local old_session = old_session_registry:get(session.username, id);
550 if old_session then 548 if old_session then
551 session.send(st.stanza("failed", { xmlns = xmlns_sm, h = format_h(old_session.h) }) 549 session.send(st.stanza("failed", { xmlns = xmlns_sm, h = format_h(old_session.h) })
552 :tag("item-not-found", { xmlns = xmlns_errors }) 550 :tag("item-not-found", { xmlns = xmlns_errors })
553 ); 551 );
554 else 552 else