Skip to content

feat: add proxy-buffering plugin#13348

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

feat: add proxy-buffering plugin#13348
AlinsRan wants to merge 12 commits into
apache:masterfrom
AlinsRan:feat/proxy-buffering-plugin

Conversation

@AlinsRan
Copy link
Copy Markdown
Contributor

@AlinsRan AlinsRan commented May 9, 2026

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)

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.

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>
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels May 9, 2026
AlinsRan and others added 2 commits May 9, 2026 17:39
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:L This PR changes 100-499 lines, ignoring generated files. labels May 12, 2026
Foo Bar and others added 2 commits May 12, 2026 16:05
- 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>
@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 12, 2026
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>
@shreemaan-abhishek
Copy link
Copy Markdown
Contributor

license check failing

@AlinsRan AlinsRan requested a review from Copilot May 13, 2026 08:04
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new proxy-buffering plugin to disable Nginx proxy buffering on a per-route basis (primarily to support SSE/streaming), wiring it into APISIX request handling and the Nginx template, plus adding docs and tests.

Changes:

  • Introduces proxy-buffering plugin (priority 21991) that flags requests to be proxied with proxy_buffering off.
  • Routes flagged requests to a dedicated named Nginx location (@disable_proxy_buffering) via apisix/init.lua + apisix/cli/ngx_tpl.lua.
  • Adds docs (EN/ZH) and adds tests/scripts for schema validation and SSE verification.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
t/test_sse.py Adds a Python SSE upstream + client used for manual/integration validation.
t/plugin/test_proxy_buffering.sh Adds a shell-based flow configuring routes/SSLs and running SSE checks.
t/plugin/proxy-buffering.t Adds schema validation tests for the new plugin.
t/certs/sse_server.key Adds TLS private key for the SSE upstream used in tests.
t/certs/sse_server.crt Adds TLS certificate for the SSE upstream used in tests.
t/admin/plugins.t Registers proxy-buffering in admin plugin listing tests.
t/APISIX.pm Adds a named Nginx location for disabling proxy buffering in test harness template.
docs/zh/latest/plugins/proxy-buffering.md Adds Chinese documentation for proxy-buffering.
docs/zh/latest/config.json Adds doc nav entry for plugin (ZH).
docs/en/latest/plugins/proxy-buffering.md Adds English documentation for proxy-buffering.
docs/en/latest/config.json Adds doc nav entry for plugin (EN).
conf/config.yaml.example Adds plugin to default plugin list with priority comment.
apisix/plugins/proxy-buffering.lua Implements the proxy-buffering plugin and schema.
apisix/init.lua Adds upstream routing to @disable_proxy_buffering and a ctx-restoring access handler.
apisix/cli/ngx_tpl.lua Adds @disable_proxy_buffering named location when plugin enabled.
apisix/cli/config.lua Adds plugin to default enabled plugins list.
.ignore_words Adds an ignored word entry.

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

Comment thread t/certs/sse_server.key
Comment thread t/test_sse.py Outdated
Comment thread t/test_sse.py Outdated
Comment thread t/test_sse.py Outdated
Comment thread t/plugin/test_proxy_buffering.sh Outdated
Comment thread t/APISIX.pm
Comment thread apisix/cli/ngx_tpl.lua
Comment thread apisix/plugins/proxy-buffering.lua
Comment thread docs/en/latest/plugins/proxy-buffering.md Outdated
Comment thread .ignore_words
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
AlinsRan and others added 5 commits May 14, 2026 04:09
- 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>
Copy link
Copy Markdown
Contributor Author

@AlinsRan AlinsRan left a comment

Choose a reason for hiding this comment

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

CI Failure Analysis

The two failing CI jobs (linux_apisix_current_luarocks and linux_apisix_current_luarocks_in_customed_nginx) fail with:

curl: (7) Failed to connect to 127.0.0.1 port 9180 after 0 ms: Could not connect to server

Root Cause

The CLI test framework in t/cli/ does not run APISIX as a live server. Looking at the runner script (ci/linux_apisix_current_luarocks_runner.sh), it:

  1. Starts and immediately stops APISIX (apisix start / stop) to verify startup
  2. Then runs all t/cli/test_*.sh scripts — these are nginx.conf generation tests, not live API tests

The correct pattern (see t/cli/test_dubbo.sh for reference) is:

. ./t/cli/common.sh

echo 'plugins:
    - proxy-buffering' > conf/config.yaml

make init

if ! grep "location @disable_proxy_buffering" conf/nginx.conf > /dev/null; then
    echo "failed: @disable_proxy_buffering location not found in nginx.conf"
    exit 1
fi
echo "passed: @disable_proxy_buffering location found when proxy-buffering is enabled"

Suggested Fix

t/cli/test_proxy_buffering.sh should be rewritten to use the make init + nginx.conf grep pattern. The t/cli/test_sse.py live SSE test does not belong in the CLI test directory.

Here is the corrected t/cli/test_proxy_buffering.sh:

#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

. ./t/cli/common.sh

# test: @disable_proxy_buffering location is generated when proxy-buffering is enabled
echo '
plugins:
    - proxy-buffering
' > conf/config.yaml

make init

if ! grep "location @disable_proxy_buffering" conf/nginx.conf > /dev/null; then
    echo "failed: @disable_proxy_buffering location not found in nginx.conf when proxy-buffering is enabled"
    exit 1
fi
echo "passed: @disable_proxy_buffering location found when proxy-buffering is enabled"

if ! grep "proxy_buffering off" conf/nginx.conf > /dev/null; then
    echo "failed: proxy_buffering off not found in nginx.conf"
    exit 1
fi
echo "passed: proxy_buffering off found in nginx.conf"

# test: @disable_proxy_buffering location is NOT generated when proxy-buffering is not enabled
echo '
plugins: []
' > conf/config.yaml

make init

if grep "location @disable_proxy_buffering" conf/nginx.conf > /dev/null; then
    echo "failed: @disable_proxy_buffering location should not be in nginx.conf when proxy-buffering is not enabled"
    exit 1
fi
echo "passed: @disable_proxy_buffering location not generated when proxy-buffering is not enabled"

The t/cli/test_sse.py file and its reference in the test script should be removed from this PR — live SSE integration testing would be better placed in a .t test file if needed.

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.

4 participants