This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-24178 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 9388c29de5fd498917bef54222e1d9dee5623e95 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Jul 20 09:50:03 2026 +0200 CAMEL-24178: camel-cxfrs - Fix producer matrix/query parsing on legal RFC 3986 URIs Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../camel/component/cxf/jaxrs/CxfRsProducer.java | 33 ++++---- .../jaxrs/CxfRsProducerParameterParsingTest.java | 88 ++++++++++++++++++++++ 2 files changed, 103 insertions(+), 18 deletions(-) diff --git a/components/camel-cxf/camel-cxf-rest/src/main/java/org/apache/camel/component/cxf/jaxrs/CxfRsProducer.java b/components/camel-cxf/camel-cxf-rest/src/main/java/org/apache/camel/component/cxf/jaxrs/CxfRsProducer.java index 84c5e7eb7b48..642f46b2e1d2 100644 --- a/components/camel-cxf/camel-cxf-rest/src/main/java/org/apache/camel/component/cxf/jaxrs/CxfRsProducer.java +++ b/components/camel-cxf/camel-cxf-rest/src/main/java/org/apache/camel/component/cxf/jaxrs/CxfRsProducer.java @@ -267,12 +267,14 @@ public class CxfRsProducer extends DefaultAsyncProducer { = (org.apache.cxf.message.Message) exchange.getIn().getHeader(CxfConstants.CAMEL_CXF_MESSAGE); if (cxfMessage != null) { String requestURL = (String) cxfMessage.get("org.apache.cxf.request.uri"); - String matrixParam = null; - int matrixStart = requestURL.indexOf(';'); - int matrixEnd = requestURL.indexOf('?') > -1 ? requestURL.indexOf('?') : requestURL.length(); + int matrixEnd = requestURL.indexOf('?'); + if (matrixEnd < 0) { + matrixEnd = requestURL.length(); + } + int matrixStart = requestURL.substring(0, matrixEnd).indexOf(';'); Map<String, String> maps = null; if (matrixStart > 0) { - matrixParam = requestURL.substring(matrixStart + 1, matrixEnd); + String matrixParam = requestURL.substring(matrixStart + 1, matrixEnd); maps = getMatrixParametersFromMatrixString(matrixParam, ExchangeHelper.getCharsetName(exchange)); } if (maps != null) { @@ -506,13 +508,9 @@ public class CxfRsProducer extends DefaultAsyncProducer { throws UnsupportedEncodingException { for (String param : queryString.split("&")) { String[] pair = param.split("=", 2); - if (pair.length == 2) { - String name = URLDecoder.decode(pair[0], charset); - String value = URLDecoder.decode(pair[1], charset); - client.query(name, value); - } else { - throw new IllegalArgumentException("Invalid parameter, expected to be a pair but was " + param); - } + String name = URLDecoder.decode(pair[0], charset); + String value = pair.length == 2 ? URLDecoder.decode(pair[1], charset) : ""; + client.query(name, value); } } @@ -570,14 +568,13 @@ public class CxfRsProducer extends DefaultAsyncProducer { throws UnsupportedEncodingException { Map<String, String> answer = new LinkedHashMap<>(); for (String param : matrixString.split(";")) { - String[] pair = param.split("=", 2); - if (pair.length == 2) { - String name = URLDecoder.decode(pair[0], charset); - String value = URLDecoder.decode(pair[1], charset); - answer.put(name, value); - } else { - throw new IllegalArgumentException("Invalid parameter, expected to be a pair but was " + param); + if (param.isEmpty()) { + continue; } + String[] pair = param.split("=", 2); + String name = URLDecoder.decode(pair[0], charset); + String value = pair.length == 2 ? URLDecoder.decode(pair[1], charset) : ""; + answer.put(name, value); } return answer; } diff --git a/components/camel-cxf/camel-cxf-rest/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsProducerParameterParsingTest.java b/components/camel-cxf/camel-cxf-rest/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsProducerParameterParsingTest.java new file mode 100644 index 000000000000..5683f15966b0 --- /dev/null +++ b/components/camel-cxf/camel-cxf-rest/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsProducerParameterParsingTest.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.cxf.jaxrs; + +import jakarta.ws.rs.core.Response; + +import org.apache.camel.Exchange; +import org.apache.camel.ExchangePattern; +import org.apache.camel.Message; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.cxf.common.CXFTestSupport; +import org.apache.camel.test.junit6.CamelTestSupport; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class CxfRsProducerParameterParsingTest extends CamelTestSupport { + + private static final int PORT = CXFTestSupport.getPort7(); + + @Test + public void testValuelessQueryParameter() { + Exchange exchange = sendWithQuery("flag&key=value"); + + assertNull(exchange.getException(), "Should not throw on valueless query parameter"); + Response response = (Response) exchange.getMessage().getBody(); + assertEquals(204, response.getStatus()); + } + + @Test + public void testQueryParameterWithEmptyValue() { + Exchange exchange = sendWithQuery("name=&key=value"); + + assertNull(exchange.getException(), "Should not throw on empty-value query parameter"); + Response response = (Response) exchange.getMessage().getBody(); + assertEquals(204, response.getStatus()); + } + + @Test + public void testMultipleValuelessQueryParameters() { + Exchange exchange = sendWithQuery("debug&trace&verbose"); + + assertNull(exchange.getException(), "Should not throw on multiple valueless query parameters"); + Response response = (Response) exchange.getMessage().getBody(); + assertEquals(204, response.getStatus()); + } + + private Exchange sendWithQuery(String queryString) { + return context.createProducerTemplate().send( + "cxfrs://http://localhost:" + PORT + "/CxfRsProducerParameterParsingTest", + exchange -> { + exchange.setPattern(ExchangePattern.InOut); + Message inMessage = exchange.getIn(); + inMessage.setHeader(Exchange.HTTP_METHOD, "GET"); + inMessage.setHeader(Exchange.HTTP_PATH, "/CxfRsProducerParameterParsingTest/"); + inMessage.setHeader(Exchange.HTTP_QUERY, queryString); + inMessage.setHeader(Exchange.CONTENT_TYPE, "application/text"); + inMessage.setBody(null); + }); + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + fromF("undertow://http://localhost:%s/CxfRsProducerParameterParsingTest/?matchOnUriPrefix=true", PORT) + .to("mock:result"); + } + }; + } +}
