Skip to content

feat(proxy-rewrite): support NGINX variables in regex_uri - #13800

Draft
ruanweihong-aaa wants to merge 2 commits into
apache:masterfrom
ruanweihong-aaa:feat/proxy-rewrite-regex-uri-nginx-vars
Draft

feat(proxy-rewrite): support NGINX variables in regex_uri#13800
ruanweihong-aaa wants to merge 2 commits into
apache:masterfrom
ruanweihong-aaa:feat/proxy-rewrite-regex-uri-nginx-vars

Conversation

@ruanweihong-aaa

Copy link
Copy Markdown

Description

Currently, proxy-rewrite.uri supports NGINX variables, but the replacement part of proxy-rewrite.regex_uri only supports regular expression captures such as $1 and $2.

This PR adds NGINX variable resolution support to regex_uri replacements.

For example:

{
  "regex_uri": [
    "^/api/(.*)/users/(.*)$",
    "/v2/$1/external-users/$arg_name"
  ]
}

The replacement is processed in explicit stages:

  1. Preserve literal dollar expressions such as $$x.
  2. Resolve regular expression captures such as $1 and $2.
  3. Resolve NGINX variables such as $arg_name and variables stored in ctx.var.
  4. Restore literal dollar expressions.

Schema validation also distinguishes NGINX variables from regular expression captures. This allows a route containing both forms to be saved successfully.

Existing $1 and $2 capture behavior and $$ literal-dollar behavior remain compatible.

The English and Chinese proxy-rewrite documentation have also been updated to describe the new behavior.

Which issue(s) this PR fixes:

Fixes #13140

Tests

  • Added t/plugin/proxy-rewrite4.t.
  • Verified regular expression captures and NGINX variables in the same replacement.
  • Verified existing $1 and $2 capture behavior.
  • Verified literal $$x behavior.
  • t/plugin/proxy-rewrite4.t: 6/6 passed.
  • luacheck: 0 warnings and 0 errors for the modified Lua file.
  • lj-releng: passed for the modified Lua file.
  • Source-matched Docker BEFORE/AFTER verification: all six checks passed.

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible

Comment on lines +250 to +257
if test_replacement ~= "" then
local err
test_replacement, err = escape_nginx_vars(test_replacement)
if err then
return false, "invalid regex_uri replacement(" ..
replacement .. "): " .. err
end
end

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.

Why is this needed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is needed because check_schema compiles the replacement with ngx.re.sub to validate both the regex pattern and replacement syntax.

Without escaping NGINX variables, $arg_name is interpreted as a named PCRE capture and route creation fails with "failed to compile the replacement template", which is the original issue.

Escaping only NGINX variable markers as $$ allows PCRE to keep validating regular captures such as $1 and invalid replacement syntax, while treating $arg_name as a literal during schema validation. The existing invalid replacement test also depends on this validation.

I can add a comment here to explain this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Added an inline comment in d1b362b explaining that schema validation must keep validating PCRE captures without treating NGINX variables as named captures.

Comment on lines +388 to +395
local replacement = preserve_literal_dollars(conf.regex_uri[i + 1])
replacement = core.utils.resolve_var_with_captures(replacement, captures)
replacement = core.utils.resolve_var(replacement, ctx.var, escape_separator)
replacement = restore_literal_dollars(replacement)
local uri, _, err = re_sub(upstream_uri,
conf.regex_uri[i], conf.regex_uri[i + 1], "jo")
conf.regex_uri[i], function()
return replacement
end, "jo")

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.

It's too complicated. Is there a simpler way?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The additional steps preserve three different replacement semantics:

  1. $1 and $2 are regex captures.
  2. $arg_name and values from ctx.var are NGINX variables.
  3. $$x must remain the literal string $x.

Resolving NGINX variables directly would also consume $1, while letting ngx.re.sub process the replacement after variable resolution could interpret dollar signs from resolved values again.

I also tested resolving variables after ngx.re.sub, but at that point an escaped NGINX variable and an existing literal $$x both become $..., so they can no longer be distinguished.

I can move these stages into a focused helper to make the rewrite path easier to read. Would that address the concern, or would you prefer a different replacement behavior for literal $$?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: As a user, I want to use Nginx variables in proxy-rewrite.regex_uri

2 participants