Skip to content

Commit d8469d1

Browse files
committed
Fix issue with extracting matrix variables
The servlet spec recommends removing path parameters from the contextPath, servletPath, and pathInfo but not from the requestURI. This poses a challenge for the UrlPathHelper, which determines the lookup path by comparing the above. This change introduces a method that matches the requestURI to the contextPath and servletPath ignoring path parameters (i.e. matrix variables) for comparison purposes while also preserving them in the resulting lookup path.
1 parent 826057b commit d8469d1

File tree

3 files changed

+77
-6
lines changed

3 files changed

+77
-6
lines changed

spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.apache.commons.logging.Log;
2929
import org.apache.commons.logging.LogFactory;
30+
import org.springframework.util.Assert;
3031
import org.springframework.util.StringUtils;
3132

3233
/**
@@ -172,9 +173,10 @@ public String getLookupPathForRequest(HttpServletRequest request) {
172173
public String getPathWithinServletMapping(HttpServletRequest request) {
173174
String pathWithinApp = getPathWithinApplication(request);
174175
String servletPath = getServletPath(request);
175-
if (pathWithinApp.startsWith(servletPath)) {
176+
String path = getRemainingPath(pathWithinApp, servletPath, false);
177+
if (path != null) {
176178
// Normal case: URI contains servlet path.
177-
return pathWithinApp.substring(servletPath.length());
179+
return path;
178180
}
179181
else {
180182
// Special case: URI is different from servlet path.
@@ -195,17 +197,54 @@ public String getPathWithinServletMapping(HttpServletRequest request) {
195197
public String getPathWithinApplication(HttpServletRequest request) {
196198
String contextPath = getContextPath(request);
197199
String requestUri = getRequestUri(request);
198-
if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
200+
String path = getRemainingPath(requestUri, contextPath, true);
201+
if (path != null) {
199202
// Normal case: URI contains context path.
200-
String path = requestUri.substring(contextPath.length());
201203
return (StringUtils.hasText(path) ? path : "/");
202204
}
203205
else {
204-
// Special case: rather unusual.
205206
return requestUri;
206207
}
207208
}
208209

210+
/**
211+
* Match the given "mapping" to the start of the "requestUri" and if there
212+
* is a match return the extra part. This method is needed because the
213+
* context path and the servlet path returned by the HttpServletRequest are
214+
* stripped of semicolon content unlike the requesUri.
215+
*/
216+
private String getRemainingPath(String requestUri, String mapping, boolean ignoreCase) {
217+
int index1 = 0;
218+
int index2 = 0;
219+
for ( ; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) {
220+
char c1 = requestUri.charAt(index1);
221+
char c2 = mapping.charAt(index2);
222+
if (c1 == ';') {
223+
index1 = requestUri.indexOf('/', index1);
224+
if (index1 == -1) {
225+
return null;
226+
}
227+
c1 = requestUri.charAt(index1);
228+
}
229+
if (c1 == c2) {
230+
continue;
231+
}
232+
if (ignoreCase && (Character.toLowerCase(c1) == Character.toLowerCase(c2))) {
233+
continue;
234+
}
235+
return null;
236+
}
237+
if (index2 != mapping.length()) {
238+
return null;
239+
}
240+
if (index1 == requestUri.length()) {
241+
return "";
242+
}
243+
else if (requestUri.charAt(index1) == ';') {
244+
index1 = requestUri.indexOf('/', index1);
245+
}
246+
return (index1 != -1) ? requestUri.substring(index1) : "";
247+
}
209248

210249
/**
211250
* Return the request URI for the given request, detecting an include request

spring-web/src/test/java/org/springframework/web/util/UrlPathHelperTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ public void getPathWithinApplicationForSlashContextPath() {
6969
assertEquals("Incorrect path returned", "/welcome.html", helper.getPathWithinApplication(request));
7070
}
7171

72+
@Test
73+
public void getPathWithinServlet() {
74+
request.setContextPath("/petclinic");
75+
request.setServletPath("/main");
76+
request.setRequestURI("/petclinic/main/welcome.html");
77+
78+
assertEquals("Incorrect path returned", "/welcome.html", helper.getPathWithinServletMapping(request));
79+
}
80+
7281
@Test
7382
public void getRequestUri() {
7483
request.setRequestURI("/welcome.html");
@@ -104,6 +113,29 @@ public void getRequestKeepSemicolonContent() throws UnsupportedEncodingException
104113
assertEquals("jsessionid should always be removed", "/foo;a=b;c=d", helper.getRequestUri(request));
105114
}
106115

116+
@Test
117+
public void getLookupPathWithSemicolonContent() {
118+
helper.setRemoveSemicolonContent(false);
119+
120+
request.setContextPath("/petclinic");
121+
request.setServletPath("/main");
122+
request.setRequestURI("/petclinic;a=b/main;b=c/welcome.html;c=d");
123+
124+
assertEquals("/welcome.html;c=d", helper.getLookupPathForRequest(request));
125+
}
126+
127+
@Test
128+
public void getLookupPathWithSemicolonContentAndNullPathInfo() {
129+
helper.setRemoveSemicolonContent(false);
130+
131+
request.setContextPath("/petclinic");
132+
request.setServletPath("/welcome.html");
133+
request.setRequestURI("/petclinic;a=b/welcome.html;c=d");
134+
135+
assertEquals("/welcome.html;c=d", helper.getLookupPathForRequest(request));
136+
}
137+
138+
107139
//
108140
// suite of tests root requests for default servlets (SRV 11.2) on Websphere vs Tomcat and other containers
109141
// see: http://jira.springframework.org/browse/SPR-7064

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ else if (paramValues.size() == 1) {
116116
protected void handleMissingValue(String name, MethodParameter param) throws ServletRequestBindingException {
117117
String paramType = param.getParameterType().getName();
118118
throw new ServletRequestBindingException(
119-
"Missing URI path parameter '" + name + "' for method parameter type [" + paramType + "]");
119+
"Missing matrix variable '" + name + "' for method parameter type [" + paramType + "]");
120120
}
121121

122122

0 commit comments

Comments
 (0)