-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: hold_body_chunk should use seperate buffer for each plugin in case of pollution #9266
Conversation
Signed-off-by: sn0rt <wangguohao.2009@gmail.com>
Signed-off-by: sn0rt <wangguohao.2009@gmail.com>
how to reprorude the duplicate logto create a new test file. #
# 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';
no_long_string();
no_shuffle();
no_root_location();
add_block_preprocessor(sub {
my ($block) = @_;
if (!$block->request) {
$block->set_value("request", "GET /t");
}
});
run_tests;
__DATA__
=== TEST 1: simulate simple SOAP proxy
--- config
location /demo {
content_by_lua_block {
local core = require("apisix.core")
local body = core.request.get_body()
local xml2lua = require("xml2lua")
local xmlhandler = require("xmlhandler.tree")
local handler = xmlhandler:new()
local parser = xml2lua.parser(handler)
parser:parse(body)
ngx.print(string.format([[
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:getCountryResponse
xmlns:ns2="http://spring.io/guides/gs-producing-web-service">
<ns2:country>
<ns2:name>%s</ns2:name>
<ns2:population>46704314</ns2:population>
<ns2:capital>Madrid</ns2:capital>
<ns2:currency>EUR</ns2:currency>
</ns2:country>
</ns2:getCountryResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
]], handler.root["soap-env:Envelope"]["soap-env:Body"]["ns0:getCountryRequest"]["ns0:name"]))
}
}
location /t {
content_by_lua_block {
local t = require("lib.test_admin")
local req_template = ngx.encode_base64[[
<?xml version="1.0"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body>
<ns0:getCountryRequest xmlns:ns0="http://spring.io/guides/gs-producing-web-service">
<ns0:name>{{_escape_xml(name)}}</ns0:name>
</ns0:getCountryRequest>
</soap-env:Body>
</soap-env:Envelope>
]]
local rsp_template = ngx.encode_base64[[
{% if Envelope.Body.Fault == nil then %}
{
"status":"{{_ctx.var.status}}",
"currency":"{{Envelope.Body.getCountryResponse.country.currency}}",
"population":{{Envelope.Body.getCountryResponse.country.population}},
"capital":"{{Envelope.Body.getCountryResponse.country.capital}}",
"name":"{{Envelope.Body.getCountryResponse.country.name}}"
}
{% else %}
{
"message":{*_escape_json(Envelope.Body.Fault.faultstring[1])*},
"code":"{{Envelope.Body.Fault.faultcode}}"
{% if Envelope.Body.Fault.faultactor ~= nil then %}
, "actor":"{{Envelope.Body.Fault.faultactor}}"
{% end %}
}
{% end %}
]]
local code, body = t.test('/apisix/admin/routes/1',
ngx.HTTP_PUT,
string.format([[{
"uri": "/ws",
"plugins": {
"proxy-rewrite": {
"uri": "/demo"
},
"body-transformer": {
"request": {
"template": "%s"
},
"response": {
"input_format": "xml",
"template": "%s"
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:%d": 1
}
}
}]], req_template, rsp_template, ngx.var.server_port)
)
if code >= 300 then
ngx.status = code
return
end
local code, body = t.test('/apisix/admin/global_rules/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"http-logger": {
"uri": "http://127.0.0.1:1980/log",
"batch_max_size": 1,
"include_resp_body": true
}
}
}]]
)
if code >= 300 then
ngx.status = code
return
end
ngx.sleep(0.5)
local core = require("apisix.core")
local http = require("resty.http")
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/ws"
local body = [[{"name": "Spain"}]]
local opt = {method = "POST", body = body, headers = {["Content-Type"] = "application/json"}}
local httpc = http.new()
local res = httpc:request_uri(uri, opt)
assert(res.status == 200)
local data1 = core.json.decode(res.body)
local data2 = core.json.decode[[{"status":"200","currency":"EUR","population":46704314,"capital":"Madrid","name":"Spain"}]]
assert(core.json.stably_encode(data1), core.json.stably_encode(data2))
}
} try to run test file and ignore the output of test. $ prove -I. -I../test-nginx/inc -I../test-nginx/lib -r t/plugin/body-transformer.t to check the log of apisix.
|
@@ -486,7 +486,7 @@ GET /grpc_plus?a=1&b=2 | |||
--- response_body eval | |||
qr/\{"result":3\}/ | |||
--- error_log eval | |||
qr/request log: \{.*body":\"\\u0000\\u0000\\u0000\\u0000\\u0002\\b\\u0003\\u0000\\u0000\\u0000\\u0000\\u0002\\b\\u0003"/ | |||
qr/request log: \{.*body":\"\\u0000\\u0000\\u0000\\u0000\\u0002\\b\\u0003"/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR fixes another bug: insert the same chunk twice (by different plugins) into the body_buffer
.
Bug reproduce:
diff --git a/apisix/core/response.lua b/apisix/core/response.lua
index b934d94b..971961b3 100644
--- a/apisix/core/response.lua
+++ b/apisix/core/response.lua
@@ -178,6 +178,8 @@ function _M.hold_body_chunk(ctx, hold_the_copy)
local body_buffer
local chunk, eof = arg[1], arg[2]
if type(chunk) == "string" and chunk ~= "" then
+ ngx.log(ngx.WARN, "#chunk: ", #chunk)
+ ngx.log(ngx.WARN, debug.traceback("hold_body_chunk", 3))
body_buffer = ctx._body_buffer
if not body_buffer then
body_buffer = {
@@ -185,10 +187,13 @@ function _M.hold_body_chunk(ctx, hold_the_copy)
n = 1
}
ctx._body_buffer = body_buffer
+ ngx.log(ngx.WARN, "create body_buffer: ", tostring(body_buffer))
+ ngx.log(ngx.WARN, "init #chunk: ", #chunk)
else
local n = body_buffer.n + 1
body_buffer.n = n
body_buffer[n] = chunk
+ ngx.log(ngx.WARN, "add #chunk: ", #chunk)
end
end
error.log from this test case:
2023/04/12 18:08:06 [warn] 2785045#2785045: *3 [lua] response.lua:181: hold_body_chunk(): #chunk: 7 while sending to client, client: 127.0.0.1, server: localhost, request: "GET /grpc_plus?a=1&b=2 HTTP/1.1", upstream: "grpc://127.0.0.1:50051", host: "localhost"
2023/04/12 18:08:06 [warn] 2785045#2785045: *3 [lua] response.lua:182: hold_body_chunk(): hold_body_chunk
stack traceback:
/opt/apisix/apisix/plugins/http-logger.lua:153: in function 'phase_func'
/opt/apisix/apisix/plugin.lua:1131: in function 'run_plugin'
/opt/apisix/apisix/plugin.lua:1164: in function 'run_global_rules'
/opt/apisix/apisix/init.lua:403: in function 'common_phase'
/opt/apisix/apisix/init.lua:747: in function 'http_body_filter_phase'
body_filter_by_lua:2: in main chunk while sending to client, client: 127.0.0.1, server: localhost, request: "GET /grpc_plus?a=1&b=2 HTTP/1.1", upstream: "grpc://127.0.0.1:50051", host: "localhost"
2023/04/12 18:08:06 [warn] 2785045#2785045: *3 [lua] response.lua:190: hold_body_chunk(): create body_buffer: table: 0x7f4aede1e4c8 while sending to client, client: 127.0.0.1, server: localhost, request: "GET /grpc_plus?a=1&b=2 HTTP/1.1", upstream: "grpc://127.0.0.1:50051", host: "localhost"
2023/04/12 18:08:06 [warn] 2785045#2785045: *3 [lua] response.lua:191: hold_body_chunk(): init #chunk: 7 while sending to client, client: 127.0.0.1, server: localhost,request: "GET /grpc_plus?a=1&b=2 HTTP/1.1", upstream: "grpc://127.0.0.1:50051", host: "localhost"
2023/04/12 18:08:06 [warn] 2785045#2785045: *3 [lua] response.lua:181: hold_body_chunk(): #chunk: 7 while sending to client, client: 127.0.0.1, server: localhost, request: "GET /grpc_plus?a=1&b=2 HTTP/1.1", upstream: "grpc://127.0.0.1:50051", host: "localhost"
2023/04/12 18:08:06 [warn] 2785045#2785045: *3 [lua] response.lua:182: hold_body_chunk(): hold_body_chunk
stack traceback:
/opt/apisix/apisix/plugins/grpc-transcode.lua:202: in function 'phase_func'
/opt/apisix/apisix/plugin.lua:1131: in function 'common_phase'
/opt/apisix/apisix/init.lua:747: in function 'http_body_filter_phase'
body_filter_by_lua:2: in main chunk while sending to client, client: 127.0.0.1, server: localhost, request: "GET /grpc_plus?a=1&b=2 HTTP/1.1", upstream: "grpc://127.0.0.1:50051", host: "localhost"
2023/04/12 18:08:06 [warn] 2785045#2785045: *3 [lua] response.lua:196: hold_body_chunk(): add #chunk: 7 while sending to client, client: 127.0.0.1, server: localhost, request: "GET /grpc_plus?a=1&b=2 HTTP/1.1", upstream: "grpc://127.0.0.1:50051", host: "localhost"
2023/04/12 18:08:06 [warn] 2785045#2785045: *3 [lua] response.lua:110: response(): #buffer: 14 while sending to client, client: 127.0.0.1, server: localhost, request: "GET /grpc_plus?a=1&b=2 HTTP/1.1", upstream: "grpc://127.0.0.1:50051", host: "localhost"
Hi @Sn0rt, this pr seems have a long description, and it fix several problems, right? |
I change the title. It's for use by body filter plugins to work together. |
Could you add test cases to prove that the current body filter plugins can't work together? |
It is used by #9267. |
I think it's unnecessary. the reasons is you can found the second commit of this PR. |
Description
Fixes #9228
and fix:
Fixed a problem with a unit test left before.
This unit test proves that there is a collaboration problem between http-logger and some plugins, which will cause http-logger to repeatedly print the content of the body.
Checklist