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

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


The following commit(s) were added to refs/heads/8.5.x by this push:
     new 582ff39  Improve handling of I/O errors during non-blocking writes
582ff39 is described below

commit 582ff396768c911ddacc35bc7b5bdaad537d8fea
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Wed Mar 3 15:12:51 2021 +0000

    Improve handling of I/O errors during non-blocking writes
---
 .../apache/catalina/connector/CoyoteAdapter.java   |  9 +++--
 .../catalina/nonblocking/TestNonBlockingAPI.java   | 42 ++++++++++++++++++----
 webapps/docs/changelog.xml                         |  5 +++
 3 files changed, 45 insertions(+), 11 deletions(-)

diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java 
b/java/org/apache/catalina/connector/CoyoteAdapter.java
index 1c4d694..9d06af9 100644
--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
+++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -169,12 +169,11 @@ public class CoyoteAdapter implements Adapter {
                     if (res.getWriteListener() != null) {
                         res.getWriteListener().onError(t);
                     }
+                    res.action(ActionCode.CLOSE_NOW, t);
+                    asyncConImpl.setErrorState(t, true);
                 } finally {
                     context.unbind(false, oldCL);
                 }
-                if (t != null) {
-                    asyncConImpl.setErrorState(t, true);
-                }
             }
 
             // Check to see if non-blocking writes or reads are being used
@@ -202,8 +201,8 @@ public class CoyoteAdapter implements Adapter {
                         // Therefore no need to set success=false as that 
would trigger a
                         // second call to AbstractProcessor.setErrorState()
                         // https://bz.apache.org/bugzilla/show_bug.cgi?id=65001
-                        res.action(ActionCode.CLOSE_NOW, t);
                         writeListener.onError(t);
+                        res.action(ActionCode.CLOSE_NOW, t);
                         asyncConImpl.setErrorState(t, true);
                     } finally {
                         context.unbind(false, oldCL);
@@ -235,8 +234,8 @@ public class CoyoteAdapter implements Adapter {
                         // Therefore no need to set success=false as that 
would trigger a
                         // second call to AbstractProcessor.setErrorState()
                         // https://bz.apache.org/bugzilla/show_bug.cgi?id=65001
-                        res.action(ActionCode.CLOSE_NOW, t);
                         readListener.onError(t);
+                        res.action(ActionCode.CLOSE_NOW, t);
                         asyncConImpl.setErrorState(t, true);
                     } finally {
                         context.unbind(false, oldCL);
diff --git a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java 
b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
index cd8321e..6217083 100644
--- a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
+++ b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java
@@ -153,11 +153,13 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
     }
 
     private void testNonBlockingWriteInternal(boolean keepAlive) throws 
Exception {
+        AtomicBoolean asyncContextIsComplete = new AtomicBoolean(false);
+
         Tomcat tomcat = getTomcatInstance();
         // No file system docBase required
         Context ctx = tomcat.addContext("", null);
 
-        NBWriteServlet servlet = new NBWriteServlet();
+        NBWriteServlet servlet = new NBWriteServlet(asyncContextIsComplete);
         String servletName = NBWriteServlet.class.getName();
         Tomcat.addServlet(ctx, servletName, servlet);
         ctx.addServletMappingDecoded("/", servletName);
@@ -313,11 +315,25 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
         }
 
         Assert.assertEquals(WRITE_SIZE, totalBodyRead);
+        Assert.assertTrue("AsyncContext should have been completed.", 
asyncContextIsComplete.get());
     }
 
 
     @Test
-    public void testNonBlockingWriteError01() throws Exception {
+    public void testNonBlockingWriteError01ListenerComplete() throws Exception 
{
+        doTestNonBlockingWriteError01NoListenerComplete(true);
+    }
+
+
+    @Test
+    public void testNonBlockingWriteError01NoListenerComplete() throws 
Exception {
+        doTestNonBlockingWriteError01NoListenerComplete(false);
+    }
+
+
+    private void doTestNonBlockingWriteError01NoListenerComplete(boolean 
listenerCompletesOnError) throws Exception {
+        AtomicBoolean asyncContextIsComplete = new AtomicBoolean(false);
+
         Tomcat tomcat = getTomcatInstance();
 
         // No file system docBase required
@@ -329,7 +345,7 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
         // Some CI platforms appear to have particularly large write buffers
         // and appear to ignore the socket.txBufSize below. Therefore, 
configure
         // configure the Servlet to keep writing until an error is encountered.
-        NBWriteServlet servlet = new NBWriteServlet(true);
+        NBWriteServlet servlet = new NBWriteServlet(asyncContextIsComplete, 
true, listenerCompletesOnError);
         String servletName = NBWriteServlet.class.getName();
         Tomcat.addServlet(ctx, servletName, servlet);
         ctx.addServletMappingDecoded("/", servletName);
@@ -392,12 +408,18 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
             count ++;
         }
 
+        while (count < 100 && !asyncContextIsComplete.get()) {
+            Thread.sleep(100);
+            count ++;
+        }
+
         while (count < 100 && alv.getEntryCount() < 1) {
             Thread.sleep(100);
             count ++;
         }
 
         Assert.assertTrue("Error listener should have been invoked.", 
servlet.wlistener.onErrorInvoked);
+        Assert.assertTrue("Async context should have been completed.", 
asyncContextIsComplete.get());
 
         // TODO Figure out why non-blocking writes with the NIO connector 
appear
         // to be slower on Linux
@@ -546,16 +568,20 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
     @WebServlet(asyncSupported = true)
     public static class NBWriteServlet extends TesterServlet {
         private static final long serialVersionUID = 1L;
+        private final AtomicBoolean asyncContextIsComplete;
         private final boolean unlimited;
+        private final boolean listenerCompletesOnError;
         public transient volatile TestWriteListener wlistener;
 
-        public NBWriteServlet() {
-            this(false);
+        public NBWriteServlet(AtomicBoolean asyncContextIsComplete) {
+            this(asyncContextIsComplete, false, true);
         }
 
 
-        public NBWriteServlet(boolean unlimited) {
+        public NBWriteServlet(AtomicBoolean asyncContextIsComplete, boolean 
unlimited, boolean listenerCompletesOnError) {
+            this.asyncContextIsComplete = asyncContextIsComplete;
             this.unlimited = unlimited;
+            this.listenerCompletesOnError = listenerCompletesOnError;
         }
 
 
@@ -579,11 +605,15 @@ public class TestNonBlockingAPI extends TomcatBaseTest {
                 @Override
                 public void onError(AsyncEvent event) throws IOException {
                     log.info("AsyncListener.onError");
+                    if (listenerCompletesOnError) {
+                        event.getAsyncContext().complete();
+                    }
                 }
 
                 @Override
                 public void onComplete(AsyncEvent event) throws IOException {
                     log.info("onComplete");
+                    asyncContextIsComplete.set(true);
                 }
             });
             // step 2 - notify on read
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index eeca5be..880dad9 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -163,6 +163,11 @@
         Make handling of OpenSSL read errors more robust when plain text data 
is
         reported to be available to read. (markt)
       </fix>
+      <fix>
+        Correct handling of write errors during non-blocking I/O to ensure that
+        the associated <code>AsyncContext</code> was closed down correctly.
+        (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Web applications">


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

Reply via email to