WebSocketServer.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Copyright (c) 2023 Project CHIP Authors
  3. * All rights reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. #include "WebSocketServer.h"
  19. #include <lib/support/ScopedBuffer.h>
  20. #include <libwebsockets.h>
  21. #include <deque>
  22. #include <mutex>
  23. constexpr uint16_t kDefaultWebSocketServerPort = 9002;
  24. constexpr uint16_t kMaxMessageBufferLen = 8192;
  25. namespace {
  26. lws * gWebSocketInstance = nullptr;
  27. std::deque<std::string> gMessageQueue;
  28. // This mutex protect the global gMessageQueue instance such that messages
  29. // can be added/removed from multiple threads.
  30. std::mutex gMutex;
  31. void LogWebSocketCallbackReason(lws_callback_reasons reason)
  32. {
  33. #if CHIP_DETAIL_LOGGING
  34. switch (reason)
  35. {
  36. case LWS_CALLBACK_GET_THREAD_ID:
  37. ChipLogDetail(chipTool, "LWS_CALLBACK_GET_THREAD_ID");
  38. break;
  39. case LWS_CALLBACK_ADD_HEADERS:
  40. ChipLogDetail(chipTool, "LWS_CALLBACK_ADD_HEADERS");
  41. break;
  42. case LWS_CALLBACK_PROTOCOL_INIT:
  43. ChipLogDetail(chipTool, "LWS_CALLBACK_PROTOCOL_INIT");
  44. break;
  45. case LWS_CALLBACK_PROTOCOL_DESTROY:
  46. ChipLogDetail(chipTool, "LWS_CALLBACK_PROTOCOL_DESTROY");
  47. break;
  48. case LWS_CALLBACK_HTTP:
  49. ChipLogDetail(chipTool, "LWS_CALLBACK_HTTP");
  50. break;
  51. case LWS_CALLBACK_EVENT_WAIT_CANCELLED:
  52. ChipLogDetail(chipTool, "LWS_CALLBACK_EVENT_WAIT_CANCELLED");
  53. break;
  54. case LWS_CALLBACK_CLIENT_WRITEABLE:
  55. ChipLogDetail(chipTool, "LWS_CALLBACK_CLIENT_WRITEABLE");
  56. break;
  57. case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
  58. ChipLogDetail(chipTool, "LWS_CALLBACK_FILTER_NETWORK_CONNECTION");
  59. break;
  60. case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
  61. ChipLogDetail(chipTool, "LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION");
  62. break;
  63. case LWS_CALLBACK_WSI_CREATE:
  64. ChipLogDetail(chipTool, "LWS_CALLBACK_WSI_CREATE");
  65. break;
  66. case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
  67. ChipLogDetail(chipTool, "LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED");
  68. break;
  69. case LWS_CALLBACK_HTTP_CONFIRM_UPGRADE:
  70. ChipLogDetail(chipTool, "LWS_CALLBACK_HTTP_CONFIRM_UPGRADE");
  71. break;
  72. case LWS_CALLBACK_HTTP_BIND_PROTOCOL:
  73. ChipLogDetail(chipTool, "LWS_CALLBACK_HTTP_BIND_PROTOCOL");
  74. break;
  75. case LWS_CALLBACK_ESTABLISHED:
  76. ChipLogDetail(chipTool, "LWS_CALLBACK_ESTABLISHED");
  77. break;
  78. case LWS_CALLBACK_RECEIVE:
  79. ChipLogDetail(chipTool, "LWS_CALLBACK_RECEIVE");
  80. break;
  81. case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
  82. ChipLogDetail(chipTool, "LWS_CALLBACK_WS_PEER_INITIATED_CLOSE");
  83. break;
  84. case LWS_CALLBACK_WSI_DESTROY:
  85. ChipLogDetail(chipTool, "LWS_CALLBACK_WSI_DESTROY");
  86. break;
  87. case LWS_CALLBACK_CLOSED:
  88. ChipLogDetail(chipTool, "LWS_CALLBACK_CLOSED");
  89. break;
  90. case LWS_CALLBACK_SERVER_WRITEABLE:
  91. ChipLogDetail(chipTool, "LWS_CALLBACK_SERVER_WRITEABLE");
  92. break;
  93. case LWS_CALLBACK_CLOSED_HTTP:
  94. ChipLogDetail(chipTool, "LWS_CALLBACK_CLOSED_HTTP");
  95. break;
  96. default:
  97. ChipLogError(chipTool, "Unknown reason: %d ", static_cast<int>(reason));
  98. }
  99. #endif // CHIP_DETAIL_LOGGING
  100. }
  101. static int OnWebSocketCallback(lws * wsi, lws_callback_reasons reason, void * user, void * in, size_t len)
  102. {
  103. LogWebSocketCallbackReason(reason);
  104. if (LWS_CALLBACK_RECEIVE == reason)
  105. {
  106. WebSocketServer * server = nullptr;
  107. auto protocol = lws_get_protocol(wsi);
  108. if (!protocol)
  109. {
  110. ChipLogError(chipTool, "Failed to retrieve the protocol.");
  111. return -1;
  112. }
  113. server = static_cast<WebSocketServer *>(protocol->user);
  114. if (nullptr == server)
  115. {
  116. ChipLogError(chipTool, "Failed to retrieve the server interactive context.");
  117. return -1;
  118. }
  119. char msg[kMaxMessageBufferLen + 1 /* for null byte */] = {};
  120. VerifyOrDie(sizeof(msg) > len);
  121. memcpy(msg, in, len);
  122. server->OnWebSocketMessageReceived(msg);
  123. }
  124. else if (LWS_CALLBACK_SERVER_WRITEABLE == reason)
  125. {
  126. std::lock_guard<std::mutex> lock(gMutex);
  127. for (auto & msg : gMessageQueue)
  128. {
  129. chip::Platform::ScopedMemoryBuffer<unsigned char> buffer;
  130. VerifyOrDie(buffer.Calloc(LWS_PRE + msg.size()));
  131. memcpy(&buffer[LWS_PRE], (void *) msg.c_str(), msg.size());
  132. lws_write(wsi, &buffer[LWS_PRE], msg.size(), LWS_WRITE_TEXT);
  133. }
  134. gMessageQueue.clear();
  135. }
  136. else if (LWS_CALLBACK_ESTABLISHED == reason)
  137. {
  138. gWebSocketInstance = wsi;
  139. }
  140. else if (LWS_CALLBACK_WSI_DESTROY == reason)
  141. {
  142. gWebSocketInstance = nullptr;
  143. }
  144. return 0;
  145. }
  146. } // namespace
  147. CHIP_ERROR WebSocketServer::Run(chip::Optional<uint16_t> port, WebSocketServerDelegate * delegate)
  148. {
  149. VerifyOrReturnError(nullptr != delegate, CHIP_ERROR_INVALID_ARGUMENT);
  150. lws_protocols protocols[] = { { "ws", OnWebSocketCallback, 0, 0, 0, this, 0 }, LWS_PROTOCOL_LIST_TERM };
  151. lws_context_creation_info info;
  152. memset(&info, 0, sizeof(info));
  153. info.port = port.ValueOr(kDefaultWebSocketServerPort);
  154. info.iface = nullptr;
  155. info.pt_serv_buf_size = kMaxMessageBufferLen;
  156. info.protocols = protocols;
  157. auto context = lws_create_context(&info);
  158. VerifyOrReturnError(nullptr != context, CHIP_ERROR_INTERNAL);
  159. mRunning = true;
  160. mDelegate = delegate;
  161. while (mRunning)
  162. {
  163. lws_service(context, -1);
  164. std::lock_guard<std::mutex> lock(gMutex);
  165. if (gMessageQueue.size())
  166. {
  167. lws_callback_on_writable(gWebSocketInstance);
  168. }
  169. }
  170. lws_context_destroy(context);
  171. return CHIP_NO_ERROR;
  172. }
  173. bool WebSocketServer::OnWebSocketMessageReceived(char * msg)
  174. {
  175. auto shouldContinue = mDelegate->OnWebSocketMessageReceived(msg);
  176. if (!shouldContinue)
  177. {
  178. mRunning = false;
  179. }
  180. return shouldContinue;
  181. }
  182. void WebSocketServer::Send(const char * msg)
  183. {
  184. std::lock_guard<std::mutex> lock(gMutex);
  185. gMessageQueue.push_back(msg);
  186. }