Software /
code /
prosody
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> |