feat(apisix): add Cloudron package

- Implements Apache APISIX packaging for Cloudron platform.
- Includes Dockerfile, CloudronManifest.json, and start.sh.
- Configured to use Cloudron's etcd addon.

🤖 Generated with Gemini CLI
Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
2025-09-04 09:42:47 -05:00
parent f7bae09f22
commit 54cc5f7308
1608 changed files with 388342 additions and 0 deletions

View File

@@ -0,0 +1,280 @@
#
# 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->request) {
$block->set_value("request", "GET /t");
}
my $extra_yaml_config = <<_EOC_;
wasm:
plugins:
- name: wasm_fault_injection
priority: 7997
file: t/wasm/fault-injection/main.go.wasm
_EOC_
$block->set_value("extra_yaml_config", $extra_yaml_config);
});
run_tests();
__DATA__
=== TEST 1: fault injection
--- 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_fault_injection": {
"conf": "{\"http_status\":401, \"body\":\"HIT\n\"}"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: hit
--- request
GET /hello
--- error_code: 401
--- response_body
HIT
=== TEST 3: fault injection, with 0 percentage
--- 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_fault_injection": {
"conf": "{\"http_status\":401, \"percentage\":0}"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: hit
--- request
GET /hello
--- response_body
hello world
=== TEST 5: fault injection without 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_fault_injection": {
"conf": "{\"http_status\":401}"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 6: hit
--- request
GET /hello
--- error_code: 401
--- response_body_like eval
qr/<title>401 Authorization Required<\/title>/
=== TEST 7: fault injection
--- 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_fault_injection": {
"conf": {
"http_status": 401,
"body": "HIT\n"
}
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 8: hit
--- request
GET /hello
--- error_code: 401
--- response_body
HIT
=== TEST 9: fault injection, with 0 percentage
--- 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_fault_injection": {
"conf": {
"http_status": 401,
"percentage": 0
}
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- ret_code: 401
--- response_body
passed
=== TEST 10: hit
--- request
GET /hello
--- response_body
hello world

View File

