moonbridge

changeset 183:478d6237e17a

Fixes and code-cleanup in HTTP module
author jbe
date Fri Jun 19 16:54:33 2015 +0200 (2015-06-19)
parents a79ed835b6de
children 97d3ca77c86a
files moonbridge_http.lua
line diff
     1.1 --- a/moonbridge_http.lua	Fri Jun 19 03:09:58 2015 +0200
     1.2 +++ b/moonbridge_http.lua	Fri Jun 19 16:54:33 2015 +0200
     1.3 @@ -336,6 +336,11 @@
     1.4        setmetatable(request, request_mt)
     1.5        -- callback for request body streaming:
     1.6        local process_body_chunk
     1.7 +      -- function to enable draining:
     1.8 +      local function enable_drain()
     1.9 +        consume = drain
    1.10 +        process_body_chunk = nil  -- allow for early garbage collection
    1.11 +      end
    1.12        -- local variables to track the state:
    1.13        local state = "init"        -- one of:
    1.14        --  "init"                  (initial state)
    1.15 @@ -350,6 +355,7 @@
    1.16        local close_requested = false  -- "Connection: close" requested
    1.17        local close_responded = false  -- "Connection: close" sent
    1.18        local content_length = nil     -- value of Content-Length header sent
    1.19 +      local bytes_sent = 0           -- number of bytes sent if Content-Length is set
    1.20        local chunk_parts = {}         -- list of chunks to send
    1.21        local chunk_bytes = 0          -- sum of lengths of chunks to send
    1.22        local streamed_post_params         = {}  -- mapping from POST field name to stream function
    1.23 @@ -385,7 +391,7 @@
    1.24        local function finish()
    1.25          if close_responded then
    1.26            -- discard any input:
    1.27 -          consume = drain
    1.28 +          enable_drain()
    1.29            -- close output stream:
    1.30            send_flush()
    1.31            assert_close(socket:finish())
    1.32 @@ -646,6 +652,7 @@
    1.33                    if not terminated then
    1.34                      request_error(true, "400 Bad Request", "Premature end of multipart/form-data request body")
    1.35                    end
    1.36 +                  request.post_params_list, request.post_params = post_params_list, post_params
    1.37                    request.post_metadata_list, request.post_metadata = post_metadata_list, post_metadata
    1.38                  end
    1.39                  if terminated then
    1.40 @@ -700,10 +707,13 @@
    1.41        -- function to prepare body processing:
    1.42        local function prepare()
    1.43          assert_not_faulty()
    1.44 +        if state ~= "init" then
    1.45 +          return
    1.46 +        end
    1.47          if process_body_chunk == nil then
    1.48            default_request_body_handling()
    1.49          end
    1.50 -        if state ~= "init" then
    1.51 +        if state ~= "init" then  -- re-check if state is still "init"
    1.52            return
    1.53          end
    1.54          consume = coroutine.wrap(read_body)
    1.55 @@ -722,9 +732,8 @@
    1.56          then
    1.57            error("All HTTP headers have already been sent")
    1.58          end
    1.59 -        local old_state = state
    1.60 -        state = "faulty"
    1.61 -        consume = drain
    1.62 +        local old_state, state = state, "faulty"
    1.63 +        enable_drain()
    1.64          close_requested = true
    1.65          if old_state == "init" then
    1.66            state = "no_status_sent"
    1.67 @@ -732,15 +741,14 @@
    1.68            state = old_state
    1.69          end
    1.70        end
    1.71 -      --
    1.72        -- method to send a HTTP response status (e.g. "200 OK"):
    1.73        function request:send_status(status)
    1.74          prepare()
    1.75 -        local old_state = state
    1.76 -        state = "faulty"
    1.77 +        local old_state, state = state, "faulty"
    1.78          if old_state == "info_status_sent" then
    1.79            send_flush("\r\n")
    1.80          elseif old_state ~= "no_status_sent" then
    1.81 +          state = old_state
    1.82            error("HTTP status has already been sent")
    1.83          end
    1.84          local status1 = string.sub(status, 1, 1)
    1.85 @@ -770,9 +778,11 @@
    1.86          then
    1.87            error("All HTTP headers have already been sent")
    1.88          end
    1.89 +        local old_state, state = state, "faulty"
    1.90          local key_lower = string.lower(key)
    1.91          if key_lower == "content-length" then
    1.92 -          if state == "info_status_sent" then
    1.93 +          if old_state == "info_status_sent" then
    1.94 +            state = old_state
    1.95              error("Cannot set Content-Length for informational status response")
    1.96            end
    1.97            local cl = assert(tonumber(value), "Invalid content-length")
    1.98 @@ -786,7 +796,8 @@
    1.99          elseif key_lower == "connection" then
   1.100            for entry in string.gmatch(string.lower(value), "[^,]+") do
   1.101              if string.match(entry, "^[ \t]*close[ \t]*$") then
   1.102 -              if state == "info_status_sent" then
   1.103 +              if old_state == "info_status_sent" then
   1.104 +                state = old_state
   1.105                  error("Cannot set \"Connection: close\" for informational status response")
   1.106                end
   1.107                close_responded = true
   1.108 @@ -795,6 +806,7 @@
   1.109            end
   1.110          end
   1.111          send(socket:write(key, ": ", value, "\r\n"))
   1.112 +        state = old_state
   1.113        end
   1.114        -- function to terminate header section in response, optionally flushing:
   1.115        -- (may be called multiple times unless response is finished)
   1.116 @@ -802,6 +814,7 @@
   1.117          if state == "finished" then
   1.118            error("Response has already been finished")
   1.119          elseif state == "info_status_sent" then
   1.120 +          state = "faulty"
   1.121            send_flush("\r\n")
   1.122            state = "no_status_sent"
   1.123          elseif state == "bodyless_status_sent" then
   1.124 @@ -839,18 +852,19 @@
   1.125        -- method to send body data:
   1.126        function request:send_data(...)
   1.127          assert_not_faulty()
   1.128 -        if output_state == "info_status_sent" then
   1.129 +        if state == "info_status_sent" then
   1.130            error("No (non-informational) HTTP status has been sent yet")
   1.131 -        elseif output_state == "bodyless_status_sent" then
   1.132 +        elseif state == "bodyless_status_sent" then
   1.133            error("Cannot send response data for body-less status message")
   1.134          end
   1.135          finish_headers(false)
   1.136 -        if output_state ~= "headers_sent" then
   1.137 +        if state ~= "headers_sent" then
   1.138            error("Unexpected internal status in HTTP engine")
   1.139          end
   1.140          if request.method == "HEAD" then
   1.141            return
   1.142          end
   1.143 +        state = "faulty"
   1.144          for i = 1, select("#", ...) do
   1.145            local str = tostring(select(i, ...))
   1.146            if #str > 0 then
   1.147 @@ -871,6 +885,7 @@
   1.148          if chunk_bytes >= output_chunk_size then
   1.149            send_chunk()
   1.150          end
   1.151 +        state = "headers_sent"
   1.152        end
   1.153        -- method to flush output buffer:
   1.154        function request:flush()
   1.155 @@ -908,21 +923,21 @@
   1.156        -- method to register POST param stream handler for a single field name:
   1.157        function request:stream_post_param(field_name, callback)
   1.158          if state ~= "init" then
   1.159 -          error("Cannot setup request body streamer at this stage")
   1.160 +          error("Cannot setup request body streamer at this stage anymore")
   1.161          end
   1.162          streamed_post_params[field_name] = callback
   1.163        end
   1.164        -- method to register POST param stream handler for a field name pattern:
   1.165        function request:stream_post_params(pattern, callback)
   1.166          if state ~= "init" then
   1.167 -          error("Cannot setup request body streamer at this stage")
   1.168 +          error("Cannot setup request body streamer at this stage anymore")
   1.169          end
   1.170          streamed_post_param_patterns[#streamed_post_param_patterns+1] = {pattern, callback}
   1.171        end
   1.172        -- method to register request body stream handler
   1.173        function request:set_request_body_streamer(callback)
   1.174          if state ~= "init" then
   1.175 -          error("Cannot setup request body streamer at this stage")
   1.176 +          error("Cannot setup request body streamer at this stage anymore")
   1.177          end
   1.178          local inprogress = false
   1.179          local buffer = {}

Impressum / About Us