moonbridge
changeset 165:00f01d945e13
Work on new HTTP module implementation
author | jbe |
---|---|
date | Sat Jun 13 18:02:57 2015 +0200 (2015-06-13) |
parents | 27dc025e76cc |
children | 41a44ae5c293 |
files | moonbridge_http.lua |
line diff
1.1 --- a/moonbridge_http.lua Sat Jun 13 00:55:01 2015 +0200 1.2 +++ b/moonbridge_http.lua Sat Jun 13 18:02:57 2015 +0200 1.3 @@ -188,8 +188,97 @@ 1.4 repeat 1.5 -- create a new request object: 1.6 local request = { 1.7 + -- allow access to underlying socket: 1.8 socket = socket, 1.9 - cookies = {} 1.10 + -- cookies are simply stored in a table: 1.11 + cookies = {}, 1.12 + -- table mapping header field names to value-lists 1.13 + -- (raw access, but case-insensitive): 1.14 + headers = setmetatable({}, { 1.15 + __index = function(self, key) 1.16 + local lowerkey = string.lower(key) 1.17 + if lowerkey == key then 1.18 + return 1.19 + end 1.20 + local result = rawget(self, lowerkey) 1.21 + if result == nil then 1.22 + result = {} 1.23 + end 1.24 + self[lowerkey] = result 1.25 + self[key] = result 1.26 + return result 1.27 + end 1.28 + }), 1.29 + -- table mapping header field names to value-lists 1.30 + -- (for headers with comma separated values): 1.31 + headers_csv_table = setmetatable({}, { 1.32 + __index = function(self, key) 1.33 + local result = {} 1.34 + for i, line in ipairs(request.headers[key]) do 1.35 + for entry in string.gmatch(line, "[^,]+") do 1.36 + local value = string.match(entry, "^[ \t]*(..-)[ \t]*$") 1.37 + if value then 1.38 + result[#result+1] = value 1.39 + end 1.40 + end 1.41 + end 1.42 + self[key] = result 1.43 + return result 1.44 + end 1.45 + }), 1.46 + -- table mapping header field names to a comma separated string 1.47 + -- (for headers with comma separated values): 1.48 + headers_csv_string = setmetatable({}, { 1.49 + __index = function(self, key) 1.50 + local result = {} 1.51 + for i, line in ipairs(request.headers[key]) do 1.52 + result[#result+1] = line 1.53 + end 1.54 + result = string.concat(result, ", ") 1.55 + self[key] = result 1.56 + return result 1.57 + end 1.58 + }), 1.59 + -- table mapping header field names to a single string value 1.60 + -- (or false if header has been sent multiple times): 1.61 + headers_value = setmetatable({}, { 1.62 + __index = function(self, key) 1.63 + if headers_value_nil[key] then 1.64 + return nil 1.65 + end 1.66 + local result = nil 1.67 + local values = request.headers_csv_table[key] 1.68 + if #values == 0 then 1.69 + headers_value_nil[key] = true 1.70 + elseif #values == 1 then 1.71 + result = values[1] 1.72 + else 1.73 + result = false 1.74 + end 1.75 + self[key] = result 1.76 + return result 1.77 + end 1.78 + }), 1.79 + -- table mapping header field names to a flag table, 1.80 + -- indicating if the comma separated value contains certain entries: 1.81 + headers_flags = setmetatable({}, { 1.82 + __index = function(self, key) 1.83 + local result = setmetatable({}, { 1.84 + __index = function(self, key) 1.85 + local lowerkey = string.lower(key) 1.86 + local result = rawget(self, lowerkey) or false 1.87 + self[lowerkey] = result 1.88 + self[key] = result 1.89 + return result 1.90 + end 1.91 + }) 1.92 + for i, value in ipairs(request.headers_csv_table[key]) do 1.93 + result[string.lower(value)] = true 1.94 + end 1.95 + self[key] = result 1.96 + return result 1.97 + end 1.98 + }) 1.99 } 1.100 -- local variables to track the state: 1.101 local state = "init" -- one of: 1.102 @@ -434,6 +523,39 @@ 1.103 send_chunk() 1.104 end 1.105 end 1.106 + -- method to flush output buffer: 1.107 + function request:flush() 1.108 + assert_not_faulty() 1.109 + send_chunk() 1.110 + send_flush() 1.111 + end 1.112 + -- method to finish response: 1.113 + function request:finish() 1.114 + assert_not_faulty() 1.115 + if state == "finished" then 1.116 + return 1.117 + elseif state == "info_status_sent" then 1.118 + error("Informational HTTP response can be finished with :finish_headers() method") 1.119 + end 1.120 + finish_headers(false) 1.121 + if state == "headers_sent" then 1.122 + if request.method ~= "HEAD" then 1.123 + state = "faulty" 1.124 + if content_length then 1.125 + if bytes_sent ~= content_length then 1.126 + error("Content length not used") 1.127 + end 1.128 + else 1.129 + send_chunk() 1.130 + send("0\r\n\r\n") 1.131 + end 1.132 + finish() 1.133 + end 1.134 + state = "finished" 1.135 + elseif state ~= "finished" then 1.136 + error("Unexpected internal status in HTTP engine") 1.137 + end 1.138 + end 1.139 -- function to report an error: 1.140 local function request_error(throw_error, status, text) 1.141 local errmsg = "Error while reading request from client. Error response: " .. status