Software /
code /
prosody
Changeset
688:5f77bbc1f04a
Automated merge with http://waqas.ath.cx:8000/
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Fri, 09 Jan 2009 19:19:06 +0000 |
parents | 687:a92d647624a1 (current diff) 684:b7d85c6a0002 (diff) |
children | 689:94b043fbaf33 |
files | |
diffstat | 4 files changed, 88 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/plugins/mod_bosh.lua Fri Jan 09 23:09:53 2009 +0500 +++ b/plugins/mod_bosh.lua Fri Jan 09 19:19:06 2009 +0000 @@ -6,27 +6,29 @@ local server = require "net.server"; local httpserver = require "net.httpserver"; local sm = require "core.sessionmanager"; +local sm_destroy_session = sm.destroy_session; local new_uuid = require "util.uuid".generate; local fire_event = require "core.eventmanager".fire_event; local core_process_stanza = core_process_stanza; local st = require "util.stanza"; local log = require "util.logger".init("bosh"); local stream_callbacks = { stream_tag = "http://jabber.org/protocol/httpbind|body" }; - +local config = require "core.configmanager"; local xmlns_bosh = "http://jabber.org/protocol/httpbind"; -- (hard-coded into a literal in session.send) -local BOSH_DEFAULT_HOLD = 1; -local BOSH_DEFAULT_INACTIVITY = 30; -local BOSH_DEFAULT_POLLING = 5; -local BOSH_DEFAULT_REQUESTS = 2; -local BOSH_DEFAULT_MAXPAUSE = 120; +local BOSH_DEFAULT_HOLD = tonumber(config.get("*", "core", "bosh_default_hold")) or 1; +local BOSH_DEFAULT_INACTIVITY = tonumber(config.get("*", "core", "bosh_max_inactivity")) or 60; +local BOSH_DEFAULT_POLLING = tonumber(config.get("*", "core", "bosh_max_polling")) or 5; +local BOSH_DEFAULT_REQUESTS = tonumber(config.get("*", "core", "bosh_max_requests")) or 2; +local BOSH_DEFAULT_MAXPAUSE = tonumber(config.get("*", "core", "bosh_max_pause")) or 300; local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; local os_time = os.time; local sessions = {}; +local inactive_sessions = {}; -- Sessions which have no open requests --- Used to respond to idle sessions +-- Used to respond to idle sessions (those with waiting requests) local waiting_requests = {}; function on_destroy_request(request) waiting_requests[request] = nil; @@ -34,7 +36,6 @@ function handle_request(method, body, request) if (not body) or request.method ~= "POST" then - --return { status = "200 OK", headers = { ["Content-Type"] = "text/html" }, body = "<html><body>You don't look like a BOSH client to me... what do you want?</body></html>" }; return "<html><body>You really don't look like a BOSH client to me... what do you want?</body></html>"; end if not method then @@ -60,19 +61,16 @@ log("debug", "...sending what is in the buffer") session.send(t_concat(session.send_buffer)); session.send_buffer = {}; - return; else -- or an empty response log("debug", "...sending an empty response"); session.send(""); - return; end elseif #session.send_buffer > 0 then log("debug", "Session has data in the send buffer, will send now.."); local resp = t_concat(session.send_buffer); session.send_buffer = {}; session.send(resp); - return; end if not request.destroyed and session.bosh_wait then @@ -86,8 +84,21 @@ end end + local function bosh_reset_stream(session) session.notopen = true; end -local function bosh_close_stream(session, reason) end + +local session_close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate" }); +local function bosh_close_stream(session, reason) + (session.log or log)("info", "BOSH client disconnected"); + session_close_reply.attr.condition = reason; + local session_close_reply = tostring(session_close_reply); + for _, held_request in ipairs(session.requests) do + held_request:send(session_close_reply); + held_request:destroy(); + end + sessions[session.sid] = nil; + sm_destroy_session(session); +end function stream_callbacks.streamopened(request, attr) print("Attr:") @@ -95,14 +106,23 @@ log("debug", "BOSH body open (sid: %s)", attr.sid); local sid = attr.sid if not sid then + -- New session request + request.notopen = nil; -- Signals that we accept this opening tag + -- TODO: Sanity checks here (rid, to, known host, etc.) - request.notopen = nil; -- Signals that we accept this opening tag + if not hosts[attr.to] then + -- Unknown host + session_close_reply.attr.condition = "host-unknown"; + request:send(tostring(session_close_reply)); + request.notopen = nil + return; + end -- New session sid = tostring(new_uuid()); local session = { type = "c2s_unauthed", conn = {}, sid = sid, rid = attr.rid, host = attr.to, bosh_version = attr.ver, bosh_wait = attr.wait, streamid = sid, - bosh_hold = BOSH_DEFAULT_HOLD, - requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream, close = bosh_close_stream }; + bosh_hold = BOSH_DEFAULT_HOLD, bosh_max_inactive = BOSH_DEFAULT_INACTIVITY, + requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream, close = bosh_close_stream, dispatch_stanza = core_process_stanza }; sessions[sid] = session; log("info", "New BOSH session, assigned it sid '%s'", sid); local r, send_buffer = session.requests, session.send_buffer; @@ -133,6 +153,10 @@ end elseif s ~= "" then log("debug", "Saved to send buffer because there are %d open requests", #r); + if session.bosh_max_inactive and not inactive_sessions[session] then + inactive_sessions[session] = os_time(); + (session.log or log)("debug", "BOSH session marked as inactive at %d", inactive_sessions[session]); + end -- Hmm, no requests are open :( t_insert(session.send_buffer, tostring(s)); log("debug", "There are now %d things in the send_buffer", #session.send_buffer); @@ -145,7 +169,7 @@ fire_event("stream-features", session, features); --xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh' local response = st.stanza("body", { xmlns = xmlns_bosh, - inactivity = "30", polling = "5", requests = "2", hold = tostring(session.bosh_hold), maxpause = "120", + inactivity = tostring(BOSH_DEFAULT_INACTIVITY), polling = tostring(BOSH_DEFAULT_POLLING), requests = tostring(BOSH_DEFAULT_REQUESTS), hold = tostring(session.bosh_hold), maxpause = "120", sid = sid, ver = '1.6', from = session.host, secure = 'true', ["xmpp:version"] = "1.0", ["xmlns:xmpp"] = "urn:xmpp:xbosh", ["xmlns:stream"] = "http://etherx.jabber.org/streams" }):add_child(features); request:send(tostring(response)); @@ -163,6 +187,19 @@ return; end + if attr.type == "terminate" then + -- Client wants to end this session + session:close(); + request.notopen = nil; + return; + end + + -- If session was inactive, make sure it is now marked as not + if #session.requests == 0 then + (session.log or log)("debug", "BOSH client now active again at %d", os_time()); + inactive_sessions[session] = nil; + end + if session.notopen then local features = st.stanza("stream:features"); fire_event("stream-features", session, features); @@ -200,6 +237,16 @@ end end end + + now = now - 3; + for session, inactive_since in pairs(inactive_sessions) do + if now - inactive_since > session.bosh_max_inactive then + (session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now); + sessions[session.sid] = nil; + inactive_sessions[session] = nil; + sm_destroy_session(session, "BOSH client silent for over "..session.bosh_max_inactive.." seconds"); + end + end end httpserver.new{ port = 5280, base = "http-bind", handler = handle_request, ssl = false}
--- a/tests/test.lua Fri Jan 09 23:09:53 2009 +0500 +++ b/tests/test.lua Fri Jan 09 19:19:06 2009 +0000 @@ -25,7 +25,8 @@ dotest "core.stanza_router" dotest "core.s2smanager" dotest "core.configmanager" - + dotest "util.stanza" + dosingletest("test_sasl.lua", "latin1toutf8"); end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_util_stanza.lua Fri Jan 09 19:19:06 2009 +0000 @@ -0,0 +1,20 @@ + +function preserialize(preserialize, st) + local stanza = st.stanza("message", { a = "a" }); + local stanza2 = preserialize(stanza); + assert_is(stanza2 and stanza.name, "preserialize returns a stanza"); + assert_is_not(stanza2.tags, "Preserialized stanza has no tag list"); + assert_is_not(stanza2.last_add, "Preserialized stanza has no last_add marker"); + assert_is_not(getmetatable(stanza2), "Preserialized stanza has no metatable"); +end + +function deserialize(deserialize, st) + local stanza = st.stanza("message", { a = "a" }); + + local stanza2 = deserialize(st.preserialize(stanza)); + assert_is(stanza2 and stanza.name, "deserialize returns a stanza"); + assert_is(stanza2.last_add, "Deserialized stanza is missing last_add for adding child tags"); + assert_table(stanza2.attr, "Deserialized stanza has attributes"); + assert_equal(stanza2.attr.a, "a", "Deserialized stanza retains attributes"); + assert_table(getmetatable(stanza2), "Deserialized stanza has metatable"); +end