Skip to content

Commit

Permalink
lua_modules/http: improve implementation
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nwf committed Feb 10, 2019
1 parent dc1991b commit 5ad612c
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 44 deletions.
3 changes: 2 additions & 1 deletion docs/lua-modules/httpserver.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 1 addition & 3 deletions lua_modules/http/http-example.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,10 +23,8 @@ 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!")
Expand Down
72 changes: 32 additions & 40 deletions lua_modules/http/httpserver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 cs = self.csend
-- 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))
cs("HTTP/1.1 ")
cs(tostring(status or 200))
-- TODO: real HTTP status code/name table
c:send(" OK\r\n")
cs(" OK\r\n")
-- we use chunked transfer encoding, to not deal with Content-Length:
-- response header
self:send_header("Transfer-Encoding", "chunked")
Expand All @@ -43,55 +40,55 @@ do
if self.send_header then
self.send_header = nil
-- end response headers
c:send("\r\n")
cs("\r\n")
end
-- chunked transfer encoding
c:send(("%X\r\n"):format(#data))
c:send(data)
c:send("\r\n")
cs(("%X\r\n"):format(#data))
cs(data)
cs("\r\n")
end
end
local send_header = function(self, name, value)
local c = self.conn
local cs = self.csend
-- NB: quite a naive implementation
c:send(name)
c:send(": ")
c:send(value)
c:send("\r\n")
cs(name)
cs(": ")
cs(value)
cs("\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
-- 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")
self.csend("0\r\n\r\n")
-- close connection
c:close()
self.cfini()
end
--
local make_res = function(conn)
local res = {
conn = conn,
}
-- return setmetatable(res, {
-- send_header = send_header,
-- send = send,
-- finish = finish,
-- })
local make_res = function(csend, cfini)
local res = { }
res.csend = csend
res.cfini = cfini
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
Expand All @@ -108,7 +105,7 @@ do
cnt_len = tonumber(v)
end
if k == "expect" and v == "100-continue" then
conn:send("HTTP/1.1 100 Continue\r\n")
cs("HTTP/1.1 100 Continue\r\n")
end
-- delegate to request object
if req and req.onheader then
Expand All @@ -118,8 +115,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)
Expand Down Expand Up @@ -153,8 +148,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
Expand All @@ -166,11 +163,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
Expand Down

0 comments on commit 5ad612c

Please sign in to comment.