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

Reply via email to