[
https://issues.apache.org/jira/browse/CXF-8557?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Noah Rickles updated CXF-8557:
------------------------------
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:
{code:java}
if (this.isRoot) {
this.addNonEmptyPath(builder,
ori.getClassResourceInfo().getURITemplate().getValue());
}
this.addNonEmptyPath(builder, ori.getURITemplate().getValue());
{code}
This leads to the {{doPath}} method in the {{UriBuilderImpl.java}} class, which
calls the following with {{checkSegments}} equal to {{true}}:
{code:java}
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));
}
{code}
The {{getPathSegments}} method is as follows and is where the {{ArrayList}}
mentioned above gets populated:
{code:java}
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;
}
{code}
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 {{@Path("/\{a : foo/bar}/\{id}")}} segments the path into the
following: {{{a : foo}}, {{bar}}}, and \{id}.
was:
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:
{code:java}
if (this.isRoot) {
this.addNonEmptyPath(builder,
ori.getClassResourceInfo().getURITemplate().getValue());
}
this.addNonEmptyPath(builder, ori.getURITemplate().getValue());
{code}
This leads to the {{doPath}} method in the {{UriBuilderImpl.java}} class, which
calls the following with {{checkSegments}} equal to {{true}}:
{code:java}
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));
}
{code}
The {{getPathSegments}} method is as follows and is where the {{ArrayList}}
mentioned above gets populated:
{code:java}
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;
}
{code}
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 {{@Path("/\{a : foo/bar}/\{id}")}} segments the path into the
following: {{{a : foo}}, {{bar}}}, and {{{id}}}.
> Incorrect Proxy Path Segmenting when @Path Annotation Regex Expression
> Contains "/"
> -----------------------------------------------------------------------------------
>
> Key: CXF-8557
> URL: https://issues.apache.org/jira/browse/CXF-8557
> Project: CXF
> Issue Type: Bug
> Components: JAX-RS
> Affects Versions: 3.4.4
> Reporter: Noah Rickles
> Priority: Major
> Labels: Bug
>
> 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:
> {code:java}
> if (this.isRoot) {
> this.addNonEmptyPath(builder,
> ori.getClassResourceInfo().getURITemplate().getValue());
> }
> this.addNonEmptyPath(builder, ori.getURITemplate().getValue());
> {code}
> This leads to the {{doPath}} method in the {{UriBuilderImpl.java}} class,
> which calls the following with {{checkSegments}} equal to {{true}}:
> {code:java}
> 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));
> }
> {code}
> The {{getPathSegments}} method is as follows and is where the {{ArrayList}}
> mentioned above gets populated:
> {code:java}
> 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;
> }
> {code}
> 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 {{@Path("/\{a : foo/bar}/\{id}")}} segments the path into
> the following: {{{a : foo}}, {{bar}}}, and \{id}.
--
This message was sent by Atlassian Jira
(v8.3.4#803005)