# HG changeset patch # User jbe # Date 1432684264 -7200 # Node ID 99a70d18e47c4f6dc231cc815e33acf18a0c1e45 # Parent 0c4221702ce17876b2e3faf20c6fe003462a2133 Further work on new HTTP layer (code cleanup, work on body reading) diff -r 0c4221702ce1 -r 99a70d18e47c moonbridge_http.lua --- a/moonbridge_http.lua Tue May 26 02:06:17 2015 +0200 +++ b/moonbridge_http.lua Wed May 27 01:51:04 2015 +0200 @@ -260,9 +260,6 @@ self._survive = true self._socket_set = {[socket] = true} self._faulty = false - self._consume_input = self._drain_input - self._headers = {} - self._headers_value_nil = {} self._connection_close_requested = false self._connection_close_responded = false self:_create_magictable("headers") @@ -270,7 +267,6 @@ self:_create_magictable("headers_csv_string") self:_create_magictable("headers_value") self:_create_magictable("headers_flags") - self.cookies = {} repeat -- wait for input: if not moonbridge_io.poll(self._socket_set, nil, self._request_idle_timeout) then @@ -302,16 +298,48 @@ end end end + -- prepare reading of body: + self._read_body_coro = coroutine.wrap(self._read_body) + -- set timeout for application handler: timeout(self._response_timeout or 0) + -- call application handler: if self._application_handler(self) ~= true then self._survive = false end + -- enforce request:finish() request:finish() + -- reset timeout of application handler timeout(0) until self._connection_close_responded return self._survive end +function request_pt:_drain_input() + self._read_body_coro = "drain" +end + +function request_pt:_consume_some_input() + local coro = self._read_body_coro + if coro == "drain" then + local bytes, status = self._socket:drain_nb(self._input_chunk_size) + if status == "eof" then + coro = nil + end + elseif coro then + local retval = coro(self) + if retval ~= nil then + coro = nil -- can't consume more data + end + end +end + +function request_pt:_consume_all_input() + while self._read_body_coro do + self._poll(socket_set) + self:_consume_some_input() + end +end + function request_pt:_error(status, explanation) end @@ -352,6 +380,8 @@ end end -- read and parse headers: + self._headers = {} + self._headers_value_nil = {} while true do local line, status = self:_read(remaining, "\n"); if status == "maxlen" then @@ -433,6 +463,7 @@ end elseif not (target == "*" and self.method == "OPTIONS") then self:_error("400 Bad Request", "Invalid request target") + return false end end -- parse GET params: @@ -441,6 +472,7 @@ self.get_params = get_first_values(self.get_params_list) end -- parse cookies: + self.cookies = {} for i, line in ipairs(self.headers["Cookie"]) do for rawkey, rawvalue in string.gmatch(line, "([^=; ]*)=([^=; ]*)") @@ -448,10 +480,11 @@ self.cookies[decode_uri(rawkey)] = decode_uri(rawvalue) end end + -- indicate success: + return true end function request_pt:_read_body() - self:_assert_not_faulty() local remaining = self._body_size_limit if request.headers_flags["Transfer-Encoding"]["chunked"] then while true do @@ -503,7 +536,8 @@ if line == "\r\n" or line == "\n" then break end remaining = remaining - #line if remaining < 0 then - request_error(true, "413 Request Entity Too Large", "Request body size limit exceeded while reading trailer section of chunked request body") + self:_error("413 Request Entity Too Large", "Request body size limit exceeded while reading trailer section of chunked request body") + return false end end elseif request_body_content_length then @@ -511,6 +545,8 @@ return false end end + -- indicate success: + return true end function request_pt:_read_body_bytes(remaining, callback) @@ -531,6 +567,7 @@ self._body_streamer(chunk) end end + return true end function request_pt:_assert_not_faulty() @@ -538,7 +575,7 @@ end function request_pt:_write_yield() - self:_consume_input() + self:_consume_some_input() self._poll(self._socket_set, self._socket_set) end @@ -550,10 +587,6 @@ assert(self._socket:write_call(self._write_yield_closure, ...)) end -function request_pt:_drain_input() - socket:drain_nb(self._input_chunk_size) -end - -- function creating a HTTP handler: function generate_handler(handler, options) -- swap arguments if necessary (for convenience):