comparison plugins/mod_bosh.lua @ 4541:05f5ec99da77

Merge with trunk
author Matthew Wild <mwild1@gmail.com>
date Sun, 22 Jan 2012 22:55:49 +0000
parents 332a5d73e5b6
children d00f31470fcf 403b56b78018
comparison
equal deleted inserted replaced
4530:40905e7bf680 4541:05f5ec99da77
7 -- 7 --
8 8
9 module.host = "*" -- Global module 9 module.host = "*" -- Global module
10 10
11 local hosts = _G.hosts; 11 local hosts = _G.hosts;
12 local lxp = require "lxp";
13 local new_xmpp_stream = require "util.xmppstream".new; 12 local new_xmpp_stream = require "util.xmppstream".new;
14 local httpserver = require "net.httpserver"; 13 local httpserver = require "net.httpserver";
15 local sm = require "core.sessionmanager"; 14 local sm = require "core.sessionmanager";
16 local sm_destroy_session = sm.destroy_session; 15 local sm_destroy_session = sm.destroy_session;
17 local new_uuid = require "util.uuid".generate; 16 local new_uuid = require "util.uuid".generate;
33 local BOSH_DEFAULT_INACTIVITY = module:get_option_number("bosh_max_inactivity", 60); 32 local BOSH_DEFAULT_INACTIVITY = module:get_option_number("bosh_max_inactivity", 60);
34 local BOSH_DEFAULT_POLLING = module:get_option_number("bosh_max_polling", 5); 33 local BOSH_DEFAULT_POLLING = module:get_option_number("bosh_max_polling", 5);
35 local BOSH_DEFAULT_REQUESTS = module:get_option_number("bosh_max_requests", 2); 34 local BOSH_DEFAULT_REQUESTS = module:get_option_number("bosh_max_requests", 2);
36 35
37 local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure"); 36 local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure");
37 local auto_cork = module:get_option_boolean("bosh_auto_cork", false);
38 38
39 local default_headers = { ["Content-Type"] = "text/xml; charset=utf-8" }; 39 local default_headers = { ["Content-Type"] = "text/xml; charset=utf-8" };
40 40
41 local cross_domain = module:get_option("cross_domain_bosh", false); 41 local cross_domain = module:get_option("cross_domain_bosh", false);
42 if cross_domain then 42 if cross_domain then
55 end 55 end
56 56
57 local trusted_proxies = module:get_option_set("trusted_proxies", {"127.0.0.1"})._items; 57 local trusted_proxies = module:get_option_set("trusted_proxies", {"127.0.0.1"})._items;
58 58
59 local function get_ip_from_request(request) 59 local function get_ip_from_request(request)
60 local ip = request.handler:ip(); 60 local ip = request.conn:ip();
61 local forwarded_for = request.headers["x-forwarded-for"]; 61 local forwarded_for = request.headers["x-forwarded-for"];
62 if forwarded_for then 62 if forwarded_for then
63 forwarded_for = forwarded_for..", "..ip; 63 forwarded_for = forwarded_for..", "..ip;
64 for forwarded_ip in forwarded_for:gmatch("[^%s,]+") do 64 for forwarded_ip in forwarded_for:gmatch("[^%s,]+") do
65 if not trusted_proxies[forwarded_ip] then 65 if not trusted_proxies[forwarded_ip] then
89 break; 89 break;
90 end 90 end
91 end 91 end
92 92
93 -- If this session now has no requests open, mark it as inactive 93 -- If this session now has no requests open, mark it as inactive
94 if #requests == 0 and session.bosh_max_inactive and not inactive_sessions[session] then 94 local max_inactive = session.bosh_max_inactive;
95 inactive_sessions[session] = os_time(); 95 if max_inactive and #requests == 0 then
96 (session.log or log)("debug", "BOSH session marked as inactive at %d", inactive_sessions[session]); 96 inactive_sessions[session] = os_time() + max_inactive;
97 (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive);
97 end 98 end
98 end 99 end
99 end 100 end
100 101
101 function handle_request(method, body, request) 102 function handle_request(method, body, request)
117 request.notopen = true; 118 request.notopen = true;
118 request.log = log; 119 request.log = log;
119 request.on_destroy = on_destroy_request; 120 request.on_destroy = on_destroy_request;
120 121
121 local stream = new_xmpp_stream(request, stream_callbacks); 122 local stream = new_xmpp_stream(request, stream_callbacks);
123
122 -- stream:feed() calls the stream_callbacks, so all stanzas in 124 -- stream:feed() calls the stream_callbacks, so all stanzas in
123 -- the body are processed in this next line before it returns. 125 -- the body are processed in this next line before it returns.
126 -- In particular, the streamopened() stream callback is where
127 -- much of the session logic happens, because it's where we first
128 -- get to see the 'sid' of this request.
124 stream:feed(body); 129 stream:feed(body);
125 130
131 -- Stanzas (if any) in the request have now been processed, and
132 -- we take care of the high-level BOSH logic here, including
133 -- giving a response or putting the request "on hold".
126 local session = sessions[request.sid]; 134 local session = sessions[request.sid];
127 if session then 135 if session then
128 -- Session was marked as inactive, since we have 136 -- Session was marked as inactive, since we have
129 -- a request open now, unmark it 137 -- a request open now, unmark it
130 if inactive_sessions[session] and #session.requests > 0 then 138 if inactive_sessions[session] and #session.requests > 0 then
211 for _, held_request in ipairs(session.requests) do 219 for _, held_request in ipairs(session.requests) do
212 held_request:send(session_close_response); 220 held_request:send(session_close_response);
213 held_request:destroy(); 221 held_request:destroy();
214 end 222 end
215 sessions[session.sid] = nil; 223 sessions[session.sid] = nil;
224 inactive_sessions[session] = nil;
216 sm_destroy_session(session); 225 sm_destroy_session(session);
217 end 226 end
218 227
228 -- Handle the <body> tag in the request payload.
219 function stream_callbacks.streamopened(request, attr) 229 function stream_callbacks.streamopened(request, attr)
220 local sid = attr.sid; 230 local sid = attr.sid;
221 log("debug", "BOSH body open (sid: %s)", sid or "<none>"); 231 log("debug", "BOSH body open (sid: %s)", sid or "<none>");
222 if not sid then 232 if not sid then
223 -- New session request 233 -- New session request
256 s = st.clone(s); 266 s = st.clone(s);
257 s.attr.xmlns = "jabber:client"; 267 s.attr.xmlns = "jabber:client";
258 end 268 end
259 --log("debug", "Sending BOSH data: %s", tostring(s)); 269 --log("debug", "Sending BOSH data: %s", tostring(s));
260 local oldest_request = r[1]; 270 local oldest_request = r[1];
261 if oldest_request then 271 if oldest_request and (not(auto_cork) or waiting_requests[oldest_request]) then
262 log("debug", "We have an open request, so sending on that"); 272 log("debug", "We have an open request, so sending on that");
263 response.body = t_concat({ 273 response.body = t_concat({
264 "<body xmlns='http://jabber.org/protocol/httpbind' ", 274 "<body xmlns='http://jabber.org/protocol/httpbind' ",
265 session.bosh_terminate and "type='terminate' " or "", 275 session.bosh_terminate and "type='terminate' " or "",
266 "sid='", sid, "' xmlns:stream = 'http://etherx.jabber.org/streams'>", 276 "sid='", sid, "' xmlns:stream = 'http://etherx.jabber.org/streams'>",
336 return; 346 return;
337 end 347 end
338 session.rid = rid; 348 session.rid = rid;
339 end 349 end
340 350
351 if attr.type == "terminate" then
352 -- Client wants to end this session, which we'll do
353 -- after processing any stanzas in this request
354 session.bosh_terminate = true;
355 end
356
357 request.notopen = nil; -- Signals that we accept this opening tag
358 t_insert(session.requests, request);
359 request.sid = sid;
360
341 if session.notopen then 361 if session.notopen then
342 local features = st.stanza("stream:features"); 362 local features = st.stanza("stream:features");
343 hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); 363 hosts[session.host].events.fire_event("stream-features", { origin = session, features = features });
344 fire_event("stream-features", session, features); 364 fire_event("stream-features", session, features);
345 session.send(features); 365 session.send(features);
346 session.notopen = nil; 366 session.notopen = nil;
347 end 367 end
348
349 if attr.type == "terminate" then
350 -- Client wants to end this session, which we'll do
351 -- after processing any stanzas in this request
352 session.bosh_terminate = true;
353 end
354
355 request.notopen = nil; -- Signals that we accept this opening tag
356 t_insert(session.requests, request);
357 request.sid = sid;
358 end 368 end
359 369
360 function stream_callbacks.handlestanza(request, stanza) 370 function stream_callbacks.handlestanza(request, stanza)
361 if request.ignore then return; end 371 if request.ignore then return; end
362 log("debug", "BOSH stanza received: %s\n", stanza:top_tag()); 372 log("debug", "BOSH stanza received: %s\n", stanza:top_tag());
400 end 410 end
401 end 411 end
402 412
403 now = now - 3; 413 now = now - 3;
404 local n_dead_sessions = 0; 414 local n_dead_sessions = 0;
405 for session, inactive_since in pairs(inactive_sessions) do 415 for session, close_after in pairs(inactive_sessions) do
406 if session.bosh_max_inactive then 416 if close_after < now then
407 if now - inactive_since > session.bosh_max_inactive then 417 (session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now);
408 (session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now); 418 sessions[session.sid] = nil;
409 sessions[session.sid] = nil;
410 inactive_sessions[session] = nil;
411 n_dead_sessions = n_dead_sessions + 1;
412 dead_sessions[n_dead_sessions] = session;
413 end
414 else
415 inactive_sessions[session] = nil; 419 inactive_sessions[session] = nil;
420 n_dead_sessions = n_dead_sessions + 1;
421 dead_sessions[n_dead_sessions] = session;
416 end 422 end
417 end 423 end
418 424
419 for i=1,n_dead_sessions do 425 for i=1,n_dead_sessions do
420 local session = dead_sessions[i]; 426 local session = dead_sessions[i];