Refactoring - function for sending headers is now in its own file. All errors are sent using coroutine. Some other memory usage improvements

This commit is contained in:
Marcos Kirsch 2015-04-19 23:42:58 -05:00
parent 453f38b52f
commit ead632d131
5 changed files with 38 additions and 60 deletions

View File

@ -11,7 +11,7 @@ SPEED=9600
# End of user config # End of user config
###################################################################### ######################################################################
HTTP_FILES := $(wildcard http/*) HTTP_FILES := $(wildcard http/*)
LUA_FILES := init.lua httpserver.lua httpserver-request.lua httpserver-static.lua httpserver-error.lua LUA_FILES := init.lua httpserver.lua httpserver-request.lua httpserver-static.lua httpserver-header.lua httpserver-error.lua
# Print usage # Print usage
usage: usage:

View File

@ -2,20 +2,13 @@
-- Part of nodemcu-httpserver, handles sending error pages to client. -- Part of nodemcu-httpserver, handles sending error pages to client.
-- Author: Marcos Kirsch -- Author: Marcos Kirsch
local function getHTTPStatusString(code)
if code == 404 then return "Not Found" end
if code == 400 then return "Bad Request" end
if code == 501 then return "Not Implemented" end
return "Unknown HTTP status"
end
local function sendHeader(connection, code, codeString, mimeType)
connection:send("HTTP/1.0 " .. code .. " " .. codeString .. "\r\nServer: nodemcu-httpserver\r\nContent-Type: " .. mimeType .. "\r\nConnection: close\r\n\r\n")
end
return function (connection, args) return function (connection, args)
local errorString = getHTTPStatusString(args.code)
print("Error " .. args.code .. ": " .. errorString) local function sendHeader(connection, code, errorString, mimeType)
sendHeader(connection, args.code, errorString, "text/html") connection:send("HTTP/1.0 " .. code .. " " .. errorString .. "\r\nServer: nodemcu-httpserver\r\nContent-Type: " .. mimeType .. "\r\nConnection: close\r\n\r\n")
connection:send("<html><head><title>" .. args.code .. " - " .. errorString .. "</title></head><body><h1>" .. args.code .. " - " .. errorString .. "</h1></body></html>\r\n") end
print("Error " .. args.code .. ": " .. args.errorString)
sendHeader(connection, args.code, args.errorString, "text/html")
connection:send("<html><head><title>" .. args.code .. " - " .. args.errorString .. "</title></head><body><h1>" .. args.code .. " - " .. args.errorString .. "</h1></body></html>\r\n")
end end

View File

@ -2,41 +2,20 @@
-- Part of nodemcu-httpserver, handles sending static files to client. -- Part of nodemcu-httpserver, handles sending static files to client.
-- Author: Marcos Kirsch -- Author: Marcos Kirsch
local function getMimeType(ext)
local gzip = false
-- A few MIME types. Keep list short. If you need something that is missing, let's add it.
local mt = {css = "text/css", gif = "image/gif", html = "text/html", ico = "image/x-icon", jpeg = "image/jpeg", jpg = "image/jpeg", js = "application/javascript", json = "application/json", png = "image/png"}
-- add comressed flag if file ends with gz
if ext:find("%.gz$") then
ext = ext:sub(1, -4)
gzip = true
end
if mt[ext] then contentType = mt[ext] else contentType = "text/plain" end
return {contentType = contentType, gzip = gzip }
end
local function sendHeader(connection, code, codeString, mimeType)
connection:send("HTTP/1.0 " .. code .. " " .. codeString .. "\r\nServer: nodemcu-httpserver\r\nContent-Type: " .. mimeType["contentType"] .. "\r\n")
if mimeType["gzip"] then
connection:send("Content-Encoding: gzip\r\n")
end
connection:send("Connection: close\r\n\r\n")
end
return function (connection, args) return function (connection, args)
sendHeader(connection, 200, "OK", getMimeType(args.ext)) dofile("httpserver-header.lc")(connection, 200, args.ext)
--print("Begin sending:", args.file) --print("Begin sending:", args.file)
-- Send file in little chunks -- Send file in little chunks
local continue = true local continue = true
local bytesSent = 0 local bytesSent = 0
while continue do while continue do
collectgarbage()
-- NodeMCU file API lets you open 1 file at a time. -- NodeMCU file API lets you open 1 file at a time.
-- So we need to open, seek, close each time in order -- So we need to open, seek, close each time in order
-- to support multiple simultaneous clients. -- to support multiple simultaneous clients.
file.open(args.file) file.open(args.file)
file.seek("set", bytesSent) file.seek("set", bytesSent)
local chunk = file.read(512) local chunk = file.read(256)
file.close() file.close()
if chunk == nil then if chunk == nil then
continue = false continue = false
@ -44,6 +23,7 @@ return function (connection, args)
coroutine.yield() coroutine.yield()
connection:send(chunk) connection:send(chunk)
bytesSent = bytesSent + #chunk bytesSent = bytesSent + #chunk
chunk = nil
--print("Sent" .. args.file, bytesSent) --print("Sent" .. args.file, bytesSent)
end end
end end

View File

@ -15,32 +15,31 @@ return function (port)
local connectionThread local connectionThread
local function onGet(connection, uri) local function onGet(connection, uri)
collectgarbage()
local fileServeFunction = nil local fileServeFunction = nil
if #(uri.file) > 32 then if #(uri.file) > 32 then
-- nodemcu-firmware cannot handle long filenames. -- nodemcu-firmware cannot handle long filenames.
uri.args['code'] = 400 uri.args = {code = 400, errorString = "Bad Request"}
fileServeFunction = dofile("httpserver-error.lc") fileServeFunction = dofile("httpserver-error.lc")
else else
local fileExists = file.open(uri.file, "r") local fileExists = file.open(uri.file, "r")
file.close() file.close()
collectgarbage()
if not fileExists then if not fileExists then
uri.args['code'] = 404 uri.args = {code = 404, errorString = "Not Found"}
fileServeFunction = dofile("httpserver-error.lc") fileServeFunction = dofile("httpserver-error.lc")
elseif uri.isScript then elseif uri.isScript then
fileServeFunction = dofile(uri.file) fileServeFunction = dofile(uri.file)
else else
uri.args['file'] = uri.file uri.args = {file = uri.file, ext = uri.ext}
uri.args['ext'] = uri.ext
fileServeFunction = dofile("httpserver-static.lc") fileServeFunction = dofile("httpserver-static.lc")
end end
end end
connectionThread = coroutine.create(fileServeFunction) connectionThread = coroutine.create(fileServeFunction)
--print("Thread created", connectionThread)
coroutine.resume(connectionThread, connection, uri.args) coroutine.resume(connectionThread, connection, uri.args)
end end
local function onReceive(connection, payload) local function onReceive(connection, payload)
collectgarbage()
-- print(payload) -- for debugging -- print(payload) -- for debugging
-- parse payload and decide what to serve. -- parse payload and decide what to serve.
local req = dofile("httpserver-request.lc")(payload) local req = dofile("httpserver-request.lc")(payload)
@ -49,25 +48,31 @@ return function (port)
onGet(connection, req.uri) onGet(connection, req.uri)
else else
local args = {} local args = {}
if req.methodIsValid then args['code'] = 501 else args['code'] = 400 end local fileServeFunction = dofile("httpserver-error.lc")
dofile("httpserver-error.lc")(connection, args) if req.methodIsValid then
args = {code = 501, errorString = "Not Implemented"}
else
args = {code = 400, errorString = "Bad Request"}
end
connectionThread = coroutine.create(fileServeFunction)
coroutine.resume(connectionThread, connection, args)
end end
end end
local function onSent(connection, payload) local function onSent(connection, payload)
collectgarbage()
if connectionThread then
local connectionThreadStatus = coroutine.status(connectionThread) local connectionThreadStatus = coroutine.status(connectionThread)
-- print (connectionThread, "status is", connectionThreadStatus)
if connectionThreadStatus == "suspended" then if connectionThreadStatus == "suspended" then
-- Not finished sending file, resume. -- Not finished sending file, resume.
-- print("Resume thread", connectionThread)
coroutine.resume(connectionThread) coroutine.resume(connectionThread)
elseif connectionThreadStatus == "dead" then elseif connectionThreadStatus == "dead" then
-- We're done sending file. -- We're done sending file.
-- print("Done thread", connectionThread)
connection:close() connection:close()
connectionThread = nil connectionThread = nil
end end
end end
end
connection:on("receive", onReceive) connection:on("receive", onReceive)
connection:on("sent", onSent) connection:on("sent", onSent)

View File

@ -43,7 +43,7 @@ local compileAndRemoveIfNeeded = function(f)
end end
end end
local serverFiles = {'httpserver.lua', 'httpserver-request.lua', 'httpserver-static.lua', 'httpserver-error.lua'} local serverFiles = {'httpserver.lua', 'httpserver-request.lua', 'httpserver-static.lua', 'httpserver-header.lua', 'httpserver-error.lua'}
for i, f in ipairs(serverFiles) do compileAndRemoveIfNeeded(f) end for i, f in ipairs(serverFiles) do compileAndRemoveIfNeeded(f) end
compileAndRemoveIfNeeded = nil compileAndRemoveIfNeeded = nil