2021-04-29 11:07:05 +08:00
|
|
|
--
|
|
|
|
-- 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.
|
|
|
|
--
|
2021-05-06 10:07:56 +08:00
|
|
|
local json = require("toolkit.json")
|
2021-04-29 11:07:05 +08:00
|
|
|
local ext = require("apisix.plugins.ext-plugin.init")
|
2021-05-04 10:41:08 +08:00
|
|
|
local constants = require("apisix.constants")
|
|
|
|
local flatbuffers = require("flatbuffers")
|
2021-05-06 10:00:12 +08:00
|
|
|
local err_code = require("A6.Err.Code")
|
|
|
|
local err_resp = require("A6.Err.Resp")
|
2021-05-04 10:41:08 +08:00
|
|
|
local prepare_conf_req = require("A6.PrepareConf.Req")
|
|
|
|
local prepare_conf_resp = require("A6.PrepareConf.Resp")
|
2021-05-06 10:07:56 +08:00
|
|
|
local a6_method = require("A6.Method")
|
|
|
|
local text_entry = require("A6.TextEntry")
|
|
|
|
local http_req_call_req = require("A6.HTTPReqCall.Req")
|
|
|
|
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")
|
2021-04-29 11:07:05 +08:00
|
|
|
|
|
|
|
|
|
|
|
local _M = {}
|
2021-05-04 10:41:08 +08:00
|
|
|
local builder = flatbuffers.Builder(0)
|
2021-04-29 11:07:05 +08:00
|
|
|
|
|
|
|
|
2021-05-06 10:07:56 +08:00
|
|
|
local function build_action(action, ty)
|
|
|
|
http_req_call_resp.Start(builder)
|
|
|
|
http_req_call_resp.AddActionType(builder, ty)
|
|
|
|
http_req_call_resp.AddAction(builder, action)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2021-05-04 10:41:08 +08:00
|
|
|
function _M.go(case)
|
2021-04-29 11:07:05 +08:00
|
|
|
local sock = ngx.req.socket()
|
|
|
|
local ty, data = ext.receive(sock)
|
|
|
|
if not ty then
|
|
|
|
ngx.log(ngx.ERR, data)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
ngx.log(ngx.WARN, "receive rpc call successfully")
|
|
|
|
|
2021-05-04 10:41:08 +08:00
|
|
|
if ty == constants.RPC_PREPARE_CONF then
|
2021-05-06 10:00:12 +08:00
|
|
|
if case.inject_error then
|
|
|
|
ty = constants.RPC_ERROR
|
|
|
|
err_resp.Start(builder)
|
|
|
|
err_resp.AddCode(builder, err_code.BAD_REQUEST)
|
|
|
|
local req = prepare_conf_req.End(builder)
|
|
|
|
builder:Finish(req)
|
|
|
|
data = builder:Output()
|
|
|
|
|
2021-05-04 10:41:08 +08:00
|
|
|
else
|
2021-05-06 10:00:12 +08:00
|
|
|
local buf = flatbuffers.binaryArray.New(data)
|
|
|
|
local pc = prepare_conf_req.GetRootAsReq(buf, 0)
|
|
|
|
|
|
|
|
if case.with_conf then
|
|
|
|
local conf = pc:Conf(1)
|
|
|
|
assert(conf:Name(), "foo")
|
|
|
|
assert(conf:Value(), "bar")
|
|
|
|
local conf = pc:Conf(2)
|
|
|
|
assert(conf:Name(), "cat")
|
|
|
|
assert(conf:Value(), "dog")
|
|
|
|
else
|
|
|
|
assert(pc:ConfLength() == 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
prepare_conf_resp.Start(builder)
|
|
|
|
prepare_conf_resp.AddConfToken(builder, 233)
|
|
|
|
local req = prepare_conf_req.End(builder)
|
|
|
|
builder:Finish(req)
|
|
|
|
data = builder:Output()
|
2021-05-04 10:41:08 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-05-06 10:07:56 +08:00
|
|
|
if ty == constants.RPC_HTTP_REQ_CALL then
|
|
|
|
local buf = flatbuffers.binaryArray.New(data)
|
|
|
|
local call_req = http_req_call_req.GetRootAsReq(buf, 0)
|
|
|
|
if case.check_input then
|
|
|
|
assert(call_req:Id() == 0)
|
|
|
|
assert(call_req:ConfToken() == 233)
|
|
|
|
assert(call_req:SrcIpLength() == 4)
|
|
|
|
assert(call_req:SrcIp(1) == 127)
|
|
|
|
assert(call_req:SrcIp(2) == 0)
|
|
|
|
assert(call_req:SrcIp(3) == 0)
|
|
|
|
assert(call_req:SrcIp(4) == 1)
|
|
|
|
assert(call_req:Method() == a6_method.PUT)
|
|
|
|
assert(call_req:Path() == "/hello")
|
|
|
|
|
|
|
|
assert(call_req:ArgsLength() == 4)
|
|
|
|
local res = {}
|
|
|
|
for i = 1, call_req:ArgsLength() do
|
|
|
|
local entry = call_req:Args(i)
|
|
|
|
local r = res[entry:Name()]
|
|
|
|
if r then
|
|
|
|
res[entry:Name()] = {r, entry:Value()}
|
|
|
|
else
|
|
|
|
res[entry:Name()] = entry:Value() or true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
assert(json.encode(res) == '{\"xx\":[\"y\",\"z\"],\"y\":\"\",\"z\":true}')
|
|
|
|
|
|
|
|
assert(call_req:HeadersLength() == 5)
|
|
|
|
local res = {}
|
|
|
|
for i = 1, call_req:HeadersLength() do
|
|
|
|
local entry = call_req:Headers(i)
|
|
|
|
local r = res[entry:Name()]
|
|
|
|
if r then
|
|
|
|
res[entry:Name()] = {r, entry:Value()}
|
|
|
|
else
|
|
|
|
res[entry:Name()] = entry:Value() or true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
assert(json.encode(res) == '{\"connection\":\"close\",\"host\":\"localhost\",' ..
|
|
|
|
'\"x-req\":[\"foo\",\"bar\"],\"x-resp\":\"cat\"}')
|
|
|
|
elseif case.check_input_ipv6 then
|
|
|
|
assert(call_req:SrcIpLength() == 16)
|
|
|
|
for i = 1, 15 do
|
|
|
|
assert(call_req:SrcIp(i) == 0)
|
|
|
|
end
|
|
|
|
assert(call_req:SrcIp(16) == 1)
|
|
|
|
elseif case.check_input_rewrite_host then
|
|
|
|
for i = 1, call_req:HeadersLength() do
|
|
|
|
local entry = call_req:Headers(i)
|
|
|
|
if entry:Name() == "host" then
|
|
|
|
assert(entry:Value() == "test.com")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif case.check_input_rewrite_path then
|
|
|
|
assert(call_req:Path() == "/xxx")
|
|
|
|
elseif case.check_input_rewrite_args then
|
|
|
|
assert(call_req:Path() == "/xxx")
|
|
|
|
assert(call_req:ArgsLength() == 1)
|
|
|
|
local entry = call_req:Args(1)
|
|
|
|
assert(entry:Name() == "x")
|
|
|
|
assert(entry:Value() == "z")
|
|
|
|
else
|
|
|
|
assert(call_req:Method() == a6_method.GET)
|
|
|
|
end
|
|
|
|
|
|
|
|
if case.stop == true then
|
|
|
|
local len = 3
|
|
|
|
http_req_call_stop.StartBodyVector(builder, len)
|
|
|
|
builder:PrependByte(string.byte("t"))
|
|
|
|
builder:PrependByte(string.byte("a"))
|
|
|
|
builder:PrependByte(string.byte("c"))
|
|
|
|
local b = builder:EndVector(len)
|
|
|
|
|
|
|
|
local hdrs = {
|
|
|
|
{"X-Resp", "foo"},
|
|
|
|
{"X-Req", "bar"},
|
|
|
|
}
|
|
|
|
local len = #hdrs
|
|
|
|
local textEntries = {}
|
|
|
|
for i = 1, len do
|
|
|
|
local name = builder:CreateString(hdrs[i][1])
|
|
|
|
local value = builder:CreateString(hdrs[i][2])
|
|
|
|
text_entry.Start(builder)
|
|
|
|
text_entry.AddName(builder, name)
|
|
|
|
text_entry.AddValue(builder, value)
|
|
|
|
local c = text_entry.End(builder)
|
|
|
|
textEntries[i] = c
|
|
|
|
end
|
|
|
|
http_req_call_stop.StartHeadersVector(builder, len)
|
|
|
|
for i = len, 1, -1 do
|
|
|
|
builder:PrependUOffsetTRelative(textEntries[i])
|
|
|
|
end
|
|
|
|
local vec = builder:EndVector(len)
|
|
|
|
|
|
|
|
http_req_call_stop.Start(builder)
|
|
|
|
http_req_call_stop.AddStatus(builder, 405)
|
|
|
|
http_req_call_stop.AddBody(builder, b)
|
|
|
|
http_req_call_stop.AddHeaders(builder, vec)
|
|
|
|
local action = http_req_call_stop.End(builder)
|
|
|
|
build_action(action, http_req_call_action.Stop)
|
|
|
|
|
|
|
|
elseif case.rewrite == true or case.rewrite_host == true then
|
|
|
|
local hdrs
|
|
|
|
if case.rewrite_host then
|
|
|
|
hdrs = {{"host", "127.0.0.1"}}
|
|
|
|
else
|
|
|
|
hdrs = {
|
|
|
|
{"X-Delete", nil},
|
|
|
|
{"X-Change", "bar"},
|
|
|
|
{"X-Add", "bar"},
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
local len = #hdrs
|
|
|
|
local textEntries = {}
|
|
|
|
for i = 1, len do
|
|
|
|
local name = builder:CreateString(hdrs[i][1])
|
|
|
|
local value
|
|
|
|
if hdrs[i][2] then
|
|
|
|
value = builder:CreateString(hdrs[i][2])
|
|
|
|
end
|
|
|
|
text_entry.Start(builder)
|
|
|
|
text_entry.AddName(builder, name)
|
|
|
|
if value then
|
|
|
|
text_entry.AddValue(builder, value)
|
|
|
|
end
|
|
|
|
local c = text_entry.End(builder)
|
|
|
|
textEntries[i] = c
|
|
|
|
end
|
|
|
|
http_req_call_rewrite.StartHeadersVector(builder, len)
|
|
|
|
for i = len, 1, -1 do
|
|
|
|
builder:PrependUOffsetTRelative(textEntries[i])
|
|
|
|
end
|
|
|
|
local vec = builder:EndVector(len)
|
|
|
|
|
|
|
|
local path = builder:CreateString("/uri")
|
|
|
|
|
|
|
|
http_req_call_rewrite.Start(builder)
|
|
|
|
http_req_call_rewrite.AddPath(builder, path)
|
|
|
|
http_req_call_rewrite.AddHeaders(builder, vec)
|
|
|
|
local action = http_req_call_rewrite.End(builder)
|
|
|
|
build_action(action, http_req_call_action.Rewrite)
|
|
|
|
|
|
|
|
elseif case.rewrite_args == true or case.rewrite_args_only == true then
|
|
|
|
local path = builder:CreateString("/plugin_proxy_rewrite_args")
|
|
|
|
|
|
|
|
local args = {
|
|
|
|
{"a", "foo"},
|
|
|
|
{"d", nil},
|
|
|
|
{"c", "bar"},
|
|
|
|
{"a", "bar"},
|
|
|
|
}
|
|
|
|
|
|
|
|
local len = #args
|
|
|
|
local textEntries = {}
|
|
|
|
for i = 1, len do
|
|
|
|
local name = builder:CreateString(args[i][1])
|
|
|
|
local value
|
|
|
|
if args[i][2] then
|
|
|
|
value = builder:CreateString(args[i][2])
|
|
|
|
end
|
|
|
|
text_entry.Start(builder)
|
|
|
|
text_entry.AddName(builder, name)
|
|
|
|
if value then
|
|
|
|
text_entry.AddValue(builder, value)
|
|
|
|
end
|
|
|
|
local c = text_entry.End(builder)
|
|
|
|
textEntries[i] = c
|
|
|
|
end
|
|
|
|
http_req_call_rewrite.StartHeadersVector(builder, len)
|
|
|
|
for i = len, 1, -1 do
|
|
|
|
builder:PrependUOffsetTRelative(textEntries[i])
|
|
|
|
end
|
|
|
|
local vec = builder:EndVector(len)
|
|
|
|
|
|
|
|
http_req_call_rewrite.Start(builder)
|
|
|
|
if not case.rewrite_args_only then
|
|
|
|
http_req_call_rewrite.AddPath(builder, path)
|
|
|
|
end
|
|
|
|
http_req_call_rewrite.AddArgs(builder, vec)
|
|
|
|
local action = http_req_call_rewrite.End(builder)
|
|
|
|
build_action(action, http_req_call_action.Rewrite)
|
|
|
|
|
|
|
|
elseif case.rewrite_bad_path == true then
|
|
|
|
local path = builder:CreateString("/plugin_proxy_rewrite_args?a=2")
|
|
|
|
http_req_call_rewrite.Start(builder)
|
|
|
|
http_req_call_rewrite.AddPath(builder, path)
|
|
|
|
local action = http_req_call_rewrite.End(builder)
|
|
|
|
build_action(action, http_req_call_action.Rewrite)
|
|
|
|
|
|
|
|
else
|
|
|
|
http_req_call_resp.Start(builder)
|
|
|
|
end
|
|
|
|
|
|
|
|
local req = http_req_call_resp.End(builder)
|
|
|
|
builder:Finish(req)
|
|
|
|
data = builder:Output()
|
|
|
|
end
|
|
|
|
|
2021-04-29 11:07:05 +08:00
|
|
|
local ok, err = ext.send(sock, ty, data)
|
|
|
|
if not ok then
|
|
|
|
ngx.log(ngx.ERR, err)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
ngx.log(ngx.WARN, "send rpc call response successfully")
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function _M.header_too_short()
|
|
|
|
local sock = ngx.req.socket()
|
|
|
|
local ty, data = ext.receive(sock)
|
|
|
|
if not ty then
|
|
|
|
ngx.log(ngx.ERR, data)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
ngx.log(ngx.WARN, "receive rpc call successfully")
|
|
|
|
|
|
|
|
local ok, err = sock:send({string.char(2), string.char(1)})
|
|
|
|
if not ok then
|
|
|
|
ngx.log(ngx.ERR, err)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function _M.data_too_short()
|
|
|
|
local sock = ngx.req.socket()
|
|
|
|
local ty, data = ext.receive(sock)
|
|
|
|
if not ty then
|
|
|
|
ngx.log(ngx.ERR, data)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
ngx.log(ngx.WARN, "receive rpc call successfully")
|
|
|
|
|
|
|
|
local ok, err = sock:send({string.char(2), string.char(1), string.rep(string.char(0), 3)})
|
|
|
|
if not ok then
|
|
|
|
ngx.log(ngx.ERR, err)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
return _M
|