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

commit ffb5df79f6a74483339b3f5a21574e1e3a5ea302
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Oct 20 17:33:05 2026 +0100

    Fix NIO2 handling of end of input
    
    Co-authored by: Claude / Sonnet 5.5
---
 java/org/apache/tomcat/util/net/Nio2Endpoint.java | 56 +++++++++++++++++------
 1 file changed, 43 insertions(+), 13 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/Nio2Endpoint.java 
b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
index 0391f63901..a1a46a2616 100644
--- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java
+++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
@@ -511,6 +511,7 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
         private final CompletionHandler<Integer,ByteBuffer> 
readCompletionHandler;
         private boolean readInterest = false; // Guarded by 
readCompletionHandler
         private boolean readNotify = false;
+        private boolean readEof = false; // Guarded by readCompletionHandler
 
         private final CompletionHandler<Integer,ByteBuffer> 
writeCompletionHandler;
         private final CompletionHandler<Long,ByteBuffer[]> 
gatheringWriteCompletionHandler;
@@ -613,17 +614,18 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
                     synchronized (readCompletionHandler) {
                         readNotify = false;
                         if (nBytes.intValue() < 0) {
-                            failed(new EOFException(), attachment);
+                            // End of input is not an error. Record it so the
+                            // next read reports it to the caller as -1.
+                            readEof = true;
+                        }
+                        if (readInterest && !isInline()) {
+                            readNotify = true;
                         } else {
-                            if (readInterest && !isInline()) {
-                                readNotify = true;
-                            } else {
-                                // Release here since there will be no
-                                // notify/dispatch to do the release.
-                                readPending.release();
-                            }
-                            readInterest = false;
+                            // Release here since there will be no
+                            // notify/dispatch to do the release.
+                            readPending.release();
                         }
+                        readInterest = false;
                         notify = readNotify;
                     }
                     if (notify) {
@@ -785,8 +787,14 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
                     readPending.release();
                     return true;
                 }
-                // Try to read some data
-                boolean isReady = fillReadBuffer(false) > 0;
+                // End of input has been reached. A read will return -1.
+                if (readEof) {
+                    readPending.release();
+                    return true;
+                }
+                // Try to read some data. A negative value is end of input and
+                // a read is possible (it will return -1).
+                boolean isReady = fillReadBuffer(false) != 0;
                 if (!isReady) {
                     readInterest = true;
                 }
@@ -857,6 +865,17 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
             }
 
             int nRead = populateReadBuffer(b, off, len);
+            if (nRead <= 0) {
+                synchronized (readCompletionHandler) {
+                    if (readEof) {
+                        // Buffer is empty and the end of input has been
+                        // reached
+                        readNotify = false;
+                        readPending.release();
+                        return -1;
+                    }
+                }
+            }
             if (nRead > 0) {
                 // The code that was notified is now reading its data
                 synchronized (readCompletionHandler) {
@@ -921,6 +940,17 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
             }
 
             int nRead = populateReadBuffer(to);
+            if (nRead <= 0) {
+                synchronized (readCompletionHandler) {
+                    if (readEof) {
+                        // Buffer is empty and the end of input has been
+                        // reached
+                        readNotify = false;
+                        readPending.release();
+                        return -1;
+                    }
+                }
+            }
             if (nRead > 0) {
                 // The code that was notified is now reading its data
                 synchronized (readCompletionHandler) {
@@ -1161,7 +1191,7 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
                 getSocket().read(to, toTimeout(getReadTimeout()), 
TimeUnit.MILLISECONDS, to, readCompletionHandler);
                 endInline();
                 if (readPending.availablePermits() == 1) {
-                    nRead = to.position();
+                    nRead = readEof ? -1 : to.position();
                 }
             }
             return nRead;
@@ -1389,7 +1419,7 @@ public class Nio2Endpoint extends 
AbstractJsseEndpoint<Nio2Channel,AsynchronousS
                 if (readPending.tryAcquire()) {
                     // No read pending, so do a read
                     try {
-                        if (fillReadBuffer(false) > 0) {
+                        if (fillReadBuffer(false) != 0) {
                             // Special case where the read completed inline, 
there is no notification
                             // in that case so it has to be done here
                             if (!getEndpoint().processSocket(this, 
SocketEvent.OPEN_READ, true)) {


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

Reply via email to