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