luci-base: fix luci.http.close()

Ensure that `http.write()` or template rendering operations after a call
to `http.close()` do not produce additional output. This is required for
certain legacy Lua apps which invoke write and close operations in the
middle of a server side cbi rendering process.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2022-11-03 00:38:00 +01:00
parent f83e4a6c36
commit e7afd0d327
3 changed files with 17 additions and 7 deletions

View file

@ -775,7 +775,8 @@ function run_action(request_path, lang, tree, resolved, action) {
break; break;
case 'call': case 'call':
http.write(render(() => { http.write_headers();
http.output(render(() => {
runtime.call(action.module, action.function, runtime.call(action.module, action.function,
...(action.parameters ?? []), ...(action.parameters ?? []),
...resolved.ctx.request_args ...resolved.ctx.request_args
@ -789,7 +790,8 @@ function run_action(request_path, lang, tree, resolved, action) {
assert(type(mod[action.function]) == 'function', assert(type(mod[action.function]) == 'function',
`Module '${action.module}' does not export function '${action.function}'`); `Module '${action.module}' does not export function '${action.function}'`);
http.write(render(() => { http.write_headers();
http.output(render(() => {
call(mod[action.function], mod, runtime.env, call(mod[action.function], mod, runtime.env,
...(action.parameters ?? []), ...(action.parameters ?? []),
...resolved.ctx.request_args ...resolved.ctx.request_args
@ -798,7 +800,8 @@ function run_action(request_path, lang, tree, resolved, action) {
break; break;
case 'cbi': case 'cbi':
http.write(render(() => { http.write_headers();
http.output(render(() => {
runtime.call('luci.dispatcher', 'invoke_cbi_action', runtime.call('luci.dispatcher', 'invoke_cbi_action',
action.path, null, action.path, null,
...resolved.ctx.request_args ...resolved.ctx.request_args
@ -807,7 +810,8 @@ function run_action(request_path, lang, tree, resolved, action) {
break; break;
case 'form': case 'form':
http.write(render(() => { http.write_headers();
http.output(render(() => {
runtime.call('luci.dispatcher', 'invoke_form_action', runtime.call('luci.dispatcher', 'invoke_form_action',
action.path, action.path,
...resolved.ctx.request_args ...resolved.ctx.request_args

View file

@ -524,7 +524,7 @@ const Class = {
// If the content chunk is nil this function will automatically invoke close. // If the content chunk is nil this function will automatically invoke close.
write: function(content) { write: function(content) {
if (content != null) { if (content != null && !this.closed) {
this.write_headers(); this.write_headers();
this.output(content); this.output(content);

View file

@ -48,8 +48,10 @@ const Class = {
let bridge = this.env.dispatcher.load_luabridge(optional); let bridge = this.env.dispatcher.load_luabridge(optional);
if (bridge) { if (bridge) {
let http = this.env.http;
this.L = bridge.create(); this.L = bridge.create();
this.L.set('L', proto({ write: print }, this.env)); this.L.set('L', proto({ write: (...args) => http.closed || print(...args) }, this.env));
this.L.invoke('require', 'luci.ucodebridge'); this.L.invoke('require', 'luci.ucodebridge');
this.env.lua_active = true; this.env.lua_active = true;
@ -61,6 +63,10 @@ const Class = {
render_ucode: function(path, scope) { render_ucode: function(path, scope) {
let tmplfunc = loadfile(path, { raw_mode: false }); let tmplfunc = loadfile(path, { raw_mode: false });
if (this.env.http.closed)
render(call, tmplfunc, null, scope ?? {});
else
call(tmplfunc, null, scope ?? {}); call(tmplfunc, null, scope ?? {});
}, },