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