From 33becd8258583f17741c8615bcd2d2dc0378c7fc Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 6 Jun 2019 11:39:53 +0200 Subject: [PATCH] Allow separator configuration in PathPatternParser This commit allows to configure a custom path separator when parsing and matching path patterns with `PathPatternParser`, but also when parsing incoming paths as `PathContainer` instances. Closes gh-23092 --- .../http/server/DefaultPathContainer.java | 8 ++++--- .../http/server/PathContainer.java | 18 +++++++++++--- .../web/util/pattern/PathPatternParser.java | 24 ++++++++++++------- .../server/DefaultPathContainerTests.java | 20 +++++++++++++--- .../util/pattern/PathPatternParserTests.java | 9 +++++++ 5 files changed, 62 insertions(+), 17 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/server/DefaultPathContainer.java b/spring-web/src/main/java/org/springframework/http/server/DefaultPathContainer.java index 3ed1bb8de2..d6e4d596ae 100644 --- a/spring-web/src/main/java/org/springframework/http/server/DefaultPathContainer.java +++ b/spring-web/src/main/java/org/springframework/http/server/DefaultPathContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,11 +89,13 @@ final class DefaultPathContainer implements PathContainer { } - static PathContainer createFromUrlPath(String path) { + static PathContainer createFromUrlPath(String path, String separator) { if (path.equals("")) { return EMPTY_PATH; } - String separator = "/"; + if (separator.length() == 0) { + throw new IllegalArgumentException("separator should not be empty"); + } Separator separatorElement = separator.equals(SEPARATOR.value()) ? SEPARATOR : () -> separator; List elements = new ArrayList<>(); int begin; diff --git a/spring-web/src/main/java/org/springframework/http/server/PathContainer.java b/spring-web/src/main/java/org/springframework/http/server/PathContainer.java index 841dda2f36..441fec2b6d 100644 --- a/spring-web/src/main/java/org/springframework/http/server/PathContainer.java +++ b/spring-web/src/main/java/org/springframework/http/server/PathContainer.java @@ -65,14 +65,26 @@ public interface PathContainer { } + /** + * Parse the path value into a sequence of {@code "/"} {@link Separator Separator} + * and {@link PathSegment PathSegment} elements. + * @param path the encoded, raw path value to parse + * @return the parsed path + */ + static PathContainer parsePath(String path) { + return DefaultPathContainer.createFromUrlPath(path, "/"); + } + /** * Parse the path value into a sequence of {@link Separator Separator} and * {@link PathSegment PathSegment} elements. - * @param path the encoded, raw URL path value to parse + * @param path the encoded, raw path value to parse + * @param separator the decoded separator for parsing patterns * @return the parsed path + * @since 5.2 */ - static PathContainer parsePath(String path) { - return DefaultPathContainer.createFromUrlPath(path); + static PathContainer parsePath(String path, String separator) { + return DefaultPathContainer.createFromUrlPath(path, separator); } diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java index 78625c653c..b7bf87b6f7 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,8 @@ public class PathPatternParser { private boolean caseSensitive = true; + private char separator = '/'; + /** * Whether a {@link PathPattern} produced by this parser should should @@ -75,14 +77,20 @@ public class PathPatternParser { } /** - * Accessor used for the separator to use. - *

Currently not exposed for configuration with URI path patterns and - * mainly for use in InternalPathPatternParser and PathPattern. If required - * in the future, a similar option would also need to be exposed in - * {@link org.springframework.http.server.PathContainer PathContainer}. + * Char that represents the separator to use in the patterns. + *

The default is {@code '/'}. + * @since 5.2 + */ + public void setSeparator(char separator) { + this.separator = separator; + } + + /** + * Char that represents the separator to use in the patterns. + * @since 5.2 */ - char getSeparator() { - return '/'; + public char getSeparator() { + return this.separator; } diff --git a/spring-web/src/test/java/org/springframework/http/server/DefaultPathContainerTests.java b/spring-web/src/test/java/org/springframework/http/server/DefaultPathContainerTests.java index 623bf82022..65380f42f6 100644 --- a/spring-web/src/test/java/org/springframework/http/server/DefaultPathContainerTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/DefaultPathContainerTests.java @@ -27,6 +27,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** * Unit tests for {@link DefaultPathContainer}. @@ -111,15 +112,18 @@ public class DefaultPathContainerTests { testPath("//%20/%20", "//%20/%20", Arrays.asList("/", "/", "%20", "/", "%20")); } - private void testPath(String input, String value, List expectedElements) { - - PathContainer path = PathContainer.parsePath(input); + private void testPath(String input, String separator, String value, List expectedElements) { + PathContainer path = PathContainer.parsePath(input, separator); assertThat(path.value()).as("value: '" + input + "'").isEqualTo(value); assertThat(path.elements().stream() .map(PathContainer.Element::value).collect(Collectors.toList())).as("elements: " + input).isEqualTo(expectedElements); } + private void testPath(String input, String value, List expectedElements) { + testPath(input, "/", value, expectedElements); + } + @Test public void subPath() throws Exception { // basic @@ -137,4 +141,14 @@ public class DefaultPathContainerTests { assertThat(path.subPath(2).value()).isEqualTo("/b/"); } + @Test + public void pathWithCustomSeparator() throws Exception { + testPath("a.b.c", ".", "a.b.c", Arrays.asList("a", ".", "b", ".", "c")); + } + + @Test + public void emptySeparator() { + assertThatIllegalArgumentException().isThrownBy(() -> PathContainer.parsePath("path", "")); + } + } diff --git a/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java index a3256b2e61..00ab6adb76 100644 --- a/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java @@ -408,6 +408,15 @@ public class PathPatternParserTests { assertThat(patterns.get(1)).isEqualTo(p2); } + @Test + public void separatorTests() { + PathPatternParser parser = new PathPatternParser(); + parser.setSeparator('.'); + String rawPattern = "first.second.{last}"; + PathPattern pattern = parser.parse(rawPattern); + assertThat(pattern.computePatternString()).isEqualTo(rawPattern); + } + private PathPattern parse(String pattern) { PathPatternParser patternParser = new PathPatternParser(); return patternParser.parse(pattern);