shashank created CAMEL-25547:
--------------------------------

             Summary: camel-knative-http - a header with several values (a List 
or an array, such as a repeated request header) is sent as one header with the 
value "[a, b]", and the reply of the consumer sends only its last value
                 Key: CAMEL-25547
                 URL: https://issues.apache.org/jira/browse/CAMEL-25547
             Project: Camel
          Issue Type: Bug
          Components: camel-knative-http
            Reporter: shashank
            Assignee: shashank


{{KnativeHttpProducer.process}} (lines 105-109 at main 9432ba16d026) copies 
each message header to the request with {{toString()}}:

{code:java}
for (Map.Entry<String, Object> entry : 
exchange.getMessage().getHeaders().entrySet()) {
    if (!headerFilterStrategy.applyFilterToCamelHeaders(entry.getKey(), 
entry.getValue(), exchange)) {
        headers.add(entry.getKey(), entry.getValue().toString());
    }
}
{code}

For a header whose value is a collection this is the {{toString()}} of the 
collection, so {{List.of("a", "b")}} is sent as the single header line 
{{X-Test: [a, b]}}, and a {{String[]}} as {{[Ljava.lang.String;@7112ce6}}. The 
Knative HTTP consumer itself maps a request header that occurs more than once 
to a List ({{CollectionHelper.appendEntry}} in 
{{KnativeHttpConsumer.toMessage}}), and so do the other HTTP consumers, so a 
route that forwards such a request to a Knative sink 
({{from("knative:channel/in").to("knative:endpoint/out")}}) sends corrupted 
header values, without any error.

The reply of the consumer has a related defect. 
{{KnativeHttpConsumer.toHttpResponse}} (lines 331-344) iterates the values of a 
header, but calls {{response.putHeader(key, headerValue)}} for each of them, 
and {{putHeader}} replaces the header. A reply header with the values {{a}} and 
{{b}} is sent as {{X-Test: b}} only.

camel-http ({{HttpProducer}}), camel-netty-http and camel-undertow iterate the 
values with {{ObjectHelper.createIterator(value, null, true)}} and 
{{HttpUtil.applyHeader}} and send one header line per value, and a String as 
one value.

h3. Reproduction

New {{KnativeHttpMultiValueHeaderTest}} (the test HTTP server and the 
platform-http setup of the existing {{KnativeHttpTest}}): a message with the 
header {{X-Test}} as a List, as an array, and as a List with a null element is 
sent to a Knative sink, and the values of the header in the received request 
are compared; a consumer route sets the header to a List and the values of the 
reply header are compared; the same with a {{byte[]}} header for both. A 
seventh test (a String with a comma stays one value) passes on main too. On 
main, twice:
{noformat}
producerSendsEachValueOfAListHeader:        Expecting actual: ["[a, b]"] to 
contain exactly (and in same order): ["a", "b"]
producerSkipsTheMissingValuesOfAListHeader: Expecting actual: ["[a, null, b]"] 
to contain exactly (and in same order): ["a", "b"]
producerSendsEachValueOfAnArrayHeader:      Expecting actual: 
["[Ljava.lang.String;@..."] to contain exactly (and in same order): ["a", "b"]
consumerRepliesWithEachValueOfAListHeader:  Expecting actual: ["b"] to contain 
exactly (and in same order): ["a", "b"]
producerSendsAByteArrayHeaderAsOneValue:    Expecting actual: ["[B@1dc9f5ad"] 
to contain exactly (and in same order): ["abc"]
consumerRepliesWithAByteArrayHeaderAsOneValue: Expecting actual: ["99"] to 
contain exactly (and in same order): ["abc"]
{noformat}

h3. Proposed fix

Both places use the same value handling as the CAMEL-25526 fix: a value that is 
an {{Iterable}} or an {{Object[]}} is iterated as camel-http does 
({{ObjectHelper.createIterator}} without a delimiter and 
{{HttpUtil.applyHeader}}, which applies the header filter per value and skips 
null values) and all the values of the header are added at once. Every other 
value is one header, converted to a String with the type converter: a String 
(also one with commas) is sent as before; a {{byte[]}} is now sent as one 
String (the producer sent its {{toString()}}, {{[B@...}}, and the reply only 
its last byte as a number). A header with a null value is not sent, as before 
(the header filter drops it). 4.23 upgrade guide note added to the existing 
camel-knative section. camel-knative-http with the fix: 183 tests, 0 failures.

Not changed here: {{KnativeHttpProducer}} keeps only the last value of a 
response header that occurs more than once ({{answer.setHeader}} per entry); 
that is the opposite direction and would change the type of such a header for 
existing routes, so it is left for a separate decision.

Found by comparing the producer with its sibling in camel-vertx-http 
(CAMEL-25526), with a small Lean 4 model of the header copy: "a header is sent 
as one line per value" (camel-http) fails for {{["a", "b"]}} in the producer 
(one line {{[a, b]}}) and in the reply (only {{b}}); the producer is proved 
wrong for every list value and the reply for every list of two or more values, 
and the fix is proved to equal camel-http's behaviour for every value and to 
send the same line as before for every String. Confirmed with the real producer 
and consumer.

Affected: main, camel-4.22.x, camel-4.18.x, camel-4.14.x (same code in both 
classes, GitHub contents API).

Duplicate check (2026-10-11): JIRA text "knative" and "header" since 2024 (14; 
CAMEL-24084 and CAMEL-24803 are about the header filter and the header 
metadata, CAMEL-25526 is the camel-vertx-http ticket); GitHub open pull 
requests with "knative" in the title: none. No open pull request changes 
{{KnativeHttpProducer}} or {{KnativeHttpConsumer}}.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to