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;
+}