Merge 0.9->trunk
authorMatthew Wild <mwild1@gmail.com>
Mon, 8 Jul 2013 23:04:30 +0000 (00:04 +0100)
committerMatthew Wild <mwild1@gmail.com>
Mon, 8 Jul 2013 23:04:30 +0000 (00:04 +0100)
1  2 
plugins/mod_bosh.lua

diff --combined plugins/mod_bosh.lua
index 9a612ae0882652d45d9c885776a7fd0d81c3bac1,d9c8defd90ead6d891c5948ede45d90e04ce98a0..d8717d18a9075982bd21ebd21d56af3f40f49571
@@@ -1,7 -1,7 +1,7 @@@
  -- Prosody IM
  -- Copyright (C) 2008-2010 Matthew Wild
  -- Copyright (C) 2008-2010 Waqas Hussain
 --- 
 +--
  -- This project is MIT/X11 licensed. Please see the
  -- COPYING file in the source package for more information.
  --
@@@ -20,6 -20,8 +20,8 @@@ local logger = require "util.logger"
  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";
@@@ -35,10 -37,24 +37,10 @@@ local BOSH_DEFAULT_REQUESTS = module:ge
  local bosh_max_wait = module:get_option_number("bosh_max_wait", 120);
  
  local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure");
 -
 -local default_headers = { ["Content-Type"] = "text/xml; charset=utf-8" };
 -
  local cross_domain = module:get_option("cross_domain_bosh", false);
 -if cross_domain then
 -      default_headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS";
 -      default_headers["Access-Control-Allow-Headers"] = "Content-Type";
 -      default_headers["Access-Control-Max-Age"] = "7200";
 -
 -      if cross_domain == true then
 -              default_headers["Access-Control-Allow-Origin"] = "*";
 -      elseif type(cross_domain) == "table" then
 -              cross_domain = table.concat(cross_domain, ", ");
 -      end
 -      if type(cross_domain) == "string" then
 -              default_headers["Access-Control-Allow-Origin"] = cross_domain;
 -      end
 -end
 +
 +if cross_domain == true then cross_domain = "*"; end
 +if type(cross_domain) == "table" then cross_domain = table.concat(cross_domain, ", "); end
  
  local trusted_proxies = module:get_option_set("trusted_proxies", {"127.0.0.1"})._items;
  
@@@ -63,7 -79,7 +65,7 @@@ local os_time = os.time
  local sessions, inactive_sessions = module:shared("sessions", "inactive_sessions");
  
  -- Used to respond to idle sessions (those with waiting requests)
 -local waiting_requests = {};
 +local waiting_requests = module:shared("waiting_requests");
  function on_destroy_request(request)
        log("debug", "Request destroyed: %s", tostring(request));
        waiting_requests[request] = nil;
        end
  end
  
 -function handle_OPTIONS(request)
 -      local headers = {};
 -      for k,v in pairs(default_headers) do headers[k] = v; end
 -      headers["Content-Type"] = nil;
 -      return { headers = headers, body = "" };
 +local function set_cross_domain_headers(response)
 +      local headers = response.headers;
 +      headers.access_control_allow_methods = "GET, POST, OPTIONS";
 +      headers.access_control_allow_headers = "Content-Type";
 +      headers.access_control_max_age = "7200";
 +      headers.access_control_allow_origin = cross_domain;
 +      return response;
 +end
 +
 +function handle_OPTIONS(event)
 +      if cross_domain and event.request.headers.origin then
 +              set_cross_domain_headers(event.response);
 +      end
 +      return "";
  end
  
  function handle_POST(event)
        local context = { request = request, response = response, notopen = true };
        local stream = new_xmpp_stream(context, stream_callbacks);
        response.context = context;
 +
 +      local headers = response.headers;
 +      headers.content_type = "text/xml; charset=utf-8";
 +
 +      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
        -- much of the session logic happens, because it's where we first
        -- get to see the 'sid' of this request.
 -      stream:feed(body);
 +      if not stream:feed(body) then
 +              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
                local r = session.requests;
                log("debug", "Session %s has %d out of %d requests open", context.sid, #r, session.bosh_hold);
                log("debug", "and there are %d things in the send_buffer:", #session.send_buffer);
 -              for i, thing in ipairs(session.send_buffer) do
 -                      log("debug", "    %s", tostring(thing));
 -              end
                if #r > session.bosh_hold then
                        -- We are holding too many requests, send what's in the buffer,
                        log("debug", "We are holding too many requests, so...");
                        return true; -- Inform http server we shall reply later
                end
        end
 +      module:log("warn", "Unable to associate request with a session (incomplete request?)");
 +      return 400;
  end
  
  
@@@ -219,9 -217,10 +221,9 @@@ local function bosh_close_stream(sessio
  
        local response_body = tostring(close_reply);
        for _, held_request in ipairs(session.requests) do
 -              held_request.headers = default_headers;
                held_request:send(response_body);
        end
 -      sessions[session.sid]  = nil;
 +      sessions[session.sid] = nil;
        inactive_sessions[session] = nil;
        sm_destroy_session(session);
  end
@@@ -280,6 -279,7 +282,6 @@@ function stream_callbacks.streamopened(
                        local oldest_request = r[1];
                        if oldest_request and not session.bosh_processing then
                                log("debug", "We have an open request, so sending on that");
 -                              oldest_request.headers = default_headers;
                                local body_attr = { xmlns = "http://jabber.org/protocol/httpbind",
                                        ["xmlns:stream"] = "http://etherx.jabber.org/streams";
                                        type = session.bosh_terminate and "terminate" or nil;
        if not session then
                -- Unknown sid
                log("info", "Client tried to use sid '%s' which we don't know about", sid);
 -              response.headers = default_headers;
                response:send(tostring(st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", condition = "item-not-found" })));
                context.notopen = nil;
                return;
                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(tostring(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
  
 -function stream_callbacks.streamclosed(request)
 -      local session = sessions[request.sid];
 +function stream_callbacks.streamclosed(context)
 +      local session = sessions[context.sid];
        if session then
                session.bosh_processing = false;
                if #session.send_buffer > 0 then
@@@ -380,6 -384,7 +385,6 @@@ function stream_callbacks.error(context
        log("debug", "Error parsing BOSH request payload; %s", error);
        if not context.sid then
                local response = context.response;
 -              response.headers = default_headers;
                response.status_code = 400;
                response:send();
                return;
        end
  end
  
 -local dead_sessions = {};
 +local dead_sessions = module:shared("dead_sessions");
  function on_timer()
        -- log("debug", "Checking for requests soon to timeout...");
        -- Identify requests timing out within the next few seconds