| /* | 
 |  * 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 "CheckVersionsMessage.h" | 
 | #include "ProtocolVersionMessage.h" | 
 | #include "ChooseTransportMessage.h" | 
 | #include "SwitchTransportMessage.h" | 
 | #include "FatalErrorMessage.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) { | 
 |   if (!port) { | 
 |     port = 9997; | 
 |   } | 
 |   Debug::log(Debug::Info) | 
 |       << "Initiating GWT Development Mode connection to host " << host | 
 |       << ", port " << port << Debug::flush; | 
 |   return sock.connect(host, port); | 
 | } | 
 |  | 
 | bool HostChannel::init(SessionHandler* handler, int minProtoVers, | 
 |     int maxProtoVers, const std::string& hostedHtmlVers) { | 
 |   this->handler = handler; | 
 |   Debug::log(Debug::Debugging) | 
 |       << "  negotiating versions - we support protocol " << minProtoVers | 
 |       << " through " << maxProtoVers << ", hostedHtmlVersion=" << hostedHtmlVers | 
 |       << Debug::flush; | 
 |   // TODO(jat): support transport selection | 
 |   CheckVersionsMessage::send(*this, minProtoVers, maxProtoVers, hostedHtmlVers); | 
 |   flush(); | 
 |   char type; | 
 |   if (!readByte(type)) { | 
 |     handler->fatalError(*this, "Failed to receive message type"); | 
 |     Debug::log(Debug::Error) << "Failed to receive message type" << Debug::flush; | 
 |     disconnectFromHost(); | 
 |     return false; | 
 |   } | 
 |   switch (type) { | 
 |     case MESSAGE_TYPE_PROTOCOL_VERSION: | 
 |     { | 
 |       scoped_ptr<ProtocolVersionMessage> imsg(ProtocolVersionMessage | 
 |           ::receive(*this)); | 
 |       if (!imsg.get()) { | 
 |         Debug::log(Debug::Error) << "Failed to receive protocol version message" | 
 |             << Debug::flush; | 
 |         return false; | 
 |       } | 
 |       // TODO(jat): save selected protocol version when we support a range | 
 |       break; | 
 |     } | 
 |     case MESSAGE_TYPE_FATAL_ERROR: | 
 |     { | 
 |       scoped_ptr<FatalErrorMessage> imsg(FatalErrorMessage::receive(*this)); | 
 |       if (!imsg.get()) { | 
 |         Debug::log(Debug::Error) << "Failed to receive fatal error message" | 
 |             << Debug::flush; | 
 |         return false; | 
 |       } | 
 |       handler->fatalError(*this, imsg.get()->getError()); | 
 |       return false; | 
 |     } | 
 |     default: | 
 |       return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | 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)) { | 
 |       if (isConnected()) { | 
 |         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; | 
 | } |