WebSocket-libwebsockets.cpp 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546
  1. /****************************************************************************
  2. Copyright (c) 2010-2012 cocos2d-x.org
  3. Copyright (c) 2013-2016 Chukong Technologies Inc.
  4. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
  5. http://www.cocos2d-x.org
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  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. "[WebSocket module] is based in part on the work of the libwebsockets project
  22. (http://libwebsockets.org)"
  23. ****************************************************************************/
  24. #include "websockets/libwebsockets.h"
  25. #include <algorithm>
  26. #include <atomic>
  27. #include <condition_variable>
  28. #include <errno.h>
  29. #include <list>
  30. #include <mutex>
  31. #include <memory> // for std::shared_ptr
  32. #include <queue>
  33. #include <string>
  34. #include <signal.h>
  35. #include <thread>
  36. #include <vector>
  37. #include "network/WebSocket.h"
  38. #include "network/Uri.h"
  39. #include "base/CCScheduler.h"
  40. #include "platform/CCFileUtils.h"
  41. #include "platform/CCStdC.h"
  42. #include "platform/CCApplication.h"
  43. #define NS_NETWORK_BEGIN namespace cocos2d { namespace network {
  44. #define NS_NETWORK_END }}
  45. #define WS_RX_BUFFER_SIZE (65536)
  46. #define WS_RESERVE_RECEIVE_BUFFER_SIZE (4096)
  47. #define LOG_TAG "WebSocket.cpp"
  48. struct lws;
  49. struct lws_protocols;
  50. struct lws_vhost;
  51. #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
  52. // log, CCLOG aren't threadsafe, since we uses sub threads for parsing pcm data, threadsafe log output
  53. // is needed. Define the following macros (ALOGV, ALOGD, ALOGI, ALOGW, ALOGE) for threadsafe log output.
  54. //IDEA: Move _winLog, winLog to a separated file
  55. static void _winLog(const char *format, va_list args)
  56. {
  57. static const int MAX_LOG_LENGTH = 16 * 1024;
  58. int bufferSize = MAX_LOG_LENGTH;
  59. char* buf = nullptr;
  60. do
  61. {
  62. buf = new (std::nothrow) char[bufferSize];
  63. if (buf == nullptr)
  64. return; // not enough memory
  65. int ret = vsnprintf(buf, bufferSize - 3, format, args);
  66. if (ret < 0)
  67. {
  68. bufferSize *= 2;
  69. delete[] buf;
  70. }
  71. else
  72. break;
  73. } while (true);
  74. strcat(buf, "\n");
  75. int pos = 0;
  76. int len = strlen(buf);
  77. char tempBuf[MAX_LOG_LENGTH + 1] = { 0 };
  78. WCHAR wszBuf[MAX_LOG_LENGTH + 1] = { 0 };
  79. do
  80. {
  81. std::copy(buf + pos, buf + pos + MAX_LOG_LENGTH, tempBuf);
  82. tempBuf[MAX_LOG_LENGTH] = 0;
  83. MultiByteToWideChar(CP_UTF8, 0, tempBuf, -1, wszBuf, sizeof(wszBuf));
  84. OutputDebugStringW(wszBuf);
  85. pos += MAX_LOG_LENGTH;
  86. } while (pos < len);
  87. delete[] buf;
  88. }
  89. static void wsLog(const char * format, ...)
  90. {
  91. va_list args;
  92. va_start(args, format);
  93. _winLog(format, args);
  94. va_end(args);
  95. }
  96. #else
  97. #define wsLog printf
  98. #endif
  99. #define QUOTEME_(x) #x
  100. #define QUOTEME(x) QUOTEME_(x)
  101. // Since CCLOG isn't thread safe, we uses LOGD for multi-thread logging.
  102. #ifdef ANDROID
  103. #if COCOS2D_DEBUG > 0
  104. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,__VA_ARGS__)
  105. #else
  106. #define LOGD(...)
  107. #endif
  108. #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,__VA_ARGS__)
  109. #else
  110. #if COCOS2D_DEBUG > 0
  111. #define LOGD(fmt, ...) wsLog("D/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
  112. #else
  113. #define LOGD(fmt, ...)
  114. #endif
  115. #define LOGE(fmt, ...) wsLog("E/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
  116. #endif
  117. static void printWebSocketLog(int level, const char *line)
  118. {
  119. #if COCOS2D_DEBUG > 0
  120. static const char * const log_level_names[] = {
  121. "ERR",
  122. "WARN",
  123. "NOTICE",
  124. "INFO",
  125. "DEBUG",
  126. "PARSER",
  127. "HEADER",
  128. "EXTENSION",
  129. "CLIENT",
  130. "LATENCY",
  131. };
  132. char buf[30] = {0};
  133. int n;
  134. for (n = 0; n < LLL_COUNT; n++) {
  135. if (level != (1 << n))
  136. continue;
  137. sprintf(buf, "%s: ", log_level_names[n]);
  138. break;
  139. }
  140. LOGD("%s%s\n", buf, line);
  141. #endif // #if COCOS2D_DEBUG > 0
  142. }
  143. class WebSocketImpl
  144. {
  145. public:
  146. static void closeAllConnections();
  147. WebSocketImpl(cocos2d::network::WebSocket* ws);
  148. ~WebSocketImpl();
  149. bool init(const cocos2d::network::WebSocket::Delegate& delegate,
  150. const std::string& url,
  151. const std::vector<std::string>* protocols = nullptr,
  152. const std::string& caFilePath = "");
  153. void send(const std::string& message);
  154. void send(const unsigned char* binaryMsg, unsigned int len);
  155. void close();
  156. void closeAsync();
  157. void closeAsync(int code, const std::string &reason);
  158. cocos2d::network::WebSocket::State getReadyState() const;
  159. const std::string& getUrl() const;
  160. const std::string& getProtocol() const;
  161. cocos2d::network::WebSocket::Delegate* getDelegate() const;
  162. size_t getBufferedAmount() const;
  163. std::string getExtensions() const;
  164. private:
  165. // The following callback functions are invoked in websocket thread
  166. void onClientOpenConnectionRequest();
  167. int onSocketCallback(struct lws *wsi, enum lws_callback_reasons reason, void* in, ssize_t len);
  168. int onClientWritable();
  169. int onClientReceivedData(void* in, ssize_t len);
  170. int onConnectionOpened();
  171. int onConnectionError();
  172. int onConnectionClosed();
  173. struct lws_vhost* createVhost(struct lws_protocols* protocols, int& sslConnection);
  174. private:
  175. cocos2d::network::WebSocket* _ws;
  176. cocos2d::network::WebSocket::State _readyState;
  177. std::mutex _readyStateMutex;
  178. std::string _url;
  179. std::vector<char> _receivedData;
  180. struct lws* _wsInstance;
  181. struct lws_protocols* _lwsProtocols;
  182. std::string _clientSupportedProtocols;
  183. std::string _selectedProtocol;
  184. std::shared_ptr<std::atomic<bool>> _isDestroyed;
  185. cocos2d::network::WebSocket::Delegate* _delegate;
  186. std::mutex _closeMutex;
  187. std::condition_variable _closeCondition;
  188. std::vector<std::string> _enabledExtensions;
  189. enum class CloseState
  190. {
  191. NONE,
  192. SYNC_CLOSING,
  193. SYNC_CLOSED,
  194. ASYNC_CLOSING
  195. };
  196. CloseState _closeState;
  197. std::string _caFilePath;
  198. friend class WsThreadHelper;
  199. friend class WebSocketCallbackWrapper;
  200. };
  201. enum WS_MSG {
  202. WS_MSG_TO_SUBTRHEAD_SENDING_STRING = 0,
  203. WS_MSG_TO_SUBTRHEAD_SENDING_BINARY,
  204. WS_MSG_TO_SUBTHREAD_CREATE_CONNECTION
  205. };
  206. class WsThreadHelper;
  207. static std::vector<WebSocketImpl*>* __websocketInstances = nullptr;
  208. static std::mutex __instanceMutex;
  209. static struct lws_context* __wsContext = nullptr;
  210. static WsThreadHelper* __wsHelper = nullptr;
  211. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
  212. static std::string getFileNameForPath(const std::string& filePath)
  213. {
  214. std::string fileName = filePath;
  215. const size_t lastSlashIdx = fileName.find_last_of("\\/");
  216. if (std::string::npos != lastSlashIdx)
  217. {
  218. fileName.erase(0, lastSlashIdx + 1);
  219. }
  220. return fileName;
  221. }
  222. #endif
  223. static struct lws_protocols __defaultProtocols[2];
  224. static lws_context_creation_info convertToContextCreationInfo(const struct lws_protocols* protocols, bool peerServerCert)
  225. {
  226. lws_context_creation_info info;
  227. memset(&info, 0, sizeof(info));
  228. /*
  229. * create the websocket context. This tracks open connections and
  230. * knows how to route any traffic and which protocol version to use,
  231. * and if each connection is client or server side.
  232. *
  233. * For this client-only demo, we tell it to not listen on any port.
  234. */
  235. info.port = CONTEXT_PORT_NO_LISTEN;
  236. info.protocols = protocols;
  237. // IDEA: Disable 'permessage-deflate' extension temporarily because of issues:
  238. // https://github.com/cocos2d/cocos2d-x/issues/16045, https://github.com/cocos2d/cocos2d-x/issues/15767
  239. // libwebsockets issue: https://github.com/warmcat/libwebsockets/issues/593
  240. // Currently, we couldn't find out the exact reason.
  241. // libwebsockets official said it's probably an issue of user code
  242. // since 'libwebsockets' passed AutoBahn stressed Test.
  243. // info.extensions = exts;
  244. info.gid = -1;
  245. info.uid = -1;
  246. if (peerServerCert)
  247. {
  248. info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
  249. }
  250. else
  251. {
  252. info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED;
  253. }
  254. info.user = nullptr;
  255. return info;
  256. }
  257. class WsMessage
  258. {
  259. public:
  260. WsMessage() : id(++__id), what(0), data(nullptr), user(nullptr){}
  261. unsigned int id;
  262. unsigned int what; // message type
  263. cocos2d::network::WebSocket::Data* data;
  264. void* user;
  265. private:
  266. static unsigned int __id;
  267. };
  268. unsigned int WsMessage::__id = 0;
  269. /**
  270. * @brief Websocket thread helper, it's used for sending message between UI thread and websocket thread.
  271. */
  272. class WsThreadHelper
  273. {
  274. public:
  275. WsThreadHelper();
  276. ~WsThreadHelper();
  277. // Creates a new thread
  278. bool createWebSocketThread();
  279. // Quits websocket thread.
  280. void quitWebSocketThread();
  281. // Sends message to Cocos thread. It's needed to be invoked in Websocket thread.
  282. void sendMessageToCocosThread(const std::function<void()>& cb);
  283. // Sends message to Websocket thread. It's needs to be invoked in Cocos thread.
  284. void sendMessageToWebSocketThread(WsMessage *msg);
  285. size_t countBufferdBytes(const WebSocketImpl *ws);
  286. // Waits the sub-thread (websocket thread) to exit,
  287. void joinWebSocketThread();
  288. void onSubThreadStarted();
  289. void onSubThreadLoop();
  290. void onSubThreadEnded();
  291. protected:
  292. void wsThreadEntryFunc();
  293. public:
  294. std::list<WsMessage*>* _subThreadWsMessageQueue;
  295. std::mutex _subThreadWsMessageQueueMutex;
  296. std::thread* _subThreadInstance;
  297. private:
  298. bool _needQuit;
  299. };
  300. // Wrapper for converting websocket callback from static function to member function of WebSocket class.
  301. class WebSocketCallbackWrapper {
  302. public:
  303. static int onSocketCallback(struct lws *wsi, enum lws_callback_reasons reason, void* user, void* in, size_t len)
  304. {
  305. // Gets the user data from context. We know that it's a 'WebSocket' instance.
  306. if (wsi == nullptr) {
  307. return 0;
  308. }
  309. int ret = 0;
  310. WebSocketImpl* ws = (WebSocketImpl*)lws_wsi_user(wsi);
  311. if (ws != nullptr && __websocketInstances != nullptr)
  312. {
  313. if (std::find(__websocketInstances->begin(), __websocketInstances->end(), ws) != __websocketInstances->end())
  314. {
  315. ret = ws->onSocketCallback(wsi, reason, in, len);
  316. }
  317. }
  318. else
  319. {
  320. // LOGD("ws instance is nullptr.\n");
  321. }
  322. return ret;
  323. }
  324. };
  325. // Implementation of WsThreadHelper
  326. WsThreadHelper::WsThreadHelper()
  327. : _subThreadInstance(nullptr)
  328. , _needQuit(false)
  329. {
  330. _subThreadWsMessageQueue = new (std::nothrow) std::list<WsMessage*>();
  331. }
  332. WsThreadHelper::~WsThreadHelper()
  333. {
  334. joinWebSocketThread();
  335. CC_SAFE_DELETE(_subThreadInstance);
  336. delete _subThreadWsMessageQueue;
  337. }
  338. bool WsThreadHelper::createWebSocketThread()
  339. {
  340. // Creates websocket thread
  341. _subThreadInstance = new (std::nothrow) std::thread(&WsThreadHelper::wsThreadEntryFunc, this);
  342. return true;
  343. }
  344. void WsThreadHelper::quitWebSocketThread()
  345. {
  346. _needQuit = true;
  347. }
  348. void WsThreadHelper::onSubThreadLoop()
  349. {
  350. if (__wsContext)
  351. {
  352. // _readyStateMutex.unlock();
  353. __wsHelper->_subThreadWsMessageQueueMutex.lock();
  354. bool isEmpty = __wsHelper->_subThreadWsMessageQueue->empty();
  355. if (!isEmpty)
  356. {
  357. auto iter = __wsHelper->_subThreadWsMessageQueue->begin();
  358. for (; iter != __wsHelper->_subThreadWsMessageQueue->end(); )
  359. {
  360. auto msg = (*iter);
  361. auto ws = (WebSocketImpl*)msg->user;
  362. // REFINE: ws may be a invalid pointer
  363. if (msg->what == WS_MSG_TO_SUBTHREAD_CREATE_CONNECTION)
  364. {
  365. ws->onClientOpenConnectionRequest();
  366. delete *iter;
  367. iter = __wsHelper->_subThreadWsMessageQueue->erase(iter);
  368. }
  369. else
  370. {
  371. ++iter;
  372. }
  373. }
  374. }
  375. __wsHelper->_subThreadWsMessageQueueMutex.unlock();
  376. // Cause delay 4ms for event WS_MSG_TO_SUBTHREAD_CREATE_CONNECTION
  377. lws_service(__wsContext, 4);
  378. }
  379. }
  380. void WsThreadHelper::onSubThreadStarted()
  381. {
  382. int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_INFO/* | LLL_DEBUG | LLL_PARSER | LLL_HEADER*/ | LLL_EXT | LLL_CLIENT | LLL_LATENCY;
  383. lws_set_log_level(log_level, printWebSocketLog);
  384. memset(__defaultProtocols, 0, 2 * sizeof(struct lws_protocols));
  385. __defaultProtocols[0].name = "";
  386. __defaultProtocols[0].callback = WebSocketCallbackWrapper::onSocketCallback;
  387. __defaultProtocols[0].rx_buffer_size = WS_RX_BUFFER_SIZE;
  388. __defaultProtocols[0].id = std::numeric_limits<uint32_t>::max();
  389. lws_context_creation_info creationInfo = convertToContextCreationInfo(__defaultProtocols, true);
  390. __wsContext = lws_create_context(&creationInfo);
  391. }
  392. void WsThreadHelper::onSubThreadEnded()
  393. {
  394. if (__wsContext != nullptr)
  395. {
  396. lws_context_destroy(__wsContext);
  397. }
  398. }
  399. void WsThreadHelper::wsThreadEntryFunc()
  400. {
  401. LOGD("WebSocket thread start, helper instance: %p\n", this);
  402. onSubThreadStarted();
  403. while (!_needQuit)
  404. {
  405. onSubThreadLoop();
  406. }
  407. onSubThreadEnded();
  408. LOGD("WebSocket thread exit, helper instance: %p\n", this);
  409. }
  410. void WsThreadHelper::sendMessageToCocosThread(const std::function<void()>& cb)
  411. {
  412. cocos2d::Application::getInstance()->getScheduler()->performFunctionInCocosThread(cb);
  413. }
  414. void WsThreadHelper::sendMessageToWebSocketThread(WsMessage *msg)
  415. {
  416. std::lock_guard<std::mutex> lk(_subThreadWsMessageQueueMutex);
  417. _subThreadWsMessageQueue->push_back(msg);
  418. }
  419. size_t WsThreadHelper::countBufferdBytes(const WebSocketImpl *ws)
  420. {
  421. std::lock_guard<std::mutex> lk(_subThreadWsMessageQueueMutex);
  422. size_t total = 0;
  423. for (auto msg : *_subThreadWsMessageQueue)
  424. {
  425. if (msg->user == ws && msg->data && (msg->what == WS_MSG_TO_SUBTRHEAD_SENDING_STRING
  426. || msg->what == WS_MSG_TO_SUBTRHEAD_SENDING_BINARY)) {
  427. total += msg->data->getRemain();
  428. }
  429. }
  430. return total;
  431. }
  432. void WsThreadHelper::joinWebSocketThread()
  433. {
  434. if (_subThreadInstance->joinable())
  435. {
  436. _subThreadInstance->join();
  437. }
  438. }
  439. // Define a WebSocket frame
  440. class WebSocketFrame
  441. {
  442. public:
  443. WebSocketFrame()
  444. : _payload(nullptr)
  445. , _payloadLength(0)
  446. , _frameLength(0)
  447. {
  448. }
  449. bool init(unsigned char* buf, ssize_t len)
  450. {
  451. if (buf == nullptr && len > 0)
  452. return false;
  453. if (!_data.empty())
  454. {
  455. LOGD("WebSocketFrame was initialized, should not init it again!\n");
  456. return false;
  457. }
  458. _data.resize(LWS_PRE + len);
  459. if (len > 0)
  460. {
  461. std::copy(buf, buf+len, _data.begin() + LWS_PRE);
  462. }
  463. _payload = _data.data() + LWS_PRE;
  464. _payloadLength = len;
  465. _frameLength = len;
  466. return true;
  467. }
  468. void update(ssize_t issued)
  469. {
  470. _payloadLength -= issued;
  471. _payload += issued;
  472. }
  473. unsigned char* getPayload() const { return _payload; }
  474. ssize_t getPayloadLength() const { return _payloadLength; }
  475. ssize_t getFrameLength() const { return _frameLength; }
  476. private:
  477. unsigned char* _payload;
  478. ssize_t _payloadLength;
  479. ssize_t _frameLength;
  480. std::vector<unsigned char> _data;
  481. };
  482. //
  483. void WebSocketImpl::closeAllConnections()
  484. {
  485. if (__websocketInstances != nullptr)
  486. {
  487. ssize_t count = __websocketInstances->size();
  488. for (ssize_t i = count-1; i >=0 ; i--)
  489. {
  490. WebSocketImpl* instance = __websocketInstances->at(i);
  491. instance->close();
  492. }
  493. std::lock_guard<std::mutex> lk(__instanceMutex);
  494. __websocketInstances->clear();
  495. delete __websocketInstances;
  496. __websocketInstances = nullptr;
  497. }
  498. }
  499. WebSocketImpl::WebSocketImpl(cocos2d::network::WebSocket* ws)
  500. : _ws(ws)
  501. , _readyState(cocos2d::network::WebSocket::State::CONNECTING)
  502. , _wsInstance(nullptr)
  503. , _lwsProtocols(nullptr)
  504. , _isDestroyed(std::make_shared<std::atomic<bool>>(false))
  505. , _delegate(nullptr)
  506. , _closeState(CloseState::NONE)
  507. {
  508. // reserve data buffer to avoid allocate memory frequently
  509. _receivedData.reserve(WS_RESERVE_RECEIVE_BUFFER_SIZE);
  510. if (__websocketInstances == nullptr)
  511. {
  512. __websocketInstances = new (std::nothrow) std::vector<WebSocketImpl*>();
  513. }
  514. __websocketInstances->push_back(this);
  515. // NOTE: !!! Be careful while merging cocos2d-x-lite back to cocos2d-x. !!!
  516. // 'close' is a synchronous operation which may wait some seconds to make sure connection is closed.
  517. // But JSB doesn't need to listen on EVENT_RESET event to close connection,
  518. // since finalize callback (refer to 'WebSocket_finalize' function in jsb_websocket.cpp) will invoke 'closeAsync'.
  519. //
  520. // std::shared_ptr<std::atomic<bool>> isDestroyed = _isDestroyed;
  521. // _resetDirectorListener = cocos2d::Director::getInstance()->getEventDispatcher()->addCustomEventListener(cocos2d::Director::EVENT_RESET, [this, isDestroyed](cocos2d::EventCustom*){
  522. // if (*isDestroyed)
  523. // return;
  524. // close();
  525. // });
  526. }
  527. WebSocketImpl::~WebSocketImpl()
  528. {
  529. LOGD("In the destructor of WebSocket (%p)\n", this);
  530. std::lock_guard<std::mutex> lk(__instanceMutex);
  531. if (__websocketInstances != nullptr)
  532. {
  533. auto iter = std::find(__websocketInstances->begin(), __websocketInstances->end(), this);
  534. if (iter != __websocketInstances->end())
  535. {
  536. __websocketInstances->erase(iter);
  537. }
  538. else
  539. {
  540. LOGD("ERROR: WebSocket instance (%p) wasn't added to the container which saves websocket instances!\n", this);
  541. }
  542. }
  543. if (__websocketInstances == nullptr || __websocketInstances->empty())
  544. {
  545. __wsHelper->quitWebSocketThread();
  546. LOGD("before join ws thread\n");
  547. __wsHelper->joinWebSocketThread();
  548. LOGD("after join ws thread\n");
  549. CC_SAFE_DELETE(__wsHelper);
  550. }
  551. // NOTE: Refer to the comment in constructor!!!
  552. // cocos2d::Director::getInstance()->getEventDispatcher()->removeEventListener(_resetDirectorListener);
  553. *_isDestroyed = true;
  554. }
  555. bool WebSocketImpl::init(const cocos2d::network::WebSocket::Delegate& delegate,
  556. const std::string& url,
  557. const std::vector<std::string>* protocols/* = nullptr*/,
  558. const std::string& caFilePath/* = ""*/)
  559. {
  560. _delegate = const_cast<cocos2d::network::WebSocket::Delegate*>(&delegate);
  561. _url = url;
  562. _caFilePath = caFilePath;
  563. if (_url.empty())
  564. return false;
  565. if (protocols != nullptr && !protocols->empty())
  566. {
  567. size_t size = protocols->size();
  568. _lwsProtocols = (struct lws_protocols*)malloc((size + 1) * sizeof(struct lws_protocols));
  569. memset(_lwsProtocols, 0, (size + 1) * sizeof(struct lws_protocols));
  570. static uint32_t __wsId = 0;
  571. for (size_t i = 0; i < size; ++i)
  572. {
  573. _lwsProtocols[i].callback = WebSocketCallbackWrapper::onSocketCallback;
  574. size_t nameLen = protocols->at(i).length();
  575. char* name = (char*)malloc(nameLen + 1);
  576. name[nameLen] = '\0';
  577. strcpy(name, protocols->at(i).c_str());
  578. _lwsProtocols[i].name = name;
  579. _lwsProtocols[i].id = ++__wsId;
  580. _lwsProtocols[i].rx_buffer_size = WS_RX_BUFFER_SIZE;
  581. _lwsProtocols[i].per_session_data_size = 0;
  582. _lwsProtocols[i].user = nullptr;
  583. _clientSupportedProtocols += name;
  584. if (i < (size - 1))
  585. {
  586. _clientSupportedProtocols += ",";
  587. }
  588. }
  589. }
  590. bool isWebSocketThreadCreated = true;
  591. if (__wsHelper == nullptr)
  592. {
  593. __wsHelper = new (std::nothrow) WsThreadHelper();
  594. isWebSocketThreadCreated = false;
  595. }
  596. WsMessage* msg = new (std::nothrow) WsMessage();
  597. msg->what = WS_MSG_TO_SUBTHREAD_CREATE_CONNECTION;
  598. msg->user = this;
  599. __wsHelper->sendMessageToWebSocketThread(msg);
  600. // fixed https://github.com/cocos2d/cocos2d-x/issues/17433
  601. // createWebSocketThread has to be after message WS_MSG_TO_SUBTHREAD_CREATE_CONNECTION was sent.
  602. // And websocket thread should only be created once.
  603. if (!isWebSocketThreadCreated)
  604. {
  605. __wsHelper->createWebSocketThread();
  606. }
  607. return true;
  608. }
  609. size_t WebSocketImpl::getBufferedAmount() const
  610. {
  611. return __wsHelper->countBufferdBytes(this);
  612. }
  613. std::string WebSocketImpl::getExtensions() const
  614. {
  615. //join vector with ";"
  616. if (_enabledExtensions.empty()) return "";
  617. std::string ret;
  618. for (int i = 0; i < _enabledExtensions.size(); i++) ret += (_enabledExtensions[i] + "; ");
  619. ret += _enabledExtensions[_enabledExtensions.size() - 1];
  620. return ret;
  621. }
  622. void WebSocketImpl::send(const std::string& message)
  623. {
  624. if (_readyState == cocos2d::network::WebSocket::State::OPEN)
  625. {
  626. // In main thread
  627. cocos2d::network::WebSocket::Data* data = new (std::nothrow) cocos2d::network::WebSocket::Data();
  628. data->bytes = (char*)malloc(message.length() + 1);
  629. // Make sure the last byte is '\0'
  630. data->bytes[message.length()] = '\0';
  631. strcpy(data->bytes, message.c_str());
  632. data->len = static_cast<ssize_t>(message.length());
  633. WsMessage* msg = new (std::nothrow) WsMessage();
  634. msg->what = WS_MSG_TO_SUBTRHEAD_SENDING_STRING;
  635. msg->data = data;
  636. msg->user = this;
  637. __wsHelper->sendMessageToWebSocketThread(msg);
  638. }
  639. else
  640. {
  641. LOGD("Couldn't send message since websocket wasn't opened!\n");
  642. }
  643. }
  644. void WebSocketImpl::send(const unsigned char* binaryMsg, unsigned int len)
  645. {
  646. if (_readyState == cocos2d::network::WebSocket::State::OPEN)
  647. {
  648. // In main thread
  649. cocos2d::network::WebSocket::Data* data = new (std::nothrow) cocos2d::network::WebSocket::Data();
  650. if (len == 0)
  651. {
  652. // If data length is zero, allocate 1 byte for safe.
  653. data->bytes = (char*)malloc(1);
  654. data->bytes[0] = '\0';
  655. }
  656. else
  657. {
  658. data->bytes = (char*)malloc(len);
  659. memcpy((void*)data->bytes, (void*)binaryMsg, len);
  660. }
  661. data->len = len;
  662. WsMessage* msg = new (std::nothrow) WsMessage();
  663. msg->what = WS_MSG_TO_SUBTRHEAD_SENDING_BINARY;
  664. msg->data = data;
  665. msg->user = this;
  666. __wsHelper->sendMessageToWebSocketThread(msg);
  667. }
  668. else
  669. {
  670. LOGD("Couldn't send message since websocket wasn't opened!\n");
  671. }
  672. }
  673. void WebSocketImpl::close()
  674. {
  675. if (_closeState != CloseState::NONE)
  676. {
  677. LOGD("close was invoked, don't invoke it again!\n");
  678. return;
  679. }
  680. _closeState = CloseState::SYNC_CLOSING;
  681. LOGD("close: WebSocket (%p) is closing...\n", this);
  682. {
  683. _readyStateMutex.lock();
  684. if (_readyState == cocos2d::network::WebSocket::State::CLOSED)
  685. {
  686. // If readState is closed, it means that onConnectionClosed was invoked in websocket thread,
  687. // but the callback of performInCocosThread has not been triggered. We need to invoke
  688. // onClose to release the websocket instance.
  689. _readyStateMutex.unlock();
  690. _delegate->onClose(_ws);
  691. return;
  692. }
  693. _readyState = cocos2d::network::WebSocket::State::CLOSING;
  694. _readyStateMutex.unlock();
  695. }
  696. {
  697. std::unique_lock<std::mutex> lkClose(_closeMutex);
  698. _closeCondition.wait(lkClose);
  699. _closeState = CloseState::SYNC_CLOSED;
  700. }
  701. // Wait 5 milliseconds for onConnectionClosed to exit!
  702. std::this_thread::sleep_for(std::chrono::milliseconds(5));
  703. _delegate->onClose(_ws);
  704. }
  705. void WebSocketImpl::closeAsync(int code, const std::string &reason)
  706. {
  707. lws_close_reason(_wsInstance, (lws_close_status)code, (unsigned char*)const_cast<char*>(reason.c_str()), reason.length());
  708. closeAsync();
  709. }
  710. void WebSocketImpl::closeAsync()
  711. {
  712. if (_closeState != CloseState::NONE)
  713. {
  714. LOGD("close was invoked, don't invoke it again!\n");
  715. return;
  716. }
  717. _closeState = CloseState::ASYNC_CLOSING;
  718. LOGD("closeAsync: WebSocket (%p) is closing...\n", this);
  719. std::lock_guard<std::mutex> lk(_readyStateMutex);
  720. if (_readyState == cocos2d::network::WebSocket::State::CLOSED || _readyState == cocos2d::network::WebSocket::State::CLOSING)
  721. {
  722. LOGD("closeAsync: WebSocket (%p) was closed, no need to close it again!\n", this);
  723. return;
  724. }
  725. _readyState = cocos2d::network::WebSocket::State::CLOSING;
  726. }
  727. cocos2d::network::WebSocket::State WebSocketImpl::getReadyState() const
  728. {
  729. std::lock_guard<std::mutex> lk(const_cast<WebSocketImpl*>(this)->_readyStateMutex);
  730. return _readyState;
  731. }
  732. const std::string& WebSocketImpl::getUrl() const
  733. {
  734. return _url;
  735. }
  736. const std::string& WebSocketImpl::getProtocol() const
  737. {
  738. return _selectedProtocol;
  739. }
  740. cocos2d::network::WebSocket::Delegate* WebSocketImpl::getDelegate() const
  741. {
  742. return _delegate;
  743. }
  744. struct lws_vhost* WebSocketImpl::createVhost(struct lws_protocols* protocols, int& sslConnection)
  745. {
  746. auto fileUtils = cocos2d::FileUtils::getInstance();
  747. bool isCAFileExist = fileUtils->isFileExist(_caFilePath);
  748. if (isCAFileExist)
  749. {
  750. _caFilePath = fileUtils->fullPathForFilename(_caFilePath);
  751. }
  752. lws_context_creation_info info = convertToContextCreationInfo(protocols, isCAFileExist);
  753. if (sslConnection != 0)
  754. {
  755. if (isCAFileExist)
  756. {
  757. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
  758. // if ca file is in the apk, try to extract it to writable path
  759. std::string writablePath = fileUtils->getWritablePath();
  760. std::string caFileName = getFileNameForPath(_caFilePath);
  761. std::string newCaFilePath = writablePath + caFileName;
  762. if (fileUtils->isFileExist(newCaFilePath))
  763. {
  764. LOGD("CA file (%s) in writable path exists!", newCaFilePath.c_str());
  765. _caFilePath = newCaFilePath;
  766. info.ssl_ca_filepath = _caFilePath.c_str();
  767. }
  768. else
  769. {
  770. if (fileUtils->isFileExist(_caFilePath))
  771. {
  772. std::string fullPath = fileUtils->fullPathForFilename(_caFilePath);
  773. LOGD("Found CA file: %s", fullPath.c_str());
  774. if (fullPath[0] != '/')
  775. {
  776. LOGD("CA file is in APK");
  777. auto caData = fileUtils->getDataFromFile(fullPath);
  778. if (!caData.isNull())
  779. {
  780. FILE* fp = fopen(newCaFilePath.c_str(), "wb");
  781. if (fp != nullptr)
  782. {
  783. LOGD("New CA file path: %s", newCaFilePath.c_str());
  784. fwrite(caData.getBytes(), caData.getSize(), 1, fp);
  785. fclose(fp);
  786. _caFilePath = newCaFilePath;
  787. info.ssl_ca_filepath = _caFilePath.c_str();
  788. }
  789. else
  790. {
  791. CCASSERT(false, "Open new CA file failed");
  792. }
  793. }
  794. else
  795. {
  796. CCASSERT(false, "CA file is empty!");
  797. }
  798. }
  799. else
  800. {
  801. LOGD("CA file isn't in APK!");
  802. _caFilePath = fullPath;
  803. info.ssl_ca_filepath = _caFilePath.c_str();
  804. }
  805. }
  806. else
  807. {
  808. CCASSERT(false, "CA file doesn't exist!");
  809. }
  810. }
  811. #else
  812. info.ssl_ca_filepath = _caFilePath.c_str();
  813. #endif
  814. }
  815. else
  816. {
  817. LOGD("WARNING: CA Root file isn't set. SSL connection will not peer server certificate\n");
  818. sslConnection = sslConnection | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
  819. }
  820. }
  821. lws_vhost* vhost = lws_create_vhost(__wsContext, &info);
  822. return vhost;
  823. }
  824. void WebSocketImpl::onClientOpenConnectionRequest()
  825. {
  826. if (nullptr != __wsContext)
  827. {
  828. static const struct lws_extension exts[] = {
  829. {
  830. "permessage-deflate",
  831. lws_extension_callback_pm_deflate,
  832. // client_no_context_takeover extension is not supported in the current version, it will cause connection fail
  833. // It may be a bug of lib websocket build
  834. // "permessage-deflate; client_no_context_takeover; client_max_window_bits"
  835. "permessage-deflate; client_max_window_bits"
  836. },
  837. {
  838. "deflate-frame",
  839. lws_extension_callback_pm_deflate,
  840. "deflate_frame"
  841. },
  842. { nullptr, nullptr, nullptr /* terminator */ }
  843. };
  844. _readyStateMutex.lock();
  845. _readyState = cocos2d::network::WebSocket::State::CONNECTING;
  846. _readyStateMutex.unlock();
  847. cocos2d::network::Uri uri = cocos2d::network::Uri::parse(_url);
  848. LOGD("scheme: %s, host: %s, port: %d, path: %s\n", uri.getScheme().c_str(), uri.getHostName().c_str(), static_cast<int>(uri.getPort()), uri.getPathEtc().c_str());
  849. int sslConnection = 0;
  850. if (uri.isSecure())
  851. sslConnection = LCCSCF_USE_SSL;
  852. struct lws_vhost* vhost = nullptr;
  853. if (_lwsProtocols != nullptr)
  854. {
  855. vhost = createVhost(_lwsProtocols, sslConnection);
  856. }
  857. else
  858. {
  859. vhost = createVhost(__defaultProtocols, sslConnection);
  860. }
  861. int port = static_cast<int>(uri.getPort());
  862. if (port == 0)
  863. port = uri.isSecure() ? 443 : 80;
  864. const std::string& hostName = uri.getHostName();
  865. std::string path = uri.getPathEtc();
  866. const std::string& authority = uri.getAuthority();
  867. if (path.empty())
  868. path = "/";
  869. struct lws_client_connect_info connectInfo;
  870. memset(&connectInfo, 0, sizeof(connectInfo));
  871. connectInfo.context = __wsContext;
  872. connectInfo.address = hostName.c_str();
  873. connectInfo.port = port;
  874. connectInfo.ssl_connection = sslConnection;
  875. connectInfo.path = path.c_str();
  876. connectInfo.host = hostName.c_str();
  877. connectInfo.origin = authority.c_str();
  878. connectInfo.protocol = _clientSupportedProtocols.empty() ? nullptr : _clientSupportedProtocols.c_str();
  879. connectInfo.ietf_version_or_minus_one = -1;
  880. connectInfo.userdata = this;
  881. connectInfo.client_exts = exts;
  882. connectInfo.vhost = vhost;
  883. _wsInstance = lws_client_connect_via_info(&connectInfo);
  884. if (nullptr == _wsInstance)
  885. {
  886. onConnectionError();
  887. return;
  888. }
  889. }
  890. else
  891. {
  892. LOGE("Create websocket context failed!");
  893. }
  894. }
  895. int WebSocketImpl::onClientWritable()
  896. {
  897. // LOGD("onClientWritable ... \n");
  898. {
  899. std::lock_guard<std::mutex> readMutex(_readyStateMutex);
  900. if (_readyState == cocos2d::network::WebSocket::State::CLOSING)
  901. {
  902. LOGD("Closing websocket (%p) connection.\n", this);
  903. return -1;
  904. }
  905. }
  906. do
  907. {
  908. std::lock_guard<std::mutex> lk(__wsHelper->_subThreadWsMessageQueueMutex);
  909. if (__wsHelper->_subThreadWsMessageQueue->empty())
  910. {
  911. break;
  912. }
  913. std::list<WsMessage*>::iterator iter = __wsHelper->_subThreadWsMessageQueue->begin();
  914. while (iter != __wsHelper->_subThreadWsMessageQueue->end())
  915. {
  916. WsMessage* msg = *iter;
  917. if (msg->user == this)
  918. {
  919. break;
  920. }
  921. else
  922. {
  923. ++iter;
  924. }
  925. }
  926. ssize_t bytesWrite = 0;
  927. if (iter != __wsHelper->_subThreadWsMessageQueue->end())
  928. {
  929. WsMessage* subThreadMsg = *iter;
  930. cocos2d::network::WebSocket::Data* data = (cocos2d::network::WebSocket::Data*)subThreadMsg->data;
  931. const ssize_t c_bufferSize = WS_RX_BUFFER_SIZE;
  932. const ssize_t remaining = data->len - data->issued;
  933. const ssize_t n = std::min(remaining, c_bufferSize);
  934. WebSocketFrame* frame = nullptr;
  935. if (data->ext)
  936. {
  937. frame = (WebSocketFrame*)data->ext;
  938. }
  939. else
  940. {
  941. frame = new (std::nothrow) WebSocketFrame();
  942. bool success = frame && frame->init((unsigned char*)(data->bytes + data->issued), n);
  943. if (success)
  944. {
  945. data->ext = frame;
  946. }
  947. else
  948. { // If frame initialization failed, delete the frame and drop the sending data
  949. // These codes should never be called.
  950. LOGD("WebSocketFrame initialization failed, drop the sending data, msg(%d)\n", (int)subThreadMsg->id);
  951. delete frame;
  952. CC_SAFE_FREE(data->bytes);
  953. CC_SAFE_DELETE(data);
  954. __wsHelper->_subThreadWsMessageQueue->erase(iter);
  955. CC_SAFE_DELETE(subThreadMsg);
  956. break;
  957. }
  958. }
  959. int writeProtocol;
  960. if (data->issued == 0)
  961. {
  962. if (WS_MSG_TO_SUBTRHEAD_SENDING_STRING == subThreadMsg->what)
  963. {
  964. writeProtocol = LWS_WRITE_TEXT;
  965. }
  966. else
  967. {
  968. writeProtocol = LWS_WRITE_BINARY;
  969. }
  970. // If we have more than 1 fragment
  971. if (data->len > c_bufferSize)
  972. writeProtocol |= LWS_WRITE_NO_FIN;
  973. } else {
  974. // we are in the middle of fragments
  975. writeProtocol = LWS_WRITE_CONTINUATION;
  976. // and if not in the last fragment
  977. if (remaining != n)
  978. writeProtocol |= LWS_WRITE_NO_FIN;
  979. }
  980. bytesWrite = lws_write(_wsInstance, frame->getPayload(), frame->getPayloadLength(), (lws_write_protocol)writeProtocol);
  981. // Handle the result of lws_write
  982. // Buffer overrun?
  983. if (bytesWrite < 0)
  984. {
  985. LOGD("ERROR: msg(%u), lws_write return: %d, but it should be %d, drop this message.\n", subThreadMsg->id, (int)bytesWrite, (int)n);
  986. // socket error, we need to close the socket connection
  987. CC_SAFE_FREE(data->bytes);
  988. delete ((WebSocketFrame*)data->ext);
  989. data->ext = nullptr;
  990. CC_SAFE_DELETE(data);
  991. __wsHelper->_subThreadWsMessageQueue->erase(iter);
  992. CC_SAFE_DELETE(subThreadMsg);
  993. closeAsync();
  994. }
  995. else if (bytesWrite < frame->getPayloadLength())
  996. {
  997. frame->update(bytesWrite);
  998. LOGD("frame wasn't sent completely, bytesWrite: %d, remain: %d\n", (int)bytesWrite, (int)frame->getPayloadLength());
  999. }
  1000. // Do we have another fragments to send?
  1001. else if (remaining > frame->getFrameLength() && bytesWrite == frame->getPayloadLength())
  1002. {
  1003. // A frame was totally sent, plus data->issued to send next frame
  1004. LOGD("msg(%u) append: %d + %d = %d\n", subThreadMsg->id, (int)data->issued, (int)frame->getFrameLength(), (int)(data->issued + frame->getFrameLength()));
  1005. data->issued += frame->getFrameLength();
  1006. delete ((WebSocketFrame*)data->ext);
  1007. data->ext = nullptr;
  1008. }
  1009. // Safely done!
  1010. else
  1011. {
  1012. LOGD("Safely done, msg(%d)!\n", subThreadMsg->id);
  1013. if (remaining == frame->getFrameLength())
  1014. {
  1015. LOGD("msg(%u) append: %d + %d = %d\n", subThreadMsg->id, (int)data->issued, (int)frame->getFrameLength(), (int)(data->issued + frame->getFrameLength()));
  1016. LOGD("msg(%u) was totally sent!\n", subThreadMsg->id);
  1017. }
  1018. else
  1019. {
  1020. LOGD("ERROR: msg(%u), remaining(%d) < bytesWrite(%d)\n", subThreadMsg->id, (int)remaining, (int)frame->getFrameLength());
  1021. LOGD("Drop the msg(%u)\n", subThreadMsg->id);
  1022. closeAsync();
  1023. }
  1024. CC_SAFE_FREE(data->bytes);
  1025. delete ((WebSocketFrame*)data->ext);
  1026. data->ext = nullptr;
  1027. CC_SAFE_DELETE(data);
  1028. __wsHelper->_subThreadWsMessageQueue->erase(iter);
  1029. CC_SAFE_DELETE(subThreadMsg);
  1030. LOGD("-----------------------------------------------------------\n");
  1031. }
  1032. }
  1033. } while(false);
  1034. if (_wsInstance != nullptr)
  1035. {
  1036. lws_callback_on_writable(_wsInstance);
  1037. }
  1038. return 0;
  1039. }
  1040. int WebSocketImpl::onClientReceivedData(void* in, ssize_t len)
  1041. {
  1042. // In websocket thread
  1043. static int packageIndex = 0;
  1044. packageIndex++;
  1045. if (in != nullptr && len > 0)
  1046. {
  1047. LOGD("Receiving data:index:%d, len=%d\n", packageIndex, (int)len);
  1048. unsigned char* inData = (unsigned char*)in;
  1049. _receivedData.insert(_receivedData.end(), inData, inData + len);
  1050. }
  1051. else
  1052. {
  1053. LOGD("Empty message received, index=%d!\n", packageIndex);
  1054. }
  1055. // If no more data pending, send it to the client thread
  1056. size_t remainingSize = lws_remaining_packet_payload(_wsInstance);
  1057. int isFinalFragment = lws_is_final_fragment(_wsInstance);
  1058. // LOGD("remainingSize: %d, isFinalFragment: %d\n", (int)remainingSize, isFinalFragment);
  1059. if (remainingSize == 0 && isFinalFragment)
  1060. {
  1061. std::vector<char>* frameData = new (std::nothrow) std::vector<char>(std::move(_receivedData));
  1062. // reset capacity of received data buffer
  1063. _receivedData.reserve(WS_RESERVE_RECEIVE_BUFFER_SIZE);
  1064. ssize_t frameSize = frameData->size();
  1065. bool isBinary = (lws_frame_is_binary(_wsInstance) != 0);
  1066. if (!isBinary)
  1067. {
  1068. frameData->push_back('\0');
  1069. }
  1070. std::shared_ptr<std::atomic<bool>> isDestroyed = _isDestroyed;
  1071. __wsHelper->sendMessageToCocosThread([this, frameData, frameSize, isBinary, isDestroyed](){
  1072. // In UI thread
  1073. LOGD("Notify data len %d to Cocos thread.\n", (int)frameSize);
  1074. cocos2d::network::WebSocket::Data data;
  1075. data.isBinary = isBinary;
  1076. data.bytes = (char*)frameData->data();
  1077. data.len = frameSize;
  1078. if (*isDestroyed)
  1079. {
  1080. LOGD("WebSocket instance was destroyed!\n");
  1081. }
  1082. else
  1083. {
  1084. _delegate->onMessage(_ws, data);
  1085. }
  1086. delete frameData;
  1087. });
  1088. }
  1089. return 0;
  1090. }
  1091. int WebSocketImpl::onConnectionOpened()
  1092. {
  1093. const lws_protocols* lwsSelectedProtocol = lws_get_protocol(_wsInstance);
  1094. _selectedProtocol = lwsSelectedProtocol->name;
  1095. LOGD("onConnectionOpened...: %p, client protocols: %s, server selected protocol: %s\n", this, _clientSupportedProtocols.c_str(), _selectedProtocol.c_str());
  1096. /*
  1097. * start the ball rolling,
  1098. * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
  1099. */
  1100. lws_callback_on_writable(_wsInstance);
  1101. {
  1102. std::lock_guard<std::mutex> lk(_readyStateMutex);
  1103. if (_readyState == cocos2d::network::WebSocket::State::CLOSING || _readyState == cocos2d::network::WebSocket::State::CLOSED)
  1104. {
  1105. return 0;
  1106. }
  1107. _readyState = cocos2d::network::WebSocket::State::OPEN;
  1108. }
  1109. std::shared_ptr<std::atomic<bool>> isDestroyed = _isDestroyed;
  1110. __wsHelper->sendMessageToCocosThread([this, isDestroyed](){
  1111. if (*isDestroyed)
  1112. {
  1113. LOGD("WebSocket instance was destroyed!\n");
  1114. }
  1115. else
  1116. {
  1117. _delegate->onOpen(_ws);
  1118. }
  1119. });
  1120. return 0;
  1121. }
  1122. int WebSocketImpl::onConnectionError()
  1123. {
  1124. {
  1125. std::lock_guard<std::mutex> lk(_readyStateMutex);
  1126. LOGD("WebSocket (%p) onConnectionError, state: %d ...\n", this, (int)_readyState);
  1127. if (_readyState == cocos2d::network::WebSocket::State::CLOSED)
  1128. {
  1129. return 0;
  1130. }
  1131. _readyState = cocos2d::network::WebSocket::State::CLOSING;
  1132. }
  1133. std::shared_ptr<std::atomic<bool>> isDestroyed = _isDestroyed;
  1134. __wsHelper->sendMessageToCocosThread([this, isDestroyed](){
  1135. if (*isDestroyed)
  1136. {
  1137. LOGD("WebSocket instance was destroyed!\n");
  1138. }
  1139. else
  1140. {
  1141. _delegate->onError(_ws, cocos2d::network::WebSocket::ErrorCode::CONNECTION_FAILURE);
  1142. }
  1143. });
  1144. onConnectionClosed();
  1145. return 0;
  1146. }
  1147. int WebSocketImpl::onConnectionClosed()
  1148. {
  1149. {
  1150. std::lock_guard<std::mutex> lk(_readyStateMutex);
  1151. LOGD("WebSocket (%p) onConnectionClosed, state: %d ...\n", this, (int)_readyState);
  1152. if (_readyState == cocos2d::network::WebSocket::State::CLOSED)
  1153. {
  1154. return 0;
  1155. }
  1156. if (_readyState == cocos2d::network::WebSocket::State::CLOSING)
  1157. {
  1158. if (_closeState == CloseState::SYNC_CLOSING)
  1159. {
  1160. LOGD("onConnectionClosed, WebSocket (%p) is closing by client synchronously.\n", this);
  1161. for(;;)
  1162. {
  1163. std::lock_guard<std::mutex> lkClose(_closeMutex);
  1164. _closeCondition.notify_one();
  1165. if (_closeState == CloseState::SYNC_CLOSED)
  1166. {
  1167. break;
  1168. }
  1169. std::this_thread::sleep_for(std::chrono::milliseconds(1));
  1170. }
  1171. return 0;
  1172. }
  1173. else if (_closeState == CloseState::ASYNC_CLOSING)
  1174. {
  1175. LOGD("onConnectionClosed, WebSocket (%p) is closing by client asynchronously.\n", this);
  1176. }
  1177. else
  1178. {
  1179. LOGD("onConnectionClosed, WebSocket (%p) is closing by server.\n", this);
  1180. }
  1181. }
  1182. else
  1183. {
  1184. LOGD("onConnectionClosed, WebSocket (%p) is closing by server.\n", this);
  1185. }
  1186. _readyState = cocos2d::network::WebSocket::State::CLOSED;
  1187. }
  1188. std::shared_ptr<std::atomic<bool>> isDestroyed = _isDestroyed;
  1189. __wsHelper->sendMessageToCocosThread([this, isDestroyed](){
  1190. if (*isDestroyed)
  1191. {
  1192. LOGD("WebSocket instance (%p) was destroyed!\n", this);
  1193. }
  1194. else
  1195. {
  1196. _delegate->onClose(_ws);
  1197. }
  1198. });
  1199. LOGD("WebSocket (%p) onConnectionClosed DONE!\n", this);
  1200. return 0;
  1201. }
  1202. int WebSocketImpl::onSocketCallback(struct lws *wsi, enum lws_callback_reasons reason, void* in, ssize_t len)
  1203. {
  1204. //LOGD("socket callback for %d reason\n", reason);
  1205. int ret = 0;
  1206. switch (reason)
  1207. {
  1208. case LWS_CALLBACK_CLIENT_ESTABLISHED:
  1209. ret = onConnectionOpened();
  1210. break;
  1211. case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
  1212. ret = onConnectionError();
  1213. break;
  1214. case LWS_CALLBACK_WSI_DESTROY:
  1215. ret = onConnectionClosed();
  1216. break;
  1217. case LWS_CALLBACK_CLIENT_RECEIVE:
  1218. ret = onClientReceivedData(in, len);
  1219. break;
  1220. case LWS_CALLBACK_CLIENT_WRITEABLE:
  1221. ret = onClientWritable();
  1222. break;
  1223. case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
  1224. case LWS_CALLBACK_LOCK_POLL:
  1225. case LWS_CALLBACK_UNLOCK_POLL:
  1226. break;
  1227. case LWS_CALLBACK_PROTOCOL_INIT:
  1228. LOGD("protocol init...");
  1229. break;
  1230. case LWS_CALLBACK_PROTOCOL_DESTROY:
  1231. LOGD("protocol destroy...");
  1232. break;
  1233. case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
  1234. if(in && len > 0)
  1235. {
  1236. _enabledExtensions.push_back(std::string((char*)in, 0, len));
  1237. }
  1238. break;
  1239. default:
  1240. LOGD("WebSocket (%p) Unhandled websocket event: %d\n", this, reason);
  1241. break;
  1242. }
  1243. return ret;
  1244. }
  1245. NS_NETWORK_BEGIN
  1246. /*static*/
  1247. void WebSocket::closeAllConnections()
  1248. {
  1249. WebSocketImpl::closeAllConnections();
  1250. }
  1251. WebSocket::WebSocket()
  1252. {
  1253. _impl = new (std::nothrow) WebSocketImpl(this);
  1254. }
  1255. WebSocket::~WebSocket()
  1256. {
  1257. delete _impl;
  1258. }
  1259. bool WebSocket::init(const Delegate& delegate,
  1260. const std::string& url,
  1261. const std::vector<std::string>* protocols/* = nullptr*/,
  1262. const std::string& caFilePath/* = ""*/)
  1263. {
  1264. return _impl->init(delegate, url, protocols, caFilePath);
  1265. }
  1266. void WebSocket::send(const std::string& message)
  1267. {
  1268. _impl->send(message);
  1269. }
  1270. void WebSocket::send(const unsigned char* binaryMsg, unsigned int len)
  1271. {
  1272. _impl->send(binaryMsg, len);
  1273. }
  1274. void WebSocket::close()
  1275. {
  1276. _impl->close();
  1277. }
  1278. void WebSocket::closeAsync()
  1279. {
  1280. _impl->closeAsync();
  1281. }
  1282. void WebSocket::closeAsync(int code, const std::string &reason)
  1283. {
  1284. _impl->closeAsync(code, reason);
  1285. }
  1286. WebSocket::State WebSocket::getReadyState() const
  1287. {
  1288. return _impl->getReadyState();
  1289. }
  1290. std::string WebSocket::getExtensions() const
  1291. {
  1292. return _impl->getExtensions();
  1293. }
  1294. size_t WebSocket::getBufferedAmount() const
  1295. {
  1296. return _impl->getBufferedAmount();
  1297. }
  1298. const std::string& WebSocket::getUrl() const
  1299. {
  1300. return _impl->getUrl();
  1301. }
  1302. const std::string& WebSocket::getProtocol() const
  1303. {
  1304. return _impl->getProtocol();
  1305. }
  1306. WebSocket::Delegate* WebSocket::getDelegate() const
  1307. {
  1308. return _impl->getDelegate();
  1309. }
  1310. NS_NETWORK_END