Revision: 8466
Author: fre...@google.com
Date: Mon Aug 2 20:55:43 2010
Log: Change RequestFactory based requests:
- No longer require a 'Content-Length' HTTP request header, thus enabling
support for 'Transfer-Encoding: chunked'
- Use 'application/json; charset=utf-8' Content-Type instead
of 'text/plain; charset=utf=8'
Review at http://gwt-code-reviews.appspot.com/674804
Review by: amitman...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=8466
Modified:
/trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
/trunk/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
/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/junit/server/JUnitHostImpl.java Fri Apr
23 06:39:33 2010
+++ /trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java Mon Aug
2 20:55:43 2010
@@ -144,7 +144,7 @@
HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
if (requestURI.endsWith("/junithost/loadError")) {
- String requestPayload = RPCServletUtils.readContentAsUtf8(request);
+ String requestPayload = RPCServletUtils.readContentAsGwtRpc(request);
JUnitResult result = new JUnitResult();
initResult(request, result);
result.setException(new JUnitFatalLaunchException(requestPayload));
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
Fri Jul 30 17:29:09 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/client/impl/RequestFactoryJsonImpl.java
Mon Aug 2 20:55:43 2010
@@ -90,6 +90,8 @@
public void fire(final RequestObject<?> requestObject) {
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST,
GWT.getHostPageBaseURL() + RequestFactory.URL);
+ builder.setHeader(
+ "Content-Type", RequestFactory.JSON_CONTENT_TYPE_UTF8);
// TODO: do something better here...
if (requestObject.getDeltaValueStore().isChanged()) {
builder.setRequestData(ClientRequestHelper.getRequestString(JsonRequestDataUtil.getRequestMap(
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
Mon Aug 2 15:57:18 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
Mon Aug 2 20:55:43 2010
@@ -15,10 +15,13 @@
*/
package com.google.gwt.requestfactory.server;
-import java.io.BufferedInputStream;
+import com.google.gwt.requestfactory.shared.RequestFactory;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+
import java.io.IOException;
import java.io.PrintWriter;
+import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -49,14 +52,17 @@
*/
@SuppressWarnings("serial")
public class RequestFactoryServlet extends HttpServlet {
-
+ private static final String JSON_CHARSET = "UTF-8";
+ private static final String JSON_CONTENT_TYPE = "application/json";
+
@SuppressWarnings("unchecked")
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse
response)
- throws IOException {
+ throws IOException, ServletException {
ensureConfig();
- String jsonRequestString = getContent(request);
+ String jsonRequestString = RPCServletUtils.readContent(
+ request, JSON_CONTENT_TYPE, JSON_CHARSET);
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter writer = response.getWriter();
@@ -72,6 +78,8 @@
JsonRequestProcessor requestProcessor = new JsonRequestProcessor();
requestProcessor.setOperationRegistry(new
ReflectionBasedOperationRegistry(
new DefaultSecurityProvider()));
+ response.setHeader(
+ "Content-Type", RequestFactory.JSON_CONTENT_TYPE_UTF8);
writer.print(requestProcessor.decodeAndInvokeRequest(jsonRequestString));
writer.flush();
}
@@ -91,22 +99,4 @@
UserInformation.setUserInformationImplClass(userInfoClass);
}
}
-
- private String getContent(HttpServletRequest request) throws IOException
{
- int contentLength = request.getContentLength();
- byte contentBytes[] = new byte[contentLength];
- BufferedInputStream bis = new
BufferedInputStream(request.getInputStream());
- try {
- int contentBytesOffset = 0;
- int readLen;
- while ((readLen = bis.read(contentBytes, contentBytesOffset,
- contentLength - contentBytesOffset)) > 0) {
- contentBytesOffset += readLen;
- }
- // TODO: encoding issues?
- return new String(contentBytes);
- } finally {
- bis.close();
- }
- }
-}
+}
=======================================
---
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
Fri Jul 30 11:32:15 2010
+++
/trunk/user/src/com/google/gwt/requestfactory/shared/RequestFactory.java
Mon Aug 2 20:55:43 2010
@@ -28,12 +28,14 @@
* Marker interface for the RequestFactory code generator.
*/
public interface RequestFactory {
+ public static final String JSON_CONTENT_TYPE_UTF8 =
+ "application/json; charset=utf-8";
+
+ String SYNC = "SYNC";
// TODO: this must be configurable
String URL = "gwtRequest";
- String SYNC = "SYNC";
-
Record create(Class token);
ValueStore getValueStore();
=======================================
---
/trunk/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
Thu Nov 19 14:53:22 2009
+++
/trunk/user/src/com/google/gwt/user/server/rpc/AbstractRemoteServiceServlet.java
Mon Aug 2 20:55:43 2010
@@ -169,14 +169,17 @@
* request. For example, you may want to bypass the check of the
Content-Type
* and character encoding headers in the request, as some proxies
re-write the
* request headers. Note that bypassing these checks may expose the
servlet to
- * some cross-site vulnerabilities.
+ * some cross-site vulnerabilities. Your implementation should comply
with the
+ * HTTP/1.1 specification, which includes handling both requests which
include
+ * a Content-Length header and requests utilizing
<code>Transfer-Encoding:
+ * chuncked</code>.
*
* @param request the incoming request
* @return the content of the incoming request encoded as a string.
*/
protected String readContent(HttpServletRequest request)
throws ServletException, IOException {
- return RPCServletUtils.readContentAsUtf8(request, true);
+ return RPCServletUtils.readContentAsGwtRpc(request);
}
/**
=======================================
--- /trunk/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java Fri
Jul 30 12:54:15 2010
+++ /trunk/user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java Mon
Aug 2 20:55:43 2010
@@ -40,6 +40,9 @@
private static final String ATTACHMENT = "attachment";
+ /**
+ * Used both as expected request charset and encoded response charset.
+ */
private static final String CHARSET_UTF8 = "UTF-8";
private static final String CONTENT_DISPOSITION = "Content-Disposition";
@@ -50,12 +53,10 @@
private static final String CONTENT_TYPE_APPLICATION_JSON_UTF8
= "application/json; charset=utf-8";
- private static final String EXPECTED_CHARSET = "charset=utf-8";
-
- private static final String EXPECTED_CONTENT_TYPE = "text/x-gwt-rpc";
-
private static final String GENERIC_FAILURE_MSG = "The call failed on
the server; see server log for details";
+ private static final String GWT_RPC_CONTENT_TYPE = "text/x-gwt-rpc";
+
/**
* Controls the compression threshold at and below which no compression
will
* take place.
@@ -132,43 +133,34 @@
/**
* Returns the content of an {...@link HttpServletRequest} by decoding it
using
- * the UTF-8 charset.
+ * <code>expectedCharSet</code>, or <code>UTF-8</code> if
+ * <code>expectedCharSet</code> is <code>null</null>.
*
* @param request the servlet request whose content we want to read
+ * @param expectedContentType the expected content (i.e. 'type/subtype'
only)
+ * in the Content-Type request header, or <code>null</code> if
no
+ * validation is to be performed, and you are willing to allow
for
+ * some types of cross type security attacks
+ * @param expectedCharSet the expected request charset, or
<code>null</code>
+ * if no charset validation is to be performed and
<code>UTF-8</code>
+ * should be assumed
* @return the content of an {...@link HttpServletRequest} by decoding it
using
- * 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'
+ * <code>expectedCharSet</code>, or <code>UTF-8</code> if
+ * <code>expectedCharSet</code> is <code>null</null>
+ * @throws IOException if the request's input stream cannot be accessed,
read
+ * from or closed
+ * @throws ServletException if the request's content type does not
+ * equal the supplied <code>expectedContentType</code> or
+ * <code>expectedCharSet</code>
*/
- public static String readContentAsUtf8(HttpServletRequest request)
+ public static String readContent(HttpServletRequest request,
+ String expectedContentType, String expectedCharSet)
throws IOException, ServletException {
- return readContentAsUtf8(request, true);
- }
-
- /**
- * Returns the content of an {...@link HttpServletRequest} by decoding it
using
- * the UTF-8 charset.
- *
- * @param request the servlet request whose content we want to read
- * @param checkHeaders Specify 'true' to check the Content-Type header
to see
- * that it matches the expected value 'text/x-gwt-rpc' and the
- * content encoding is UTF-8. Disabling this check may allow
some
- * types of cross type security attacks.
- * @return the content of an {...@link HttpServletRequest} by decoding it
using
- * the UTF-8 charset
- * @throws IOException if the requests input stream cannot be accessed,
read
- * from or closed
- * @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 {
- if (checkHeaders) {
- checkContentType(request);
- checkCharacterEncoding(request);
+ if (expectedContentType != null) {
+ checkContentTypeIgnoreCase(request, expectedContentType);
+ }
+ if (expectedCharSet != null) {
+ checkCharacterEncodingIgnoreCase(request, expectedCharSet);
}
/*
@@ -186,13 +178,32 @@
}
out.write(buffer, 0, byteCount);
}
- return out.toString(CHARSET_UTF8);
+ String contentCharSet = expectedCharSet != null
+ ? expectedCharSet : CHARSET_UTF8;
+ return out.toString(contentCharSet);
} finally {
if (in != null) {
in.close();
}
}
}
+
+ /**
+ * Returns the content of an {...@link HttpServletRequest}, after verifying
a
+ * <code>gwt/x-gwt-rpc; charset=utf-8</code> content type.
+ *
+ * @param request the servlet request whose content we want to read
+ * @return the content of an {...@link HttpServletRequest} by decoding it
using
+ * <code>UTF-8</code>
+ * @throws IOException if the request's input stream cannot be accessed,
read
+ * from or closed
+ * @throws ServletException if the request's content type is not
+ * <code>gwt/x-gwt-rpc; charset=utf-8</code>, ignoring case
+ */
+ public static String readContentAsGwtRpc(HttpServletRequest request)
+ throws IOException, ServletException {
+ return readContent(request, GWT_RPC_CONTENT_TYPE, CHARSET_UTF8);
+ }
/**
* Sets the correct header to indicate that a response is gzipped.
@@ -307,12 +318,15 @@
}
/**
- * Performs validation of the character encoding.
+ * Performs validation of the character encoding, ignoring case.
*
- * @param request the incoming request.
- * @throws ServletException if requests encoding is not UTF-8
+ * @param request the incoming request
+ * @param expectedCharSet the expected charset of the request
+ * @throws ServletException if requests encoding is not
<code>null</code> and
+ * does not equal, ignoring case, <code>expectedCharSet</code>
*/
- private static void checkCharacterEncoding(HttpServletRequest request)
+ private static void checkCharacterEncodingIgnoreCase(
+ HttpServletRequest request, String expectedCharSet)
throws ServletException {
boolean encodingOkay = false;
String characterEncoding = request.getCharacterEncoding();
@@ -323,7 +337,8 @@
* properly parsed character encoding string if we decide to make
this
* change.
*/
- if
(characterEncoding.toLowerCase().indexOf(CHARSET_UTF8.toLowerCase()) != -1)
{
+ if
(characterEncoding.toLowerCase().indexOf(expectedCharSet.toLowerCase())
+ != -1) {
encodingOkay = true;
}
}
@@ -331,18 +346,24 @@
if (!encodingOkay) {
throw new ServletException("Character Encoding is '"
+ (characterEncoding == null ? "(null)" : characterEncoding)
- + "'. Expected '" + EXPECTED_CHARSET + "'");
+ + "'. Expected '" + expectedCharSet + "'");
}
}
/**
- * Performs Content-Type validation of the incoming request.
- *
- * @param request the incoming request.
+ * Performs Content-Type validation of the incoming request, ignoring
case
+ * and any <code>charset</code> parameter.
+ *
+ * @see #checkCharacterEncodingIgnoreCase(HttpServletRequest, String)
+ * @param request the incoming request
+ * @param expectedContentType the expected Content-Type for the incoming
+ * request
* @throws ServletException if the request's content type is not
- * 'text/x-gwt-rpc'
+ * <code>null</code> and does not, ignoring case, equal
+ * <code>expectedContentType</code>,
*/
- private static void checkContentType(HttpServletRequest request)
+ private static void checkContentTypeIgnoreCase(
+ HttpServletRequest request, String expectedContentType)
throws ServletException {
String contentType = request.getContentType();
boolean contentTypeIsOkay = false;
@@ -350,12 +371,10 @@
if (contentType != null) {
contentType = contentType.toLowerCase();
/*
- * The Content-Type must be text/x-gwt-rpc.
- *
* NOTE:We use startsWith because some servlet engines, i.e. Tomcat,
do
* not remove the charset component but others do.
*/
- if (contentType.startsWith(EXPECTED_CONTENT_TYPE)) {
+ if (contentType.startsWith(expectedContentType.toLowerCase())) {
contentTypeIsOkay = true;
}
}
@@ -363,7 +382,7 @@
if (!contentTypeIsOkay) {
throw new ServletException("Content-Type was '"
+ (contentType == null ? "(null)" : contentType) + "'.
Expected '"
- + EXPECTED_CONTENT_TYPE + "'.");
+ + expectedContentType + "'.");
}
}
=======================================
---
/trunk/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java
Fri Jul 30 12:54:15 2010
+++
/trunk/user/test/com/google/gwt/user/server/rpc/RPCServletUtilsTest.java
Mon Aug 2 20:55:43 2010
@@ -178,7 +178,7 @@
}
};
- RPCServletUtils.readContentAsUtf8(m, false);
+ RPCServletUtils.readContent(m, null, null);
}
/**
@@ -186,7 +186,7 @@
*/
public void testIgnoreContentType() throws IOException, ServletException
{
HttpServletRequest m = new
MockReqContentType("application/www-form-encoded");
- RPCServletUtils.readContentAsUtf8(m, false);
+ RPCServletUtils.readContent(m, null, null);
}
/**
@@ -203,7 +203,7 @@
boolean gotException = false;
try {
- RPCServletUtils.readContentAsUtf8(m);
+ RPCServletUtils.readContentAsGwtRpc(m);
} catch (ServletException se) {
if (se.getMessage().indexOf("Character Encoding") != 0) {
fail(" Unexpected exception " + se);
@@ -224,7 +224,7 @@
"application/www-form-encoded");
boolean gotException = false;
try {
- RPCServletUtils.readContentAsUtf8(m);
+ RPCServletUtils.readContentAsGwtRpc(m);
} catch (ServletException se) {
if (se.getMessage().indexOf("Content-Type") != 0) {
fail(" Unexpected exception " + se);
@@ -241,7 +241,16 @@
*/
public void testReadGoodContentType() throws IOException,
ServletException {
HttpServletRequest m = new MockReqContentType("text/x-gwt-rpc");
- RPCServletUtils.readContentAsUtf8(m);
+ RPCServletUtils.readContentAsGwtRpc(m);
+ }
+
+ /**
+ * Content-Type validation should ignore case.
+ */
+ public void testReadGoodContentTypeIgnoreCase()
+ throws IOException, ServletException {
+ HttpServletRequest m = new MockReqContentType("tExt/X-gwt-rPc");
+ RPCServletUtils.readContentAsGwtRpc(m);
}
/**
@@ -257,7 +266,7 @@
};
boolean gotException = false;
try {
- RPCServletUtils.readContentAsUtf8(m);
+ RPCServletUtils.readContentAsGwtRpc(m);
} catch (ServletException se) {
if (se.getMessage().indexOf("Character Encoding") != 0) {
fail(" Unexpected exception " + se);
@@ -276,7 +285,7 @@
HttpServletRequest m = new MockReqContentType(null);
boolean gotException = false;
try {
- RPCServletUtils.readContentAsUtf8(m);
+ RPCServletUtils.readContentAsGwtRpc(m);
} catch (ServletException se) {
if (se.getMessage().indexOf("Content-Type") != 0) {
fail(" Unexpected exception " + se);
@@ -289,7 +298,8 @@
}
private String readContentAsUtf8(String content) throws IOException,
ServletException {
- HttpServletRequest m = new MockReqContentType("text/x-gwt-rpc",
content);
- return RPCServletUtils.readContentAsUtf8(m, false);
+ HttpServletRequest m = new MockReqContentType(null, content);
+ // ignore Content-Type, read as UTF-8
+ return RPCServletUtils.readContent(m, null, null);
}
}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors