Software / code / prosody-modules
Comparison
mod_websocket/mod_websocket.lua @ 1027:6a2dfa8af421
mod_websocket: Optimize string concatenation
| author | Florian Zeitz <florob@babelmonkeys.de> |
|---|---|
| date | Thu, 30 May 2013 23:36:58 +0200 |
| parent | 1026:e254cf49e14d |
| child | 1028:81065638299d |
comparison
equal
deleted
inserted
replaced
| 1026:e254cf49e14d | 1027:6a2dfa8af421 |
|---|---|
| 44 -- Websocket helpers | 44 -- Websocket helpers |
| 45 local function parse_frame(frame) | 45 local function parse_frame(frame) |
| 46 local result = {}; | 46 local result = {}; |
| 47 local pos = 1; | 47 local pos = 1; |
| 48 local length_bytes = 0; | 48 local length_bytes = 0; |
| 49 local counter = 0; | |
| 50 local tmp_byte; | 49 local tmp_byte; |
| 51 | 50 |
| 52 if #frame < 2 then return; end | 51 if #frame < 2 then return; end |
| 53 | 52 |
| 54 tmp_byte = s_byte(frame, pos); | 53 tmp_byte = s_byte(frame, pos); |
| 79 end | 78 end |
| 80 | 79 |
| 81 if #frame < (2 + length_bytes + (result.MASK and 4 or 0) + result.length) then return; end | 80 if #frame < (2 + length_bytes + (result.MASK and 4 or 0) + result.length) then return; end |
| 82 | 81 |
| 83 if result.MASK then | 82 if result.MASK then |
| 83 local counter = 0; | |
| 84 local data = {}; | |
| 84 result.key = {s_byte(frame, pos+1), s_byte(frame, pos+2), | 85 result.key = {s_byte(frame, pos+1), s_byte(frame, pos+2), |
| 85 s_byte(frame, pos+3), s_byte(frame, pos+4)} | 86 s_byte(frame, pos+3), s_byte(frame, pos+4)} |
| 86 | 87 |
| 87 pos = pos + 5; | 88 pos = pos + 5; |
| 88 result.data = ""; | |
| 89 for i = pos, pos + result.length - 1 do | 89 for i = pos, pos + result.length - 1 do |
| 90 result.data = result.data .. s_char(bxor(result.key[counter+1], s_byte(frame, i))); | 90 data[#data+1] = s_char(bxor(result.key[counter+1], s_byte(frame, i))); |
| 91 counter = (counter + 1) % 4; | 91 counter = (counter + 1) % 4; |
| 92 end | 92 end |
| 93 result.data = t_concat(data, ""); | |
| 93 else | 94 else |
| 94 result.data = frame:sub(pos + 1, pos + result.length); | 95 result.data = frame:sub(pos + 1, pos + result.length); |
| 95 end | 96 end |
| 96 | 97 |
| 97 return result, 2 + length_bytes + (result.MASK and 4 or 0) + result.length; | 98 return result, 2 + length_bytes + (result.MASK and 4 or 0) + result.length; |
| 98 end | 99 end |
| 99 | 100 |
| 100 local function build_frame(desc) | 101 local function build_frame(desc) |
| 101 local length; | 102 local length; |
| 102 local result = ""; | 103 local result = {}; |
| 103 local data = desc.data or ""; | 104 local data = desc.data or ""; |
| 104 | 105 |
| 105 result = result .. s_char(0x80 * (desc.FIN and 1 or 0) + desc.opcode); | 106 result[#result+1] = s_char(0x80 * (desc.FIN and 1 or 0) + desc.opcode); |
| 106 | 107 |
| 107 length = #data; | 108 length = #data; |
| 108 if length <= 125 then -- 7-bit length | 109 if length <= 125 then -- 7-bit length |
| 109 result = result .. s_char(length); | 110 result[#result+1] = s_char(length); |
| 110 elseif length <= 0xFFFF then -- 2-byte length | 111 elseif length <= 0xFFFF then -- 2-byte length |
| 111 result = result .. s_char(126); | 112 result[#result+1] = s_char(126); |
| 112 result = result .. s_char(rshift(length, 8)) .. s_char(length%0x100); | 113 result[#result+1] = s_char(rshift(length, 8)) .. s_char(length%0x100); |
| 113 else -- 8-byte length | 114 else -- 8-byte length |
| 115 result[#result+1] = s_char(127); | |
| 114 local length_bytes = {}; | 116 local length_bytes = {}; |
| 115 result = result .. s_char(127); | |
| 116 for i = 8, 1, -1 do | 117 for i = 8, 1, -1 do |
| 117 length_bytes[i] = s_char(length % 0x100); | 118 length_bytes[i] = s_char(length % 0x100); |
| 118 length = rshift(length, 8); | 119 length = rshift(length, 8); |
| 119 end | 120 end |
| 120 result = result .. t_concat(length_bytes, ""); | 121 result[#result+1] = t_concat(length_bytes, ""); |
| 121 end | 122 end |
| 122 | 123 |
| 123 result = result .. data; | 124 result[#result+1] = data; |
| 124 | 125 |
| 125 return result; | 126 return t_concat(result, ""); |
| 126 end | 127 end |
| 127 | 128 |
| 128 --- Filter stuff | 129 --- Filter stuff |
| 129 function handle_request(event, path) | 130 function handle_request(event, path) |
| 130 local request, response = event.request, event.response; | 131 local request, response = event.request, event.response; |
| 187 return false; | 188 return false; |
| 188 end | 189 end |
| 189 | 190 |
| 190 -- Valid cases | 191 -- Valid cases |
| 191 if frame.opcode == 0x0 then -- Continuation frame | 192 if frame.opcode == 0x0 then -- Continuation frame |
| 192 dataBuffer = dataBuffer .. frame.data; | 193 dataBuffer[#dataBuffer+1] = frame.data; |
| 193 elseif frame.opcode == 0x1 then -- Text frame | 194 elseif frame.opcode == 0x1 then -- Text frame |
| 194 dataBuffer = frame.data; | 195 dataBuffer = {frame.data}; |
| 195 elseif frame.opcode == 0x2 then -- Binary frame | 196 elseif frame.opcode == 0x2 then -- Binary frame |
| 196 websocket_close(1003, "Only text frames are supported"); | 197 websocket_close(1003, "Only text frames are supported"); |
| 197 return; | 198 return; |
| 198 elseif frame.opcode == 0x8 then -- Close request | 199 elseif frame.opcode == 0x8 then -- Close request |
| 199 websocket_close(1000, "Goodbye"); | 200 websocket_close(1000, "Goodbye"); |
| 206 log("warn", "Received frame with unsupported opcode %i", frame.opcode); | 207 log("warn", "Received frame with unsupported opcode %i", frame.opcode); |
| 207 return ""; | 208 return ""; |
| 208 end | 209 end |
| 209 | 210 |
| 210 if frame.FIN then | 211 if frame.FIN then |
| 211 data = dataBuffer; | 212 local data = t_concat(dataBuffer, ""); |
| 212 dataBuffer = nil; | 213 dataBuffer = nil; |
| 213 | |
| 214 return data; | 214 return data; |
| 215 end | 215 end |
| 216 return ""; | 216 return ""; |
| 217 end | 217 end |
| 218 | 218 |
| 219 conn:setlistener(c2s_listener); | 219 conn:setlistener(c2s_listener); |
| 220 c2s_listener.onconnect(conn); | 220 c2s_listener.onconnect(conn); |
| 221 | 221 |
| 222 local frameBuffer = ""; | 222 local frameBuffer = ""; |
| 223 add_filter(sessions[conn], "bytes/in", function(data) | 223 add_filter(sessions[conn], "bytes/in", function(data) |
| 224 local cache = ""; | 224 local cache = {}; |
| 225 frameBuffer = frameBuffer .. data; | 225 frameBuffer = frameBuffer .. data; |
| 226 local frame, length = parse_frame(frameBuffer); | 226 local frame, length = parse_frame(frameBuffer); |
| 227 | 227 |
| 228 while frame do | 228 while frame do |
| 229 frameBuffer = frameBuffer:sub(length + 1); | 229 frameBuffer = frameBuffer:sub(length + 1); |
| 230 local result = handle_frame(frame); | 230 local result = handle_frame(frame); |
| 231 if not result then return; end | 231 if not result then return; end |
| 232 cache = cache .. result; | 232 cache[#cache+1] = result; |
| 233 frame, length = parse_frame(frameBuffer); | 233 frame, length = parse_frame(frameBuffer); |
| 234 end | 234 end |
| 235 return cache; | 235 return t_concat(cache, ""); |
| 236 | |
| 237 end); | 236 end); |
| 238 | 237 |
| 239 add_filter(sessions[conn], "bytes/out", function(data) | 238 add_filter(sessions[conn], "bytes/out", function(data) |
| 240 return build_frame({ FIN = true, opcode = 0x01, data = tostring(data)}); | 239 return build_frame({ FIN = true, opcode = 0x01, data = tostring(data)}); |
| 241 end); | 240 end); |