Author: markt
Date: Mon Dec 15 11:41:49 2014
New Revision: 1645626

URL: http://svn.apache.org/r1645626
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=57324
If it is known that the connection is going to be closed when committing the 
response, send the connection: close header.

Modified:
    tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java
    tomcat/trunk/test/org/apache/catalina/startup/TomcatBaseTest.java
    tomcat/trunk/test/org/apache/coyote/http11/TestAbstractHttp11Processor.java

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=1645626&r1=1645625&r2=1645626&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java 
(original)
+++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java Mon 
Dec 15 11:41:49 2014
@@ -1098,16 +1098,11 @@ public abstract class AbstractHttp11Proc
                     // input. This way uploading a 100GB file doesn't tie up 
the
                     // thread if the servlet has rejected it.
                     getInputBuffer().setSwallowInput(false);
-                } else if (expectation &&
-                        (response.getStatus() < 200 || response.getStatus() > 
299)) {
-                    // Client sent Expect: 100-continue but received a
-                    // non-2xx final response. Disable keep-alive (if enabled)
-                    // to ensure that the connection is closed. Some clients 
may
-                    // still send the body, some may send the next request.
-                    // No way to differentiate, so close the connection to
-                    // force the client to send the next request.
-                    getInputBuffer().setSwallowInput(false);
-                    keepAlive = false;
+                } else {
+                    // Need to check this again here in case the response was
+                    // committed before the error that requires the connection
+                    // to be closed occurred.
+                    checkExpectationAndResponseStatus();
                 }
                 endRequest();
             }
@@ -1169,6 +1164,20 @@ public abstract class AbstractHttp11Proc
     }
 
 
+    private void checkExpectationAndResponseStatus() {
+        if (expectation && (response.getStatus() < 200 || response.getStatus() 
> 299)) {
+            // Client sent Expect: 100-continue but received a
+            // non-2xx final response. Disable keep-alive (if enabled)
+            // to ensure that the connection is closed. Some clients may
+            // still send the body, some may send the next request.
+            // No way to differentiate, so close the connection to
+            // force the client to send the next request.
+            getInputBuffer().setSwallowInput(false);
+            keepAlive = false;
+        }
+    }
+
+
     /**
      * After reading the request headers, we have to setup the request filters.
      */
@@ -1493,6 +1502,10 @@ public abstract class AbstractHttp11Proc
             keepAlive = false;
         }
 
+        // This may disabled keep-alive to check before working out the
+        // Connection header.
+        checkExpectationAndResponseStatus();
+
         // If we know that the request is bad this early, add the
         // Connection: close header.
         keepAlive = keepAlive && !statusDropsConnection(statusCode);

Modified: tomcat/trunk/test/org/apache/catalina/startup/TomcatBaseTest.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TomcatBaseTest.java?rev=1645626&r1=1645625&r2=1645626&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/TomcatBaseTest.java (original)
+++ tomcat/trunk/test/org/apache/catalina/startup/TomcatBaseTest.java Mon Dec 
15 11:41:49 2014
@@ -565,6 +565,37 @@ public abstract class TomcatBaseTest ext
     }
 
 
+    /**
+     * Servlet that simply echos the request body back as the response body.
+     */
+    public static class EchoBodyServlet extends HttpServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            // NO-OP - No body to echo
+        }
+
+        @Override
+        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            // Beware of clients that try to send the whole request body before
+            // reading any of the response. They may cause this test to lock 
up.
+            byte[] buffer = new byte[8096];
+            int read = 0;
+            try (InputStream is = req.getInputStream();
+                    OutputStream os = resp.getOutputStream()) {
+                while (read > -1) {
+                    os.write(buffer, 0, read);
+                    read = is.read(buffer);
+                }
+            }
+        }
+    }
+
+
     /*
      *  Wrapper for getting the response.
      */

Modified: 
tomcat/trunk/test/org/apache/coyote/http11/TestAbstractHttp11Processor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http11/TestAbstractHttp11Processor.java?rev=1645626&r1=1645625&r2=1645626&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/http11/TestAbstractHttp11Processor.java 
(original)
+++ tomcat/trunk/test/org/apache/coyote/http11/TestAbstractHttp11Processor.java 
Mon Dec 15 11:41:49 2014
@@ -23,8 +23,10 @@ import java.io.Writer;
 import java.net.Socket;
 import java.nio.CharBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 
@@ -49,6 +51,8 @@ import org.apache.catalina.startup.Tomca
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.tomcat.util.buf.B2CConverter;
 import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
 
 public class TestAbstractHttp11Processor extends TomcatBaseTest {
 
@@ -536,6 +540,59 @@ public class TestAbstractHttp11Processor
         }
     }
 
+
+    // https://issues.apache.org/bugzilla/show_bug.cgi?id=57324
+    @Test
+    public void testNon2xxResponseWithExpectation() throws Exception {
+        doTestNon2xxResponseAndExpectation(true);
+    }
+
+    @Test
+    public void testNon2xxResponseWithoutExpectation() throws Exception {
+        doTestNon2xxResponseAndExpectation(false);
+    }
+
+    private void doTestNon2xxResponseAndExpectation(boolean useExpectation) 
throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        // No file system docBase required
+        Context ctx = tomcat.addContext("", null);
+
+        Tomcat.addServlet(ctx, "echo", new EchoBodyServlet());
+        ctx.addServletMapping("/echo", "echo");
+
+        SecurityCollection collection = new SecurityCollection("All", "");
+        collection.addPattern("/*");
+        SecurityConstraint constraint = new SecurityConstraint();
+        constraint.addAuthRole("Any");
+        constraint.addCollection(collection);
+        ctx.addConstraint(constraint);
+
+        tomcat.start();
+
+        byte[] requestBody = "HelloWorld".getBytes(StandardCharsets.UTF_8);
+        Map<String,List<String>> reqHeaders = null;
+        if (useExpectation) {
+            reqHeaders = new HashMap<>();
+            List<String> expectation = new ArrayList<>();
+            expectation.add("100-continue");
+            reqHeaders.put("Expect", expectation);
+        }
+        ByteChunk responseBody = new ByteChunk();
+        Map<String,List<String>> responseHeaders = new HashMap<>();
+        int rc = postUrl(requestBody, "http://localhost:"; + getPort() + 
"/echo",
+                responseBody, reqHeaders, responseHeaders);
+
+        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, rc);
+        List<String> connectionHeaders = responseHeaders.get("Connection");
+        if (useExpectation) {
+            Assert.assertEquals(1, connectionHeaders.size());
+            Assert.assertEquals("close", 
connectionHeaders.get(0).toLowerCase(Locale.ENGLISH));
+        } else {
+            Assert.assertNull(connectionHeaders);
+        }
+    }
+
 
     private static class Bug55772Servlet extends HttpServlet {
 



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

Reply via email to