From 855b7582d3576f45693e3a48fdb253c813cf4dce Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Sat, 14 Jun 2008 14:12:12 +0000 Subject: [PATCH] * Rewrote Luci to be coroutine-safe allowing the use of non-forking webservers * Setting base version to 0.7 --- Makefile | 2 +- NOTICE | 6 + .../luasrc/controller/splash/splash.lua | 2 +- .../controller/luci_statistics/luci_statistics.lua | 12 +- .../luci-statistics/luasrc/statistics/rrdtool.lua | 4 +- build/config.mk | 7 +- build/module.mk | 6 +- contrib/luaposix/Makefile | 4 +- contrib/package/luci/Makefile | 2 +- libs/cbi/luasrc/view/cbi/header.htm | 2 +- libs/core/luasrc/init.lua | 2 +- libs/core/luasrc/util.lua | 79 +++++++++++- libs/fastindex/Makefile | 4 +- libs/sgi-haserl/luasrc/sgi/haserl.lua | 107 ++++++---------- libs/sgi-haserl/root/www/cgi-bin/luci | 4 +- libs/sgi-webuci/luasrc/sgi/webuci.lua | 113 +++++------------ libs/sgi-webuci/root/usr/lib/boa/luci.lua | 3 +- libs/uci/luasrc/model/uci.lua | 2 +- libs/web/luasrc/dispatcher.lua | 116 +++++++++--------- libs/web/luasrc/http.lua | 136 ++++++++++++++++++++- libs/web/luasrc/i18n.lua | 29 +++-- libs/web/luasrc/template.lua | 14 ++- .../luasrc/view/admin_system/upgrade.htm | 2 +- modules/admin-core/luasrc/view/error404.htm | 2 +- .../luasrc/controller/freifunk/luciinfo.lua | 22 ++-- .../luasrc/view/themes/fledermaus/header.htm | 4 +- .../luasrc/view/themes/openwrt.org/header.htm | 4 +- 27 files changed, 415 insertions(+), 275 deletions(-) diff --git a/Makefile b/Makefile index 435d59c59..796bb4154 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ hostcopy: mkdir -p host for i in $(MODULES); do cp -a $$i/dist/* host/ -R 2>/dev/null || true; done rm -f host/luci - ln -s .$(LUCI_INSTALLDIR) host/luci + ln -s .$(LUCI_MODULEDIR) host/luci run: host libs/sgi-webuci/host/buildconfig.sh `pwd`/host > host/etc/boa/boa.conf diff --git a/NOTICE b/NOTICE index b9784e4a0..e2c3383e3 100644 --- a/NOTICE +++ b/NOTICE @@ -1,7 +1,13 @@ LuCI - Lua Configuration Interface Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich Licensed under the Apache License, Version 2.0. +Contains code from: +BinDecHex - Copyright 2007 Tim Kelly/Dialectronics +coxpcall - Copyright 2005 - Kepler Project (www.keplerproject.org) + + Luci-Statistics - Statistics for LuCI Copyright 2008 Jo-Philipp Wich Licensed under the Apache License, Version 2.0. diff --git a/applications/luci-splash/luasrc/controller/splash/splash.lua b/applications/luci-splash/luasrc/controller/splash/splash.lua index e7448d7fb..617e0f877 100644 --- a/applications/luci-splash/luasrc/controller/splash/splash.lua +++ b/applications/luci-splash/luasrc/controller/splash/splash.lua @@ -12,7 +12,7 @@ function index() end function action_activate() - local mac = luci.sys.net.ip4mac(luci.http.env.REMOTE_ADDR) + local mac = luci.sys.net.ip4mac(luci.http.getenv("REMOTE_ADDR")) if mac and luci.http.formvalue("accept") then os.execute("luci-splash add "..mac.." >/dev/null 2>&1") luci.http.redirect(luci.model.uci.get("freifunk", "community", "homepage")) diff --git a/applications/luci-statistics/luasrc/controller/luci_statistics/luci_statistics.lua b/applications/luci-statistics/luasrc/controller/luci_statistics/luci_statistics.lua index be6430eed..36c6d9b57 100644 --- a/applications/luci-statistics/luasrc/controller/luci_statistics/luci_statistics.lua +++ b/applications/luci-statistics/luasrc/controller/luci_statistics/luci_statistics.lua @@ -149,8 +149,8 @@ function statistics_render() require("luci.model.uci") local vars = luci.http.formvalues() - local req = luci.dispatcher.request - local path = luci.dispatcher.dispatched.path + local req = luci.dispatcher.context.request + local path = luci.dispatcher.context.dispatched.path local uci = luci.model.uci local spans = luci.util.split( uci.get( "luci_statistics", "collectd_rrdtool", "RRATimespans" ), "%s+", nil, true ) local span = vars.timespan or uci.get( "luci_statistics", "rrdtool", "default_timespan" ) or spans[1] @@ -160,10 +160,10 @@ function statistics_render() local images = { } -- find requested plugin and instance - for i, p in ipairs( luci.dispatcher.dispatched.path ) do - if luci.dispatcher.dispatched.path[i] == "graph" then - plugin = luci.dispatcher.dispatched.path[i+1] - instances = { luci.dispatcher.dispatched.path[i+2] } + for i, p in ipairs( luci.dispatcher.context.dispatched.path ) do + if luci.dispatcher.context.dispatched.path[i] == "graph" then + plugin = luci.dispatcher.context.dispatched.path[i+1] + instances = { luci.dispatcher.context.dispatched.path[i+2] } end end diff --git a/applications/luci-statistics/luasrc/statistics/rrdtool.lua b/applications/luci-statistics/luasrc/statistics/rrdtool.lua index 69fb5cff6..433524583 100644 --- a/applications/luci-statistics/luasrc/statistics/rrdtool.lua +++ b/applications/luci-statistics/luasrc/statistics/rrdtool.lua @@ -500,7 +500,7 @@ function Graph.render( self, plugin, plugin_instance ) -- check for a whole graph handler local plugin_def = "luci.statistics.rrdtool.definitions." .. plugin - local stat, def = pcall( require, plugin_def ) + local stat, def = luci.util.copcall( require, plugin_def ) if stat and def and type(def.rrdargs) == "function" then @@ -539,7 +539,7 @@ function Graph.render( self, plugin, plugin_instance ) -- check for data type handler local dtype_def = plugin_def .. "." .. dtype - local stat, def = pcall( require, dtype_def ) + local stat, def = luci.util.copcall( require, dtype_def ) if stat and def and type(def.rrdargs) == "function" then diff --git a/build/config.mk b/build/config.mk index 1755974f2..81d3e0836 100644 --- a/build/config.mk +++ b/build/config.mk @@ -1,4 +1,9 @@ LUAC = luac LUAC_OPTIONS = -s -LUCI_INSTALLDIR = /usr/lib/lua/luci + +LUA_MODULEDIR = /usr/lib/lua +LUA_LIBRARYDIR = /usr/lib/lua + +LUCI_MODULEDIR = $(LUA_MODULEDIR)/luci +LUCI_LIBRARYDIR = $(LUA_LIBRARYDIR)/luci diff --git a/build/module.mk b/build/module.mk index 7fe96cac5..4ef3668c8 100644 --- a/build/module.mk +++ b/build/module.mk @@ -12,9 +12,11 @@ compile: clean: luaclean luasource: - mkdir -p dist$(LUCI_INSTALLDIR) + mkdir -p dist$(LUA_MODULEDIR) + mkdir -p dist$(LUCI_MODULEDIR) cp -a root/* dist -R 2>/dev/null || true - cp -a luasrc/* dist$(LUCI_INSTALLDIR) -R 2>/dev/null || true + cp -a luasrc/* dist$(LUCI_MODULEDIR) -R 2>/dev/null || true + cp -a lua/* dist$(LUA_MODULEDIR) -R 2>/dev/null || true for i in $$(find dist -name .svn); do rm $$i -rf; done luacompile: luasource diff --git a/contrib/luaposix/Makefile b/contrib/luaposix/Makefile index ec832cc2d..c357f5d2c 100644 --- a/contrib/luaposix/Makefile +++ b/contrib/luaposix/Makefile @@ -34,8 +34,8 @@ $(LUAPOSIX_DIR)/.patched: $(LUAPOSIX_DIR)/.prepared $(LUAPOSIX_PATCHDIR)/series compile: $(LUAPOSIX_DIR)/.patched $(MAKE) -C $(LUAPOSIX_DIR) CC=$(CC) CFLAGS="$(CFLAGS) $(LUA_CFLAGS)" LDFLAGS="$(LDFLAGS) $(LUA_SHLIBS)" OS="$(OS)" - mkdir -p dist/usr/lib/lua - cp $(LUAPOSIX_DIR)/posix.so dist/usr/lib/lua/ + mkdir -p dist$(LUA_LIBRARYDIR) + cp $(LUAPOSIX_DIR)/posix.so dist$(LUA_LIBRARYDIR) luasource: compile-all: compile diff --git a/contrib/package/luci/Makefile b/contrib/package/luci/Makefile index af662f8cb..04229dac5 100644 --- a/contrib/package/luci/Makefile +++ b/contrib/package/luci/Makefile @@ -5,7 +5,7 @@ PKG_SOURCE_URL:=https://dev.leipzig.freifunk.net/svn/ff-luci/$(PKG_BRANCH) PKG_REV:=$(shell LC_ALL=C svn info ${PKG_SOURCE_URL} | sed -ne's/^Last Changed Rev: //p') PKG_NAME:=luci -PKG_VERSION:=0.6+svn$(PKG_REV) +PKG_VERSION:=0.7+svn$(PKG_REV) PKG_RELEASE:=1 PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) diff --git a/libs/cbi/luasrc/view/cbi/header.htm b/libs/cbi/luasrc/view/cbi/header.htm index 0af52b49e..ca6ef2fdd 100644 --- a/libs/cbi/luasrc/view/cbi/header.htm +++ b/libs/cbi/luasrc/view/cbi/header.htm @@ -1,5 +1,5 @@ <%+header%> -
+ ">
diff --git a/libs/core/luasrc/init.lua b/libs/core/luasrc/init.lua index b390df3ac..b4ecb93c1 100644 --- a/libs/core/luasrc/init.lua +++ b/libs/core/luasrc/init.lua @@ -25,5 +25,5 @@ limitations under the License. ]]-- module("luci", package.seeall) -__version__ = "0.6" +__version__ = "0.7" __appname__ = "LuCI" diff --git a/libs/core/luasrc/util.lua b/libs/core/luasrc/util.lua index ad53138e6..fda4563ad 100644 --- a/libs/core/luasrc/util.lua +++ b/libs/core/luasrc/util.lua @@ -36,7 +36,7 @@ function class(base) setmetatable(inst, {__index = class}) if inst.__init__ then - local stat, err = pcall(inst.__init__, inst, ...) + local stat, err = copcall(inst.__init__, inst, ...) if not stat then error(err) end @@ -152,6 +152,12 @@ function pcdata(value) end +-- Returns an error message to stdout +function perror(obj) + io.stderr:write(tostring(obj) .. "\n") +end + + -- Resets the scope of f doing a shallow copy of its scope into a new table function resfenv(f) setfenv(f, clone(getfenv(f))) @@ -255,6 +261,34 @@ function strip_bytecode(dump) end +-- Creates a new threadlocal store +function threadlocal() + local tbl = {} + + local function get(self, key) + local c = coroutine.running() + local thread = coxpt[c] or c or 0 + if not rawget(self, thread) then + rawset(self, thread, {}) + end + return rawget(self, thread)[key] + end + + local function set(self, key, value) + local c = coroutine.running() + local thread = coxpt[c] or c or 0 + if not rawget(self, thread) then + rawset(self, thread, {}) + end + rawget(self, thread)[key] = value + end + + setmetatable(tbl, {__index = get, __newindex = set}) + + return tbl +end + + -- Removes whitespace from beginning and end of a string function trim(str) local s = str:gsub("^%s*(.-)%s*$", "%1") @@ -355,3 +389,46 @@ end function vspairs(t) return _sortiter( t, function (a,b) return t[a] < t[b] end ) end + + +-- Coroutine safe xpcall and pcall versions modified for Luci +-- original version: +-- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org) +local performResume, handleReturnValue +local oldpcall, oldxpcall = pcall, xpcall +coxpt = {} + +function handleReturnValue(err, co, status, ...) + if not status then + return false, err(debug.traceback(co, (...)), ...) + end + if coroutine.status(co) == 'suspended' then + return performResume(err, co, coroutine.yield(...)) + else + return true, ... + end +end + +function performResume(err, co, ...) + return handleReturnValue(err, co, coroutine.resume(co, ...)) +end + +function coxpcall(f, err, ...) + local res, co = oldpcall(coroutine.create, f) + if not res then + local params = {...} + local newf = function() return f(unpack(params)) end + co = coroutine.create(newf) + end + local c = coroutine.running() + coxpt[co] = coxpt[c] or c or 0 + return performResume(err, co, ...) +end + +local function id(trace, ...) + return ... +end + +function copcall(f, ...) + return coxpcall(f, id, ...) +end diff --git a/libs/fastindex/Makefile b/libs/fastindex/Makefile index 566b38d0b..ee1a40ea8 100644 --- a/libs/fastindex/Makefile +++ b/libs/fastindex/Makefile @@ -6,8 +6,8 @@ include ../../build/gccconfig.mk $(COMPILE) $(LUA_CFLAGS) $(FPIC) -c -o $@ $< compile: src/fastindex.o - mkdir -p dist$(LUCI_INSTALLDIR) - $(LINK) $(SHLIB_FLAGS) -o dist$(LUCI_INSTALLDIR)/fastindex.so src/fastindex.o $(LUA_SHLIBS) + mkdir -p dist$(LUCI_LIBRARYDIR) + $(LINK) $(SHLIB_FLAGS) -o dist$(LUCI_LIBRARYDIR)/fastindex.so src/fastindex.o $(LUA_SHLIBS) clean: rm -f src/*.o diff --git a/libs/sgi-haserl/luasrc/sgi/haserl.lua b/libs/sgi-haserl/luasrc/sgi/haserl.lua index 35bc1c902..315b4da73 100644 --- a/libs/sgi-haserl/luasrc/sgi/haserl.lua +++ b/libs/sgi-haserl/luasrc/sgi/haserl.lua @@ -24,80 +24,49 @@ limitations under the License. ]]-- module("luci.sgi.haserl", package.seeall) -require("luci.fs") - --- Environment Table -luci.http.env = ENV - --- Enforces user authentification -function luci.http.basic_auth(verify_callback, realm) - -- Dummy for Haserl - return true -end - --- Returns the main dispatcher URL -function luci.http.dispatcher() - return luci.http.env.SCRIPT_NAME or "" -end - --- Returns the upload dispatcher URL -function luci.http.dispatcher_upload() - return luci.http.dispatcher() .. "-upload" -end - --- Returns a table of all COOKIE, GET and POST Parameters -function luci.http.formvalues() - return FORM -end - --- Gets form value from key -function luci.http.formvalue(key, default) - local c = luci.http.formvalues() +require("luci.http") +require("luci.util") +require("luci.dispatcher") + +function run() + local r = luci.http.Request() + r.env = ENV + r.request = normalize_table(FORM) - for match in key:gmatch("[%w-_]+") do - c = c[match] - if c == nil then - return default + local x = coroutine.create(luci.dispatcher.httpdispatch) + while coroutine.status(x) ~= "dead" do + local res, id, data1, data2 = coroutine.resume(x, r) + + if not res then + print("Status: 500 Internal Server Error") + print("Content-Type: text/plain\n") + print(id) + break; + end + + if id == 1 then + io.write("Status: " .. tostring(data1) .. " " .. data2 .. "\n") + elseif id == 2 then + io.write(data1 .. ": " .. data2 .. "\n") + elseif id == 3 then + io.write("\n") + elseif id == 4 then + io.write(data1) end end - - return c end --- Gets a table of values with a certain prefix -function luci.http.formvaluetable(prefix) - return luci.http.formvalue(prefix, {}) -end - --- Sends a custom HTTP-Header -function luci.http.header(key, value) - print(key .. ": " .. value) -end - --- Set Content-Type -function luci.http.prepare_content(type) - print("Content-Type: "..type.."\n") -end - --- Asks the browser to redirect to "url" -function luci.http.redirect(url) - luci.http.status(302, "Found") - luci.http.header("Location", url) - print() -end - --- Returns the path of an uploaded file --- WARNING! File uploads can be easily spoofed! Do additional sanity checks! -function luci.http.upload(name) - local fpath = luci.http.formvalue(name) - local fname = luci.http.formvalue(name .. "_name") +function normalize_table(table, prefix) + prefix = prefix and prefix .. "." or "" + local new = {} - if fpath and fname and luci.fs.isfile(fpath) then - return fpath + for k,v in pairs(table) do + if type(v) == "table" and #v == 0 then + luci.util.update(new, normalize_table(v, prefix .. k)) + else + new[prefix .. k] = v + end end -end - --- Sets HTTP-Status-Header -function luci.http.status(code, message) - print("Status: " .. tostring(code) .. " " .. message) + + return new end diff --git a/libs/sgi-haserl/root/www/cgi-bin/luci b/libs/sgi-haserl/root/www/cgi-bin/luci index ab2a3e18f..fd8e5941a 100755 --- a/libs/sgi-haserl/root/www/cgi-bin/luci +++ b/libs/sgi-haserl/root/www/cgi-bin/luci @@ -1,4 +1,4 @@ #!/usr/bin/haserl --shell=luac -require("luci.dispatcher") +require("luci.sgi.haserl") luci.dispatcher.indexcache = "/tmp/.luciindex" -luci.dispatcher.httpdispatch() \ No newline at end of file +luci.sgi.haserl.run() \ No newline at end of file diff --git a/libs/sgi-webuci/luasrc/sgi/webuci.lua b/libs/sgi-webuci/luasrc/sgi/webuci.lua index a40888e7e..abe279d05 100644 --- a/libs/sgi-webuci/luasrc/sgi/webuci.lua +++ b/libs/sgi-webuci/luasrc/sgi/webuci.lua @@ -24,93 +24,36 @@ limitations under the License. ]]-- module("luci.sgi.webuci", package.seeall) - -local status_set = false - --- Initialize the environment -function initenv(env, vars) - luci.http.env = env - luci.http.vars = vars -end - --- Enforces user authentification -function luci.http.basic_auth(verify_callback, realm) - local user = luci.http.env.auth_user - local pass = luci.http.env.auth_password - realm = realm or "" +require("luci.http") +require("luci.util") +require("luci.dispatcher") + +function run(env, vars) + local r = luci.http.Request() + r.env = env + r.request = vars - if not user or not verify_callback(user, pass) then - luci.http.status("401", "Unauthorized") - luci.http.header("WWW-Authenticate", string.format('Basic realm="%s"', realm)) - return false - else - return true - end -end - --- Returns the main dispatcher URL -function luci.http.dispatcher() - return luci.http.env.SCRIPT_NAME or "" -end - --- Returns the upload dispatcher URL -function luci.http.dispatcher_upload() - -- To be implemented -end - --- Returns a table of all COOKIE, GET and POST Parameters -function luci.http.formvalues() - return luci.http.vars -end - --- Gets form value from key -function luci.http.formvalue(key, default) - return luci.http.formvalues()[key] or default -end - --- Gets a table of values with a certain prefix -function luci.http.formvaluetable(prefix) - local vals = {} - prefix = prefix and prefix .. "." or "." + local x = coroutine.create(luci.dispatcher.httpdispatch) - for k, v in pairs(luci.http.formvalues()) do - if k:find(prefix, 1, true) == 1 then - vals[k:sub(#prefix + 1)] = v + while coroutine.status(x) ~= "dead" do + local res, id, data1, data2 = coroutine.resume(x, r) + + if not res then + print(env.SERVER_PROTOCOL .. " 500 Internal Server Error") + print("Content-Type: text/plain\n") + print(id) + break; end + + if id == 1 then + io.write(env.SERVER_PROTOCOL .. " " .. tostring(data1) .. " " .. data2 .. "\n") + elseif id == 2 then + io.write(data1 .. ": " .. data2 .. "\n") + elseif id == 3 then + io.write("\n") + elseif id == 4 then + io.write(data1) + end + end - - return vals -end - --- Sends a custom HTTP-Header -function luci.http.header(key, value) - print(key .. ": " .. value) -end - --- Set Content-Type -function luci.http.prepare_content(type) - if not status_set then - luci.http.status(200, "OK") - end - - print("Content-Type: "..type.."\n") -end - --- Asks the browser to redirect to "url" -function luci.http.redirect(url) - luci.http.status(302, "Found") - luci.http.header("Location", url) - print() -end - --- Returns the path of an uploaded file --- WARNING! File uploads can be easily spoofed! Do additional sanity checks! -function luci.http.upload(name) - -- To be implemented -end - --- Sets HTTP-Status-Header -function luci.http.status(code, message) - print(luci.http.env.SERVER_PROTOCOL .. " " .. tostring(code) .. " " .. message) - status_set = true end diff --git a/libs/sgi-webuci/root/usr/lib/boa/luci.lua b/libs/sgi-webuci/root/usr/lib/boa/luci.lua index 39cee1c60..879ef19b4 100644 --- a/libs/sgi-webuci/root/usr/lib/boa/luci.lua +++ b/libs/sgi-webuci/root/usr/lib/boa/luci.lua @@ -60,6 +60,5 @@ function handle_req(context) env.SERVER_ADDR = context.server_addr env.SCRIPT_NAME = env.REQUEST_URI:sub(1, #env.REQUEST_URI - #env.PATH_INFO) - luci.sgi.webuci.initenv(env, vars) - luci.dispatcher.httpdispatch() + luci.sgi.webuci.run(env, vars) end diff --git a/libs/uci/luasrc/model/uci.lua b/libs/uci/luasrc/model/uci.lua index bd6aba41a..6662943cf 100644 --- a/libs/uci/luasrc/model/uci.lua +++ b/libs/uci/luasrc/model/uci.lua @@ -27,7 +27,7 @@ local uci = require("uci") local util = require("luci.util") local setmetatable, rawget, rawset = setmetatable, rawget, rawset local error, pairs, ipairs, tostring = error, pairs, ipairs, tostring -local table, print = table, print +local table = table module("luci.model.uci", function(m) setmetatable(m, {__index = uci}) end) diff --git a/libs/web/luasrc/dispatcher.lua b/libs/web/luasrc/dispatcher.lua index 64355439b..8516c6f1b 100644 --- a/libs/web/luasrc/dispatcher.lua +++ b/libs/web/luasrc/dispatcher.lua @@ -34,21 +34,10 @@ if (os.time() < 1000000000) then os.execute('date -s '..os.date('%m%d%H%M%Y', luci.fs.mtime(luci.sys.libpath() .. "/dispatcher.lua"))..' > /dev/null 2>&1') end --- Local dispatch database -local tree = {nodes={}} +context = luci.util.threadlocal() -- Index table -local index = {} - --- Global request object -request = {} - --- Active dispatched node -dispatched = nil - --- Status fields -built_index = false -built_tree = false +local index = nil -- Fastindex local fi @@ -64,9 +53,9 @@ function error401(message) message = message or "Unauthorized" require("luci.template") - if not pcall(luci.template.render, "error401") then + if not luci.util.copcall(luci.template.render, "error401") then luci.http.prepare_content("text/plain") - print(message) + luci.http.write(message) end return false end @@ -77,9 +66,9 @@ function error404(message) message = message or "Not Found" require("luci.template") - if not pcall(luci.template.render, "error404") then + if not luci.util.copcall(luci.template.render, "error404") then luci.http.prepare_content("text/plain") - print(message) + luci.http.write(message) end return false end @@ -89,31 +78,39 @@ function error500(message) luci.http.status(500, "Internal Server Error") require("luci.template") - if not pcall(luci.template.render, "error500", {message=message}) then + if not luci.util.copcall(luci.template.render, "error500", {message=message}) then luci.http.prepare_content("text/plain") - print(message) + luci.http.write(message) end return false end -- Creates a request object for dispatching -function httpdispatch() - local pathinfo = luci.http.env.PATH_INFO or "" +function httpdispatch(request) + luci.http.context.request = request + context.request = {} + local pathinfo = request.env.PATH_INFO or "" for node in pathinfo:gmatch("[^/]+") do - table.insert(request, node) + table.insert(context.request, node) end - dispatch() + dispatch(context.request) + luci.http.close() end -- Dispatches a request -function dispatch() - if not built_tree then +function dispatch(request) + context.path = request + + require("luci.i18n") + luci.i18n.setlanguage(require("luci.config").main.lang) + + if not context.tree then createtree() end - - local c = tree + + local c = context.tree local track = {} for i, s in ipairs(request) do @@ -131,6 +128,7 @@ function dispatch() local accs = track.sysauth accs = (type(accs) == "string") and {accs} or accs + --[[ local function sysauth(user, password) return (luci.util.contains(accs, user) and luci.sys.user.checkpasswd(user, password)) @@ -140,6 +138,7 @@ function dispatch() error401() return end + ]]-- end if track.i18n then @@ -156,22 +155,24 @@ function dispatch() -- Init template engine local tpl = require("luci.template") - tpl.viewns.translate = function(...) return require("luci.i18n").translate(...) end - tpl.viewns.controller = luci.http.dispatcher() - tpl.viewns.uploadctrl = luci.http.dispatcher_upload() -- DEPRECATED - tpl.viewns.media = luci.config.main.mediaurlbase - tpl.viewns.resource = luci.config.main.resourcebase - tpl.viewns.REQUEST_URI = luci.http.env.SCRIPT_NAME .. (luci.http.env.PATH_INFO or "") + local viewns = {} + tpl.context.viewns = viewns + viewns.write = luci.http.write + viewns.translate = function(...) return require("luci.i18n").translate(...) end + viewns.controller = luci.http.getenv("SCRIPT_NAME") + viewns.media = luci.config.main.mediaurlbase + viewns.resource = luci.config.main.resourcebase + viewns.REQUEST_URI = luci.http.getenv("SCRIPT_NAME") .. (luci.http.getenv("PATH_INFO") or "") if c and type(c.target) == "function" then - dispatched = c - stat, mod = pcall(require, c.module) + context.dispatched = c + stat, mod = luci.util.copcall(require, c.module) if stat then luci.util.updfenv(c.target, mod) end - stat, err = pcall(c.target) + stat, err = luci.util.copcall(c.target) if not stat then error500(err) end @@ -182,21 +183,20 @@ end -- Generates the dispatching tree function createindex() - index = {} local path = luci.sys.libpath() .. "/controller/" local suff = ".lua" - if pcall(require, "luci.fastindex") then + if luci.util.copcall(require, "luci.fastindex") then createindex_fastindex(path, suff) else createindex_plain(path, suff) end - - built_index = true end -- Uses fastindex to create the dispatching tree -function createindex_fastindex(path, suffix) +function createindex_fastindex(path, suffix) + index = {} + if not fi then fi = luci.fastindex.new("index") fi.add(path .. "*" .. suffix) @@ -212,9 +212,7 @@ end -- Calls the index function of all available controllers -- Fallback for transition purposes / Leave it in as long as it works otherwise throw it away function createindex_plain(path, suffix) - if built_index then - return - end + index = {} local cache = nil @@ -246,7 +244,7 @@ function createindex_plain(path, suffix) end if not cache or stime > ctime then - stat, mod = pcall(require, module) + stat, mod = luci.util.copcall(require, module) if stat and mod and type(mod.index) == "function" then index[module] = mod.index @@ -263,10 +261,11 @@ end -- Creates the dispatching tree from the index function createtree() - if not built_index then + if not index then createindex() end + context.tree = {nodes={}} require("luci.i18n") -- Load default translation @@ -283,14 +282,12 @@ function createtree() scope._NAME = k setfenv(v, scope) - local stat, err = pcall(v) + local stat, err = luci.util.copcall(v) if not stat then error500(err) os.exit(1) end end - - built_tree = true end -- Reassigns a node to another position @@ -302,7 +299,7 @@ function assign(path, clone, title, order) obj.title = title obj.order = order - local c = tree + local c = context.tree for k, v in ipairs(clone) do if not c.nodes[v] then c.nodes[v] = {nodes={}} @@ -330,8 +327,7 @@ end -- Fetch a dispatching node function node(...) - local c = tree - + local c = context.tree arg.n = nil if arg[1] then if type(arg[1]) == "table" then @@ -357,8 +353,7 @@ end function alias(...) local req = arg return function() - request = req - dispatch() + dispatch(req) end end @@ -366,19 +361,20 @@ function rewrite(n, ...) local req = arg return function() for i=1,n do - table.remove(request, 1) + table.remove(context.path, 1) end for i,r in ipairs(req) do - table.insert(request, i, r) + table.insert(context.path, i, r) end dispatch() end end -function call(name) - return function() getfenv()[name]() end +function call(name, ...) + local argv = {...} + return function() return getfenv()[name](unpack(argv)) end end function template(name) @@ -391,13 +387,13 @@ function cbi(model) require("luci.template") return function() - local stat, res = pcall(luci.cbi.load, model) + local stat, res = luci.util.copcall(luci.cbi.load, model) if not stat then error500(res) return true end - local stat, err = pcall(res.parse, res) + local stat, err = luci.util.copcall(res.parse, res) if not stat then error500(err) return true diff --git a/libs/web/luasrc/http.lua b/libs/web/luasrc/http.lua index 3bff28add..8ee864ac7 100644 --- a/libs/web/luasrc/http.lua +++ b/libs/web/luasrc/http.lua @@ -28,13 +28,141 @@ limitations under the License. ]]-- module("luci.http", package.seeall) +require("luci.util") +context = luci.util.threadlocal() -if ENV and ENV.HASERLVER then - require("luci.sgi.haserl") -elseif webuci then - require("luci.sgi.webuci") + +Request = luci.util.class() +function Request.__init__(self) + self.headers = {} + self.request = {} + self.uploads = {} + self.env = {} + self.data = "" +end + +function Request.formvalue(self, name, default) + return self.request[name] or default +end + +function Request.formvalues(self) + return self.request +end + +function Request.formvaluetable(self, prefix) + local vals = {} + prefix = prefix and prefix .. "." or "." + + for k, v in pairs(self.request) do + if k:find(prefix, 1, true) == 1 then + vals[k:sub(#prefix + 1)] = v + end + end + + return vals +end + +function Request.getenv(self, name) + return self.env[name] +end + +function Request.upload(self, name) + return self.uploads[name] +end + + +function close() + if not context.eoh then + context.eoh = true + coroutine.yield(3) + end + + if not context.closed then + context.closed = true + coroutine.yield(5) + end +end + +function formvalue(...) + return context.request:formvalue(...) +end + +function formvalues(...) + return context.request:formvalues(...) +end + +function formvaluetable(...) + return context.request:formvaluetable(...) end +function getenv(...) + return context.request:getenv(...) +end + +function header(key, value) + if not context.status then + status() + end + if not context.headers then + context.headers = {} + end + context.headers[key:lower()] = value + coroutine.yield(2, key, value) +end + +function prepare_content(mime) + header("Content-Type", mime) +end + +function status(code, message) + code = code or 200 + message = message or "OK" + context.status = code + coroutine.yield(1, code, message) +end + +function write(content) + if not content or #content == 0 then + return + end + if not context.eoh then + if not context.status then + status() + end + if not context.headers or not context.headers["content-type"] then + header("Content-Type", "text/html; charset=utf-8") + end + + context.eoh = true + coroutine.yield(3) + end + coroutine.yield(4, content) +end + + +function basic_auth(realm, errorpage) + header("Status", "401 Unauthorized") + header("WWW-Authenticate", string.format('Basic realm="%s"', realm or "")) + + if errorpage then + errorpage() + end + + close() +end + +function redirect(url) + header("Status", "302 Found") + header("Location", url) + close() +end + +function upload(...) + return context.request:upload(...) +end + + + function build_querystring(table) local s="?" diff --git a/libs/web/luasrc/i18n.lua b/libs/web/luasrc/i18n.lua index 3ed1ce017..35ad0965d 100644 --- a/libs/web/luasrc/i18n.lua +++ b/libs/web/luasrc/i18n.lua @@ -30,6 +30,8 @@ require("luci.sys") table = {} i18ndir = luci.sys.libpath() .. "/i18n/" loaded = {} +context = luci.util.threadlocal() +default = "en" -- Clears the translation table function clear() @@ -37,13 +39,17 @@ function clear() end -- Loads a translation and copies its data into the global translation table -function load(file, force) - if force or not loaded[file] then - local f = loadfile(i18ndir..file..".lua") or loadfile(i18ndir..file) +function load(file, lang, force) + lang = lang or "" + if force or not loaded[lang] or not loaded[lang][file] then + local f = loadfile(i18ndir .. file .. "." .. lang .. ".lua") + or loadfile(i18ndir .. file .. "." .. lang) if f then - setfenv(f, table) + table[lang] = table[lang] or {} + setfenv(f, table[lang]) f() - loaded[file] = true + loaded[lang] = loaded[lang] or {} + loaded[lang][file] = true return true else return false @@ -55,13 +61,20 @@ end -- Same as load but autocompletes the filename with .LANG from config.lang function loadc(file, force) - load(file .. ".en", force) - return load(file .. "." .. require("luci.config").main.lang, force) + load(file, default, force) + return load(file, context.lang, force) +end + +-- Sets the context language +function setlanguage(lang) + context.lang = lang end -- Returns the i18n-value defined by "key" or if there is no such: "default" function translate(key, default) - return table[key] or default + return (table[context.lang] and table[context.lang][key]) + or (table[default] and table[default][key]) + or default end -- Translate shourtcut with sprintf/string.format inclusion diff --git a/libs/web/luasrc/template.lua b/libs/web/luasrc/template.lua index 61e4e39ec..29aedcdad 100644 --- a/libs/web/luasrc/template.lua +++ b/libs/web/luasrc/template.lua @@ -44,9 +44,10 @@ compiler_mode = luci.config.template.compiler_mode or "memory" -- Define the namespace for template modules +context = luci.util.threadlocal() + viewns = { - write = io.write, - include = function(name) Template(name):render(getfenv(2)) end, + include = function(name) Template(name):render(getfenv(2)) end, } -- Compiles a given template into an executable Lua module @@ -113,7 +114,7 @@ end -- Oldstyle render shortcut function render(name, scope, ...) scope = scope or getfenv(2) - local s, t = pcall(Template, name) + local s, t = luci.util.copcall(Template, name) if not s then error(t) else @@ -141,9 +142,10 @@ function Template.__init__(self, name) self.viewns = {} -- Copy over from general namespace - for k, v in pairs(viewns) do - self.viewns[k] = v - end + luci.util.update(self.viewns, viewns) + if context.viewns then + luci.util.update(self.viewns, context.viewns) + end -- If we have a cached template, skip compiling and loading if self.template then diff --git a/modules/admin-core/luasrc/view/admin_system/upgrade.htm b/modules/admin-core/luasrc/view/admin_system/upgrade.htm index 7e23c5400..cc7438655 100644 --- a/modules/admin-core/luasrc/view/admin_system/upgrade.htm +++ b/modules/admin-core/luasrc/view/admin_system/upgrade.htm @@ -4,7 +4,7 @@

<%:a_s_flash_upgrade1%>


<% if sysupgrade and not ret then %> - +" enctype="multipart/form-data">
<%:a_s_flash_fwimage%>
diff --git a/modules/admin-core/luasrc/view/error404.htm b/modules/admin-core/luasrc/view/error404.htm index 60daee2cb..03f6c54f3 100644 --- a/modules/admin-core/luasrc/view/error404.htm +++ b/modules/admin-core/luasrc/view/error404.htm @@ -1,5 +1,5 @@ <%+header%>

404 Not Found

Sorry, the object you requested was not found.

-Unable to dispatch: <%=luci.http.env.PATH_INFO%> +Unable to dispatch: <%=luci.http.request.env.PATH_INFO%> <%+footer%> \ No newline at end of file diff --git a/modules/freifunk/luasrc/controller/freifunk/luciinfo.lua b/modules/freifunk/luasrc/controller/freifunk/luciinfo.lua index 4c24c5732..4ab63a068 100644 --- a/modules/freifunk/luasrc/controller/freifunk/luciinfo.lua +++ b/modules/freifunk/luasrc/controller/freifunk/luciinfo.lua @@ -22,8 +22,8 @@ function action_index() luci.http.prepare_content("text/plain") -- General - print("luciinfo.api=1") - print("luciinfo.version=" .. tostring(require("luci").__version__)) + luci.http.write("luciinfo.api=1\n") + luci.http.write("luciinfo.version=" .. tostring(require("luci").__version__) .. "\n") -- Sysinfo local s, m, r = luci.sys.sysinfo() @@ -31,14 +31,14 @@ function action_index() dr = dr and luci.sys.net.hexip4(dr.Gateway) or "" local l1, l5, l15 = luci.sys.loadavg() - print("sysinfo.system=" .. sanitize(s)) - print("sysinfo.cpu=" .. sanitize(m)) - print("sysinfo.ram=" .. sanitize(r)) - print("sysinfo.hostname=" .. sanitize(luci.sys.hostname())) - print("sysinfo.load1=" .. tostring(l1)) - print("sysinfo.load5=" .. tostring(l5)) - print("sysinfo.load15=" .. tostring(l15)) - print("sysinfo.defaultgw=" .. dr) + luci.http.write("sysinfo.system=" .. sanitize(s) .. "\n") + luci.http.write("sysinfo.cpu=" .. sanitize(m) .. "\n") + luci.http.write("sysinfo.ram=" .. sanitize(r) .. "\n") + luci.http.write("sysinfo.hostname=" .. sanitize(luci.sys.hostname()) .. "\n") + luci.http.write("sysinfo.load1=" .. tostring(l1) .. "\n") + luci.http.write("sysinfo.load5=" .. tostring(l5) .. "\n") + luci.http.write("sysinfo.load15=" .. tostring(l15) .. "\n") + luci.http.write("sysinfo.defaultgw=" .. dr .. "\n") -- Freifunk @@ -46,7 +46,7 @@ function action_index() for k, v in pairs(ff) do for i, j in pairs(v) do if i:sub(1, 1) ~= "." then - print("freifunk." .. k .. "." .. i .. "=" .. j) + luci.http.write("freifunk." .. k .. "." .. i .. "=" .. j .. "\n") end end end diff --git a/themes/fledermaus/luasrc/view/themes/fledermaus/header.htm b/themes/fledermaus/luasrc/view/themes/fledermaus/header.htm index 327e33964..7f625fd38 100644 --- a/themes/fledermaus/luasrc/view/themes/fledermaus/header.htm +++ b/themes/fledermaus/luasrc/view/themes/fledermaus/header.htm @@ -1,11 +1,11 @@ <% require("luci.sys") local load1, load5, load15 = luci.sys.loadavg() -local request = require("luci.dispatcher").request +local request = require("luci.dispatcher").context.path local category = request[1] local tree = luci.dispatcher.node() local cattree = category and luci.dispatcher.node(category) -local node = luci.dispatcher.dispatched +local node = luci.dispatcher.context.dispatched local c = tree for i,r in ipairs(request) do diff --git a/themes/openwrt.org/luasrc/view/themes/openwrt.org/header.htm b/themes/openwrt.org/luasrc/view/themes/openwrt.org/header.htm index 889fdd01f..d1b751871 100644 --- a/themes/openwrt.org/luasrc/view/themes/openwrt.org/header.htm +++ b/themes/openwrt.org/luasrc/view/themes/openwrt.org/header.htm @@ -1,11 +1,11 @@ <% require("luci.sys") local load1, load5, load15 = luci.sys.loadavg() -local request = require("luci.dispatcher").request +local request = require("luci.dispatcher").context.path local category = request[1] local tree = luci.dispatcher.node() local cattree = category and luci.dispatcher.node(category) -local node = luci.dispatcher.dispatched +local node = luci.dispatcher.context.dispatched local c = tree for i,r in ipairs(request) do -- 2.11.0