sawyerward opened a new issue, #14009: URL: https://github.com/apache/apisix/issues/14009
### Current behavior On a route whose upstream scheme is `grpc`, the `client-control` plugin does not change the request body limit. APISIX applies the global `nginx_config.http.client_max_body_size` instead. With a global limit of `1m` and `client-control` set to `max_body_size: 5242880`, a 2 MiB gRPC request gets a 413. The same plugin config on an HTTP route accepts the 2 MiB request. Lowering the limit fails too when the client doesn't send Content-Length, which is common for gRPC clients. A gRPC route with `max_body_size: 524288` accepts a streamed 900 KiB request. Possible cause: for gRPC upstreams, APISIX hands the request to `@grpc_pass` with `ngx.exec` ([init.lua#L674](https://github.com/apache/apisix/blob/3.18.0/apisix/init.lua#L674)). nginx clears module contexts on a named-location redirect, so the value that `client-control` stores with `set_client_max_body_size()` ([client-control.lua#L67](https://github.com/apache/apisix/blob/3.18.0/apisix/plugins/client-control.lua#L67)) is gone. `grpc_access_phase()` re-applies the mirror setting after the redirect but not the body size ([init.lua#L972-L989](https://github.com/apache/apisix/blob/3.18.0/apisix/init.lua#L972-L989)). When `grpc_pass` reads the body, [`ngx_http_apisix_client_max_body_size()`](https://github.com/api7/apisix-nginx-module/blob/84eac096e2250d3e79a8388fba80141ddb005e37/src/ngx_http_apisix_module.c#L462-L479) finds no override and falls back to the global value. The plugin's own Content-Length check runs before the redirect, which is why lowering the limit works only when Content-Length is present. ### Expected behavior `client-control` sets the body limit on gRPC routes the same way it does on HTTP routes. ### Error logs ``` [error] 51#51: *152246813 client intended to send too large chunked body: 1081325 bytes while sending request to upstream, client: 10.38.x.x ``` ``` "POST /vector.Vector/PushEvents HTTP/2.0" 413 255 0.000 "-" "curl/8.11.1" - - - "grpc://..." ``` ### Steps to reproduce 1. Set the global limit in `config.yaml`: ```yaml nginx_config: http: client_max_body_size: 1m ``` 2. Create one gRPC route and one HTTP route, both with `client-control` at 5 MiB. Any gRPC server that reads the full message works as the backend. We used Vector's `vector` source. ```bash curl http://127.0.0.1:9180/apisix/admin/routes/grpc -H "X-API-KEY: $admin_key" -X PUT -d '{ "uri": "/*", "host": "grpc.test", "plugins": {"client-control": {"max_body_size": 5242880}}, "upstream": {"scheme": "grpc", "type": "roundrobin", "nodes": {"127.0.0.1:50051": 1}} }' curl http://127.0.0.1:9180/apisix/admin/routes/http -H "X-API-KEY: $admin_key" -X PUT -d '{ "uri": "/*", "host": "http.test", "plugins": {"client-control": {"max_body_size": 5242880}}, "upstream": {"type": "roundrobin", "nodes": {"127.0.0.1:8080": 1}} }' ``` 3. Send a 2 MiB body to each route. The gRPC request streams the body without Content-Length. ```bash python3 -c "import sys; n=2*1024*1024-5; sys.stdout.buffer.write(b'\x00'+n.to_bytes(4,'big')+b'\x00'*n)" > grpc_2m.bin head -c 2097152 /dev/zero > body_2m.bin curl -s -o /dev/null -w '%{http_code}\n' --http2-prior-knowledge -H 'Host: grpc.test' \ -H 'content-type: application/grpc' -H 'te: trailers' \ -X POST -T - http://127.0.0.1:9080/test.Service/Method < grpc_2m.bin # 413 curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: http.test' \ --data-binary @body_2m.bin http://127.0.0.1:9080/ # 200 ``` Our full results with the global limit at 1 MiB: | Route | 900 KiB | 2 MiB | 6 MiB | |---|---|---|---| | HTTP, no plugin | 200 | 413 | 413 | | HTTP, `client-control` 5 MiB | 200 | 200 | 413 | | HTTP, `client-control` 512 KiB | 413 | 413 | 413 | | gRPC, no plugin | 200 | 413 | 413 | | gRPC, `client-control` 5 MiB | 200 | 413 | 413 | | gRPC, `client-control` 512 KiB, with Content-Length | 413 | 413 | 413 | | gRPC, `client-control` 512 KiB, no Content-Length | 200 | 413 | 413 | HTTP results were the same with Content-Length and with chunked bodies. We hit this through apisix-ingress-controller 2.2.0 in standalone mode, with the plugin set both inline on an `ApisixRoute` and through an `ApisixPluginConfig`. ### Environment - APISIX version: 3.18.0 (`apache/apisix:3.18.0-ubuntu`) - Operating system: Ubuntu 24.04.4 LTS, Linux 6.8.0-1064-azure aarch64 - OpenResty / Nginx version: openresty/1.29.2.4 - etcd version: not used (standalone mode, apisix-ingress-controller 2.2.0) - APISIX Dashboard version: not used - Plugin runner version: not used - LuaRocks version: not used -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
