This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push:
new c0cae30 CXF-8557: Incorrect Proxy Path Segmenting when @Path
Annotation RegexExpression Contains '/' (#822)
c0cae30 is described below
commit c0cae301ccfcb75b5ad011fd2192b5385539d9dd
Author: Andriy Redko <[email protected]>
AuthorDate: Wed Jul 7 21:52:21 2021 -0400
CXF-8557: Incorrect Proxy Path Segmenting when @Path Annotation
RegexExpression Contains '/' (#822)
* CXF-8557: Incorrect Proxy Path Segmenting when @Path Annotation Regex
Expression Contains '/'
* Added more sophisticated handling of the incorrect template definitions
and fallback to simple split by path segment separator
---
.../org/apache/cxf/jaxrs/utils/JAXRSUtils.java | 73 ++++++++++++---
.../apache/cxf/jaxrs/impl/UriBuilderImplTest.java | 100 +++++++++++++++++++++
2 files changed, 161 insertions(+), 12 deletions(-)
diff --git
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
index 3f9c7f1..e0ecec3 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
@@ -181,20 +181,69 @@ public final class JAXRSUtils {
return getPathSegments(thePath, decode, true);
}
+ /**
+ * Parses path segments taking into account the URI templates and template
regexes. Per RFC-3986,
+ * "A path consists of a sequence of path segments separated by a slash
("/") character.", however
+ * it is possible to include slash ("/") inside template regex, for
example "/my/path/{a:b/c}", see
+ * please {@link URITemplate}. In this case, the whole template definition
is extracted as a path
+ * segment, without breaking it.
+ * @param thePath path
+ * @param decode should the path segments be decoded or not
+ * @param ignoreLastSlash should the last slash be ignored or not
+ * @return
+ */
public static List<PathSegment> getPathSegments(String thePath, boolean
decode,
boolean ignoreLastSlash) {
- List<PathSegment> theList =
- Arrays.asList(thePath.split("/")).stream()
- .filter(StringUtils.notEmpty())
- .map(p -> new PathSegmentImpl(p, decode))
- .collect(Collectors.toList());
-
- int len = thePath.length();
- if (len > 0 && thePath.charAt(len - 1) == '/') {
- String value = ignoreLastSlash ? "" : "/";
- theList.add(new PathSegmentImpl(value, false));
- }
- return theList;
+
+ final List<PathSegment> segments = new ArrayList<>();
+ int templateDepth = 0;
+ int start = 0;
+ for (int i = 0; i < thePath.length(); ++i) {
+ if (thePath.charAt(i) == '/') {
+ // The '/' is in template (possibly, with arbitrary regex)
definition
+ if (templateDepth != 0) {
+ continue;
+ } else if (start != i) {
+ final String segment = thePath.substring(start, i);
+ segments.add(new PathSegmentImpl(segment, decode));
+ }
+
+ // advance the positions, empty path segments
+ start = i + 1;
+ } else if (thePath.charAt(i) == '{') {
+ ++templateDepth;
+ } else if (thePath.charAt(i) == '}') {
+ --templateDepth; // could go negative, since the template
could be unbalanced
+ }
+ }
+
+ // the URI has unbalanced curly braces, backtrack to the last seen
position of the path
+ // segment separator and just split segments as-is from there
+ if (templateDepth != 0) {
+ segments.addAll(
+ Arrays
+ .stream(thePath.substring(start).split("/"))
+ .filter(StringUtils.notEmpty())
+ .map(p -> new PathSegmentImpl(p, decode))
+ .collect(Collectors.toList()));
+
+ int len = thePath.length();
+ if (len > 0 && thePath.charAt(len - 1) == '/') {
+ String value = ignoreLastSlash ? "" : "/";
+ segments.add(new PathSegmentImpl(value, false));
+ }
+ } else {
+ // the last symbol is slash
+ if (start == thePath.length() && start > 0 && thePath.charAt(start
- 1) == '/') {
+ String value = ignoreLastSlash ? "" : "/";
+ segments.add(new PathSegmentImpl(value, false));
+ } else if (!thePath.isEmpty()) {
+ final String segment = thePath.substring(start);
+ segments.add(new PathSegmentImpl(segment, decode));
+ }
+ }
+
+ return segments;
}
private static String[] getUserMediaTypes(Object provider, boolean
consumes) {
diff --git
a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
index 289003b..9232344 100644
---
a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
+++
b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
@@ -1875,4 +1875,104 @@ public class UriBuilderImplTest {
.toTemplate();
assertEquals("my/path?p=%25250%25", template);
}
+
+ @Test
+ public void pathParamFromTemplateWithOrRegex() {
+ final URI uri = UriBuilder
+ .fromUri("my/path")
+ .path("{p:my|his}")
+ .build("my");
+ assertEquals("my/path/my", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromTemplateWithRegex() {
+ final URI uri = UriBuilder
+ .fromUri("my/path")
+ .path("{p:his/him}")
+ .buildFromEncoded("his/him");
+ assertEquals("my/path/his/him", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromNestedTemplateWithRegex() {
+ // The nested templates are not supported and are not detected
+ final URI uri = UriBuilder
+ .fromUri("my/path")
+ .path("{{p:his/him}}")
+ .build();
+ assertEquals("my/path/%7B%7Bp:his/him%7D%7D", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromBadTemplateNested() {
+ final URI uri = UriBuilder
+ .fromUri("my/path")
+ .path("{p{d}}")
+ .build("my");
+ assertEquals("my/path/%7Bp%7Bd%7D%7D", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromBadTemplateUnopened() {
+ final URI uri = UriBuilder
+ .fromUri("my/path")
+ .path("p{d}/}")
+ .build("my");
+ assertEquals("my/path/pmy/%7D", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromBadTemplateUnclosed() {
+ final URI uri = UriBuilder
+ .fromUri("my/path")
+ .path("{p/{d}")
+ .build("my");
+ assertEquals("my/path/%7Bp/my", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromEmpty() {
+ final URI uri = UriBuilder
+ .fromUri("/")
+ .path("/")
+ .build();
+ assertEquals("/", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromEmptyWithSpaces() {
+ final URI uri = UriBuilder
+ .fromUri("/")
+ .path(" / ")
+ .build();
+ assertEquals("/%20%20%20/%20%20%20", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromBadTemplateUnopenedAndEnclosedSlash() {
+ final URI uri = UriBuilder
+ .fromUri("my/path")
+ .path("p{d:my/day}/}")
+ .buildFromEncoded("my/day");
+ assertEquals("my/path/pmy/day/%7D", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromBadTemplateUnclosedAndEnclosedSlash() {
+ final URI uri = UriBuilder
+ .fromUri("my/path")
+ .path("{p/{d:my/day}")
+ .build();
+ assertEquals("my/path/%7Bp/%7Bd:my/day%7D", uri.toString());
+ }
+
+ @Test
+ public void pathParamFromBadTemplate() {
+ final URI uri = UriBuilder
+ .fromUri("/")
+ .path("{")
+ .build();
+ assertEquals("/%7B", uri.toString());
+ }
}
\ No newline at end of file