Repository navigation
Description
Current behavior
The Spring contract creates static header values with Collections.singleton, while request/template cloning copies at most the outer header map. RequestHeaderParameterProcessor then appends dynamic values to the existing value collection.
There are two manifestations of the same collection-ownership problem:
- Normal annotation-created static headers are immutable: combining one with a dynamic parameter for the same header throws before any transport request.
- If a programmatic/customized template uses mutable value collections, the shallow copy shares those values with the reusable template. Dynamic values accumulate across subsequent invocations.
Locations / reachable flow
- SpringMvcContract.java:123–129 creates singleton static values.
- RequestTemplate.java:81–103 shares/copies only the outer map.
- RequestHeaderParameterProcessor.java:49–77 mutates existing collections.
CookieValueParameterProcessor.java:54–62has the same ownership pattern, taking the collection from the template before merging it into request headers.
Reproduction / validation
Define this ordinary Spring SDK method:
@GetMapping(value = "/items", headers = "X-Mode=static")
String call(@RequestHeader("X-Mode") String value);A probe compiled the current contract, RequestTemplate, response handler and header processor. It parsed the actual interface through SpringMvcContract, then called public ShenyuClientMethodHandler.invoke() with a recording in-process transport:
BUILTIN_CONTRACT_EXCEPTION=UnsupportedOperationException TRANSPORT_CALLS=0
As a separate control, the probe changed only that parsed template's value collection to a mutable [static], then invoked with one and two:
MUTABLE_TEMPLATE_REQUESTS=[[static, one], [static, one, two]]
TEMPLATE_AFTER_CALLS=[static, one, two]
The ordinary built-in case is the first-call exception, not repeated accumulation from a built-in ArrayList. Independent review corrected that initial assumption after inspecting Collections.singleton. The executed reproduction covers the RequestHeader branch; the CookieValue counterpart is source-inspected. No external HTTP service or sensitive header values were involved.
Impact / suggested fix
Combining supported static and dynamic headers fails; mutable reusable templates can also retain prior invocation state. Give each invocation mutable, independently owned value collections before parameter processing, and avoid modifying template-owned collections in the processors. Preserve the intended append/replace contract without duplicating static cookie/header values during merges.
Related existing work
#7711 concerns response status handling, #7712 path replacement syntax, #7464 query encoding and #7465 annotation name aliases. #6254 concerns a different MCP request-template header substitution path. Complete-title/open-PR and live RequestTemplate/header/UnsupportedOperationException searches found no matching collection-ownership report.
Reviewed revision
Apache ShenYu 2.7.2-SNAPSHOT, master@66d7d954ca37d651a5ad916f988a8081157a7ab4. P2 / Medium; high confidence. Unit-test and CI issue categories excluded.
Activity
konlue commentedon Oct 17, 2026
/assign
fix(sdk): give each invocation independently owned header values (#7771…