9    [206] = "Partial Content",
 
  10    [301] = "Moved Permanently",
 
  12    [304] = "Not Modified",
 
  13    [400] = "Bad Request",
 
  16    [405] = "Method Not Allowed",
 
  17    [408] = "Request Time-out",
 
  18    [411] = "Length Required",
 
  19    [412] = "Precondition Failed",
 
  20    [416] = "Requested range not satisfiable",
 
  21    [500] = "Internal Server Error",
 
  22    [503] = "Server Unavailable",
 
  25 -- call this function passing an empy table. use that table for successive calls.
 
  27    req.content_length = os.getenv("CONTENT_LENGTH")
 
  28    req.request_method = os.getenv("REQUEST_METHOD")
 
  29    if req.request_method == "POST" then
 
  30       req.content_type, req.boundary = string.match(os.getenv("CONTENT_TYPE"),"^(multipart/form%-data); boundary=\"?(.+)\"?$")
 
  31       req.boundary = "--" .. req.boundary
 
  33    -- this is useful only if you have /tmp on tmpfs like in openwrt. otherwise can be set to ""
 
  38 -- this function is needed to clean temp file since and hide implementation details
 
  39 function _M.cleanup(req)
 
  40    for k, v in pairs(req.post) do
 
  41       for j, v in pairs(req.post[k]) do
 
  42          if req.post[k][j].tempname then
 
  43             os.remove(req.post[k][j].tempname) -- if file unused
 
  44             os.remove("/tmp/" .. string.match(req.post[k][j].tempname,"^" .. req.tempdir .. "(.+)"))
 
  51 -- return chunk (string), found (boolean)
 
  52 local function chunkread(del)
 
  54    local del = del or "\r\n"
 
  56    buf = io.read(math.max(0,blocksize + #del - #prevbuf))
 
  57    if prevbuf ~= "" then buf = prevbuf .. ( buf or "" ); prevbuf = "" end
 
  58    if not buf then return end
 
  60    s, e = string.find(buf,del,1,true)
 
  63       prevbuf = string.sub(buf,e+1)
 
  64       buf = string.sub(buf,1,s-1)
 
  66       prevbuf = string.sub(buf,math.min(blocksize,#buf)+1)
 
  67       buf = string.sub(buf,1,math.min(blocksize,#buf))
 
  74 function _M.parse_request_body (req)
 
  75    local chunk, found, type, tempname, tempfile
 
  78    -- read first boundary line
 
  79    chunk, found = chunkread(req.boundary)
 
  80    chunk, found = chunkread("\r\n")
 
  82       -- read part headers and get parameters value
 
  84          chunk, found = chunkread("\r\n")
 
  85          if not found then return 400, "Malformed POST. Missing Part Header or Part Header too long." end
 
  86          string.gsub(chunk, ';%s*([^%s=]+)="(.-[^\\])"', function(k, v) param[k] = v end)
 
  87          param.type = param.type or string.match(chunk, "^Content%-Type: (.+)$")
 
  90       -- prepare file data read
 
  91       if not param.name then return 400, "Malformed POST. Check Header parameters." end
 
  93       param.tempname = req.tempdir .. string.match(os.tmpname(),  "^/tmp(.+)")
 
  94       tempfile = io.open(param.tempname, "w")
 
  96       -- read part body content until boundary
 
  98          chunk, found = chunkread("\r\n" .. req.boundary)
 
  99          if not chunk then return 400, "Malformed POST. Incomplete Part received." end
 
 100          tempfile:write(chunk)
 
 101          param.size = param.size + #chunk
 
 104       req.post[param.name] = req.post[param.name] or {}
 
 105       table.insert(req.post[param.name], 1, param)
 
 108       -- read after boundary. if CRLF ("") repeat. if "--" end POST processing
 
 109       chunk, found = chunkread("\r\n")
 
 112    if found and chunk == "--" then return 0, "OK" end
 
 113    return 400, "Malformed POST. Boundary not properly ended with CRLF or --."