[ 
https://issues.apache.org/jira/browse/CAMEL-25547?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

shashank updated CAMEL-25547:
-----------------------------
    Description: 
Correction (2026-10-12): an earlier version of this description said camel-http 
sends one header per value. It does not: {{HttpProducer.applyHeader}} sends two 
or more values as one header {{[a, b]}} (on purpose, see CAMEL-4212 and 
CAMEL-11662). The proposed change aligns camel-knative-http with 
camel-netty-http and camel-undertow, which send one header per value; the 
statements about camel-http further down are wrong in that respect. Whether the 
producer should follow camel-http or camel-netty-http/camel-undertow is a 
maintainer decision (see the pull request). The reply keeping only the last 
value, and the byte[] and array cases, do not depend on that.

{{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._


  was:
{{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._



> 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
>            Priority: Major
>
> Correction (2026-10-12): an earlier version of this description said 
> camel-http sends one header per value. It does not: 
> {{HttpProducer.applyHeader}} sends two or more values as one header {{[a, 
> b]}} (on purpose, see CAMEL-4212 and CAMEL-11662). The proposed change aligns 
> camel-knative-http with camel-netty-http and camel-undertow, which send one 
> header per value; the statements about camel-http further down are wrong in 
> that respect. Whether the producer should follow camel-http or 
> camel-netty-http/camel-undertow is a maintainer decision (see the pull 
> request). The reply keeping only the last value, and the byte[] and array 
> cases, do not depend on that.
> {{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