allthingssecurity opened a new pull request, #26931:
URL: https://github.com/apache/camel/pull/26931

   # Description
   
   [CAMEL-25051](https://issues.apache.org/jira/browse/CAMEL-25051)
   
   Since 4.21 (CAMEL-23691, #23766), `CaseInsensitiveMap.put` stores a 
registered "known" key in place of the caller's key when the two are equal 
ignoring case (`deduplicateKey` uses `equalsIgnoreCase`). 
`DefaultHeadersMapFactory` registers every `Exchange` constant, among them 
`Content-Type`, `Content-Length`, `Content-Encoding`, `Transfer-Encoding`, 
`breadcrumbId` and all `Camel*` names. So a message header set or received as 
`content-type` is stored, iterated and sent on as `Content-Type`, and 
`camelfilename` becomes `CamelFileName`:
   
   ```
   template.sendBodyAndHeader("direct:in", body, "ce_id", "1") to
   from("direct:in").setHeader("content-type", 
...).setHeader("transfer-encoding", ...)
       .setHeader("breadcrumbid", ...).setHeader("x-trace", ...).to("mock:out")
   
   header names at mock:out on main: [ce_id, Content-Type, Transfer-Encoding, 
breadcrumbId, x-trace]
   expected (as with the TreeMap up to 4.20): [ce_id, content-type, 
transfer-encoding, breadcrumbid, x-trace]
   ```
   
   The key case was meant to be kept:
   - The class javadoc says "A map that uses case insensitive keys, but 
preserves the original key cases" (CAMEL-8095).
   - #23766 lists under "After": "Original key case preserved (first-put case 
wins, same as before)", and presents the deduplication as replacing 
deserialized keys that match a known constant with the canonical interned 
reference, which is a memory optimisation for equal strings.
   
   The `registerKnownKeys` javadoc and 
`CaseInsensitiveMapTest.testKnownKeyDeduplication` do describe the case 
folding. Neither the JIRA, the PR review nor the 4.21 upgrade guide mention it, 
though, and it changes header names on the wire. Kafka is one example: 
`KafkaRecordProcessor` puts the record header names into the message as they 
are, and `KafkaProducer.getPropagatedHeaders` sends `entry.getKey()`. So a 
Kafka to Kafka route now forwards a `content-type` record header as 
`Content-Type`, and Kafka header names are case-sensitive. The same applies to 
other transports whose header names are case-sensitive. I checked the Kafka 
path in the code but did not run it against a broker. The camel-4.18.x and 
4.14.x branches still use the `TreeMap` and are not affected.
   
   This change:
   - `deduplicateKey` reuses the canonical key only when it is `equals` to the 
caller's key. That keeps the full memory benefit. A key that differs in case 
could never share the canonical `String` instance anyway, and the hash is 
computed from the key, so no other state depends on it. Lookups stay 
case-insensitive, and the first put still decides the key case.
   - The `registerKnownKeys` javadoc now says "equal" and that the key case is 
kept.
   - Upgrade guide 4.23: a short section saying that header names keep their 
case again, as in 4.20.
   
   Compatibility: from 4.21 to 4.22, some case-sensitive checks on header names 
started to match lower-case names by accident. With this change they go back to 
their 4.20 behaviour:
   - `CxfHeaderHelper.propagateCamelToCxf` checks 
`Exchange.CONTENT_TYPE.equals(entry.getKey())` and maps the name through a 
case-sensitive `HashMap`. A header set as `content-type` is therefore dropped 
by the CXF header filter again, as in 4.20, instead of being used as the CXF 
message content type.
   - `NettyHttpProducer.removeCamelHeaders` uses `key.startsWith("Camel")`, so 
a response header named `camelfoo` is kept again.
   
   Neither is a security filter: `DefaultHeaderFilterStrategy` matches `Camel*` 
case-insensitively. I did not run the camel-cxf or camel-netty-http tests. 
Their dependencies (CXF, Jetty, Spring, several Camel components) are not in my 
offline repository, so I left them to CI.
   
   Tests:
   - `CaseInsensitiveMapTest.testKnownKeyDeduplication`: the second half now 
asserts that `put("camelcharsetname")` keeps `camelcharsetname`, and that 
`get("CamelCharsetName")` still finds it. The first half is unchanged: a `new 
String("CamelCharsetName")` is still replaced by the canonical instance.
   - New `CaseInsensitiveMapTest.testKnownKeyKeepsKeyCase`: `content-type` and 
`camelfilename` keep their case in `keySet()`, `entrySet()` and a copy. 
`get("Content-Type")` and `get("CONTENT-TYPE")` work. A later 
`put("Content-Type", ...)` only replaces the value (first put wins). `new 
String("Content-Type")` still shares the canonical instance (`assertSame`).
   - Both tests restore the global known keys 
(`ExchangeConstantProvider.values()`) in a `finally`, so they no longer leave a 
3-key table for the rest of the surefire fork.
   - New `DefaultMessageHeaderTest.testKnownHeaderNameKeepsKeyCase`: through a 
real message and its copy.
   
   Without the change in `CaseInsensitiveMap`, the three tests fail (for 
example `expected: <[content-type, camelfilename, Content-Length]> but was: 
<[Content-Type, CamelFileName, Content-Length]>`). With it, 
`*CaseInsensitive*,*Header*,*Message*,*Simple*,*Exchange*,*Mock*,*Copy*` in 
camel-util, camel-support, camel-core and camel-console pass: 1422 tests, 0 
failures.
   
   Found with a Lean model of `put` with the known-key table. It shows that 
`put` stores another key whenever a known key matches the given one ignoring 
case but differs from it, and it proves that the exact-match variant always 
stores the caller's key. I then reproduced the renaming against the real 
classes and a route, with a `TreeMap(CASE_INSENSITIVE_ORDER)` as the 4.20 
control.
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL) filed for 
the change (usually before you start working on it).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
     (I built and tested the affected modules, including the formatter and 
import-sort plugins. I did not run the full root build.)
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
     This PR was prepared with Claude Code (Claude Opus 5.5). The commit 
carries a `Co-Authored-By` trailer.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


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