Skip to content

feat: add exit-transformer plugin#13343

Merged
AlinsRan merged 12 commits into
apache:masterfrom
AlinsRan:feat/exit-transformer-plugin
May 12, 2026
Merged

feat: add exit-transformer plugin#13343
AlinsRan merged 12 commits into
apache:masterfrom
AlinsRan:feat/exit-transformer-plugin

Conversation

@AlinsRan
Copy link
Copy Markdown
Contributor

@AlinsRan AlinsRan commented May 9, 2026

Summary

This PR introduces the exit-transformer plugin and extends core/response.lua with an exit callback mechanism to support it.

Description

The exit-transformer plugin intercepts APISIX exit responses (plugin rejections, route-not-found, auth failures, etc.) and transforms them using user-defined Lua functions before the response is sent to the client.

Use cases:

  • Normalize error response format across all upstream and gateway errors
  • Remap status codes (e.g. translate 401200 for legacy clients)
  • Inject custom headers or bodies into error responses
  • Apply uniform error envelope format across all routes

Example

The following example wraps all error responses in a JSON envelope {"error": <original body>} and forces the status code to 200:

curl http://127.0.0.1:9180/apisix/admin/routes/1 \
  -H "X-API-KEY: $admin_key" -X PUT -d '{
  "uri": "/api/*",
  "plugins": {
    "key-auth": {},
    "exit-transformer": {
      "functions": [
        "return function(code, body, headers)\n  return 200, [[{\"error\":]] .. (body or \"null\") .. [[}]], headers\nend"
      ]
    }
  },
  "upstream": {
    "type": "roundrobin",
    "nodes": { "127.0.0.1:1980": 1 }
  }
}'

A request without a valid API key would now receive HTTP 200 with {"error":"Missing API key in request"} instead of the default HTTP 401.

Changes

apisix/core/response.lua

Two additions to support the callback mechanism:

  1. exit_insert_callback(func, conf) — registers a per-request callback in ngx.ctx. Each callback has the signature (code, body, headers, conf) → (code, body, headers).

  2. resp_exit with callback path — when callbacks are registered, resp_exit builds a structured (code, body, headers) representation from the variadic arguments, runs all callbacks in order, then sends the transformed response. The original variadic fast path is preserved when no callbacks are present, maintaining full backward compatibility.

apisix/plugins/exit-transformer.lua

New plugin. In the rewrite phase, each configured Lua function is registered as an exit callback. Functions are cached by an LRU cache to avoid repeated load() calls.

Files changed

  • apisix/core/response.lua — add exit_insert_callback and callback path in resp_exit
  • apisix/plugins/exit-transformer.lua — plugin implementation
  • t/plugin/exit-transformer.t — test cases
  • docs/en/latest/plugins/exit-transformer.md — English documentation
  • docs/zh/latest/plugins/exit-transformer.md — Chinese documentation
  • apisix/cli/config.lua — register plugin in default list
  • docs/en/latest/config.json / docs/zh/latest/config.json — add to sidebar

The exit-transformer plugin allows intercepting and transforming APISIX
exit responses (e.g. plugin rejections, route-not-found errors) using
user-defined Lua functions before the response is sent to the client.

Users configure a list of Lua code strings. In the rewrite phase each
function is registered as an exit callback via the new
core.response.exit_insert_callback() API. When core.response.exit() is
called anywhere in the request lifecycle, all registered callbacks
receive (code, body, headers) and may return modified values.

This PR also extends apisix/core/response.lua with:
- exit_insert_callback(func, conf): stores callbacks in ngx.ctx per request
- resp_exit: when callbacks are present, builds a structured (code, body,
  headers) representation from the variadic args, runs all callbacks, then
  sends the transformed response. Falls back to the original variadic path
  when no callbacks are registered, preserving full backward compatibility.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels May 9, 2026
AlinsRan and others added 5 commits May 9, 2026 09:12
- Add Apache license headers to exit-transformer.lua and test file
- Localize tostring to avoid luacheck global access warning in response.lua

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Pass original body (table/string) to exit callbacks before JSON encoding,
  matching EE behavior so body fields are accessible in user functions
- After callbacks, encode table bodies to JSON with trailing newline
- Change priority from 9999999 to 22950 (below real-ip/23000) to preserve
  existing t/core/config.t expectations
- Reorder exit-transformer after real-ip in config.lua and admin/plugins.t
- Fix key-auth error message: 'Missing API key found in request' was removed
  upstream; now 'Missing API key in request'

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ntions

- Rename APISIX CRD tab to APISIX Ingress Controller
- Simplify admin_key note block
- Fix attributes table format (add Valid values column)
- Fix error message in example output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:XXL This PR changes 1000+ lines, ignoring generated files. labels May 9, 2026
AlinsRan and others added 6 commits May 9, 2026 14:23
…disabled by default)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AlinsRan AlinsRan merged commit 4d4e0d4 into apache:master May 12, 2026
22 checks passed
@AlinsRan AlinsRan deleted the feat/exit-transformer-plugin branch May 12, 2026 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants