203 lines
9.2 KiB
Diff
203 lines
9.2 KiB
Diff
|
|
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties
|
||
|
|
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties 2019-06-09 20:45:15.320000000 -0400
|
||
|
|
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties 2019-06-09 20:46:36.793000000 -0400
|
||
|
|
@@ -98,6 +98,7 @@ stream.writeTimeout=Timeout waiting for
|
||
|
|
stream.inputBuffer.copy=Copying [{0}] bytes from inBuffer to outBuffer
|
||
|
|
stream.inputBuffer.dispatch=Data added to inBuffer when read interest is registered. Triggering a read dispatch
|
||
|
|
stream.inputBuffer.empty=The Stream input buffer is empty. Waiting for more data
|
||
|
|
+stream.inputBuffer.readTimeout=Timeout waiting to read data from client
|
||
|
|
stream.inputBuffer.reset=Stream reset
|
||
|
|
stream.inputBuffer.signal=Data added to inBuffer when read thread is waiting. Signalling that thread to continue
|
||
|
|
|
||
|
|
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
|
||
|
|
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:45:15.321000000 -0400
|
||
|
|
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:48:21.509000000 -0400
|
||
|
|
@@ -888,10 +888,22 @@ class Stream extends AbstractStream impl
|
||
|
|
if (log.isDebugEnabled()) {
|
||
|
|
log.debug(sm.getString("stream.inputBuffer.empty"));
|
||
|
|
}
|
||
|
|
- inBuffer.wait();
|
||
|
|
+
|
||
|
|
+ inBuffer.wait(handler.getProtocol().getStreamReadTimeout());
|
||
|
|
+
|
||
|
|
if (reset) {
|
||
|
|
throw new IOException(sm.getString("stream.inputBuffer.reset"));
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+ if (inBuffer.position() == 0) {
|
||
|
|
+ String msg = sm.getString("stream.inputBuffer.readTimeout");
|
||
|
|
+ StreamException se = new StreamException(
|
||
|
|
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
|
||
|
|
+ // Trigger a reset once control returns to Tomcat
|
||
|
|
+ coyoteResponse.setError();
|
||
|
|
+ streamOutputBuffer.reset = se;
|
||
|
|
+ throw new CloseNowException(msg, se);
|
||
|
|
+ }
|
||
|
|
} catch (InterruptedException e) {
|
||
|
|
// Possible shutdown / rst or similar. Use an
|
||
|
|
// IOException to signal to the client that further I/O
|
||
|
|
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java
|
||
|
|
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java 2019-06-09 20:45:15.323000000 -0400
|
||
|
|
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java 2019-06-09 20:53:54.809000000 -0400
|
||
|
|
@@ -28,6 +28,7 @@ import java.nio.charset.StandardCharsets
|
||
|
|
import java.util.ArrayList;
|
||
|
|
import java.util.List;
|
||
|
|
import java.util.Locale;
|
||
|
|
+import java.util.Map;
|
||
|
|
import java.util.Random;
|
||
|
|
|
||
|
|
import javax.net.SocketFactory;
|
||
|
|
@@ -300,6 +301,22 @@ public abstract class Http2TestBase exte
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
+ protected void sendParameterPostRequest(int streamId, byte[] padding, String body,
|
||
|
|
+ long contentLength, boolean useExpectation) throws IOException {
|
||
|
|
+ byte[] headersFrameHeader = new byte[9];
|
||
|
|
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
|
||
|
|
+ byte[] dataFrameHeader = new byte[9];
|
||
|
|
+ ByteBuffer dataPayload = ByteBuffer.allocate(128);
|
||
|
|
+
|
||
|
|
+ buildPostRequest(headersFrameHeader, headersPayload, useExpectation,
|
||
|
|
+ "application/x-www-form-urlencoded", contentLength, "/parameter", dataFrameHeader,
|
||
|
|
+ dataPayload, padding, null, null, streamId);
|
||
|
|
+ writeFrame(headersFrameHeader, headersPayload);
|
||
|
|
+ if (body != null) {
|
||
|
|
+ dataPayload.put(body.getBytes(StandardCharsets.ISO_8859_1));
|
||
|
|
+ writeFrame(dataFrameHeader, dataPayload);
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
|
||
|
|
protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
|
||
|
|
boolean useExpectation, byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
|
||
|
|
@@ -311,14 +328,29 @@ public abstract class Http2TestBase exte
|
||
|
|
protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
|
||
|
|
boolean useExpectation, byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
|
||
|
|
byte[] trailersFrameHeader, ByteBuffer trailersPayload, int streamId) {
|
||
|
|
+ buildPostRequest(headersFrameHeader, headersPayload, useExpectation, null, -1, "/simple",
|
||
|
|
+ dataFrameHeader, dataPayload, padding, trailersFrameHeader, trailersPayload, streamId);
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
|
||
|
|
+ boolean useExpectation, String contentType, long contentLength, String path,
|
||
|
|
+ byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
|
||
|
|
+ byte[] trailersFrameHeader, ByteBuffer trailersPayload, int streamId) {
|
||
|
|
+
|
||
|
|
MimeHeaders headers = new MimeHeaders();
|
||
|
|
headers.addValue(":method").setString("POST");
|
||
|
|
headers.addValue(":scheme").setString("http");
|
||
|
|
- headers.addValue(":path").setString("/simple");
|
||
|
|
+ headers.addValue(":path").setString(path);
|
||
|
|
headers.addValue(":authority").setString("localhost:" + getPort());
|
||
|
|
if (useExpectation) {
|
||
|
|
headers.addValue("expect").setString("100-continue");
|
||
|
|
}
|
||
|
|
+ if (contentType != null) {
|
||
|
|
+ headers.addValue("content-type").setString(contentType);
|
||
|
|
+ }
|
||
|
|
+ if (contentLength > -1) {
|
||
|
|
+ headers.addValue("content-length").setLong(contentLength);
|
||
|
|
+ }
|
||
|
|
hpackEncoder.encode(headers, headersPayload);
|
||
|
|
|
||
|
|
headersPayload.flip();
|
||
|
|
@@ -507,6 +539,8 @@ public abstract class Http2TestBase exte
|
||
|
|
ctxt.addServletMappingDecoded("/large", "large");
|
||
|
|
Tomcat.addServlet(ctxt, "cookie", new CookieServlet());
|
||
|
|
ctxt.addServletMappingDecoded("/cookie", "cookie");
|
||
|
|
+ Tomcat.addServlet(ctxt, "parameter", new ParameterServlet());
|
||
|
|
+ ctxt.addServletMappingDecoded("/parameter", "parameter");
|
||
|
|
|
||
|
|
tomcat.start();
|
||
|
|
}
|
||
|
|
@@ -1205,6 +1239,24 @@ public abstract class Http2TestBase exte
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
+
|
||
|
|
+ static class ParameterServlet extends HttpServlet {
|
||
|
|
+
|
||
|
|
+ private static final long serialVersionUID = 1L;
|
||
|
|
+
|
||
|
|
+ @Override
|
||
|
|
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
|
||
|
|
+ throws ServletException, IOException {
|
||
|
|
+
|
||
|
|
+ Map<String,String[]> params = req.getParameterMap();
|
||
|
|
+
|
||
|
|
+ resp.setContentType("text/plain");
|
||
|
|
+ resp.setCharacterEncoding("UTF-8");
|
||
|
|
+
|
||
|
|
+ resp.getWriter().print(params.size());
|
||
|
|
+ }
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
|
||
|
|
static class SettingValue {
|
||
|
|
private final int setting;
|
||
|
|
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java
|
||
|
|
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java 2019-06-09 20:45:15.323000000 -0400
|
||
|
|
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java 2019-06-09 20:57:22.652000000 -0400
|
||
|
|
@@ -26,7 +26,6 @@ public class TestHttp2Timeouts extends H
|
||
|
|
@Before
|
||
|
|
public void http2Connect() throws Exception {
|
||
|
|
super.http2Connect();
|
||
|
|
- sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
@@ -36,7 +35,7 @@ public class TestHttp2Timeouts extends H
|
||
|
|
*/
|
||
|
|
@Test
|
||
|
|
public void testClientWithEmptyWindow() throws Exception {
|
||
|
|
- sendSimpleGetRequest(3);
|
||
|
|
+ sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
|
||
|
|
|
||
|
|
// Settings
|
||
|
|
parser.readFrame(false);
|
||
|
|
@@ -57,6 +56,7 @@ public class TestHttp2Timeouts extends H
|
||
|
|
*/
|
||
|
|
@Test
|
||
|
|
public void testClientWithEmptyWindowLargeResponse() throws Exception {
|
||
|
|
+ sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
|
||
|
|
sendLargeGetRequest(3);
|
||
|
|
|
||
|
|
// Settings
|
||
|
|
@@ -70,4 +70,36 @@ public class TestHttp2Timeouts extends H
|
||
|
|
Assert.assertEquals("3-RST-[11]\n", output.getTrace());
|
||
|
|
}
|
||
|
|
|
||
|
|
+ /*
|
||
|
|
+ * Timeout with app reading request body directly.
|
||
|
|
+ */
|
||
|
|
+ @Test
|
||
|
|
+ public void testClientPostsNoBody() throws Exception {
|
||
|
|
+ sendSimplePostRequest(3, null, false);
|
||
|
|
+
|
||
|
|
+ // Headers
|
||
|
|
+ parser.readFrame(false);
|
||
|
|
+ output.clearTrace();
|
||
|
|
+
|
||
|
|
+ parser.readFrame(false);
|
||
|
|
+
|
||
|
|
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+
|
||
|
|
+ /*
|
||
|
|
+ * Timeout with app processing parameters.
|
||
|
|
+ */
|
||
|
|
+ @Test
|
||
|
|
+ public void testClientPostsNoParameters() throws Exception {
|
||
|
|
+ sendParameterPostRequest(3, null, null, 10, false);
|
||
|
|
+
|
||
|
|
+ // Headers
|
||
|
|
+ parser.readFrame(false);
|
||
|
|
+ output.clearTrace();
|
||
|
|
+
|
||
|
|
+ parser.readFrame(false);
|
||
|
|
+
|
||
|
|
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
|
||
|
|
+ }
|
||
|
|
}
|