WebSocket.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. //
  2. // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../Core/Profiler.h"
  24. #include "../IO/BufferQueue.h"
  25. #include "../IO/Log.h"
  26. #include "../Web/Web.h"
  27. #include "../Web/WebSocket.h"
  28. #ifdef EMSCRIPTEN
  29. #include "../DebugNew.h"
  30. // Add code to use an HTML5 style WebSocket here.
  31. #else
  32. #include "../Web/WebInternalConfig.h"
  33. #include <websocketpp/config/asio_no_tls_client.hpp>
  34. #include <websocketpp/client.hpp>
  35. #include <iostream>
  36. #include "../DebugNew.h"
  37. typedef websocketpp::client<websocketpp::config::asio_client> client;
  38. typedef websocketpp::config::asio_client::message_type::ptr message_ptr;
  39. using websocketpp::lib::placeholders::_1;
  40. using websocketpp::lib::placeholders::_2;
  41. using websocketpp::lib::bind;
  42. namespace Atomic
  43. {
  44. struct WebSocketInternalState
  45. {
  46. /// The WebSocket external state.
  47. WebSocket *es;
  48. /// URL.
  49. String url;
  50. /// Error string. Empty if no error.
  51. String error;
  52. /// Connection state.
  53. WebSocketState state;
  54. /// WebSocket client.
  55. client c;
  56. /// WebSocket connection.
  57. client::connection_ptr con;
  58. WebSocketInternalState(WebSocket *es_)
  59. : es(es_)
  60. {
  61. ATOMIC_LOGDEBUG("Create WebSocketInternalState");
  62. }
  63. ~WebSocketInternalState()
  64. {
  65. ATOMIC_LOGDEBUG("Destroy WebSocketInternalState");
  66. }
  67. void OnOpen(websocketpp::connection_hdl hdl)
  68. {
  69. state = WS_OPEN;
  70. ATOMIC_LOGDEBUG("WebSocket CONNECTED to: " + url);
  71. if (es)
  72. {
  73. es->SendEvent("open");
  74. }
  75. }
  76. void OnClose(websocketpp::connection_hdl hdl)
  77. {
  78. state = WS_CLOSED;
  79. ATOMIC_LOGDEBUG("WebSocket DISCONNECTED from: " + url);
  80. if (es)
  81. {
  82. es->SendEvent("close");
  83. }
  84. }
  85. void OnFail(websocketpp::connection_hdl hdl)
  86. {
  87. state = WS_FAIL_TO_CONNECT;
  88. ATOMIC_LOGDEBUG("WebSocket FAILED to connect to: " + url);
  89. if (es)
  90. {
  91. es->SendEvent("fail_to_connect");
  92. }
  93. }
  94. void OnMessage(websocketpp::connection_hdl hdl, message_ptr msg)
  95. {
  96. if (!es)
  97. {
  98. return;
  99. }
  100. VariantMap eventData;
  101. const std::string& payload(msg->get_payload());
  102. SharedPtr<BufferQueue> message(new BufferQueue(es->GetContext()));
  103. message->Write((const void*)payload.data(), (unsigned)payload.size());
  104. eventData.Insert(MakePair(StringHash("type"), Variant(int(msg->get_opcode()))));
  105. eventData.Insert(MakePair(StringHash("message"), Variant(message)));
  106. es->SendEvent("message", eventData);
  107. }
  108. void MakeConnection()
  109. {
  110. websocketpp::lib::error_code ec;
  111. con = c.get_connection(url.CString(), ec);
  112. if (ec)
  113. {
  114. state = WS_INVALID;
  115. error = ec.message().c_str();
  116. ATOMIC_LOGDEBUG("WebSocket error: " + error);
  117. if (es)
  118. {
  119. es->SendEvent("invalid");
  120. }
  121. return;
  122. }
  123. c.connect(con);
  124. }
  125. };
  126. WebSocket::WebSocket(Context* context, const String& url) :
  127. Object(context),
  128. is_(new WebSocketInternalState(this))
  129. {
  130. is_->url = url.Trimmed();
  131. is_->state = WS_CONNECTING;
  132. is_->c.clear_access_channels(websocketpp::log::alevel::all);
  133. is_->c.clear_error_channels(websocketpp::log::elevel::all);
  134. Web* web = GetSubsystem<Web>();
  135. web->setup(*this);
  136. }
  137. void WebSocket::setup(asio::io_service *service)
  138. {
  139. ATOMIC_LOGDEBUG("Create WebSocket");
  140. websocketpp::lib::error_code ec;
  141. is_->c.init_asio(service, ec);
  142. if (ec)
  143. {
  144. is_->state = WS_INVALID;
  145. is_->error = ec.message().c_str();
  146. ATOMIC_LOGDEBUG("WebSocket error: " + is_->error);
  147. SendEvent("invalid");
  148. return;
  149. }
  150. is_->c.set_open_handler(bind(&WebSocketInternalState::OnOpen, is_, ::_1));
  151. is_->c.set_close_handler(bind(&WebSocketInternalState::OnClose, is_, ::_1));
  152. is_->c.set_fail_handler(bind(&WebSocketInternalState::OnFail, is_, ::_1));
  153. is_->c.set_message_handler(bind(&WebSocketInternalState::OnMessage, is_, ::_1, ::_2));
  154. ATOMIC_LOGDEBUG("WebSocket request to URL " + is_->url);
  155. is_->MakeConnection();
  156. }
  157. WebSocket::~WebSocket()
  158. {
  159. ATOMIC_LOGDEBUG("Destroy WebSocket");
  160. std::error_code ec;
  161. is_->es = nullptr;
  162. is_->con->terminate(ec);
  163. is_->c.set_open_handler(nullptr);
  164. is_->c.set_close_handler(nullptr);
  165. is_->c.set_fail_handler(nullptr);
  166. is_->c.set_message_handler(nullptr);
  167. is_->con.reset();
  168. is_.reset();
  169. }
  170. const String& WebSocket::GetURL() const
  171. {
  172. return is_->url;
  173. }
  174. String WebSocket::GetError() const
  175. {
  176. return is_->error;
  177. }
  178. WebSocketState WebSocket::GetState() const
  179. {
  180. return is_->state;
  181. }
  182. void WebSocket::Send(const String& message)
  183. {
  184. is_->c.send(is_->con, message.CString(), message.Length(), websocketpp::frame::opcode::text);
  185. }
  186. void WebSocket::SendBinary(const PODVector<unsigned char>& message)
  187. {
  188. is_->c.send(is_->con, message.Buffer(), message.Size(), websocketpp::frame::opcode::binary);
  189. }
  190. void WebSocket::Close()
  191. {
  192. is_->state = WS_CLOSING;
  193. websocketpp::lib::error_code ec;
  194. ATOMIC_LOGDEBUG("WebSocket atempting to close URL " + is_->url);
  195. is_->con->terminate(ec);
  196. }
  197. void WebSocket::OpenAgain()
  198. {
  199. is_->state = WS_CONNECTING;
  200. ATOMIC_LOGDEBUG("WebSocket request (again) to URL " + is_->url);
  201. is_->MakeConnection();
  202. }
  203. }
  204. #endif