On 04/06/2026 11:47, Mark Thomas wrote:
On 04/06/2026 10:59, Rémy Maucherat wrote:
On Thu, Jun 4, 2026 at 11:01 AM Mark Thomas <[email protected]> wrote:
On 04/06/2026 07:59, Rémy Maucherat wrote:
Hi,

There are some discrepancies in URL encoding in the request dispatcher
for jakarta.servlet.include.context_path.
- Request.getContextPath is URL encoded, this is settled
- jakarta.servlet.forward.context_path saw a bug come in some time
ago, and it was switched to being URL encoded
- jakarta.servlet.include.context_path remains in the decoded camp,
but there's some mixed matching with a comparison with a
request.getContextPath, which is incorrect (lines 698, 701 and 703)

The specification language is not very clear.
"These attributes are accessible from the included servlet via the
getAttribute method on the
request object and their values must be equal to the request URI,
context path, servlet path,
mapping, path info, and query string of the included servlet, respectively."

I would think "jakarta.servlet.include.context_path" should be URL
encoded, rather than decoded as it is now, with changes to be made to
lines 387 and 703 to use context.getEncodedPath().

<snip/>

Can I have a little more time to (try and) get my head around this code. My sense is that there is a simple solution to the issue that I can't quite because of the naming.

I think I have a solution that works for all current versions.

It works because the jakarta.servlet.include.context_path attribute is set from the target context of the include - so it is effectively canonical - unlike the value from the request which can vary.

There is the option to switch to the standard (decoded) path in 12.0.x but that could be a follow-on fix.

Patch (if the list allows it) attached.

Mark
diff --git a/java/org/apache/catalina/core/ApplicationDispatcher.java b/java/org/apache/catalina/core/ApplicationDispatcher.java
index 2dc17975a9..5c257619e6 100644
--- a/java/org/apache/catalina/core/ApplicationDispatcher.java
+++ b/java/org/apache/catalina/core/ApplicationDispatcher.java
@@ -18,6 +18,7 @@ package org.apache.catalina.core;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
 
 import jakarta.servlet.AsyncContext;
 import jakarta.servlet.DispatcherType;
@@ -42,6 +43,7 @@ import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.RequestFacade;
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.connector.ResponseFacade;
+import org.apache.catalina.util.URLEncoder;
 import org.apache.coyote.BadRequestException;
 import org.apache.coyote.CloseNowException;
 import org.apache.tomcat.util.ExceptionUtils;
@@ -384,7 +386,7 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
         } else {
             // Handle an HTTP path based include
 
-            String contextPath = context.getPath();
+            String contextPath = context.getEncodedPath();
             if (requestURI != null) {
                 wrequest.setAttribute(INCLUDE_REQUEST_URI, requestURI);
             }
@@ -698,9 +700,10 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
                 Object contextPath = houterRequest.getAttribute(INCLUDE_CONTEXT_PATH);
                 if (contextPath == null) {
                     // Forward
-                    contextPath = houterRequest.getContextPath();
+                    contextPath = URLEncoder.DEFAULT.encode(houterRequest.getServletContext().getContextPath(),
+                            StandardCharsets.UTF_8);
                 }
-                crossContext = !context.getPath().equals(contextPath);
+                crossContext = !context.getEncodedPath().equals(contextPath);
             }
             wrapper = new ApplicationHttpRequest(hcurrent, context, crossContext);
         } else {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to