Software /
code /
prosody
Comparison
plugins/mod_bosh.lua @ 7382:c8923f882274
Merge 0.10->trunk
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Tue, 19 Apr 2016 17:10:42 +0200 |
parent | 7359:a5a080c12c96 |
parent | 7381:a05bf94646ba |
child | 7388:77a3ef937152 |
comparison
equal
deleted
inserted
replaced
7373:9b37aaea68e9 | 7382:c8923f882274 |
---|---|
20 local log = logger.init("mod_bosh"); | 20 local log = logger.init("mod_bosh"); |
21 local initialize_filters = require "util.filters".initialize; | 21 local initialize_filters = require "util.filters".initialize; |
22 local math_min = math.min; | 22 local math_min = math.min; |
23 local xpcall, tostring, type = xpcall, tostring, type; | 23 local xpcall, tostring, type = xpcall, tostring, type; |
24 local traceback = debug.traceback; | 24 local traceback = debug.traceback; |
25 local runner = require"util.async".runner; | |
26 | 25 |
27 local xmlns_streams = "http://etherx.jabber.org/streams"; | 26 local xmlns_streams = "http://etherx.jabber.org/streams"; |
28 local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; | 27 local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; |
29 local xmlns_bosh = "http://jabber.org/protocol/httpbind"; -- (hard-coded into a literal in session.send) | 28 local xmlns_bosh = "http://jabber.org/protocol/httpbind"; -- (hard-coded into a literal in session.send) |
30 | 29 |
61 | 60 |
62 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; | 61 local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; |
63 local os_time = os.time; | 62 local os_time = os.time; |
64 | 63 |
65 -- All sessions, and sessions that have no requests open | 64 -- All sessions, and sessions that have no requests open |
66 local sessions = module:shared("sessions"); | 65 local sessions, inactive_sessions = module:shared("sessions", "inactive_sessions"); |
67 | 66 |
68 -- Used to respond to idle sessions (those with waiting requests) | 67 -- Used to respond to idle sessions (those with waiting requests) |
68 local waiting_requests = module:shared("waiting_requests"); | |
69 function on_destroy_request(request) | 69 function on_destroy_request(request) |
70 log("debug", "Request destroyed: %s", tostring(request)); | 70 log("debug", "Request destroyed: %s", tostring(request)); |
71 waiting_requests[request] = nil; | |
71 local session = sessions[request.context.sid]; | 72 local session = sessions[request.context.sid]; |
72 if session then | 73 if session then |
73 local requests = session.requests; | 74 local requests = session.requests; |
74 for i, r in ipairs(requests) do | 75 for i, r in ipairs(requests) do |
75 if r == request then | 76 if r == request then |
79 end | 80 end |
80 | 81 |
81 -- If this session now has no requests open, mark it as inactive | 82 -- If this session now has no requests open, mark it as inactive |
82 local max_inactive = session.bosh_max_inactive; | 83 local max_inactive = session.bosh_max_inactive; |
83 if max_inactive and #requests == 0 then | 84 if max_inactive and #requests == 0 then |
84 if session.inactive_timer then | 85 inactive_sessions[session] = os_time() + max_inactive; |
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"); | |
89 (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive); | 86 (session.log or log)("debug", "BOSH session marked as inactive (for %ds)", max_inactive); |
90 end | 87 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); | |
102 end | 88 end |
103 end | 89 end |
104 | 90 |
105 local function set_cross_domain_headers(response) | 91 local function set_cross_domain_headers(response) |
106 local headers = response.headers; | 92 local headers = response.headers; |
130 response.context = context; | 116 response.context = context; |
131 | 117 |
132 local headers = response.headers; | 118 local headers = response.headers; |
133 headers.content_type = "text/xml; charset=utf-8"; | 119 headers.content_type = "text/xml; charset=utf-8"; |
134 | 120 |
135 if cross_domain and request.headers.origin then | 121 if cross_domain and event.request.headers.origin then |
136 set_cross_domain_headers(response); | 122 set_cross_domain_headers(response); |
137 end | 123 end |
138 | 124 |
139 -- stream:feed() calls the stream_callbacks, so all stanzas in | 125 -- stream:feed() calls the stream_callbacks, so all stanzas in |
140 -- the body are processed in this next line before it returns. | 126 -- the body are processed in this next line before it returns. |
141 -- In particular, the streamopened() stream callback is where | 127 -- In particular, the streamopened() stream callback is where |
142 -- much of the session logic happens, because it's where we first | 128 -- much of the session logic happens, because it's where we first |
143 -- get to see the 'sid' of this request. | 129 -- get to see the 'sid' of this request. |
144 if not stream:feed(body) then | 130 local ok, err = stream:feed(body); |
145 module:log("warn", "Error parsing BOSH payload") | 131 if not ok then |
146 return 400; | 132 module:log("warn", "Error parsing BOSH payload; %s", err) |
133 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", | |
134 ["xmlns:stream"] = xmlns_streams, condition = "bad-request" }); | |
135 return tostring(close_reply); | |
147 end | 136 end |
148 | 137 |
149 -- Stanzas (if any) in the request have now been processed, and | 138 -- Stanzas (if any) in the request have now been processed, and |
150 -- we take care of the high-level BOSH logic here, including | 139 -- we take care of the high-level BOSH logic here, including |
151 -- giving a response or putting the request "on hold". | 140 -- giving a response or putting the request "on hold". |
198 session:close(); | 187 session:close(); |
199 return nil; | 188 return nil; |
200 else | 189 else |
201 return true; -- Inform http server we shall reply later | 190 return true; -- Inform http server we shall reply later |
202 end | 191 end |
192 elseif response.finished then | |
193 return; -- A response has been sent already | |
203 end | 194 end |
204 module:log("warn", "Unable to associate request with a session (incomplete request?)"); | 195 module:log("warn", "Unable to associate request with a session (incomplete request?)"); |
205 return 400; | 196 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", |
197 ["xmlns:stream"] = xmlns_streams, condition = "item-not-found" }); | |
198 return tostring(close_reply) .. "\n"; | |
206 end | 199 end |
207 | 200 |
208 function after_bosh_wait(now, request, session) | 201 function after_bosh_wait(now, request, session) |
209 if request.conn then | 202 if request.conn then |
210 session.send(""); | 203 session.send(""); |
261 log("debug", "BOSH body open (sid: %s)", sid or "<none>"); | 254 log("debug", "BOSH body open (sid: %s)", sid or "<none>"); |
262 if not sid then | 255 if not sid then |
263 -- New session request | 256 -- New session request |
264 context.notopen = nil; -- Signals that we accept this opening tag | 257 context.notopen = nil; -- Signals that we accept this opening tag |
265 | 258 |
266 -- TODO: Sanity checks here (rid, to, known host, etc.) | 259 local to_host = nameprep(attr.to); |
267 if not hosts[attr.to] then | 260 local rid = tonumber(attr.rid); |
261 local wait = tonumber(attr.wait); | |
262 if not to_host then | |
263 log("debug", "BOSH client tried to connect to invalid host: %s", tostring(attr.to)); | |
264 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", | |
265 ["xmlns:stream"] = xmlns_streams, condition = "improper-addressing" }); | |
266 response:send(tostring(close_reply)); | |
267 return; | |
268 elseif not hosts[to_host] then | |
268 -- Unknown host | 269 -- Unknown host |
269 log("debug", "BOSH client tried to connect to unknown host: %s", tostring(attr.to)); | 270 log("debug", "BOSH client tried to connect to unknown host: %s", tostring(attr.to)); |
270 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", | 271 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", |
271 ["xmlns:stream"] = xmlns_streams, condition = "host-unknown" }); | 272 ["xmlns:stream"] = xmlns_streams, condition = "host-unknown" }); |
272 response:send(tostring(close_reply)); | 273 response:send(tostring(close_reply)); |
273 return; | 274 return; |
274 end | 275 end |
276 if not rid or (not wait and attr.wait or wait < 0 or wait % 1 ~= 0) then | |
277 log("debug", "BOSH client sent invalid rid or wait attributes: rid=%s, wait=%s", tostring(attr.rid), tostring(attr.wait)); | |
278 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", | |
279 ["xmlns:stream"] = xmlns_streams, condition = "bad-request" }); | |
280 response:send(tostring(close_reply)); | |
281 return; | |
282 end | |
283 | |
284 rid = rid - 1; | |
285 wait = math_min(wait, bosh_max_wait); | |
275 | 286 |
276 -- New session | 287 -- New session |
277 sid = new_uuid(); | 288 sid = new_uuid(); |
278 local session = { | 289 local session = { |
279 type = "c2s_unauthed", conn = {}, sid = sid, rid = tonumber(attr.rid)-1, host = attr.to, | 290 type = "c2s_unauthed", conn = {}, sid = sid, rid = rid-1, host = attr.to, |
280 bosh_version = attr.ver, bosh_wait = math_min(attr.wait, bosh_max_wait), streamid = sid, | 291 bosh_version = attr.ver, bosh_wait = wait, streamid = sid, |
281 bosh_hold = BOSH_DEFAULT_HOLD, bosh_max_inactive = BOSH_DEFAULT_INACTIVITY, | 292 bosh_hold = BOSH_DEFAULT_HOLD, bosh_max_inactive = BOSH_DEFAULT_INACTIVITY, |
282 requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream, | 293 requests = { }, send_buffer = {}, reset_stream = bosh_reset_stream, |
283 close = bosh_close_stream, dispatch_stanza = core_process_stanza, notopen = true, | 294 close = bosh_close_stream, dispatch_stanza = core_process_stanza, notopen = true, |
284 log = logger.init("bosh"..sid), secure = consider_bosh_secure or request.secure, | 295 log = logger.init("bosh"..sid), secure = consider_bosh_secure or request.secure, |
285 ip = get_ip_from_request(request); | 296 ip = get_ip_from_request(request); |
418 | 429 |
419 function stream_callbacks.error(context, error) | 430 function stream_callbacks.error(context, error) |
420 log("debug", "Error parsing BOSH request payload; %s", error); | 431 log("debug", "Error parsing BOSH request payload; %s", error); |
421 if not context.sid then | 432 if not context.sid then |
422 local response = context.response; | 433 local response = context.response; |
423 response.status_code = 400; | 434 local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", |
424 response:send(); | 435 ["xmlns:stream"] = xmlns_streams, condition = "bad-request" }); |
436 response:send(tostring(close_reply)); | |
425 return; | 437 return; |
426 end | 438 end |
427 | 439 |
428 local session = sessions[context.sid]; | 440 local session = sessions[context.sid]; |
429 if error == "stream-error" then -- Remote stream error, we close normally | 441 if error == "stream-error" then -- Remote stream error, we close normally |