Author: markt Date: Thu Jun 19 09:06:39 2014 New Revision: 1603770 URL: http://svn.apache.org/r1603770 Log: Add a new limit, defaulting to 2MB, for the amount of data Tomcat will swallow for an aborted upload.
Modified: tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java tomcat/trunk/java/org/apache/coyote/http11/Http11AprProcessor.java tomcat/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Processor.java tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Protocol.java tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java tomcat/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java tomcat/trunk/java/org/apache/coyote/http11/Http11Protocol.java tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityInputFilter.java tomcat/trunk/java/org/apache/coyote/http11/filters/LocalStrings.properties tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java tomcat/trunk/webapps/docs/changelog.xml tomcat/trunk/webapps/docs/config/http.xml Modified: tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java Thu Jun 19 09:06:39 2014 @@ -647,14 +647,15 @@ public abstract class AbstractHttp11Proc /** * Initialize standard input and output filters. */ - protected void initializeFilters(int maxTrailerSize, int maxExtensionSize) { + protected void initializeFilters(int maxTrailerSize, int maxExtensionSize, + int maxSwallowSize) { // Create and add the identity filters. - getInputBuffer().addFilter(new IdentityInputFilter()); + getInputBuffer().addFilter(new IdentityInputFilter(maxSwallowSize)); getOutputBuffer().addFilter(new IdentityOutputFilter()); // Create and add the chunked filters. getInputBuffer().addFilter( - new ChunkedInputFilter(maxTrailerSize, maxExtensionSize)); + new ChunkedInputFilter(maxTrailerSize, maxExtensionSize, maxSwallowSize)); getOutputBuffer().addFilter(new ChunkedOutputFilter()); // Create and add the void filters. Modified: tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java Thu Jun 19 09:06:39 2014 @@ -155,6 +155,16 @@ public abstract class AbstractHttp11Prot /** + * Maximum amount of request body to swallow. + */ + private int maxSwallowSize = 2 * 1024 * 1024; + public int getMaxSwallowSize() { return maxSwallowSize; } + public void setMaxSwallowSize(int maxSwallowSize) { + this.maxSwallowSize = maxSwallowSize; + } + + + /** * This field indicates if the protocol is treated as if it is secure. This * normally means https is being used but can be used to fake https e.g * behind a reverse proxy. Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11AprProcessor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11AprProcessor.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11AprProcessor.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11AprProcessor.java Thu Jun 19 09:06:39 2014 @@ -59,7 +59,7 @@ public class Http11AprProcessor extends public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint, - int maxTrailerSize, int maxExtensionSize) { + int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { super(endpoint); @@ -69,7 +69,7 @@ public class Http11AprProcessor extends outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize); response.setOutputBuffer(outputBuffer); - initializeFilters(maxTrailerSize, maxExtensionSize); + initializeFilters(maxTrailerSize, maxExtensionSize, maxSwallowSize); } Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java Thu Jun 19 09:06:39 2014 @@ -319,7 +319,8 @@ public class Http11AprProtocol extends A protected Http11AprProcessor createProcessor() { Http11AprProcessor processor = new Http11AprProcessor( proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint, - proto.getMaxTrailerSize(), proto.getMaxExtensionSize()); + proto.getMaxTrailerSize(), proto.getMaxExtensionSize(), + proto.getMaxSwallowSize()); processor.setAdapter(proto.getAdapter()); processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Processor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Processor.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Processor.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Processor.java Thu Jun 19 09:06:39 2014 @@ -60,7 +60,7 @@ public class Http11Nio2Processor extends public Http11Nio2Processor(int maxHttpHeaderSize, Nio2Endpoint endpoint, - int maxTrailerSize, int maxExtensionSize) { + int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { super(endpoint); @@ -70,7 +70,7 @@ public class Http11Nio2Processor extends outputBuffer = new InternalNio2OutputBuffer(response, maxHttpHeaderSize); response.setOutputBuffer(outputBuffer); - initializeFilters(maxTrailerSize, maxExtensionSize); + initializeFilters(maxTrailerSize, maxExtensionSize, maxSwallowSize); } Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Protocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Protocol.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Protocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11Nio2Protocol.java Thu Jun 19 09:06:39 2014 @@ -248,7 +248,8 @@ public class Http11Nio2Protocol extends public Http11Nio2Processor createProcessor() { Http11Nio2Processor processor = new Http11Nio2Processor( proto.getMaxHttpHeaderSize(), (Nio2Endpoint) proto.endpoint, - proto.getMaxTrailerSize(), proto.getMaxExtensionSize()); + proto.getMaxTrailerSize(), proto.getMaxExtensionSize(), + proto.getMaxSwallowSize()); processor.setAdapter(proto.getAdapter()); processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11NioProcessor.java Thu Jun 19 09:06:39 2014 @@ -63,7 +63,7 @@ public class Http11NioProcessor extends public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, - int maxTrailerSize, int maxExtensionSize) { + int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { super(endpoint); @@ -73,7 +73,7 @@ public class Http11NioProcessor extends outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize); response.setOutputBuffer(outputBuffer); - initializeFilters(maxTrailerSize, maxExtensionSize); + initializeFilters(maxTrailerSize, maxExtensionSize, maxSwallowSize); } Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java Thu Jun 19 09:06:39 2014 @@ -280,7 +280,8 @@ public class Http11NioProtocol extends A public Http11NioProcessor createProcessor() { Http11NioProcessor processor = new Http11NioProcessor( proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint, - proto.getMaxTrailerSize(), proto.getMaxExtensionSize()); + proto.getMaxTrailerSize(), proto.getMaxExtensionSize(), + proto.getMaxSwallowSize()); processor.setAdapter(proto.getAdapter()); processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java Thu Jun 19 09:06:39 2014 @@ -49,7 +49,7 @@ public class Http11Processor extends Abs public Http11Processor(int headerBufferSize, JIoEndpoint endpoint, - int maxTrailerSize, int maxExtensionSize) { + int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { super(endpoint); @@ -59,7 +59,7 @@ public class Http11Processor extends Abs outputBuffer = new InternalOutputBuffer(response, headerBufferSize); response.setOutputBuffer(outputBuffer); - initializeFilters(maxTrailerSize, maxExtensionSize); + initializeFilters(maxTrailerSize, maxExtensionSize, maxSwallowSize); } Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11Protocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11Protocol.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11Protocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11Protocol.java Thu Jun 19 09:06:39 2014 @@ -186,7 +186,8 @@ public class Http11Protocol extends Abst protected Http11Processor createProcessor() { Http11Processor processor = new Http11Processor( proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint, - proto.getMaxTrailerSize(),proto.getMaxExtensionSize()); + proto.getMaxTrailerSize(),proto.getMaxExtensionSize(), + proto.getMaxSwallowSize()); processor.setAdapter(proto.getAdapter()); processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java Thu Jun 19 09:06:39 2014 @@ -137,6 +137,9 @@ public class ChunkedInputFilter implemen private long extensionSize; + private final int maxSwallowSize; + + /** * Flag that indicates if an error has occurred. */ @@ -145,10 +148,11 @@ public class ChunkedInputFilter implemen // ----------------------------------------------------------- Constructors - public ChunkedInputFilter(int maxTrailerSize, int maxExtensionSize) { + public ChunkedInputFilter(int maxTrailerSize, int maxExtensionSize, int maxSwallowSize) { this.trailingHeaders.setLimit(maxTrailerSize); this.maxExtensionSize = maxExtensionSize; this.maxTrailerSize = maxTrailerSize; + this.maxSwallowSize = maxSwallowSize; } @@ -234,9 +238,14 @@ public class ChunkedInputFilter implemen */ @Override public long end() throws IOException { + long swallowed = 0; + int read = 0; // Consume extra bytes : parse the stream until the end chunk is found - while (doRead(readChunk, null) >= 0) { - // NOOP: Just consume the input + while ((read = doRead(readChunk, null)) >= 0) { + swallowed += read; + if (maxSwallowSize > -1 && swallowed > maxSwallowSize) { + throwIOException(sm.getString("inputFilter.maxSwallow")); + } } // Return the number of extra bytes which were consumed Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityInputFilter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityInputFilter.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityInputFilter.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/filters/IdentityInputFilter.java Thu Jun 19 09:06:39 2014 @@ -24,6 +24,7 @@ import org.apache.coyote.InputBuffer; import org.apache.coyote.Request; import org.apache.coyote.http11.InputFilter; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.res.StringManager; /** * Identity input filter. @@ -32,6 +33,9 @@ import org.apache.tomcat.util.buf.ByteCh */ public class IdentityInputFilter implements InputFilter { + private static final StringManager sm = StringManager.getManager( + IdentityInputFilter.class.getPackage().getName()); + // -------------------------------------------------------------- Constants @@ -76,6 +80,14 @@ public class IdentityInputFilter impleme protected final ByteChunk endChunk = new ByteChunk(); + private final int maxSwallowSize; + + + public IdentityInputFilter(int maxSwallowSize) { + this.maxSwallowSize = maxSwallowSize; + } + + // ---------------------------------------------------- InputBuffer Methods /** @@ -137,8 +149,11 @@ public class IdentityInputFilter impleme * End the current request. */ @Override - public long end() - throws IOException { + public long end() throws IOException { + + if (maxSwallowSize > -1 && remaining > maxSwallowSize) { + throw new IOException(sm.getString("inputFilter.maxSwallow")); + } // Consume extra bytes. while (remaining > 0) { Modified: tomcat/trunk/java/org/apache/coyote/http11/filters/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/filters/LocalStrings.properties?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/filters/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/coyote/http11/filters/LocalStrings.properties Thu Jun 19 09:06:39 2014 @@ -22,4 +22,6 @@ chunkedInputFilter.invalidCrlfNoCR=Inval chunkedInputFilter.invalidCrlfNoData=Invalid end of line sequence (no data available to read) chunkedInputFilter.invalidHeader=Invalid chunk header chunkedInputFilter.maxExtension=maxExtensionSize exceeded -chunkedInputFilter.maxTrailer=maxTrailerSize exceeded \ No newline at end of file +chunkedInputFilter.maxTrailer=maxTrailerSize exceeded + +inputFilter.maxSwallow=maxSwallowSize exceeded \ No newline at end of file Modified: tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java (original) +++ tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java Thu Jun 19 09:06:39 2014 @@ -16,8 +16,14 @@ */ package org.apache.catalina.core; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.io.Writer; +import java.net.Socket; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; @@ -32,6 +38,7 @@ import javax.servlet.http.Part; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import org.junit.Assert; import org.junit.Test; import org.apache.catalina.Context; @@ -113,7 +120,7 @@ public class TestSwallowAbortedUploads e Exception ex = doAbortedUploadTest(client, true, true); assertNull("Limited upload with swallow enabled generates client exception", ex); - assertTrue("Limited upload with swallow enabled returns error status code", + assertTrue("Limited upload with swallow enabled returns non-500 status code", client.isResponse500()); client.reset(); } @@ -400,4 +407,72 @@ public class TestSwallowAbortedUploads e } } + + @Test + public void testChunkedPUTLimit() throws Exception { + doTestChunkedPUT(true); + } + + + @Test + public void testChunkedPUTNoLimit() throws Exception { + doTestChunkedPUT(false); + } + + + public void doTestChunkedPUT(boolean limit) throws Exception { + + Tomcat tomcat = getTomcatInstance(); + tomcat.addContext("", TEMP_DIR); + // No need for target to exist. + + if (!limit) { + tomcat.getConnector().setAttribute("maxSwallowSize", "-1"); + } + + tomcat.start(); + + Exception writeEx = null; + Exception readEx = null; + String responseLine = null; + + try (Socket conn = new Socket("localhost", getPort())) { + Writer writer = new OutputStreamWriter( + conn.getOutputStream(), StandardCharsets.US_ASCII); + writer.write("PUT /does-not-exist HTTP/1.1\r\n"); + writer.write("Host: any\r\n"); + writer.write("Transfer-encoding: chunked\r\n"); + writer.write("\r\n"); + + // Smarter than the typical client. Attempts to read the response + // even if the request is not fully written. + try { + // Write (or try to write) 16MB + for (int i = 0; i < 1024 * 1024; i++) { + writer.write("10\r\n"); + writer.write("0123456789ABCDEF\r\n"); + } + } catch (Exception e) { + writeEx = e; + } + + try { + BufferedReader reader = new BufferedReader(new InputStreamReader( + conn.getInputStream(), StandardCharsets.US_ASCII)); + + responseLine = reader.readLine(); + } catch (IOException e) { + readEx = e; + } + } + + if (limit) { + Assert.assertNotNull(writeEx); + } else { + Assert.assertNull(writeEx); + } + Assert.assertNull(readEx); + Assert.assertNotNull(responseLine); + Assert.assertTrue(responseLine.contains("404")); + } } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Thu Jun 19 09:06:39 2014 @@ -205,6 +205,10 @@ <fix> Improve configuration of cache sizes in the endpoint. (markt) </fix> + <add> + Add a new limit, defaulting to 2MB, for the amount of data Tomcat will + swallow for an aborted upload. (markt) + </add> </changelog> </subsection> <subsection name="Jasper"> Modified: tomcat/trunk/webapps/docs/config/http.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/http.xml?rev=1603770&r1=1603769&r2=1603770&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/http.xml (original) +++ tomcat/trunk/webapps/docs/config/http.xml Thu Jun 19 09:06:39 2014 @@ -436,6 +436,16 @@ If not specified, this attribute is set to 100.</p> </attribute> + <attribute name="maxSwallowSize" required="false"> + <p>The maximum number of request body bytes (excluding transfer encoding + overhead) that will be swallowed by Tomcat for an aborted upload. An + aborted upload is when Tomcat knows that the request body is going to be + ignored but the client still sends it. If Tomcat does not swallow the body + the client is unlikely to see the response. If not specified the default + of 2097152 (2 megabytes) will be used. A value of less than zero indicates + that no limit should be enforced.</p> + </attribute> + <attribute name="maxThreads" required="false"> <p>The maximum number of request processing threads to be created by this <strong>Connector</strong>, which therefore determines the --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org