moonbridge
changeset 163:80266ee1593f
Work on new HTTP module implementation
author | jbe |
---|---|
date | Fri Jun 12 01:55:50 2015 +0200 (2015-06-12) |
parents | 6c1561547f79 |
children | 27dc025e76cc |
files | moonbridge_http.lua |
line diff
1.1 --- a/moonbridge_http.lua Thu Jun 11 21:17:29 2015 +0200 1.2 +++ b/moonbridge_http.lua Fri Jun 12 01:55:50 2015 +0200 1.3 @@ -163,11 +163,12 @@ 1.4 local consume -- function that reads some input if possible 1.5 -- function that drains some input if possible: 1.6 local function drain() 1.7 - local bytes, status = assert(socket:drain_nb(input_chunk_size)) 1.8 - if status == "eof" then 1.9 + local bytes, status = socket:drain_nb(input_chunk_size) 1.10 + if not bytes or status == "eof" then 1.11 consume = nil 1.12 end 1.13 end 1.14 + -- function trying to unblock socket by reading: 1.15 local function unblock() 1.16 if consume then 1.17 poll(socket_set, socket_set) 1.18 @@ -176,11 +177,14 @@ 1.19 poll(nil, socket_set) 1.20 end 1.21 end 1.22 + -- function that enforces consumption of all input: 1.23 local function consume_all() 1.24 while consume do 1.25 + poll(socket_set, nil) 1.26 consume() 1.27 end 1.28 end 1.29 + -- handle requests in a loop: 1.30 repeat 1.31 -- create a new request object: 1.32 local request = { 1.33 @@ -190,23 +194,56 @@ 1.34 -- local variables to track the state: 1.35 local state = "init" -- one of: 1.36 -- "init" (initial state) 1.37 + -- "prepare" (configureation in preparation) 1.38 -- "no_status_sent" (configuration complete) 1.39 -- "info_status_sent" (1xx status code has been sent) 1.40 -- "bodyless_status_sent" (204/304 status code has been sent) 1.41 -- "status_sent" (regular status code has been sent) 1.42 -- "headers_sent" (headers have been terminated) 1.43 -- "finished" (request has been answered completely) 1.44 + -- "faulty" (I/O or protocaol error) 1.45 local close_requested = false -- "Connection: close" requested 1.46 local close_responded = false -- "Connection: close" sent 1.47 local content_length = nil -- value of Content-Length header sent 1.48 + -- functions to assert proper output/closing: 1.49 + local function assert_output(...) 1.50 + local retval, errmsg = ... 1.51 + if retval then return ... end 1.52 + state = "faulty" 1.53 + socket:reset() 1.54 + error("Could not send data to client: " .. errmsg) 1.55 + end 1.56 + local function assert_close(...) 1.57 + local retval, errmsg = ... 1.58 + if retval then return ... end 1.59 + state = "faulty" 1.60 + error("Could not finish sending data to client: " .. errmsg) 1.61 + end 1.62 -- functions to send data to the browser: 1.63 local function send(...) 1.64 - assert(socket:write_call(unblock, ...)) 1.65 + assert_output(socket:write_call(unblock, ...)) 1.66 end 1.67 local function send_flush(...) 1.68 - assert(socket:flush_call(unblock, ...)) 1.69 + assert_output(socket:flush_call(unblock, ...)) 1.70 end 1.71 - -- TODO: 1.72 + -- function to finish request: 1.73 + local function finish() 1.74 + if close_responded then 1.75 + -- discard any input: 1.76 + consume = drain 1.77 + -- close output stream: 1.78 + send_flush() 1.79 + assert_close(socket:finish()) 1.80 + -- wait for EOF of peer to avoid immediate TCP RST condition: 1.81 + consume_all() 1.82 + -- fully close socket: 1.83 + assert_close(socket:close()) 1.84 + else 1.85 + send_flush() 1.86 + consume_all() 1.87 + end 1.88 + end 1.89 + -- function to prepare body processing: 1.90 local function prepare() 1.91 if state == "prepare" then 1.92 error("Unexpected internal status in HTTP engine") 1.93 @@ -217,24 +254,22 @@ 1.94 -- TODO 1.95 state = "no_status_sent" 1.96 end 1.97 - -- TODO: 1.98 - local function finish_response() 1.99 - if close_responded then 1.100 - -- discard any input: 1.101 - consume = drain 1.102 - -- close output stream: 1.103 - send_flush() 1.104 - assert(socket:finish()) 1.105 - -- wait for EOF of peer to avoid immediate TCP RST condition: 1.106 - consume_all() 1.107 - -- fully close socket: 1.108 - --socket_closed = true -- avoid double close on error -- TODO 1.109 - assert(socket:close()) 1.110 - else 1.111 - assert(socket:flush()) 1.112 - consume_all() 1.113 + -- method to ignore input and close connection after response: 1.114 + function request:monologue() 1.115 + prepare() 1.116 + if 1.117 + state == "headers_sent" or 1.118 + state == "finished" 1.119 + then 1.120 + error("All HTTP headers have already been sent") 1.121 + end 1.122 + consume = drain 1.123 + close_requested = true 1.124 + if state == "init" or state == "prepare" then -- TODO: ok? 1.125 + state = "no_status_sent" 1.126 end 1.127 end 1.128 + -- 1.129 -- method to send a HTTP response status (e.g. "200 OK"): 1.130 function request:send_status(status) 1.131 prepare() 1.132 @@ -297,17 +332,6 @@ 1.133 end 1.134 assert_output(socket:write(key, ": ", value, "\r\n")) 1.135 end 1.136 - -- method to close connection after sending the response: 1.137 - function request:close_after_finish() 1.138 - prepare() 1.139 - if 1.140 - state == "headers_sent" or 1.141 - state == "finished" 1.142 - then 1.143 - error("All HTTP headers have already been sent") 1.144 - end 1.145 - close_requested = true 1.146 - end 1.147 -- function to terminate header section in response, optionally flushing: 1.148 -- (may be called multiple times unless response is finished) 1.149 local function finish_headers(with_flush) 1.150 @@ -321,7 +345,7 @@ 1.151 request:send_header("Connection", "close") 1.152 end 1.153 send("\r\n") 1.154 - --finish_response() -- TODO 1.155 + finish() 1.156 state = "finished" 1.157 elseif state == "status_sent" then 1.158 if not content_length then 1.159 @@ -332,7 +356,7 @@ 1.160 end 1.161 send("\r\n") 1.162 if request.method == "HEAD" then 1.163 - --finish_response() -- TODO 1.164 + finish() 1.165 elseif with_flush then 1.166 send_flush() 1.167 end 1.168 @@ -346,10 +370,43 @@ 1.169 prepare() 1.170 finish_headers(true) 1.171 end 1.172 + -- function to report an error: 1.173 + local function request_error(throw_error, status, text) 1.174 + local errmsg = "Error while reading request from client. Error response: " .. status 1.175 + if text then 1.176 + errmsg = errmsg .. " (" .. text .. ")" 1.177 + end 1.178 + if 1.179 + state == "init" or 1.180 + state == "prepare" or 1.181 + state == "no_status_sent" or 1.182 + state == "info_status_sent" 1.183 + then 1.184 + local error_response_status, errmsg2 = pcall(function() 1.185 + request:monologue() 1.186 + request:send_status(status) 1.187 + request:send_header("Content-Type", "text/plain") 1.188 + request:send_data(status, "\n") 1.189 + if text then 1.190 + request:send_data("\n", text, "\n") 1.191 + end 1.192 + request:finish() 1.193 + end) 1.194 + if not error_response_status then 1.195 + error("Unexpected error while sending error response: " .. errmsg2) 1.196 + end 1.197 + elseif state ~= "faulty" then 1.198 + assert_close(socket:reset()) 1.199 + end 1.200 + if throw_error then 1.201 + error(errmsg) 1.202 + else 1.203 + return survive 1.204 + end 1.205 + end 1.206 -- wait for input: 1.207 if not poll(socket_set, nil, request_idle_timeout) then 1.208 - -- TODO: send error 1.209 - return survive 1.210 + return request_error(false, "408 Request Timeout", "Idle connection timed out") 1.211 end 1.212 until close_responded 1.213 return survive