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]

Reply via email to