Skip to content

Commit

Permalink
feat(ext-plugin-post-resp): support get response body by extra_info (#…
Browse files Browse the repository at this point in the history
…7947)

Co-authored-by: soulbird <zhaothree@gmail.com>
  • Loading branch information
soulbird and soulbird committed Sep 23, 2022
1 parent 20efe29 commit 1d29d4b
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 97 deletions.
55 changes: 33 additions & 22 deletions apisix/plugins/ext-plugin-post-resp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
--
local core = require("apisix.core")
local ext = require("apisix.plugins.ext-plugin.init")
local helper = require("apisix.plugins.ext-plugin.helper")
local constants = require("apisix.constants")
local http = require("resty.http")

Expand Down Expand Up @@ -100,36 +101,46 @@ local function get_response(ctx, http_obj)
return res, err
end

local function send_chunk(chunk)
if not chunk then
return nil
end

local function send_response(res, code)
ngx.status = code or res.status
local ok, print_err = ngx_print(chunk)
if not ok then
return "output response failed: ".. (print_err or "")
end
local ok, flush_err = ngx_flush(true)
if not ok then
core.log.warn("flush response failed: ", flush_err)
end

local reader = res.body_reader
repeat
local chunk, ok, read_err, print_err, flush_err
-- TODO: HEAD or 304
chunk, read_err = reader()
if read_err then
return "read response failed: ".. (read_err or "")
end
return nil
end

if chunk then
ok, print_err = ngx_print(chunk)
if not ok then
return "output response failed: ".. (print_err or "")
end
ok, flush_err = ngx_flush(true)
if not ok then
core.log.warn("flush response failed: ", flush_err)
-- TODO: response body is empty (304 or HEAD)
-- If the upstream returns 304 or the request method is HEAD,
-- there is no response body. In this case,
-- we need to send a response to the client in the plugin,
-- instead of continuing to execute the subsequent plugin.
local function send_response(ctx, res, code)
ngx.status = code or res.status

local chunks = ctx.runner_ext_response_body
if chunks then
for i=1, #chunks do
local err = send_chunk(chunks[i])
if err then
return err
end
end
until not chunk
return
end

return nil
return helper.response_reader(res.body_reader, send_chunk)
end



function _M.check_schema(conf)
return core.schema.check(_M.schema, conf)
end
Expand Down Expand Up @@ -157,7 +168,7 @@ function _M.before_proxy(conf, ctx)
core.log.info("ext-plugin will send response")

-- send origin response, status maybe changed.
err = send_response(res, code)
err = send_response(ctx, res, code)
close(http_obj)

if err then
Expand Down
22 changes: 22 additions & 0 deletions apisix/plugins/ext-plugin/helper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,26 @@ function _M.get_conf_token_cache_time()
end


function _M.response_reader(reader, callback, ...)
if not reader then
return "get response reader failed"
end

repeat
local chunk, read_err, cb_err
chunk, read_err = reader()
if read_err then
return "read response failed: ".. (read_err or "")
end

if chunk then
cb_err = callback(chunk, ...)
if cb_err then
return cb_err
end
end
until not chunk
end


return _M
50 changes: 46 additions & 4 deletions apisix/plugins/ext-plugin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ 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 extra_info_reqbody = require("A6.ExtraInfo.ReqBody")
local extra_info_respbody = require("A6.ExtraInfo.RespBody")
local text_entry = require("A6.TextEntry")
local err_resp = require("A6.Err.Resp")
local err_code = require("A6.Err.Code")
Expand Down Expand Up @@ -304,7 +305,31 @@ local function handle_extra_info(ctx, input)
if err then
core.log.error("failed to read request body: ", err)
end

elseif info_type == extra_info.RespBody then
local ext_res = ctx.runner_ext_response
if ext_res then
local info = req:Info()
local respbody_req = extra_info_respbody.New()
respbody_req:Init(info.byte, info.pos)

local chunks = {}
local err = helper.response_reader(ext_res.body_reader, function (chunk, chunks)
-- When the upstream response is chunked type,
-- we will receive the complete response body
-- before sending it to the runner program
-- to reduce the number of RPC calls.
core.table.insert_tail(chunks, chunk)
end, chunks)
if err then
-- TODO: send RPC_ERROR to runner
core.log.error(err)
else
res = core.table.concat(chunks)
ctx.runner_ext_response_body = chunks
end
else
core.log.error("failed to read response body: not exits")
end
else
return nil, "unsupported info type: " .. info_type
end
Expand Down Expand Up @@ -732,9 +757,26 @@ local rpc_handlers = {
return nil, "failed to send RPC_HTTP_RESP_CALL: " .. err
end

local ty, resp = receive(sock)
if ty == nil then
return nil, "failed to receive RPC_HTTP_RESP_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_RESP_CALL then
Expand Down
4 changes: 0 additions & 4 deletions docs/en/latest/plugins/ext-plugin-post-resp.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ See [External Plugin](../external-plugin.md) to learn more.

Execution of External Plugins will affect the response of the current request.

External Plugin does not yet support getting request context information.

External Plugin does not yet support getting the response body of an upstream response.

:::

## Attributes
Expand Down
4 changes: 0 additions & 4 deletions docs/zh/latest/plugins/ext-plugin-post-resp.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ description: 本文介绍了关于 Apache APISIX `ext-plugin-post-resp` 插件

External Plugin 执行的结果会影响当前请求的响应。

External Plugin 尚不支持获取请求的上下文信息。

External Plugin 尚不支持获取上游响应的响应体。

:::

## 属性
Expand Down
2 changes: 1 addition & 1 deletion rockspec/apisix-master-0.rockspec
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ dependencies = {
"luasec = 0.9-1",
"lua-resty-consul = 0.3-2",
"penlight = 1.9.2-1",
"ext-plugin-proto = 0.5.0",
"ext-plugin-proto = 0.6.0",
"casbin = 1.41.1",
"api7-snowflake = 2.0-1",
"inspect == 3.1.1",
Expand Down
Loading

0 comments on commit 1d29d4b

Please sign in to comment.