(tomcat) branch 10.1.x updated: Improve AsyncContext.dispatch() when dispatchersUseEncodedPaths=false

[email protected]
Newsgroups gmane.comp.jakarta.tomcat.devel
Message-ID <[email protected]>
This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/10.1.x by this push:
     new 10c1df3da7 Improve AsyncContext.dispatch() when dispatchersUseEncodedPaths=false
10c1df3da7 is described below

commit 10c1df3da7a6267b75f47a8c3a8c4961203500e7
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Aug 13 10:37:18 2026 +0100

    Improve AsyncContext.dispatch() when dispatchersUseEncodedPaths=false
---
 .../apache/catalina/core/ApplicationContext.java    |  7 +++++--
 java/org/apache/catalina/core/AsyncContextImpl.java | 21 +++++++++++++++++----
 webapps/docs/changelog.xml                          |  8 ++++++++
 3 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/java/org/apache/catalina/core/ApplicationContext.java b/java/org/apache/catalina/core/ApplicationContext.java
index 76f64f7e94..f8400d3c57 100644
--- a/java/org/apache/catalina/core/ApplicationContext.java
+++ b/java/org/apache/catalina/core/ApplicationContext.java
@@ -372,8 +372,11 @@ public class ApplicationContext implements ServletContext {
         // Remove path parameters
         String uriToMap = org.apache.catalina.util.RequestUtil.stripPathParams(uri, null);
 
+        boolean pathIsEncoded =
+                getContext().getDispatchersUseEncodedPaths() || AsyncContextImpl.uriEncoded.get().booleanValue();
+
         // Decode only if the uri derived from the provided path is expected to be encoded
-        if (getContext().getDispatchersUseEncodedPaths()) {
+        if (pathIsEncoded) {
             uriToMap = UDecoder.URLDecode(uriToMap, StandardCharsets.UTF_8, context.getEncodedSolidusHandlingEnum(),
                     context.getEncodedReverseSolidusHandlingEnum());
         }
@@ -391,7 +394,7 @@ public class ApplicationContext implements ServletContext {
          * getRequestURI() which returns encoded values. getContextPath() returns a decoded value. uri may be encoded or
          * not. Need to prepend the context path to uri and ensure the result is correctly encoded.
          */
-        if (getContext().getDispatchersUseEncodedPaths()) {
+        if (pathIsEncoded) {
             uri = URLEncoder.DEFAULT.encode(getContextPath(), StandardCharsets.UTF_8) + uri;
         } else {
             uri = URLEncoder.DEFAULT.encode(getContextPath() + uri, StandardCharsets.UTF_8);
diff --git a/java/org/apache/catalina/core/AsyncContextImpl.java b/java/org/apache/catalina/core/AsyncContextImpl.java
index d9280ae729..a2a28a25dd 100644
--- a/java/org/apache/catalina/core/AsyncContextImpl.java
+++ b/java/org/apache/catalina/core/AsyncContextImpl.java
@@ -128,6 +128,10 @@ public class AsyncContextImpl implements AsyncContext, AsyncContextCallback {
     private final AtomicBoolean hasErrorProcessingStarted = new AtomicBoolean(false);
     private final AtomicBoolean hasOnErrorReturned = new AtomicBoolean(false);
 
+    /*
+     * ThreadLocal for passing over-ridden URL encoding state when dispatching.
+     */
+    static ThreadLocal<Boolean> uriEncoded = ThreadLocal.withInitial(() -> Boolean.FALSE);
 
     /**
      * Constructs an AsyncContextImpl for the given request.
@@ -219,7 +223,6 @@ public class AsyncContextImpl implements AsyncContext, AsyncContextCallback {
         String path;
         String cpath;
         Request request = this.request;
-        Context context = this.context;
         // Calls check() so local copies are validated
         ServletRequest servletRequest = getRequest();
         if (servletRequest instanceof HttpServletRequest) {
@@ -233,10 +236,20 @@ public class AsyncContextImpl implements AsyncContext, AsyncContextCallback {
         if (cpath.length() > 1) {
             path = path.substring(cpath.length());
         }
-        if (!context.getDispatchersUseEncodedPaths()) {
-            path = UDecoder.URLDecode(path, StandardCharsets.UTF_8);
+        /*
+         * This is a dispatch of the original request path. That path will always be URI-decoded. That will cause
+         * problems in ServletContext.getRequestDispatcher() if the decoded URI contains a literal '?' as it will be
+         * treated as a query delimiter. Therefore, ignore context.getDispatchersUseEncodedPaths() here and always
+         * encode. Also need to mark this request as ignoring context.getDispatchersUseEncodedPaths() so it is handled
+         * correctly in ServletContext.getRequestDispatcher().
+         */
+        path = UDecoder.URLDecode(path, StandardCharsets.UTF_8);
+        try {
+            uriEncoded.set(Boolean.TRUE);
+            dispatch(path);
+        } finally {
+            uriEncoded.set(Boolean.FALSE);
         }
-        dispatch(path);
     }
 
     @Override
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index f94f467190..bad9fc0709 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -210,6 +210,14 @@
         <code>SPNEGO</code> authentication, the user must exist in the database
         for authentication to succeed. (markt)
       </fix>
+      <fix>
+        Improve the handling of <code>AsyncContext.dispatch()</code> when the
+        <code>Context</code> attribute <code>dispatchersUseEncodedPaths</code>
+        is set to <code>false</code> since the application has no control over
+        the path used for the <code>AsyncContext.dispatch()</code>. Prior to
+        this fix, paths containing literal <code>'?'</code> characters were
+        truncated. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.