When I hit RpcServlet(gadgets-metadata) with a POST that has
Transfer-Encoding chunked the server returns a 411 Length Required.
Problem is the http 1.1 spec says that Content-Length should not be
there, and more importantly Content-Length must be ignored if the
Transfer-Encoding is present and has a value other than identity.
I've done a basic search of the mailing list and I can't see why the
length checks are present, but I suspect they are some basic DOS
protection. Just to give additional information all of my posted
content to /gadgets/metadata have had Content-Length settings in the
range of 210-220. The check if the content length is too long is
1024*128. Seems to be a check to avoid malformed or maliciously
formed POSTS. I suspect there are issues around transfer-encoding
where someone could choke a server with extremely large POST entity
requests. So RpcServlet may have avoided that issue, but did so by
breaking the http 1.1 spec.
Attached are a unit test (JUnit 3, requires httpclient 3.1) and a
patch file that causes the test to pass. I don't believe that this is
necessarily the right fix, but to know that I would have to know why
the checks are there.
Can anyone shed any light on this issue?
--
David S Boyer (IBM Jazz Web UI Foundation)
[email protected]
703.499.8728(h)
703.408.5395(m)
### Jazz Patch 1.0
#comment: "shindig /gadgets/metadata fails on a post with transfer-encoding \"chunked\""
#changeset: "_7oCOIqOkEd6nWIGVCz8vZg"
#date: "2009-09-17 12:37:29.000000099 -0400"
#work_item: "_T3zS4KOnEd6aXrImTl-GCA" "WorkItem" "com.ibm.team.workitem"
#itemid: "org.apache.shindig/src-shindig/org/apache/shindig/gadgets/servlet/RpcServlet.java" "_1PdA4KLzEd67a_hGj--Rpw" "_9lwKIF9rEd6yqpSYkR3zcw"
#itemid: "org.apache.shindig/src-shindig/org/apache/shindig/gadgets/servlet" "_1Nl_sKLzEd67a_hGj--Rpw" "_9lwKIF9rEd6yqpSYkR3zcw"
#itemid: "org.apache.shindig" "_p261AIEKEd6WONUw66A2xQ" "_9lwKIF9rEd6yqpSYkR3zcw"
#itemid: "org.apache.shindig/src-tests" "_H0gE4KL-Ed67a_hGj--Rpw" "_9lwKIF9rEd6yqpSYkR3zcw"
#before_state: "org.apache.shindig/src-shindig/org/apache/shindig/gadgets/servlet/RpcServlet.java" "_1PdA4KLzEd67a_hGj--Rpw" "_PkyfNKOfEd6nWIGVCz8vZg"
#after_state: "org.apache.shindig/src-shindig/org/apache/shindig/gadgets/servlet/RpcServlet.java" "_1PdA4KLzEd67a_hGj--Rpw" "_7tIJhKOkEd6nWIGVCz8vZg"
#after_state: "org.apache.shindig/src-tests" "_H0gE4KL-Ed67a_hGj--Rpw" "_F43kwKOlEd6nWIGVCz8vZg"
#create_folder: "org.apache.shindig/src-tests"
#
diff -u -N org.apache.shindig/src-shindig/org/apache/shindig/gadgets/servlet/RpcServlet.java org.apache.shindig/src-shindig/org/apache/shindig/gadgets/servlet/RpcServlet.java
--- org.apache.shindig/src-shindig/org/apache/shindig/gadgets/servlet/RpcServlet.java 2009-09-17 11:31:40.000000000 -0400
+++ org.apache.shindig/src-shindig/org/apache/shindig/gadgets/servlet/RpcServlet.java 2009-09-17 12:06:27.000000000 -0400
@@ -84,29 +84,33 @@
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
-
- int length = request.getContentLength();
- if (length <= 0) {
- logger.info("No Content-Length specified.");
- response.setStatus(HttpServletResponse.SC_LENGTH_REQUIRED);
- return;
- }
- if (length > POST_REQUEST_MAX_SIZE) {
- logger.info("Request size too large: " + length);
- response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
- return;
- }
-
- ServletInputStream is = request.getInputStream();
- byte[] body = IOUtils.toByteArray(is);
- if (body.length != length) {
- logger.info("Wrong size. Length: " + length + " real: " + body.length);
- response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
-
- Result result = process(request, response, body);
- response.getWriter().write(result.getOutput());
+ byte[] body = null;
+ if ( request.getHeader("Transfer-Encoding").equals("chunked") ) {
+ ServletInputStream is = request.getInputStream();
+ body = IOUtils.toByteArray(is);
+ } else {
+ int length = request.getContentLength();
+ if (length <= 0) {
+ logger.info("No Content-Length specified.");
+ response.setStatus(HttpServletResponse.SC_LENGTH_REQUIRED);
+ return;
+ }
+ if (length > POST_REQUEST_MAX_SIZE) {
+ logger.info("Request size too large: " + length);
+ response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
+ return;
+ }
+ ServletInputStream is = request.getInputStream();
+ body = IOUtils.toByteArray(is);
+ if (body.length != length) {
+ logger.info("Wrong size. Length: " + length + " real: "
+ + body.length);
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ }
+ Result result = process(request, response, body);
+ response.getWriter().write(result.getOutput());
}
private String validateParameterValue(HttpServletRequest request, String parameter)
package org.apache.shindig;
import java.io.IOException;
import junit.framework.TestCase;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import sun.net.www.protocol.http.HttpURLConnection;
public class RpcServletTest extends TestCase {
private final static String HOST = "localhost";
private final static String PORT = "9081";
private final static String URL = "http://"+HOST+":"+PORT+"/gadgets/metadata";
private final static String REQUEST = "blah blah blah";
public void testTransferEncodingChunked() throws IOException {
HttpClient client = new HttpClient();
HttpState state = null;
HostConfiguration config = new HostConfiguration();
PostMethod post = new PostMethod(URL);
post.setContentChunked(true);
post.setRequestEntity(new StringRequestEntity(REQUEST,null,null));
int x = client.executeMethod(post);
if ( x == HttpURLConnection.HTTP_LENGTH_REQUIRED ) {
fail("HTTP Length Required not a valid error when using Transfer-Encoding chunked");
}
}
}