Mercurial > prosody-hg
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 |
| parents | f0687c313cf1 |
| children | 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> |
