This is an automated email from the ASF dual-hosted git repository.
nic-6443 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 75bb63148 fix(proxy-mirror): keep the original method path when
mirroring gRPC requests (#13499)
75bb63148 is described below
commit 75bb63148a9f281cac03f12ab75380350d9c7a4b
Author: Nic <[email protected]>
AuthorDate: Wed Jun 10 14:31:17 2026 +0800
fix(proxy-mirror): keep the original method path when mirroring gRPC
requests (#13499)
---
apisix/cli/ngx_tpl.lua | 2 ++
apisix/core/ctx.lua | 1 +
apisix/plugins/proxy-mirror.lua | 15 +++++++++++
t/APISIX.pm | 2 ++
t/plugin/proxy-mirror3.t | 58 ++++++++++++++++++++++++++++++++++++++---
5 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/apisix/cli/ngx_tpl.lua b/apisix/cli/ngx_tpl.lua
index 9603eae46..ca6642b9a 100644
--- a/apisix/cli/ngx_tpl.lua
+++ b/apisix/cli/ngx_tpl.lua
@@ -794,6 +794,7 @@ http {
location / {
set $upstream_mirror_host '';
set $upstream_mirror_uri '';
+ set $upstream_mirror_grpc_path '';
set $upstream_upgrade '';
set $upstream_connection '';
@@ -1046,6 +1047,7 @@ http {
grpc_send_timeout {* proxy_mirror_timeouts.send *};
{% end %}
{% end %}
+ rewrite ^ $upstream_mirror_grpc_path break;
grpc_pass $upstream_mirror_host;
}
{% end %}
diff --git a/apisix/core/ctx.lua b/apisix/core/ctx.lua
index 92a57629d..07b30d7dd 100644
--- a/apisix/core/ctx.lua
+++ b/apisix/core/ctx.lua
@@ -207,6 +207,7 @@ do
upstream_mirror_host = true,
upstream_mirror_uri = true,
+ upstream_mirror_grpc_path = true,
upstream_cache_zone = true,
upstream_cache_zone_info = true,
diff --git a/apisix/plugins/proxy-mirror.lua b/apisix/plugins/proxy-mirror.lua
index d6cede6e9..8ffb534ec 100644
--- a/apisix/plugins/proxy-mirror.lua
+++ b/apisix/plugins/proxy-mirror.lua
@@ -16,6 +16,7 @@
--
local core = require("apisix.core")
local url = require("net.url")
+local ngx = ngx
local math_random = math.random
local has_mod, apisix_ngx_client = pcall(require, "resty.apisix.client")
@@ -130,4 +131,18 @@ function _M.rewrite(conf, ctx)
end
+-- The :path of the mirrored gRPC request is taken from the subrequest URI
rewritten
+-- in the proxy_mirror_grpc location, since grpc_pass cannot carry a URI. It
must be
+-- captured after all access phase plugins (e.g. grpc-web) have rewritten the
URI,
+-- which is too late for the rewrite phase above.
+function _M.before_proxy(conf, ctx)
+ if not ctx.enable_mirror then
+ return
+ end
+
+ -- read the var directly as ctx.var.uri may be cached before the rewrite
+ ctx.var.upstream_mirror_grpc_path = ngx.var.uri
+end
+
+
return _M
diff --git a/t/APISIX.pm b/t/APISIX.pm
index 945305068..540c48f0e 100644
--- a/t/APISIX.pm
+++ b/t/APISIX.pm
@@ -889,6 +889,7 @@ _EOC_
location / {
set \$upstream_mirror_host '';
set \$upstream_mirror_uri '';
+ set \$upstream_mirror_grpc_path '';
set \$upstream_upgrade '';
set \$upstream_connection '';
@@ -1004,6 +1005,7 @@ _EOC_
}
$config .= <<_EOC_;
+ rewrite ^ \$upstream_mirror_grpc_path break;
grpc_pass \$upstream_mirror_host;
}
_EOC_
diff --git a/t/plugin/proxy-mirror3.t b/t/plugin/proxy-mirror3.t
index 2fdc90d3b..b730cc49e 100644
--- a/t/plugin/proxy-mirror3.t
+++ b/t/plugin/proxy-mirror3.t
@@ -35,6 +35,28 @@ _EOC_
$block->set_value("yaml_config", $yaml_config);
+ # a h2c server that records the mirrored gRPC request
+ my $http_config = $block->http_config // <<_EOC_;
+ server {
+ listen 19797;
+ http2 on;
+ location / {
+ content_by_lua_block {
+ ngx.req.read_body()
+ local body = ngx.req.get_body_data()
+ ngx.log(ngx.WARN, "grpc mirror server got request: path=",
+ ngx.var.request_uri,
+ " content_type=", ngx.var.http_content_type or "",
+ " body_len=", body and #body or 0)
+ ngx.header["Content-Type"] = "application/grpc"
+ ngx.exit(200)
+ }
+ }
+ }
+_EOC_
+
+ $block->set_value("http_config", $http_config);
+
if (!$block->request) {
$block->set_value("request", "POST /hello");
}
@@ -45,7 +67,6 @@ run_tests;
__DATA__
=== TEST 1: grpc mirror
---- log_level: debug
--- http2
--- apisix_yaml
routes:
@@ -68,9 +89,40 @@ routes:
#END
--- exec
grpcurl -import-path ./t/grpc_server_example/proto -proto helloworld.proto
-plaintext -d '{"name":"apisix"}' 127.0.0.1:1984 helloworld.Greeter.SayHello
+sleep 0.5
--- response_body
{
"message": "Hello apisix"
}
---- error_log eval
-qr/Connection refused\) while connecting to upstream/
+--- error_log
+grpc mirror server got request: path=/helloworld.Greeter/SayHello
content_type=application/grpc body_len=13
+
+
+
+=== TEST 2: grpc mirror keeps the URI rewritten by access phase plugins
(grpc-web)
+--- apisix_yaml
+routes:
+ -
+ id: 1
+ uris:
+ - /grpc/web/*
+ plugins:
+ grpc-web: {}
+ proxy-mirror:
+ host: grpc://127.0.0.1:19797
+ sample_ratio: 1
+ upstream:
+ scheme: grpc
+ nodes:
+ "127.0.0.1:10051": 1
+ type: roundrobin
+#END
+--- exec
+printf '\000\000\000\000\010\012\006apisix' | curl -s -o /dev/null -w
"code=%{http_code}" -X POST \
+ -H 'Content-Type: application/grpc-web+proto' --data-binary @- \
+ http://127.0.0.1:1984/grpc/web/helloworld.Greeter/SayHello
+sleep 0.5
+--- response_body chomp
+code=200
+--- error_log
+grpc mirror server got request: path=/helloworld.Greeter/SayHello
content_type=application/grpc body_len=13