comparison plugins/mod_bosh.lua @ 10411:db2a06b9ff98

Merge 0.11->trunk
author Kim Alvefur <zash@zash.se>
date Sat, 16 Nov 2019 16:52:31 +0100
parents 4c36bc28b99e
children 2764beb552cd
comparison
equal deleted inserted replaced
10410:659b577f280c 10411:db2a06b9ff98
42 -- The maximum amount of time that the server will hold onto a request before replying 42 -- The maximum amount of time that the server will hold onto a request before replying
43 -- (the client can set this to a lower value when it connects, if it chooses) 43 -- (the client can set this to a lower value when it connects, if it chooses)
44 local bosh_max_wait = module:get_option_number("bosh_max_wait", 120); 44 local bosh_max_wait = module:get_option_number("bosh_max_wait", 120);
45 45
46 local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure"); 46 local consider_bosh_secure = module:get_option_boolean("consider_bosh_secure");
47 local cross_domain = module:get_option("cross_domain_bosh", false); 47 local cross_domain = module:get_option("cross_domain_bosh");
48 48
49 if cross_domain == true then cross_domain = "*"; end 49 if cross_domain ~= nil then
50 if type(cross_domain) == "table" then cross_domain = table.concat(cross_domain, ", "); end 50 module:log("info", "The 'cross_domain_bosh' option has been deprecated");
51 end
51 52
52 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; 53 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat;
53 54
54 -- All sessions, and sessions that have no requests open 55 -- All sessions, and sessions that have no requests open
55 local sessions = module:shared("sessions"); 56 local sessions = module:shared("sessions");
56 57
58 local measure_active = module:measure("active_sessions", "amount");
59 local measure_inactive = module:measure("inactive_sessions", "amount");
60 local report_bad_host = module:measure("bad_host", "rate");
61 local report_bad_sid = module:measure("bad_sid", "rate");
62 local report_new_sid = module:measure("new_sid", "rate");
63 local report_timeout = module:measure("timeout", "rate");
64
65 module:hook("stats-update", function ()
66 local active = 0;
67 local inactive = 0;
68 for _, session in pairs(sessions) do
69 if #session.requests > 0 then
70 active = active + 1;
71 else
72 inactive = inactive + 1;
73 end
74 end
75 measure_active(active);
76 measure_inactive(inactive);
77 end);
78
57 -- Used to respond to idle sessions (those with waiting requests) 79 -- Used to respond to idle sessions (those with waiting requests)
58 function on_destroy_request(request) 80 function on_destroy_request(request)
59 log("debug", "Request destroyed: %s", tostring(request)); 81 log("debug", "Request destroyed: %s", request);
60 local session = sessions[request.context.sid]; 82 local session = sessions[request.context.sid];
61 if session then 83 if session then
62 local requests = session.requests; 84 local requests = session.requests;
63 for i, r in ipairs(requests) do 85 for i, r in ipairs(requests) do
64 if r == request then 86 if r == request then
71 local max_inactive = session.bosh_max_inactive; 93 local max_inactive = session.bosh_max_inactive;
72 if max_inactive and #requests == 0 then 94 if max_inactive and #requests == 0 then
73 if session.inactive_timer then 95 if session.inactive_timer then
74 session.inactive_timer:stop(); 96 session.inactive_timer:stop();
75 end 97 end
76 session.inactive_timer = module:add_timer(max_inactive, check_inactive, session, request.context, 98 session.inactive_timer = module:add_timer(max_inactive, session_timeout, session, request.context,
77 "BOSH client silent for over "..max_inactive.." seconds"); 99 "BOSH client silent for over "..max_inactive.." seconds");
78 (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive); 100 (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive);
79 end 101 end
80 if session.bosh_wait_timer then 102 if session.bosh_wait_timer then
81 session.bosh_wait_timer:stop(); 103 session.bosh_wait_timer:stop();
82 session.bosh_wait_timer = nil; 104 session.bosh_wait_timer = nil;
83 end 105 end
84 end 106 end
85 end 107 end
86 108
87 function check_inactive(now, session, context, reason) -- luacheck: ignore 212/now 109 function session_timeout(now, session, context, reason) -- luacheck: ignore 212/now
88 if not session.destroyed then 110 if not session.destroyed then
111 report_timeout();
89 sessions[context.sid] = nil; 112 sessions[context.sid] = nil;
90 sm_destroy_session(session, reason); 113 sm_destroy_session(session, reason);
91 end 114 end
92 end 115 end
93 116
94 local function set_cross_domain_headers(response)
95 local headers = response.headers;
96 headers.access_control_allow_methods = "GET, POST, OPTIONS";
97 headers.access_control_allow_headers = "Content-Type";
98 headers.access_control_max_age = "7200";
99 headers.access_control_allow_origin = cross_domain;
100 return response;
101 end
102
103 function handle_OPTIONS(event)
104 if cross_domain and event.request.headers.origin then
105 set_cross_domain_headers(event.response);
106 end
107 return "";
108 end
109
110 function handle_POST(event) 117 function handle_POST(event)
111 log("debug", "Handling new request %s: %s\n----------", tostring(event.request), tostring(event.request.body)); 118 log("debug", "Handling new request %s: %s\n----------", event.request, event.request.body);
112 119
113 local request, response = event.request, event.response; 120 local request, response = event.request, event.response;
114 response.on_destroy = on_destroy_request; 121 response.on_destroy = on_destroy_request;
115 local body = request.body; 122 local body = request.body;
116 123
118 local stream = new_xmpp_stream(context, stream_callbacks); 125 local stream = new_xmpp_stream(context, stream_callbacks);
119 response.context = context; 126 response.context = context;
120 127
121 local headers = response.headers; 128 local headers = response.headers;
122 headers.content_type = "text/xml; charset=utf-8"; 129 headers.content_type = "text/xml; charset=utf-8";
123
124 if cross_domain and request.headers.origin then
125 set_cross_domain_headers(response);
126 end
127 130
128 -- stream:feed() calls the stream_callbacks, so all stanzas in 131 -- stream:feed() calls the stream_callbacks, so all stanzas in
129 -- the body are processed in this next line before it returns. 132 -- the body are processed in this next line before it returns.
130 -- In particular, the streamopened() stream callback is where 133 -- In particular, the streamopened() stream callback is where
131 -- much of the session logic happens, because it's where we first 134 -- much of the session logic happens, because it's where we first
203 -- A response has been sent already, or we're ignoring this request 206 -- A response has been sent already, or we're ignoring this request
204 -- (e.g. so a different instance of the module can handle it) 207 -- (e.g. so a different instance of the module can handle it)
205 return; 208 return;
206 end 209 end
207 module:log("warn", "Unable to associate request with a session (incomplete request?)"); 210 module:log("warn", "Unable to associate request with a session (incomplete request?)");
211 report_bad_sid();
208 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", 212 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate",
209 ["xmlns:stream"] = xmlns_streams, condition = "item-not-found" }); 213 ["xmlns:stream"] = xmlns_streams, condition = "item-not-found" });
210 return tostring(close_reply) .. "\n"; 214 return tostring(close_reply) .. "\n";
211 end 215 end
212 216
218 222
219 local function bosh_reset_stream(session) session.notopen = true; end 223 local function bosh_reset_stream(session) session.notopen = true; end
220 224
221 local stream_xmlns_attr = { xmlns = "urn:ietf:params:xml:ns:xmpp-streams" }; 225 local stream_xmlns_attr = { xmlns = "urn:ietf:params:xml:ns:xmpp-streams" };
222 local function bosh_close_stream(session, reason) 226 local function bosh_close_stream(session, reason)
223 (session.log or log)("info", "BOSH client disconnected: %s", tostring((reason and reason.condition or reason) or "session close")); 227 (session.log or log)("info", "BOSH client disconnected: %s", (reason and reason.condition or reason) or "session close");
224 228
225 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", 229 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate",
226 ["xmlns:stream"] = xmlns_streams }); 230 ["xmlns:stream"] = xmlns_streams });
227 231
228 232
243 end 247 end
244 elseif reason.name then -- a stanza 248 elseif reason.name then -- a stanza
245 close_reply = reason; 249 close_reply = reason;
246 end 250 end
247 end 251 end
248 log("info", "Disconnecting client, <stream:error> is: %s", tostring(close_reply)); 252 log("info", "Disconnecting client, <stream:error> is: %s", close_reply);
249 end 253 end
250 254
251 local response_body = tostring(close_reply); 255 local response_body = tostring(close_reply);
252 for _, held_request in ipairs(session.requests) do 256 for _, held_request in ipairs(session.requests) do
253 held_request:send(response_body); 257 held_request:send(response_body);
266 context.rid = rid; 270 context.rid = rid;
267 if not sid then 271 if not sid then
268 -- New session request 272 -- New session request
269 context.notopen = nil; -- Signals that we accept this opening tag 273 context.notopen = nil; -- Signals that we accept this opening tag
270 274
271 local to_host = nameprep(attr.to); 275 if not attr.to then
272 local wait = tonumber(attr.wait); 276 log("debug", "BOSH client tried to connect without specifying a host");
273 if not to_host then 277 report_bad_host();
274 log("debug", "BOSH client tried to connect to invalid host: %s", tostring(attr.to));
275 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", 278 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate",
276 ["xmlns:stream"] = xmlns_streams, condition = "improper-addressing" }); 279 ["xmlns:stream"] = xmlns_streams, condition = "improper-addressing" });
277 response:send(tostring(close_reply)); 280 response:send(tostring(close_reply));
278 return; 281 return;
279 end 282 end
283
284 local to_host = nameprep(attr.to);
285 local wait = tonumber(attr.wait);
286 if not to_host then
287 log("debug", "BOSH client tried to connect to invalid host: %s", attr.to);
288 report_bad_host();
289 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate",
290 ["xmlns:stream"] = xmlns_streams, condition = "improper-addressing" });
291 response:send(tostring(close_reply));
292 return;
293 end
280 if not rid or (not attr.wait or not wait or wait < 0 or wait % 1 ~= 0) then 294 if not rid or (not attr.wait or not wait or wait < 0 or wait % 1 ~= 0) then
281 log("debug", "BOSH client sent invalid rid or wait attributes: rid=%s, wait=%s", tostring(attr.rid), tostring(attr.wait)); 295 log("debug", "BOSH client sent invalid rid or wait attributes: rid=%s, wait=%s", attr.rid, attr.wait);
282 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", 296 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate",
283 ["xmlns:stream"] = xmlns_streams, condition = "bad-request" }); 297 ["xmlns:stream"] = xmlns_streams, condition = "bad-request" });
284 response:send(tostring(close_reply)); 298 response:send(tostring(close_reply));
285 return; 299 return;
286 end 300 end
307 321
308 local filter = initialize_filters(session); 322 local filter = initialize_filters(session);
309 323
310 session.log("debug", "BOSH session created for request from %s", session.ip); 324 session.log("debug", "BOSH session created for request from %s", session.ip);
311 log("info", "New BOSH session, assigned it sid '%s'", sid); 325 log("info", "New BOSH session, assigned it sid '%s'", sid);
326 report_new_sid();
312 327
313 module:fire_event("bosh-session", { session = session, request = request }); 328 module:fire_event("bosh-session", { session = session, request = request });
314 329
315 -- Send creation response 330 -- Send creation response
316 local creating_session = true; 331 local creating_session = true;
321 if s.attr and not s.attr.xmlns then 336 if s.attr and not s.attr.xmlns then
322 s = st.clone(s); 337 s = st.clone(s);
323 s.attr.xmlns = "jabber:client"; 338 s.attr.xmlns = "jabber:client";
324 end 339 end
325 s = filter("stanzas/out", s); 340 s = filter("stanzas/out", s);
326 --log("debug", "Sending BOSH data: %s", tostring(s)); 341 --log("debug", "Sending BOSH data: %s", s);
327 if not s then return true end 342 if not s then return true end
328 t_insert(session.send_buffer, tostring(s)); 343 t_insert(session.send_buffer, tostring(s));
329 344
330 local oldest_request = r[1]; 345 local oldest_request = r[1];
331 if oldest_request and not session.bosh_processing then 346 if oldest_request and not session.bosh_processing then
361 376
362 local session = sessions[sid]; 377 local session = sessions[sid];
363 if not session then 378 if not session then
364 -- Unknown sid 379 -- Unknown sid
365 log("info", "Client tried to use sid '%s' which we don't know about", sid); 380 log("info", "Client tried to use sid '%s' which we don't know about", sid);
381 report_bad_sid();
366 response:send(tostring(st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", condition = "item-not-found" }))); 382 response:send(tostring(st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", condition = "item-not-found" })));
367 context.notopen = nil; 383 context.notopen = nil;
368 return; 384 return;
369 end 385 end
370 386
423 session.send(features); 439 session.send(features);
424 session.notopen = nil; 440 session.notopen = nil;
425 end 441 end
426 end 442 end
427 443
428 local function handleerr(err) log("error", "Traceback[bosh]: %s", traceback(tostring(err), 2)); end 444 local function handleerr(err) log("error", "Traceback[bosh]: %s", traceback(err, 2)); end
429 445
430 function runner_callbacks:error(err) -- luacheck: ignore 212/self 446 function runner_callbacks:error(err) -- luacheck: ignore 212/self
431 return handleerr(err); 447 return handleerr(err);
432 end 448 end
433 449
509 module:provides("http", { 525 module:provides("http", {
510 default_path = "/http-bind"; 526 default_path = "/http-bind";
511 route = { 527 route = {
512 ["GET"] = GET_response; 528 ["GET"] = GET_response;
513 ["GET /"] = GET_response; 529 ["GET /"] = GET_response;
514 ["OPTIONS"] = handle_OPTIONS;
515 ["OPTIONS /"] = handle_OPTIONS;
516 ["POST"] = handle_POST; 530 ["POST"] = handle_POST;
517 ["POST /"] = handle_POST; 531 ["POST /"] = handle_POST;
518 }; 532 };
519 }); 533 });