Comparison

plugins/mod_websocket.lua @ 11120:b2331f3dfeea

Merge 0.11->trunk
author Matthew Wild <mwild1@gmail.com>
date Wed, 30 Sep 2020 09:50:33 +0100
parent 11114:6a608ecb3471
child 11384:f9edf26c66fc
comparison
equal deleted inserted replaced
11119:68df52bf08d5 11120:b2331f3dfeea
31 local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 10 * 1024 * 1024); 31 local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 10 * 1024 * 1024);
32 local frame_buffer_limit = module:get_option_number("websocket_frame_buffer_limit", 2 * stanza_size_limit); 32 local frame_buffer_limit = module:get_option_number("websocket_frame_buffer_limit", 2 * stanza_size_limit);
33 local frame_fragment_limit = module:get_option_number("websocket_frame_fragment_limit", 8); 33 local frame_fragment_limit = module:get_option_number("websocket_frame_fragment_limit", 8);
34 local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5); 34 local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5);
35 local consider_websocket_secure = module:get_option_boolean("consider_websocket_secure"); 35 local consider_websocket_secure = module:get_option_boolean("consider_websocket_secure");
36 local cross_domain = module:get_option_set("cross_domain_websocket", {}); 36 local cross_domain = module:get_option("cross_domain_websocket");
37 if cross_domain:contains("*") or cross_domain:contains(true) then 37 if cross_domain ~= nil then
38 cross_domain = true; 38 module:log("info", "The 'cross_domain_websocket' option has been deprecated");
39 end 39 end
40
41 local function check_origin(origin)
42 if cross_domain == true then
43 return true;
44 end
45 return cross_domain:contains(origin);
46 end
47
48 local xmlns_framing = "urn:ietf:params:xml:ns:xmpp-framing"; 40 local xmlns_framing = "urn:ietf:params:xml:ns:xmpp-framing";
49 local xmlns_streams = "http://etherx.jabber.org/streams"; 41 local xmlns_streams = "http://etherx.jabber.org/streams";
50 local xmlns_client = "jabber:client"; 42 local xmlns_client = "jabber:client";
51 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; 43 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'};
52 44
90 end 82 end
91 elseif reason.name then -- a stanza 83 elseif reason.name then -- a stanza
92 stream_error = reason; 84 stream_error = reason;
93 end 85 end
94 end 86 end
95 log("debug", "Disconnecting client, <stream:error> is: %s", tostring(stream_error)); 87 log("debug", "Disconnecting client, <stream:error> is: %s", stream_error);
96 session.send(stream_error); 88 session.send(stream_error);
97 end 89 end
98 90
99 session.send(st.stanza("close", { xmlns = xmlns_framing })); 91 session.send(st.stanza("close", { xmlns = xmlns_framing }));
100 function session.send() return false; end 92 function session.send() return false; end
141 end 133 end
142 134
143 return data; 135 return data;
144 end 136 end
145 137
138 local default_get_response_body = [[<!DOCTYPE html><html><head><title>Websocket</title></head><body>
139 <p>It works! Now point your WebSocket client to this URL to connect to Prosody.</p>
140 </body></html>]]
141 local websocket_get_response_body = module:get_option_string("websocket_get_response_body", default_get_response_body)
142
146 local function validate_frame(frame, max_length) 143 local function validate_frame(frame, max_length)
147 local opcode, length = frame.opcode, frame.length; 144 local opcode, length = frame.opcode, frame.length;
148 145
149 if max_length and length > max_length then 146 if max_length and length > max_length then
150 return false, 1009, "Payload too large"; 147 return false, 1009, "Payload too large";
205 local request, response = event.request, event.response; 202 local request, response = event.request, event.response;
206 local conn = response.conn; 203 local conn = response.conn;
207 204
208 conn.starttls = false; -- Prevent mod_tls from believing starttls can be done 205 conn.starttls = false; -- Prevent mod_tls from believing starttls can be done
209 206
210 if not request.headers.sec_websocket_key then 207 if not request.headers.sec_websocket_key or request.method ~= "GET" then
211 response.headers.content_type = "text/html"; 208 response.headers.content_type = "text/html";
212 return [[<!DOCTYPE html><html><head><title>Websocket</title></head><body> 209 return websocket_get_response_body;
213 <p>It works! Now point your WebSocket client to this URL to connect to Prosody.</p>
214 </body></html>]];
215 end 210 end
216 211
217 local wants_xmpp = contains_token(request.headers.sec_websocket_protocol or "", "xmpp"); 212 local wants_xmpp = contains_token(request.headers.sec_websocket_protocol or "", "xmpp");
218 213
219 if not wants_xmpp then 214 if not wants_xmpp then
220 module:log("debug", "Client didn't want to talk XMPP, list of protocols was %s", request.headers.sec_websocket_protocol or "(empty)"); 215 module:log("debug", "Client didn't want to talk XMPP, list of protocols was %s", request.headers.sec_websocket_protocol or "(empty)");
221 return 501; 216 return 501;
222 end
223
224 if not check_origin(request.headers.origin or "") then
225 module:log("debug", "Origin %s is not allowed by 'cross_domain_websocket' [ %s ]", request.headers.origin or "(missing header)", cross_domain);
226 return 403;
227 end 217 end
228 218
229 local function websocket_close(code, message) 219 local function websocket_close(code, message)
230 conn:write(build_close(code, message)); 220 conn:write(build_close(code, message));
231 conn:close(); 221 conn:close();
362 }; 352 };
363 }); 353 });
364 354
365 function module.add_host(module) 355 function module.add_host(module)
366 module:hook("c2s-read-timeout", keepalive, -0.9); 356 module:hook("c2s-read-timeout", keepalive, -0.9);
367 357 end
368 if cross_domain ~= true then
369 local url = require "socket.url";
370 local ws_url = module:http_url("websocket", "xmpp-websocket");
371 local url_components = url.parse(ws_url);
372 -- The 'Origin' consists of the base URL without path
373 url_components.path = nil;
374 local this_origin = url.build(url_components);
375 local local_cross_domain = module:get_option_set("cross_domain_websocket", { this_origin });
376 if local_cross_domain:contains(true) then
377 module:log("error", "cross_domain_websocket = true only works in the global section");
378 return;
379 end
380
381 -- Don't add / remove something added by another host
382 -- This might be weird with random load order
383 local_cross_domain:exclude(cross_domain);
384 cross_domain:include(local_cross_domain);
385 module:log("debug", "cross_domain = %s", tostring(cross_domain));
386 function module.unload()
387 cross_domain:exclude(local_cross_domain);
388 end
389 end
390 end