Author: rjung
Date: Tue Mar  8 17:18:16 2011
New Revision: 1079444

URL: http://svn.apache.org/viewvc?rev=1079444&view=rev
Log:
New context attribute "swallowAbortedUploads" allows
to make request data swallowing configurable for requests
that are too large.

Added:
    tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java   
(with props)
Modified:
    tomcat/trunk/java/org/apache/catalina/Context.java
    tomcat/trunk/java/org/apache/catalina/connector/Request.java
    tomcat/trunk/java/org/apache/catalina/connector/Response.java
    tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
    tomcat/trunk/java/org/apache/coyote/ActionCode.java
    tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
    tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java
    tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java
    tomcat/trunk/webapps/docs/changelog.xml
    tomcat/trunk/webapps/docs/config/context.xml

Modified: tomcat/trunk/java/org/apache/catalina/Context.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Context.java?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Context.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Context.java Tue Mar  8 17:18:16 2011
@@ -111,6 +111,24 @@ public interface Context extends Contain
     public boolean getAllowCasualMultipartParsing();
 
     /**
+     * Set to <code>false</code> to disable request data swallowing
+     * after an upload was aborted due to size constraints.
+     *
+     * @param swallowAbortedUploads <code>false</code> to disable
+     *        swallowing, <code>true</code> otherwise (default).
+     */
+    public void setSwallowAbortedUploads(boolean swallowAbortedUploads);
+
+    /**
+     * Returns <code>true</code> if remaining request data will be read
+     * (swallowed) even the request violates a data size constraint.
+     *
+     * @return <code>true</code> if data will be swallowed (default),
+     *    <code>false</code> otherwise.
+     */
+    public boolean getSwallowAbortedUploads();
+
+    /**
      * Return the set of initialized application event listener objects,
      * in the order they were specified in the web application deployment
      * descriptor, for this application.

Modified: tomcat/trunk/java/org/apache/catalina/connector/Request.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Request.java?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/Request.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/Request.java Tue Mar  8 
17:18:16 2011
@@ -800,6 +800,9 @@ public class Request
      */
     public void finishRequest() throws IOException {
         // The reader and input stream don't need to be closed
+        // TODO: Is this ever called?
+        // If so, move input swallow disabling from 
+        // Response.finishResponse() to here
     }
 
 
@@ -2450,6 +2453,16 @@ public class Request
         return (inputBuffer.available() > 0);
     }
 
+    /**
+     * Disable swallowing of remaining input if configured
+     */
+    protected void disableSwallowInput() {
+        Context context = getContext();
+        if (context != null && !context.getSwallowAbortedUploads()) {
+            coyoteRequest.action(ActionCode.DISABLE_SWALLOW_INPUT, null);
+        }
+    }
+    
     public void cometClose() {
         coyoteRequest.action(ActionCode.COMET_CLOSE,getEvent());
     }
@@ -2620,6 +2633,7 @@ public class Request
         } catch (InvalidContentTypeException e) {
             partsParseException = new ServletException(e);
         } catch (FileUploadBase.SizeException e) {
+            disableSwallowInput();
             partsParseException = new IllegalStateException(e);
         } catch (FileUploadException e) {
             partsParseException = new IOException(e);
@@ -2845,6 +2859,7 @@ public class Request
                     context.getLogger().debug(
                             sm.getString("coyoteRequest.postTooLarge"));
                 }
+                disableSwallowInput();
                 return;
             }
             byte[] formData = null;
@@ -2922,6 +2937,7 @@ public class Request
             if (connector.getMaxPostSize() > 0 &&
                     (body.getLength() + len) > connector.getMaxPostSize()) {
                 // Too much data
+                disableSwallowInput();
                 throw new IllegalArgumentException(
                         sm.getString("coyoteRequest.chunkedPostTooLarge"));
             }

Modified: tomcat/trunk/java/org/apache/catalina/connector/Response.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Response.java?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/Response.java (original)
+++ tomcat/trunk/java/org/apache/catalina/connector/Response.java Tue Mar  8 
17:18:16 2011
@@ -49,6 +49,7 @@ import org.apache.catalina.core.Applicat
 import org.apache.catalina.security.SecurityUtil;
 import org.apache.catalina.util.CharsetMapper;
 import org.apache.catalina.util.DateTool;
+import org.apache.coyote.ActionCode;
 import org.apache.tomcat.util.buf.CharChunk;
 import org.apache.tomcat.util.buf.UEncoder;
 import org.apache.tomcat.util.http.FastHttpDateFormat;
