diff --git a/Makefile b/Makefile index 348bfcc..10c6c27 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ SPEED=9600 # End of user config ###################################################################### 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 usage: diff --git a/httpserver-error.lua b/httpserver-error.lua index 3bc5af9..a8dfe84 100644 --- a/httpserver-error.lua +++ b/httpserver-error.lua @@ -2,20 +2,13 @@ -- Part of nodemcu-httpserver, handles sending error pages to client. -- 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) - local errorString = getHTTPStatusString(args.code) - print("Error " .. args.code .. ": " .. errorString) - sendHeader(connection, args.code, errorString, "text/html") - connection:send("" .. args.code .. " - " .. errorString .. "

" .. args.code .. " - " .. errorString .. "

\r\n") + + local function sendHeader(connection, code, errorString, mimeType) + connection:send("HTTP/1.0 " .. code .. " " .. errorString .. "\r\nServer: nodemcu-httpserver\r\nContent-Type: " .. mimeType .. "\r\nConnection: close\r\n\r\n") + end + + print("Error " .. args.code .. ": " .. args.errorString) + sendHeader(connection, args.code, args.errorString, "text/html") + connection:send("" .. args.code .. " - " .. args.errorString .. "

" .. args.code .. " - " .. args.errorString .. "

\r\n") end diff --git a/httpserver-static.lua b/httpserver-static.lua index e7ca343..d3ac1bd 100644 --- a/httpserver-static.lua +++ b/httpserver-static.lua @@ -2,41 +2,20 @@ -- Part of nodemcu-httpserver, handles sending static files to client. -- 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) - sendHeader(connection, 200, "OK", getMimeType(args.ext)) + dofile("httpserver-header.lc")(connection, 200, args.ext) --print("Begin sending:", args.file) -- Send file in little chunks local continue = true local bytesSent = 0 while continue do + collectgarbage() -- NodeMCU file API lets you open 1 file at a time. -- So we need to open, seek, close each time in order -- to support multiple simultaneous clients. file.open(args.file) file.seek("set", bytesSent) - local chunk = file.read(512) + local chunk = file.read(256) file.close() if chunk == nil then continue = false @@ -44,6 +23,7 @@ return function (connection, args) coroutine.yield() connection:send(chunk) bytesSent = bytesSent + #chunk + chunk = nil --print("Sent" .. args.file, bytesSent) end end diff --git a/httpserver.lua b/httpserver.lua index 23b7ac6..1a2a0b7 100644 --- a/httpserver.lua +++ b/httpserver.lua @@ -15,32 +15,31 @@ return function (port) local connectionThread local function onGet(connection, uri) + collectgarbage() local fileServeFunction = nil if #(uri.file) > 32 then -- nodemcu-firmware cannot handle long filenames. - uri.args['code'] = 400 + uri.args = {code = 400, errorString = "Bad Request"} fileServeFunction = dofile("httpserver-error.lc") else local fileExists = file.open(uri.file, "r") file.close() - collectgarbage() if not fileExists then - uri.args['code'] = 404 + uri.args = {code = 404, errorString = "Not Found"} fileServeFunction = dofile("httpserver-error.lc") elseif uri.isScript then fileServeFunction = dofile(uri.file) else - uri.args['file'] = uri.file - uri.args['ext'] = uri.ext + uri.args = {file = uri.file, ext = uri.ext} fileServeFunction = dofile("httpserver-static.lc") end end connectionThread = coroutine.create(fileServeFunction) - --print("Thread created", connectionThread) coroutine.resume(connectionThread, connection, uri.args) end local function onReceive(connection, payload) + collectgarbage() -- print(payload) -- for debugging -- parse payload and decide what to serve. local req = dofile("httpserver-request.lc")(payload) @@ -49,23 +48,29 @@ return function (port) onGet(connection, req.uri) else local args = {} - if req.methodIsValid then args['code'] = 501 else args['code'] = 400 end - dofile("httpserver-error.lc")(connection, args) + local fileServeFunction = dofile("httpserver-error.lc") + 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 local function onSent(connection, payload) - local connectionThreadStatus = coroutine.status(connectionThread) - -- print (connectionThread, "status is", connectionThreadStatus) - if connectionThreadStatus == "suspended" then - -- Not finished sending file, resume. - -- print("Resume thread", connectionThread) - coroutine.resume(connectionThread) - elseif connectionThreadStatus == "dead" then - -- We're done sending file. - -- print("Done thread", connectionThread) - connection:close() - connectionThread = nil + collectgarbage() + if connectionThread then + local connectionThreadStatus = coroutine.status(connectionThread) + if connectionThreadStatus == "suspended" then + -- Not finished sending file, resume. + coroutine.resume(connectionThread) + elseif connectionThreadStatus == "dead" then + -- We're done sending file. + connection:close() + connectionThread = nil + end end end diff --git a/init.lua b/init.lua index 07df51d..99a33df 100644 --- a/init.lua +++ b/init.lua @@ -43,7 +43,7 @@ local compileAndRemoveIfNeeded = function(f) 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 compileAndRemoveIfNeeded = nil