This is an automated email from the ASF dual-hosted git repository.
nic-6443 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 9ef7709d1 fix: split global rules phase execution for client-control
compatibility (#13345)
9ef7709d1 is described below
commit 9ef7709d189d82136a02f7de772d346e539a022b
Author: Nic <[email protected]>
AuthorDate: Mon May 11 16:50:47 2026 +0800
fix: split global rules phase execution for client-control compatibility
(#13345)
---
apisix/init.lua | 11 +++-
apisix/plugin.lua | 12 ++--
t/plugin/client-control.t | 145 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 157 insertions(+), 11 deletions(-)
diff --git a/apisix/init.lua b/apisix/init.lua
index 3e2db0d67..5bffaa316 100644
--- a/apisix/init.lua
+++ b/apisix/init.lua
@@ -754,7 +754,8 @@ function _M.http_access_phase()
match_span:finish(ngx.ctx)
-- run global rule when there is no matching route
local global_rules, conf_version = apisix_global_rules.global_rules()
- plugin.run_global_rules(api_ctx, global_rules, conf_version, nil)
+ plugin.run_global_rules(api_ctx, global_rules, conf_version, "rewrite")
+ plugin.run_global_rules(api_ctx, global_rules, conf_version, "access")
core.log.info("not find any matched route")
return core.response.exit(404,
@@ -806,12 +807,15 @@ function _M.http_access_phase()
api_ctx.route_id = route.value.id
api_ctx.route_name = route.value.name
- -- run global rule
+ -- Split global rule execution: run rewrite first so route/service plugins
+ -- can set overrides (e.g., client-control FFI) before global rule access
+ -- phase runs (e.g., logger body collection).
local global_rules, conf_version = apisix_global_rules.global_rules()
- plugin.run_global_rules(api_ctx, global_rules, conf_version, nil)
+ plugin.run_global_rules(api_ctx, global_rules, conf_version, "rewrite")
if route.value.script then
script.load(route, api_ctx)
+ plugin.run_global_rules(api_ctx, global_rules, conf_version, "access")
script.run("access", api_ctx)
else
@@ -850,6 +854,7 @@ function _M.http_access_phase()
plugin.run_plugin(phase, api_ctx.plugins, api_ctx)
end
end
+ plugin.run_global_rules(api_ctx, global_rules, conf_version, "access")
plugin.run_plugin("access", plugins, api_ctx)
end
span:finish(ngx_ctx)
diff --git a/apisix/plugin.lua b/apisix/plugin.lua
index 6c89a034b..4f7410a25 100644
--- a/apisix/plugin.lua
+++ b/apisix/plugin.lua
@@ -1432,12 +1432,13 @@ end
function _M.run_global_rules(api_ctx, global_rules, conf_version, phase_name)
if global_rules and #global_rules > 0 then
- local span = tracer.start(api_ctx.ngx_ctx, "run_global_rules",
tracer.kind.internal)
+ local span_name = "run_global_rules." .. phase_name
+ local span = tracer.start(api_ctx.ngx_ctx, span_name,
tracer.kind.internal)
local orig_conf_type = api_ctx.conf_type
local orig_conf_version = api_ctx.conf_version
local orig_conf_id = api_ctx.conf_id
- if phase_name == nil then
+ if phase_name == "rewrite" then
api_ctx.global_rules = global_rules
end
@@ -1456,12 +1457,7 @@ function _M.run_global_rules(api_ctx, global_rules,
conf_version, phase_name)
core.table.clear(plugins)
plugins = _M.filter(api_ctx, dummy_global_rule, plugins, route)
- if phase_name == nil then
- _M.run_plugin("rewrite", plugins, api_ctx)
- _M.run_plugin("access", plugins, api_ctx)
- else
- _M.run_plugin(phase_name, plugins, api_ctx)
- end
+ _M.run_plugin(phase_name, plugins, api_ctx)
core.tablepool.release("plugins", plugins)
api_ctx.conf_type = orig_conf_type
diff --git a/t/plugin/client-control.t b/t/plugin/client-control.t
index 3f174ebbe..48dca8b0d 100644
--- a/t/plugin/client-control.t
+++ b/t/plugin/client-control.t
@@ -28,6 +28,7 @@ if ($version !~ m/\/apisix-nginx-module/) {
repeat_each(1);
log_level('info');
no_root_location();
+no_long_string();
no_shuffle();
add_block_preprocessor(sub {
@@ -185,3 +186,147 @@ passed
--- request
POST /hello
1
+
+
+
+=== TEST 8: setup global rule with body reader and route with client-control
+The global rule reads the body in access phase (simulates a logger with
+include_req_body). The route has client-control raising the body size limit
+above test-nginx's hardcoded 30M so the body read succeeds.
+--- upstream_server_config
+ client_max_body_size 0;
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+
+ -- global rule: read body in access phase
+ local code, body = t('/apisix/admin/global_rules/1',
+ ngx.HTTP_PUT,
+ [[{
+ "plugins": {
+ "serverless-post-function": {
+ "phase": "access",
+ "functions": ["return function(conf, ctx)
ngx.req.read_body() end"]
+ }
+ }
+ }]]
+ )
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+
+ -- route with client-control raising the limit above 30M
+ code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/hello",
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:1980": 1
+ }
+ },
+ "plugins": {
+ "client-control": {
+ "max_body_size": 52428800
+ }
+ }
+ }]]
+ )
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 9: client-control should override body limit before global rule reads
body
+With the global rules phase split, client-control runs in route rewrite
+(setting FFI override to 50MB) before the global rule access phase reads
+the body. The body exceeds test-nginx's hardcoded 30M but is within the
+50MB override, so the request should succeed.
+--- upstream_server_config
+ client_max_body_size 0;
+--- request eval
+"POST /hello\n" . "A" x (31 * 1024 * 1024)
+--- error_code: 200
+--- timeout: 30
+
+
+
+=== TEST 10: remove client-control from route
+--- upstream_server_config
+ client_max_body_size 0;
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/routes/1',
+ ngx.HTTP_PUT,
+ [[{
+ "uri": "/hello",
+ "upstream": {
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:1980": 1
+ }
+ }
+ }]]
+ )
+ if code >= 300 then
+ ngx.status = code
+ ngx.say(body)
+ return
+ end
+ ngx.say("passed")
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed
+
+
+
+=== TEST 11: without client-control, body exceeds hardcoded 30M limit
+Without client-control the FFI override is not set, so the global rule's
+read_body() triggers the hardcoded 30M body size check. The same body
+that succeeded in TEST 9 now gets rejected with 413.
+--- upstream_server_config
+ client_max_body_size 0;
+--- request eval
+"POST /hello\n" . "A" x (31 * 1024 * 1024)
+--- error_code: 413
+--- error_log
+client intended to send too large body
+--- timeout: 30
+
+
+
+=== TEST 12: cleanup global rules
+--- upstream_server_config
+ client_max_body_size 0;
+--- config
+ location /t {
+ content_by_lua_block {
+ local t = require("lib.test_admin").test
+ local code, body = t('/apisix/admin/global_rules/1',
ngx.HTTP_DELETE)
+ ngx.say(body)
+ }
+ }
+--- request
+GET /t
+--- response_body
+passed