moonbridge
view moonbridge_http.lua @ 53:aaf67ef45c22
Timeout management
| author | jbe | 
|---|---|
| date | Sun Mar 22 13:19:54 2015 +0100 (2015-03-22) | 
| parents | 2be59069a184 | 
| children | e3df61bf62f4 | 
 line source
     1 #!/usr/bin/env lua
     3 -- module preamble
     4 local _G, _M = _ENV, {}
     5 _ENV = setmetatable({}, {
     6   __index = function(self, key)
     7     local value = _M[key]; if value ~= nil then return value end
     8     return _G[key]
     9   end,
    10   __newindex = function(self, key, value) _M[key] = value end
    11 })
    13 -- function that encodes certain HTML entities:
    14 -- (not used by the library itself)
    15 function encode_html(text)
    16   return (
    17     string.gsub(
    18       text, '[<>&"]',
    19       function(char)
    20         if char == '<' then
    21           return "<"
    22         elseif char == '>' then
    23           return ">"
    24         elseif char == '&' then
    25           return "&"
    26         elseif char == '"' then
    27           return """
    28         end
    29       end
    30     )
    31   )
    33 end
    35 -- function that encodes special characters for URIs:
    36 -- (not used by the library itself)
    37 function encode_uri(text)
    38   return (
    39     string.gsub(text, "[^0-9A-Za-z_%.~-]",
    40       function (char)
    41         return string.format("%%%02x", string.byte(char))
    42       end
    43     )
    44   )
    45 end
    47 -- function undoing URL encoding:
    48 do
    49   local b0 = string.byte("0")
    50   local b9 = string.byte("9")
    51   local bA = string.byte("A")
    52   local bF = string.byte("F")
    53   local ba = string.byte("a")
    54   local bf = string.byte("f")
    55   function decode_uri(str)
    56     return (
    57       string.gsub(
    58         string.gsub(str, "%+", " "),
    59         "%%([0-9A-Fa-f][0-9A-Fa-f])",
    60         function(hex)
    61           local n1, n2 = string.byte(hex, 1, 2)
    62           if n1 >= b0 and n1 <= b9 then n1 = n1 - b0
    63           elseif n1 >= bA and n1 <= bF then n1 = n1 - bA + 10
    64           elseif n1 >= ba and n1 <= bf then n1 = n1 - ba + 10
    65           else error("Assertion failed") end
    66           if n2 >= b0 and n2 <= b9 then n2 = n2 - b0
    67           elseif n2 >= bA and n2 <= bF then n2 = n2 - bA + 10
    68           elseif n2 >= ba and n2 <= bf then n2 = n2 - ba + 10
    69           else error("Assertion failed") end
    70           return string.char(n1 * 16 + n2)
    71         end
    72       )
    73     )
    74   end
    75 end
    77 -- status codes that carry no response body (in addition to 1xx):
    78 -- (set to "zero_content_length" if Content-Length header is required)
    79 status_without_response_body = {
    80   ["101"] = true,  -- list 101 to allow protocol switch
    81   ["204"] = true,
    82   ["205"] = "zero_content_length",
    83   ["304"] = true
    84 }
    86 -- handling of GET/POST param tables:
    87 local new_params_list  -- defined later
    88 do
    89   local params_list_mapping = setmetatable({}, {__mode="k"})
    90   local function nextnonempty(tbl, key)
    91     while true do
    92       key = next(tbl, key)
    93       if key == nil then
    94         return nil
    95       end
    96       local value = tbl[key]
    97       if #value > 0 then
    98         return key, value
    99       end
   100     end
   101   end
   102   local function nextvalue(tbl, key)
   103     key = next(tbl, key)
   104     if key == nil then
   105       return nil
   106     end
   107     return key, tbl[key][1]
   108   end
   109   local params_list_metatable = {
   110     __index = function(self, key)
   111       local tbl = {}
   112       self[key] = tbl
   113       return tbl
   114     end,
   115     __pairs = function(self)
   116       return nextnonempty, self, nil
   117     end
   118   }
   119   local params_metatable = {
   120     __index = function(self, key)
   121       return params_list_mapping[self][key][1]
   122     end,
   123     __newindex = function(self, key, value)
   124       params_list_mapping[self][key] = {value}
   125     end,
   126     __pairs = function(self)
   127       return nextvalue, params_list_mapping[self], nil
   128     end
   129   }
   130   -- returns a table to store key value-list pairs (i.e. multiple values),
   131   -- and a second table automatically mapping keys to the first value
   132   -- using the key value-list pairs in the first table:
   133   new_params_list = function()
   134     local params_list = setmetatable({}, params_list_metatable)
   135     local params = setmetatable({}, params_metatable)
   136     params_list_mapping[params] = params_list
   137     return params_list, params
   138   end
   139 end
   140 -- parses URL encoded form data and stores it in
   141 -- a key value-list pairs structure that has to be
   142 -- previously obtained by calling by new_params_list():
   143 local function read_urlencoded_form(tbl, data)
   144   for rawkey, rawvalue in string.gmatch(data, "([^?=&]*)=([^?=&]*)") do
   145     local subtbl = tbl[decode_uri(rawkey)]
   146     subtbl[#subtbl+1] = decode_uri(rawvalue)
   147   end
   148 end
   150 -- function creating a HTTP handler:
   151 function generate_handler(handler, options)
   152   -- swap arguments if necessary (for convenience):
   153   if type(handler) ~= "function" and type(options) == "function" then
   154     handler, options = options, handler
   155   end
   156   -- process options:
   157   options = options or {}
   158   local preamble = ""  -- preamble sent with every(!) HTTP response
   159   do
   160     -- named arg "static_headers" is used to create the preamble:
   161     local s = options.static_headers
   162     local t = {}
   163     if s then
   164       if type(s) == "string" then
   165         for line in string.gmatch(s, "[^\r\n]+") do
   166           t[#t+1] = line
   167         end
   168       else
   169         for i, kv in ipairs(options.static_headers) do
   170           if type(kv) == "string" then
   171             t[#t+1] = kv
   172           else
   173             t[#t+1] = kv[1] .. ": " .. kv[2]
   174           end
   175         end
   176       end
   177     end
   178     t[#t+1] = ""
   179     preamble = table.concat(t, "\r\n")
   180   end
   181   -- desired chunk sizes:
   182   local input_chunk_size = options.maximum_input_chunk_size or options.chunk_size or 16384
   183   local output_chunk_size = options.minimum_output_chunk_size or options.chunk_size or 1024
   184   -- timeouts:
   185   local request_header_timeout = options.http_request_header_timeout
   186   local request_body_timeout   = options.http_request_body_timeout
   187   local request_timeout        = options.http_request_timeout
   188   local response_timeout       = options.http_timeout
   189   if request_header_timeout == nil then
   190     request_header_timeout = 60
   191   end
   192   if response_timeout == nil then
   193     response_timeout = 60
   194   end
   195   -- return connect handler:
   196   return function(socket)
   197     local survive = true  -- set to false if process shall be terminated later
   198     repeat
   199       -- timeout management:
   200       if request_header_timeout then
   201         timeout(request_header_timeout)
   202       elseif request_timeout then
   203         timeout(request_timeout)
   204       else
   205         timeout(0)
   206       end
   207       -- process named arguments "request_header_size_limit" and "request_body_size_limit":
   208       local remaining_header_size_limit = options.request_header_size_limit or 1024*1024
   209       local remaining_body_size_limit = options.request_body_size_limit or 64*1024*1024
   210       -- state variables for request handling:
   211       local output_state = "no_status_sent"  -- one of:
   212       --  "no_status_sent"        (initial state)
   213       --  "info_status_sent"      (1xx status code has been sent)
   214       --  "bodyless_status_sent"  (204/304 status code has been sent)
   215       --  "status_sent"           (regular status code has been sent)
   216       --  "headers_sent"          (headers have been terminated)
   217       --  "finished"              (request has been answered completely)
   218       local content_length    -- value of Content-Length header sent
   219       local bytes_sent = 0    -- number of bytes sent if Content-Length is set
   220       local chunk_parts = {}  -- list of chunks to send
   221       local chunk_bytes = 0   -- sum of lengths of chunks to send
   222       local connection_close_requested = false
   223       local connection_close_responded = false
   224       local headers_value_nil = {}          -- header values that are nil
   225       local request_body_content_length     -- Content-Length of request body
   226       local input_state = "pending"  -- one of:
   227       -- "pending"    (request body has not been processed yet)
   228       -- "deferred"   (request body processing is deferred)
   229       -- "inprogress" (request body is currently being read)
   230       -- "finished"   (request body has been read)
   231       local streamed_post_params         = {}  -- mapping from POST field name to stream function
   232       local streamed_post_param_patterns = {}  -- list of POST field pattern and stream function pairs
   233       -- object passed to handler (with methods, GET/POST params, etc.):
   234       local request
   235       -- handling I/O errors (including protocol violations):
   236       local socket_closed = false
   237       local function assert_output(retval, errmsg)
   238         if retval then
   239           return retval
   240         end
   241         request.faulty = true
   242         errmsg = "Could not send data to client: " .. errmsg
   243         io.stderr:write(errmsg, "\n")
   244         if not socket_closed then
   245           socket_closed = true
   246           socket:cancel()
   247         end
   248         error("Could not send data to client: " .. errmsg)
   249       end
   250       local function request_error(throw_error, status, text)
   251         local errmsg = "Error while reading request from client. Error response: " .. status
   252         if text then
   253           errmsg = errmsg .. " (" .. text .. ")"
   254         end
   255         io.stderr:write(errmsg, "\n")  -- needs to be written now, because of possible timeout error later
   256         if
   257           output_state == "no_status_sent" or
   258           output_state == "info_status_sent"
   259         then
   260           local error_response_status, errmsg2 = pcall(function()
   261             request:defer_reading()  -- don't read request body (because of possibly invalid state)
   262             request:send_status(status)
   263             request:send_header("Content-Type", "text/plain")
   264             request:send_header("Connection", "close")
   265             request:send_data(status, "\n")
   266             if text then
   267               request:send_data("\n", text, "\n")
   268             end
   269             request:finish()
   270           end)
   271           if not error_response_status and not request.faulty then
   272             request.faulty = true
   273             error("Unexpected error while sending error response: " .. errmsg2)
   274           end
   275         else
   276           if not socket_closed then
   277             socket_closed = true
   278             socket:cancel()
   279           end
   280         end
   281         if throw_error then
   282           request.faulty = true
   283           error(errmsg)
   284         else
   285           return survive
   286         end
   287       end
   288       local function assert_not_faulty()
   289         assert(not request.faulty, "Tried to use faulty request handle")
   290       end
   291       -- reads a number of bytes from the socket,
   292       -- optionally feeding these bytes chunk-wise
   293       -- into a callback function:
   294       local function read_body_bytes(remaining, callback)
   295         while remaining > 0 do
   296           local limit
   297           if remaining > input_chunk_size then
   298             limit = input_chunk_size
   299           else
   300             limit = remaining
   301           end
   302           local chunk = socket:read(limit)
   303           if not chunk or #chunk ~= limit then
   304             request_error(true, "400 Bad Request", "Unexpected EOF or read error while reading chunk of request body")
   305           end
   306           remaining = remaining - limit
   307           if callback then
   308             callback(chunk)
   309           end
   310         end
   311       end
   312       -- flushes or closes the socket (depending on
   313       -- whether "Connection: close" header was sent):
   314       local function finish_response()
   315         if connection_close_responded then
   316           -- close output stream:
   317           assert_output(socket.output:close())
   318           -- wait for EOF of peer to avoid immediate TCP RST condition:
   319           timeout(2, function()
   320             while socket.input:read(input_chunk_size) do end
   321           end)
   322           -- fully close socket:
   323           socket_closed = true  -- avoid double close on error
   324           assert_output(socket:close())
   325         else
   326           assert_output(socket:flush())
   327           request:stream_request_body()
   328         end
   329       end
   330       -- writes out buffered chunks (without flushing the socket):
   331       local function send_chunk()
   332         if chunk_bytes > 0 then
   333           assert_output(socket:write(string.format("%x\r\n", chunk_bytes)))
   334           for i = 1, #chunk_parts do
   335             assert_output(socket:write(chunk_parts[i]))
   336           end
   337           chunk_parts = {}
   338           chunk_bytes = 0
   339           assert_output(socket:write("\r\n"))
   340         end
   341       end
   342       -- terminate header section in response, optionally flushing:
   343       -- (may be called multiple times unless response is finished)
   344       local function finish_headers(flush)
   345         if output_state == "no_status_sent" then
   346           error("HTTP status has not been sent yet")
   347         elseif output_state == "finished" then
   348           error("Response has already been finished")
   349         elseif output_state == "info_status_sent" then
   350           assert_output(socket:write("\r\n"))
   351           assert_output(socket:flush())
   352           output_state = "no_status_sent"
   353         elseif output_state == "bodyless_status_sent" then
   354           if connection_close_requested and not connection_close_responded then
   355             request:send_header("Connection", "close")
   356           end
   357           assert_output(socket:write("\r\n"))
   358           finish_response()
   359           output_state = "finished"
   360         elseif output_state == "status_sent" then
   361           if not content_length then
   362             assert_output(socket:write("Transfer-Encoding: chunked\r\n"))
   363           end
   364           if connection_close_requested and not connection_close_responded then
   365             request:send_header("Connection", "close")
   366           end
   367           assert_output(socket:write("\r\n"))
   368           if request.method == "HEAD" then
   369             finish_response()
   370           elseif flush then
   371             assert_output(socket:flush())
   372           end
   373           output_state = "headers_sent"
   374         elseif output_state ~= "headers_sent" then
   375           error("Unexpected internal status in HTTP engine")
   376         end
   377       end
   378       -- create request object and set several functions and values:
   379       request = {
   380         -- error state:
   381         faulty = false,
   382         -- allow raw socket access:
   383         socket = socket,
   384         -- parsed cookies:
   385         cookies = {},
   386         -- send a HTTP response status (e.g. "200 OK"):
   387         send_status = function(self, value)
   388           assert_not_faulty()
   389           if input_state == "pending" then
   390             request:process_request_body()
   391           end
   392           if output_state == "info_status_sent" then
   393             assert_output(socket:write("\r\n"))
   394             assert_output(socket:flush())
   395           elseif output_state ~= "no_status_sent" then
   396             error("HTTP status has already been sent")
   397           end
   398           local status1 = string.sub(value, 1, 1)
   399           local status3 = string.sub(value, 1, 3)
   400           assert_output(socket:write("HTTP/1.1 ", value, "\r\n", preamble))
   401           local without_response_body = status_without_response_body[status3]
   402           if without_response_body then
   403             output_state = "bodyless_status_sent"
   404             if without_response_body == "zero_content_length" then
   405               request:send_header("Content-Length", 0)
   406             end
   407           elseif status1 == "1" then
   408             output_state = "info_status_sent"
   409           else
   410             output_state = "status_sent"
   411           end
   412         end,
   413         -- send a HTTP response header
   414         -- (key and value as separate args):
   415         send_header = function(self, key, value)
   416           assert_not_faulty()
   417           if output_state == "no_status_sent" then
   418             error("HTTP status has not been sent yet")
   419           elseif
   420             output_state ~= "info_status_sent" and
   421             output_state ~= "bodyless_status_sent" and
   422             output_state ~= "status_sent"
   423           then
   424             error("All HTTP headers have already been sent")
   425           end
   426           local key_lower = string.lower(key)
   427           if key_lower == "content-length" then
   428             if output_state == "info_status_sent" then
   429               error("Cannot set Content-Length for informational status response")
   430             end
   431             local new_content_length = assert(tonumber(value), "Invalid content-length")
   432             if content_length == nil then
   433               content_length = new_content_length
   434             elseif content_length == new_content_length then
   435               return
   436             else
   437               error("Content-Length has been set multiple times with different values")
   438             end
   439           elseif key_lower == "connection" then
   440             for entry in string.gmatch(string.lower(value), "[^,]+") do
   441               if string.match(entry, "^[ \t]*close[ \t]*$") then
   442                 if output_state == "info_status_sent" then
   443                   error("Cannot set \"Connection: close\" for informational status response")
   444                 end
   445                 if connection_close_responded then
   446                   return
   447                 else
   448                   connection_close_responded = true
   449                   break
   450                 end
   451               end
   452             end
   453           end
   454           assert_output(socket:write(key, ": ", value, "\r\n"))
   455         end,
   456         -- method to finish and flush headers:
   457         finish_headers = function()
   458           assert_not_faulty()
   459           finish_headers(true)
   460         end,
   461         -- send data for response body:
   462         send_data = function(self, ...)
   463           assert_not_faulty()
   464           if output_state == "info_status_sent" then
   465             error("No (non-informational) HTTP status has been sent yet")
   466           elseif output_state == "bodyless_status_sent" then
   467             error("Cannot send response data for body-less status message")
   468           end
   469           finish_headers(false)
   470           if output_state ~= "headers_sent" then
   471             error("Unexpected internal status in HTTP engine")
   472           end
   473           if request.method == "HEAD" then
   474             return
   475           end
   476           for i = 1, select("#", ...) do
   477             local str = tostring(select(i, ...))
   478             if #str > 0 then
   479               if content_length then
   480                 local bytes_to_send = #str
   481                 if bytes_sent + bytes_to_send > content_length then
   482                   assert_output(socket:write(string.sub(str, 1, content_length - bytes_sent)))
   483                   bytes_sent = content_length
   484                   error("Content length exceeded")
   485                 else
   486                   assert_output(socket:write(str))
   487                   bytes_sent = bytes_sent + bytes_to_send
   488                 end
   489               else
   490                 chunk_bytes = chunk_bytes + #str
   491                 chunk_parts[#chunk_parts+1] = str
   492               end
   493             end
   494           end
   495           if chunk_bytes >= output_chunk_size then
   496             send_chunk()
   497           end
   498         end,
   499         -- flush output buffer:
   500         flush = function(self)
   501           assert_not_faulty()
   502           send_chunk()
   503           assert_output(socket:flush())
   504         end,
   505         -- finish response:
   506         finish = function(self)
   507           assert_not_faulty()
   508           if output_state == "finished" then
   509             return
   510           elseif output_state == "info_status_sent" then
   511             error("Informational HTTP response can be finished with :finish_headers() method")
   512           end
   513           finish_headers(false)
   514           if output_state == "headers_sent" then
   515             if request.method ~= "HEAD" then
   516               if content_length then
   517                 if bytes_sent ~= content_length then
   518                   error("Content length not used")
   519                 end
   520               else
   521                 send_chunk()
   522                 assert_output(socket:write("0\r\n\r\n"))
   523               end
   524               finish_response()
   525             end
   526             output_state = "finished"
   527           elseif output_state ~= "finished" then
   528             error("Unexpected internal status in HTTP engine")
   529           end
   530         end,
   531         -- table mapping header field names to value-lists
   532         -- (raw access):
   533         headers = setmetatable({}, {
   534           __index = function(self, key)
   535             local lowerkey = string.lower(key)
   536             if lowerkey == key then
   537               return
   538             end
   539             local result = rawget(self, lowerkey)
   540             if result == nil then
   541               result = {}
   542             end
   543             self[lowerkey] = result
   544             self[key] = result
   545             return result
   546           end
   547         }),
   548         -- table mapping header field names to value-lists
   549         -- (for headers with comma separated values):
   550         headers_csv_table = setmetatable({}, {
   551           __index = function(self, key)
   552             local result = {}
   553             for i, line in ipairs(request.headers[key]) do
   554               for entry in string.gmatch(line, "[^,]+") do
   555                 local value = string.match(entry, "^[ \t]*(..-)[ \t]*$")
   556                 if value then
   557                   result[#result+1] = value
   558                 end
   559               end
   560             end
   561             self[key] = result
   562             return result
   563           end
   564         }),
   565         -- table mapping header field names to a comma separated string
   566         -- (for headers with comma separated values):
   567         headers_csv_string = setmetatable({}, {
   568           __index = function(self, key)
   569             local result = {}
   570             for i, line in ipairs(request.headers[key]) do
   571               result[#result+1] = line
   572             end
   573             result = string.concat(result, ", ")
   574             self[key] = result
   575             return result
   576           end
   577         }),
   578         -- table mapping header field names to a single string value
   579         -- (or false if header has been sent multiple times):
   580         headers_value = setmetatable({}, {
   581           __index = function(self, key)
   582             if headers_value_nil[key] then
   583               return nil
   584             end
   585             local result = nil
   586             local values = request.headers_csv_table[key]
   587             if #values == 0 then
   588               headers_value_nil[key] = true
   589             elseif #values == 1 then
   590               result = values[1]
   591             else
   592               result = false
   593             end
   594             self[key] = result
   595             return result
   596           end
   597         }),
   598         -- table mapping header field names to a flag table,
   599         -- indicating if the comma separated value contains certain entries:
   600         headers_flags = setmetatable({}, {
   601           __index = function(self, key)
   602             local result = setmetatable({}, {
   603               __index = function(self, key)
   604                 local lowerkey = string.lower(key)
   605                 local result = rawget(self, lowerkey) or false
   606                 self[lowerkey] = result
   607                 self[key] = result
   608                 return result
   609               end
   610             })
   611             for i, value in ipairs(request.headers_csv_table[key]) do
   612               result[string.lower(value)] = true
   613             end
   614             self[key] = result
   615             return result
   616           end
   617         }),
   618         -- register POST param stream handler for a single field name:
   619         stream_post_param = function(self, field_name, callback)
   620           assert_not_faulty()
   621           if input_state == "inprogress" or input_state == "finished" then
   622             error("Cannot register POST param streaming function if request body is already processed")
   623           end
   624           streamed_post_params[field_name] = callback
   625         end,
   626         -- register POST param stream handler for a field name pattern:
   627         stream_post_params = function(self, pattern, callback)
   628           assert_not_faulty()
   629           if input_state == "inprogress" or input_state == "finished" then
   630             error("Cannot register POST param streaming function if request body is already processed")
   631           end
   632           streamed_post_param_patterns[#streamed_post_param_patterns+1] = {pattern, callback}
   633         end,
   634         -- disables automatic request body processing on write
   635         -- (use with caution):
   636         defer_reading = function(self)
   637           assert_not_faulty()
   638           if input_state == "pending" then
   639             input_state = "deferred"
   640           end
   641         end,
   642         -- processes the request body and sets the request.post_params,
   643         -- request.post_params_list, request.meta_post_params, and
   644         -- request.meta_post_params_list values (can be called manually or
   645         -- automatically if post_params are accessed or data is written out)
   646         process_request_body = function(self)
   647           assert_not_faulty()
   648           if input_state == "finished" then
   649             return
   650           end
   651           local post_params_list, post_params = new_params_list()
   652           local content_type = request.headers_value["Content-Type"]
   653           if content_type then
   654             if
   655               content_type == "application/x-www-form-urlencoded" or
   656               string.match(content_type, "^application/x%-www%-form%-urlencoded *;")
   657             then
   658               read_urlencoded_form(post_params_list, request.body)
   659             else
   660               local boundary = string.match(
   661                 content_type,
   662                 '^multipart/form%-data[ \t]*[;,][ \t]*boundary="([^"]+)"$'
   663               ) or string.match(
   664                 content_type,
   665                 '^multipart/form%-data[ \t]*[;,][ \t]*boundary=([^"; \t]+)$'
   666               )
   667               if boundary then
   668                 local post_metadata_list, post_metadata = new_params_list()
   669                 boundary = "--" .. boundary
   670                 local headerdata = ""
   671                 local streamer
   672                 local field_name
   673                 local metadata = {}
   674                 local value_parts
   675                 local function default_streamer(chunk)
   676                   value_parts[#value_parts+1] = chunk
   677                 end
   678                 local function stream_part_finish()
   679                   if streamer == default_streamer then
   680                     local value = table.concat(value_parts)
   681                     value_parts = nil
   682                     if field_name then
   683                       local values = post_params_list[field_name]
   684                       values[#values+1] = value
   685                       local metadata_entries = post_metadata_list[field_name]
   686                       metadata_entries[#metadata_entries+1] = metadata
   687                     end
   688                   else
   689                     streamer()
   690                   end
   691                   headerdata   = ""
   692                   streamer     = nil
   693                   field_name   = nil
   694                   metadata     = {}
   695                 end
   696                 local function stream_part_chunk(chunk)
   697                   if streamer then
   698                     streamer(chunk)
   699                   else
   700                     headerdata = headerdata .. chunk
   701                     while true do
   702                       local line, remaining = string.match(headerdata, "^(.-)\r?\n(.*)$")
   703                       if not line then
   704                         break
   705                       end
   706                       if line == "" then
   707                         streamer = streamed_post_params[field_name]
   708                         if not streamer then
   709                           for i, rule in ipairs(streamed_post_param_patterns) do
   710                             if string.match(field_name, rule[1]) then
   711                               streamer = rule[2]
   712                               break
   713                             end
   714                           end
   715                         end
   716                         if not streamer then
   717                           value_parts = {}
   718                           streamer = default_streamer
   719                         end
   720                         streamer(remaining, field_name, metadata)
   721                         return
   722                       end
   723                       headerdata = remaining
   724                       local header_key, header_value = string.match(line, "^([^:]*):[ \t]*(.-)[ \t]*$")
   725                       if not header_key then
   726                         request_error(true, "400 Bad Request", "Invalid header in multipart/form-data part")
   727                       end
   728                       header_key = string.lower(header_key)
   729                       if header_key == "content-disposition" then
   730                         local escaped_header_value = string.gsub(header_value, '"[^"]*"', function(str)
   731                           return string.gsub(str, "=", "==")
   732                         end)
   733                         field_name = string.match(escaped_header_value, ';[ \t]*name="([^"]*)"')
   734                         if field_name then
   735                           field_name = string.gsub(field_name, "==", "=")
   736                         else
   737                           field_name = string.match(header_value, ';[ \t]*name=([^"; \t]+)')
   738                         end
   739                         metadata.file_name = string.match(escaped_header_value, ';[ \t]*filename="([^"]*)"')
   740                         if metadata.file_name then
   741                           metadata.file_name = string.gsub(metadata.file_name, "==", "=")
   742                         else
   743                           string.match(header_value, ';[ \t]*filename=([^"; \t]+)')
   744                         end
   745                       elseif header_key == "content-type" then
   746                         metadata.content_type = header_value
   747                       elseif header_key == "content-transfer-encoding" then
   748                         request_error(true, "400 Bad Request", "Content-transfer-encoding not supported by multipart/form-data parser")
   749                       end
   750                     end
   751                   end
   752                 end
   753                 local skippart   = true   -- ignore data until first boundary
   754                 local afterbound = false  -- interpret 2 bytes after boundary ("\r\n" or "--")
   755                 local terminated = false  -- final boundary read
   756                 local bigchunk = ""
   757                 request:stream_request_body(function(chunk)
   758                   if terminated then
   759                     return
   760                   end
   761                   bigchunk = bigchunk .. chunk
   762                   while true do
   763                     if afterbound then
   764                       if #bigchunk <= 2 then
   765                         return
   766                       end
   767                       local terminator = string.sub(bigchunk, 1, 2)
   768                       if terminator == "\r\n" then
   769                         afterbound = false
   770                         bigchunk = string.sub(bigchunk, 3)
   771                       elseif terminator == "--" then
   772                         terminated = true
   773                         bigchunk = nil
   774                         return
   775                       else
   776                         request_error(true, "400 Bad Request", "Error while parsing multipart body (expected CRLF or double minus)")
   777                       end
   778                     end
   779                     local pos1, pos2 = string.find(bigchunk, boundary, 1, true)
   780                     if not pos1 then
   781                       if not skippart then
   782                         local safe = #bigchunk-#boundary
   783                         if safe > 0 then
   784                           stream_part_chunk(string.sub(bigchunk, 1, safe))
   785                           bigchunk = string.sub(bigchunk, safe+1)
   786                         end
   787                       end
   788                       return
   789                     end
   790                     if not skippart then
   791                       stream_part_chunk(string.sub(bigchunk, 1, pos1 - 1))
   792                       stream_part_finish()
   793                     else
   794                       boundary = "\r\n" .. boundary
   795                       skippart = false
   796                     end
   797                     bigchunk = string.sub(bigchunk, pos2 + 1)
   798                     afterbound = true
   799                   end
   800                 end)
   801                 if not terminated then
   802                   request_error(true, "400 Bad Request", "Premature end of multipart/form-data request body")
   803                 end
   804                 request.post_metadata_list, request.post_metadata = post_metadata_list, post_metadata
   805               else
   806                 request_error(true, "415 Unsupported Media Type", "Unknown Content-Type of request body")
   807               end
   808             end
   809           end
   810           request.post_params_list, request.post_params = post_params_list, post_params
   811         end,
   812         -- stream request body to an (optional) callback function
   813         -- without processing it otherwise:
   814         stream_request_body = function(self, callback)
   815           assert_not_faulty()
   816           if input_state ~= "pending" and input_state ~= "deferred" then
   817             if callback then
   818               if input_state == "inprogress" then
   819                 error("Request body is currently being processed")
   820               else
   821                 error("Request body has already been processed")
   822               end
   823             end
   824             return
   825           end
   826           input_state = "inprogress"
   827           if request.headers_flags["Expect"]["100-continue"] then
   828             request:send_status("100 Continue")
   829             request:finish_headers()
   830           end
   831           if request.headers_flags["Transfer-Encoding"]["chunked"] then
   832             while true do
   833               local line = socket:readuntil("\n", 32 + remaining_body_size_limit)
   834               if not line then
   835                 request_error(true, "400 Bad Request", "Unexpected EOF while reading next chunk of request body")
   836               end
   837               local zeros, lenstr = string.match(line, "^(0*)([1-9A-Fa-f]+[0-9A-Fa-f]*)\r?\n$")
   838               local chunkext
   839               if lenstr then
   840                 chunkext = ""
   841               else
   842                 zeros, lenstr, chunkext = string.match(line, "^(0*)([1-9A-Fa-f]+[0-9A-Fa-f]*)([ \t;].-)\r?\n$")
   843               end
   844               if not lenstr or #lenstr > 13 then
   845                 request_error(true, "400 Bad Request", "Encoding error or unexpected EOF or read error while reading chunk of request body")
   846               end
   847               local len = tonumber("0x" .. lenstr)
   848               remaining_body_size_limit = remaining_body_size_limit - (#zeros + #chunkext + len)
   849               if remaining_body_size_limit < 0 then
   850                 request_error(true, "413 Request Entity Too Large", "Request body size limit exceeded")
   851               end
   852               if len == 0 then break end
   853               read_body_bytes(len, callback)
   854               local term = socket:readuntil("\n", 2)
   855               if term ~= "\r\n" and term ~= "\n" then
   856                 request_error(true, "400 Bad Request", "Encoding error while reading chunk of request body")
   857               end
   858             end
   859             while true do
   860               local line = socket:readuntil("\n", 2 + remaining_body_size_limit)
   861               if line == "\r\n" or line == "\n" then break end
   862               remaining_body_size_limit = remaining_body_size_limit - #line
   863               if remaining_body_size_limit < 0 then
   864                 request_error(true, "413 Request Entity Too Large", "Request body size limit exceeded while reading trailer section of chunked request body")
   865               end
   866             end
   867           elseif request_body_content_length then
   868             read_body_bytes(request_body_content_length, callback)
   869           end
   870           input_state = "finished"
   871         end
   872       }
   873       -- initialize tables for GET params in request object:
   874       request.get_params_list, request.get_params = new_params_list()
   875       -- add meta table to request object to allow access to "body" and POST params:
   876       setmetatable(request, {
   877         __index = function(self, key)
   878           if key == "body" then
   879             local chunks = {}
   880             request:stream_request_body(function(chunk)
   881               chunks[#chunks+1] = chunk
   882             end)
   883             self.body = table.concat(chunks)
   884             return self.body
   885           elseif
   886             key == "post_params_list" or key == "post_params" or
   887             key == "post_metadata_list" or key == "post_metadata"
   888           then
   889             request:process_request_body()
   890             return request[key]
   891           end
   892         end
   893       })
   894       -- read and parse request line:
   895       local line = socket:readuntil("\n", remaining_header_size_limit)
   896       if not line then return survive end
   897       remaining_header_size_limit = remaining_header_size_limit - #line
   898       if remaining_header_size_limit == 0 then
   899         return request_error(false, "414 Request-URI Too Long")
   900       end
   901       local target, proto
   902       request.method, target, proto =
   903         line:match("^([^ \t\r]+)[ \t]+([^ \t\r]+)[ \t]*([^ \t\r]*)[ \t]*\r?\n$")
   904       if not request.method then
   905         return request_error(false, "400 Bad Request")
   906       elseif proto ~= "HTTP/1.1" then
   907         return request_error(false, "505 HTTP Version Not Supported")
   908       end
   909       -- read and parse headers:
   910       while true do
   911         local line = socket:readuntil("\n", remaining_header_size_limit);
   912         remaining_header_size_limit = remaining_header_size_limit - #line
   913         if not line then
   914           return request_error(false, "400 Bad Request")
   915         end
   916         if line == "\r\n" or line == "\n" then
   917           break
   918         end
   919         if remaining_header_size_limit == 0 then
   920           return request_error(false, "431 Request Header Fields Too Large")
   921         end
   922         local key, value = string.match(line, "^([^ \t\r]+):[ \t]*(.-)[ \t]*\r?\n$")
   923         if not key then
   924           return request_error(false, "400 Bad Request")
   925         end
   926         local values = request.headers[key]
   927         values[#values+1] = value
   928       end
   929       -- process "Connection: close" header if existent:
   930       connection_close_requested = request.headers_flags["Connection"]["close"]
   931       -- process "Content-Length" header if existent:
   932       do
   933         local values = request.headers_csv_table["Content-Length"]
   934         if #values > 0 then
   935           request_body_content_length = tonumber(values[1])
   936           local proper_value = tostring(request_body_content_length)
   937           for i, value in ipairs(values) do
   938             value = string.match(value, "^0*(.*)")
   939             if value ~= proper_value then
   940               return request_error(false, "400 Bad Request", "Content-Length header(s) invalid")
   941             end
   942           end
   943           if request_body_content_length > remaining_body_size_limit then
   944             return request_error(false, "413 Request Entity Too Large", "Announced request body size is too big")
   945           end
   946         end
   947       end
   948       -- process "Transfer-Encoding" header if existent:
   949       do
   950         local flag = request.headers_flags["Transfer-Encoding"]["chunked"]
   951         local list = request.headers_csv_table["Transfer-Encoding"]
   952         if (flag and #list ~= 1) or (not flag and #list ~= 0) then
   953           return request_error(false, "400 Bad Request", "Unexpected Transfer-Encoding")
   954         end
   955       end
   956       -- process "Expect" header if existent:
   957       for i, value in ipairs(request.headers_csv_table["Expect"]) do
   958         if string.lower(value) ~= "100-continue" then
   959           return request_error(false, "417 Expectation Failed", "Unexpected Expect header")
   960         end
   961       end
   962       -- get mandatory Host header according to RFC 7230:
   963       request.host = request.headers_value["Host"]
   964       if not request.host then
   965         return request_error(false, "400 Bad Request", "No valid host header")
   966       end
   967       -- parse request target:
   968       request.path, request.query = string.match(target, "^/([^?]*)(.*)$")
   969       if not request.path then
   970         local host2
   971         host2, request.path, request.query = string.match(target, "^[Hh][Tt][Tt][Pp]://([^/?]+)/?([^?]*)(.*)$")
   972         if host2 then
   973           if request.host ~= host2 then
   974             return request_error(false, "400 Bad Request", "No valid host header")
   975           end
   976         elseif not (target == "*" and request.method == "OPTIONS") then
   977           return request_error(false, "400 Bad Request", "Invalid request target")
   978         end
   979       end
   980       -- parse GET params:
   981       if request.query then
   982         read_urlencoded_form(request.get_params_list, request.query)
   983       end
   984       -- parse cookies:
   985       for i, line in ipairs(request.headers["Cookie"]) do
   986         for rawkey, rawvalue in
   987           string.gmatch(line, "([^=; ]*)=([^=; ]*)")
   988         do
   989           request.cookies[decode_uri(rawkey)] = decode_uri(rawvalue)
   990         end
   991       end
   992       -- timeout management:
   993       if request_body_timeout then
   994         timeout(request_body_timeout)
   995         request:process_request_body()
   996       elseif request_timeout then
   997         timeout(request_timeout)
   998         request:process_request_body()
   999       end
  1000       timeout(response_timeout or 0)
  1001       -- call underlying handler and remember boolean result:
  1002       if handler(request) ~= true then survive = false end
  1003       -- finish request (unless already done by underlying handler):
  1004       request:finish()
  1005     until connection_close_responded
  1006     return survive
  1007   end
  1008 end
  1010 return _M
