From 134fa58c46fedd882e297c15cb33159867e517ab Mon Sep 17 00:00:00 2001 From: spacewander Date: Mon, 16 Aug 2021 19:43:05 +0800 Subject: [PATCH 1/4] feat(ext-plugin): support ExtraInfo Signed-off-by: spacewander --- apisix/constants.lua | 1 + apisix/plugins/ext-plugin/init.lua | 64 +++++++++- rockspec/apisix-master-0.rockspec | 2 +- t/lib/ext-plugin.lua | 49 ++++++++ t/plugin/ext-plugin/extra-info.t | 183 +++++++++++++++++++++++++++++ 5 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 t/plugin/ext-plugin/extra-info.t diff --git a/apisix/constants.lua b/apisix/constants.lua index 8dac0cbabae8..f49e81d1cc9a 100644 --- a/apisix/constants.lua +++ b/apisix/constants.lua @@ -18,6 +18,7 @@ return { RPC_ERROR = 0, RPC_PREPARE_CONF = 1, RPC_HTTP_REQ_CALL = 2, + RPC_EXTRA_INFO = 3, HTTP_ETCD_DIRECTORY = { ["/upstreams"] = true, ["/plugins"] = true, diff --git a/apisix/plugins/ext-plugin/init.lua b/apisix/plugins/ext-plugin/init.lua index d4392a43b7d9..04680e04233d 100644 --- a/apisix/plugins/ext-plugin/init.lua +++ b/apisix/plugins/ext-plugin/init.lua @@ -24,6 +24,10 @@ local http_req_call_resp = require("A6.HTTPReqCall.Resp") local http_req_call_action = require("A6.HTTPReqCall.Action") local http_req_call_stop = require("A6.HTTPReqCall.Stop") local http_req_call_rewrite = require("A6.HTTPReqCall.Rewrite") +local extra_info = require("A6.ExtraInfo.Info") +local extra_info_req = require("A6.ExtraInfo.Req") +local extra_info_var = require("A6.ExtraInfo.Var") +local extra_info_resp = require("A6.ExtraInfo.Resp") local text_entry = require("A6.TextEntry") local err_resp = require("A6.Err.Resp") local err_code = require("A6.Err.Code") @@ -251,6 +255,41 @@ local function build_headers(var, builder, key, val) end +local function handle_extra_info(ctx, input) + -- exact request + local buf = flatbuffers.binaryArray.New(input) + local req = extra_info_req.GetRootAsReq(buf, 0) + + local res + local info_type = req:InfoType() + if info_type == extra_info.Var then + local info = req:Info() + local var_req = extra_info_var.New() + var_req:Init(info.bytes, info.pos) + + local var_name = var_req:Name() + res = ctx.var[var_name] + else + return nil, "unsupported info type: " .. info_type + end + + -- build response + builder:Clear() + + local packed_res + if res then + packed_res = builder:CreateByteVector(res) + end + extra_info_resp.Start(builder) + if packed_res then + extra_info_resp.AddResult(builder, packed_res) + end + local resp = extra_info_resp.End(builder) + builder:Finish(resp) + return builder:Output() +end + + local rpc_call local rpc_handlers = { nil, @@ -339,7 +378,7 @@ local rpc_handlers = { local path = builder:CreateString(uri) local bin_addr = var.binary_remote_addr - local src_ip = builder.CreateByteVector(builder, bin_addr) + local src_ip = builder:CreateByteVector(bin_addr) local args = core.request.get_uri_args(ctx) local textEntries = {} @@ -400,9 +439,26 @@ local rpc_handlers = { return nil, "failed to send RPC_HTTP_REQ_CALL: " .. err end - local ty, resp = receive(sock) - if ty == nil then - return nil, "failed to receive RPC_HTTP_REQ_CALL: " .. resp + local ty, resp + while true do + ty, resp = receive(sock) + if ty == nil then + return nil, "failed to receive RPC_HTTP_REQ_CALL: " .. resp + end + + if ty ~= constants.RPC_EXTRA_INFO then + break + end + + local out, err = handle_extra_info(ctx, resp) + if not out then + return nil, "failed to handle RPC_EXTRA_INFO: " .. err + end + + local ok, err = send(sock, constants.RPC_EXTRA_INFO, out) + if not ok then + return nil, "failed to reply RPC_EXTRA_INFO: " .. err + end end if ty ~= constants.RPC_HTTP_REQ_CALL then diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec index a171d94ccfd8..7dd1c937608f 100644 --- a/rockspec/apisix-master-0.rockspec +++ b/rockspec/apisix-master-0.rockspec @@ -66,7 +66,7 @@ dependencies = { "luasec = 0.9-1", "lua-resty-consul = 0.3-2", "penlight = 1.9.2-1", - "ext-plugin-proto = 0.2.1", + "ext-plugin-proto = 0.3.0", "casbin = 1.26.0", "api7-snowflake = 2.0-1", } diff --git a/t/lib/ext-plugin.lua b/t/lib/ext-plugin.lua index 38f379f1fb23..c005c80cd49c 100644 --- a/t/lib/ext-plugin.lua +++ b/t/lib/ext-plugin.lua @@ -29,12 +29,23 @@ local http_req_call_resp = require("A6.HTTPReqCall.Resp") local http_req_call_action = require("A6.HTTPReqCall.Action") local http_req_call_stop = require("A6.HTTPReqCall.Stop") local http_req_call_rewrite = require("A6.HTTPReqCall.Rewrite") +local extra_info = require("A6.ExtraInfo.Info") +local extra_info_req = require("A6.ExtraInfo.Req") +local extra_info_var = require("A6.ExtraInfo.Var") +local extra_info_resp = require("A6.ExtraInfo.Resp") local _M = {} local builder = flatbuffers.Builder(0) +local function build_extra_info(info, ty) + extra_info_req.Start(builder) + extra_info_req.AddInfoType(builder, ty) + extra_info_req.AddInfo(builder, info) +end + + local function build_action(action, ty) http_req_call_resp.Start(builder) http_req_call_resp.AddActionType(builder, ty) @@ -162,6 +173,44 @@ function _M.go(case) assert(call_req:Method() == a6_method.GET) end + if case.extra_info then + for _, action in ipairs(case.extra_info) do + if action.type == "closed" then + ngx.exit(-1) + return + end + + if action.type == "var" then + local name = builder:CreateString(action.name) + extra_info_var.Start(builder) + extra_info_var.AddName(builder, name) + local var_req = extra_info_var.End(builder) + build_extra_info(var_req, extra_info.Var) + local req = extra_info_req.End(builder) + builder:Finish(req) + data = builder:Output() + local ok, err = ext.send(sock, constants.RPC_EXTRA_INFO, data) + if not ok then + ngx.log(ngx.ERR, err) + return + end + ngx.log(ngx.WARN, "send extra info req successfully") + + local ty, data = ext.receive(sock) + if not ty then + ngx.log(ngx.ERR, data) + return + end + + assert(ty == constants.RPC_EXTRA_INFO, ty) + local buf = flatbuffers.binaryArray.New(data) + local resp = extra_info_resp.GetRootAsResp(buf, 0) + local res = resp:ResultAsString() + assert(res == action.result, res) + end + end + end + if case.stop == true then local len = 3 http_req_call_stop.StartBodyVector(builder, len) diff --git a/t/plugin/ext-plugin/extra-info.t b/t/plugin/ext-plugin/extra-info.t new file mode 100644 index 000000000000..56b67be0f89b --- /dev/null +++ b/t/plugin/ext-plugin/extra-info.t @@ -0,0 +1,183 @@ +# +# 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 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); + +add_block_preprocessor(sub { + my ($block) = @_; + + $block->set_value("stream_conf_enable", 1); + + if (!defined $block->extra_stream_config) { + my $stream_config = <<_EOC_; + server { + listen unix:\$TEST_NGINX_HTML_DIR/nginx.sock; + + content_by_lua_block { + local ext = require("lib.ext-plugin") + ext.go({}) + } + } + +_EOC_ + $block->set_value("extra_stream_config", $stream_config); + } + + my $unix_socket_path = $ENV{"TEST_NGINX_HTML_DIR"} . "/nginx.sock"; + my $cmd = $block->ext_plugin_cmd // "['sleep', '5s']"; + my $extra_yaml_config = <<_EOC_; +ext-plugin: + path_for_test: $unix_socket_path + cmd: $cmd +_EOC_ + + $block->set_value("extra_yaml_config", $extra_yaml_config); + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: add route +--- config + location /t { + content_by_lua_block { + local json = require("toolkit.json") + local t = require("lib.test_admin") + + local code, message, res = t.test('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "plugins": { + "ext-plugin-pre-req": { + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(message) + return + end + + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 2: var +--- request +GET /hello?x= +--- extra_stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + content_by_lua_block { + local ext = require("lib.ext-plugin") + local actions = { + {type = "var", name = "server_addr", result = "127.0.0.1"}, + {type = "var", name = "remote_addr", result = "127.0.0.1"}, + {type = "var", name = "route_id", result = "1"}, + {type = "var", name = "arg_x", result = ""}, + } + ext.go({extra_info = actions, stop = true}) + } + } +--- error_code: 405 +--- grep_error_log eval +qr/send extra info req successfully/ +--- grep_error_log_out +send extra info req successfully +send extra info req successfully +send extra info req successfully +send extra info req successfully + + + +=== TEST 3: ask nonexistent var +--- request +GET /hello +--- more_headers +X-Change: foo +X-Delete: foo +--- extra_stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + content_by_lua_block { + local ext = require("lib.ext-plugin") + local actions = { + {type = "var", name = "erver_addr"}, + } + ext.go({extra_info = actions, rewrite = true}) + } + } +--- response_body +uri: /uri +host: localhost +x-add: bar +x-change: bar +x-real-ip: 127.0.0.1 +--- grep_error_log eval +qr/send extra info req successfully/ +--- grep_error_log_out +send extra info req successfully + + + +=== TEST 4: network is down in the middle +--- request +GET /hello +--- extra_stream_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock; + + content_by_lua_block { + local ext = require("lib.ext-plugin") + local actions = { + {type = "var", name = "server_addr", result = "127.0.0.1"}, + {type = "closed"}, + } + ext.go({extra_info = actions, stop = true}) + } + } +--- error_code: 503 +--- error_log +failed to receive RPC_HTTP_REQ_CALL: closed From 643bea8aced566fc1754c81dbbb7409574683f34 Mon Sep 17 00:00:00 2001 From: spacewander Date: Tue, 17 Aug 2021 16:43:18 +0800 Subject: [PATCH 2/4] ensure to use str Signed-off-by: spacewander --- apisix/plugins/ext-plugin/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apisix/plugins/ext-plugin/init.lua b/apisix/plugins/ext-plugin/init.lua index 04680e04233d..8e659442a9ae 100644 --- a/apisix/plugins/ext-plugin/init.lua +++ b/apisix/plugins/ext-plugin/init.lua @@ -59,6 +59,7 @@ local str_sub = string.sub local error = error local ipairs = ipairs local pairs = pairs +local tostring = tostring local type = type @@ -268,7 +269,7 @@ local function handle_extra_info(ctx, input) var_req:Init(info.bytes, info.pos) local var_name = var_req:Name() - res = ctx.var[var_name] + res = tostring(ctx.var[var_name]) else return nil, "unsupported info type: " .. info_type end From 6582214e52a2b58f8ab031811e1a6cf8a95490cc Mon Sep 17 00:00:00 2001 From: spacewander Date: Tue, 17 Aug 2021 17:26:15 +0800 Subject: [PATCH 3/4] fix test Signed-off-by: spacewander --- apisix/plugins/ext-plugin/init.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apisix/plugins/ext-plugin/init.lua b/apisix/plugins/ext-plugin/init.lua index 8e659442a9ae..c1a97ab2c3e4 100644 --- a/apisix/plugins/ext-plugin/init.lua +++ b/apisix/plugins/ext-plugin/init.lua @@ -269,7 +269,7 @@ local function handle_extra_info(ctx, input) var_req:Init(info.bytes, info.pos) local var_name = var_req:Name() - res = tostring(ctx.var[var_name]) + res = ctx.var[var_name] else return nil, "unsupported info type: " .. info_type end @@ -279,6 +279,8 @@ local function handle_extra_info(ctx, input) local packed_res if res then + -- ensure to pass the res in string type + res = tostring(res) packed_res = builder:CreateByteVector(res) end extra_info_resp.Start(builder) From e2ce0daa08c0ec7235645704e2d05b7efd603064 Mon Sep 17 00:00:00 2001 From: spacewander Date: Tue, 17 Aug 2021 18:14:29 +0800 Subject: [PATCH 4/4] ws Signed-off-by: spacewander --- apisix/plugins/ext-plugin/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/plugins/ext-plugin/init.lua b/apisix/plugins/ext-plugin/init.lua index c1a97ab2c3e4..3dfedadcd057 100644 --- a/apisix/plugins/ext-plugin/init.lua +++ b/apisix/plugins/ext-plugin/init.lua @@ -279,7 +279,7 @@ local function handle_extra_info(ctx, input) local packed_res if res then - -- ensure to pass the res in string type + -- ensure to pass the res in string type res = tostring(res) packed_res = builder:CreateByteVector(res) end