Skip to content

fix: split global rules phase execution for client-control compatibility#13345

Merged
nic-6443 merged 8 commits into
apache:masterfrom
nic-6443:fix/split-global-rules-phase
May 11, 2026
Merged

fix: split global rules phase execution for client-control compatibility#13345
nic-6443 merged 8 commits into
apache:masterfrom
nic-6443:fix/split-global-rules-phase

Conversation

@nic-6443

@nic-6443 nic-6443 commented May 9, 2026

Copy link
Copy Markdown
Member

Description

Split global rules phase execution so that route/service rewrite plugins run between global rule rewrite and global rule access phases. This fixes a conflict where client-control (route rewrite, priority 22000) cannot set the nginx body size override via FFI before a global rule access-phase plugin (e.g., a logger with include_req_body) calls ngx.req.read_body().

Before

global_rules rewrite + access → route/service rewrite → route/service access

A global rule logger reading the body in access phase triggers nginx body size check before client-control can set the FFI override → 413.

After

global_rules rewrite → route/service rewrite → global_rules access → route/service access

client-control sets the FFI override in route rewrite, then the global rule access phase reads the body using the overridden limit.

Changes

  • apisix/init.lua: Split run_global_rules(nil) into separate "rewrite" and "access" calls in both the no-route and matched-route paths
  • apisix/plugin.lua: Simplified run_global_rules() — removed nil phase_name handling, always uses explicit phase name with phase-specific tracer span naming
  • t/plugin/client-control.t: Added regression tests with 31MB body exceeding nginx's 30M limit, verifying client-control FFI override works when global rule reads body in access phase

Behavioral Changes

  1. Global rule access now runs after route/service rewrite (more correct phase ordering)
  2. consumer-restriction on global rule now works correctly (consumer info available after merge)
  3. If route/service rewrite terminates request (401/403), global rule access won't run — but log phase always runs

When a global rule plugin reads the request body in access phase (e.g.,
loggers with include_req_body), it triggers nginx's body size check before
client-control on route/service can set the FFI override in rewrite phase,
causing false 413 rejections.

Split run_global_rules in http_access_phase so that:
1. Global rules rewrite runs first
2. Route/service plugins rewrite runs (client-control sets FFI override)
3. Global rules access runs (body reader uses correct limit)
4. Route/service plugins access runs

Also add phase-specific tracer span names (run_global_rules.rewrite,
run_global_rules.access) for better observability.
Copilot AI review requested due to automatic review settings May 9, 2026 06:05
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels May 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an execution-order bug where global rule plugins that read the request body in access could trigger Nginx body-size checks before route/service client-control had a chance to apply its max-body-size override, causing incorrect 413 responses.

Changes:

  • Split global rules execution in http_access_phase to run global-rule rewrite before route/service rewrite, and global-rule access before route/service access.
  • Add phase-specific tracing span names for global rules (run_global_rules.rewrite / run_global_rules.access).
  • Add regression tests ensuring client-control overrides the body limit before a global rule reads the body, and that 413 still occurs without client-control.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
apisix/init.lua Reorders/splits global rule execution to interleave correctly with route/service plugin phases.
apisix/plugin.lua Adds phase-specific span naming and adjusts when api_ctx.global_rules is set.
t/plugin/client-control.t Adds tests covering the regression scenario with global rules + client-control body-size override.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apisix/init.lua
nic-6443 added 6 commits May 9, 2026 15:02
The previous approach tried to set nginx client_max_body_size via
extra_yaml_config, but test-nginx hardcodes client_max_body_size 30M
in its generated nginx.conf, ignoring the APISIX config.yaml value.

Replace with a direct phase-ordering test using grep_error_log to
verify: global-rewrite → route-rewrite → global-access → route-access.
This proves client-control (route rewrite) runs before any global rule
access-phase plugin reads the request body.
The grep_error_log regex was picking up the log marker from function
source code strings logged during config sync. Use string concatenation
('PO ' .. 'global-rewrite') so the full marker only exists at runtime,
not in the source code. Also use [a-z]+-[a-z]+ regex to avoid matching
trailing commas from nginx error log format.
…phase ordering

Replace the grep_error_log-based phase ordering tests with actual
client-control body size verification:

- TEST 9: send 1MB+1 body with client-control(10MB) on route +
  body reader on global rule access → expect 200 (FFI override works)
- TEST 11: same body without client-control → expect 413 (nginx
  default 1m limit enforced)

This tests the real bug scenario: client-control must set the FFI
body size override in route rewrite before a global rule access-phase
plugin reads the body.
The APISIX server block shares test-nginx's server block which has
client_max_body_size 30M hardcoded. Use a 31MB body to exceed this
limit, with client-control setting the FFI override to 50MB.

Also add upstream_server_config with client_max_body_size 0 so the
upstream fake server accepts the large body when proxied.
AlinsRan
AlinsRan previously approved these changes May 11, 2026
Comment thread apisix/init.lua Outdated

@membphis membphis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it time to remove route.value.script, service.value.script

those code: https://github.com/apache/apisix/pull/13345/changes#diff-7439ffe94cc4bcad12066d5d83f6c1004b8e9b3ff5097b645fa7c700eb3b3743R815-R818

anyway, this is another question, we can do it in a new PR

Split the no-route path from run_global_rules(nil) into separate
rewrite and access calls, matching the matched-route path. This
removes the nil phase_name handling from run_global_rules() entirely,
making the function simpler and the call sites consistent.
@nic-6443
nic-6443 merged commit 9ef7709 into apache:master May 11, 2026
23 checks passed
@nic-6443
nic-6443 deleted the fix/split-global-rules-phase branch May 11, 2026 08:50
wistefan pushed a commit to wistefan/apisix that referenced this pull request Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants