Details
Description
Follow up to https://issues.apache.org/jira/browse/CXF-8556.
A service in question needs to be reachable via multiple paths. The @Path param allows for regex matching on a class-level like the following: @Path("/{a: regexExpression}"). A class-level @PathParam is created by the name of a. The following annotations both correctly receive requests at /foo/bar:
@Path("/{a : foo/bar}") @Path("/{a : foo\\/bar}")
When the proxy implementation is invoked, these paths are segmented as an ArrayList with two entries: {a : foo and bar} and {a : foo\ and bar} respectively. The expected behavior is for there to be one segment corresponding to this path, {a : foo/bar}, so that when variables are replaced later (substituteVarargs method in UriBuilderImpl.java), the path parameter a can be recognized as a vararg and replaced by a phrase matching the regex expression.
The flow that results in the fragmented segments begins here in the invoke method of the ClientProxyImpl.java class:
if (this.isRoot) { this.addNonEmptyPath(builder, ori.getClassResourceInfo().getURITemplate().getValue()); } this.addNonEmptyPath(builder, ori.getURITemplate().getValue());
This leads to the doPath method in the UriBuilderImpl.java class, which calls the following with checkSegments equal to true:
List<PathSegment> segments; if (checkSegments) { segments = JAXRSUtils.getPathSegments(path, false, false); } else { segments = new ArrayList<>(); path = path.replaceAll("/", "%2F"); segments.add(new PathSegmentImpl(path, false)); }
The getPathSegments method is as follows and is where the ArrayList mentioned above gets populated:
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; }
The path is split based on the presence of "/", without regard for if the path segment is defined as a path parameter regex expression.
The same behavior applies on paths not at a class-level also. For example, the path denoted by a @Path("/{a : foo/bar}/{id}") on a lower level resource segments the path into the following: {a : foo, bar}, and {id}, when it would be expected to segment into two segments, {a : foo/bar} and {id}. The only difference here being when the path is segmented. Since it is not a root path, it happens after the isRoot check, with the same addNonEmptyPath method.
Attachments
Issue Links
- links to