Mercurial > prosody-modules
comparison mod_websocket/mod_websocket.lua @ 677:eeb41cd5e9f3
mod_websocket: Move frame handling into a separate function
author | Florian Zeitz <florob@babelmonkeys.de> |
---|---|
date | Sat, 26 May 2012 03:09:09 +0200 |
parents | 54fa9d6d7809 |
children | d141375ece4b |
comparison
equal
deleted
inserted
replaced
676:54fa9d6d7809 | 677:eeb41cd5e9f3 |
---|---|
16 local st = require "util.stanza"; | 16 local st = require "util.stanza"; |
17 local sm_new_session, sm_destroy_session = sessionmanager.new_session, sessionmanager.destroy_session; | 17 local sm_new_session, sm_destroy_session = sessionmanager.new_session, sessionmanager.destroy_session; |
18 local uuid_generate = require "util.uuid".generate; | 18 local uuid_generate = require "util.uuid".generate; |
19 local sha1 = require "util.hashes".sha1; | 19 local sha1 = require "util.hashes".sha1; |
20 local base64 = require "util.encodings".base64.encode; | 20 local base64 = require "util.encodings".base64.encode; |
21 local band = require "bit".band; | |
21 local bxor = require "bit".bxor; | 22 local bxor = require "bit".bxor; |
22 local tohex = require "bit".tohex; | 23 local tohex = require "bit".tohex; |
23 | 24 |
24 module:depends("http") | 25 module:depends("http") |
25 | 26 |
36 | 37 |
37 local sessions = module:shared("sessions"); | 38 local sessions = module:shared("sessions"); |
38 | 39 |
39 local stream_callbacks = { default_ns = "jabber:client", handlestanza = core_process_stanza }; | 40 local stream_callbacks = { default_ns = "jabber:client", handlestanza = core_process_stanza }; |
40 local listener = {}; | 41 local listener = {}; |
42 | |
43 -- Websocket helpers | |
44 local function parse_frame(frame) | |
45 local result = {}; | |
46 local pos = 1; | |
47 local length_bytes = 0; | |
48 local counter = 0; | |
49 local tmp_byte; | |
50 | |
51 tmp_byte = string.byte(frame, pos); | |
52 result.FIN = band(tmp_byte, 0x80) > 0; | |
53 result.RSV1 = band(tmp_byte, 0x40) > 0; | |
54 result.RSV2 = band(tmp_byte, 0x20) > 0; | |
55 result.RSV3 = band(tmp_byte, 0x10) > 0; | |
56 result.opcode = band(tmp_byte, 0x0F) > 0; | |
57 | |
58 pos = pos + 1; | |
59 tmp_byte = string.byte(frame, pos); | |
60 result.MASK = band(tmp_byte, 0x80) > 0; | |
61 result.length = band(tmp_byte, 0x7F); | |
62 | |
63 if result.length == 126 then | |
64 length_bytes = 2; | |
65 result.length = 0; | |
66 elseif result.length == 127 then | |
67 length_bytes = 8; | |
68 result.length = 0; | |
69 end | |
70 | |
71 for i = 1, length_bytes do | |
72 pos = pos + 1; | |
73 result.length = result.length * 255 + string.byte(frame, pos); | |
74 end | |
75 | |
76 if result.MASK then | |
77 result.key = {string.byte(frame, pos+1), string.byte(frame, pos+2), | |
78 string.byte(frame, pos+3), string.byte(frame, pos+4)} | |
79 | |
80 pos = pos + 5; | |
81 result.data = ""; | |
82 for i = pos, pos + result.length - 1 do | |
83 result.data = result.data .. string.char(bxor(result.key[counter+1], string.byte(frame, i))); | |
84 counter = (counter + 1) % 4; | |
85 end | |
86 else | |
87 result.data = frame:sub(pos + 1, pos + result.length); | |
88 end | |
89 | |
90 return result; | |
91 end | |
41 | 92 |
42 --- Stream events handlers | 93 --- Stream events handlers |
43 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; | 94 local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; |
44 local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; | 95 local default_stream_attr = { ["xmlns:stream"] = "http://etherx.jabber.org/streams", xmlns = stream_callbacks.default_ns, version = "1.0", id = "" }; |
45 | 96 |
59 -- We don't serve this host... | 110 -- We don't serve this host... |
60 session:close{ condition = "host-unknown", text = "This server does not serve "..tostring(session.host)}; | 111 session:close{ condition = "host-unknown", text = "This server does not serve "..tostring(session.host)}; |
61 return; | 112 return; |
62 end | 113 end |
63 | 114 |
115 -- COMPAT: Current client implementations need this to be self-closing | |
64 send("<?xml version='1.0'?>"..(tostring(st.stanza("stream:stream", { | 116 send("<?xml version='1.0'?>"..(tostring(st.stanza("stream:stream", { |
65 xmlns = 'jabber:client', ["xmlns:stream"] = 'http://etherx.jabber.org/streams'; | 117 xmlns = 'jabber:client', ["xmlns:stream"] = 'http://etherx.jabber.org/streams'; |
66 id = session.streamid, from = session.host, version = '1.0', ["xml:lang"] = 'en' }):top_tag()):gsub(">", "/>"))); | 118 id = session.streamid, from = session.host, version = '1.0', ["xml:lang"] = 'en' }):top_tag()):gsub(">", "/>"))); |
67 | 119 |
68 (session.log or log)("debug", "Sent reply <stream:stream> to client"); | 120 (session.log or log)("debug", "Sent reply <stream:stream> to client"); |
183 session.stream:reset(); | 235 session.stream:reset(); |
184 end | 236 end |
185 | 237 |
186 local filter = session.filter; | 238 local filter = session.filter; |
187 function session.data(data) | 239 function session.data(data) |
188 local off = 0; | 240 data = parse_frame(data).data; |
189 local len = string.byte(data, 2) - 0x80; | 241 module:log("debug", "Websocket received: %s %i", data, #data) |
190 if len == 126 then | 242 -- COMPAT: Current client implementations send a self-closing <stream:stream> |
191 off = 2; | 243 data = data:gsub("/>$", ">"); |
192 elseif len ==127 then | 244 |
193 off = 8; | 245 data = filter("bytes/in", data); |
194 end | |
195 local key = {string.byte(data, off+3), string.byte(data, off+4), string.byte(data, off+5), string.byte(data, off+6)} | |
196 local decoded = ""; | |
197 local counter = 0; | |
198 for i = off+7, #data do | |
199 decoded = decoded .. string.char(bxor(key[counter+1], string.byte(data, i))); | |
200 counter = (counter + 1) % 4; | |
201 end | |
202 module:log("debug", "Websocket received: %s %i", decoded, #decoded) | |
203 decoded = decoded:gsub("/>$", ">"); | |
204 | |
205 data = filter("bytes/in", decoded); | |
206 if data then | 246 if data then |
207 local ok, err = stream:feed(data); | 247 local ok, err = stream:feed(data); |
208 if ok then return; end | 248 if ok then return; end |
209 log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); | 249 log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); |
210 session:close("not-well-formed"); | 250 session:close("not-well-formed"); |