local log = logger.init("mod_bosh");
local initialize_filters = require "util.filters".initialize;
local math_min = math.min;
+local xpcall, tostring, type = xpcall, tostring, type;
+local traceback = debug.traceback;
local xmlns_streams = "http://etherx.jabber.org/streams";
local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams";
break;
end
end
-
+
-- If this session now has no requests open, mark it as inactive
local max_inactive = session.bosh_max_inactive;
if max_inactive and #requests == 0 then
if cross_domain and event.request.headers.origin then
set_cross_domain_headers(response);
end
-
+
-- stream:feed() calls the stream_callbacks, so all stanzas in
-- the body are processed in this next line before it returns.
-- In particular, the streamopened() stream callback is where
module:log("warn", "Error parsing BOSH payload")
return 400;
end
-
+
-- Stanzas (if any) in the request have now been processed, and
-- we take care of the high-level BOSH logic here, including
-- giving a response or putting the request "on hold".
session.send_buffer = {};
session.send(resp);
end
-
+
if not response.finished then
-- We're keeping this request open, to respond later
log("debug", "Have nothing to say, so leaving request unanswered for now");
waiting_requests[response] = os_time() + session.bosh_wait;
end
end
-
+
if session.bosh_terminate then
session.log("debug", "Closing session with %d requests open", #session.requests);
session:close();
local function bosh_close_stream(session, reason)
(session.log or log)("info", "BOSH client disconnected");
-
+
local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate",
["xmlns:stream"] = xmlns_streams });
-
+
if reason then
close_reply.attr.condition = "remote-stream-error";
if not sid then
-- New session request
context.notopen = nil; -- Signals that we accept this opening tag
-
+
-- TODO: Sanity checks here (rid, to, known host, etc.)
if not hosts[attr.to] then
-- Unknown host
response:send(tostring(close_reply));
return;
end
-
+
-- New session
sid = new_uuid();
local session = {
ip = get_ip_from_request(request);
};
sessions[sid] = session;
-
+
local filter = initialize_filters(session);
-
+
session.log("debug", "BOSH session created for request from %s", session.ip);
log("info", "New BOSH session, assigned it sid '%s'", sid);
end
request.sid = sid;
end
-
+
local session = sessions[sid];
if not session then
-- Unknown sid
context.notopen = nil;
return;
end
-
+
if session.rid then
local rid = tonumber(attr.rid);
local diff = rid - session.rid;
end
session.rid = rid;
end
-
+
if attr.type == "terminate" then
-- Client wants to end this session, which we'll do
-- after processing any stanzas in this request
if session.notopen then
local features = st.stanza("stream:features");
hosts[session.host].events.fire_event("stream-features", { origin = session, features = features });
- fire_event("stream-features", session, features);
session.send(features);
session.notopen = nil;
end
end
+local function handleerr(err) log("error", "Traceback[bosh]: %s", traceback(tostring(err), 2)); end
function stream_callbacks.handlestanza(context, stanza)
if context.ignore then return; end
log("debug", "BOSH stanza received: %s\n", stanza:top_tag());
stanza.attr.xmlns = nil;
end
stanza = session.filter("stanzas/in", stanza);
- core_process_stanza(session, stanza);
+ if stanza then
+ return xpcall(function () return core_process_stanza(session, stanza) end, handleerr);
+ end
end
end
response:send();
return;
end
-
+
local session = sessions[context.sid];
if error == "stream-error" then -- Remote stream error, we close normally
session:close();
end
end
end
-
+
now = now - 3;
local n_dead_sessions = 0;
for session, close_after in pairs(inactive_sessions) do