@@ -497,6 +498,15 @@ public class Response
      */
     public void finishResponse() 
         throws IOException {
+        // Optionally disable swallowing of additional request data.
+        // TODO: Should be in Request.finishRequest(), but that method
+        // seems to get called never.
+        Context context = getContext();
+        if (context != null
+                && getStatus() == 
HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE
+                && !context.getSwallowAbortedUploads()) {
+            coyoteResponse.action(ActionCode.DISABLE_SWALLOW_INPUT, null);
+        }
         // Writing leftover bytes
         outputBuffer.close();
     }

Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContext.java?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Tue Mar  8 
17:18:16 2011
@@ -197,6 +197,12 @@ public class StandardContext extends Con
     protected boolean allowCasualMultipartParsing = false;
      
     /**
+     * Control whether remaining request data will be read
+     * (swallowed) even if the request violates a data size constraint.
+     */
+    public boolean swallowAbortedUploads = true;
+
+    /**
      * The alternate deployment descriptor name.
      */
     private String altDDName = null;
@@ -1066,6 +1072,30 @@ public class StandardContext extends Con
     }
 
     /**
+     * Set to <code>false</code> to disable request data swallowing
+     * after an upload was aborted due to size constraints.
+     *
+     * @param swallowAbortedUploads <code>false</code> to disable
+     *        swallowing, <code>true</code> otherwise (default).
+     */
+    @Override
+    public void setSwallowAbortedUploads(boolean swallowAbortedUploads) {
+        this.swallowAbortedUploads = swallowAbortedUploads;
+    }
+
+    /**
+     * Returns <code>true</code> if remaining request data will be read
+     * (swallowed) even the request violates a data size constraint.
+     *
+     * @return <code>true</code> if data will be swallowed (default),
+     *    <code>false</code> otherwise.
+     */
+    @Override
+    public boolean getSwallowAbortedUploads() {
+        return this.swallowAbortedUploads;
+    }
+
+    /**
      * Set cache TTL.
      */
     public void setCacheTTL(int cacheTTL) {
@@ -6440,4 +6470,4 @@ public class StandardContext extends Con
         return false;
     }
 
-}
+}
\ No newline at end of file

Modified: tomcat/trunk/java/org/apache/coyote/ActionCode.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ActionCode.java?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ActionCode.java (original)
+++ tomcat/trunk/java/org/apache/coyote/ActionCode.java Tue Mar  8 17:18:16 2011
@@ -49,6 +49,13 @@ public enum ActionCode {
     POST_REQUEST,
 
     /**
+     * Hook called if swallowing request input should be disabled.
+     * Example: Cancel a large file upload.
+     * 
+     */
+    DISABLE_SWALLOW_INPUT,
+
+    /**
      * Callback for lazy evaluation - extract the remote host address.
      */
     REQ_HOST_ATTRIBUTE,

Modified: tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java Tue Mar  
8 17:18:16 2011
@@ -266,6 +266,11 @@ public abstract class AbstractAjpProcess
                error = true;
            }
 
+       } else if (actionCode == ActionCode.DISABLE_SWALLOW_INPUT) {
+           // TODO: Do not swallow request input but
+           // make sure we are closing the connection
+           error = true;
+
        } else if (actionCode == ActionCode.CLOSE) {
            // Close
            // End the processing of the current request, and stop any further

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=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java 
(original)
+++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java Tue 
Mar  8 17:18:16 2011
@@ -767,6 +767,12 @@ public abstract class AbstractHttp11Proc
                 response.setErrorException(e);
             }
 
