|
@@ -13,10 +13,58 @@ return function (port)
|
|
-- We do it in a separate thread because we need to yield when sending lots
|
|
-- We do it in a separate thread because we need to yield when sending lots
|
|
-- of data in order to avoid overflowing the mcu's buffer.
|
|
-- of data in order to avoid overflowing the mcu's buffer.
|
|
local connectionThread
|
|
local connectionThread
|
|
|
|
+
|
|
|
|
+ local allowStatic = {GET=true, HEAD=true, POST=false, PUT=false, DELETE=false, TRACE=false, OPTIONS=false, CONNECT=false, PATCH=false}
|
|
|
|
|
|
- local function onGet(connection, uri)
|
|
|
|
|
|
+ local function startServing(fileServeFunction, connection, req, args)
|
|
|
|
+ local bufferedConnection = {}
|
|
|
|
+ connectionThread = coroutine.create(function(fileServeFunction, bconnection, req, args)
|
|
|
|
+ fileServeFunction(bconnection, req, args)
|
|
|
|
+ if not bconnection:flush() then
|
|
|
|
+ connection:close()
|
|
|
|
+ connectionThread = nil
|
|
|
|
+ end
|
|
|
|
+ end)
|
|
|
|
+ function bufferedConnection:flush()
|
|
|
|
+ if self.size > 0 then
|
|
|
|
+ connection:send(table.concat(self.data, ""))
|
|
|
|
+ self.data = {}
|
|
|
|
+ self.size = 0
|
|
|
|
+ return true
|
|
|
|
+ end
|
|
|
|
+ return false
|
|
|
|
+ end
|
|
|
|
+ function bufferedConnection:send(payload)
|
|
|
|
+ local l = payload:len()
|
|
|
|
+ if l + self.size > 1000 then
|
|
|
|
+ if self:flush() then
|
|
|
|
+ coroutine.yield()
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+ if l > 800 then
|
|
|
|
+ connection:send(payload)
|
|
|
|
+ coroutine.yield()
|
|
|
|
+ else
|
|
|
|
+ table.insert(self.data, payload)
|
|
|
|
+ self.size = self.size + l
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+ bufferedConnection.size = 0
|
|
|
|
+ bufferedConnection.data = {}
|
|
|
|
+ local status, err = coroutine.resume(connectionThread, fileServeFunction, bufferedConnection, req, args)
|
|
|
|
+ if not status then
|
|
|
|
+ print(err)
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+
|
|
|
|
+ local function onRequest(connection, req)
|
|
collectgarbage()
|
|
collectgarbage()
|
|
|
|
+ local method = req.method
|
|
|
|
+ local uri = req.uri
|
|
local fileServeFunction = nil
|
|
local fileServeFunction = nil
|
|
|
|
+
|
|
|
|
+ print("Method: " .. method);
|
|
|
|
+
|
|
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, errorString = "Bad Request"}
|
|
uri.args = {code = 400, errorString = "Bad Request"}
|
|
@@ -24,18 +72,35 @@ return function (port)
|
|
else
|
|
else
|
|
local fileExists = file.open(uri.file, "r")
|
|
local fileExists = file.open(uri.file, "r")
|
|
file.close()
|
|
file.close()
|
|
|
|
+
|
|
|
|
+ if not fileExists then
|
|
|
|
+ -- gzip check
|
|
|
|
+ fileExists = file.open(uri.file .. ".gz", "r")
|
|
|
|
+ file.close()
|
|
|
|
+
|
|
|
|
+ if fileExists then
|
|
|
|
+ print("gzip variant exists, serving that one")
|
|
|
|
+ uri.file = uri.file .. ".gz"
|
|
|
|
+ uri.isGzipped = true
|
|
|
|
+ end
|
|
|
|
+ end
|
|
|
|
+
|
|
if not fileExists then
|
|
if not fileExists then
|
|
uri.args = {code = 404, errorString = "Not Found"}
|
|
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, ext = uri.ext}
|
|
|
|
- fileServeFunction = dofile("httpserver-static.lc")
|
|
|
|
|
|
+ if allowStatic[method] then
|
|
|
|
+ uri.args = {file = uri.file, ext = uri.ext, gzipped = uri.isGzipped}
|
|
|
|
+ fileServeFunction = dofile("httpserver-static.lc")
|
|
|
|
+ else
|
|
|
|
+ uri.args = {code = 405, errorString = "Method not supported"}
|
|
|
|
+ fileServeFunction = dofile("httpserver-error.lc")
|
|
|
|
+ end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
- connectionThread = coroutine.create(fileServeFunction)
|
|
|
|
- coroutine.resume(connectionThread, connection, uri.args)
|
|
|
|
|
|
+ startServing(fileServeFunction, connection, req, uri.args)
|
|
end
|
|
end
|
|
|
|
|
|
local function onReceive(connection, payload)
|
|
local function onReceive(connection, payload)
|
|
@@ -51,8 +116,9 @@ return function (port)
|
|
auth = dofile("httpserver-basicauth.lc")
|
|
auth = dofile("httpserver-basicauth.lc")
|
|
user = auth.authenticate(payload) -- authenticate returns nil on failed auth
|
|
user = auth.authenticate(payload) -- authenticate returns nil on failed auth
|
|
end
|
|
end
|
|
- if user and req.methodIsValid and req.method == "GET" then
|
|
|
|
- onGet(connection, req.uri)
|
|
|
|
|
|
+
|
|
|
|
+ if user and req.methodIsValid and (req.method == "GET" or req.method == "POST" or req.method == "PUT") then
|
|
|
|
+ onRequest(connection, req)
|
|
else
|
|
else
|
|
local args = {}
|
|
local args = {}
|
|
local fileServeFunction = dofile("httpserver-error.lc")
|
|
local fileServeFunction = dofile("httpserver-error.lc")
|
|
@@ -63,18 +129,20 @@ return function (port)
|
|
else
|
|
else
|
|
args = {code = 400, errorString = "Bad Request"}
|
|
args = {code = 400, errorString = "Bad Request"}
|
|
end
|
|
end
|
|
- connectionThread = coroutine.create(fileServeFunction)
|
|
|
|
- coroutine.resume(connectionThread, connection, args)
|
|
|
|
|
|
+ startServing(fileServeFunction, connection, req, args)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
local function onSent(connection, payload)
|
|
local function onSent(connection, payload)
|
|
collectgarbage()
|
|
collectgarbage()
|
|
if connectionThread then
|
|
if connectionThread then
|
|
- local connectionThreadStatus = coroutine.status(connectionThread)
|
|
|
|
|
|
+ local connectionThreadStatus = coroutine.status(connectionThread)
|
|
if connectionThreadStatus == "suspended" then
|
|
if connectionThreadStatus == "suspended" then
|
|
-- Not finished sending file, resume.
|
|
-- Not finished sending file, resume.
|
|
- coroutine.resume(connectionThread)
|
|
|
|
|
|
+ local status, err = coroutine.resume(connectionThread)
|
|
|
|
+ if not status then
|
|
|
|
+ print(err)
|
|
|
|
+ end
|
|
elseif connectionThreadStatus == "dead" then
|
|
elseif connectionThreadStatus == "dead" then
|
|
-- We're done sending file.
|
|
-- We're done sending file.
|
|
connection:close()
|
|
connection:close()
|
|
@@ -85,6 +153,12 @@ return function (port)
|
|
|
|
|
|
connection:on("receive", onReceive)
|
|
connection:on("receive", onReceive)
|
|
connection:on("sent", onSent)
|
|
connection:on("sent", onSent)
|
|
|
|
+ connection:on("disconnection",function(c)
|
|
|
|
+ if connectionThread then
|
|
|
|
+ connectionThread = nil
|
|
|
|
+ collectgarbage()
|
|
|
|
+ end
|
|
|
|
+ end)
|
|
|
|
|
|
end
|
|
end
|
|
)
|
|
)
|