You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/03/07 02:13:08 UTC

[apisix] branch master updated: feat(Wasm): get response body (#6514)

This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new fede22e  feat(Wasm): get response body (#6514)
fede22e is described below

commit fede22edb50b70a54f59f6ca244bc48c46ca93de
Author: 罗泽轩 <sp...@gmail.com>
AuthorDate: Mon Mar 7 10:13:05 2022 +0800

    feat(Wasm): get response body (#6514)
---
 apisix/cli/ngx_tpl.lua          |  1 +
 apisix/wasm.lua                 | 35 +++++++++++++++
 docs/en/latest/wasm.md          |  1 +
 t/APISIX.pm                     |  1 +
 t/wasm/response-rewrite.t       | 98 +++++++++++++++++++++++++++++++++++++++++
 t/wasm/response-rewrite/main.go | 25 +++++++++++
 6 files changed, 161 insertions(+)

diff --git a/apisix/cli/ngx_tpl.lua b/apisix/cli/ngx_tpl.lua
index 186884f..aaac144 100644
--- a/apisix/cli/ngx_tpl.lua
+++ b/apisix/cli/ngx_tpl.lua
@@ -621,6 +621,7 @@ http {
 
             {% if wasm then %}
             set $wasm_process_req_body       '';
+            set $wasm_process_resp_body      '';
             {% end %}
 
             # http server location configuration snippet starts
diff --git a/apisix/wasm.lua b/apisix/wasm.lua
index d406089..56a44b8 100644
--- a/apisix/wasm.lua
+++ b/apisix/wasm.lua
@@ -112,6 +112,37 @@ local function header_filter_wrapper(self, conf, ctx)
         core.log.error(name, ": failed to run wasm plugin: ", err)
         return 503
     end
+
+    -- $wasm_process_resp_body is predefined in ngx_tpl.lua
+    local handle_body = ngx_var.wasm_process_resp_body
+    if handle_body ~= '' then
+        -- reset the flag so we can use it for the next Wasm plugin
+        -- use ngx.var to bypass the cache
+        ngx_var.wasm_process_resp_body = ""
+        ctx["wasm_" .. name .. "_process_resp_body"] = true
+    end
+end
+
+
+local function body_filter_wrapper(self, conf, ctx)
+    local name = self.name
+
+    local enabled = ctx["wasm_" .. name .. "_process_resp_body"]
+    if not enabled then
+        return
+    end
+
+    local plugin_ctx, err = fetch_plugin_ctx(conf, ctx, self.plugin)
+    if not plugin_ctx then
+        core.log.error(name, ": failed to fetch wasm plugin ctx: ", err)
+        return
+    end
+
+    local ok, err = wasm.on_http_response_body(plugin_ctx)
+    if not ok then
+        core.log.error(name, ": failed to run wasm plugin: ", err)
+        return
+    end
 end
 
 
@@ -151,6 +182,10 @@ function _M.require(attrs)
         return header_filter_wrapper(mod, conf, ctx)
     end
 
+    mod.body_filter = function (conf, ctx)
+        return body_filter_wrapper(mod, conf, ctx)
+    end
+
     -- the returned values need to be the same as the Lua's 'require'
     return true, mod
 end
diff --git a/docs/en/latest/wasm.md b/docs/en/latest/wasm.md
index 52089dd..f0579c5 100644
--- a/docs/en/latest/wasm.md
+++ b/docs/en/latest/wasm.md
@@ -103,6 +103,7 @@ For example, when the first request hits the route which has Wasm plugin configu
 * `proxy_on_http_request_headers`: run in the access/rewrite phase, depends on the configuration of `http_request_phase`.
 * `proxy_on_http_request_body`: run in the same phase of `proxy_on_http_request_headers`. To run this callback, we need to set property `wasm_process_req_body` to non-empty value in `proxy_on_http_request_headers`. See `t/wasm/request-body/main.go` as an example.
 * `proxy_on_http_response_headers`: run in the header_filter phase.
+* `proxy_on_http_response_body`: run in the body_filter phase. To run this callback, we need to set property `wasm_process_resp_body` to non-empty value in `proxy_on_http_response_headers`. See `t/wasm/response-rewrite/main.go` as an example.
 
 ## Example
 
diff --git a/t/APISIX.pm b/t/APISIX.pm
index bcd8daf..671ffe3 100644
--- a/t/APISIX.pm
+++ b/t/APISIX.pm
@@ -222,6 +222,7 @@ my $a6_ngx_vars = "";
 if ($version =~ m/\/apisix-nginx-module/) {
     $a6_ngx_vars = <<_EOC_;
     set \$wasm_process_req_body       '';
+    set \$wasm_process_resp_body      '';
 _EOC_
 }
 
diff --git a/t/wasm/response-rewrite.t b/t/wasm/response-rewrite.t
index 250a07c..15b9bd1 100644
--- a/t/wasm/response-rewrite.t
+++ b/t/wasm/response-rewrite.t
@@ -42,6 +42,9 @@ wasm:
         - name: wasm-response-rewrite
           priority: 7997
           file: t/wasm/response-rewrite/main.go.wasm
+        - name: wasm-response-rewrite2
+          priority: 7996
+          file: t/wasm/response-rewrite/main.go.wasm
 _EOC_
     $block->set_value("extra_yaml_config", $extra_yaml_config);
 });
@@ -92,3 +95,98 @@ passed
 GET /hello
 --- response_headers
 x-wasm: apisix
+
+
+
+=== TEST 3: log response body
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "wasm-response-rewrite": {
+                            "conf": "{\"body\":\"a\"}"
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 4: hit
+--- request
+GET /hello
+--- grep_error_log eval
+qr/get body .+/
+--- grep_error_log_out
+get body [hello world
+
+
+
+=== TEST 5: ensure the process body flag is plugin independent
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "wasm-response-rewrite": {
+                            "conf": "{\"body\":\"a\"}"
+                        },
+                        "wasm-response-rewrite2": {
+                            "conf": "{\"headers\":[{\"name\":\"x-wasm\",\"value\":\"apisix\"}]}"
+                        }
+                    }
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 6: hit
+--- request
+GET /hello
+--- grep_error_log eval
+qr/get body .+/
+--- grep_error_log_out
+get body [hello world
diff --git a/t/wasm/response-rewrite/main.go b/t/wasm/response-rewrite/main.go
index 41fa7b1..fb7b184 100644
--- a/t/wasm/response-rewrite/main.go
+++ b/t/wasm/response-rewrite/main.go
@@ -44,6 +44,7 @@ type header struct {
 type pluginContext struct {
 	types.DefaultPluginContext
 	Headers []header
+	Body    []byte
 }
 
 func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
@@ -68,6 +69,9 @@ func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPlu
 		}
 	}
 
+	body := v.GetStringBytes("body")
+	ctx.Body = body
+
 	return types.OnPluginStartStatusOK
 }
 
@@ -85,5 +89,26 @@ func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool)
 	for _, hdr := range plugin.Headers {
 		proxywasm.ReplaceHttpResponseHeader(hdr.Name, hdr.Value)
 	}
+
+	if len(plugin.Body) > 0 {
+		proxywasm.SetProperty([]string{"wasm_process_resp_body"}, []byte("true"))
+	}
+
+	return types.ActionContinue
+}
+
+func (ctx *httpContext) OnHttpResponseBody(bodySize int, endOfStream bool) types.Action {
+	plugin := ctx.parent
+
+	if len(plugin.Body) > 0 && !endOfStream {
+		// TODO support changing body
+		body, err := proxywasm.GetHttpResponseBody(0, bodySize)
+		if err != nil {
+			proxywasm.LogErrorf("failed to get body: %v", err)
+			return types.ActionContinue
+		}
+		proxywasm.LogWarnf("get body [%s]", string(body))
+	}
+
 	return types.ActionContinue
 }