Skip to content

Commit d55a173

Browse files
committed
Fix issue w/ use of UrlPathHelper's urlDecode property
Before this change the getPathWithinServletMapping method of UrlPathHelper could not work properly when a default servlet mapping (i.e. "/") was used in combination with urlDecode=false. The fact that the getServletPath() method of HttpServletRequest always returns a decoded path was getting in the way. Although there is no way to check Servlet mappings through the Servlet API, this change aims to detect the given scenario and returns the full path following the context path thus avoiding URL decoding. Note that the same can be achieved by setting urlDecode=false and alwaysUseFullPath=true. However this change ensures that urlDecode works properly without having to know that. Issue: SPR-11101 (cherry picked from commit 12598f8)
1 parent 4d2d223 commit d55a173

File tree

2 files changed

+39
-18
lines changed

2 files changed

+39
-18
lines changed

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

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,11 +22,11 @@
2222
import java.util.Map;
2323
import java.util.Map.Entry;
2424
import java.util.Properties;
25-
2625
import javax.servlet.http.HttpServletRequest;
2726

2827
import org.apache.commons.logging.Log;
2928
import org.apache.commons.logging.LogFactory;
29+
3030
import org.springframework.util.LinkedMultiValueMap;
3131
import org.springframework.util.MultiValueMap;
3232
import org.springframework.util.StringUtils;
@@ -181,11 +181,24 @@ public String getPathWithinServletMapping(HttpServletRequest request) {
181181
}
182182
else {
183183
// Special case: URI is different from servlet path.
184-
// Can happen e.g. with index page: URI="/", servletPath="/index.html"
185-
// Use path info if available, as it indicates an index page within
186-
// a servlet mapping. Otherwise, use the full servlet path.
187184
String pathInfo = request.getPathInfo();
188-
return (pathInfo != null ? pathInfo : servletPath);
185+
if (pathInfo != null) {
186+
// Use path info if available. Indicates index page within a servlet mapping?
187+
// e.g. with index page: URI="/", servletPath="/index.html"
188+
return pathInfo;
189+
}
190+
if (!this.urlDecode) {
191+
// No path info... (not mapped by prefix, nor by extension, nor "/*")
192+
// For the default servlet mapping (i.e. "/"), urlDecode=false can
193+
// cause issues since getServletPath() returns a decoded path.
194+
// If decoding pathWithinApp yields a match just use pathWithinApp.
195+
path = getRemainingPath(decodeInternal(request, pathWithinApp), servletPath, false);
196+
if (path != null) {
197+
return pathWithinApp;
198+
}
199+
}
200+
// Otherwise, use the full servlet path.
201+
return servletPath;
189202
}
190203
}
191204

@@ -217,7 +230,7 @@ public String getPathWithinApplication(HttpServletRequest request) {
217230
private String getRemainingPath(String requestUri, String mapping, boolean ignoreCase) {
218231
int index1 = 0;
219232
int index2 = 0;
220-
for ( ; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) {
233+
for (; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) {
221234
char c1 = requestUri.charAt(index1);
222235
char c2 = mapping.charAt(index2);
223236
if (c1 == ';') {
@@ -244,7 +257,7 @@ private String getRemainingPath(String requestUri, String mapping, boolean ignor
244257
else if (requestUri.charAt(index1) == ';') {
245258
index1 = requestUri.indexOf('/', index1);
246259
}
247-
return (index1 != -1) ? requestUri.substring(index1) : "";
260+
return (index1 != -1 ? requestUri.substring(index1) : "");
248261
}
249262

250263
/**
@@ -299,8 +312,7 @@ public String getServletPath(HttpServletRequest request) {
299312
if (servletPath == null) {
300313
servletPath = request.getServletPath();
301314
}
302-
if (servletPath.length() > 1 && servletPath.endsWith("/") &&
303-
shouldRemoveTrailingServletPathSlash(request)) {
315+
if (servletPath.length() > 1 && servletPath.endsWith("/") && shouldRemoveTrailingServletPathSlash(request)) {
304316
// On WebSphere, in non-compliant mode, for a "/foo/" case that would be "/foo"
305317
// on all other servlet containers: removing trailing slash, proceeding with
306318
// that remaining slash as final lookup path...
@@ -436,7 +448,6 @@ protected String determineEncoding(HttpServletRequest request) {
436448
* Remove ";" (semicolon) content from the given request URI if the
437449
* {@linkplain #setRemoveSemicolonContent(boolean) removeSemicolonContent}
438450
* property is set to "true". Note that "jssessionid" is always removed.
439-
*
440451
* @param requestUri the request URI string to remove ";" content from
441452
* @return the updated URI string
442453
*/
@@ -473,7 +484,6 @@ private String removeJsessionid(String requestUri) {
473484
* assumed the URL path from which the variables were extracted is already
474485
* decoded through a call to
475486
* {@link #getLookupPathForRequest(HttpServletRequest)}.
476-
*
477487
* @param request current HTTP request
478488
* @param vars URI variables extracted from the URL path
479489
* @return the same Map or a new Map instance
@@ -498,7 +508,6 @@ public Map<String, String> decodePathVariables(HttpServletRequest request, Map<S
498508
* assumed the URL path from which the variables were extracted is already
499509
* decoded through a call to
500510
* {@link #getLookupPathForRequest(HttpServletRequest)}.
501-
*
502511
* @param request current HTTP request
503512
* @param vars URI variables extracted from the URL path
504513
* @return the same Map or a new Map instance

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,18 +32,20 @@
3232
*/
3333
public class UrlPathHelperTests {
3434

35+
private static final String WEBSPHERE_URI_ATTRIBUTE = "com.ibm.websphere.servlet.uri_non_decoded";
36+
3537
private UrlPathHelper helper;
3638

3739
private MockHttpServletRequest request;
3840

39-
private static final String WEBSPHERE_URI_ATTRIBUTE = "com.ibm.websphere.servlet.uri_non_decoded";
4041

4142
@Before
4243
public void setUp() {
4344
helper = new UrlPathHelper();
4445
request = new MockHttpServletRequest();
4546
}
4647

48+
4749
@Test
4850
public void getPathWithinApplication() {
4951
request.setContextPath("/petclinic");
@@ -77,6 +79,17 @@ public void getPathWithinServlet() {
7779
assertEquals("Incorrect path returned", "/welcome.html", helper.getPathWithinServletMapping(request));
7880
}
7981

82+
@Test // SPR-11101
83+
public void getPathWithinServletWithoutUrlDecoding() {
84+
request.setContextPath("/SPR-11101");
85+
request.setServletPath("/test_url_decoding/a/b");
86+
request.setRequestURI("/test_url_decoding/a%2Fb");
87+
88+
helper.setUrlDecode(false);
89+
String actual = helper.getPathWithinServletMapping(request);
90+
assertEquals("/test_url_decoding/a%2Fb", actual);
91+
}
92+
8093
@Test
8194
public void getRequestUri() {
8295
request.setRequestURI("/welcome.html");
@@ -141,11 +154,10 @@ public void getLookupPathWithSemicolonContentAndNullPathInfo() {
141154

142155

143156
//
144-
// suite of tests root requests for default servlets (SRV 11.2) on Websphere vs Tomcat and other containers
145-
// see: http://jira.springframework.org/browse/SPR-7064
157+
// Suite of tests root requests for default servlets (SRV 11.2) on WebSphere vs Tomcat and other containers
158+
// See: http://jira.springframework.org/browse/SPR-7064
146159
//
147160

148-
149161
//
150162
// / mapping (default servlet)
151163
//

0 commit comments

Comments
 (0)