Software /
code /
prosody
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 |