Skip to content

feat: add proxy-buffering plugin#13446

Merged
AlinsRan merged 22 commits into
apache:masterfrom
AlinsRan:feat/proxy-buffering-plugin-v2
Jun 4, 2026
Merged

feat: add proxy-buffering plugin#13446
AlinsRan merged 22 commits into
apache:masterfrom
AlinsRan:feat/proxy-buffering-plugin-v2

Conversation

@AlinsRan

Copy link
Copy Markdown
Contributor

Summary

The proxy-buffering plugin controls nginx proxy buffering behavior per route. When proxy buffering is disabled, nginx streams responses directly to clients without buffering, which is essential for Server-Sent Events (SSE), streaming APIs, and real-time data delivery.

This plugin runs at a very high priority (21991) so it takes effect before authentication plugins, ensuring the streaming behavior is established early in the request lifecycle.

Changes

  • New plugin apisix/plugins/proxy-buffering.lua (priority: 21991)
  • Extends apisix/init.lua with a disable_proxy_buffering_access_phase() handler and routing check
  • Extends apisix/cli/ngx_tpl.lua with a @disable_proxy_buffering nginx location block (similar to @grpc_pass), wrapped in {% if enabled_plugins["proxy-buffering"] then %}
  • CLI test using make init + nginx.conf verification pattern

Example

admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')

# Configure a route with proxy buffering disabled for SSE
curl http://127.0.0.1:9180/apisix/admin/routes/1 -X PUT   -H "X-API-KEY: ${admin_key}"   -d '{
    "uri": "/events",
    "plugins": {
      "proxy-buffering": {
        "disable_proxy_buffering": true
      }
    },
    "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } }
  }'

Requests to /events will be proxied with proxy_buffering off, allowing SSE or chunked streaming responses to be delivered immediately to the client.

AlinsRan and others added 13 commits May 9, 2026 17:22
The proxy-buffering plugin controls nginx proxy buffering behavior per route.
When proxy buffering is disabled, nginx streams the response directly to the
client without buffering, which is essential for Server-Sent Events (SSE),
streaming APIs, and real-time data delivery.

This commit also extends the APISIX HTTP pipeline with a dedicated nginx
location (@disable_proxy_buffering) to handle routes where buffering should
be disabled, similar to the existing @grpc_pass and @dubbo_pass locations.

Co-authored-by: Copilot <[email protected]>
- Replace AI-generated tests with tests based on actual streaming behavior:
  - Use hello_chunked upstream (chunked streaming) instead of /hello
  - Test baseline (without plugin) then with disable_proxy_buffering=true
  - Remove redundant schema validation and duplicate route tests
- Update English doc: add canonical link, simplify description (remove
  internal priority detail), restructure to Examples format with named
  subsection
- Update Chinese doc: add canonical link

Co-authored-by: Copilot <[email protected]>
Replace AI-generated Test::Nginx integration tests with the enterprise
edition test scripts. Keep only schema validation tests in .t file.

- t/plugin/test_proxy_buffering.sh: shell script that tests SSE streaming
  via real HTTP requests against a running APISIX instance
- t/test_sse.py: Python SSE server + client used by the shell script
- t/certs/sse_server.crt, sse_server.key: TLS certs for SSL SSE test

Co-authored-by: Copilot <[email protected]>
The sse_server.key file contains base64 data with the string 'bRe'
which codespell incorrectly flags as a misspelling.

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
- Re-add 'bre' to .ignore_words to fix codespell CI failure (the
  previous commit incorrectly removed it)
- Add Apache license headers to t/test_sse.py and
  t/plugin/test_proxy_buffering.sh to fix check-license CI failure
- Rename Response subclass to SseResponse to avoid shadowing
  aiohttp.web.Response import
- Use headers.pop() instead of del to safely remove X-Accel-Buffering
  header even if absent
- Add --fail-with-body to curl wrapper so Admin API errors fail the test
- Add additionalProperties = false to plugin schema for stricter validation
- Fix Required column value from 'False' to 'No' in docs attribute table

Co-authored-by: Copilot <[email protected]>
The previous test_proxy_buffering.sh incorrectly tried to call the Admin
API directly, which fails because CLI tests run without a live APISIX
instance.

Rewrite the test to follow the standard CLI test pattern (see
test_dubbo.sh for reference): write conf/config.yaml, run 'make init',
and verify the generated nginx.conf contains the expected directives.

Also verify that the `@disable_proxy_buffering` location block is NOT
generated when the plugin is not in the enabled plugins list.

Remove t/cli/test_sse.py as it belongs to a live integration test
context, not the CLI test framework.
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels May 28, 2026
Replace the live test_sse.py (which required a running APISIX instance)
with a proper Test::Nginx .t test that:

- Validates plugin schema (valid config, default values, invalid types)
- Spins up an in-process SSE mock upstream using ngx.flush(true) to
  simulate real event-stream responses
- Verifies SSE events are correctly proxied through when
  disable_proxy_buffering = true
- Verifies responses are still delivered when disable_proxy_buffering = false
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels May 28, 2026
Foo Bar added 4 commits May 28, 2026 09:47
- Restore test_sse.py with SseResponse class (avoids name collision with
  aiohttp's Response) and use .pop() instead of del for header removal
- Rewrite test_proxy_buffering.sh to follow EE format: make run + Admin
  API route setup + real SSE streaming verification
- Add Python dependencies (aiohttp, aiohttp-sse, sseclient) to CI runner
- Remove mock .t test in favor of real SSE functional test
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels May 28, 2026
Foo Bar and others added 4 commits May 28, 2026 12:35
Without this, stale routes from this test would remain in etcd after
make stop, causing subsequent tests (test_snippet.sh) to pick them up
when they restart APISIX, leading to unexpected 502 responses.

Co-authored-by: Copilot <[email protected]>
Align with EE test coverage:
- Add SSL test scenario: create SSL resource, route with HTTPS upstream,
  verify SSE streaming works end-to-end over TLS
- Add t/certs/server.crt and t/certs/server.key (CN=localhost) for
  APISIX SSL termination in the SSL test
- Add ssl argument support to test_sse.py: server starts with TLS using
  sse_server.crt/key, client connects to https://localhost:9443 with
  verify=False

Co-authored-by: Copilot <[email protected]>
@AlinsRan
AlinsRan merged commit 0048226 into apache:master Jun 4, 2026
22 checks passed
@AlinsRan
AlinsRan deleted the feat/proxy-buffering-plugin-v2 branch June 4, 2026 06:28
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

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants