# HG changeset patch # User jbe # Date 1434211377 -7200 # Node ID 00f01d945e13c38adcd31cac496d1bf7398b7080 # Parent 27dc025e76cc0a1d9e01a86026afe8f36b7a92c6 Work on new HTTP module implementation diff -r 27dc025e76cc -r 00f01d945e13 moonbridge_http.lua --- a/moonbridge_http.lua Sat Jun 13 00:55:01 2015 +0200 +++ b/moonbridge_http.lua Sat Jun 13 18:02:57 2015 +0200 @@ -188,8 +188,97 @@ repeat -- create a new request object: local request = { + -- allow access to underlying socket: socket = socket, - cookies = {} + -- cookies are simply stored in a table: + cookies = {}, + -- table mapping header field names to value-lists + -- (raw access, but case-insensitive): + headers = setmetatable({}, { + __index = function(self, key) + local lowerkey = string.lower(key) + if lowerkey == key then + return + end + local result = rawget(self, lowerkey) + if result == nil then + result = {} + end + self[lowerkey] = result + self[key] = result + return result + end + }), + -- table mapping header field names to value-lists + -- (for headers with comma separated values): + headers_csv_table = setmetatable({}, { + __index = function(self, key) + local result = {} + for i, line in ipairs(request.headers[key]) do + for entry in string.gmatch(line, "[^,]+") do + local value = string.match(entry, "^[ \t]*(..-)[ \t]*$") + if value then + result[#result+1] = value + end + end + end + self[key] = result + return result + end + }), + -- table mapping header field names to a comma separated string + -- (for headers with comma separated values): + headers_csv_string = setmetatable({}, { + __index = function(self, key) + local result = {} + for i, line in ipairs(request.headers[key]) do + result[#result+1] = line + end + result = string.concat(result, ", ") + self[key] = result + return result + end + }), + -- table mapping header field names to a single string value + -- (or false if header has been sent multiple times): + headers_value = setmetatable({}, { + __index = function(self, key) + if headers_value_nil[key] then + return nil + end + local result = nil + local values = request.headers_csv_table[key] + if #values == 0 then + headers_value_nil[key] = true + elseif #values == 1 then + result = values[1] + else + result = false + end + self[key] = result + return result + end + }), + -- table mapping header field names to a flag table, + -- indicating if the comma separated value contains certain entries: + headers_flags = setmetatable({}, { + __index = function(self, key) + local result = setmetatable({}, { + __index = function(self, key) + local lowerkey = string.lower(key) + local result = rawget(self, lowerkey) or false + self[lowerkey] = result + self[key] = result + return result + end + }) + for i, value in ipairs(request.headers_csv_table[key]) do + result[string.lower(value)] = true + end + self[key] = result + return result + end + }) } -- local variables to track the state: local state = "init" -- one of: @@ -434,6 +523,39 @@ send_chunk() end end + -- method to flush output buffer: + function request:flush() + assert_not_faulty() + send_chunk() + send_flush() + end + -- method to finish response: + function request:finish() + assert_not_faulty() + if state == "finished" then + return + elseif state == "info_status_sent" then + error("Informational HTTP response can be finished with :finish_headers() method") + end + finish_headers(false) + if state == "headers_sent" then + if request.method ~= "HEAD" then + state = "faulty" + if content_length then + if bytes_sent ~= content_length then + error("Content length not used") + end + else + send_chunk() + send("0\r\n\r\n") + end + finish() + end + state = "finished" + elseif state ~= "finished" then + 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