From d8469d118b41fd18b70a11f0c0dcd12b60a8f6c6 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 29 Oct 2012 19:09:00 -0400 Subject: [PATCH] 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. --- .../web/util/UrlPathHelper.java | 49 +++++++++++++++++-- .../web/util/UrlPathHelperTests.java | 32 ++++++++++++ .../MatrixVariableMethodArgumentResolver.java | 2 +- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java index e5ca94a4ac..7a8ce598d0 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java +++ b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java @@ -27,6 +27,7 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -172,9 +173,10 @@ public class UrlPathHelper { public String getPathWithinServletMapping(HttpServletRequest request) { String pathWithinApp = getPathWithinApplication(request); String servletPath = getServletPath(request); - if (pathWithinApp.startsWith(servletPath)) { + String path = getRemainingPath(pathWithinApp, servletPath, false); + if (path != null) { // Normal case: URI contains servlet path. - return pathWithinApp.substring(servletPath.length()); + return path; } else { // Special case: URI is different from servlet path. @@ -195,17 +197,54 @@ public class UrlPathHelper { public String getPathWithinApplication(HttpServletRequest request) { String contextPath = getContextPath(request); String requestUri = getRequestUri(request); - if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) { + String path = getRemainingPath(requestUri, contextPath, true); + if (path != null) { // Normal case: URI contains context path. - String path = requestUri.substring(contextPath.length()); return (StringUtils.hasText(path) ? path : "/"); } else { - // Special case: rather unusual. return requestUri; } } + /** + * Match the given "mapping" to the start of the "requestUri" and if there + * is a match return the extra part. This method is needed because the + * context path and the servlet path returned by the HttpServletRequest are + * stripped of semicolon content unlike the requesUri. + */ + private String getRemainingPath(String requestUri, String mapping, boolean ignoreCase) { + int index1 = 0; + int index2 = 0; + for ( ; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) { + char c1 = requestUri.charAt(index1); + char c2 = mapping.charAt(index2); + if (c1 == ';') { + index1 = requestUri.indexOf('/', index1); + if (index1 == -1) { + return null; + } + c1 = requestUri.charAt(index1); + } + if (c1 == c2) { + continue; + } + if (ignoreCase && (Character.toLowerCase(c1) == Character.toLowerCase(c2))) { + continue; + } + return null; + } + if (index2 != mapping.length()) { + return null; + } + if (index1 == requestUri.length()) { + return ""; + } + else if (requestUri.charAt(index1) == ';') { + index1 = requestUri.indexOf('/', index1); + } + return (index1 != -1) ? requestUri.substring(index1) : ""; + } /** * Return the request URI for the given request, detecting an include request diff --git a/spring-web/src/test/java/org/springframework/web/util/UrlPathHelperTests.java b/spring-web/src/test/java/org/springframework/web/util/UrlPathHelperTests.java index 4ba2a16aa9..069b99f44e 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UrlPathHelperTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UrlPathHelperTests.java @@ -69,6 +69,15 @@ public class UrlPathHelperTests { assertEquals("Incorrect path returned", "/welcome.html", helper.getPathWithinApplication(request)); } + @Test + public void getPathWithinServlet() { + request.setContextPath("/petclinic"); + request.setServletPath("/main"); + request.setRequestURI("/petclinic/main/welcome.html"); + + assertEquals("Incorrect path returned", "/welcome.html", helper.getPathWithinServletMapping(request)); + } + @Test public void getRequestUri() { request.setRequestURI("/welcome.html"); @@ -104,6 +113,29 @@ public class UrlPathHelperTests { assertEquals("jsessionid should always be removed", "/foo;a=b;c=d", helper.getRequestUri(request)); } + @Test + public void getLookupPathWithSemicolonContent() { + helper.setRemoveSemicolonContent(false); + + request.setContextPath("/petclinic"); + request.setServletPath("/main"); + request.setRequestURI("/petclinic;a=b/main;b=c/welcome.html;c=d"); + + assertEquals("/welcome.html;c=d", helper.getLookupPathForRequest(request)); + } + + @Test + public void getLookupPathWithSemicolonContentAndNullPathInfo() { + helper.setRemoveSemicolonContent(false); + + request.setContextPath("/petclinic"); + request.setServletPath("/welcome.html"); + request.setRequestURI("/petclinic;a=b/welcome.html;c=d"); + + assertEquals("/welcome.html;c=d", helper.getLookupPathForRequest(request)); + } + + // // suite of tests root requests for default servlets (SRV 11.2) on Websphere vs Tomcat and other containers // see: http://jira.springframework.org/browse/SPR-7064 diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java index c8c4e4fc61..5b511116db 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java @@ -116,7 +116,7 @@ public class MatrixVariableMethodArgumentResolver extends AbstractNamedValueMeth protected void handleMissingValue(String name, MethodParameter param) throws ServletRequestBindingException { String paramType = param.getParameterType().getName(); throw new ServletRequestBindingException( - "Missing URI path parameter '" + name + "' for method parameter type [" + paramType + "]"); + "Missing matrix variable '" + name + "' for method parameter type [" + paramType + "]"); }