Comparison

plugins/mod_bosh.lua @ 6989:118858bf47cd

mod_bosh: Instead of a global once-per-second timer add a timer for each session when needed
author Kim Alvefur <zash@zash.se>
date Mon, 07 Dec 2015 19:44:08 +0100
parent 6528:f0687c313cf1
child 7048:b58fd349ddfe
comparison
equal deleted inserted replaced
6988:329d5fb8a9d3 6989:118858bf47cd
61 61
62 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; 62 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat;
63 local os_time = os.time; 63 local os_time = os.time;
64 64
65 -- All sessions, and sessions that have no requests open 65 -- All sessions, and sessions that have no requests open
66 local sessions, inactive_sessions = module:shared("sessions", "inactive_sessions"); 66 local sessions = module:shared("sessions");
67 67
68 -- Used to respond to idle sessions (those with waiting requests) 68 -- Used to respond to idle sessions (those with waiting requests)
69 local waiting_requests = module:shared("waiting_requests");
70 function on_destroy_request(request) 69 function on_destroy_request(request)
71 log("debug", "Request destroyed: %s", tostring(request)); 70 log("debug", "Request destroyed: %s", tostring(request));
72 waiting_requests[request] = nil;
73 local session = sessions[request.context.sid]; 71 local session = sessions[request.context.sid];
74 if session then 72 if session then
75 local requests = session.requests; 73 local requests = session.requests;
76 for i, r in ipairs(requests) do 74 for i, r in ipairs(requests) do
77 if r == request then 75 if r == request then
81 end 79 end
82 80
83 -- If this session now has no requests open, mark it as inactive 81 -- If this session now has no requests open, mark it as inactive
84 local max_inactive = session.bosh_max_inactive; 82 local max_inactive = session.bosh_max_inactive;
85 if max_inactive and #requests == 0 then 83 if max_inactive and #requests == 0 then
86 inactive_sessions[session] = os_time() + max_inactive; 84 if session.inactive_timer then
85 session.inactive_timer:stop();
86 end
87 session.inactive_timer = module:add_timer(max_inactive, check_inactive, session, request.context,
88 "BOSH client silent for over "..max_inactive.." seconds");
87 (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive); 89 (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive);
88 end 90 end
91 if session.bosh_wait_timer then
92 session.bosh_wait_timer:stop();
93 session.bosh_wait_timer = nil;
94 end
95 end
96 end
97
98 function check_inactive(now, session, context, reason)
99 if not sessions.destroyed then
100 sessions[context.sid] = nil;
101 sm_destroy_session(session, reason);
89 end 102 end
90 end 103 end
91 104
92 local function set_cross_domain_headers(response) 105 local function set_cross_domain_headers(response)
93 local headers = response.headers; 106 local headers = response.headers;
117 response.context = context; 130 response.context = context;
118 131
119 local headers = response.headers; 132 local headers = response.headers;
120 headers.content_type = "text/xml; charset=utf-8"; 133 headers.content_type = "text/xml; charset=utf-8";
121 134
122 if cross_domain and event.request.headers.origin then 135 if cross_domain and request.headers.origin then
123 set_cross_domain_headers(response); 136 set_cross_domain_headers(response);
124 end 137 end
125 138
126 -- stream:feed() calls the stream_callbacks, so all stanzas in 139 -- stream:feed() calls the stream_callbacks, so all stanzas in
127 -- the body are processed in this next line before it returns. 140 -- the body are processed in this next line before it returns.
138 -- giving a response or putting the request "on hold". 151 -- giving a response or putting the request "on hold".
139 local session = sessions[context.sid]; 152 local session = sessions[context.sid];
140 if session then 153 if session then
141 -- Session was marked as inactive, since we have 154 -- Session was marked as inactive, since we have
142 -- a request open now, unmark it 155 -- a request open now, unmark it
143 if inactive_sessions[session] and #session.requests > 0 then 156 if session.inactive_timer and #session.requests > 0 then
144 inactive_sessions[session] = nil; 157 session.inactive_timer:stop();
158 session.inactive_timer = nil;
159 end
160
161 if session.bosh_wait_timer then
162 session.bosh_wait_timer:stop();
163 session.bosh_wait_timer = nil;
145 end 164 end
146 165
147 local r = session.requests; 166 local r = session.requests;
148 log("debug", "Session %s has %d out of %d requests open", context.sid, #r, session.bosh_hold); 167 log("debug", "Session %s has %d out of %d requests open", context.sid, #r, session.bosh_hold);
149 log("debug", "and there are %d things in the send_buffer:", #session.send_buffer); 168 log("debug", "and there are %d things in the send_buffer:", #session.send_buffer);
168 187
169 if not response.finished then 188 if not response.finished then
170 -- We're keeping this request open, to respond later 189 -- We're keeping this request open, to respond later
171 log("debug", "Have nothing to say, so leaving request unanswered for now"); 190 log("debug", "Have nothing to say, so leaving request unanswered for now");
172 if session.bosh_wait then 191 if session.bosh_wait then
173 waiting_requests[response] = os_time() + session.bosh_wait; 192 session.bosh_wait_timer = module:add_timer(session.bosh_wait, after_bosh_wait, request, session)
174 end 193 end
175 end 194 end
176 195
177 if session.bosh_terminate then 196 if session.bosh_terminate then
178 session.log("debug", "Closing session with %d requests open", #session.requests); 197 session.log("debug", "Closing session with %d requests open", #session.requests);
184 end 203 end
185 module:log("warn", "Unable to associate request with a session (incomplete request?)"); 204 module:log("warn", "Unable to associate request with a session (incomplete request?)");
186 return 400; 205 return 400;
187 end 206 end
188 207
208 function after_bosh_wait(now, request, session)
209 if request.conn then
210 session.send("");
211 end
212 end
189 213
190 local function bosh_reset_stream(session) session.notopen = true; end 214 local function bosh_reset_stream(session) session.notopen = true; end
191 215
192 local stream_xmlns_attr = { xmlns = "urn:ietf:params:xml:ns:xmpp-streams" }; 216 local stream_xmlns_attr = { xmlns = "urn:ietf:params:xml:ns:xmpp-streams" };
193 217
223 local response_body = tostring(close_reply); 247 local response_body = tostring(close_reply);
224 for _, held_request in ipairs(session.requests) do 248 for _, held_request in ipairs(session.requests) do
225 held_request:send(response_body); 249 held_request:send(response_body);
226 end 250 end
227 sessions[session.sid] = nil; 251 sessions[session.sid] = nil;
228 inactive_sessions[session] = nil;
229 sm_destroy_session(session); 252 sm_destroy_session(session);
230 end 253 end
231 254
232 local runner_callbacks = { }; 255 local runner_callbacks = { };
233 256
405 else 428 else
406 session:close({ condition = "bad-format", text = "Error processing stream" }); 429 session:close({ condition = "bad-format", text = "Error processing stream" });
407 end 430 end
408 end 431 end
409 432
410 local dead_sessions = module:shared("dead_sessions");
411 function on_timer()
412 -- log("debug", "Checking for requests soon to timeout...");
413 -- Identify requests timing out within the next few seconds
414 local now = os_time() + 3;
415 for request, reply_before in pairs(waiting_requests) do
416 if reply_before <= now then
417 log("debug", "%s was soon to timeout (at %d, now %d), sending empty response", tostring(request), reply_before, now);
418 -- Send empty response to let the
419 -- client know we're still here
420 if request.conn then
421 sessions[request.context.sid].send("");
422 end
423 end
424 end
425
426 now = now - 3;
427 local n_dead_sessions = 0;
428 for session, close_after in pairs(inactive_sessions) do
429 if close_after < now then
430 (session.log or log)("debug", "BOSH client inactive too long, destroying session at %d", now);
431 sessions[session.sid] = nil;
432 inactive_sessions[session] = nil;
433 n_dead_sessions = n_dead_sessions + 1;
434 dead_sessions[n_dead_sessions] = session;
435 end
436 end
437
438 for i=1,n_dead_sessions do
439 local session = dead_sessions[i];
440 dead_sessions[i] = nil;
441 sm_destroy_session(session, "BOSH client silent for over "..session.bosh_max_inactive.." seconds");
442 end
443 return 1;
444 end
445 module:add_timer(1, on_timer);
446
447
448 local GET_response = { 433 local GET_response = {
449 headers = { 434 headers = {
450 content_type = "text/html"; 435 content_type = "text/html";
451 }; 436 };
452 body = [[<html><body> 437 body = [[<html><body>