Add support for RpcTokens, which, if set, are sent with each RPCRequest to
the server. RpcTokens can be used to implement XSRF protection for GWT RPC
calls.
Review at http://gwt-code-reviews.appspot.com/1107801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9289 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/rpc/server/RPC.java b/user/src/com/google/gwt/rpc/server/RPC.java
index afd7b47..dc1abc4 100644
--- a/user/src/com/google/gwt/rpc/server/RPC.java
+++ b/user/src/com/google/gwt/rpc/server/RPC.java
@@ -162,7 +162,7 @@
parameterValues[i] = o;
}
- return new RPCRequest(method, parameterValues, null, 0);
+ return new RPCRequest(method, parameterValues, null, null, 0);
} catch (NoSuchMethodException e) {
throw new IncompatibleRemoteServiceException(
diff --git a/user/src/com/google/gwt/user/client/rpc/HasRpcToken.java b/user/src/com/google/gwt/user/client/rpc/HasRpcToken.java
new file mode 100644
index 0000000..fd44963
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/HasRpcToken.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+/**
+ * An interface implemented by client-side RPC proxy objects. Cast the object
+ * returned from {@link com.google.gwt.core.client.GWT#create(Class)} on a
+ * {@link RemoteService} to this interface to set {@link RpcToken} and
+ * {@link RpcTokenExceptionHandler}.
+ */
+public interface HasRpcToken {
+
+ /**
+ * Return RPC token used with this RPC instance.
+ *
+ * @return RPC token or {@code null} if none set.
+ */
+ RpcToken getRpcToken();
+
+ /**
+ * Return RPC token exception handler used with this RPC instance.
+ *
+ * @return Exception handler or {@code null} if none set.
+ */
+ RpcTokenExceptionHandler getRpcTokenExceptionHandler();
+
+ /**
+ * Sets the {@link RpcToken} to be included with each RPC call.
+ */
+ void setRpcToken(RpcToken token);
+
+ /**
+ * Sets the handler for exceptions that occurred during RPC token processing.
+ */
+ void setRpcTokenExceptionHandler(RpcTokenExceptionHandler handler);
+}
diff --git a/user/src/com/google/gwt/user/client/rpc/RpcToken.java b/user/src/com/google/gwt/user/client/rpc/RpcToken.java
new file mode 100644
index 0000000..5e6dd87
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/RpcToken.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+import java.io.Serializable;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An interface for RPC token implementation objects included with each RPC
+ * call. RPC tokens can be used to implement XSRF protection for RPC calls.
+ */
+public interface RpcToken extends Serializable {
+ /**
+ * {@link RemoteService} interfaces specifying {@link RpcToken} implementation
+ * using this annotation will only have serializers for the specific class
+ * generated, as opposed to generating serializers for all {@link RpcToken}
+ * implementations.
+ */
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface RpcTokenImplementation {
+ String value();
+ }
+}
diff --git a/user/src/com/google/gwt/user/client/rpc/RpcTokenException.java b/user/src/com/google/gwt/user/client/rpc/RpcTokenException.java
new file mode 100644
index 0000000..63bff8c
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/RpcTokenException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+/**
+ * Exception that will be passed to the
+ * {@link RpcTokenExceptionHandler#onRpcTokenException(RpcTokenException)}
+ * method when RPC token processing resulted in an error.
+ */
+public class RpcTokenException extends RuntimeException
+ implements IsSerializable {
+
+ private static final String DEFAULT_MESSAGE = "Invalid RPC token";
+
+ /**
+ * Constructs an instance with the default message.
+ */
+ public RpcTokenException() {
+ super(DEFAULT_MESSAGE);
+ }
+
+ /**
+ * Constructs an instance with the specified message.
+ */
+ public RpcTokenException(String msg) {
+ super(DEFAULT_MESSAGE + " (" + msg + ")");
+ }
+}
diff --git a/user/src/com/google/gwt/user/client/rpc/RpcTokenExceptionHandler.java b/user/src/com/google/gwt/user/client/rpc/RpcTokenExceptionHandler.java
new file mode 100644
index 0000000..f34aedd
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/RpcTokenExceptionHandler.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+/**
+ * Handles an exception produced while processing {@link RpcToken}.
+ */
+public interface RpcTokenExceptionHandler {
+
+ /**
+ * Process RPC token exception.
+ *
+ * @param exception exception that occurred during RPC token processing.
+ */
+ public void onRpcTokenException(RpcTokenException exception);
+}
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
index cc9d86b..9a8d093 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
@@ -41,10 +41,9 @@
/**
* The current RPC protocol version. This version differs from the previous
- * one in that primitive long values are represented as single-quoted base-64
- * strings with an alphabet of [A-Za-z0-9$_], rather than as pairs of doubles.
+ * one in that it supports {@links RpcToken}s.
*/
- public static final int SERIALIZATION_STREAM_VERSION = 6;
+ public static final int SERIALIZATION_STREAM_VERSION = 7;
/**
* The oldest supported RPC protocol version.
@@ -55,6 +54,16 @@
* Indicates that obfuscated type names should be used in the RPC payload.
*/
public static final int FLAG_ELIDE_TYPE_NAMES = 0x1;
+
+ /**
+ * Indicates that RPC token is included in the RPC payload.
+ */
+ public static final int FLAG_RPC_TOKEN_INCLUDED = 0x2;
+
+ /**
+ * Bit mask representing all valid flags.
+ */
+ public static final int VALID_FLAGS_MASK = 0x3;
private int flags = DEFAULT_FLAGS;
private int version = SERIALIZATION_STREAM_VERSION;
@@ -62,6 +71,16 @@
public final void addFlags(int flags) {
this.flags |= flags;
}
+
+ /**
+ * Checks if flags are valid.
+ *
+ * @return <code>true</code> if flags are valid and <code>false</code>
+ * otherwise.
+ */
+ public final boolean areFlagsValid() {
+ return (((flags | VALID_FLAGS_MASK) ^ VALID_FLAGS_MASK) == 0);
+ }
public final int getFlags() {
return flags;
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
index 83f2e61..6a9b797 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamReader.java
@@ -57,6 +57,11 @@
+ SERIALIZATION_STREAM_VERSION + " from server, got " + getVersion()
+ ".");
}
+
+ if (!areFlagsValid()) {
+ throw new IncompatibleRemoteServiceException("Got an unknown flag from "
+ + "server: " + getFlags());
+ }
stringTable = readJavaScriptObject();
}
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java b/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java
index 849ffe6..f7df811 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/RemoteServiceProxy.java
@@ -21,8 +21,11 @@
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.HasRpcToken;
import com.google.gwt.user.client.rpc.InvocationException;
import com.google.gwt.user.client.rpc.RpcRequestBuilder;
+import com.google.gwt.user.client.rpc.RpcToken;
+import com.google.gwt.user.client.rpc.RpcTokenExceptionHandler;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamFactory;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
@@ -37,7 +40,7 @@
* For internal use only.
*/
public abstract class RemoteServiceProxy implements SerializationStreamFactory,
- ServiceDefTarget {
+ ServiceDefTarget, HasRpcToken {
/**
* The content type to be used in HTTP requests.
@@ -151,6 +154,10 @@
private RpcRequestBuilder rpcRequestBuilder;
+ private RpcToken rpcToken;
+
+ private RpcTokenExceptionHandler rpcTokenExceptionHandler;
+
/**
* The name of the serialization policy file specified during construction.
*/
@@ -216,6 +223,20 @@
return clientSerializationStreamWriter;
}
+ /**
+ * @see ServiceDefTarget#getRpcToken()
+ */
+ public RpcToken getRpcToken() {
+ return rpcToken;
+ }
+
+ /**
+ * @see ServiceDefTarget#getRpcTokenExceptionHandler()
+ */
+ public RpcTokenExceptionHandler getRpcTokenExceptionHandler() {
+ return rpcTokenExceptionHandler;
+ }
+
public String getSerializationPolicyName() {
return serializationPolicyName;
}
@@ -232,17 +253,43 @@
}
/**
+ * @see HasRpcToken#setRpcToken(RpcToken)
+ */
+ public void setRpcToken(RpcToken token) {
+ checkRpcTokenType(token);
+ this.rpcToken = token;
+ }
+
+ /**
+ * @see HasRpcToken#setRpcTokenExceptionHandler(RpcTokenExceptionHandler)
+ */
+ public void setRpcTokenExceptionHandler(RpcTokenExceptionHandler handler) {
+ this.rpcTokenExceptionHandler = handler;
+ }
+
+ /**
* @see ServiceDefTarget#setServiceEntryPoint(String)
*/
public void setServiceEntryPoint(String url) {
this.remoteServiceURL = url;
}
+
+ /**
+ * This method is overridden by generated proxy classes to ensure that
+ * current service's {@link RpcToken} is of the type specified in {@link
+ * RpcToken.RpcTokenImplementation} annotation.
+ *
+ * @param token currently set {@link RpcToken}.
+ * @throws RpcTokenException if types mismatch.
+ */
+ protected void checkRpcTokenType(RpcToken token) {
+ }
protected <T> RequestCallback doCreateRequestCallback(
ResponseReader responseReader, String methodName, RpcStatsContext statsContext,
AsyncCallback<T> callback) {
return new RequestCallbackAdapter<T>(this, methodName, statsContext,
- callback, responseReader);
+ callback, getRpcTokenExceptionHandler(), responseReader);
}
/**
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/RequestCallbackAdapter.java b/user/src/com/google/gwt/user/client/rpc/impl/RequestCallbackAdapter.java
index 6e18d20..d6c3cbf 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/RequestCallbackAdapter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/RequestCallbackAdapter.java
@@ -21,6 +21,8 @@
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwt.user.client.rpc.RpcTokenException;
+import com.google.gwt.user.client.rpc.RpcTokenExceptionHandler;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamFactory;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
@@ -152,6 +154,11 @@
* {@link SerializationStreamReader}.
*/
private final ResponseReader responseReader;
+
+ /**
+ * {@link RpcTokenExceptionHandler} to notify of token exceptions.
+ */
+ private final RpcTokenExceptionHandler tokenExceptionHandler;
/**
* {@link SerializationStreamFactory} for creating
@@ -160,7 +167,16 @@
private final SerializationStreamFactory streamFactory;
public RequestCallbackAdapter(SerializationStreamFactory streamFactory,
- String methodName, RpcStatsContext statsContext, AsyncCallback<T> callback,
+ String methodName, RpcStatsContext statsContext,
+ AsyncCallback<T> callback, ResponseReader responseReader) {
+ this(streamFactory, methodName, statsContext, callback, null,
+ responseReader);
+ }
+
+ public RequestCallbackAdapter(SerializationStreamFactory streamFactory,
+ String methodName, RpcStatsContext statsContext,
+ AsyncCallback<T> callback,
+ RpcTokenExceptionHandler tokenExceptionHandler,
ResponseReader responseReader) {
assert (streamFactory != null);
assert (callback != null);
@@ -171,6 +187,7 @@
this.methodName = methodName;
this.statsContext = statsContext;
this.responseReader = responseReader;
+ this.tokenExceptionHandler = tokenExceptionHandler;
}
public void onError(Request request, Throwable exception) {
@@ -213,6 +230,9 @@
try {
if (caught == null) {
callback.onSuccess(result);
+ } else if (tokenExceptionHandler != null &&
+ caught instanceof RpcTokenException) {
+ tokenExceptionHandler.onRpcTokenException((RpcTokenException) caught);
} else {
callback.onFailure(caught);
}
diff --git a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
index 3da97e0..6e5ac08 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/ProxyCreator.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -42,6 +42,9 @@
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+import com.google.gwt.user.client.rpc.RpcToken;
+import com.google.gwt.user.client.rpc.RpcToken.RpcTokenImplementation;
+import com.google.gwt.user.client.rpc.RpcTokenException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter;
@@ -94,7 +97,7 @@
TypeOracle typeOracle,
SerializableTypeOracleBuilder typesSentFromBrowser,
SerializableTypeOracleBuilder typesSentToBrowser, JClassType remoteService)
- throws NotFoundException {
+ throws NotFoundException, UnableToCompleteException {
logger = logger.branch(TreeLogger.DEBUG, "Analyzing '"
+ remoteService.getParameterizedQualifiedSourceName()
+ "' for serializable types", null);
@@ -103,6 +106,33 @@
JClassType exceptionClass = typeOracle.getType(Exception.class.getName());
+ JClassType rteType = typeOracle.getType(RpcTokenException.class.getName());
+ JClassType rpcTokenClass = typeOracle.getType(RpcToken.class.getName());
+ RpcTokenImplementation tokenClassToUse =
+ remoteService.findAnnotationInTypeHierarchy(
+ RpcTokenImplementation.class);
+ if (tokenClassToUse != null) {
+ // only include serializer for the specified class literal
+ JClassType rpcTokenType = typeOracle.getType(tokenClassToUse.value());
+ if (rpcTokenType.isAssignableTo(rpcTokenClass)) {
+ typesSentFromBrowser.addRootType(logger, rpcTokenType);
+ typesSentToBrowser.addRootType(logger, rteType);
+ } else {
+ logger.branch(TreeLogger.ERROR,
+ "RPC token class " + tokenClassToUse.value() + " must implement " +
+ RpcToken.class.getName(), null);
+ throw new UnableToCompleteException();
+ }
+ } else {
+ JClassType[] rpcTokenSubclasses = rpcTokenClass.getSubtypes();
+ for (JClassType rpcTokenSubclass : rpcTokenSubclasses) {
+ typesSentFromBrowser.addRootType(logger, rpcTokenSubclass);
+ }
+ if (rpcTokenSubclasses.length > 0) {
+ typesSentToBrowser.addRootType(logger, rteType);
+ }
+ }
+
TreeLogger validationLogger = logger.branch(TreeLogger.DEBUG,
"Analyzing methods:", null);
for (JMethod method : methods) {
@@ -213,7 +243,7 @@
/**
* Creates the client-side proxy class.
- *
+ *
* @throws UnableToCompleteException
*/
public String create(TreeLogger logger, GeneratorContext context)
@@ -314,12 +344,13 @@
generateProxyContructor(srcWriter);
- generateProxyMethods(srcWriter, typesSentFromBrowser,
+ generateProxyMethods(srcWriter, typesSentFromBrowser, typeOracle,
syncMethToAsyncMethMap);
- if (elideTypeNames) {
- generateStreamWriterOverride(srcWriter);
- }
+ generateStreamWriterOverride(srcWriter);
+
+ generateCheckRpcTokenTypeOverride(srcWriter, typeOracle,
+ typesSentFromBrowser);
srcWriter.commit(logger);
@@ -359,6 +390,38 @@
return typeName == null ? null : ('"' + typeName + '"');
}
+ protected void generateCheckRpcTokenTypeOverride(SourceWriter srcWriter,
+ TypeOracle typeOracle, SerializableTypeOracle typesSentFromBrowser) {
+ JClassType rpcTokenType = typeOracle.findType(RpcToken.class.getName());
+ JClassType[] rpcTokenSubtypes = rpcTokenType.getSubtypes();
+ String rpcTokenImplementation = "";
+ for (JClassType rpcTokenSubtype : rpcTokenSubtypes) {
+ if (typesSentFromBrowser.isSerializable(rpcTokenSubtype)) {
+ if (!rpcTokenImplementation.isEmpty()) {
+ // >1 implematation of RpcToken, bail
+ rpcTokenImplementation = "";
+ break;
+ } else {
+ rpcTokenImplementation = rpcTokenSubtype.getQualifiedSourceName();
+ }
+ }
+ }
+ if (!rpcTokenImplementation.isEmpty()) {
+ srcWriter.println("@Override");
+ srcWriter.println("protected void checkRpcTokenType(RpcToken token) {");
+ srcWriter.indent();
+ srcWriter.println("if (!(token instanceof " + rpcTokenImplementation
+ + ")) {");
+ srcWriter.indent();
+ srcWriter.println("throw new RpcTokenException(\"Invalid RpcToken type: "
+ + "expected '" + rpcTokenImplementation + "' but got '\" + "
+ + "token.getClass() + \"'\");");
+ srcWriter.outdent();
+ srcWriter.println("}");
+ srcWriter.outdent();
+ srcWriter.println("}");
+ }
+ }
/**
* Generate the proxy constructor and delegate to the superclass constructor
* using the default address for the
@@ -379,7 +442,7 @@
/**
* Generate any fields required by the proxy.
- *
+ *
* @param serializableTypeOracle the type oracle
*/
protected void generateProxyFields(SourceWriter srcWriter,
@@ -398,12 +461,12 @@
/**
* Generates the client's asynchronous proxy method.
- *
+ *
* @param serializableTypeOracle the type oracle
*/
protected void generateProxyMethod(SourceWriter w,
- SerializableTypeOracle serializableTypeOracle, JMethod syncMethod,
- JMethod asyncMethod) {
+ SerializableTypeOracle serializableTypeOracle, TypeOracle typeOracle,
+ JMethod syncMethod, JMethod asyncMethod) {
w.println();
@@ -459,6 +522,12 @@
w.println("try {");
w.indent();
+ w.println("if (getRpcToken() != null) {");
+ w.indent();
+ w.println(streamWriterName + ".writeObject(getRpcToken());");
+ w.outdent();
+ w.println("}");
+
w.println(streamWriterName + ".writeString(REMOTE_SERVICE_INTERFACE_NAME);");
// Write the method name
@@ -552,7 +621,7 @@
}
protected void generateProxyMethods(SourceWriter w,
- SerializableTypeOracle serializableTypeOracle,
+ SerializableTypeOracle serializableTypeOracle, TypeOracle typeOracle,
Map<JMethod, JMethod> syncMethToAsyncMethMap) {
JMethod[] syncMethods = serviceIntf.getOverridableMethods();
for (JMethod syncMethod : syncMethods) {
@@ -575,7 +644,8 @@
}
}
- generateProxyMethod(w, serializableTypeOracle, syncMethod, asyncMethod);
+ generateProxyMethod(w, serializableTypeOracle, typeOracle, syncMethod,
+ asyncMethod);
}
}
@@ -599,7 +669,16 @@
*/
srcWriter.println("ClientSerializationStreamWriter toReturn =");
srcWriter.indentln("(ClientSerializationStreamWriter) super.createStreamWriter();");
- srcWriter.println("toReturn.addFlags(ClientSerializationStreamWriter.FLAG_ELIDE_TYPE_NAMES);");
+ if (elideTypeNames) {
+ srcWriter.println("toReturn.addFlags(ClientSerializationStreamWriter."
+ + "FLAG_ELIDE_TYPE_NAMES);");
+ }
+ srcWriter.println("if (getRpcToken() != null) {");
+ srcWriter.indent();
+ srcWriter.println("toReturn.addFlags(ClientSerializationStreamWriter."
+ + "FLAG_RPC_TOKEN_INCLUDED);");
+ srcWriter.outdent();
+ srcWriter.println("}");
srcWriter.println("return toReturn;");
srcWriter.outdent();
srcWriter.println("}");
@@ -814,6 +893,8 @@
SerializationStreamWriter.class.getCanonicalName(),
GWT.class.getCanonicalName(), ResponseReader.class.getCanonicalName(),
SerializationException.class.getCanonicalName(),
+ RpcToken.class.getCanonicalName(),
+ RpcTokenException.class.getCanonicalName(),
Impl.class.getCanonicalName(),
RpcStatsContext.class.getCanonicalName()};
for (String imp : imports) {
diff --git a/user/src/com/google/gwt/user/server/rpc/HybridServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/HybridServiceServlet.java
index 39e7b22..9d7c619 100644
--- a/user/src/com/google/gwt/user/server/rpc/HybridServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/HybridServiceServlet.java
@@ -18,6 +18,7 @@
import com.google.gwt.rpc.server.ClientOracle;
import com.google.gwt.rpc.server.RpcServlet;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.RpcTokenException;
import com.google.gwt.user.client.rpc.SerializationException;
import java.io.IOException;
@@ -132,6 +133,10 @@
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailure(null, ex);
+ } catch (RpcTokenException tokenException) {
+ log("An RpcTokenException was thrown while processing this call.",
+ tokenException);
+ return RPC.encodeResponseForFailure(null, tokenException);
}
}
diff --git a/user/src/com/google/gwt/user/server/rpc/RPC.java b/user/src/com/google/gwt/user/server/rpc/RPC.java
index ae49bab..e0a292b 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPC.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPC.java
@@ -17,6 +17,7 @@
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RpcToken;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.impl.AbstractSerializationStream;
import com.google.gwt.user.server.rpc.impl.LegacySerializationPolicy;
@@ -235,6 +236,12 @@
classLoader, serializationPolicyProvider);
streamReader.prepareToRead(encodedRequest);
+ RpcToken rpcToken = null;
+ if (streamReader.hasFlags(AbstractSerializationStream.FLAG_RPC_TOKEN_INCLUDED)) {
+ // Read the RPC token
+ rpcToken = (RpcToken) streamReader.deserializeValue(RpcToken.class);
+ }
+
// Read the name of the RemoteService interface
String serviceIntfName = maybeDeobfuscate(streamReader,
streamReader.readString());
@@ -296,8 +303,8 @@
parameterValues[i] = streamReader.deserializeValue(parameterTypes[i]);
}
- return new RPCRequest(method, parameterValues, serializationPolicy,
- streamReader.getFlags());
+ return new RPCRequest(method, parameterValues, rpcToken,
+ serializationPolicy, streamReader.getFlags());
} catch (NoSuchMethodException e) {
throw new IncompatibleRemoteServiceException(
diff --git a/user/src/com/google/gwt/user/server/rpc/RPCRequest.java b/user/src/com/google/gwt/user/server/rpc/RPCRequest.java
index 8f14fdd..d673500 100644
--- a/user/src/com/google/gwt/user/server/rpc/RPCRequest.java
+++ b/user/src/com/google/gwt/user/server/rpc/RPCRequest.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.user.server.rpc;
+import com.google.gwt.user.client.rpc.RpcToken;
+
import java.lang.reflect.Method;
/**
@@ -37,6 +39,11 @@
* The parameters for this request.
*/
private final Object[] parameters;
+
+ /**
+ * The RPC token for this request.
+ */
+ private final RpcToken rpcToken;
/**
* {@link SerializationPolicy} used for decoding this request and for encoding
@@ -48,9 +55,10 @@
* Construct an RPCRequest.
*/
public RPCRequest(Method method, Object[] parameters,
- SerializationPolicy serializationPolicy, int flags) {
+ RpcToken rpcToken, SerializationPolicy serializationPolicy, int flags) {
this.method = method;
this.parameters = parameters;
+ this.rpcToken = rpcToken;
this.serializationPolicy = serializationPolicy;
this.flags = flags;
}
@@ -74,6 +82,13 @@
}
/**
+ * Get the request's RPC token.
+ */
+ public RpcToken getRpcToken() {
+ return rpcToken;
+ }
+
+ /**
* Returns the {@link SerializationPolicy} used to decode this request. This
* is also the <code>SerializationPolicy</code> that should be used to encode
* responses.
diff --git a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
index 0605721..9bddd66 100644
--- a/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
+++ b/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java
@@ -16,6 +16,7 @@
package com.google.gwt.user.server.rpc;
import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
+import com.google.gwt.user.client.rpc.RpcTokenException;
import com.google.gwt.user.client.rpc.SerializationException;
import java.io.IOException;
@@ -212,6 +213,10 @@
"An IncompatibleRemoteServiceException was thrown while processing this call.",
ex);
return RPC.encodeResponseForFailure(null, ex);
+ } catch (RpcTokenException tokenException) {
+ log("An RpcTokenException was thrown while processing this call.",
+ tokenException);
+ return RPC.encodeResponseForFailure(null, tokenException);
}
}
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 4b642e1..3b5da63 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
@@ -437,9 +437,14 @@
+ SERIALIZATION_STREAM_VERSION + " from client, got " + getVersion()
+ ".");
}
+
+ // Check the flags
+ if (!areFlagsValid()) {
+ throw new IncompatibleRemoteServiceException("Got an unknown flag from "
+ + "client: " + getFlags());
+ }
// Read the type name table
- //
deserializeStringTable();
// Write the serialization policy info
diff --git a/user/test/com/google/gwt/user/RPCSuite.gwt.xml b/user/test/com/google/gwt/user/RPCSuite.gwt.xml
index 2360e11..0a1658c 100644
--- a/user/test/com/google/gwt/user/RPCSuite.gwt.xml
+++ b/user/test/com/google/gwt/user/RPCSuite.gwt.xml
@@ -33,6 +33,10 @@
class='com.google.gwt.user.server.rpc.ValueTypesTestServiceImpl' />
<servlet path='/servlettest'
class='com.google.gwt.user.server.rpc.RemoteServiceServletTestServiceImpl' />
+ <servlet path='/rpctokentest'
+ class='com.google.gwt.user.server.rpc.RpcTokenServiceImpl' />
+ <servlet path='/rpctokentest-annotation'
+ class='com.google.gwt.user.server.rpc.AnnotatedRpcTokenTestServiceImpl' />
<servlet path='/unicodeEscape'
class='com.google.gwt.user.server.rpc.UnicodeEscapingServiceImpl' />
diff --git a/user/test/com/google/gwt/user/RPCSuite.java b/user/test/com/google/gwt/user/RPCSuite.java
index 9821dc0..8f0182a 100644
--- a/user/test/com/google/gwt/user/RPCSuite.java
+++ b/user/test/com/google/gwt/user/RPCSuite.java
@@ -38,6 +38,7 @@
import com.google.gwt.user.client.rpc.InheritanceTestWithTypeObfuscation;
import com.google.gwt.user.client.rpc.ObjectGraphTest;
import com.google.gwt.user.client.rpc.ObjectGraphTestWithTypeObfuscation;
+import com.google.gwt.user.client.rpc.RpcTokenTest;
import com.google.gwt.user.client.rpc.RunTimeSerializationErrorsTest;
import com.google.gwt.user.client.rpc.UnicodeEscapingTest;
import com.google.gwt.user.client.rpc.UnicodeEscapingTestWithTypeObfuscation;
@@ -97,6 +98,7 @@
suite.addTestSuite(CustomFieldSerializerTest.class);
suite.addTestSuite(ObjectGraphTest.class);
suite.addTestSuite(com.google.gwt.user.client.rpc.RemoteServiceServletTest.class);
+ suite.addTestSuite(RpcTokenTest.class);
suite.addTestSuite(UnicodeEscapingTest.class);
suite.addTestSuite(RunTimeSerializationErrorsTest.class);
diff --git a/user/test/com/google/gwt/user/client/rpc/AnnotatedRpcTokenTestService.java b/user/test/com/google/gwt/user/client/rpc/AnnotatedRpcTokenTestService.java
new file mode 100644
index 0000000..e2c8d02
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/AnnotatedRpcTokenTestService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+import com.google.gwt.user.client.rpc.RpcToken.RpcTokenImplementation;
+
+/**
+ * Version of {@link RpcTokenTestService} annotated with
+ * {@link RpcToken.RpcTokenImplementation}.
+ */
+@RpcTokenImplementation(
+ "com.google.gwt.user.client.rpc.RpcTokenTest.AnotherTestRpcToken")
+public interface AnnotatedRpcTokenTestService extends RpcTokenTestService {
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/AnnotatedRpcTokenTestServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/AnnotatedRpcTokenTestServiceAsync.java
new file mode 100644
index 0000000..f5bd750
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/AnnotatedRpcTokenTestServiceAsync.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+/**
+ * Async peer of {@link AnnotatedRpcTokenTestService}.
+ */
+public interface AnnotatedRpcTokenTestServiceAsync {
+
+ void test(AsyncCallback<Void> callback);
+
+ void capitalize(String input, AsyncCallback<String> callback);
+
+ void getRpcTokenFromRequest(AsyncCallback<RpcToken> callback);
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/RpcTokenTest.java b/user/test/com/google/gwt/user/client/rpc/RpcTokenTest.java
new file mode 100644
index 0000000..60ceba2
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/RpcTokenTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+import com.google.gwt.core.client.GWT;
+
+/**
+ * Tests RpcToken functionality.
+ */
+public class RpcTokenTest extends RpcTestBase {
+
+ /**
+ * First RpcToken implementation
+ */
+ public static class TestRpcToken implements RpcToken {
+ String tokenValue;
+ }
+
+ /**
+ * Second RpcToken implementation
+ */
+ public static class AnotherTestRpcToken implements RpcToken {
+ int token;
+ }
+
+ protected static RpcTokenTestServiceAsync getAsyncService() {
+ RpcTokenTestServiceAsync service =
+ (RpcTokenTestServiceAsync) GWT.create(RpcTokenTestService.class);
+
+ ((ServiceDefTarget) service).setServiceEntryPoint(GWT.getModuleBaseURL()
+ + "rpctokentest");
+
+ return service;
+ }
+
+ protected static AnnotatedRpcTokenTestServiceAsync getAnnotatedAsyncService() {
+ AnnotatedRpcTokenTestServiceAsync service =
+ (AnnotatedRpcTokenTestServiceAsync) GWT.create(AnnotatedRpcTokenTestService.class);
+
+ ((ServiceDefTarget) service).setServiceEntryPoint(GWT.getModuleBaseURL()
+ + "rpctokentest-annotation");
+
+ return service;
+ }
+
+ public void testRpcTokenMissing() {
+ RpcTokenTestServiceAsync service = getAsyncService();
+
+ delayTestFinishForRpc();
+
+ service.getRpcTokenFromRequest(new AsyncCallback<RpcToken>() {
+
+ public void onFailure(Throwable caught) {
+ TestSetValidator.rethrowException(caught);
+ }
+
+ public void onSuccess(RpcToken token) {
+ assertNull(token);
+ finishTest();
+ }
+ });
+ }
+
+ public void testRpcTokenPresent() {
+ RpcTokenTestServiceAsync service = getAsyncService();
+
+ final TestRpcToken token = new TestRpcToken();
+ token.tokenValue = "Drink kumys!";
+ ((HasRpcToken) service).setRpcToken(token);
+
+ delayTestFinishForRpc();
+
+ service.getRpcTokenFromRequest(new AsyncCallback<RpcToken>() {
+
+ public void onFailure(Throwable caught) {
+ TestSetValidator.rethrowException(caught);
+ }
+
+ public void onSuccess(RpcToken rpcToken) {
+ assertNotNull(rpcToken);
+ assertTrue(rpcToken instanceof TestRpcToken);
+ assertEquals(token.tokenValue, ((TestRpcToken) rpcToken).tokenValue);
+ finishTest();
+ }
+ });
+ }
+
+ public void testRpcTokenExceptionHandler() {
+ RpcTokenTestServiceAsync service = getAsyncService();
+ ((ServiceDefTarget) service).setServiceEntryPoint(GWT.getModuleBaseURL()
+ + "rpctokentest?throw=true");
+ ((HasRpcToken) service).setRpcTokenExceptionHandler(new RpcTokenExceptionHandler() {
+ public void onRpcTokenException(RpcTokenException exception) {
+ assertNotNull(exception);
+ finishTest();
+ }
+ });
+
+ delayTestFinishForRpc();
+
+ service.getRpcTokenFromRequest(new AsyncCallback<RpcToken>() {
+
+ public void onFailure(Throwable caught) {
+ TestSetValidator.rethrowException(caught);
+ }
+
+ public void onSuccess(RpcToken rpcToken) {
+ fail("Should've called RpcTokenExceptionHandler");
+ }
+ });
+ }
+
+ public void testRpcTokenAnnotationDifferentFromActualType() {
+ AnnotatedRpcTokenTestServiceAsync service = getAnnotatedAsyncService();
+
+ // service is annotated to use AnotherTestRpcToken and not TestRpcToken,
+ // generated proxy should catch this error
+ final TestRpcToken token = new TestRpcToken();
+ token.tokenValue = "Drink kumys!";
+ try {
+ ((HasRpcToken) service).setRpcToken(token);
+ fail("Should have thrown an RpcTokenException");
+ } catch (RpcTokenException e) {
+ // Expected
+ }
+ }
+
+ public void testRpcTokenAnnotation() {
+ AnnotatedRpcTokenTestServiceAsync service = getAnnotatedAsyncService();
+
+ final AnotherTestRpcToken token = new AnotherTestRpcToken();
+ token.token = 1337;
+ ((HasRpcToken) service).setRpcToken(token);
+
+ service.getRpcTokenFromRequest(new AsyncCallback<RpcToken>() {
+
+ public void onSuccess(RpcToken result) {
+ assertNotNull(result);
+ assertTrue(result instanceof AnotherTestRpcToken);
+ assertEquals(token.token, ((AnotherTestRpcToken) result).token);
+ finishTest();
+ }
+
+ public void onFailure(Throwable caught) {
+ TestSetValidator.rethrowException(caught);
+ }
+ });
+ }
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/RpcTokenTestService.java b/user/test/com/google/gwt/user/client/rpc/RpcTokenTestService.java
new file mode 100644
index 0000000..dd7e017
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/RpcTokenTestService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+/**
+ * Remote service for testing RPC tokens support.
+ */
+public interface RpcTokenTestService extends RemoteService {
+
+ void test();
+
+ String capitalize(String input);
+
+ RpcToken getRpcTokenFromRequest();
+}
diff --git a/user/test/com/google/gwt/user/client/rpc/RpcTokenTestServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/RpcTokenTestServiceAsync.java
new file mode 100644
index 0000000..f9e7e75
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/rpc/RpcTokenTestServiceAsync.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.rpc;
+
+/**
+ * Async peer of {@link RpcTokenTestService}.
+ */
+public interface RpcTokenTestServiceAsync {
+
+ void test(AsyncCallback<Void> callback);
+
+ void capitalize(String input, AsyncCallback<String> callback);
+
+ void getRpcTokenFromRequest(AsyncCallback<RpcToken> callback);
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/AnnotatedRpcTokenTestServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/AnnotatedRpcTokenTestServiceImpl.java
new file mode 100644
index 0000000..1c18751
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/AnnotatedRpcTokenTestServiceImpl.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.AnnotatedRpcTokenTestService;
+
+/**
+ * Remote service for testing RPC tokens support annotated with
+ * {@link RpcTokenImplementation.Class}.
+ */
+public class AnnotatedRpcTokenTestServiceImpl extends RpcTokenServiceImpl
+ implements AnnotatedRpcTokenTestService {
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCRequestTest.java b/user/test/com/google/gwt/user/server/rpc/RPCRequestTest.java
index 5aa9aa9..b06b165 100644
--- a/user/test/com/google/gwt/user/server/rpc/RPCRequestTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RPCRequestTest.java
@@ -43,7 +43,7 @@
// Test simple case
Object params[] = new Object[] {"abcdefg", 1234};
- RPCRequest request = new RPCRequest(method, params, null, 0);
+ RPCRequest request = new RPCRequest(method, params, null, null, 0);
String strRequest = request.toString();
assertEquals("com.google.gwt.user.server.rpc.RPCRequestTest$"
+ "MockRequestImplementation.doSomething(\"abcdefg\", 1234)",
@@ -51,7 +51,7 @@
// Test case with a string that needs escaping
Object params2[] = new Object[] {"ab\"cde\"fg", 1234};
- RPCRequest request2 = new RPCRequest(method, params2, null, 0);
+ RPCRequest request2 = new RPCRequest(method, params2, null, null, 0);
String strRequest2 = request2.toString();
assertEquals("com.google.gwt.user.server.rpc.RPCRequestTest$"
+ "MockRequestImplementation.doSomething(\"ab\\\"cde\\\"fg\", 1234)",
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 bc63ab6..6bc88c2 100644
--- a/user/test/com/google/gwt/user/server/rpc/RPCTest.java
+++ b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
@@ -20,6 +20,7 @@
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.RpcToken;
import com.google.gwt.user.client.rpc.SerializableException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.impl.AbstractSerializationStream;
@@ -233,6 +234,24 @@
"1" + RPC_SEPARATOR_CHAR + // param count
"5" + RPC_SEPARATOR_CHAR + // 'J' == long param type
"P7cuph2VDIQ" + RPC_SEPARATOR_CHAR; // long in base-64 encoding
+
+ private static final String VALID_V6_ENCODED_REQUEST_WITH_INVALID_FLAGS = ""
+ + AbstractSerializationStream.SERIALIZATION_STREAM_VERSION +
+ RPC_SEPARATOR_CHAR + // version
+ "123" + RPC_SEPARATOR_CHAR + // flags
+ "5" + RPC_SEPARATOR_CHAR + // string table count
+ "moduleBaseUrl" + RPC_SEPARATOR_CHAR + // string table entry #1
+ "whitelistHashCode" + RPC_SEPARATOR_CHAR + // string table entry #2
+ D.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #3
+ "echo" + RPC_SEPARATOR_CHAR + // string table entry #4
+ "J" + RPC_SEPARATOR_CHAR + // string table entry #5
+ "1" + RPC_SEPARATOR_CHAR + // moduleBaseUrl
+ "2" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+ "3" + RPC_SEPARATOR_CHAR + // interface name
+ "4" + RPC_SEPARATOR_CHAR + // method name
+ "1" + RPC_SEPARATOR_CHAR + // param count
+ "5" + RPC_SEPARATOR_CHAR + // 'J' == long param type
+ "P7cuph2VDIQ" + RPC_SEPARATOR_CHAR; // long in base-64 encoding
/**
* Tests that out-of-range or other illegal integer values generated
@@ -344,6 +363,15 @@
// Expected
}
}
+
+ public void testDecodeInvalidFlags() {
+ try {
+ RPC.decodeRequest(VALID_V6_ENCODED_REQUEST_WITH_INVALID_FLAGS);
+ fail("Should have thrown an IncompatibleRemoteServiceException");
+ } catch (IncompatibleRemoteServiceException e) {
+ // Expected
+ }
+ }
/**
* Tests for method {@link RPC#decodeRequest(String)}.
@@ -441,6 +469,79 @@
}
}
+ private static class TestRpcToken implements RpcToken {
+ String tokenValue;
+ public TestRpcToken() { }
+ }
+
+ private static final String VALID_V6_ENCODED_REQUEST_WITH_RPC_TOKEN = "" +
+ AbstractSerializationStream.SERIALIZATION_STREAM_VERSION +
+ RPC_SEPARATOR_CHAR + // version
+ AbstractSerializationStream.FLAG_RPC_TOKEN_INCLUDED + // flags
+ RPC_SEPARATOR_CHAR +
+ "7" + RPC_SEPARATOR_CHAR + // string table count
+ "moduleBaseUrl" + RPC_SEPARATOR_CHAR + // string table entry #1
+ "whitelistHashCode" + RPC_SEPARATOR_CHAR + // string table entry #2
+ TestRpcToken.class.getName() + "/3856085925" + // string table entry #3
+ RPC_SEPARATOR_CHAR +
+ "RPC_TOKEN_VALUE" + RPC_SEPARATOR_CHAR + // string table entry #4
+ D.class.getName() + // string table entry #5
+ RPC_SEPARATOR_CHAR +
+ "echo" + RPC_SEPARATOR_CHAR + // string table entry #6
+ "J" + RPC_SEPARATOR_CHAR + // string table entry #7
+ "1" + RPC_SEPARATOR_CHAR + // moduleBaseUrl
+ "2" + RPC_SEPARATOR_CHAR + // whitelist hashcode
+ "3" + RPC_SEPARATOR_CHAR + // RPC token class
+ "4" + RPC_SEPARATOR_CHAR + // RPC token value
+ "5" + RPC_SEPARATOR_CHAR + // interface name
+ "6" + RPC_SEPARATOR_CHAR + // method name
+ "1" + RPC_SEPARATOR_CHAR + // param count
+ "7" + RPC_SEPARATOR_CHAR + // 'J' == long param type
+ "P7cuph2VDIQ" + RPC_SEPARATOR_CHAR; // long in base-64 encoding
+
+ public void testDecodeRpcToken() {
+ class TestPolicy extends SerializationPolicy {
+ @Override
+ public boolean shouldDeserializeFields(Class<?> clazz) {
+ return TestRpcToken.class.equals(clazz);
+ }
+
+ @Override
+ public boolean shouldSerializeFields(Class<?> clazz) {
+ return TestRpcToken.class.equals(clazz);
+ }
+
+ @Override
+ public void validateDeserialize(Class<?> clazz)
+ throws SerializationException {
+ }
+
+ @Override
+ public void validateSerialize(Class<?> clazz)
+ throws SerializationException {
+ }
+
+ @Override
+ public Set<String> getClientFieldNamesForEnhancedClass(Class<?> clazz) {
+ return null;
+ }
+ }
+ class TestPolicyProvider implements SerializationPolicyProvider {
+ public SerializationPolicy getSerializationPolicy(
+ String moduleBaseURL, String serializationPolicyStrongName) {
+ return new TestPolicy();
+ }
+ }
+ RPCRequest requestWithoutToken = RPC.decodeRequest(
+ VALID_V6_ENCODED_REQUEST);
+ assertNull(requestWithoutToken.getRpcToken());
+ RPCRequest requestWithToken = RPC.decodeRequest(
+ VALID_V6_ENCODED_REQUEST_WITH_RPC_TOKEN, null, new TestPolicyProvider());
+ assertNotNull(requestWithToken.getRpcToken());
+ TestRpcToken token = (TestRpcToken) requestWithToken.getRpcToken();
+ assertEquals("RPC_TOKEN_VALUE", token.tokenValue);
+ }
+
public void testDecodeV5Long() {
try {
RPCRequest request = RPC.decodeRequest(VALID_V5_ENCODED_REQUEST,
diff --git a/user/test/com/google/gwt/user/server/rpc/RpcTokenAwareRemoteService.java b/user/test/com/google/gwt/user/server/rpc/RpcTokenAwareRemoteService.java
new file mode 100644
index 0000000..8a53c84
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/RpcTokenAwareRemoteService.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.RpcToken;
+import com.google.gwt.user.client.rpc.RpcTokenException;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * {@link RemoteServiceServlet} implementation for RPC tokens support tests.
+ */
+public class RpcTokenAwareRemoteService extends RemoteServiceServlet {
+
+ public static final String TOKEN = "TOKEN";
+
+ @Override
+ protected void onAfterRequestDeserialized(RPCRequest rpcRequest) {
+ HttpServletRequest req = getThreadLocalRequest();
+
+ if (req.getParameter("throw") != null) {
+ throw new RpcTokenException("This is OK. Testing RpcTokenException handler.");
+ } else {
+ RpcToken token = rpcRequest.getRpcToken();
+ req.setAttribute(TOKEN, token);
+ }
+ }
+}
diff --git a/user/test/com/google/gwt/user/server/rpc/RpcTokenServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/RpcTokenServiceImpl.java
new file mode 100644
index 0000000..d755717
--- /dev/null
+++ b/user/test/com/google/gwt/user/server/rpc/RpcTokenServiceImpl.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.server.rpc;
+
+import com.google.gwt.user.client.rpc.RpcToken;
+import com.google.gwt.user.client.rpc.RpcTokenTestService;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Remote service for testing RPC tokens support.
+ */
+public class RpcTokenServiceImpl extends RpcTokenAwareRemoteService
+ implements RpcTokenTestService {
+
+ public void test() { }
+
+ public String capitalize(String input) {
+ return input.toUpperCase();
+ }
+
+ public RpcToken getRpcTokenFromRequest() {
+ HttpServletRequest req = getThreadLocalRequest();
+ RpcToken token = (RpcToken) req.getAttribute(RpcTokenAwareRemoteService.TOKEN);
+ return token;
+ }
+}