mirror of
https://gitee.com/iresty/apisix.git
synced 2024-12-02 03:58:02 +08:00
feat(ext-plugin): implement the http-req-call protocol (#4183)
This commit is contained in:
parent
50e9bfa54e
commit
ffed4383f1
@ -29,6 +29,8 @@ local io_open = io.open
|
||||
local req_read_body = ngx.req.read_body
|
||||
local req_get_body_data = ngx.req.get_body_data
|
||||
local req_get_body_file = ngx.req.get_body_file
|
||||
local req_get_uri_args = ngx.req.get_uri_args
|
||||
local req_set_uri_args = ngx.req.set_uri_args
|
||||
|
||||
|
||||
local _M = {}
|
||||
@ -122,6 +124,32 @@ function _M.get_remote_client_port(ctx)
|
||||
end
|
||||
|
||||
|
||||
function _M.get_uri_args(ctx)
|
||||
if not ctx then
|
||||
ctx = ngx.ctx.api_ctx
|
||||
end
|
||||
|
||||
if not ctx.req_uri_args then
|
||||
-- use 0 to avoid truncated result and keep the behavior as the
|
||||
-- same as other platforms
|
||||
local args = req_get_uri_args(0)
|
||||
ctx.req_uri_args = args
|
||||
end
|
||||
|
||||
return ctx.req_uri_args
|
||||
end
|
||||
|
||||
|
||||
function _M.set_uri_args(ctx, args)
|
||||
if not ctx then
|
||||
ctx = ngx.ctx.api_ctx
|
||||
end
|
||||
|
||||
ctx.req_uri_args = nil
|
||||
return req_set_uri_args(args)
|
||||
end
|
||||
|
||||
|
||||
local function get_file(file_name)
|
||||
local f, err = io_open(file_name, 'r')
|
||||
if not f then
|
||||
|
@ -16,8 +16,14 @@
|
||||
--
|
||||
local is_http = ngx.config.subsystem == "http"
|
||||
local flatbuffers = require("flatbuffers")
|
||||
local a6_method = require("A6.Method")
|
||||
local prepare_conf_req = require("A6.PrepareConf.Req")
|
||||
local prepare_conf_resp = require("A6.PrepareConf.Resp")
|
||||
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")
|
||||
local text_entry = require("A6.TextEntry")
|
||||
local err_resp = require("A6.Err.Resp")
|
||||
local err_code = require("A6.Err.Code")
|
||||
@ -35,16 +41,23 @@ local band = bit.band
|
||||
local lshift = bit.lshift
|
||||
local rshift = bit.rshift
|
||||
local ffi = require("ffi")
|
||||
local ffi_new = ffi.new
|
||||
local ffi_str = ffi.string
|
||||
local socket_tcp = ngx.socket.tcp
|
||||
local str_byte = string.byte
|
||||
local str_format = string.format
|
||||
local worker_id = ngx.worker.id
|
||||
local ngx_timer_at = ngx.timer.at
|
||||
local exiting = ngx.worker.exiting
|
||||
local str_byte = string.byte
|
||||
local str_format = string.format
|
||||
local str_lower = string.lower
|
||||
local str_sub = string.sub
|
||||
local error = error
|
||||
local ipairs = ipairs
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
|
||||
|
||||
local events_list
|
||||
|
||||
|
||||
local lrucache = core.lrucache.new({
|
||||
type = "plugin",
|
||||
ttl = helper.get_conf_token_cache_time(),
|
||||
@ -171,6 +184,80 @@ end
|
||||
_M.receive = receive
|
||||
|
||||
|
||||
local generate_id
|
||||
do
|
||||
local count = 0
|
||||
local MAX_COUNT = lshift(1, 22)
|
||||
|
||||
function generate_id()
|
||||
local wid = worker_id()
|
||||
local id = lshift(wid, 22) + count
|
||||
count = count + 1
|
||||
if count == MAX_COUNT then
|
||||
count = 0
|
||||
end
|
||||
return id
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local encode_a6_method
|
||||
do
|
||||
local map = {
|
||||
GET = a6_method.GET,
|
||||
HEAD = a6_method.HEAD,
|
||||
POST = a6_method.POST,
|
||||
PUT = a6_method.PUT,
|
||||
DELETE = a6_method.DELETE,
|
||||
MKCOL = a6_method.MKCOL,
|
||||
COPY = a6_method.COPY,
|
||||
MOVE = a6_method.MOVE,
|
||||
OPTIONS = a6_method.OPTIONS,
|
||||
PROPFIND = a6_method.PROPFIND,
|
||||
PROPPATCH = a6_method.PROPPATCH,
|
||||
LOCK = a6_method.LOCK,
|
||||
UNLOCK = a6_method.UNLOCK,
|
||||
PATCH = a6_method.PATCH,
|
||||
TRACE = a6_method.TRACE,
|
||||
}
|
||||
|
||||
function encode_a6_method(name)
|
||||
return map[name]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function build_args(builder, key, val)
|
||||
local name = builder:CreateString(key)
|
||||
local value
|
||||
if val ~= true then
|
||||
value = builder:CreateString(val)
|
||||
end
|
||||
|
||||
text_entry.Start(builder)
|
||||
text_entry.AddName(builder, name)
|
||||
if val ~= true then
|
||||
text_entry.AddValue(builder, value)
|
||||
end
|
||||
return text_entry.End(builder)
|
||||
end
|
||||
|
||||
|
||||
local function build_headers(var, builder, key, val)
|
||||
if key == "host" then
|
||||
val = var.upstream_host
|
||||
end
|
||||
|
||||
local name = builder:CreateString(key)
|
||||
local value = builder:CreateString(val)
|
||||
|
||||
text_entry.Start(builder)
|
||||
text_entry.AddName(builder, name)
|
||||
text_entry.AddValue(builder, value)
|
||||
return text_entry.End(builder)
|
||||
end
|
||||
|
||||
|
||||
local rpc_call
|
||||
local rpc_handlers = {
|
||||
nil,
|
||||
@ -232,8 +319,90 @@ local rpc_handlers = {
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local req = "hello"
|
||||
local ok, err = send(sock, constants.RPC_HTTP_REQ_CALL, req)
|
||||
builder:Clear()
|
||||
local var = ctx.var
|
||||
|
||||
local uri
|
||||
if var.upstream_uri == "" then
|
||||
-- use original uri instead of rewritten one
|
||||
uri = var.uri
|
||||
else
|
||||
uri = var.upstream_uri
|
||||
|
||||
-- the rewritten one may contain new args
|
||||
local index = core.string.find(uri, "?")
|
||||
if index then
|
||||
local raw_uri = uri
|
||||
uri = str_sub(raw_uri, 1, index - 1)
|
||||
core.request.set_uri_args(ctx, str_sub(raw_uri, index + 1))
|
||||
end
|
||||
end
|
||||
|
||||
local path = builder:CreateString(uri)
|
||||
|
||||
local bin_addr = var.binary_remote_addr
|
||||
local len = #bin_addr
|
||||
http_req_call_req.StartSrcIpVector(builder, len)
|
||||
for i = len, 1, -1 do
|
||||
builder:PrependByte(str_byte(bin_addr, i))
|
||||
end
|
||||
local src_ip = builder:EndVector(len)
|
||||
|
||||
local args = core.request.get_uri_args(ctx)
|
||||
local textEntries = {}
|
||||
for key, val in pairs(args) do
|
||||
local ty = type(val)
|
||||
if ty == "table" then
|
||||
for _, v in ipairs(val) do
|
||||
core.table.insert(textEntries, build_args(builder, key, v))
|
||||
end
|
||||
else
|
||||
core.table.insert(textEntries, build_args(builder, key, val))
|
||||
end
|
||||
end
|
||||
local len = #textEntries
|
||||
http_req_call_req.StartArgsVector(builder, len)
|
||||
for i = len, 1, -1 do
|
||||
builder:PrependUOffsetTRelative(textEntries[i])
|
||||
end
|
||||
local args_vec = builder:EndVector(len)
|
||||
|
||||
local hdrs = core.request.headers(ctx)
|
||||
core.table.clear(textEntries)
|
||||
for key, val in pairs(hdrs) do
|
||||
local ty = type(val)
|
||||
if ty == "table" then
|
||||
for _, v in ipairs(val) do
|
||||
core.table.insert(textEntries, build_headers(var, builder, key, v))
|
||||
end
|
||||
else
|
||||
core.table.insert(textEntries, build_headers(var, builder, key, val))
|
||||
end
|
||||
end
|
||||
local len = #textEntries
|
||||
http_req_call_req.StartHeadersVector(builder, len)
|
||||
for i = len, 1, -1 do
|
||||
builder:PrependUOffsetTRelative(textEntries[i])
|
||||
end
|
||||
local hdrs_vec = builder:EndVector(len)
|
||||
|
||||
local id = generate_id()
|
||||
local method = var.method
|
||||
|
||||
http_req_call_req.Start(builder)
|
||||
http_req_call_req.AddId(builder, id)
|
||||
http_req_call_req.AddConfToken(builder, token)
|
||||
http_req_call_req.AddSrcIp(builder, src_ip)
|
||||
http_req_call_req.AddPath(builder, path)
|
||||
http_req_call_req.AddArgs(builder, args_vec)
|
||||
http_req_call_req.AddHeaders(builder, hdrs_vec)
|
||||
http_req_call_req.AddMethod(builder, encode_a6_method(method))
|
||||
-- TODO: handle extraInfo
|
||||
|
||||
local req = http_req_call_req.End(builder)
|
||||
builder:Finish(req)
|
||||
|
||||
local ok, err = send(sock, constants.RPC_HTTP_REQ_CALL, builder:Output())
|
||||
if not ok then
|
||||
return nil, "failed to send RPC_HTTP_REQ_CALL: " .. err
|
||||
end
|
||||
@ -247,7 +416,95 @@ local rpc_handlers = {
|
||||
return nil, "failed to receive RPC_HTTP_REQ_CALL: unexpected type " .. ty
|
||||
end
|
||||
|
||||
core.log.warn(resp)
|
||||
local buf = flatbuffers.binaryArray.New(resp)
|
||||
local call_resp = http_req_call_resp.GetRootAsResp(buf, 0)
|
||||
local action_type = call_resp:ActionType()
|
||||
|
||||
if action_type == http_req_call_action.Stop then
|
||||
local action = call_resp:Action()
|
||||
local stop = http_req_call_stop.New()
|
||||
stop:Init(action.bytes, action.pos)
|
||||
|
||||
local len = stop:HeadersLength()
|
||||
if len > 0 then
|
||||
for i = 1, len do
|
||||
local entry = stop:Headers(i)
|
||||
core.response.set_header(entry:Name(), entry:Value())
|
||||
end
|
||||
end
|
||||
|
||||
local body
|
||||
local len = stop:BodyLength()
|
||||
if len > 0 then
|
||||
-- TODO: support empty body
|
||||
body = ffi_new("unsigned char[?]", len)
|
||||
for i = 1, len do
|
||||
body[i - 1] = stop:Body(i)
|
||||
end
|
||||
body = ffi_str(body, len)
|
||||
end
|
||||
return true, nil, stop:Status(), body
|
||||
end
|
||||
|
||||
if action_type == http_req_call_action.Rewrite then
|
||||
ctx.request_rewritten = constants.REWRITTEN_BY_EXT_PLUGIN
|
||||
|
||||
local action = call_resp:Action()
|
||||
local rewrite = http_req_call_rewrite.New()
|
||||
rewrite:Init(action.bytes, action.pos)
|
||||
|
||||
local path = rewrite:Path()
|
||||
if path then
|
||||
path = core.utils.uri_safe_encode(path)
|
||||
var.upstream_uri = path
|
||||
end
|
||||
|
||||
local len = rewrite:HeadersLength()
|
||||
if len > 0 then
|
||||
for i = 1, len do
|
||||
local entry = rewrite:Headers(i)
|
||||
local name = entry:Name()
|
||||
core.request.set_header(ctx, name, entry:Value())
|
||||
|
||||
if str_lower(name) == "host" then
|
||||
var.upstream_host = entry:Value()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local len = rewrite:ArgsLength()
|
||||
if len > 0 then
|
||||
local changed = {}
|
||||
for i = 1, len do
|
||||
local entry = rewrite:Args(i)
|
||||
local name = entry:Name()
|
||||
local value = entry:Value()
|
||||
if value == nil then
|
||||
args[name] = nil
|
||||
|
||||
else
|
||||
if changed[name] then
|
||||
if type(args[name]) == "table" then
|
||||
core.table.insert(args[name], value)
|
||||
else
|
||||
args[name] = {args[name], entry:Value()}
|
||||
end
|
||||
else
|
||||
args[name] = entry:Value()
|
||||
end
|
||||
|
||||
changed[name] = true
|
||||
end
|
||||
end
|
||||
|
||||
core.request.set_uri_args(ctx, args)
|
||||
|
||||
if path then
|
||||
var.upstream_uri = path .. '?' .. var.args
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
}
|
||||
@ -263,8 +520,8 @@ rpc_call = function (ty, conf, ctx)
|
||||
return nil, "failed to connect to the unix socket " .. path .. ": " .. err
|
||||
end
|
||||
|
||||
local ok, err = rpc_handlers[ty + 1](conf, ctx, sock)
|
||||
if not ok then
|
||||
local res, err, code, body = rpc_handlers[ty + 1](conf, ctx, sock)
|
||||
if not res then
|
||||
sock:close()
|
||||
return nil, err
|
||||
end
|
||||
@ -273,16 +530,22 @@ rpc_call = function (ty, conf, ctx)
|
||||
if not ok then
|
||||
core.log.info("failed to setkeepalive: ", err)
|
||||
end
|
||||
return true
|
||||
|
||||
return res, nil, code, body
|
||||
end
|
||||
|
||||
|
||||
function _M.communicate(conf, ctx)
|
||||
local ok, err = rpc_call(constants.RPC_HTTP_REQ_CALL, conf, ctx)
|
||||
local ok, err, code, body = rpc_call(constants.RPC_HTTP_REQ_CALL, conf, ctx)
|
||||
if not ok then
|
||||
core.log.error(err)
|
||||
return 503
|
||||
end
|
||||
|
||||
if code then
|
||||
return code, body
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
@ -66,7 +66,7 @@ dependencies = {
|
||||
"luasec = 0.9-1",
|
||||
"lua-resty-consul = 0.3-2",
|
||||
"penlight = 1.9.2-1",
|
||||
"ext-plugin-proto = 0.1.0",
|
||||
"ext-plugin-proto = 0.1.1",
|
||||
}
|
||||
|
||||
build = {
|
||||
|
@ -14,6 +14,7 @@
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
--
|
||||
local json = require("toolkit.json")
|
||||
local ext = require("apisix.plugins.ext-plugin.init")
|
||||
local constants = require("apisix.constants")
|
||||
local flatbuffers = require("flatbuffers")
|
||||
@ -21,12 +22,26 @@ local err_code = require("A6.Err.Code")
|
||||
local err_resp = require("A6.Err.Resp")
|
||||
local prepare_conf_req = require("A6.PrepareConf.Req")
|
||||
local prepare_conf_resp = require("A6.PrepareConf.Resp")
|
||||
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")
|
||||
|
||||
|
||||
local _M = {}
|
||||
local builder = flatbuffers.Builder(0)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
function _M.go(case)
|
||||
local sock = ngx.req.socket()
|
||||
local ty, data = ext.receive(sock)
|
||||
@ -68,6 +83,205 @@ function _M.go(case)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
local ok, err = ext.send(sock, ty, data)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, err)
|
||||
|
@ -94,7 +94,11 @@ function _M.plugin_proxy_rewrite_args()
|
||||
table.sort(keys)
|
||||
|
||||
for _, key in ipairs(keys) do
|
||||
ngx.say(key, ": ", args[key])
|
||||
if type(args[key]) == "table" then
|
||||
ngx.say(key, ": ", table.concat(args[key], ','))
|
||||
else
|
||||
ngx.say(key, ": ", args[key])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
517
t/plugin/ext-plugin/http-req-call.t
vendored
Normal file
517
t/plugin/ext-plugin/http-req-call.t
vendored
Normal file
@ -0,0 +1,517 @@
|
||||
#
|
||||
# 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();
|
||||
log_level("info");
|
||||
|
||||
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 $extra_yaml_config = <<_EOC_;
|
||||
ext-plugin:
|
||||
path_for_test: $unix_socket_path
|
||||
_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: stop
|
||||
--- request
|
||||
GET /hello
|
||||
--- response_body chomp
|
||||
cat
|
||||
--- extra_stream_config
|
||||
server {
|
||||
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
|
||||
|
||||
content_by_lua_block {
|
||||
local ext = require("lib.ext-plugin")
|
||||
ext.go({stop = true})
|
||||
}
|
||||
}
|
||||
--- error_code: 405
|
||||
--- response_headers
|
||||
X-Resp: foo
|
||||
X-Req: bar
|
||||
|
||||
|
||||
|
||||
=== TEST 3: check input
|
||||
--- request
|
||||
PUT /hello?xx=y&xx=z&&y=&&z
|
||||
--- more_headers
|
||||
X-Req: foo
|
||||
X-Req: bar
|
||||
X-Resp: cat
|
||||
--- extra_stream_config
|
||||
server {
|
||||
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
|
||||
|
||||
content_by_lua_block {
|
||||
local ext = require("lib.ext-plugin")
|
||||
ext.go({check_input = true})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
=== TEST 4: check input (ipv6)
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local t = require("lib.test_admin").test_ipv6
|
||||
t('/hello')
|
||||
}
|
||||
}
|
||||
--- extra_stream_config
|
||||
server {
|
||||
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
|
||||
|
||||
content_by_lua_block {
|
||||
local ext = require("lib.ext-plugin")
|
||||
ext.go({check_input_ipv6 = true})
|
||||
}
|
||||
}
|
||||
--- listen_ipv6
|
||||
|
||||
|
||||
|
||||
=== TEST 5: rewrite
|
||||
--- 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")
|
||||
ext.go({rewrite = true})
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
uri: /uri
|
||||
host: localhost
|
||||
x-add: bar
|
||||
x-change: bar
|
||||
x-real-ip: 127.0.0.1
|
||||
|
||||
|
||||
|
||||
=== TEST 6: rewrite host
|
||||
--- 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")
|
||||
ext.go({rewrite_host = true})
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
uri: /uri
|
||||
host: 127.0.0.1
|
||||
x-real-ip: 127.0.0.1
|
||||
|
||||
|
||||
|
||||
=== TEST 7: rewrite args
|
||||
--- request
|
||||
GET /hello?c=foo&d=bar
|
||||
--- extra_stream_config
|
||||
server {
|
||||
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
|
||||
|
||||
content_by_lua_block {
|
||||
local ext = require("lib.ext-plugin")
|
||||
ext.go({rewrite_args = true})
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
uri: /plugin_proxy_rewrite_args
|
||||
a: foo,bar
|
||||
c: bar
|
||||
|
||||
|
||||
|
||||
=== TEST 8: proxy-rewrite + rewrite host
|
||||
--- 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": {
|
||||
"proxy-rewrite": {
|
||||
"host": "test.com"
|
||||
},
|
||||
"ext-plugin-post-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 9: hit
|
||||
--- 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")
|
||||
ext.go({rewrite_host = true, check_input_rewrite_host = true})
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
uri: /uri
|
||||
host: 127.0.0.1
|
||||
x-real-ip: 127.0.0.1
|
||||
|
||||
|
||||
|
||||
=== TEST 10: proxy-rewrite + rewrite path
|
||||
--- 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": {
|
||||
"proxy-rewrite": {
|
||||
"uri": "/xxx"
|
||||
},
|
||||
"ext-plugin-post-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 11: hit
|
||||
--- 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")
|
||||
ext.go({rewrite_host = true, check_input_rewrite_path = true})
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
uri: /uri
|
||||
host: 127.0.0.1
|
||||
x-real-ip: 127.0.0.1
|
||||
|
||||
|
||||
|
||||
=== TEST 12: proxy-rewrite + rewrite path with args
|
||||
--- 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": {
|
||||
"proxy-rewrite": {
|
||||
"uri": "/xxx?x=z"
|
||||
},
|
||||
"ext-plugin-post-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 13: hit
|
||||
--- 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")
|
||||
ext.go({rewrite_args = true, check_input_rewrite_args = true})
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
uri: /plugin_proxy_rewrite_args
|
||||
a: foo,bar
|
||||
c: bar
|
||||
x: z
|
||||
|
||||
|
||||
|
||||
=== TEST 14: rewrite args only
|
||||
--- 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": "/plugin_proxy_rewrite_args",
|
||||
"plugins": {
|
||||
"ext-plugin-post-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 15: hit
|
||||
--- request
|
||||
GET /plugin_proxy_rewrite_args
|
||||
--- extra_stream_config
|
||||
server {
|
||||
listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
|
||||
|
||||
content_by_lua_block {
|
||||
local ext = require("lib.ext-plugin")
|
||||
ext.go({rewrite_args_only = true})
|
||||
}
|
||||
}
|
||||
--- response_body
|
||||
uri: /plugin_proxy_rewrite_args
|
||||
a: foo,bar
|
||||
c: bar
|
||||
|
||||
|
||||
|
||||
=== TEST 16: rewrite, bad path
|
||||
--- 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-post-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 17: hit
|
||||
--- 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")
|
||||
ext.go({rewrite_bad_path = true})
|
||||
}
|
||||
}
|
||||
--- access_log
|
||||
GET /plugin_proxy_rewrite_args%3Fa=2
|
||||
--- error_code: 404
|
Loading…
Reference in New Issue
Block a user