Skip to content

feat: add proxy-buffering plugin#13446

Open
AlinsRan wants to merge 22 commits into
apache:masterfrom
AlinsRan:feat/proxy-buffering-plugin-v2
Open

feat: add proxy-buffering plugin#13446
AlinsRan wants to merge 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 <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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 <223556219+Copilot@users.noreply.github.com>
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 <223556219+Copilot@users.noreply.github.com>
The sse_server.key file contains base64 data with the string 'bRe'
which codespell incorrectly flags as a misspelling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- 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 <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>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 <223556219+Copilot@users.noreply.github.com>
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 <223556219+Copilot@users.noreply.github.com>
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.

2 participants