From 7b803adcfb39f65a521c4768fb54ef560cf23b55 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Wed, 6 Feb 2019 17:27:11 +0000 Subject: [PATCH] lua_modules/http: improve implementation Switch to fifosock for in-order sending and waiting for everything to be sent before closing. Fix header callback by moving the invocation of the handler higher --- docs/lua-modules/httpserver.md | 3 +- lua_modules/http/http-example.lua | 6 +-- lua_modules/http/httpserver.lua | 84 +++++++++++++------------------ 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/docs/lua-modules/httpserver.md b/docs/lua-modules/httpserver.md index fccf7b460c..957bac041f 100644 --- a/docs/lua-modules/httpserver.md +++ b/docs/lua-modules/httpserver.md @@ -32,7 +32,8 @@ Function to start HTTP server. #### Notes Callback function has 2 arguments: `req` (request) and `res` (response). The first object holds values: -- `conn`: `net.socket` sub module +- `conn`: `net.socket` sub module. **DO NOT** call `:on` or `:send` on this + object. - `method`: Request method that was used (e.g.`POST` or `GET`) - `url`: Requested URL - `onheader`: value to setup handler function for HTTP headers like `content-type`. Handler function has 3 parameters: diff --git a/lua_modules/http/http-example.lua b/lua_modules/http/http-example.lua index 3080dcb60f..ea03a46e5d 100644 --- a/lua_modules/http/http-example.lua +++ b/lua_modules/http/http-example.lua @@ -9,7 +9,7 @@ require("httpserver").createServer(80, function(req, res) print("+R", req.method, req.url, node.heap()) -- setup handler of headers, if any req.onheader = function(self, name, value) - -- print("+H", name, value) + print("+H", name, value) -- E.g. look for "content-type" header, -- setup body parser to particular format -- if name == "content-type" then @@ -23,13 +23,11 @@ require("httpserver").createServer(80, function(req, res) -- setup handler of body, if any req.ondata = function(self, chunk) print("+B", chunk and #chunk, node.heap()) - -- request ended? if not chunk then -- reply - --res:finish("") res:send(nil, 200) res:send_header("Connection", "close") - res:send("Hello, world!") + res:send("Hello, world!\n") res:finish() end end diff --git a/lua_modules/http/httpserver.lua b/lua_modules/http/httpserver.lua index 9f1e3f25e2..8455c6885f 100644 --- a/lua_modules/http/httpserver.lua +++ b/lua_modules/http/httpserver.lua @@ -12,27 +12,24 @@ do -- request methods ------------------------------------------------------------------------------ local make_req = function(conn, method, url) - local req = { + return { conn = conn, method = method, url = url, } - -- return setmetatable(req, { - -- }) - return req end ------------------------------------------------------------------------------ -- response methods ------------------------------------------------------------------------------ - local send = function(self, data, status) - local c = self.conn + local make_res = function(csend, cfini) + local send = function(self, data, status) -- TODO: req.send should take care of response headers! if self.send_header then - c:send("HTTP/1.1 ") - c:send(tostring(status or 200)) + csend("HTTP/1.1 ") + csend(tostring(status or 200)) -- TODO: real HTTP status code/name table - c:send(" OK\r\n") + csend(" OK\r\n") -- we use chunked transfer encoding, to not deal with Content-Length: -- response header self:send_header("Transfer-Encoding", "chunked") @@ -43,55 +40,51 @@ do if self.send_header then self.send_header = nil -- end response headers - c:send("\r\n") + csend("\r\n") end -- chunked transfer encoding - c:send(("%X\r\n"):format(#data)) - c:send(data) - c:send("\r\n") + csend(("%X\r\n"):format(#data)) + csend(data) + csend("\r\n") end - end - local send_header = function(self, name, value) - local c = self.conn + end + local send_header = function(self, name, value) -- NB: quite a naive implementation - c:send(name) - c:send(": ") - c:send(value) - c:send("\r\n") - end - -- finalize request, optionally sending data - local finish = function(self, data, status) - local c = self.conn - -- NB: req.send takes care of response headers + csend(name) + csend(": ") + csend(value) + csend("\r\n") + end + -- finalize request, optionally sending data + local finish = function(self, data, status) + -- NB: res.send takes care of response headers if data then self:send(data, status) end -- finalize chunked transfer encoding - c:send("0\r\n\r\n") + csend("0\r\n\r\n") -- close connection - c:close() - end - -- - local make_res = function(conn) - local res = { - conn = conn, - } - -- return setmetatable(res, { - -- send_header = send_header, - -- send = send, - -- finish = finish, - -- }) + cfini() + end + -- + local res = { } res.send_header = send_header res.send = send res.finish = finish return res end - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ -- HTTP parser ------------------------------------------------------------------------------ local http_handler = function(handler) return function(conn) + local csend = (require "fifosock")(conn) + local cfini = function() + conn:on("receive", nil) + conn:on("disconnection", nil) + csend(function() conn:on("sent", nil) conn:close() end) + end local req, res local buf = "" local method, url @@ -108,7 +101,7 @@ do cnt_len = tonumber(v) end if k == "expect" and v == "100-continue" then - conn:send("HTTP/1.1 100 Continue\r\n") + csend("HTTP/1.1 100 Continue\r\n") end -- delegate to request object if req and req.onheader then @@ -118,8 +111,6 @@ do -- body data handler local body_len = 0 local ondata = function(conn, chunk) - -- NB: do not reset node in case of lengthy requests - tmr.wdclr() -- feed request data to request handler if not req or not req.ondata then return end req:ondata(chunk) @@ -153,8 +144,10 @@ do if method then -- make request and response objects req = make_req(conn, method, url) - res = make_res(conn) + res = make_res(csend, cfini) end + -- spawn request handler + handler(req, res) -- header line? elseif #line > 0 then -- parse header @@ -166,11 +159,6 @@ do end -- headers end else - -- spawn request handler - -- NB: do not reset in case of lengthy requests - tmr.wdclr() - handler(req, res) - tmr.wdclr() -- NB: we feed the rest of the buffer as starting chunk of body ondata(conn, buf) -- buffer no longer needed