Revision: 8449
Author: [email protected]
Date: Fri Jul 30 12:54:15 2010
Log: No longer require RPCs to contain a 'Content-Length' HTTP request
header, thus
enabling support for XHR with 'Transfer-Encoding: Chunked'.
Review at http://gwt-code-reviews.appspot.com/727801
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=8449
Modified:
/trunk/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
/trunk/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java
=======================================
--- /trunk/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java Fri
Nov 20 09:36:38 2009
+++ /trunk/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java Fri
Jul 30 12:54:15 2010
@@ -31,6 +31,11 @@
* the RPC system.
*/
public class RPCServletUtils {
+ /**
+ * Package protected for use in tests.
+ */
+ static final int BUFFER_SIZE = 4096;
+
private static final String ACCEPT_ENCODING = "Accept-Encoding";
private static final String ATTACHMENT = "attachment";
@@ -156,39 +161,32 @@
* the UTF-8 charset
* @throws IOException if the requests input stream cannot be accessed,
read
* from or closed
- * @throws ServletException if the content length of the request is not
- * specified of if the request's content type is not
- * 'text/x-gwt-rpc' and 'charset=utf-8'
+ * @throws ServletException if the request's content type is not
+ * 'text/x-gwt-rpc' and 'charset=utf-8'
*/
public static String readContentAsUtf8(HttpServletRequest request,
boolean checkHeaders) throws IOException, ServletException {
- int contentLength = request.getContentLength();
- if (contentLength == -1) {
- // Content length must be known.
- throw new ServletException("Content-Length must be specified");
- }
-
if (checkHeaders) {
checkContentType(request);
checkCharacterEncoding(request);
}
+ /*
+ * Need to support 'Transfer-Encoding: chunked', so do not rely on
+ * presence of a 'Content-Length' request header.
+ */
InputStream in = request.getInputStream();
+ byte[] buffer = new byte[BUFFER_SIZE];
+ ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
try {
- byte[] payload = new byte[contentLength];
- int offset = 0;
- int len = contentLength;
- int byteCount;
- while (offset < contentLength) {
- byteCount = in.read(payload, offset, len);
+ while (true) {
+ int byteCount = in.read(buffer);
if (byteCount == -1) {
- throw new ServletException("Client did not send " + contentLength
- + " bytes as expected");
- }
- offset += byteCount;
- len -= byteCount;
- }
- return new String(payload, CHARSET_UTF8);
+ break;
+ }
+ out.write(buffer, 0, byteCount);
+ }
+ return out.toString(CHARSET_UTF8);
} finally {
if (in != null) {
in.close();
=======================================
---
/trunk/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java
Wed Oct 28 09:10:53 2009
+++
/trunk/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java
Fri Jul 30 12:54:15 2010
@@ -16,9 +16,13 @@
package com.google.gwt.user.server.rpc;
+import com.google.gwt.user.client.rpc.UnicodeEscapingTest;
+
import junit.framework.TestCase;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
@@ -34,11 +38,17 @@
* Mocks a request with the specified Content-Type.
*/
class MockReqContentType extends MockHttpServletRequest {
- String mockContent = "abcdefg";
+ final String mockContent;
final String mockContentType;
+
public MockReqContentType(String contentType) {
+ this(contentType, "abcdefg");
+ }
+
+ public MockReqContentType(String contentType, String content) {
this.mockContentType = contentType;
+ this.mockContent = content;
}
@Override
@@ -58,12 +68,9 @@
@Override
public String getHeader(String name) {
- if (name.toLowerCase().equals("Content-Type")) {
- return mockContentType;
- }
- return "";
- }
-
+ return null;
+ }
+
@SuppressWarnings("unused")
@Override
public ServletInputStream getInputStream() throws IOException {
@@ -72,11 +79,10 @@
}
static class MockServletInputStream extends ServletInputStream {
- private boolean readOnce = false;
- final private String value;
-
- MockServletInputStream(String value) {
- this.value = value;
+ private ByteArrayInputStream realStream;
+
+ MockServletInputStream(String mockContent) throws
UnsupportedEncodingException {
+ realStream = new ByteArrayInputStream(mockContent.getBytes("UTF-8"));
}
@Override
@@ -86,33 +92,77 @@
@Override
public int read(byte[] b, int off, int len) throws IOException {
- if (readOnce) {
- // simulate EOF
- return -1;
- }
- readOnce = true;
-
- int pos = 0;
- int i;
- for (i = off; i < len; ++i, ++pos) {
- b[i] = (byte) (this.value.charAt(pos) % 0xff);
- }
- return i;
- }
-
- @Override
- public int readLine(byte[] b, int off, int len) throws IOException {
- return read(b, off, len);
- }
+ return realStream.read(b, off, len);
+ }
+ }
+
+ /**
+ * Large content length should be read correctly.
+ */
+ public void testContentLengthLarge() throws IOException,
ServletException {
+ // Choose a non trivial size RPC payload
+ int contentLength = 50000;
+ String content =
UnicodeEscapingTest.getStringContainingCharacterRange(0, contentLength);
+ String result = readContentAsUtf8(content);
+ assertEquals(content, result);
}
/**
- * Content type doesn't match x-gwt-rpc, but ignore it.
+ * Content length smaller than the buffer size should be read correctly.
*/
- public void testIgnoreContentType() throws IOException, ServletException
{
- HttpServletRequest m = new MockReqContentType(
- "application/www-form-encoded");
- RPCServletUtils.readContentAsUtf8(m, false);
+ public void testContentLengthLessThanBufferSize() throws IOException,
ServletException {
+ // Choose a value smaller than the buffer
+ int contentLength = RPCServletUtils.BUFFER_SIZE - 1;
+ String content =
UnicodeEscapingTest.getStringContainingCharacterRange(0, contentLength);
+ String result = readContentAsUtf8(content);
+ assertEquals(content, result);
+ }
+
+ /**
+ * Content length which is an integer multiple of buffer size should be
read
+ * correctly.
+ */
+ public void testContentLengthMultipleOfBufferSize() throws IOException,
ServletException {
+ // Choose a value which is not a multiple of the buffer size
+ int contentLength = RPCServletUtils.BUFFER_SIZE * 3;
+ String content =
UnicodeEscapingTest.getStringContainingCharacterRange(0, contentLength);
+ String result = readContentAsUtf8(content);
+ assertEquals(content, result);
+ }
+
+ /**
+ * Content length which is not an integer multiple of buffer size should
be
+ * read correctly.
+ */
+ public void testContentLengthNotMultipleOfBufferSize() throws
IOException, ServletException {
+ // Choose a value which is not a multiple of the buffer size
+ int contentLength = RPCServletUtils.BUFFER_SIZE * 3 + 1;
+ String content =
UnicodeEscapingTest.getStringContainingCharacterRange(0, contentLength);
+ String result = readContentAsUtf8(content);
+ assertEquals(content, result);
+ }
+
+ /**
+ * Content length smaller than the buffer size should be read correctly.
+ */
+ public void testContentLengthSlightlyLargerThanBufferSize() throws
IOException, ServletException {
+ // Choose a value slightly larger than the buffer
+ int contentLength = RPCServletUtils.BUFFER_SIZE + 1;
+ String content =
UnicodeEscapingTest.getStringContainingCharacterRange(0, contentLength);
+ String result = readContentAsUtf8(content);
+ assertEquals(content, result);
+ }
+
+ /**
+ * Zero content length is never expected, but being able to correctly
read
+ * zero length content is a useful boundary condition test.
+ */
+ public void testContentLengthZero() throws IOException, ServletException
{
+ // While zero content length is not actually useful, a test
+ int contentLength = 0;
+ String content =
UnicodeEscapingTest.getStringContainingCharacterRange(0, contentLength);
+ String result = readContentAsUtf8(content);
+ assertEquals(content, result);
}
/**
@@ -130,6 +180,14 @@
RPCServletUtils.readContentAsUtf8(m, false);
}
+
+ /**
+ * Content type doesn't match x-gwt-rpc, but ignore it.
+ */
+ public void testIgnoreContentType() throws IOException, ServletException
{
+ HttpServletRequest m = new
MockReqContentType("application/www-form-encoded");
+ RPCServletUtils.readContentAsUtf8(m, false);
+ }
/**
* A non UTF-8 character encoding should be rejected.
@@ -229,4 +287,9 @@
fail("Expected exception from null content type");
}
}
-}
+
+ private String readContentAsUtf8(String content) throws IOException,
ServletException {
+ HttpServletRequest m = new MockReqContentType("text/x-gwt-rpc",
content);
+ return RPCServletUtils.readContentAsUtf8(m, false);
+ }
+}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors