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
commit 5d989435c1a8445159a1104ba9be5a3ab05ce736 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 024a97c47a..e00513fc14 100644 --- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java +++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java @@ -522,6 +522,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; @@ -627,17 +628,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) { @@ -799,8 +801,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; } @@ -871,6 +879,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) { @@ -935,6 +954,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) { @@ -1175,7 +1205,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; @@ -1441,7 +1471,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]
