# HG changeset patch # User jbe # Date 1434327295 -7200 # Node ID d4f5d6a7d4016d13026e939e28812d4e64fd1f55 # Parent 625ab06babe9e91a8af82d3996ede71fbf6b4889 Work on new HTTP module implementation diff -r 625ab06babe9 -r d4f5d6a7d401 moonbridge_http.lua --- a/moonbridge_http.lua Mon Jun 15 00:07:08 2015 +0200 +++ b/moonbridge_http.lua Mon Jun 15 02:14:55 2015 +0200 @@ -235,6 +235,9 @@ end -- handle requests in a loop: repeat + -- copy limits: + local remaining_header_size_limit = header_size_limit + local remaining_body_size_limit = body_size_limit -- table for caching nil values: local headers_value_nil = {} -- create a new request object: @@ -402,6 +405,111 @@ send("\r\n") end end + -- function to report an error: + local function request_error(throw_error, status, text) + local errmsg = "Error while reading request from client. Error response: " .. status + if text then + errmsg = errmsg .. " (" .. text .. ")" + end + if + state == "init" or + state == "prepare" or + state == "no_status_sent" or + state == "info_status_sent" + then + local error_response_status, errmsg2 = pcall(function() + request:monologue() + request:send_status(status) + request:send_header("Content-Type", "text/plain") + request:send_data(status, "\n") + if text then + request:send_data("\n", text, "\n") + end + request:finish() + end) + if not error_response_status then + error("Unexpected error while sending error response: " .. errmsg2) + end + elseif state ~= "faulty" then + assert_close(socket:reset()) + end + if throw_error then + error(errmsg) + else + return survive + end + end + -- callback for request body streaming: + local process_body_chunk + -- reads a number of bytes from the socket, + -- optionally feeding these bytes chunk-wise + -- into a callback function: + local function read_body_bytes(remaining) + while remaining > 0 do + local limit + if remaining > input_chunk_size then + limit = input_chunk_size + else + limit = remaining + end + local chunk = socket:read_yield(limit) + if not chunk then + request_error(true, "400 Bad Request", "Read error while reading chunk of request body") + end + if #chunk ~= limit then + request_error(true, "400 Bad Request", "Unexpected EOF while reading chunk of request body") + end + remaining = remaining - limit + if process_body_chunk then + process_body_chunk(chunk) + end + end + end + -- coroutine for request body processing: + local function read_body() + if request.headers_flags["Transfer-Encoding"]["chunked"] then + while true do + local line, status = socket:read_yield(32 + remaining_body_size_limit, "\n") + if not line then + request_error(true, "400 Bad Request", "Read error while reading next chunk of request body") + end + if status == "eof" then + request_error(true, "400 Bad Request", "Unexpected EOF while reading next chunk of request body") + end + local zeros, lenstr = string.match(line, "^(0*)([1-9A-Fa-f]+[0-9A-Fa-f]*)\r?\n$") + local chunkext + if lenstr then + chunkext = "" + else + zeros, lenstr, chunkext = string.match(line, "^(0*)([1-9A-Fa-f]+[0-9A-Fa-f]*)([ \t;].-)\r?\n$") + end + if not lenstr or #lenstr > 13 then + request_error(true, "400 Bad Request", "Encoding error while reading chunk of request body") + end + local len = tonumber("0x" .. lenstr) + remaining_body_size_limit = remaining_body_size_limit - (#zeros + #chunkext + len) + if remaining_body_size_limit < 0 then + request_error(true, "413 Request Entity Too Large", "Request body size limit exceeded") + end + if len == 0 then break end + read_body_bytes(len) + local term = socket:read(2, "\n") + if term ~= "\r\n" and term ~= "\n" then + request_error(true, "400 Bad Request", "Encoding error while reading chunk of request body") + end + end + while true do + local line = socket:read(2 + remaining_body_size_limit, "\n") + if line == "\r\n" or line == "\n" then break end + remaining_body_size_limit = remaining_body_size_limit - #line + if remaining_body_size_limit < 0 then + request_error(true, "413 Request Entity Too Large", "Request body size limit exceeded while reading trailer section of chunked request body") + end + end + elseif request_body_content_length then + read_body_bytes(request_body_content_length) + end + end -- function to prepare (or skip) body processing: local function prepare() assert_not_faulty() @@ -608,40 +716,6 @@ error("Unexpected internal status in HTTP engine") end end - -- function to report an error: - local function request_error(throw_error, status, text) - local errmsg = "Error while reading request from client. Error response: " .. status - if text then - errmsg = errmsg .. " (" .. text .. ")" - end - if - state == "init" or - state == "prepare" or - state == "no_status_sent" or - state == "info_status_sent" - then - local error_response_status, errmsg2 = pcall(function() - request:monologue() - request:send_status(status) - request:send_header("Content-Type", "text/plain") - request:send_data(status, "\n") - if text then - request:send_data("\n", text, "\n") - end - request:finish() - end) - if not error_response_status then - error("Unexpected error while sending error response: " .. errmsg2) - end - elseif state ~= "faulty" then - assert_close(socket:reset()) - end - if throw_error then - error(errmsg) - else - return survive - end - end -- wait for input: if not poll(socket_set, nil, request_idle_timeout) then return request_error(false, "408 Request Timeout", "Idle connection timed out")