WSServer.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //
  2. // Copyright (c) 2008-2020 the Urho3D project.
  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 "../Network.h"
  23. #include "../../Core/WorkQueue.h"
  24. #include "../../IO/Log.h"
  25. #include "../../IO/MemoryBuffer.h"
  26. #include "WSHandler.h"
  27. #include "WSServer.h"
  28. #include <libwebsockets.h>
  29. #include <signal.h>
  30. #include <string.h>
  31. static Urho3D::WSServer* WSServerInstance = nullptr;
  32. static struct lws_context *context;
  33. static int WSCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
  34. URHO3D_LOGINFOF("Incoming server messsage reason %d", reason);
  35. switch (reason) {
  36. case LWS_CALLBACK_PROTOCOL_INIT:
  37. break;
  38. case LWS_CALLBACK_ESTABLISHED:
  39. if (WSServerInstance) {
  40. WSServerInstance->AddPendingConnection(wsi);
  41. }
  42. break;
  43. case LWS_CALLBACK_SERVER_WRITEABLE:
  44. case LWS_CALLBACK_CLIENT_WRITEABLE:
  45. if (WSServerInstance) {
  46. Urho3D::MutexLock lock(WSServerInstance->GetOutgoingMutex());
  47. auto* packets = WSServerInstance->GetOutgoingPackets(wsi);
  48. for (auto it = packets->Begin(); it != packets->End(); ++it) {
  49. WSPacket& packet = (*it);
  50. unsigned char buf[LWS_PRE + packet.second_.GetSize()];
  51. memcpy(&buf[LWS_PRE], packet.second_.GetData(), packet.second_.GetSize());
  52. int retval = lws_write(packet.first_.GetWS(), &buf[LWS_PRE], packet.second_.GetSize(), LWS_WRITE_BINARY);
  53. if (retval < packet.second_.GetSize()) {
  54. URHO3D_LOGERRORF("Failed to write to WS, ret = %d", retval);
  55. break;
  56. }
  57. packets->Erase(it);
  58. lws_callback_on_writable(wsi);
  59. break;
  60. }
  61. }
  62. break;
  63. case LWS_CALLBACK_RECEIVE: {
  64. Urho3D::VectorBuffer b((unsigned char*)in, len);
  65. if (b.GetData()[0] == URHO3D_MESSAGE) {
  66. WSPacket packet(wsi, b);
  67. if (WSServerInstance) {
  68. WSServerInstance->AddIncomingPacket(packet);
  69. }
  70. } else {
  71. URHO3D_LOGINFOF("Received message that is not part of the engine %d", b.GetData()[0]);
  72. }
  73. break;
  74. }
  75. case LWS_CALLBACK_CLOSED: {
  76. if (WSServerInstance) {
  77. WSServerInstance->AddClosedConnection(wsi);
  78. }
  79. URHO3D_LOGINFOF("LWS_CALLBACK_CLOSED");
  80. break;
  81. }
  82. default:
  83. break;
  84. }
  85. return 0;
  86. }
  87. static struct lws_protocols protocols[] = {
  88. { "ws-server", WSCallback, 0, 0 },
  89. { NULL, NULL, 0, 0 } /* terminator */
  90. };
  91. static int interrupted;
  92. void sigint_handler(int sig)
  93. {
  94. interrupted = 1;
  95. }
  96. using namespace Urho3D;
  97. static void RunService(const WorkItem* item, unsigned threadIndex) {
  98. auto packets = WSServerInstance->GetAllOutgoingPackets();
  99. for (auto it = packets->Begin(); it != packets->End(); ++it) {
  100. if (!(*it).second_.Empty()) {
  101. auto ws = (*it).second_.Front().first_;
  102. URHO3D_LOGINFOF("Outgoing packet count (server) %d", WSServerInstance->GetNumOutgoingPackets(ws));
  103. lws_callback_on_writable(ws.GetWS());
  104. }
  105. }
  106. int result = lws_service(context, 0);
  107. if (result < 0 && WSServerInstance) {
  108. WSServerInstance->StopServer();
  109. }
  110. URHO3D_LOGINFOF("Running server service");
  111. }
  112. WSServer::WSServer(Context* context):
  113. Object(context)
  114. {
  115. WSServerInstance = this;
  116. }
  117. WSServer::~WSServer()
  118. {
  119. WSServerInstance = nullptr;
  120. }
  121. int WSServer::StartServer()
  122. {
  123. struct lws_context_creation_info info;
  124. const char *p;
  125. int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
  126. /* for LLL_ verbosity above NOTICE to be built into lws,
  127. * lws must have been configured and built with
  128. * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
  129. /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
  130. /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
  131. /* | LLL_DEBUG */;
  132. signal(SIGINT, sigint_handler);
  133. lws_set_log_level(logs, OutputWSLog);
  134. lwsl_user("LWS minimal ws server | visit http://localhost:9292 (-s = use TLS / https)\n");
  135. memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
  136. info.port = 9292;
  137. // info.mounts = &mount;
  138. info.protocols = protocols;
  139. // info.vhost_name = "localhost";
  140. info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
  141. info.gid = -1;
  142. info.uid = -1;
  143. // info.username = "";
  144. // info.groupname = "";
  145. #if defined(LWS_WITH_TLS)
  146. if (lws_cmdline_option(argc, argv, "-s")) {
  147. lwsl_user("Server using TLS\n");
  148. info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
  149. info.ssl_cert_filepath = "localhost-100y.cert";
  150. info.ssl_private_key_filepath = "localhost-100y.key";
  151. }
  152. #endif
  153. lwsl_err("creating context");
  154. context = lws_create_context(&info);
  155. lwsl_err("context started");
  156. if (!context) {
  157. lwsl_err("lws init failed\n");
  158. return 1;
  159. }
  160. lwsl_err("Server started");
  161. SubscribeToEvent(E_WORKITEMCOMPLETED, URHO3D_HANDLER(WSServer, HandleWorkItemFinished));
  162. return 0;
  163. // struct lws_context_creation_info info;
  164. // memset(&info, 0, sizeof(info));
  165. // info.protocols = protocols2;
  166. // info.port = 9090;//config.port;
  167. // info.gid = -1;
  168. // info.uid = -1;
  169. //#define WS_OPT(member, var) info.member = (!var.empty()) ? var.c_str() : NULL
  170. // WS_OPT(iface, config.iface);
  171. // WS_OPT(ssl_cert_filepath, config.sslCertFile);
  172. // WS_OPT(ssl_private_key_filepath, config.sslPrivateKeyFile);
  173. // WS_OPT(ssl_ca_filepath, config.sslCACertFile);
  174. // WS_OPT(ssl_cipher_list, config.sslCipherList);
  175. // WS_OPT(http_proxy_address, config.httpProxyAddress);
  176. //#undef WS_OPT
  177. // context = libwebsocket_create_context(&info);
  178. //
  179. //
  180. // initialized_ = true;
  181. return 0;
  182. }
  183. void WSServer::Update(float timestep)
  184. {
  185. if (currentState_ == WSS_RUNNING && nextState_ == WSS_STOPPED) {
  186. GetSubsystem<Network>()->StopServer();
  187. }
  188. while(!closedConnections.Empty()) {
  189. auto ws = closedConnections.Front().GetWS();
  190. GetSubsystem<Network>()->ClientDisconnected(closedConnections.Front());
  191. auto packets = GetAllOutgoingPackets();
  192. packets->Erase(ws);
  193. closedConnections.PopFront();
  194. }
  195. while(!pendingConnections_.Empty()) {
  196. auto ws = pendingConnections_.Front();
  197. GetSubsystem<Network>()->NewConnectionEstablished(ws);
  198. pendingConnections_.PopFront();
  199. }
  200. if (context) {
  201. if (!serviceWorkItem_) {
  202. WorkQueue *workQueue = GetSubsystem<WorkQueue>();
  203. serviceWorkItem_ = workQueue->GetFreeItem();
  204. serviceWorkItem_->priority_ = M_MAX_INT;
  205. serviceWorkItem_->workFunction_ = RunService;
  206. serviceWorkItem_->aux_ = this;
  207. serviceWorkItem_->sendEvent_ = true;
  208. workQueue->AddWorkItem(serviceWorkItem_);
  209. }
  210. }
  211. while (GetNumIncomingPackets()) {
  212. auto packet = GetIncomingPacket();
  213. GetSubsystem<Network>()->HandleIncomingPacket(packet, true);
  214. RemoveIncomingPacket();
  215. }
  216. if (currentState_ != nextState_) {
  217. currentState_ = nextState_;
  218. }
  219. }
  220. void WSServer::StopServer()
  221. {
  222. SetState(WSS_STOPPED);
  223. if (context) {
  224. lws_context_destroy(context);
  225. context = nullptr;
  226. }
  227. }
  228. void WSServer::HandleWorkItemFinished(StringHash eventType, VariantMap& eventData)
  229. {
  230. using namespace WorkItemCompleted;
  231. WorkItem *workItem = reinterpret_cast<WorkItem *>(eventData[P_ITEM].GetPtr());
  232. if (workItem->aux_ != this) {
  233. return;
  234. }
  235. if (workItem->workFunction_ == RunService) {
  236. serviceWorkItem_.Reset();
  237. }
  238. }
  239. void WSServer::AddPendingConnection(lws* ws)
  240. {
  241. pendingConnections_.Push(ws);
  242. }
  243. void WSServer::AddClosedConnection(lws* ws)
  244. {
  245. closedConnections.Push(ws);
  246. }
  247. void WSServer::SetState(WSServerState state)
  248. {
  249. nextState_ = state;
  250. }