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]