Make RPC failures due to bad numerical values clearer (external issue 4263).
Review by: jat
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7238 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
index cb5bf63..55274a6 100644
--- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -457,12 +457,18 @@
}
public byte readByte() throws SerializationException {
- return Byte.parseByte(extract());
+ String value = extract();
+ try {
+ return Byte.parseByte(value);
+ } catch (NumberFormatException e) {
+ throw getNumberFormatException(value, "byte",
+ Byte.MIN_VALUE, Byte.MAX_VALUE);
+ }
}
public char readChar() throws SerializationException {
// just use an int, it's more foolproof
- return (char) Integer.parseInt(extract());
+ return (char) readInt();
}
public double readDouble() throws SerializationException {
@@ -474,7 +480,13 @@
}
public int readInt() throws SerializationException {
- return Integer.parseInt(extract());
+ String value = extract();
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw getNumberFormatException(value, "int",
+ Integer.MIN_VALUE, Integer.MAX_VALUE);
+ }
}
public long readLong() throws SerializationException {
@@ -484,7 +496,13 @@
}
public short readShort() throws SerializationException {
- return Short.parseShort(extract());
+ String value = extract();
+ try {
+ return Short.parseShort(value);
+ } catch (NumberFormatException e) {
+ throw getNumberFormatException(value, "short",
+ Short.MIN_VALUE, Short.MAX_VALUE);
+ }
}
public String readString() throws SerializationException {
@@ -771,6 +789,36 @@
throw new SerializationException("Too few tokens in RPC request", e);
}
}
+
+ /**
+ * Returns a suitable NumberFormatException with an explanatory message
+ * when a numerical value cannot be parsed according to its expected
+ * type.
+ *
+ * @param value the value as read from the RPC stream
+ * @param type the name of the expected type
+ * @param minValue the smallest valid value for the expected type
+ * @param maxValue the largest valid value for the expected type
+ * @return a NumberFormatException with an explanatory message
+ */
+ private NumberFormatException getNumberFormatException(String value,
+ String type, double minValue, double maxValue) {
+ String message = "a non-numerical value";
+ try {
+ // Check the field contents in order to produce a more comprehensible
+ // error message
+ double d = Double.parseDouble(value);
+ if (d < minValue || d > maxValue) {
+ message = "an out-of-range value";
+ } else if (d != Math.floor(d)) {
+ message = "a fractional value";
+ }
+ } catch (NumberFormatException e2) {
+ }
+
+ return new NumberFormatException("Expected type '" + type + "' but received " +
+ message + ": " + value);
+ }
/**
* Returns a Map from a field name to the setter method for that field, for a
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
index f59efd6..47a7b2b 100644
--- a/user/test/com/google/gwt/user/server/rpc/RPCTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
@@ -18,6 +18,7 @@
import static com.google.gwt.user.client.rpc.impl.AbstractSerializationStream.RPC_SEPARATOR_CHAR;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializableException;
import com.google.gwt.user.client.rpc.SerializationException;
@@ -68,6 +69,24 @@
void method1();
}
+ /**
+ * Test error message for an out=of-range int value.
+ *
+ * @see RPCTest#testDecodeBadIntegerValue()
+ */
+ private static class Wrapper implements IsSerializable {
+ byte value1;
+ char value2;
+ short value3;
+ int value4;
+ public Wrapper() { }
+ }
+
+ @SuppressWarnings("rpc-validation")
+ private static interface WrapperIF extends RemoteService {
+ void method1(Wrapper w);
+ }
+
private static final String VALID_ENCODED_REQUEST = ""
+ AbstractSerializationStream.SERIALIZATION_STREAM_VERSION
+ RPC_SEPARATOR_CHAR + // version
@@ -164,6 +183,90 @@
"1\uffff" + // interface name
"2\uffff" + // method name
"0\uffff"; // param count
+
+ /**
+ * Tests that out-of-range or other illegal integer values generated
+ * by client-side serialization get a nested exception with a reasonable
+ * error message.
+ */
+ public void testDecodeBadIntegerValue() {
+ String requestBase = "" +
+ AbstractSerializationStream.SERIALIZATION_STREAM_VERSION +
+ RPC_SEPARATOR_CHAR + // version
+ "0" + RPC_SEPARATOR_CHAR + // flags
+ "6" + RPC_SEPARATOR_CHAR + // string table entry count
+ WrapperIF.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1
+ "method1" + RPC_SEPARATOR_CHAR + // string table entry #2
+ "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3
+ "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4
+ Wrapper.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #5
+ Wrapper.class.getName() +
+ "/316143997" + RPC_SEPARATOR_CHAR + // string table entry #6
+ "3" + RPC_SEPARATOR_CHAR + // module base URL
+ "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+ "1" + RPC_SEPARATOR_CHAR + // interface name
+ "2" + RPC_SEPARATOR_CHAR + // method name
+ "1" + RPC_SEPARATOR_CHAR + // param count
+ "5" + RPC_SEPARATOR_CHAR + // IntWrapper class name
+ "6" + RPC_SEPARATOR_CHAR; // IntWrapper signature
+
+ // Valid values
+ String goodRequest = requestBase + "12" + RPC_SEPARATOR_CHAR + // byte
+ "345" + RPC_SEPARATOR_CHAR + // char
+ "678" + RPC_SEPARATOR_CHAR + // short
+ "9101112" + RPC_SEPARATOR_CHAR; // int
+
+ RPC.decodeRequest(goodRequest); // should succeed
+
+ // Create bad RPC messages with out of range, fractional, and non-numerical
+ // values for byte, char, short, and int fields.
+ for (int idx = 0; idx < 12; idx++) {
+ String b = "12";
+ String c = "345";
+ String s = "678";
+ String i = "9101112";
+ String message = null;
+ String badValue = null;
+
+ // Choose type of bad value and expected error message string
+ switch (idx / 4) {
+ case 0:
+ badValue = "123456789123456789";
+ message = "out-of-range";
+ break;
+ case 1:
+ badValue = "1.25";
+ message = "fractional";
+ break;
+ case 2:
+ badValue = "123ABC";
+ message = "non-numerical";
+ break;
+ }
+
+ // Choose field to hold bad value
+ switch (idx % 4) {
+ case 0: b = badValue; break;
+ case 1: c = badValue; break;
+ case 2: s = badValue; break;
+ case 3: i = badValue; break;
+ }
+
+ // Form the request
+ String request = requestBase + b + RPC_SEPARATOR_CHAR + // byte
+ c + RPC_SEPARATOR_CHAR + // char
+ s + RPC_SEPARATOR_CHAR + // short
+ i + RPC_SEPARATOR_CHAR; // int
+
+ // Check that request fails with the expected message
+ try {
+ RPC.decodeRequest(request);
+ fail();
+ } catch (IncompatibleRemoteServiceException e) {
+ assertTrue(e.getMessage().contains(message));
+ }
+ }
+ }
/**
* Tests that seeing obsolete RPC formats throws an