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


The following commit(s) were added to refs/heads/9.0.x by this push:
     new 5b86b0ef87 Close NIO2 connections gracefully
5b86b0ef87 is described below

commit 5b86b0ef8762eaf9abef5f78ce3b681a5bf4c6f6
Author: Mark Thomas <[email protected]>
AuthorDate: Sat Nov 7 22:18:43 2026 +0000

    Close NIO2 connections gracefully
    
    On Windows, closing an NIO2 connection with a read pending aborted the
    connection so the client saw a reset rather than the end of the stream.
    Shut down output before closing the channel.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 java/org/apache/tomcat/util/net/Nio2Channel.java   | 24 +++++++++++++++++++++-
 .../apache/tomcat/util/net/SecureNio2Channel.java  |  2 +-
 webapps/docs/changelog.xml                         |  6 ++++++
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/Nio2Channel.java 
b/java/org/apache/tomcat/util/net/Nio2Channel.java
index ae27692bd5..3d799f9cfc 100644
--- a/java/org/apache/tomcat/util/net/Nio2Channel.java
+++ b/java/org/apache/tomcat/util/net/Nio2Channel.java
@@ -22,6 +22,7 @@ import java.nio.channels.AsynchronousByteChannel;
 import java.nio.channels.AsynchronousSocketChannel;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.CompletionHandler;
+import java.nio.channels.NotYetConnectedException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -77,7 +78,28 @@ public class Nio2Channel implements AsynchronousByteChannel {
      */
     @Override
     public void close() throws IOException {
-        sc.close();
+        closeChannel();
+    }
+
+
+    /**
+     * Close the underlying channel, sending a FIN to the client before the 
channel is closed. On some platforms
+     * (Windows) closing an asynchronous channel with a read pending aborts 
the connection (the client sees a reset
+     * rather than the end of the stream) and any data not yet read by the 
client may be lost. Shutting down output first
+     * ensures that the client sees a graceful close.
+     *
+     * @throws IOException If an I/O error occurs
+     */
+    protected void closeChannel() throws IOException {
+        try {
+            if (sc.isOpen()) {
+                sc.shutdownOutput();
+            }
+        } catch (IOException | NotYetConnectedException ignore) {
+            // Ignore. The channel is about to be closed and the graceful 
close is on a best efforts basis.
+        } finally {
+            sc.close();
+        }
     }
 
 
diff --git a/java/org/apache/tomcat/util/net/SecureNio2Channel.java 
b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
index 89e238b4ad..deaf8703fc 100644
--- a/java/org/apache/tomcat/util/net/SecureNio2Channel.java
+++ b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
@@ -722,7 +722,7 @@ public class SecureNio2Channel extends Nio2Channel {
         } finally {
             if (force || closed) {
                 closed = true;
-                sc.close();
+                closeChannel();
             }
         }
     }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index ae9e1921bb..b0e5674649 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -572,6 +572,12 @@
         When validating HTTP/2 frames, make sure more serious errors take
         priority over less serious errors. (markt)
       </fix>
+      <fix>
+        Ensure that NIO2 connections are closed gracefully. On Windows, closing
+        an NIO2 connection with a read pending previously aborted the
+        connection, so the client saw a connection reset rather than the end of
+        the stream and could lose response data it had not yet read. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Jasper">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to