luci-base: fix luci.http.close()
authorJo-Philipp Wich <jo@mein.io>
Wed, 2 Nov 2022 23:38:00 +0000 (00:38 +0100)
committerJo-Philipp Wich <jo@mein.io>
Wed, 2 Nov 2022 23:38:00 +0000 (00:38 +0100)
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>
modules/luci-base/ucode/dispatcher.uc
modules/luci-base/ucode/http.uc
modules/luci-base/ucode/runtime.uc

index 86cab9b88f6204997059e63485180cecdc4bf5ed..8ac32c3d1d364d92d078a1b69721bc79e7e039ce 100644 (file)
@@ -775,7 +775,8 @@ function run_action(request_path, lang, tree, resolved, action) {
                break;
 
        case 'call':
-               http.write(render(() => {
+               http.write_headers();
+               http.output(render(() => {
                        runtime.call(action.module, action.function,
                                ...(action.parameters ?? []),
                                ...resolved.ctx.request_args
@@ -789,7 +790,8 @@ function run_action(request_path, lang, tree, resolved, action) {
                assert(type(mod[action.function]) == '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,
                                ...(action.parameters ?? []),
                                ...resolved.ctx.request_args
@@ -798,7 +800,8 @@ function run_action(request_path, lang, tree, resolved, action) {
                break;
 
        case 'cbi':
-               http.write(render(() => {
+               http.write_headers();
+               http.output(render(() => {
                        runtime.call('luci.dispatcher', 'invoke_cbi_action',
                                action.path, null,
                                ...resolved.ctx.request_args
@@ -807,7 +810,8 @@ function run_action(request_path, lang, tree, resolved, action) {
                break;
 
        case 'form':
-               http.write(render(() => {
+               http.write_headers();
+               http.output(render(() => {
                        runtime.call('luci.dispatcher', 'invoke_form_action',
                                action.path,
                                ...resolved.ctx.request_args
index 4856c47a0d6fb68c275502559028e5bbd874a33d..e8982e77a0027227c9a9a32cc71a615c09903b14 100644 (file)
@@ -524,7 +524,7 @@ const Class = {
 
        // If the content chunk is nil this function will automatically invoke close.
        write: function(content) {
-               if (content != null) {
+               if (content != null && !this.closed) {
                        this.write_headers();
                        this.output(content);
 
index ed330abcc1bfce263d00bf895384734bdea43257..da73d13fe4a7a4cd018794cd8d01c34e2f6e82f1 100644 (file)
@@ -48,8 +48,10 @@ const Class = {
                        let bridge = this.env.dispatcher.load_luabridge(optional);
 
                        if (bridge) {
+                               let http = this.env.http;
+
                                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.env.lua_active = true;
@@ -61,7 +63,11 @@ const Class = {
 
        render_ucode: function(path, scope) {
                let tmplfunc = loadfile(path, { raw_mode: false });
-               call(tmplfunc, null, scope ?? {});
+
+               if (this.env.http.closed)
+                       render(call, tmplfunc, null, scope ?? {});
+               else
+                       call(tmplfunc, null, scope ?? {});
        },
 
        render_lua: function(path, scope) {