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

Impressum / About Us