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
parents e254cf49e14d
children 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);