This is an automated email from the ASF dual-hosted git repository.

markt 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 52f007711c Ensure onAllDataRead() fires with chunked request body
52f007711c is described below

commit 52f007711c73c4733c9acf4c53d988f193853306
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue Apr 23 13:48:49 2024 +0100

    Ensure onAllDataRead() fires with chunked request body
---
 .../coyote/http11/filters/ChunkedInputFilter.java  | 15 ++++++++
 .../catalina/nonblocking/TestNonBlockingAPI.java   | 45 ++++++++++++++++++++++
 webapps/docs/changelog.xml                         |  5 +++
 3 files changed, 65 insertions(+)

diff --git a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java 
b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
index 13dcb7d53f..c0b74a185f 100644
--- a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -24,6 +24,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.coyote.ActionCode;
 import org.apache.coyote.BadRequestException;
 import org.apache.coyote.InputBuffer;
 import org.apache.coyote.Request;
@@ -167,6 +168,20 @@ public class ChunkedInputFilter implements InputFilter, 
ApplicationBufferHandler
             }
             if (endChunk) {
                 parseEndChunk();
+                if (!request.isRequestThread()) {
+                    /*
+                     * Perform the dispatch back to the container for the 
onAllDataRead() event. For non-chunked input
+                     * this would be performed when isReady() is next called.
+                     *
+                     * Chunked input returns one chunk at a time for 
non-blocking reads. A consequence of this is that
+                     * reading the final chunk returns -1 which signals the 
end of stream. The application code reading
+                     * the request body probably won't call isReady() after 
receiving the -1 return value since it
+                     * already knows it is at end of stream. Therefore we 
trigger the dispatch back to the container
+                     * here which in turn ensures the onAllDataRead() event is 
fired.
+                     */
+                    request.action(ActionCode.DISPATCH_READ, null);
+                    request.action(ActionCode.DISPATCH_EXECUTE, null);
+                }
                 return -1;
             }
         }
diff --git a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java 
b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
index c86a500239..eca6090712 100644
--- a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
+++ b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
@@ -154,6 +154,51 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
     }
 
 
+    @Test
+    public void testNonBlockingReadChunked() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        // No file system docBase required
+        Context ctx = getProgrammaticRootContext();
+
+        NBReadServlet servlet = new NBReadServlet(false, true);
+        String servletName = NBReadServlet.class.getName();
+        Tomcat.addServlet(ctx, servletName, servlet);
+        ctx.addServletMappingDecoded("/", servletName);
+
+        tomcat.start();
+
+        Client client = new Client();
+        client.setPort(getPort());
+        client.setRequest(new String[] { "GET / HTTP/1.1" + 
SimpleHttpClient.CRLF +
+                                         "Host: localhost:" + getPort() + 
SimpleHttpClient.CRLF +
+                                         "Transfer-Encoding: chunked" + 
SimpleHttpClient.CRLF +
+                                         SimpleHttpClient.CRLF +
+                                         "14" + SimpleHttpClient.CRLF +
+                                         "01234567890123456789" + 
SimpleHttpClient.CRLF +
+                                         "14" + SimpleHttpClient.CRLF +
+                                         "012345678901FINISHED" + 
SimpleHttpClient.CRLF +
+                                         "0" + SimpleHttpClient.CRLF +
+                                         SimpleHttpClient.CRLF});
+        client.connect();
+        client.sendRequest();
+
+        client.setUseContentLength(true);
+        client.readResponse(true);
+
+        Assert.assertTrue(client.isResponse200());
+        Assert.assertTrue(client.isResponseBodyOK());
+    }
+
+
+    private static class Client extends SimpleHttpClient {
+        @Override
+        public boolean isResponseBodyOK() {
+            return "OK".equals(getResponseBody());
+        }
+    }
+
+
     @Test
     public void testNonBlockingWrite() throws Exception {
         testNonBlockingWriteInternal(false);
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index c2a903f944..036fed1454 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -164,6 +164,11 @@
         failed. The wait time is now 100ms plus twice
         <code>socket.unlockTimeout</code>. (markt)
       </fix>
+      <fix>
+        Ensure that the <code>onAllDataRead()</code> event is triggered when 
the
+        request body uses chunked encoding and is read using non-blocking IO.
+        (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Other">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to