1
0
mirror of https://github.com/lammertb/libhttp.git synced 2025-12-22 04:02:04 +03:00

Performance optimization for the form handling example in Lua

Optimization of example for question ("issue") #70:

Concatenating many strings in a loop in Lua is not efficient.
The standard solution for this is a Lua string builder pattern:
Store all strings in a table and use table.concat
This commit is contained in:
bel
2015-03-18 00:27:55 +01:00
parent 821a36af17
commit a1d9858c15

View File

@@ -1,64 +1,114 @@
mg.write("HTTP/1.0 200 OK\r\n") function add_data(data)
mg.write("Connection: close\r\n") local additional_data = mg.read()
mg.write("Content-Type: text/plain; charset=utf-8\r\n") if additional_data then
mg.write("Cache-Control: max-age=0, must-revalidate\r\n") return data .. additional_data, true
mg.write("\r\n") end
return data, false
-- Read the entire body data (POST content) into "bdata" variable. end
bdata = ""
repeat function parse_multipart_form(got_field)
local add_data = mg.read()
if add_data then local data, ok = add_data("")
bdata = bdata .. add_data if not ok then
end return "Can not read data"
until (add_data == nil); end
-- Get the boundary string. --[[
bs = "--" .. ((mg.request_info.content_type):upper():match("BOUNDARY=(.*)")); local b = mg.request_info.content_type:upper():find("BOUNDARY=");
if b then
-- The POST data has to start with the boundary string. b = mg.request_info.content_type:sub(b+9)
-- Check this and remove the starting boundary. end
if bdata:sub(1, #bs) ~= bs then if not b or #b<16 or #b>1024 then
error "invalid format of POST data" return false, "Boundary string not reasonable"
end end
bdata = bdata:sub(#bs) ]]
local b = "--" .. mg.request_info.content_type:upper():match("BOUNDARY=(.*)");
-- The boundary now starts with CR LF.
bs = "\r\n" .. bs --b = "--" .. b
if data:sub(1, #b) ~= b then
-- Now loop through all the parts return false, "Multipart body does not start properly"
while #bdata>4 do end
-- Find the header of new part. data = data:sub(#b)
part_header_end = bdata:find("\r\n\r\n", 1, true) b = "\r\n" .. b
-- Parse the header. -- while there are unread parts
h = bdata:sub(1, part_header_end+2) while #data>0 and data~="--\r\n" do
for key,val in h:gmatch("([^%:\r\n]*)%s*%:%s*([^\r\n]*)\r\n") do
if key:upper() == "CONTENT-DISPOSITION" then local name = nil
form_field_name = val:match('name=%"([^%"]*)%"') local value = nil
file_name = val:match('filename=%"([^%"]*)%"') local file_name = nil
end local file_type = nil
end
-- isolate the header of the new part
-- Remove the header from "bdata". local part_header_end
bdata = bdata:sub(part_header_end+4) repeat
part_header_end = data:find("\r\n\r\n", 1, true)
-- Find the end of the body by locating the boundary string. if not part_header_end then
part_body_end = bdata:find(bs, 1, true) data, ok = add_data(data)
if not ok then
-- Isolate the content, and drop it from "bdata". return false, "protocol violation: header does not end properly"
form_field_value = bdata:sub(1,part_body_end-1) end
bdata = bdata:sub(part_body_end+#bs) end
until part_header_end
-- Now the data (file content or field value) is isolated: We know form_field_name and form_field_value.
-- Here the script should do something useful with the data. This example just sends it back to the client. -- parse the header of the new part
mg.write("Field name: " .. form_field_name .. "\r\n") local header = {}
local len = #form_field_value for k,v in data:sub(1,part_header_end+2):gmatch("([^%:\r\n]*)%s*%:%s*([^\r\n]*)\r\n") do
if len<50 then header[k] = v
mg.write("Field value: " .. form_field_value .. "\r\n") local kupper = k:upper()
else if (kupper=="CONTENT-DISPOSITION") then
mg.write("Field value: " .. form_field_value:sub(1, 40) .. " .. (" .. len .. " bytes)\r\n") name = v:match('name=%"([^%"]*)%"')
end file_name = v:match('filename=%"([^%"]*)%"')
mg.write("\r\n") elseif (kupper=="CONTENT-TYPE") then
file_type = v
end end
end
-- isolate the body of the new part
local part_body_end
data = data:sub(part_header_end+4)
repeat
part_body_end = data:find(b, 1, true)
if not part_body_end then
data, ok = add_data(data)
if not ok then
return false, "protocol violation: body does not end properly"
end
end
until part_body_end
local value = data:sub(1,part_body_end-1)
data = data:sub(part_body_end+#b)
data = add_data(data)
-- send the result to the caller
got_field(name, value, file_name, file_type)
end
return true, ""
end
mg.write("HTTP/1.0 200 OK\r\n")
mg.write("Connection: close\r\n")
mg.write("Content-Type: text/plain; charset=utf-8\r\n")
mg.write("Cache-Control: max-age=0, must-revalidate\r\n")
mg.write("\r\n")
mg.write("Parse request:\r\n")
function fetch(k, v, fn, ft)
mg.write(k .. " = " .. v .. "\r\n")
end
ok, errtxt = parse_multipart_form(fetch)
if not ok then
mg.write("Error: " .. errtxt .. "\r\n")
else
mg.write("Parsing OK\r\n")
end