Blob Blame History Raw
--- java/org/apache/coyote/ajp/AbstractAjpProcessor.java.orig	2014-03-14 17:13:46.228345000 -0400
+++ java/org/apache/coyote/ajp/AbstractAjpProcessor.java	2014-03-18 13:54:13.570758000 -0400
@@ -25,6 +25,8 @@
 import java.security.cert.X509Certificate;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import javax.servlet.http.HttpServletResponse;
+
 import org.apache.coyote.AbstractProcessor;
 import org.apache.coyote.ActionCode;
 import org.apache.coyote.AsyncContextCallback;
@@ -651,7 +653,7 @@
 
         // Set this every time in case limit has been changed via JMX
         headers.setLimit(endpoint.getMaxHeaderCount());
-
+        boolean contentLengthSet = false;
         int hCount = requestHeaderMessage.getInt();
         for(int i = 0 ; i < hCount ; i++) {
             String hName = null;
@@ -686,10 +688,15 @@
 
             if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
                     (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
-                // just read the content-length header, so set it
                 long cl = vMB.getLong();
-                if(cl < Integer.MAX_VALUE)
-                    request.setContentLength( (int)cl );
+                if (contentLengthSet) {
+                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                    error = true;
+                } else {
+                    contentLengthSet = true;
+                    // Set the content-length header for the request
+                   request.setContentLength((int)cl);
+                }
             } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
                     (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
                 // just read the content-type header, so set it
--- java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig	2014-03-14 17:13:46.514347000 -0400
+++ java/org/apache/coyote/http11/AbstractHttp11Processor.java	2014-03-14 17:13:46.353345000 -0400
@@ -1277,10 +1277,20 @@
 
         // Parse content-length header
         long contentLength = request.getContentLengthLong();
-        if (contentLength >= 0 && !contentDelimitation) {
-            getInputBuffer().addActiveFilter
-                (inputFilters[Constants.IDENTITY_FILTER]);
-            contentDelimitation = true;
+        if (contentLength >= 0) {
+            if (contentDelimitation) {
+                // contentDelimitation being true at this point indicates that
+                // chunked encoding is being used but chunked encoding should
+                // not be used with a content length. RFC 2616, section 4.4,
+                // bullet 3 states Content-Length must be ignored in this case -
+                // so remove it.
+                headers.removeHeader("content-length");
+                request.setContentLength(-1);
+            } else {
+                getInputBuffer().addActiveFilter
+                        (inputFilters[Constants.IDENTITY_FILTER]);
+               contentDelimitation = true;
+           }
         }
 
         MessageBytes valueMB = headers.getValue("host");
--- test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java.orig	2014-03-14 17:13:52.878367000 -0400
+++ test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java	2014-03-14 17:21:43.278956000 -0400
@@ -90,6 +90,61 @@
         ajpClient.disconnect();
     }
 
+    @Test
+    public void testPost() throws Exception {
+        doTestPost(false, HttpServletResponse.SC_OK);
+    }
+
+    public void testPostMultipleContentLength() throws Exception {
+        // Multiple content lengths
+        doTestPost(true, HttpServletResponse.SC_BAD_REQUEST);
+    }
+
+
+    public void doTestPost(boolean multipleCL, int expectedStatus) 
+        throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+       // Use the normal Tomcat ROOT context
+       File root = new File("test/webapp-3.0");
+       tomcat.addWebapp("", root.getAbsolutePath());
+       tomcat.start();
+       SimpleAjpClient ajpClient = new SimpleAjpClient();
+       ajpClient.setPort(getPort());
+       ajpClient.connect();
+
+       validateCpong(ajpClient.cping());
+
+       TesterAjpMessage forwardMessage =
+                   ajpClient.createForwardMessage("/echo-params.jsp", 4);
+       forwardMessage.addHeader(0xA008, "9");
+       if (multipleCL) {
+          forwardMessage.addHeader(0xA008, "99");
+       }
+       forwardMessage.addHeader(0xA007, "application/x-www-form-urlencoded");
+       forwardMessage.end();
+       TesterAjpMessage bodyMessage =
+                   ajpClient.createBodyMessage("test=data".getBytes());
+
+       TesterAjpMessage responseHeaders =
+                   ajpClient.sendMessage(forwardMessage, bodyMessage);
+
+       validateResponseHeaders(responseHeaders, expectedStatus);
+       if (expectedStatus == HttpServletResponse.SC_OK) {
+           // Expect 3 messages: headers, body, end for a valid request
+           TesterAjpMessage responseBody = ajpClient.readMessage();
+           validateResponseBody(responseBody, "test - data");
+           validateResponseEnd(ajpClient.readMessage(), true);
+           // Double check the connection is still open
+           validateCpong(ajpClient.cping());
+       } else {
+           // Expect 2 messages: headers, end for an invalid request
+           validateResponseEnd(ajpClient.readMessage(), false);
+       }
+
+       ajpClient.disconnect();
+                                                                                    }
+
+
     /**
      * Process response header packet and checks the status. Any other data is
      * ignored.
--- test/org/apache/coyote/http11/TestAbstractHttp11Processor.java.orig	2014-03-14 17:13:52.946367000 -0400
+++ test/org/apache/coyote/http11/TestAbstractHttp11Processor.java	2014-03-14 17:13:52.925368000 -0400
@@ -87,7 +87,7 @@
             "Transfer-encoding: buffered" + SimpleHttpClient.CRLF +
             "Content-Length: 9" + SimpleHttpClient.CRLF +
             "Content-Type: application/x-www-form-urlencoded" +
-                    SimpleHttpClient.CRLF +
+            SimpleHttpClient.CRLF +
             SimpleHttpClient.CRLF +
             "test=data";
 
@@ -99,6 +99,54 @@
         assertTrue(client.isResponse501());
     }
 
+     @Test
+     public void testWithTEChunked() throws Exception {
+         doTestWithTEChunked(false);
+     }
+     
+     @Test
+     public void testWithTEChunkedWithCL() throws Exception {
+        // Should be ignored
+        doTestWithTEChunked(true);
+     }
+     
+     private void doTestWithTEChunked(boolean withCL)
+             throws Exception {
+     
+        Tomcat tomcat = getTomcatInstance();
+    
+        // Use the normal Tomcat ROOT context
+        File root = new File("test/webapp-3.0");
+        tomcat.addWebapp("", root.getAbsolutePath());
+        
+        tomcat.start();
+        
+        String request =
+                 "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF +
+                 "Host: any" + SimpleHttpClient.CRLF +
+                 (withCL ? "Content-length: 1" + SimpleHttpClient.CRLF : "") +
+                 "Transfer-encoding: chunked" + SimpleHttpClient.CRLF +
+                 "Content-Type: application/x-www-form-urlencoded" +
+                 SimpleHttpClient.CRLF +
+                 "Connection: close" + SimpleHttpClient.CRLF +
+                 SimpleHttpClient.CRLF +
+                 "9" + SimpleHttpClient.CRLF +
+                 "test=data" + SimpleHttpClient.CRLF +
+                 "0" + SimpleHttpClient.CRLF +
+                 SimpleHttpClient.CRLF;
+                
+        Client client = new Client(tomcat.getConnector().getLocalPort());
+        client.setRequest(new String[] {request});
+       
+        client.connect();
+        client.processRequest();
+        assertTrue(client.isResponse200());
+        assertTrue(client.getResponseBody().contains("test - data"));
+    }
+    
+    
+
+
 
     @Test
     public void testWithTEIdentity() throws Exception {