From 4ff9afe599dd97bace235000685874e12d1949d7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 10 Apr 2018 20:34:29 +0100 Subject: mod_bosh: Improve connection robustness with better handling of unexpected rids --- plugins/mod_bosh.lua | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 082a24ed..5ed742fc 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -20,6 +20,7 @@ local tostring, type = tostring, type; local traceback = debug.traceback; local runner = require"util.async".runner; local nameprep = require "util.encodings".stringprep.nameprep; +local cache = require "util.cache"; local xmlns_streams = "http://etherx.jabber.org/streams"; local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; @@ -250,14 +251,14 @@ local runner_callbacks = { }; -- Handle the tag in the request payload. function stream_callbacks.streamopened(context, attr) local request, response = context.request, context.response; - local sid = attr.sid; + local sid, rid = attr.sid, tonumber(attr.rid); log("debug", "BOSH body open (sid: %s)", sid or ""); + context.rid = rid; if not sid then -- New session request context.notopen = nil; -- Signals that we accept this opening tag local to_host = nameprep(attr.to); - local rid = tonumber(attr.rid); local wait = tonumber(attr.wait); if not to_host then log("debug", "BOSH client tried to connect to invalid host: %s", tostring(attr.to)); @@ -278,15 +279,15 @@ function stream_callbacks.streamopened(context, attr) return; end - rid = rid - 1; wait = math_min(wait, bosh_max_wait); -- New session sid = new_uuid(); local session = { - type = "c2s_unauthed", conn = request.conn, sid = sid, rid = rid, host = attr.to, + type = "c2s_unauthed", conn = request.conn, sid = sid, host = attr.to, + rid = rid - 1, -- Hack for initial session setup, "previous" rid was $current_request - 1 bosh_version = attr.ver, bosh_wait = wait, streamid = sid, - bosh_max_inactive = bosh_max_inactivity, + bosh_max_inactive = bosh_max_inactivity, bosh_responses = cache.new(BOSH_HOLD):table(); requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream, close = bosh_close_stream, dispatch_stanza = core_process_stanza, notopen = true, log = logger.init("bosh"..sid), secure = consider_bosh_secure or request.secure, @@ -342,8 +343,9 @@ function stream_callbacks.streamopened(context, attr) body_attr["xmlns:xmpp"] = "urn:xmpp:xbosh"; body_attr["xmpp:version"] = "1.0"; end - session.bosh_last_response = st.stanza("body", body_attr):top_tag()..t_concat(session.send_buffer)..""; - oldest_request:send(session.bosh_last_response); + local response_xml = st.stanza("body", body_attr):top_tag()..t_concat(session.send_buffer)..""; + session.bosh_responses[oldest_request.context.rid] = response_xml; + oldest_request:send(response_xml); session.send_buffer = {}; end return true; @@ -363,9 +365,9 @@ function stream_callbacks.streamopened(context, attr) session.conn = request.conn; if session.rid then - local rid = tonumber(attr.rid); local diff = rid - session.rid; -- Diff should be 1 for a healthy request + session.log("debug", "rid: %d, sess: %s, diff: %d", rid, session.rid, diff) if diff ~= 1 then context.sid = sid; context.notopen = nil; @@ -379,10 +381,15 @@ function stream_callbacks.streamopened(context, attr) -- Set a marker to indicate that stanzas in this request should NOT be processed -- (these stanzas will already be in the XML parser's buffer) context.ignore = true; - if diff == 0 then - -- Re-send previous response, ignore stanzas in this request - session.log("debug", "rid repeated, ignoring: %s (diff %d)", session.rid, diff); - response:send(session.bosh_last_response); + if session.bosh_responses[rid] then + -- Re-send past response, ignore stanzas in this request + session.log("debug", "rid repeated within window, replaying old response"); + response:send(session.bosh_responses[rid]); + return; + elseif diff == 0 then + session.log("debug", "current rid repeated, ignoring stanzas"); + t_insert(session.requests, response); + context.sid = sid; return; end -- Session broken, destroy it -- cgit v1.2.3