mirror of
https://gitee.com/iresty/apisix.git
synced 2024-12-03 20:47:35 +08:00
feat(wasm): run in http header_filter (#5544)
This commit is contained in:
parent
b9ecafab5f
commit
fc5d70ac68
@ -65,7 +65,7 @@ end
|
||||
local function access_wrapper(self, conf, ctx)
|
||||
local plugin_ctx, err = fetch_plugin_ctx(conf, ctx, self.plugin)
|
||||
if not plugin_ctx then
|
||||
core.log.error("failed to init wasm plugin ctx: ", err)
|
||||
core.log.error("failed to fetch wasm plugin ctx: ", err)
|
||||
return 503
|
||||
end
|
||||
|
||||
@ -77,6 +77,21 @@ local function access_wrapper(self, conf, ctx)
|
||||
end
|
||||
|
||||
|
||||
local function header_filter_wrapper(self, conf, ctx)
|
||||
local plugin_ctx, err = fetch_plugin_ctx(conf, ctx, self.plugin)
|
||||
if not plugin_ctx then
|
||||
core.log.error("failed to fetch wasm plugin ctx: ", err)
|
||||
return 503
|
||||
end
|
||||
|
||||
local ok, err = wasm.on_http_response_headers(plugin_ctx)
|
||||
if not ok then
|
||||
core.log.error("failed to run wasm plugin: ", err)
|
||||
return 503
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _M.require(attrs)
|
||||
if not support_wasm then
|
||||
return nil, "need to build APISIX-OpenResty to support wasm"
|
||||
@ -101,6 +116,9 @@ function _M.require(attrs)
|
||||
mod.access = function (conf, ctx)
|
||||
return access_wrapper(mod, conf, ctx)
|
||||
end
|
||||
mod.header_filter = function (conf, ctx)
|
||||
return header_filter_wrapper(mod, conf, ctx)
|
||||
end
|
||||
|
||||
-- the returned values need to be the same as the Lua's 'require'
|
||||
return true, mod
|
||||
|
@ -93,4 +93,11 @@ Attributes below can be configured in the plugin:
|
||||
|
||||
| Name | Type | Requirement | Default | Valid | Description |
|
||||
| --------------------------------------| ------------| -------------- | -------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| conf | string | required | | != "" | the plugin ctx configuration which can be fetched via Proxy WASM SDK |
|
||||
| conf | string | required | | != "" | the plugin ctx configuration which can be fetched via Proxy WASM SDK |
|
||||
|
||||
Here is the mapping between Proxy WASM callbacks and APISIX's phases:
|
||||
|
||||
* `proxy_on_configure`: run once there is not PluginContext for the new configuration.
|
||||
For example, when the first request hits the route which has WASM plugin configured.
|
||||
* `proxy_on_http_request_headers`: run in the access phase.
|
||||
* `proxy_on_http_response_headers`: run in the header_filter phase.
|
||||
|
94
t/wasm/response-rewrite.t
vendored
Normal file
94
t/wasm/response-rewrite.t
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
use t::APISIX;
|
||||
|
||||
my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
|
||||
my $version = eval { `$nginx_binary -V 2>&1` };
|
||||
|
||||
if ($version !~ m/\/apisix-nginx-module/) {
|
||||
plan(skip_all => "apisix-nginx-module not installed");
|
||||
} else {
|
||||
plan('no_plan');
|
||||
}
|
||||
|
||||
add_block_preprocessor(sub {
|
||||
my ($block) = @_;
|
||||
|
||||
if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
|
||||
$block->set_value("no_error_log", "[error]");
|
||||
}
|
||||
|
||||
if (!defined $block->request) {
|
||||
$block->set_value("request", "GET /t");
|
||||
}
|
||||
|
||||
my $extra_yaml_config = <<_EOC_;
|
||||
wasm:
|
||||
plugins:
|
||||
- name: wasm-response-rewrite
|
||||
priority: 7997
|
||||
file: t/wasm/response-rewrite/main.go.wasm
|
||||
_EOC_
|
||||
$block->set_value("extra_yaml_config", $extra_yaml_config);
|
||||
});
|
||||
|
||||
run_tests();
|
||||
|
||||
__DATA__
|
||||
|
||||
=== TEST 1: response rewrite headers
|
||||
--- 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": "{\"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 2: hit
|
||||
--- request
|
||||
GET /hello
|
||||
--- response_headers
|
||||
x-wasm: apisix
|
89
t/wasm/response-rewrite/main.go
Normal file
89
t/wasm/response-rewrite/main.go
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
|
||||
|
||||
"github.com/valyala/fastjson"
|
||||
)
|
||||
|
||||
func main() {
|
||||
proxywasm.SetVMContext(&vmContext{})
|
||||
}
|
||||
|
||||
type vmContext struct {
|
||||
types.DefaultVMContext
|
||||
}
|
||||
|
||||
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
|
||||
return &pluginContext{}
|
||||
}
|
||||
|
||||
type header struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
type pluginContext struct {
|
||||
types.DefaultPluginContext
|
||||
Headers []header
|
||||
}
|
||||
|
||||
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
|
||||
data, err := proxywasm.GetPluginConfiguration()
|
||||
if err != nil {
|
||||
proxywasm.LogErrorf("error reading plugin configuration: %v", err)
|
||||
return types.OnPluginStartStatusFailed
|
||||
}
|
||||
|
||||
var p fastjson.Parser
|
||||
v, err := p.ParseBytes(data)
|
||||
if err != nil {
|
||||
proxywasm.LogErrorf("erorr decoding plugin configuration: %v", err)
|
||||
return types.OnPluginStartStatusFailed
|
||||
}
|
||||
headers := v.GetArray("headers")
|
||||
ctx.Headers = make([]header, len(headers))
|
||||
for i, hdr := range headers {
|
||||
ctx.Headers[i] = header{
|
||||
Name: string(hdr.GetStringBytes("name")),
|
||||
Value: string(hdr.GetStringBytes("value")),
|
||||
}
|
||||
}
|
||||
|
||||
return types.OnPluginStartStatusOK
|
||||
}
|
||||
|
||||
func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
|
||||
return &httpContext{parent: ctx}
|
||||
}
|
||||
|
||||
type httpContext struct {
|
||||
types.DefaultHttpContext
|
||||
parent *pluginContext
|
||||
}
|
||||
|
||||
func (ctx *httpContext) OnHttpResponseHeaders(numHeaders int, endOfStream bool) types.Action {
|
||||
plugin := ctx.parent
|
||||
for _, hdr := range plugin.Headers {
|
||||
proxywasm.ReplaceHttpResponseHeader(hdr.Name, hdr.Value)
|
||||
}
|
||||
return types.ActionContinue
|
||||
}
|
Loading…
Reference in New Issue
Block a user