This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 7e8fd4350e2922c282d92787b08392d1e2949992 Author: Mark Thomas <ma...@apache.org> AuthorDate: Fri Apr 26 16:02:09 2024 +0100 Additional tests for non-blocking chunked reads --- .../catalina/nonblocking/TestNonBlockingAPI.java | 442 ++++++++++++++++++++- .../http11/filters/TestChunkedInputFilter.java | 176 +++++++- 2 files changed, 587 insertions(+), 31 deletions(-) diff --git a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java index 1df7859c2d..e451835a72 100644 --- a/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java +++ b/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java @@ -72,6 +72,9 @@ public class TestNonBlockingAPI extends TomcatBaseTest { private static final Log log = LogFactory.getLog(TestNonBlockingAPI.class); + private static String TRAILER_HEADER_NAME = "x-test"; + private static String TRAILER_HEADER_VALUE = "abcde"; + private static final int CHUNK_SIZE = 1024 * 1024; private static final int WRITE_SIZE = CHUNK_SIZE * 10; private static final byte[] DATA = new byte[WRITE_SIZE]; @@ -130,7 +133,7 @@ public class TestNonBlockingAPI extends TomcatBaseTest { // No file system docBase required Context ctx = getProgrammaticRootContext(); - NBReadServlet servlet = new NBReadServlet(ignoreIsReady, async); + NBReadServlet servlet = new NBReadServlet(ignoreIsReady, async, null); String servletName = NBReadServlet.class.getName(); Tomcat.addServlet(ctx, servletName, servlet); ctx.addServletMappingDecoded("/", servletName); @@ -154,31 +157,416 @@ public class TestNonBlockingAPI extends TomcatBaseTest { @Test - public void testNonBlockingReadChunked() throws Exception { + public void testNonBlockingReadChunkedNoSplits() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitBeforeChunkHeader() throws Exception { + String[] requestBody = new String[] { + "", + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitInChunkHeader() throws Exception { + String[] requestBody = new String[] { + "1", + "4" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterChunkHeader() throws Exception { + String[] requestBody = new String[] { + "14", + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitInHeaderCrlf() throws Exception { + String[] requestBody = new String[] { + "14\r", + "\n" + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterHeaderCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF, + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitBeforeExtensionDelimter() throws Exception { + String[] requestBody = new String[] { + "14", + ";a=b" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterExtensionDelimter() throws Exception { + String[] requestBody = new String[] { + "14;", + "a=b" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitInExtension() throws Exception { + String[] requestBody = new String[] { + "14;a", + "=b" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterExtension() throws Exception { + String[] requestBody = new String[] { + "14;a=b", + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitInChunkBody() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345", + "678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitBeforeChunkBodyCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED", + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitInChunkBodyCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED\r", + "\n" + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterChunkBodyCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF, + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitBeforeEndChunkCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0", + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitInEndChunkCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + + "\r", + "\n" + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterEndChunkCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + + SimpleHttpClient.CRLF, + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitBeforeTrailer() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF, + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitInTrailerName() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + "x-te", + "st" + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterTrailerName() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + TRAILER_HEADER_NAME, + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterTrailerDelimter() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + TRAILER_HEADER_NAME + ":", + " " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitBeforeTrailerValue() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + TRAILER_HEADER_NAME + ": ", + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitInTrailerValue() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + TRAILER_HEADER_NAME + ": abc", + "de" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterTrailerValue() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE, + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitInTrailerCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + "\r", + "\n" + + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitAfterTrailerCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF, + SimpleHttpClient.CRLF}; + + doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); + } + + + @Test + public void testNonBlockingReadChunkedSplitInFinalCrlf() throws Exception { + String[] requestBody = new String[] { + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + + SimpleHttpClient.CRLF + + "\r", + "\n"}; + + doTestNonBlockingReadChunked(requestBody); + } + + + @Test + public void testNonBlockingReadChunkedSplitMaximum() throws Exception { + String requestBody = new String( + "14" + SimpleHttpClient.CRLF + + "012345678901FINISHED" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF); + + String[] requestBodySplit = new String[requestBody.length()]; + for (int i = 0; i < requestBody.length(); i++) { + requestBodySplit[i] = Character.toString(requestBody.charAt(i)); + } + + doTestNonBlockingReadChunked(requestBodySplit, TRAILER_HEADER_VALUE); + } + + + private void doTestNonBlockingReadChunked(String[] requestBody) throws Exception { + doTestNonBlockingReadChunked(requestBody, null); + } + + + private void doTestNonBlockingReadChunked(String[] requestBody, String expectedTrailerFieldValue) throws Exception { Tomcat tomcat = getTomcatInstance(); // No file system docBase required Context ctx = getProgrammaticRootContext(); - NBReadServlet servlet = new NBReadServlet(false, true); + NBReadServlet servlet = new NBReadServlet(false, true, expectedTrailerFieldValue); String servletName = NBReadServlet.class.getName(); Tomcat.addServlet(ctx, servletName, servlet); ctx.addServletMappingDecoded("/", servletName); + tomcat.getConnector().setProperty("allowedTrailerHeaders", TRAILER_HEADER_NAME); + tomcat.start(); + // Add the headers to the first part of the chunked body + requestBody[0] = + "GET / HTTP/1.1" + SimpleHttpClient.CRLF + + "Host: localhost" + getPort() + SimpleHttpClient.CRLF + + "Transfer-Encoding: chunked" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF + + requestBody[0]; + Client client = new Client(); client.setPort(getPort()); - client.setRequest(new String[] { "GET / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Transfer-Encoding: chunked" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "14" + SimpleHttpClient.CRLF + - "01234567890123456789" + SimpleHttpClient.CRLF + - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}); + client.setRequest(requestBody); + /* + * Reduce default pause to speed up test execution. Pause only needs to be long enough that each part of the + * request is read separately. + */ + client.setRequestPause(200); client.connect(); client.sendRequest(); @@ -575,11 +963,13 @@ public class TestNonBlockingAPI extends TomcatBaseTest { private static final long serialVersionUID = 1L; private final boolean async; private final boolean ignoreIsReady; + private final String expectedTrailerFieldValue; transient TestReadListener listener; - public NBReadServlet(boolean ignoreIsReady, boolean async) { + public NBReadServlet(boolean ignoreIsReady, boolean async, String expectedTrailerFieldValue) { this.async = async; this.ignoreIsReady = ignoreIsReady; + this.expectedTrailerFieldValue = expectedTrailerFieldValue; } @Override @@ -616,9 +1006,9 @@ public class TestNonBlockingAPI extends TomcatBaseTest { // step 2 - notify on read ServletInputStream in = req.getInputStream(); if (async) { - listener = new TestAsyncReadListener(actx, false, ignoreIsReady); + listener = new TestAsyncReadListener(actx, false, ignoreIsReady, expectedTrailerFieldValue); } else { - listener = new TestReadListener(actx, false, ignoreIsReady); + listener = new TestReadListener(actx, false, ignoreIsReady, expectedTrailerFieldValue); } in.setReadListener(listener); } @@ -707,15 +1097,18 @@ public class TestNonBlockingAPI extends TomcatBaseTest { protected final AsyncContext ctx; protected final boolean usingNonBlockingWrite; protected final boolean ignoreIsReady; + protected final String expectedTrailerFieldValue; protected final StringBuilder body = new StringBuilder(); TestReadListener(AsyncContext ctx, boolean usingNonBlockingWrite, - boolean ignoreIsReady) { + boolean ignoreIsReady, + String expectedTrailerFieldValue) { this.ctx = ctx; this.usingNonBlockingWrite = usingNonBlockingWrite; this.ignoreIsReady = ignoreIsReady; + this.expectedTrailerFieldValue = expectedTrailerFieldValue; } @Override @@ -743,7 +1136,14 @@ public class TestNonBlockingAPI extends TomcatBaseTest { if (!usingNonBlockingWrite) { String msg; if (body.toString().endsWith("FINISHED")) { - msg = "OK"; + String trailerFieldValue = ((HttpServletRequest) ctx.getRequest()).getTrailerFields().get("x-test"); + if (trailerFieldValue == null && expectedTrailerFieldValue == null || + trailerFieldValue != null && trailerFieldValue.equals(expectedTrailerFieldValue)) { + msg = "OK"; + } else { + System.out.println("Trailer value was [" + trailerFieldValue + "]"); + msg = "FAILED"; + } } else { msg = "FAILED"; } @@ -770,9 +1170,9 @@ public class TestNonBlockingAPI extends TomcatBaseTest { AtomicInteger containerThreadCount = new AtomicInteger(0); AtomicInteger nonContainerThreadCount = new AtomicInteger(0); - TestAsyncReadListener(AsyncContext ctx, - boolean usingNonBlockingWrite, boolean ignoreIsReady) { - super(ctx, usingNonBlockingWrite, ignoreIsReady); + TestAsyncReadListener(AsyncContext ctx, boolean usingNonBlockingWrite, boolean ignoreIsReady, + String expectedTrailerFieldValue) { + super(ctx, usingNonBlockingWrite, ignoreIsReady, expectedTrailerFieldValue); } @Override diff --git a/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java b/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java index 4cb41304df..9a0d41eff6 100644 --- a/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java +++ b/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java @@ -30,6 +30,7 @@ import org.junit.Test; import org.apache.catalina.Context; import org.apache.catalina.startup.SimpleHttpClient; +import org.apache.catalina.startup.TesterServlet; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; @@ -65,7 +66,7 @@ public class TestChunkedInputFilter extends TomcatBaseTest { @Test public void testFirstTrailingHeadersLF() throws Exception { - doTestChunkingCRLF(true, true, false, true, true, true); + doTestChunkingCRLF(true, true, false, true, true, false); } @Test @@ -75,7 +76,7 @@ public class TestChunkedInputFilter extends TomcatBaseTest { @Test public void testSecondTrailingHeadersLF() throws Exception { - doTestChunkingCRLF(true, true, true, false, true, true); + doTestChunkingCRLF(true, true, true, false, true, false); } @Test @@ -157,8 +158,54 @@ public class TestChunkedInputFilter extends TomcatBaseTest { } } + + @Test + public void testTrailingHeadersSizeLimitBelowLimit() throws Exception { + doTestTrailingHeadersSizeLimit(17, "x-trailer: Test", false); + } + + + @Test + public void testTrailingHeadersSizeLimitAtLimit() throws Exception { + doTestTrailingHeadersSizeLimit(18, "x-trailer: Test", false); + } + + + @Test + public void testTrailingHeadersSizeLimitAboveLimit() throws Exception { + doTestTrailingHeadersSizeLimit(19, "x-trailer: Test", true); + } + + + /* + * This test uses the fact that the header is simply concatenated to insert a pipelined request. The pipelined + * request should not trigger the trailing header size limit. Note that 19 is just enough for the first request. + */ @Test - public void testTrailingHeadersSizeLimit() throws Exception { + public void testTrailingHeadersSizeLimitPipelining() throws Exception { + doTestTrailingHeadersSizeLimit(19, + "x-trailer: Test" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF + + "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + + "Host: any" + SimpleHttpClient.CRLF + + "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + "Content-Type: application/x-www-form-urlencoded" + SimpleHttpClient.CRLF + + "Connection: close" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF + + "3" + SimpleHttpClient.CRLF + + "a=0" + SimpleHttpClient.CRLF + + "4" + SimpleHttpClient.CRLF + + "&b=1" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + "x-trailer: Test", + true); + } + + + /* + * Since limit includes CRLF at end of trailer and final CRLF + */ + private void doTestTrailingHeadersSizeLimit(int trailerSizeLimit, String trailerHeader, boolean pass) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -169,15 +216,14 @@ public class TestChunkedInputFilter extends TomcatBaseTest { ctx.addServletMappingDecoded("/", "servlet"); // Limit the size of the trailing header - Assert.assertTrue(tomcat.getConnector().setProperty("maxTrailerSize", "10")); + Assert.assertTrue(tomcat.getConnector().setProperty("maxTrailerSize", Integer.toString(trailerSizeLimit))); tomcat.start(); String[] request = new String[]{ "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + SimpleHttpClient.CRLF + "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + - "Content-Type: application/x-www-form-urlencoded" + - SimpleHttpClient.CRLF + + "Content-Type: application/x-www-form-urlencoded" + SimpleHttpClient.CRLF + "Connection: close" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF + "3" + SimpleHttpClient.CRLF + @@ -185,7 +231,7 @@ public class TestChunkedInputFilter extends TomcatBaseTest { "4" + SimpleHttpClient.CRLF + "&b=1" + SimpleHttpClient.CRLF + "0" + SimpleHttpClient.CRLF + - "x-trailer: Test" + SimpleHttpClient.CRLF + + trailerHeader + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF }; TrailerClient client = @@ -194,9 +240,11 @@ public class TestChunkedInputFilter extends TomcatBaseTest { client.connect(); client.processRequest(); - // Expected to fail because the trailers are longer - // than the set limit of 10 bytes - Assert.assertTrue(client.isResponse500()); + if (pass) { + Assert.assertTrue(client.isResponse200()); + } else { + Assert.assertTrue(client.isResponse500()); + } } @@ -621,4 +669,112 @@ public class TestChunkedInputFilter extends TomcatBaseTest { return getResponseBody().contains("TestTestTest"); } } + + + @Test + public void doTestIncompleteChunkedBody() throws Exception { + + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = getProgrammaticRootContext(); + + Tomcat.addServlet(ctx, "servlet", new SwallowBodyServlet(false)); + ctx.addServletMappingDecoded("/", "servlet"); + + tomcat.start(); + + String[] request = new String[]{ + "POST / HTTP/1.1" + SimpleHttpClient.CRLF + + "Host: localhost" + SimpleHttpClient.CRLF + + "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF + + "3" + SimpleHttpClient.CRLF }; + + TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); + client.setUseContentLength(true); + + client.setRequest(request); + client.connect(); + try { + client.processRequest(); + } catch (IOException ioe) { + // Ignore - Triggered by connection being dropped after error + } + // NIO2 may (will?) return null here + String responseLine = client.getResponseLine(); + if (responseLine == null) { + // 400 response not read(/written?) before connection was dropped. + } else { + Assert.assertTrue(client.getResponseLine(), client.isResponse400()); + } + } + + + @Test + public void doTestMaxSwallowSizeBelow() throws Exception { + doTestMaxSwallowSize(1000, true); + } + + + @Test + public void doTestMaxSwallowSizeAbove() throws Exception { + doTestMaxSwallowSize(10, false); + } + + + private void doTestMaxSwallowSize(int maxSwallowSize, boolean pass) throws Exception { + + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + tomcat.getConnector().setProperty("connectionTimeout", "300000"); + // Reduce limits to facilitate testing + tomcat.getConnector().setProperty("maxSwallowSize", Integer.toString(maxSwallowSize)); + + // No file system docBase required + Context ctx = getProgrammaticRootContext(); + + Tomcat.addServlet(ctx, "servlet", new TesterServlet(false)); + ctx.addServletMappingDecoded("/", "servlet"); + + tomcat.start(); + + String[] request = new String[]{ + "GET / HTTP/1.1" + SimpleHttpClient.CRLF + + "Host: localhost" + SimpleHttpClient.CRLF + + "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF + + "20" + SimpleHttpClient.CRLF + + "01234567890123456789012345678901" + SimpleHttpClient.CRLF + + "0" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF }; + + TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); + client.setUseContentLength(true); + + client.setRequest(request); + client.connect(); + client.sendRequest(); + client.readResponse(true); + + // Response is committed before connection is closed. + Assert.assertTrue(client.getResponseLine(), client.isResponse200()); + + // Repeat request - should fail + client.resetResponse(); + client.sendRequest(); + try { + client.readResponse(true); + } catch (IOException ioe) { + // Ignore - in case the read fails due to a closed connection + } + if (pass) { + Assert.assertTrue(client.getResponseLine(), client.isResponse200()); + } else { + // Connection reset + Assert.assertNull(client.getResponseLine()); + } + } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org