1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.web.util;
  17. import java.io.UnsupportedEncodingException;
  18. import java.net.URLDecoder;
  19. import javax.servlet.http.HttpServletRequest;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. /**
  23. * Helper class for URL path matching. Provides support for URL paths
  24. * in ServletDispatcher includes, and support for URL decoding.
  25. *
  26. * <p>Used by AbstractUrlHandlerMapping and AbstractMethodNameResolver.
  27. *
  28. * @author Juergen Hoeller
  29. * @since 14.01.2004
  30. * @see org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
  31. * @see org.springframework.web.servlet.mvc.multiaction.AbstractUrlMethodNameResolver
  32. */
  33. public class UrlPathHelper {
  34. /**
  35. * Standard servlet spec request attributes for include URI and paths.
  36. * <p>If included via a RequestDispatcher, the current resource will see the
  37. * original request. Its own URI and paths are exposed as request attributes.
  38. */
  39. public static final String INCLUDE_URI_REQUEST_ATTRIBUTE = "javax.servlet.include.request_uri";
  40. public static final String INCLUDE_CONTEXT_PATH_REQUEST_ATTRIBUTE = "javax.servlet.include.context_path";
  41. public static final String INCLUDE_SERVLET_PATH_REQUEST_ATTRIBUTE = "javax.servlet.include.servlet_path";
  42. private final Log logger = LogFactory.getLog(getClass());
  43. private boolean alwaysUseFullPath = false;
  44. private boolean urlDecode = false;
  45. private String defaultEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
  46. /**
  47. * Set if URL lookup should always use full path within current servlet
  48. * context. Else, the path within the current servlet mapping is used
  49. * if applicable (i.e. in the case of a ".../*" servlet mapping in web.xml).
  50. * Default is false.
  51. */
  52. public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
  53. this.alwaysUseFullPath = alwaysUseFullPath;
  54. }
  55. /**
  56. * Set if context path and request URI should be URL-decoded.
  57. * Both are returned <i>undecoded</i> by the Servlet API,
  58. * in contrast to the servlet path.
  59. * <p>Uses either the request encoding or the default encoding according
  60. * to the Servlet spec (ISO-8859-1).
  61. * <p>Note: Setting this to true requires J2SE 1.4, as J2SE 1.3's
  62. * URLDecoder class does not offer a way to specify the encoding.
  63. * @see #getServletPath
  64. * @see #getContextPath
  65. * @see #getRequestUri
  66. * @see WebUtils#DEFAULT_CHARACTER_ENCODING
  67. * @see javax.servlet.ServletRequest#getCharacterEncoding
  68. * @see java.net.URLDecoder#decode(String, String)
  69. */
  70. public void setUrlDecode(boolean urlDecode) {
  71. this.urlDecode = urlDecode;
  72. }
  73. /**
  74. * Set the default character encoding to use for URL decoding.
  75. * Default is ISO-8859-1, according to the Servlet spec.
  76. * <p>If the request specifies a character encoding itself, the request
  77. * encoding will override this setting. This also allows for generically
  78. * overriding the character encoding in a filter that invokes the
  79. * ServletRequest.setCharacterEncoding method.
  80. * @param defaultEncoding the character encoding to use
  81. * @see #determineEncoding
  82. * @see javax.servlet.ServletRequest#getCharacterEncoding
  83. * @see javax.servlet.ServletRequest#setCharacterEncoding
  84. * @see WebUtils#DEFAULT_CHARACTER_ENCODING
  85. */
  86. public void setDefaultEncoding(String defaultEncoding) {
  87. this.defaultEncoding = defaultEncoding;
  88. }
  89. /**
  90. * Return the default character encoding to use for URL decoding.
  91. */
  92. protected String getDefaultEncoding() {
  93. return defaultEncoding;
  94. }
  95. /**
  96. * Return the mapping lookup path for the given request, within the current
  97. * servlet mapping if applicable, else within the web application.
  98. * <p>Regards include request URL if called within a RequestDispatcher include.
  99. * @param request current HTTP request
  100. * @return the lookup path
  101. * @see #getPathWithinApplication
  102. * @see #getPathWithinServletMapping
  103. */
  104. public String getLookupPathForRequest(HttpServletRequest request) {
  105. // always use full path within current servlet context?
  106. if (this.alwaysUseFullPath) {
  107. return getPathWithinApplication(request);
  108. }
  109. // else use path within current servlet mapping if applicable
  110. String rest = getPathWithinServletMapping(request);
  111. if (!"".equals(rest)) {
  112. return rest;
  113. }
  114. else {
  115. return getPathWithinApplication(request);
  116. }
  117. }
  118. /**
  119. * Return the path within the servlet mapping for the given request,
  120. * i.e. the part of the request's URL beyond the part that called the servlet,
  121. * or "" if the whole URL has been used to identify the servlet.
  122. * <p>Regards include request URL if called within a RequestDispatcher include.
  123. * <p>E.g.: servlet mapping = "/test/*"; request URI = "/test/a" -> "/a".
  124. * <p>E.g.: servlet mapping = "/test"; request URI = "/test" -> "".
  125. * <p>E.g.: servlet mapping = "/*.test"; request URI = "/a.test" -> "".
  126. * @param request current HTTP request
  127. * @return the path within the servlet mapping, or ""
  128. */
  129. public String getPathWithinServletMapping(HttpServletRequest request) {
  130. return getPathWithinApplication(request).substring(getServletPath(request).length());
  131. }
  132. /**
  133. * Return the path within the web application for the given request.
  134. * <p>Regards include request URL if called within a RequestDispatcher include.
  135. * @param request current HTTP request
  136. * @return the path within the web application
  137. */
  138. public String getPathWithinApplication(HttpServletRequest request) {
  139. return getRequestUri(request).substring(getContextPath(request).length());
  140. }
  141. /**
  142. * Return the servlet path for the given request, regarding an include request
  143. * URL if called within a RequestDispatcher include.
  144. * <p>As the value returned by request.getServletPath() is already decoded by
  145. * the servlet container, this method will not attempt to decode it.
  146. * @param request current HTTP request
  147. * @return the servlet path
  148. */
  149. public String getServletPath(HttpServletRequest request) {
  150. String servletPath = (String) request.getAttribute(INCLUDE_SERVLET_PATH_REQUEST_ATTRIBUTE);
  151. if (servletPath == null) {
  152. servletPath = request.getServletPath();
  153. }
  154. return servletPath;
  155. }
  156. /**
  157. * Return the context path for the given request, regarding an include request
  158. * URL if called within a RequestDispatcher include.
  159. * <p>As the value returned by request.getContextPath() is <i>not</i> decoded by
  160. * the servlet container, this method will decode it.
  161. * @param request current HTTP request
  162. * @return the context path
  163. */
  164. public String getContextPath(HttpServletRequest request) {
  165. String contextPath = (String) request.getAttribute(INCLUDE_CONTEXT_PATH_REQUEST_ATTRIBUTE);
  166. if (contextPath == null) {
  167. contextPath = request.getContextPath();
  168. }
  169. return decodeRequestString(request, contextPath);
  170. }
  171. /**
  172. * Return the request URI for the given request, regarding an include request
  173. * URL if called within a RequestDispatcher include.
  174. * <p>As the value returned by request.getRequestURI() is <i>not</i> decoded by
  175. * the servlet container, this method will decode it.
  176. * <p>The URI that the web container resolves <i>should</i> be correct, but some
  177. * containers like JBoss/Jetty incorrectly include ";" strings like ";jsessionid"
  178. * in the URI. This method cuts off such incorrect appendices.
  179. * @param request current HTTP request
  180. * @return the request URI
  181. */
  182. public String getRequestUri(HttpServletRequest request) {
  183. String uri = (String) request.getAttribute(INCLUDE_URI_REQUEST_ATTRIBUTE);
  184. if (uri == null) {
  185. uri = request.getRequestURI();
  186. }
  187. uri = decodeRequestString(request, uri);
  188. int semicolonIndex = uri.indexOf(';');
  189. return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
  190. }
  191. /**
  192. * Decode the given source string with a URLEncoder. The encoding will be taken
  193. * from the request, falling back to the default "ISO-8859-1".
  194. * @param request current HTTP request
  195. * @param source the String to decode
  196. * @return the decoded String
  197. * @see WebUtils#DEFAULT_CHARACTER_ENCODING
  198. * @see javax.servlet.ServletRequest#getCharacterEncoding
  199. * @see java.net.URLDecoder
  200. */
  201. public String decodeRequestString(HttpServletRequest request, String source) {
  202. if (this.urlDecode) {
  203. String enc = determineEncoding(request);
  204. try {
  205. return URLDecoder.decode(source, enc);
  206. }
  207. catch (UnsupportedEncodingException ex) {
  208. logger.warn("Could not decode request string [" + source +
  209. "] with encoding '" + enc + "': using platform default");
  210. return URLDecoder.decode(source);
  211. }
  212. }
  213. return source;
  214. }
  215. /**
  216. * Determine the encoding for the given request.
  217. * Can be overridden in subclasses.
  218. * <p>The default implementation checks the request encoding,
  219. * falling back to the default encoding specified for this resolver.
  220. * @param request current HTTP request
  221. * @return the encoding for the request (never null)
  222. * @see javax.servlet.ServletRequest#getCharacterEncoding
  223. * @see #setDefaultEncoding
  224. */
  225. protected String determineEncoding(HttpServletRequest request) {
  226. String enc = request.getCharacterEncoding();
  227. if (enc == null) {
  228. enc = this.defaultEncoding;
  229. }
  230. return enc;
  231. }
  232. }