allow running server from LFS (#125)

This commit is contained in:
seregaxvm 2019-11-18 22:32:47 +03:00 committed by Marcos
parent 6c7e451663
commit 6511dad8df
16 changed files with 199 additions and 31 deletions

View File

@ -56,7 +56,7 @@ Let the abuse begin.
3. Visit your server from a web browser.
__Example:__ Say the IP for your ESP8266 is 2.2.2.2 and the server is
running in the default port 80. Go to (http://2.2.2.2/index.html)[http://2.2.2.2/index.html] using your web browser.
running in the default port 80. Go to <http://2.2.2.2/index.html> using your web browser.
The ESP8266 will serve you with the contents of the file "http/index.html" (if it exists). If you visit the root (/)
then index.html is served. By the way, unlike most HTTP servers, nodemcu_httpserver treats the URLs in a
case-sensitive manner.
@ -88,6 +88,20 @@ Let the abuse begin.
For example, if the client requests _http://2.2.2.2/foo.lua?color=red_ then the server will execute the function
in your Lua script _foo.lua_ and pass in _connection_ and _args_, where _args.color = "red"_.
## [LFS](https://nodemcu.readthedocs.io/en/master/lfs/) support.
NodeMCU allows to run Lua scripts from LFS in order to save RAM resources.
*nodemcu-httpserver* makes it easy to move your code to LFS.
In order to run *nodemcu-httpserver* from LFS:
1. move your code to `srv` folder (if you want it to be included in LFS image)
1. Compile contents of `srv` into LFS image. There's a [cloud service](https://blog.ellisons.org.uk/article/nodemcu/a-lua-cross-compile-web-service/) and [docker image](https://github.com/marcelstoer/docker-nodemcu-build) that will help you with that.
1. Upload image file under `lfs.img` name.
1. Reboot you NodeMCU. Init script will pick up image and apply it for you.
### Example: Garage door opener
#### Purpose

View File

@ -4,30 +4,37 @@
local compileAndRemoveIfNeeded = function(f)
if file.exists(f) then
print('Compiling:', f)
node.compile(f)
file.remove(f)
local newf = f:gsub("%w+/", "")
file.rename(f, newf)
print('Compiling:', newf)
node.compile(newf)
file.remove(newf)
collectgarbage()
end
end
local serverFiles = {
'httpserver.lua',
'httpserver-b64decode.lua',
'httpserver-basicauth.lua',
'httpserver-compile.lua',
'httpserver-conf.lua',
'httpserver-connection.lua',
'httpserver-error.lua',
'httpserver-header.lua',
'httpserver-init.lua',
'httpserver-request.lua',
'httpserver-static.lua',
'httpserver-wifi.lua',
'srv/httpserver.lua',
'srv/httpserver-b64decode.lua',
'srv/httpserver-basicauth.lua',
'srv/httpserver-buffer.lua',
'srv/httpserver-connection.lua',
'srv/httpserver-error.lua',
'srv/httpserver-header.lua',
'srv/httpserver-init.lua',
'srv/httpserver-request.lua',
'srv/httpserver-static.lua',
'srv/httpserver-wifi.lua',
}
local lfsFiles = {
'srv/_init.lua',
'srv/dummy_strings.lua',
}
for i, f in ipairs(serverFiles) do compileAndRemoveIfNeeded(f) end
for i, f in ipairs(lfsFiles) do file.remove(f) end
compileAndRemoveIfNeeded = nil
serverFiles = nil
lfsFiles = nil
collectgarbage()

View File

@ -1,10 +1,26 @@
-- Compile freshly uploaded nodemcu-httpserver lua files.
if file.exists("httpserver-compile.lc") then
dofile("httpserver-compile.lc")
else
dofile("httpserver-compile.lua")
if node.getpartitiontable().lfs_size > 0 then
if file.exists("lfs.img") then
if file.exists("lfs_lock") then
file.remove("lfs_lock")
file.remove("lfs.img")
else
local f = file.open("lfs_lock", "w")
f:flush()
f:close()
file.remove("httpserver-compile.lua")
node.flashreload("lfs.img")
end
end
pcall(node.flashindex("_init"))
end
if file.exists("httpserver-compile.lua") then
dofile("httpserver-compile.lua")
file.remove("httpserver-compile.lua")
end
-- Set up NodeMCU's WiFi
dofile("httpserver-wifi.lc")

99
srv/_init.lua Normal file
View File

@ -0,0 +1,99 @@
--
-- File: _init.lua
--[[
This is a template for the LFS equivalent of the SPIFFS init.lua.
It is a good idea to such an _init.lua module to your LFS and do most of the LFS
module related initialisaion in this. This example uses standard Lua features to
simplify the LFS API.
The first section adds a 'LFS' table to _G and uses the __index metamethod to
resolve functions in the LFS, so you can execute the main function of module
'fred' by executing LFS.fred(params), etc. It also implements some standard
readonly properties:
LFS._time The Unix Timestamp when the luac.cross was executed. This can be
used as a version identifier.
LFS._config This returns a table of useful configuration parameters, hence
print (("0x%6x"):format(LFS._config.lfs_base))
gives you the parameter to use in the luac.cross -a option.
LFS._list This returns a table of the LFS modules, hence
print(table.concat(LFS._list,'\n'))
gives you a single column listing of all modules in the LFS.
---------------------------------------------------------------------------------]]
local index = node.flashindex
local lfs_t = {
__index = function(_, name)
local fn_ut, ba, ma, size, modules = index(name)
if not ba then
return fn_ut
elseif name == '_time' then
return fn_ut
elseif name == '_config' then
local fs_ma, fs_size = file.fscfg()
return {lfs_base = ba, lfs_mapped = ma, lfs_size = size,
fs_mapped = fs_ma, fs_size = fs_size}
elseif name == '_list' then
return modules
else
return nil
end
end,
__newindex = function(_, name, value)
error("LFS is readonly. Invalid write to LFS." .. name, 2)
end,
}
local G=getfenv()
G.LFS = setmetatable(lfs_t,lfs_t)
--[[-------------------------------------------------------------------------------
The second section adds the LFS to the require searchlist, so that you can
require a Lua module 'jean' in the LFS by simply doing require "jean". However
note that this is at the search entry following the FS searcher, so if you also
have jean.lc or jean.lua in SPIFFS, then this SPIFFS version will get loaded into
RAM instead of using. (Useful, for development).
See docs/en/lfs.md and the 'loaders' array in app/lua/loadlib.c for more details.
---------------------------------------------------------------------------------]]
package.loaders[3] = function(module) -- loader_flash
local fn, ba = index(module)
return ba and "Module not in LFS" or fn
end
--[[-------------------------------------------------------------------------------
You can add any other initialisation here, for example a couple of the globals
are never used, so setting them to nil saves a couple of global entries
---------------------------------------------------------------------------------]]
G.module = nil -- disable Lua 5.0 style modules to save RAM
package.seeall = nil
--[[-------------------------------------------------------------------------------
These replaces the builtins loadfile & dofile with ones which preferentially
loads the corresponding module from LFS if present. Flipping the search order
is an exercise left to the reader.-
---------------------------------------------------------------------------------]]
local lf, df = loadfile, dofile
G.loadfile = function(n)
local mod, ext = n:match("(.*)%.(l[uc]a?)");
local fn, ba = index(mod)
if ba or (ext ~= 'lc' and ext ~= 'lua') then return lf(n) else return fn end
end
G.dofile = function(n)
local mod, ext = n:match("(.*)%.(l[uc]a?)");
local fn, ba = index(mod)
if ba or (ext ~= 'lc' and ext ~= 'lua') then return df(n) else return fn() end
end

37
srv/dummy_strings.lua Normal file
View File

@ -0,0 +1,37 @@
--
-- File: LFS_dummy_strings.lua
--[[
luac.cross -f generates a ROM string table which is part of the compiled LFS
image. This table includes all strings referenced in the loaded modules.
If you want to preload other string constants, then one way to achieve this is
to include a dummy module in the LFS that references the strings that you want
to load. You never need to call this module; it's inclusion in the LFS image is
enough to add the strings to the ROM table. Your application can use any strings
in the ROM table without incuring any RAM or Lua Garbage Collector (LGC)
overhead.
The local preload example is a useful starting point. However, if you call the
following code in your application during testing, then this will provide a
listing of the current RAM string table.
do
local a=debug.getstrings'RAM'
for i =1, #a do a[i] = ('%q'):format(a[i]) end
print ('local preload='..table.concat(a,','))
end
This will exclude any strings already in the ROM table, so the output is the list
of putative strings that you should consider adding to LFS ROM table.
---------------------------------------------------------------------------------]]
local preload = "?.lc;?.lua", "/\n;\n?\n!\n-", "@init.lua", "_G", "_LOADED",
"_LOADLIB", "__add", "__call", "__concat", "__div", "__eq", "__gc", "__index",
"__le", "__len", "__lt", "__mod", "__mode", "__mul", "__newindex", "__pow",
"__sub", "__tostring", "__unm", "collectgarbage", "cpath", "debug", "file",
"file.obj", "file.vol", "flash", "getstrings", "index", "ipairs", "list", "loaded",
"loader", "loaders", "loadlib", "module", "net.tcpserver", "net.tcpsocket",
"net.udpsocket", "newproxy", "package", "pairs", "path", "preload", "reload",
"require", "seeall", "wdclr", "not enough memory", "sjson.decoder","sjson.encoder",
"tmr.timer"

View File

View File

@ -18,7 +18,7 @@ end
-- Returns the username if header contains valid credentials,
-- nil otherwise.
function basicAuth.authenticate(header)
local conf = dofile("httpserver-conf.lc")
local conf = dofile("httpserver-conf.lua")
local credentials_enc = header:match("Authorization: Basic ([A-Za-z0-9+/=]+)")
if not credentials_enc then
return nil
@ -35,7 +35,7 @@ function basicAuth.authenticate(header)
end
function basicAuth.authErrorHeader()
local conf = dofile("httpserver-conf.lc")
local conf = dofile("httpserver-conf.lua")
return "WWW-Authenticate: Basic realm=\"" .. conf.auth.realm .. "\""
end

View File

@ -5,7 +5,7 @@
-- Function for starting the server.
-- If you compiled the mdns module, then it will also register with mDNS.
local startServer = function(ip)
local conf = dofile('httpserver-conf.lc')
local conf = dofile('httpserver-conf.lua')
if (dofile("httpserver.lc")(conf['general']['port'])) then
print("nodemcu-httpserver running at:")
print(" http://" .. ip .. ":" .. conf['general']['port'])

View File

@ -2,12 +2,7 @@
-- Part of nodemcu-httpserver, configures NodeMCU's WiFI in boot.
-- Author: Marcos Kirsch
local conf = nil
if file.exists("httpserver-conf.lc") then
conf = dofile("httpserver-conf.lc")
else
conf = dofile("httpserver-conf.lua")
end
local conf = dofile("httpserver-conf.lua")
wifi.setmode(conf.wifi.mode)

View File

@ -103,7 +103,7 @@ return function (port)
local function onReceive(connection, payload)
-- collectgarbage()
local conf = dofile("httpserver-conf.lc")
local conf = dofile("httpserver-conf.lua")
local auth
local user = "Anonymous"