+        } else if (actionCode == ActionCode.DISABLE_SWALLOW_INPUT) {
+            // Do not swallow request input but
+            // make sure we are closing the connection
+            error = true;
+            getInputBuffer().setSwallowInput(false);
+
         } else if (actionCode == ActionCode.RESET) {
             // Reset response
             // Note: This must be called before the response is committed

Added: 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=1079444&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java 
(added)
+++ tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java 
Tue Mar  8 17:18:16 2011
@@ -0,0 +1,399 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.catalina.core;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.servlet.MultipartConfigElement;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.MultipartConfig;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.Part;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.startup.SimpleHttpClient;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+public class TestSwallowAbortedUploads extends TomcatBaseTest {
+
+    private static Log log = 
LogFactory.getLog(TestSwallowAbortedUploads.class);
+
+    /**
+     * Test whether size limited uploads correctly handle connection draining.
+     */
+    public Exception doAbortedUploadTest(AbortedUploadClient client, boolean 
limited,
+                            boolean swallow) {
+        client.setPort(getPort());
+        Exception ex = client.doRequest(limited, swallow);
+        if (log.isDebugEnabled()) {
+            log.debug("Response line: " + client.getResponseLine());
+            log.debug("Response headers: " + client.getResponseHeaders());
+            log.debug("Response body: " + client.getResponseBody());
+            if (ex != null) {
+                log.debug("Exception in client: ", ex);
+            }
+
+        }
+        return ex;
+    }
+
+    /**
+     * Test whether aborted POST correctly handle connection draining.
+     */
+    public Exception doAbortedPOSTTest(AbortedPOSTClient client, int status,
+                            boolean swallow) {
+        client.setPort(getPort());
+        Exception ex = client.doRequest(status, swallow);
+        if (log.isDebugEnabled()) {
+            log.debug("Response line: " + client.getResponseLine());
+            log.debug("Response headers: " + client.getResponseHeaders());
+            log.debug("Response body: " + client.getResponseBody());
+            if (ex != null) {
+                log.info("Exception in client: ", ex);
+            }
+
+        }
+        return ex;
+    }
+
+    public void testAbortedUploadUnlimitedSwallow() {
+        log.info("Unlimited, swallow enabled");
+        AbortedUploadClient client = new AbortedUploadClient();
+        Exception ex = doAbortedUploadTest(client, false, true);
+        assertNull("Unlimited upload with swallow enabled generates client 
exception",
+                   ex);
+        assertTrue("Unlimited upload with swallow enabled returns error status 
code",
+                   client.isResponse200());
+        client.reset();
+    }
+
+    public void testAbortedUploadUnlimitedNoSwallow() {
+        log.info("Unlimited, swallow disabled");
+        AbortedUploadClient client = new AbortedUploadClient();
+        Exception ex = doAbortedUploadTest(client, false, false);
+        assertNull("Unlimited upload with swallow disabled generates client 
exception",
+                   ex);
+        assertTrue("Unlimited upload with swallow disabled returns error 
status code",
+                   client.isResponse200());
+        client.reset();
+    }
+
+    public void testAbortedUploadLimitedSwallow() {
+        log.info("Limited, swallow enabled");
+        AbortedUploadClient client = new AbortedUploadClient();
+        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",
+                   client.isResponse500());
+        client.reset();
+    }
+
+    public void testAbortedUploadLimitedNoSwallow() {
+        log.info("Limited, swallow disabled");
+        AbortedUploadClient client = new AbortedUploadClient();
+        Exception ex = doAbortedUploadTest(client, true, false);
+        assertTrue("Limited upload with swallow disabled does not generate 
client exception",
+                   ex != null && ex instanceof java.net.SocketException);
+        client.reset();
+    }
+
+    public void testAbortedPOSTOKSwallow() {
+        log.info("Aborted (OK), swallow enabled");
+        AbortedPOSTClient client = new AbortedPOSTClient();
+        Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_OK, 
true);
+        assertNull("Unlimited upload with swallow enabled generates client 
exception",
+                   ex);
+        assertTrue("Unlimited upload with swallow enabled returns error status 
code",
+                   client.isResponse200());
+        client.reset();
+    }
+
+    public void testAbortedPOSTOKNoSwallow() {
+        log.info("Aborted (OK), swallow disabled");
+        AbortedPOSTClient client = new AbortedPOSTClient();
+        Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_OK, 
false);
+        assertNull("Unlimited upload with swallow disabled generates client 
exception",
+                   ex);
+        assertTrue("Unlimited upload with swallow disabled returns error 
status code",
+                   client.isResponse200());
+        client.reset();
+    }
+
+    public void testAbortedPOST413Swallow() {
+        log.info("Aborted (413), swallow enabled");
+        AbortedPOSTClient client = new AbortedPOSTClient();
+        Exception ex = doAbortedPOSTTest(client, 
HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, true);
+        assertNull("Limited upload with swallow enabled generates client 
exception",
+                   ex);
+        assertTrue("Limited upload with swallow enabled returns error status 
code",
+                   client.isResponse413());
+        client.reset();
+    }
+
+    public void testAbortedPOST413NoSwallow() {
+        log.info("Aborted (413), swallow disabled");
+        AbortedPOSTClient client = new AbortedPOSTClient();
+        Exception ex = doAbortedPOSTTest(client, 
HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, false);
+        assertTrue("Limited upload with swallow disabled does not generate 
client exception",
+                   ex != null && ex instanceof java.net.SocketException);
+        client.reset();
+    }
+
+    @MultipartConfig
+    private static class AbortedUploadServlet extends HttpServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            PrintWriter out = resp.getWriter();
+            resp.setContentType("text/plain");
+            resp.setCharacterEncoding("UTF-8");
+            StringBuilder sb = new StringBuilder();
+            try {
+                Collection<Part> c = req.getParts();
+                if (c == null) {
+                    log.debug("Count: -1");
+                    sb.append("Count: -1\n");
+                } else {
+                    log.debug("Count: " + c.size());
+                    sb.append("Count: " + c.size() + "\n");
+                    for (Part p : c) {
+                        log.debug("Name: " + p.getName() + ", Size: "
+                                + p.getSize());
+                        sb.append("Name: " + p.getName() + ", Size: "
+                                + p.getSize() + "\n");
+                    }
+                }
+            } catch (IllegalStateException ex) {
+                log.debug("IllegalStateException during getParts()");
+                sb.append("IllegalStateException during getParts()\n");
+                resp.setStatus(500);
+            } catch (Throwable ex) {
+                log.error("Exception during getParts()", ex);
+                sb.append(ex);
+                resp.setStatus(500);
+            }
+            out.print(sb.toString());
+            resp.flushBuffer();
+        }
+
+    }
+
+    /**
+     * Test no connection draining when upload too large
+     */
+    private class AbortedUploadClient extends SimpleHttpClient {
+
+        private static final String URI = "/uploadAborted";
+        private static final String servletName = "uploadAborted";
+        private static final int limitSize = 100;
+        private static final int hugeSize = 200000;
+
+        private boolean init;
+        private Context context;
+
+        private synchronized void init(boolean limited, boolean swallow)
+                throws Exception {
+            if (init)
+                return;
+
+            Tomcat tomcat = getTomcatInstance();
+            context = tomcat.addContext("", TEMP_DIR);
+            Wrapper w;
+            w = Tomcat.addServlet(context, servletName,
+                                  new AbortedUploadServlet());
+            // Tomcat.addServlet does not respect annotations, so we have
+            // to set our own MultipartConfigElement.
+            // Choose upload file size limit.
+            if (limited) {
+                w.setMultipartConfigElement(new MultipartConfigElement("",
+                        limitSize, -1, -1));
+            } else {
+                w.setMultipartConfigElement(new MultipartConfigElement(""));
+            }
+            context.addServletMapping(URI, servletName);
+            context.setSwallowAbortedUploads(swallow);
+
+            tomcat.start();
+
+            init = true;
+        }
+
+        private Exception doRequest(boolean limited, boolean swallow) {
+            char body[] = new char[hugeSize];
+            Arrays.fill(body, 'X');
+
+            try {
+                init(limited, swallow);
+
+                // Open connection
+                connect();
+
+                // Send specified request body using method
+                String[] request;
+
+                String boundary = "--simpleboundary";
+                StringBuilder sb = new StringBuilder();
+
+                sb.append("--");
+                sb.append(boundary);
+                sb.append(CRLF);
+                sb.append("Content-Disposition: form-data; name=\"part\"");
+                sb.append(CRLF);
+                sb.append(CRLF);
+                sb.append(body);
+                sb.append(CRLF);
+                sb.append("--");
+                sb.append(boundary);
+                sb.append("--");
+                sb.append(CRLF);
+
+                // Re-encode the content so that bytes = characters
+                String content = new String(sb.toString().getBytes("UTF-8"),
+                        "ASCII");
+
+                request = new String[] { "POST http://localhost:"; + getPort() 
+ URI + " HTTP/1.1" + CRLF 
+                        + "Host: localhost" + CRLF
+                        + "Connection: close" + CRLF
+                        + "Content-Type: multipart/form-data; boundary=" + 
boundary + CRLF 
+                        + "Content-Length: " + content.length() + CRLF 
+                        + CRLF 
+                        + content + CRLF };
+
+                setRequest(request);
+                processRequest(); // blocks until response has been read
+
+                // Close the connection
+                disconnect();
+            } catch (Exception e) {
+                return e;
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isResponseBodyOK() {
+            return false; // Don't care
+        }
+    }
+
+    private static class AbortedPOSTServlet extends HttpServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        private int status = 200;
+        
+        public void setStatus(int status) {
+            this.status = status;
+        }
+        
+        @Override
+        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            resp.setContentType("text/plain");
+            resp.setCharacterEncoding("UTF-8");
+            resp.setStatus(status);
+            PrintWriter out = resp.getWriter();
+            out.print("OK");
+            resp.flushBuffer();
+        }
+
+    }
+
+    /**
+     * Test no connection draining when upload too large
+     */
+    private class AbortedPOSTClient extends SimpleHttpClient {
+
+        private static final String URI = "/uploadAborted";
+        private static final String servletName = "uploadAborted";
+        private static final int hugeSize = 200000;
+
+        private boolean init;
+        private Context context;
+
+        private synchronized void init(int status, boolean swallow)
+                throws Exception {
+            if (init)
+                return;
+
+            Tomcat tomcat = getTomcatInstance();
+            context = tomcat.addContext("", TEMP_DIR);
+            AbortedPOSTServlet servlet = new AbortedPOSTServlet();
+            servlet.setStatus(status);
+            Tomcat.addServlet(context, servletName,
+                              servlet);
+            context.addServletMapping(URI, servletName);
+            context.setSwallowAbortedUploads(swallow);
+
+            tomcat.start();
+
+            init = true;
+        }
+
+        private Exception doRequest(int status, boolean swallow) {
+            char body[] = new char[hugeSize];
+            Arrays.fill(body, 'X');
+
+            try {
+                init(status, swallow);
+
+                // Open connection
+                connect();
+
+                // Send specified request body using method
+                String[] request;
+
+                String content = new String(body);
+
+                request = new String[] { "POST http://localhost:"; + getPort() 
+ URI + " HTTP/1.1" + CRLF
+                        + "Host: localhost" + CRLF
+                        + "Connection: close" + CRLF
+                        + "Content-Length: " + content.length() + CRLF
+                        + CRLF
+                        + content + CRLF };
+
+                setRequest(request);
+                processRequest(); // blocks until response has been read
+
+                // Close the connection
+                disconnect();
+            } catch (Exception e) {
+                return e;
+            }
+            return null;
+        }
+
+        @Override
+        public boolean isResponseBodyOK() {
+            return false; // Don't care
+        }
+    }
+
+}

