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"); |
