Aias00 opened a new issue, #7807:
URL: https://github.com/apache/shenyu/issues/7807

   ### Is there an existing issue for this?
   
   - [x] I searched existing issues for ClusterForwardFilter, chunked responses 
and Transfer-Encoding. Previous data-plane response/header issues such as #5788 
and #1861 are different paths; this report concerns Admin follower-to-master 
HTTP forwarding.
   
   ### Current Behavior
   
   An authenticated HTTP API request sent to an Admin **follower** with 
`Connection: close` receives `Transfer-Encoding: chunked` but an unchunked JSON 
body. Python urllib raises `http.client.IncompleteRead` while trying to parse 
the opening JSON bytes as a chunk size.
   
   The same request sent directly to the master is correctly chunk-encoded. 
With keep-alive, the follower body is chunk-encoded but the response contains 
duplicate Transfer-Encoding headers and duplicated Connection values.
   
   This was verified from raw TCP response bytes, not inferred solely from a 
client exception.
   
   ### Expected Behavior
   
   A follower should emit a valid HTTP response independently of the 
master-to-follower connection framing. Transfer-Encoding and Connection must 
describe the follower-to-client hop. Both close and keep-alive clients should 
be able to read the complete JSON response.
   
   ### Steps To Reproduce
   
   1. Start two Admin nodes in JDBC cluster mode against the same MySQL 
database, with matching JWT and cluster event secrets.
   2. Identify the elected master and follower. Obtain a normal Admin login 
token.
   3. Send the same authenticated GET to each node:
   
   ```python
   import os
   import urllib.request
   
   for port in (9095, 9096):
       req = urllib.request.Request(
           
f"http://127.0.0.1:{port}/selector?pluginId=5&currentPage=1&pageSize=100";,
           headers={
               "X-Access-Token": os.environ["ADMIN_TOKEN"],
               "Connection": "close",
           },
       )
       with urllib.request.urlopen(req, timeout=10) as response:
           print(port, response.status, 
response.headers.get("Transfer-Encoding"))
           print(len(response.read()))
   ```
   
   4. The master's response is readable; reading the follower response raises 
IncompleteRead. A matching request using keep-alive may be readable but still 
exhibits duplicated framing headers.
   
   ### Raw Response Evidence
   
   With master on port 9095 and follower on port 9096 in this run:
   
   | Node / request | Response headers | Raw body prefix |
   | --- | --- | --- |
   | master / close | `Transfer-Encoding: chunked`, `Connection: close` | 
`5fb\r\n{"code":200,...}` |
   | follower / close | `Transfer-Encoding: chunked`, `Connection: close` | 
`{"code":200,...}` — no chunk-size line |
   | follower / keep-alive | two `Transfer-Encoding: chunked` header lines; 
`Connection: keep-alive, keep-alive` | `5fb\r\n{"code":200,...}` |
   
   The chunk size above depends on the selector data present. The relevant 
invariant is that the follower/close response advertises chunked encoding 
without transmitting it.
   
   ### Environment
   
   - ShenYu `2.7.2-SNAPSHOT`, master `04f7c2e8b7d282f14510157a9ab0bfaab538ecac`
   - Java 17, ARM64 Linux Docker containers, MySQL 8.0
   - Two Admin nodes with JDBC cluster mode enabled
   
   ### Code Evidence / Suggested Scope
   
   
[ClusterForwardFilter.java](https://github.com/apache/shenyu/blob/04f7c2e8b7d282f14510157a9ab0bfaab538ecac/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/cluster/filter/ClusterForwardFilter.java#L132-L149):
   
   1. Line 138 buffers the master response as `ResponseEntity<byte[]>`; the 
HTTP client has already decoded the master's transfer framing.
   2. Line 143 copies the master response headers to the follower Servlet 
response.
   3. Line 148 writes the decoded byte array to the Servlet output stream.
   4. [The header-copy 
helper](https://github.com/apache/shenyu/blob/04f7c2e8b7d282f14510157a9ab0bfaab538ecac/shenyu-admin/src/main/java/org/apache/shenyu/admin/mode/cluster/filter/ClusterForwardFilter.java#L182-L199)
 does not filter hop-by-hop/framing headers.
   
   Please filter connection-specific/framing headers at the forwarding boundary 
and let the Servlet container produce the client-facing framing. Review 
request-side forwarding as well. Preserve ordinary representation headers and 
ensure Content-Length, if set, matches the actual forwarded bytes.
   
   Regression coverage should include real HTTP master/follower responses for 
both Connection modes. A mocked Servlet output stream alone will not exercise 
Tomcat's framing decisions.
   


-- 
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