Propchange: 
tomcat/trunk/test/org/apache/catalina/core/TestSwallowAbortedUploads.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/startup/SimpleHttpClient.java Tue Mar 
 8 17:18:16 2011
@@ -48,6 +48,7 @@ public abstract class SimpleHttpClient {
     public static final String OK_200 = "HTTP/1.1 200";
     public static final String REDIRECT_302 = "HTTP/1.1 302";
     public static final String FAIL_404 = "HTTP/1.1 404";
+    public static final String FAIL_413 = "HTTP/1.1 413";
     public static final String FAIL_50X = "HTTP/1.1 50";
     public static final String FAIL_500 = "HTTP/1.1 500";
     public static final String FAIL_501 = "HTTP/1.1 501";
@@ -218,6 +219,10 @@ public abstract class SimpleHttpClient {
         return getResponseLine().startsWith(FAIL_404);
     }
 
+    public boolean isResponse413() {
+        return getResponseLine().startsWith(FAIL_413);
+    }
+
     public boolean isResponse50x() {
         return getResponseLine().startsWith(FAIL_50X);
     }

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Tue Mar  8 17:18:16 2011
@@ -53,6 +53,11 @@
         <bug>50855</bug>: Fix NPE on HttpServletRequest.logout() when debug
         logging is enabled. (markt)
       </fix>
+      <fix>
+        New context attribute "swallowAbortedUploads" allows
+        to make request data swallowing configurable for requests
+        that are too large. (rjung)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">

Modified: tomcat/trunk/webapps/docs/config/context.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/context.xml?rev=1079444&r1=1079443&r2=1079444&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/context.xml (original)
+++ tomcat/trunk/webapps/docs/config/context.xml Tue Mar  8 17:18:16 2011
@@ -569,6 +569,26 @@
         default value of <code>true</code> will be used.</p>
       </attribute>
 
+      <attribute name="swallowAbortedUploads" required="false">
+        <p>Set to false if Tomcat should <b>not</b> read any additional
+        request body data for aborted uploads and instead abort the client
+        connection. This setting is used in the following situations:
+        <ul>
+        <li>the size of the request body is larger than the
+        <code>maxPostSize</code> configured in the connector
+        </li>
+        <li>the size limit of a MultiPart upload is reached
+        </li>
+        <li>the servlet sets the response status to 413
+        (Request Entity Too Large)
+        </li>
+        </ul>
+        Not reading the additional data will free the request processing thread
+        more quickly. Unfortunately most HTTP clients will not read the 
response
+        in case they can not write the full request.</p>
+        <p>The default is <code>true</code>, so additional data is being 
read.</p>
+      </attribute>
+
       <attribute name="swallowOutput" required="false">
         <p>If the value of this flag is <code>true</code>, the bytes output to
         System.out and System.err by the web application will be redirected to



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to