Initial checkin of OOPHM plugins into trunk. Testing of non-XPCOM plugins
is still required, and more platforms need to be built.
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5868 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/plugins/common/HostChannel.cpp b/plugins/common/HostChannel.cpp
new file mode 100644
index 0000000..896667e
--- /dev/null
+++ b/plugins/common/HostChannel.cpp
@@ -0,0 +1,393 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cerrno>
+
+#include "Debug.h"
+
+#ifdef _WINDOWS
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#include "Platform.h"
+#include "ByteOrder.h"
+
+#include "FreeValueMessage.h"
+#include "HostChannel.h"
+#include "LoadJsniMessage.h"
+#include "InvokeMessage.h"
+#include "InvokeSpecialMessage.h"
+#include "QuitMessage.h"
+#include "ReturnMessage.h"
+#include "Value.h"
+#include "scoped_ptr/scoped_ptr.h"
+
+using namespace std;
+
+ByteOrder HostChannel::byteOrder;
+
+bool HostChannel::connectToHost(const char* host, unsigned port) {
+ Debug::log(Debug::Info) << "Initiating GWT hosted mode connection to host "
+ << host << ", port " << port << Debug::flush;
+ if (!port) {
+ port = 9997;
+ }
+ if (!whitelist.isAllowed(host, port)) {
+ Debug::log(Debug::Error) << "Permission to connect to " << host << ":" << port
+ << " denied" << Debug::flush;
+ return false;
+ }
+ return sock.connect(host, port);
+}
+
+bool HostChannel::disconnectFromHost() {
+ Debug::log(Debug::Debugging) << "Disconnecting channel" << Debug::flush;
+ if (!isConnected()) {
+ Debug::log(Debug::Error) << "Disconnecting already disconnected channel" << Debug::flush;
+ return false;
+ }
+ QuitMessage::send(*this);
+ flush();
+ sock.disconnect();
+ return true;
+}
+
+bool HostChannel::readInt(int32_t& data) {
+ int32_t d;
+ if (!readBytes(&d, sizeof(d))) return false;
+ data = ntohl(d);
+ return true;
+}
+
+bool HostChannel::sendInt(int32_t data) {
+ uint32_t d = htonl(data);
+ return sendBytes(&d, sizeof(d));
+}
+
+bool HostChannel::readShort(short& data) {
+ int16_t d;
+ if (!readBytes(&d, sizeof(d))) return false;
+ data = ntohs(d);
+ return true;
+}
+
+bool HostChannel::sendShort(const short data) {
+ uint16_t d = htons(data);
+ return sendBytes(&d, sizeof(d));
+}
+
+bool HostChannel::readLong(int64_t& data) {
+ // network is big-endian
+ int32_t d[2];
+ if (!readInt(d[0])) return false;
+ if (!readInt(d[1])) return false;
+ data = (static_cast<int64_t>(d[0]) << 32) | ntohl(d[1]);
+ return true;
+}
+
+bool HostChannel::sendLong(const int64_t data) {
+ if (!sendInt(static_cast<int32_t>(data >> 32))) {
+ return false;
+ }
+ return sendInt(static_cast<int32_t>(data));
+}
+
+bool HostChannel::readFloat(float& data) {
+ char bytes[sizeof(data)];
+ if (!readBytes(bytes, sizeof(bytes))) {
+ return false;
+ }
+ data = byteOrder.floatFromBytes(bytes);
+ return true;
+}
+
+bool HostChannel::sendFloat(const float data) {
+ char bytes[sizeof(data)];
+ byteOrder.bytesFromFloat(data, bytes);
+ return sendBytes(bytes, sizeof(bytes));
+}
+
+bool HostChannel::readDouble(double& data) {
+ char bytes[sizeof(data)];
+ if (!readBytes(bytes, sizeof(bytes))) {
+ return false;
+ }
+ data = byteOrder.doubleFromBytes(bytes);
+ return true;
+}
+
+bool HostChannel::sendDouble(const double data) {
+ char bytes[sizeof(data)];
+ byteOrder.bytesFromDouble(data, bytes);
+ return sendBytes(bytes, sizeof(bytes));
+}
+
+bool HostChannel::readStringLength(uint32_t& data) {
+ int32_t val;
+ if (!readInt(val)) return false;
+ // TODO: assert positive?
+ data = val;
+ return true;
+}
+
+bool HostChannel::readStringBytes(char* data, const uint32_t len) {
+ return readBytes(data, len);
+}
+
+bool HostChannel::readString(std::string& strRef) {
+ uint32_t len;
+ if (!readStringLength(len)) {
+ Debug::log(Debug::Error) << "readString: failed to read length"
+ << Debug::flush;
+ return false;
+ }
+ // Allocating variable-length arrays on the stack is a GCC feature,
+ // and is vulnerable to stack overflow attacks, so we allocate on the heap.
+ scoped_array<char> buf(new char[len]);
+ if (!readStringBytes(buf.get(), len)) {
+ Debug::log(Debug::Error) << "readString: failed to read " << len
+ << " bytes" << Debug::flush;
+ return false;
+ }
+ strRef.assign(buf.get(), len);
+ return true;
+}
+
+static inline double operator-(const struct timeval& end,
+ const struct timeval& begin) {
+ double us = end.tv_sec * 1000000.0 + end.tv_usec - begin.tv_sec * 1000000.0
+ - begin.tv_usec;
+ return us;
+}
+
+ReturnMessage* HostChannel::reactToMessages(SessionHandler* handler, bool expectReturn) {
+ char type;
+ while (true) {
+ flush();
+ Debug::log(Debug::Spam) << "Waiting for response, flushed output" << Debug::flush;
+ if (!readByte(type)) {
+ Debug::log(Debug::Error) << "Failed to receive message type" << Debug::flush;
+ return 0;
+ }
+ switch (type) {
+ case MESSAGE_TYPE_INVOKE:
+ {
+ scoped_ptr<InvokeMessage> imsg(InvokeMessage::receive(*this));
+ if (!imsg.get()) {
+ Debug::log(Debug::Error) << "Failed to receive invoke message" << Debug::flush;
+ return 0;
+ }
+ Value returnValue;
+ bool exception = handler->invoke(*this, imsg->getThis(), imsg->getMethodName(),
+ imsg->getNumArgs(), imsg->getArgs(), &returnValue);
+ handler->sendFreeValues(*this);
+ ReturnMessage::send(*this, exception, returnValue);
+ }
+ break;
+ case MESSAGE_TYPE_INVOKESPECIAL:
+ {
+ // scottb: I think this is never used; I think server never sends invokeSpecial
+ scoped_ptr<InvokeSpecialMessage> imsg(InvokeSpecialMessage::receive(*this));
+ if (!imsg.get()) {
+ Debug::log(Debug::Error) << "Failed to receive invoke special message" << Debug::flush;
+ return 0;
+ }
+ Value returnValue;
+ bool exception = handler->invokeSpecial(*this, imsg->getDispatchId(),
+ imsg->getNumArgs(), imsg->getArgs(), &returnValue);
+ handler->sendFreeValues(*this);
+ ReturnMessage::send(*this, exception, returnValue);
+ }
+ break;
+ case MESSAGE_TYPE_FREEVALUE:
+ {
+ scoped_ptr<FreeValueMessage> freeMsg(FreeValueMessage::receive(*this));
+ if (!freeMsg.get()) {
+ Debug::log(Debug::Error) << "Failed to receive free value message" << Debug::flush;
+ return 0;
+ }
+ handler->freeValue(*this, freeMsg->getIdCount(), freeMsg->getIds());
+ }
+ // do not send a response
+ break;
+ case MESSAGE_TYPE_LOADJSNI:
+ {
+ scoped_ptr<LoadJsniMessage> loadMsg(LoadJsniMessage::receive(*this));
+ if (!loadMsg.get()) {
+ Debug::log(Debug::Error) << "Failed to receive load JSNI message" << Debug::flush;
+ return 0;
+ }
+ handler->loadJsni(*this, loadMsg->getJs());
+ }
+ // do not send a response
+ break;
+ case MESSAGE_TYPE_RETURN:
+ if (!expectReturn) {
+ Debug::log(Debug::Error) << "Received unexpected RETURN" << Debug::flush;
+ }
+ return ReturnMessage::receive(*this);
+ case MESSAGE_TYPE_QUIT:
+ if (expectReturn) {
+ Debug::log(Debug::Error) << "Received QUIT while waiting for return" << Debug::flush;
+ }
+ disconnectFromHost();
+ return 0;
+ default:
+ // TODO(jat): error handling
+ Debug::log(Debug::Error) << "Unexpected message type " << type
+ << ", expectReturn=" << expectReturn << Debug::flush;
+ disconnectFromHost();
+ return 0;
+ }
+ }
+}
+
+bool HostChannel::readValue(Value& valueRef) {
+ char typeBuf;
+ if (!readByte(typeBuf)) return false;
+ Value::ValueType type = Value::ValueType(typeBuf);
+ switch (type) {
+ case Value::NULL_TYPE:
+ valueRef.setNull();
+ return true;
+ case Value::UNDEFINED:
+ valueRef.setUndefined();
+ return true;
+ case Value::BOOLEAN:
+ {
+ char val;
+ if (!readByte(val)) return false;
+ valueRef.setBoolean(val != 0);
+ }
+ return true;
+ case Value::BYTE:
+ {
+ char val;
+ if (!readByte(val)) return false;
+ valueRef.setByte(val);
+ }
+ return true;
+ case Value::CHAR:
+ {
+ short val;
+ if (!readShort(val)) return false;
+ valueRef.setChar(val);
+ }
+ return true;
+ case Value::SHORT:
+ {
+ short val;
+ if (!readShort(val)) return false;
+ valueRef.setShort(val);
+ }
+ return true;
+ case Value::STRING:
+ {
+ std::string val;
+ if (!readString(val)) return false;
+ valueRef.setString(val);
+ }
+ return true;
+ case Value::INT:
+ {
+ int val;
+ if (!readInt(val)) return false;
+ valueRef.setInt(val);
+ }
+ return true;
+ case Value::LONG:
+ {
+ int64_t val;
+ if (!readLong(val)) return false;
+ valueRef.setLong(val);
+ }
+ return true;
+ case Value::DOUBLE:
+ {
+ double val;
+ if (!readDouble(val)) return false;
+ valueRef.setDouble(val);
+ }
+ return true;
+ case Value::JAVA_OBJECT:
+ {
+ int objId;
+ if (!readInt(objId)) return false;
+ valueRef.setJavaObject(objId);
+ }
+ return true;
+ case Value::JS_OBJECT:
+ {
+ int val;
+ if (!readInt(val)) return false;
+ valueRef.setJsObjectId(val);
+ }
+ return true;
+ default:
+ Debug::log(Debug::Error) << "Unhandled value type sent from server: " << type << Debug::flush;
+ break;
+ }
+ return false;
+}
+
+bool HostChannel::sendValue(const Value& value) {
+ Value::ValueType type = value.getType();
+ if (!sendByte(type)) return false;
+ switch (type) {
+ case Value::NULL_TYPE:
+ case Value::UNDEFINED:
+ // Null and Undefined only have the tag byte, no data
+ return true;
+ case Value::BOOLEAN:
+ return sendByte(value.getBoolean() ? 1 : 0);
+ case Value::BYTE:
+ return sendByte(value.getByte());
+ case Value::CHAR:
+ return sendShort(short(value.getChar()));
+ case Value::SHORT:
+ return sendShort(value.getShort());
+ case Value::INT:
+ return sendInt(value.getInt());
+ case Value::LONG:
+ return sendLong(value.getLong());
+ case Value::STRING:
+ return sendString(value.getString());
+ case Value::DOUBLE:
+ return sendDouble(value.getDouble());
+ case Value::FLOAT:
+ return sendFloat(value.getFloat());
+ case Value::JS_OBJECT:
+ return sendInt(value.getJsObjectId());
+ case Value::JAVA_OBJECT:
+ return sendInt(value.getJavaObjectId());
+ default:
+ Debug::log(Debug::Error) << "Unhandled value type sent to server: " << type << Debug::flush;
+ break;
+ }
+ return false;
+}