This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 7ccdb9cfcbe111f027d797466cebcf78ffffd5a0 Author: opencode <[email protected]> AuthorDate: Wed Sep 30 10:53:16 2026 +0200 Return 0 rather than -1 for zero-length reads from InputBuffer at end of stream, as required by the InputStream/Reader contract, and avoid the unnecessary blocking fill these requests previously triggered --- .../org/apache/catalina/connector/InputBuffer.java | 8 +++++ .../apache/catalina/connector/TestInputBuffer.java | 39 ++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/java/org/apache/catalina/connector/InputBuffer.java b/java/org/apache/catalina/connector/InputBuffer.java index 17128b4bec..6135bf2847 100644 --- a/java/org/apache/catalina/connector/InputBuffer.java +++ b/java/org/apache/catalina/connector/InputBuffer.java @@ -412,6 +412,10 @@ public class InputBuffer extends Reader implements ByteChunk.ByteInputChannel, A public int read(byte[] b, int off, int len) throws IOException { throwIfClosed(); + if (len == 0) { + return 0; + } + if (checkByteBufferEof()) { return -1; } @@ -514,6 +518,10 @@ public class InputBuffer extends Reader implements ByteChunk.ByteInputChannel, A public int read(char[] cbuf, int off, int len) throws IOException { throwIfClosed(); + if (len == 0) { + return 0; + } + if (checkCharBufferEof()) { return -1; } diff --git a/test/org/apache/catalina/connector/TestInputBuffer.java b/test/org/apache/catalina/connector/TestInputBuffer.java index bb7ce5ce76..2880a68ace 100644 --- a/test/org/apache/catalina/connector/TestInputBuffer.java +++ b/test/org/apache/catalina/connector/TestInputBuffer.java @@ -18,6 +18,7 @@ package org.apache.catalina.connector; import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.Reader; import java.io.Writer; import java.nio.charset.MalformedInputException; @@ -135,6 +136,26 @@ public class TestInputBuffer extends TomcatBaseTest { } + @Test + public void testZeroLengthReadAtEof() throws Exception { + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + Tomcat.addServlet(root, "ZeroRead", new ZeroReadServlet()); + root.addServletMapping("/test", "ZeroRead"); + + tomcat.start(); + + ByteChunk bc = new ByteChunk(); + Map<String,List<String>> responseHeaders = new HashMap<>(); + int rc = postUrl("abc".getBytes(StandardCharsets.US_ASCII), + "http://localhost:" + getPort() + "/test", bc, responseHeaders); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + // Per the InputStream contract, a zero-length read returns 0, even at EOF + Assert.assertEquals("0", responseHeaders.get("X-Zero").get(0)); + } + + private void doUtf8BodyTest(String description, int[] input, String expected) throws Exception { byte[] bytes = new byte[input.length]; @@ -192,6 +213,24 @@ public class TestInputBuffer extends TomcatBaseTest { } + private static class ZeroReadServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + InputStream is = req.getInputStream(); + while (is.read() >= 0) { + // Drain the body + } + int zero = is.read(new byte[1], 0, 0); + + resp.setHeader("X-Zero", Integer.toString(zero)); + resp.getWriter().write("ok"); + } + } + + private static class SkipServlet extends HttpServlet { private static final long serialVersionUID = 1L; --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
