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 = {}