diff --git a/java/org/apache/coyote/http2/LocalStrings.properties b/java/org/apache/coyote/http2/LocalStrings.properties
index 5f6cadde1108..68baa3d265a2 100644
--- a/java/org/apache/coyote/http2/LocalStrings.properties
+++ b/java/org/apache/coyote/http2/LocalStrings.properties
@@ -121,6 +121,7 @@ streamProcessor.error.connection=Connection [{0}], Stream [{1}], An error occurr
streamProcessor.error.stream=Connection [{0}], Stream [{1}], An error occurred during processing that was fatal to the stream
streamProcessor.flushBufferedWrite.entry=Connection [{0}], Stream [{1}], Flushing buffered writes
streamProcessor.service.error=Error during request processing
+streamProcessor.streamReadTimeout=Stream read timeout
streamStateMachine.debug.change=Connection [{0}], Stream [{1}], State changed from [{2}] to [{3}]
streamStateMachine.invalidFrame=Connection [{0}], Stream [{1}], State [{2}], Frame type [{3}]
diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java
index a0a5f1f7f94c..0151414589b8 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -1075,6 +1075,8 @@ abstract class StreamInputBuffer implements InputBuffer {
abstract boolean isRequestBodyFullyRead();
abstract void insertReplayedBody(ByteChunk body);
+
+ protected abstract boolean timeoutRead(long now);
}
@@ -1101,6 +1103,8 @@ class StandardStreamInputBuffer extends StreamInputBuffer {
// 'write mode'.
private volatile ByteBuffer inBuffer;
private volatile boolean readInterest;
+ // If readInterest is true, data must be available to read no later than this time.
+ private volatile long readTimeoutExpiry;
private volatile boolean closed;
private boolean resetReceived;
@@ -1199,6 +1203,12 @@ final boolean isReadyForRead() {
if (!isRequestBodyFullyRead()) {
readInterest = true;
+ long readTimeout = handler.getProtocol().getStreamReadTimeout();
+ if (readTimeout > 0) {
+ readTimeoutExpiry = System.currentTimeMillis() + handler.getProtocol().getStreamReadTimeout();
+ } else {
+ readTimeoutExpiry = Long.MAX_VALUE;
+ }
}
return false;
@@ -1350,6 +1360,12 @@ final void swallowUnread() throws IOException {
}
}
}
+
+
+ @Override
+ protected boolean timeoutRead(long now) {
+ return readInterest && now > readTimeoutExpiry;
+ }
}
@@ -1411,5 +1427,12 @@ boolean isRequestBodyFullyRead() {
void insertReplayedBody(ByteChunk body) {
// NO-OP
}
+
+
+ @Override
+ protected boolean timeoutRead(long now) {
+ // Reading from a saved request. Will never time out.
+ return false;
+ }
}
}
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
index ab01e7ae7d0d..fdc8c4b160de 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -18,6 +18,7 @@
import java.io.File;
import java.io.IOException;
+import java.net.SocketTimeoutException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
@@ -25,6 +26,7 @@
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletConnection;
import jakarta.servlet.http.HttpServletResponse;
@@ -543,4 +545,22 @@ protected final boolean flushBufferedWrite() throws IOException {
protected final SocketState dispatchEndRequest() throws IOException {
return SocketState.CLOSED;
}
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * First checks for a stream read timeout and processes it if detected. If no stream read timeout is detected then
+ * the superclass is called to check for an asynchronous processing timeout.
+ */
+ @Override
+ public void timeoutAsync(long now) {
+ if (stream.getInputBuffer().timeoutRead(now)) {
+ stream.getCoyoteRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,
+ new SocketTimeoutException(sm.getString("streamProcessor.streamReadTimeout")));
+ processSocketEvent(SocketEvent.ERROR, true);
+ } else {
+ super.timeoutAsync(now);
+ }
+ }
}
diff --git a/test/org/apache/coyote/http2/Http2TestBase.java b/test/org/apache/coyote/http2/Http2TestBase.java
index 714ff14b90dc..dc9c22cb3487 100644
--- a/test/org/apache/coyote/http2/Http2TestBase.java
+++ b/test/org/apache/coyote/http2/Http2TestBase.java
@@ -113,7 +113,7 @@ public static Collection