This is an automated email from the ASF dual-hosted git repository. markt-asf pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 0a612fd6d39b7a7319f6cf30086ce4071629561c Author: Mark Thomas <[email protected]> AuthorDate: Tue Nov 3 14:23:20 2026 +0000 Refactor request.getContextPath() --- .../catalina/connector/LocalStrings.properties | 1 - java/org/apache/catalina/connector/Request.java | 113 +------------------ .../apache/catalina/util/LocalStrings.properties | 2 + java/org/apache/catalina/util/RequestUtil.java | 125 +++++++++++++++++++++ 4 files changed, 128 insertions(+), 113 deletions(-) diff --git a/java/org/apache/catalina/connector/LocalStrings.properties b/java/org/apache/catalina/connector/LocalStrings.properties index 9ba1ec3918..e04286d4b3 100644 --- a/java/org/apache/catalina/connector/LocalStrings.properties +++ b/java/org/apache/catalina/connector/LocalStrings.properties @@ -58,7 +58,6 @@ coyoteRequest.changeSessionId=Cannot change session ID. There is no session asso coyoteRequest.chunkedPostTooLarge=Parameters were not parsed because the size of the posted data was too big. Because this request was a chunked request, it could not be processed further. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs. coyoteRequest.deletePartFailed=Failed to delete temporary file used for part [{0}] coyoteRequest.filterAsyncSupportUnknown=Unable to determine if any filters do not support async processing -coyoteRequest.getContextPath.ise=Unable to find match between the canonical context path [{0}] and the URI presented by the user agent [{1}] coyoteRequest.getInputStream.ise=getReader() has already been called for this request coyoteRequest.getReader.ise=getInputStream() has already been called for this request coyoteRequest.gssLifetimeFail=Failed to obtain remaining lifetime for user principal [{0}] diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java index 1811114189..9e5b9ea4ce 100644 --- a/java/org/apache/catalina/connector/Request.java +++ b/java/org/apache/catalina/connector/Request.java @@ -97,10 +97,8 @@ import org.apache.tomcat.InstanceManager; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.ByteChunk; -import org.apache.tomcat.util.buf.EncodedSolidusHandling; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.buf.StringUtils; -import org.apache.tomcat.util.buf.UDecoder; import org.apache.tomcat.util.http.CookieProcessor; import org.apache.tomcat.util.http.FastHttpDateFormat; import org.apache.tomcat.util.http.Parameters; @@ -2127,116 +2125,7 @@ public class Request implements HttpServletRequest { @Override public String getContextPath() { - int lastSlash = mappingData.contextSlashCount; - // Special case handling for the root context - if (lastSlash == 0) { - return ""; - } - - String canonicalContextPath = getServletContext().getContextPath(); - - String uri = getRequestURI(); - int pos = 0; - if (!getContext().getAllowMultipleLeadingForwardSlashInPath()) { - // Ensure that the returned value only starts with a single '/'. - // This prevents the value being misinterpreted as a protocol-relative - // URI if used with sendRedirect(). - do { - pos++; - } while (pos < uri.length() && uri.charAt(pos) == '/'); - pos--; - uri = uri.substring(pos); - } - - char[] uriChars = uri.toCharArray(); - // Need at least the number of slashes in the context path - while (lastSlash > 0) { - pos = nextSlash(uriChars, pos + 1); - if (pos == -1) { - break; - } - lastSlash--; - } - // Now allow for path parameters, normalization and/or encoding. - // Essentially, keep extending the candidate path up to the next slash - // until the decoded and normalized candidate path (with the path - // parameters removed) is the same as the canonical path. - String candidate; - if (pos == -1) { - candidate = uri; - } else { - candidate = uri.substring(0, pos); - } - candidate = removePathParameters(candidate); - candidate = UDecoder.URLDecode(candidate, connector.getURICharset()); - candidate = org.apache.tomcat.util.http.RequestUtil.normalize(candidate); - boolean match = canonicalContextPath.equals(candidate); - while (!match && pos != -1) { - pos = nextSlash(uriChars, pos + 1); - if (pos == -1) { - candidate = uri; - } else { - candidate = uri.substring(0, pos); - } - candidate = removePathParameters(candidate); - candidate = UDecoder.URLDecode(candidate, connector.getURICharset()); - candidate = org.apache.tomcat.util.http.RequestUtil.normalize(candidate); - match = canonicalContextPath.equals(candidate); - } - if (match) { - if (pos == -1) { - return uri; - } else { - return uri.substring(0, pos); - } - } else { - // Should never happen - throw new IllegalStateException( - sm.getString("coyoteRequest.getContextPath.ise", canonicalContextPath, uri)); - } - } - - - private String removePathParameters(String input) { - int nextSemiColon = input.indexOf(';'); - // Shortcut - if (nextSemiColon == -1) { - return input; - } - StringBuilder result = new StringBuilder(input.length()); - result.append(input, 0, nextSemiColon); - while (true) { - int nextSlash = input.indexOf('/', nextSemiColon); - if (nextSlash == -1) { - break; - } - nextSemiColon = input.indexOf(';', nextSlash); - if (nextSemiColon == -1) { - result.append(input.substring(nextSlash)); - break; - } else { - result.append(input, nextSlash, nextSemiColon); - } - } - - return result.toString(); - } - - - private int nextSlash(char[] uri, int startPos) { - int len = uri.length; - int pos = startPos; - while (pos < len) { - if (uri[pos] == '/') { - return pos; - } else if (connector.getEncodedSolidusHandlingInternal() == EncodedSolidusHandling.DECODE && - uri[pos] == '%' && pos + 2 < len && uri[pos + 1] == '2' && - (uri[pos + 2] == 'f' || uri[pos + 2] == 'F')) { - return pos; - } - pos++; - } - return -1; + return RequestUtil.getContextPathUndecoded(this); } diff --git a/java/org/apache/catalina/util/LocalStrings.properties b/java/org/apache/catalina/util/LocalStrings.properties index 7f3a337f4f..4a392275b7 100644 --- a/java/org/apache/catalina/util/LocalStrings.properties +++ b/java/org/apache/catalina/util/LocalStrings.properties @@ -49,6 +49,8 @@ netmask.invalidPort=The port part in the pattern [{0}] is not valid parameterMap.locked=No modifications are allowed to a locked ParameterMap +requestUtil.getContextPath.ise=Unable to find match between the canonical context path [{0}] and the URI presented by the user agent [{1}] + resourceSet.locked=No modifications are allowed to a locked ResourceSet sessionIdGeneratorBase.createRandom=Creation of SecureRandom instance for session ID generation using [{0}] took [{1}] milliseconds. diff --git a/java/org/apache/catalina/util/RequestUtil.java b/java/org/apache/catalina/util/RequestUtil.java index 58f7ab9b29..accb972ff8 100644 --- a/java/org/apache/catalina/util/RequestUtil.java +++ b/java/org/apache/catalina/util/RequestUtil.java @@ -22,13 +22,20 @@ import java.util.Enumeration; import javax.servlet.http.HttpServletRequest; +import org.apache.catalina.Context; +import org.apache.catalina.connector.Connector; import org.apache.catalina.connector.Request; +import org.apache.tomcat.util.buf.EncodedSolidusHandling; import org.apache.tomcat.util.buf.UDecoder; +import org.apache.tomcat.util.res.StringManager; /** * General purpose request parsing and encoding utility methods. */ public final class RequestUtil { + + private static final StringManager sm = StringManager.getManager(RequestUtil.class); + /** * Default constructor. */ @@ -199,4 +206,122 @@ public final class RequestUtil { } return result.toString(); } + + + public static String getContextPathUndecoded(Request request) { + + int lastSlash = request.getMappingData().contextSlashCount; + // Special case handling for the root context + if (lastSlash == 0) { + return ""; + } + + String uri = request.getRequestURI(); + Context context = request.getContext(); + String canonicalContextPath = request.getServletContext().getContextPath(); + Connector connector = request.getConnector(); + EncodedSolidusHandling encodedSolidusHandling = connector.getEncodedSolidusHandlingInternal(); + + int pos = 0; + if (!context.getAllowMultipleLeadingForwardSlashInPath()) { + // Ensure that the returned value only starts with a single '/'. + // This prevents the value being misinterpreted as a protocol-relative + // URI if used with sendRedirect(). + do { + pos++; + } while (pos < uri.length() && uri.charAt(pos) == '/'); + pos--; + uri = uri.substring(pos); + } + + char[] uriChars = uri.toCharArray(); + // Need at least the number of slashes in the context path + while (lastSlash > 0) { + pos = nextSlash(uriChars, pos + 1, encodedSolidusHandling); + if (pos == -1) { + break; + } + lastSlash--; + } + // Now allow for path parameters, normalization and/or encoding. + // Essentially, keep extending the candidate path up to the next slash + // until the decoded and normalized candidate path (with the path + // parameters removed) is the same as the canonical path. + String candidate; + if (pos == -1) { + candidate = uri; + } else { + candidate = uri.substring(0, pos); + } + candidate = removePathParameters(candidate); + candidate = UDecoder.URLDecode(candidate, connector.getURICharset()); + candidate = org.apache.tomcat.util.http.RequestUtil.normalize(candidate); + boolean match = canonicalContextPath.equals(candidate); + while (!match && pos != -1) { + pos = nextSlash(uriChars, pos + 1, encodedSolidusHandling); + if (pos == -1) { + candidate = uri; + } else { + candidate = uri.substring(0, pos); + } + candidate = removePathParameters(candidate); + candidate = UDecoder.URLDecode(candidate, connector.getURICharset()); + candidate = org.apache.tomcat.util.http.RequestUtil.normalize(candidate); + match = canonicalContextPath.equals(candidate); + } + if (match) { + if (pos == -1) { + return uri; + } else { + return uri.substring(0, pos); + } + } else { + // Should never happen + throw new IllegalStateException( + sm.getString("requestUtil.getContextPath.ise", canonicalContextPath, uri)); + } + } + + + private static String removePathParameters(String input) { + int nextSemiColon = input.indexOf(';'); + // Shortcut + if (nextSemiColon == -1) { + return input; + } + StringBuilder result = new StringBuilder(input.length()); + result.append(input, 0, nextSemiColon); + while (true) { + int nextSlash = input.indexOf('/', nextSemiColon); + if (nextSlash == -1) { + break; + } + nextSemiColon = input.indexOf(';', nextSlash); + if (nextSemiColon == -1) { + result.append(input.substring(nextSlash)); + break; + } else { + result.append(input, nextSlash, nextSemiColon); + } + } + + return result.toString(); + } + + + private static int nextSlash(char[] uri, int startPos, EncodedSolidusHandling encodedSolidusHandling) { + int len = uri.length; + int pos = startPos; + while (pos < len) { + if (uri[pos] == '/') { + return pos; + } else if (encodedSolidusHandling == EncodedSolidusHandling.DECODE && + uri[pos] == '%' && pos + 2 < len && uri[pos + 1] == '2' && + (uri[pos + 2] == 'f' || uri[pos + 2] == 'F')) { + return pos; + } + pos++; + } + return -1; + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
