From f9af5d400d31bb65c55e7c5987ac0d1c087c9722 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 24 Jun 2024 11:31:39 +0200 Subject: [PATCH] Use custom path separator for pattern comparisons As pointed out in gh-33085, the `AntPatternComparator` hardcodes the "/" separator when checking for "catch all" patterns like "/**". This commit ensures that the custom path separator is used for those checks, in order to guarantee consistent comparator results. See gh-33085 --- .../springframework/util/AntPathMatcher.java | 19 +++++++++++++------ .../util/AntPathMatcherTests.java | 10 ++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java index 9c3923096c..f17d7ba8cd 100644 --- a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java +++ b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java @@ -635,7 +635,7 @@ public class AntPathMatcher implements PathMatcher { */ @Override public Comparator getPatternComparator(String path) { - return new AntPatternComparator(path); + return new AntPatternComparator(path, this.pathSeparator); } @@ -770,8 +770,15 @@ public class AntPathMatcher implements PathMatcher { private final String path; + private final String pathSeparator; + public AntPatternComparator(String path) { + this(path, DEFAULT_PATH_SEPARATOR); + } + + public AntPatternComparator(String path, String pathSeparator) { this.path = path; + this.pathSeparator = pathSeparator; } /** @@ -782,8 +789,8 @@ public class AntPathMatcher implements PathMatcher { */ @Override public int compare(String pattern1, String pattern2) { - PatternInfo info1 = new PatternInfo(pattern1); - PatternInfo info2 = new PatternInfo(pattern2); + PatternInfo info1 = new PatternInfo(pattern1, this.pathSeparator); + PatternInfo info2 = new PatternInfo(pattern2, this.pathSeparator); if (info1.isLeastSpecific() && info2.isLeastSpecific()) { return 0; @@ -865,12 +872,12 @@ public class AntPathMatcher implements PathMatcher { @Nullable private Integer length; - public PatternInfo(@Nullable String pattern) { + PatternInfo(@Nullable String pattern, String pathSeparator) { this.pattern = pattern; if (this.pattern != null) { initCounters(); - this.catchAllPattern = this.pattern.equals("/**"); - this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**"); + this.catchAllPattern = this.pattern.equals(pathSeparator + "**"); + this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith(pathSeparator + "**"); } if (this.uriVars == 0) { this.length = (this.pattern != null ? this.pattern.length() : 0); diff --git a/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java b/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java index 032971e6fe..e1352e5eec 100644 --- a/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java +++ b/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java @@ -524,6 +524,16 @@ class AntPathMatcherTests { assertThat(comparator.compare("*/**", "*")).isEqualTo(1); } + @Test + void patternComparatorWithDotSeparator() { + Comparator comparator = dotSeparatedPathMatcher.getPatternComparator("price.stock.spring"); + + assertThat(comparator.compare(null, null)).isEqualTo(0); + assertThat(comparator.compare("price.stock.ticker/symbol", "price.stock.ticker/symbol")).isEqualTo(0); + assertThat(comparator.compare("price.stock.**", "price.stock.ticker")).isEqualTo(1); + } + + @Test void patternComparatorSort() { Comparator comparator = pathMatcher.getPatternComparator("/hotels/new");