@@ -0,0 +1,108 @@
/*
* 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 (
"math/rand"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
// tinygo doesn't support encoding/json, see https://github.com/tinygo-org/tinygo/issues/447
"github.com/valyala/fastjson"
)
func main() {
proxywasm.SetVMContext(&vmContext{})
}
type vmContext struct {
types.DefaultVMContext
}
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{}
}
type pluginContext struct {
types.DefaultPluginContext
Body []byte
HttpStatus uint32
Percentage int
}
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("error decoding plugin configuration: %v", err)
return types.OnPluginStartStatusFailed
}
ctx.Body = v.GetStringBytes("body")
ctx.HttpStatus = uint32(v.GetUint("http_status"))
if v.Exists("percentage") {
ctx.Percentage = v.GetInt("percentage")
} else {
ctx.Percentage = 100
}
// schema check
if ctx.HttpStatus < 200 {
proxywasm.LogError("bad http_status")
return types.OnPluginStartStatusFailed
}
if ctx.Percentage < 0 || ctx.Percentage > 100 {
proxywasm.LogError("bad percentage")
return types.OnPluginStartStatusFailed
}
return types.OnPluginStartStatusOK
}
func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
return &httpLifecycle{parent: ctx}
}
type httpLifecycle struct {
types.DefaultHttpContext
parent *pluginContext
}
func sampleHit(percentage int) bool {
return rand.Intn(100) < percentage
}
func (ctx *httpLifecycle) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
plugin := ctx.parent
if !sampleHit(plugin.Percentage) {
return types.ActionContinue
}
err := proxywasm.SendHttpResponse(plugin.HttpStatus, nil, plugin.Body, -1)
if err != nil {
proxywasm.LogErrorf("failed to send local response: %v", err)
return types.ActionContinue
}
return types.ActionPause
}

View File

@@ -0,0 +1,219 @@
/*
* 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 (
"net/url"
"strconv"
"strings"
"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{
contextID: contextID,
upstreamHeaders: map[string]struct{}{},
clientHeaders: map[string]struct{}{},
requestHeaders: map[string]struct{}{},
}
}
type pluginContext struct {
types.DefaultPluginContext
contextID uint32
host string
path string
scheme string
upstreamHeaders map[string]struct{}
clientHeaders map[string]struct{}
requestHeaders map[string]struct{}
timeout uint32
}
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("error decoding plugin configuration: %v", err)
return types.OnPluginStartStatusFailed
}
ctx.timeout = uint32(v.GetUint("timeout"))
if ctx.timeout == 0 {
ctx.timeout = 3000
}
// schema check
if ctx.timeout < 1 || ctx.timeout > 60000 {
proxywasm.LogError("bad timeout")
return types.OnPluginStartStatusFailed
}
s := string(v.GetStringBytes("uri"))
if s == "" {
proxywasm.LogError("bad uri")
return types.OnPluginStartStatusFailed
}
uri, err := url.Parse(s)
if err != nil {
proxywasm.LogErrorf("bad uri: %v", err)
return types.OnPluginStartStatusFailed
}
ctx.host = uri.Host
ctx.path = uri.Path
ctx.scheme = uri.Scheme
arr := v.GetArray("upstream_headers")
for _, a := range arr {
ctx.upstreamHeaders[strings.ToLower(string(a.GetStringBytes()))] = struct{}{}
}
arr = v.GetArray("request_headers")
for _, a := range arr {
ctx.requestHeaders[string(a.GetStringBytes())] = struct{}{}
}
arr = v.GetArray("client_headers")
for _, a := range arr {
ctx.clientHeaders[strings.ToLower(string(a.GetStringBytes()))] = struct{}{}
}
return types.OnPluginStartStatusOK
}
func (pluginCtx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
ctx := &httpContext{contextID: contextID, pluginCtx: pluginCtx}
return ctx
}
type httpContext struct {
types.DefaultHttpContext
contextID uint32
pluginCtx *pluginContext
}
func (ctx *httpContext) dispatchHttpCall(elem *fastjson.Value) {
method, _ := proxywasm.GetHttpRequestHeader(":method")
uri, _ := proxywasm.GetHttpRequestHeader(":path")
scheme, _ := proxywasm.GetHttpRequestHeader(":scheme")
host, _ := proxywasm.GetHttpRequestHeader("host")
addr, _ := proxywasm.GetProperty([]string{"remote_addr"})
pctx := ctx.pluginCtx
hs := [][2]string{}
hs = append(hs, [2]string{":scheme", pctx.scheme})
hs = append(hs, [2]string{"host", pctx.host})
hs = append(hs, [2]string{":path", pctx.path})
hs = append(hs, [2]string{"X-Forwarded-Proto", scheme})
hs = append(hs, [2]string{"X-Forwarded-Method", method})
hs = append(hs, [2]string{"X-Forwarded-Host", host})
hs = append(hs, [2]string{"X-Forwarded-Uri", uri})
hs = append(hs, [2]string{"X-Forwarded-For", string(addr)})
for k := range pctx.requestHeaders {
h, err := proxywasm.GetHttpRequestHeader(k)
if err != nil && err != types.ErrorStatusNotFound {
proxywasm.LogErrorf("httpcall failed: %v", err)
return
}
hs = append(hs, [2]string{k, h})
}
calloutID, err := proxywasm.DispatchHttpCall(pctx.host, hs, nil, nil,
pctx.timeout, ctx.httpCallback)
if err != nil {
proxywasm.LogErrorf("httpcall failed: %v", err)
return
}
proxywasm.LogInfof("httpcall calloutID %d, pluginCtxID %d", calloutID, ctx.pluginCtx.contextID)
}
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
data, err := proxywasm.GetPluginConfiguration()
if err != nil {
proxywasm.LogErrorf("error reading plugin configuration: %v", err)
return types.ActionContinue
}
var p fastjson.Parser
v, err := p.ParseBytes(data)
if err != nil {
proxywasm.LogErrorf("error decoding plugin configuration: %v", err)
return types.ActionContinue
}
ctx.dispatchHttpCall(v)
return types.ActionContinue
}
func (ctx *httpContext) httpCallback(numHeaders int, bodySize int, numTrailers int) {
hs, err := proxywasm.GetHttpCallResponseHeaders()
if err != nil {
proxywasm.LogErrorf("callback err: %v", err)
return
}
var status int
for _, h := range hs {
if h[0] == ":status" {
status, _ = strconv.Atoi(h[1])
}
if _, ok := ctx.pluginCtx.upstreamHeaders[h[0]]; ok {
err := proxywasm.ReplaceHttpRequestHeader(h[0], h[1])
if err != nil {
proxywasm.LogErrorf("set header failed: %v", err)
}
}
}
if status >= 300 {
chs := [][2]string{}
for _, h := range hs {
if _, ok := ctx.pluginCtx.clientHeaders[h[0]]; ok {
chs = append(chs, [2]string{h[0], h[1]})
}
}
if err := proxywasm.SendHttpResponse(403, chs, nil, -1); err != nil {
proxywasm.LogErrorf("send http failed: %v", err)
return
}
}
}

View File

@@ -0,0 +1,253 @@
#
# 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');
}
repeat_each(1);
no_long_string();
no_root_location();
add_block_preprocessor(sub {
my ($block) = @_;
if (!defined $block->request) {
$block->set_value("request", "GET /t");
}
my $extra_yaml_config = <<_EOC_;
wasm:
plugins:
- name: wasm-forward-auth
priority: 7997
file: t/wasm/forward-auth.go.wasm
_EOC_
$block->set_value("extra_yaml_config", $extra_yaml_config);
});
run_tests();
__DATA__
=== TEST 1: setup route with plugin
--- config
location /t {
content_by_lua_block {
local data = {
{
url = "/apisix/admin/upstreams/u1",
data = [[{
"nodes": {
"127.0.0.1:1984": 1
},
"type": "roundrobin"
}]],
},
{
url = "/apisix/admin/routes/auth",
data = [[{
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function(conf, ctx)
local core = require(\"apisix.core\");
if core.request.header(ctx, \"Authorization\") == \"111\" then
core.response.exit(200);
end
end",
"return function(conf, ctx)
local core = require(\"apisix.core\");
if core.request.header(ctx, \"Authorization\") == \"222\" then
core.response.set_header(\"X-User-ID\", \"i-am-an-user\");
core.response.exit(200);
end
end",]] .. [[
"return function(conf, ctx)
local core = require(\"apisix.core\");
if core.request.header(ctx, \"Authorization\") == \"333\" then
core.response.set_header(\"X-User-ID\", \"i-am-an-user\");
core.response.exit(401);
end
end",
"return function(conf, ctx)
local core = require(\"apisix.core\");
if core.request.header(ctx, \"Authorization\") == \"444\" then
local auth_headers = {
'X-Forwarded-Proto',
'X-Forwarded-Method',
'X-Forwarded-Host',
'X-Forwarded-Uri',
'X-Forwarded-For',
}
for _, k in ipairs(auth_headers) do
core.log.warn('get header ', string.lower(k), ': ', core.request.header(ctx, k))
end
core.response.exit(403);
end
end"
]
}
},
"uri": "/auth"
}]],
},
{
url = "/apisix/admin/routes/echo",
data = [[{
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function (conf, ctx)
local core = require(\"apisix.core\");
core.response.exit(200, core.request.headers(ctx));
end"
]
}
},
"uri": "/echo"
}]],
},
{
url = "/apisix/admin/routes/1",
data = [[{
"plugins": {
"wasm-forward-auth": {
"conf": "{
\"uri\": \"http://127.0.0.1:1984/auth\",
\"request_headers\": [\"Authorization\"],
\"client_headers\": [\"X-User-ID\"],
\"upstream_headers\": [\"X-User-ID\"]
}"
},
"proxy-rewrite": {
"uri": "/echo"
}
},
"upstream_id": "u1",
"uri": "/hello"
}]],
},
{
url = "/apisix/admin/routes/2",
data = [[{
"plugins": {
"wasm-forward-auth": {
"conf": "{
\"uri\": \"http://127.0.0.1:1984/auth\",
\"request_headers\": [\"Authorization\"]
}"
},
"proxy-rewrite": {
"uri": "/echo"
}
},
"upstream_id": "u1",
"uri": "/empty"
}]],
},
}
local t = require("lib.test_admin").test
for _, data in ipairs(data) do
local code, body = t(data.url, ngx.HTTP_PUT, data.data)
ngx.say(body)
end
}
}
--- response_body eval
"passed\n" x 5
=== TEST 2: hit route (test request_headers)
--- request
GET /hello
--- more_headers
Authorization: 111
--- response_body_like eval
qr/\"authorization\":\"111\"/
=== TEST 3: hit route (test upstream_headers)
--- request
GET /hello
--- more_headers
Authorization: 222
--- response_body_like eval
qr/\"x-user-id\":\"i-am-an-user\"/
=== TEST 4: hit route (test client_headers)
--- request
GET /hello
--- more_headers
Authorization: 333
--- error_code: 403
--- response_headers
x-user-id: i-am-an-user
=== TEST 5: hit route (check APISIX generated headers and ignore client headers)
--- request
GET /hello
--- more_headers
Authorization: 444
X-Forwarded-Host: apisix.apache.org
--- error_code: 403
--- grep_error_log eval
qr/get header \S+: \S+/
--- grep_error_log_out
get header x-forwarded-proto: http,
get header x-forwarded-method: GET,
get header x-forwarded-host: localhost,
get header x-forwarded-uri: /hello,
get header x-forwarded-for: 127.0.0.1,
=== TEST 6: hit route (not send upstream headers)
--- request
GET /empty
--- more_headers
Authorization: 222
--- response_body_unlike eval
qr/\"x-user-id\":\"i-am-an-user\"/
=== TEST 7: hit route (not send client headers)
--- request
GET /empty
--- more_headers
Authorization: 333
--- error_code: 403
--- response_headers
!x-user-id

View File

@@ -0,0 +1,175 @@
#
# 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->request) {
$block->set_value("request", "GET /t");
}
my $extra_yaml_config = <<_EOC_;
wasm:
plugins:
- name: wasm_log
priority: 7999
file: t/wasm/log/main.go.wasm
- name: wasm_log2
priority: 7998
file: t/wasm/log/main.go.wasm
_EOC_
$block->set_value("extra_yaml_config", $extra_yaml_config);
});
run_tests();
__DATA__
=== TEST 1: sanity
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/global_rules/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"wasm_log": {
"conf": "blahblah"
},
"wasm_log2": {
"conf": "zzz"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: hit
--- request
GET /hello
--- grep_error_log eval
qr/run plugin ctx \d+ with conf \S+ in http ctx \d+/
--- grep_error_log_out
run plugin ctx 1 with conf blahblah in http ctx 2
run plugin ctx 1 with conf zzz in http ctx 2
=== TEST 3: global rule + route
--- 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,
[[{
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"plugins": {
"wasm_log2": {
"conf": "www"
}
},
"uri": "/hello"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 4: hit
--- request
GET /hello
--- grep_error_log eval
qr/run plugin ctx \d+ with conf \S+ in http ctx \d+/
--- grep_error_log_out
run plugin ctx 1 with conf blahblah in http ctx 2
run plugin ctx 1 with conf zzz in http ctx 2
run plugin ctx 3 with conf www in http ctx 4
=== TEST 5: delete global rule
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/global_rules/1', ngx.HTTP_DELETE)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- response_body
passed

View File

@@ -0,0 +1,10 @@
module github.com/api7/wasm-nginx-module
go 1.17
require (
github.com/tetratelabs/proxy-wasm-go-sdk v0.16.0
github.com/valyala/fastjson v1.6.3
)
//replace github.com/tetratelabs/proxy-wasm-go-sdk => ../proxy-wasm-go-sdk

View File

@@ -0,0 +1,16 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tetratelabs/proxy-wasm-go-sdk v0.16.0 h1:6xhDLV4DD2+q3Rs4CDh7cqo69rQ50XgCusv/58D44o4=
github.com/tetratelabs/proxy-wasm-go-sdk v0.16.0/go.mod h1:8CxNZJ+9yDEvNnAog384fC8j1tKNF0tTZevGjOuY9ds=
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,81 @@
/*
* 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"
)
func main() {
proxywasm.SetVMContext(&vmContext{})
}
type vmContext struct {
// Embed the default VM context here,
// so that we don't need to reimplement all the methods.
types.DefaultVMContext
}
func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
return &pluginContext{contextID: contextID}
}
type pluginContext struct {
// Embed the default plugin context here,
// so that we don't need to reimplement all the methods.
types.DefaultPluginContext
conf string
contextID uint32
}
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
data, err := proxywasm.GetPluginConfiguration()
if err != nil {
proxywasm.LogCriticalf("error reading plugin configuration: %v", err)
return types.OnPluginStartStatusFailed
}
ctx.conf = string(data)
return types.OnPluginStartStatusOK
}
func (ctx *pluginContext) OnPluginDone() bool {
proxywasm.LogInfo("do clean up...")
return true
}
func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
return &httpLifecycle{pluginCtxID: ctx.contextID, conf: ctx.conf, contextID: contextID}
}
type httpLifecycle struct {
// Embed the default http context here,
// so that we don't need to reimplement all the methods.
types.DefaultHttpContext
pluginCtxID uint32
contextID uint32
conf string
}
func (ctx *httpLifecycle) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
proxywasm.LogWarnf("run plugin ctx %d with conf %s in http ctx %d",
ctx.pluginCtxID, ctx.conf, ctx.contextID)
// TODO: support access/modify http request headers
return types.ActionContinue
}

View File

@@ -0,0 +1,251 @@
#
# 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->request) {
$block->set_value("request", "GET /t");
}
my $extra_yaml_config = <<_EOC_;
wasm:
plugins:
- name: wasm-request-body
priority: 7997
file: t/wasm/request-body/main.go.wasm
- name: wasm-request-body2
priority: 7996
file: t/wasm/request-body/main.go.wasm
_EOC_
$block->set_value("extra_yaml_config", $extra_yaml_config);
});
run_tests();
__DATA__
=== TEST 1: sanity
--- 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-request-body": {
"conf": "{\"processReqBody\":true, \"start\":1, \"size\":3}"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 2: hit
--- request
POST /hello
hello
--- grep_error_log eval
qr/request get body: \w+/
--- grep_error_log_out
request get body: ell
=== TEST 3: no body
--- request
POST /hello
--- error_log
error status returned by host: not found
=== TEST 4: do not process 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-request-body": {
"conf": "{\"processReqBody\":false, \"start\":1, \"size\":3}"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 5: hit
--- request
POST /hello
hello
--- grep_error_log eval
qr/request get body: \w+/
--- grep_error_log_out
=== TEST 6: 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-request-body": {
"conf": "{\"processReqBody\":true, \"start\":1, \"size\":3}"
},
"wasm-request-body2": {
"conf": "{\"processReqBody\":false, \"start\":2, \"size\":3}"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 7: hit
--- request
POST /hello
hello
--- grep_error_log eval
qr/request get body: \w+/
--- grep_error_log_out
request get body: ell
=== TEST 8: invalid conf type no set conf
--- 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-request-body": {
"setting": {"processReqBody":true, "start":1, "size":3}
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- error_code: 400
--- response_body_like eval
qr/property.*conf.*is required/
=== TEST 9: hit
--- request
POST /hello
hello
--- grep_error_log eval
qr/request get body: \w+/
--- grep_error_log_out
request get body: ell

View File

@@ -0,0 +1,99 @@
/*
* 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{contextID: contextID}
}
type pluginContext struct {
types.DefaultPluginContext
contextID uint32
start int
size int
processReqBody bool
}
func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
data, err := proxywasm.GetPluginConfiguration()
if err != nil {
proxywasm.LogCriticalf("error reading plugin configuration: %v", err)
return types.OnPluginStartStatusFailed
}
var conf *fastjson.Value
var p fastjson.Parser
conf, err = p.ParseBytes(data)
if err != nil {
proxywasm.LogErrorf("error decoding plugin configuration: %v", err)
return types.OnPluginStartStatusFailed
}
ctx.start = conf.GetInt("start")
ctx.size = conf.GetInt("size")
ctx.processReqBody = conf.GetBool("processReqBody")
return types.OnPluginStartStatusOK
}
func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
return &httpContext{pluginCtx: ctx, contextID: contextID}
}
type httpContext struct {
types.DefaultHttpContext
pluginCtx *pluginContext
contextID uint32
}
func (ctx *httpContext) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
if ctx.pluginCtx.processReqBody {
proxywasm.SetProperty([]string{"wasm_process_req_body"}, []byte("true"))
}
return types.ActionContinue
}
func (ctx *httpContext) OnHttpRequestBody(bodySize int, endOfStream bool) types.Action {
size := ctx.pluginCtx.size
if size == 0 {
size = bodySize
}
body, err := proxywasm.GetHttpRequestBody(ctx.pluginCtx.start, size)
if err != nil {
proxywasm.LogErrorf("failed to get body: %v", err)
return types.ActionContinue
}
proxywasm.LogWarnf("request get body: %v", string(body))
return types.ActionContinue
}

View File

@@ -0,0 +1,188 @@
#
# 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->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
- name: wasm-response-rewrite2
priority: 7996
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
=== 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

View File

@@ -0,0 +1,114 @@
/*
* 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
Body []byte
}
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("error 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")),
}
}
body := v.GetStringBytes("body")
ctx.Body = body
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)
}
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
}

View File

@@ -0,0 +1,464 @@
#
# 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->request) {
$block->set_value("request", "GET /t");
}
if (!defined $block->extra_yaml_config) {
my $extra_yaml_config = <<_EOC_;
wasm:
plugins:
- name: wasm_log
priority: 7999
file: t/wasm/log/main.go.wasm
- name: wasm_log2
priority: 7998
file: t/wasm/log/main.go.wasm
_EOC_
$block->set_value("extra_yaml_config", $extra_yaml_config);
}
});
run_tests();
__DATA__
=== TEST 1: scheme check with empty json 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_log": {}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- error_code: 400
--- error_log eval
qr/invalid request body/
=== TEST 2: scheme check with conf type number
--- 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_log": {"conf": 123}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- error_code: 400
--- error_log eval
qr/invalid request body/
=== TEST 3: scheme check with conf json type
--- 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_log": {"conf": {}}}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- error_code: 400
--- response_body_like eval
qr/value should match only one schema, but matches none/
=== TEST 4: scheme check with conf json type
--- 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_log": {"conf": ""}}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- error_code: 400
--- response_body_like eval
qr/value should match only one schema, but matches none/
=== TEST 5: sanity
--- 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_log": {
"conf": "blahblah"
},
"wasm_log2": {
"conf": "zzz"
}
}
}]]
)
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/run plugin ctx \d+ with conf \S+ in http ctx \d+/
--- grep_error_log_out
run plugin ctx 1 with conf blahblah in http ctx 2
run plugin ctx 1 with conf zzz in http ctx 2
=== TEST 7: run wasm plugin in rewrite phase (prior to the one run in access phase)
--- extra_yaml_config
wasm:
plugins:
- name: wasm_log
priority: 7999
file: t/wasm/log/main.go.wasm
- name: wasm_log2
priority: 7998
file: t/wasm/log/main.go.wasm
http_request_phase: rewrite
--- request
GET /hello
--- grep_error_log eval
qr/run plugin ctx \d+ with conf \S+ in http ctx \d+/
--- grep_error_log_out
run plugin ctx 1 with conf zzz in http ctx 2
run plugin ctx 1 with conf blahblah in http ctx 2
=== TEST 8: plugin from service
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/services/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"plugins": {
"wasm_log": {
"id": "log",
"conf": "blahblah"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"service_id": "1",
"hosts": ["foo.com"]
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
local code, body = t('/apisix/admin/routes/2',
ngx.HTTP_PUT,
[[{
"uri": "/hello",
"service_id": "1",
"hosts": ["bar.com"]
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 9: hit
--- config
location /t {
content_by_lua_block {
local http = require "resty.http"
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello"
for i = 1, 4 do
local host = "foo.com"
if i % 2 == 0 then
host = "bar.com"
end
local httpc = http.new()
local res, err = httpc:request_uri(uri, {headers = {host = host}})
if not res then
ngx.say(err)
return
end
end
}
}
--- grep_error_log eval
qr/run plugin ctx \d+ with conf \S+ in http ctx \d+/
--- grep_error_log_out
run plugin ctx 1 with conf blahblah in http ctx 2
run plugin ctx 3 with conf blahblah in http ctx 4
run plugin ctx 1 with conf blahblah in http ctx 2
run plugin ctx 3 with conf blahblah in http ctx 4
=== TEST 10: plugin from plugin_config
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/plugin_configs/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"wasm_log": {
"id": "log",
"conf": "blahblah"
}
}
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"uri": "/hello",
"plugin_config_id": "1",
"hosts": ["foo.com"]
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
local code, body = t('/apisix/admin/routes/2',
ngx.HTTP_PUT,
[[{
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
},
"uri": "/hello",
"plugin_config_id": "1",
"hosts": ["bar.com"]
}]]
)
if code >= 300 then
ngx.status = code
ngx.say(body)
return
end
ngx.say(body)
}
}
--- response_body
passed
=== TEST 11: hit
--- config
location /t {
content_by_lua_block {
local http = require "resty.http"
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello"
for i = 1, 4 do
local host = "foo.com"
if i % 2 == 0 then
host = "bar.com"
end
local httpc = http.new()
local res, err = httpc:request_uri(uri, {headers = {host = host}})
if not res then
ngx.say(err)
return
end
end
}
}
--- grep_error_log eval
qr/run plugin ctx \d+ with conf \S+ in http ctx \d+/
--- grep_error_log_out
run plugin ctx 1 with conf blahblah in http ctx 2
run plugin ctx 3 with conf blahblah in http ctx 4
run plugin ctx 1 with conf blahblah in http ctx 2
run plugin ctx 3 with conf blahblah in http ctx 4