This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-24524 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 27a2d82c2c53c5da21ec3050e758e962cddb63be Author: Claus Ibsen <[email protected]> AuthorDate: Thu Aug 27 13:54:48 2026 +0200 CAMEL-24524: camel-util - Fix URISupport.normalizeUri fast path producing different output depending on original parameter order The fast normalizer's buildReorderingParameters() only rebuilt (and thereby encoded) the query string when the parameter keys were not already in alphabetical order. Since rebuilding was the only place encoding happened, two logically identical endpoint URIs differing only in original parameter order could normalize to different strings whenever a value needed encoding (eg a colon in a host:port value). As normalizeUri() is used to compute the endpoint registry key, this could silently create duplicate endpoints. Always rebuild the query so the result no longer depends on incidental parameter order, matching the behavior already used by the complex normalizer path. Co-Authored-By: Claude Sonnet 5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../java/org/apache/camel/util/URISupport.java | 39 ++++------- .../java/org/apache/camel/util/URISupportTest.java | 75 ++++++++++++++++++++++ 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/core/camel-util/src/main/java/org/apache/camel/util/URISupport.java b/core/camel-util/src/main/java/org/apache/camel/util/URISupport.java index 8ab513173e17..699ee24c9fbe 100644 --- a/core/camel-util/src/main/java/org/apache/camel/util/URISupport.java +++ b/core/camel-util/src/main/java/org/apache/camel/util/URISupport.java @@ -797,36 +797,21 @@ public final class URISupport { } private static String buildReorderingParameters(String scheme, String path, String query) throws URISyntaxException { - Map<String, Object> parameters = null; - if (query.indexOf('&') != -1) { - // only parse if there are parameters - parameters = URISupport.parseQuery(query, false, false); - } - - if (parameters != null && parameters.size() != 1) { - final Set<String> entries = parameters.keySet(); + Map<String, Object> parameters = URISupport.parseQuery(query, false, false); + if (parameters.size() > 1) { // reorder parameters a..z - // optimize and only build new query if the keys was resorted - boolean sort = false; - String prev = null; - for (String key : entries) { - if (prev != null) { - int comp = key.compareTo(prev); - if (comp < 0) { - sort = true; - break; - } - } - prev = key; - } - if (sort) { - final String[] array = entries.toArray(new String[0]); - Arrays.sort(array); - - query = URISupport.createQueryString(array, parameters, true); - } + // always rebuild (and thereby re-encode) the query, even if the keys were already in + // order, as rebuilding is the only place where parameter values get URL-encoded; skipping + // it would make the encoded output depend on the incidental original parameter order + final String[] array = parameters.keySet().toArray(new String[0]); + Arrays.sort(array); + query = URISupport.createQueryString(array, parameters, true); + } else { + // 0 or 1 parameter: order is not ambiguous, but still rebuild so the value is + // consistently encoded the same way as the multi-parameter case above + query = URISupport.createQueryString(parameters); } return buildUri(scheme, path, query); } diff --git a/core/camel-util/src/test/java/org/apache/camel/util/URISupportTest.java b/core/camel-util/src/test/java/org/apache/camel/util/URISupportTest.java index ac01d6891a94..311bb471bb1e 100644 --- a/core/camel-util/src/test/java/org/apache/camel/util/URISupportTest.java +++ b/core/camel-util/src/test/java/org/apache/camel/util/URISupportTest.java @@ -261,6 +261,81 @@ public class URISupportTest { assertEquals(out1, out2); } + @Test + public void testNormalizeEndpointUriOrderIndependentWithColonValue() throws Exception { + // CAMEL-24524: a value containing a colon (eg host:port) must normalize the same way + // regardless of whether the original parameter order already happened to be alphabetical + String out1 = URISupport.normalizeUri("kafka:mytopic?brokers=localhost:19092&groupId=mygroup"); + String out2 = URISupport.normalizeUri("kafka:mytopic?groupId=mygroup&brokers=localhost:19092"); + + assertThat(out1).isEqualTo(out2); + assertThat(out1).isEqualTo("kafka://mytopic?brokers=localhost%3A19092&groupId=mygroup"); + } + + @Test + public void testNormalizeEndpointUriOrderIndependentWithThreeParameters() throws Exception { + // all 6 permutations of 3 keys (one already alphabetical, some not) must normalize identically + String[] permutations = new String[] { + "kafka:mytopic?brokers=localhost:19092&groupId=mygroup&clientId=myclient", + "kafka:mytopic?brokers=localhost:19092&clientId=myclient&groupId=mygroup", + "kafka:mytopic?groupId=mygroup&brokers=localhost:19092&clientId=myclient", + "kafka:mytopic?groupId=mygroup&clientId=myclient&brokers=localhost:19092", + "kafka:mytopic?clientId=myclient&brokers=localhost:19092&groupId=mygroup", + "kafka:mytopic?clientId=myclient&groupId=mygroup&brokers=localhost:19092" }; + + String expected = "kafka://mytopic?brokers=localhost%3A19092&clientId=myclient&groupId=mygroup"; + for (String uri : permutations) { + assertThat(URISupport.normalizeUri(uri)).as("normalizing: " + uri).isEqualTo(expected); + } + } + + @Test + public void testNormalizeEndpointUriOrderIndependentSingleParameterWithColonValue() throws Exception { + // CAMEL-24524: the single-parameter shortcut must also encode the value consistently + String out = URISupport.normalizeUri("kafka:mytopic?brokers=localhost:19092"); + assertThat(out).isEqualTo("kafka://mytopic?brokers=localhost%3A19092"); + } + + @Test + public void testNormalizeEndpointUriOrderIndependentWithCommaValue() throws Exception { + // a value with a comma (safe for the fast parser, but URL-encoded when the query is + // rebuilt) must also normalize the same regardless of key order + String out1 = URISupport.normalizeUri("smtp://localhost?subject=Hello,World&username=davsclaus"); + String out2 = URISupport.normalizeUri("smtp://localhost?username=davsclaus&subject=Hello,World"); + + assertThat(out1).isEqualTo(out2); + assertThat(out1).isEqualTo("smtp://localhost?subject=Hello%2CWorld&username=davsclaus"); + } + + @Test + public void testNormalizeEndpointUriOrderIndependentIsIdempotent() throws Exception { + // normalizing an already-normalized uri must return the exact same string + String out1 = URISupport.normalizeUri("kafka:mytopic?groupId=mygroup&brokers=localhost:19092"); + String out2 = URISupport.normalizeUri(out1); + + assertThat(out2).isEqualTo(out1); + } + + @Test + public void testNormalizeEndpointUriOrderIndependentWithRawValue() throws Exception { + // RAW() values must not be further encoded, regardless of key order + String out1 = URISupport.normalizeUri("kafka:mytopic?password=RAW(p@ss:word)&username=scott"); + String out2 = URISupport.normalizeUri("kafka:mytopic?username=scott&password=RAW(p@ss:word)"); + + assertThat(out1).isEqualTo(out2); + assertThat(out1).isEqualTo("kafka://mytopic?password=RAW(p@ss:word)&username=scott"); + } + + @Test + public void testNormalizeEndpointUriOrderIndependentWithDualParametersAndColonValue() throws Exception { + // duplicate keys (list values) combined with a value that needs encoding + String out1 = URISupport.normalizeUri("smtp://localhost?to=foo:1&to=bar:2&from=me"); + String out2 = URISupport.normalizeUri("smtp://localhost?from=me&to=foo:1&to=bar:2"); + + assertThat(out1).isEqualTo(out2); + assertThat(out1).isEqualTo("smtp://localhost?from=me&to=foo%3A1&to=bar%3A2"); + } + @Test public void testSanitizeAccessToken() { String out1 = URISupport
