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)