WebSockets.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. /**
  2. * @file WebSockets.cpp
  3. * @date 20.05.2015
  4. * @author Markus Sattler
  5. *
  6. * Copyright (c) 2015 Markus Sattler. All rights reserved.
  7. * This file is part of the WebSockets for Arduino.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation; either
  12. * version 2.1 of the License, or (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. */
  24. #include "WebSockets.h"
  25. #ifdef ESP8266
  26. #include <core_esp8266_features.h>
  27. #endif
  28. extern "C" {
  29. #ifdef CORE_HAS_LIBB64
  30. #include <libb64/cencode.h>
  31. #else
  32. #include "libb64/cencode_inc.h"
  33. #endif
  34. }
  35. #ifdef ESP8266
  36. #include <Hash.h>
  37. #elif defined(ESP32)
  38. #include <esp_system.h>
  39. #if ESP_IDF_VERSION_MAJOR >= 4
  40. #include <esp32/sha.h>
  41. #else
  42. #include <hwcrypto/sha.h>
  43. #endif
  44. #else
  45. extern "C" {
  46. #include "libsha1/libsha1.h"
  47. }
  48. #endif
  49. /**
  50. *
  51. * @param client WSclient_t * ptr to the client struct
  52. * @param code uint16_t see RFC
  53. * @param reason ptr to the disconnect reason message
  54. * @param reasonLen length of the disconnect reason message
  55. */
  56. void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) {
  57. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code);
  58. if(client->status == WSC_CONNECTED && code) {
  59. if(reason) {
  60. sendFrame(client, WSop_close, (uint8_t *)reason, reasonLen);
  61. } else {
  62. uint8_t buffer[2];
  63. buffer[0] = ((code >> 8) & 0xFF);
  64. buffer[1] = (code & 0xFF);
  65. sendFrame(client, WSop_close, &buffer[0], 2);
  66. }
  67. }
  68. clientDisconnect(client);
  69. }
  70. /**
  71. *
  72. * @param buf uint8_t * ptr to the buffer for writing
  73. * @param opcode WSopcode_t
  74. * @param length size_t length of the payload
  75. * @param mask bool add dummy mask to the frame (needed for web browser)
  76. * @param maskkey uint8_t[4] key used for payload
  77. * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
  78. */
  79. uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) {
  80. uint8_t headerSize;
  81. // calculate header Size
  82. if(length < 126) {
  83. headerSize = 2;
  84. } else if(length < 0xFFFF) {
  85. headerSize = 4;
  86. } else {
  87. headerSize = 10;
  88. }
  89. if(mask) {
  90. headerSize += 4;
  91. }
  92. // create header
  93. // byte 0
  94. *headerPtr = 0x00;
  95. if(fin) {
  96. *headerPtr |= bit(7); ///< set Fin
  97. }
  98. *headerPtr |= opcode; ///< set opcode
  99. headerPtr++;
  100. // byte 1
  101. *headerPtr = 0x00;
  102. if(mask) {
  103. *headerPtr |= bit(7); ///< set mask
  104. }
  105. if(length < 126) {
  106. *headerPtr |= length;
  107. headerPtr++;
  108. } else if(length < 0xFFFF) {
  109. *headerPtr |= 126;
  110. headerPtr++;
  111. *headerPtr = ((length >> 8) & 0xFF);
  112. headerPtr++;
  113. *headerPtr = (length & 0xFF);
  114. headerPtr++;
  115. } else {
  116. // Normally we never get here (to less memory)
  117. *headerPtr |= 127;
  118. headerPtr++;
  119. *headerPtr = 0x00;
  120. headerPtr++;
  121. *headerPtr = 0x00;
  122. headerPtr++;
  123. *headerPtr = 0x00;
  124. headerPtr++;
  125. *headerPtr = 0x00;
  126. headerPtr++;
  127. *headerPtr = ((length >> 24) & 0xFF);
  128. headerPtr++;
  129. *headerPtr = ((length >> 16) & 0xFF);
  130. headerPtr++;
  131. *headerPtr = ((length >> 8) & 0xFF);
  132. headerPtr++;
  133. *headerPtr = (length & 0xFF);
  134. headerPtr++;
  135. }
  136. if(mask) {
  137. *headerPtr = maskKey[0];
  138. headerPtr++;
  139. *headerPtr = maskKey[1];
  140. headerPtr++;
  141. *headerPtr = maskKey[2];
  142. headerPtr++;
  143. *headerPtr = maskKey[3];
  144. headerPtr++;
  145. }
  146. return headerSize;
  147. }
  148. /**
  149. *
  150. * @param client WSclient_t * ptr to the client struct
  151. * @param opcode WSopcode_t
  152. * @param length size_t length of the payload
  153. * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
  154. * @return true if ok
  155. */
  156. bool WebSockets::sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length, bool fin) {
  157. uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
  158. uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
  159. uint8_t headerSize = createHeader(&buffer[0], opcode, length, client->cIsClient, maskKey, fin);
  160. if(write(client, &buffer[0], headerSize) != headerSize) {
  161. return false;
  162. }
  163. return true;
  164. }
  165. /**
  166. *
  167. * @param client WSclient_t * ptr to the client struct
  168. * @param opcode WSopcode_t
  169. * @param payload uint8_t * ptr to the payload
  170. * @param length size_t length of the payload
  171. * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
  172. * @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
  173. * @return true if ok
  174. */
  175. bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin, bool headerToPayload) {
  176. if(client->tcp && !client->tcp->connected()) {
  177. DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num);
  178. return false;
  179. }
  180. if(client->status != WSC_CONNECTED) {
  181. DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num);
  182. return false;
  183. }
  184. DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num);
  185. DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, client->cIsClient, length, headerToPayload);
  186. if(opcode == WSop_text) {
  187. DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
  188. }
  189. uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
  190. uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
  191. uint8_t headerSize;
  192. uint8_t * headerPtr;
  193. uint8_t * payloadPtr = payload;
  194. bool useInternBuffer = false;
  195. bool ret = true;
  196. // calculate header Size
  197. if(length < 126) {
  198. headerSize = 2;
  199. } else if(length < 0xFFFF) {
  200. headerSize = 4;
  201. } else {
  202. headerSize = 10;
  203. }
  204. if(client->cIsClient) {
  205. headerSize += 4;
  206. }
  207. #ifdef WEBSOCKETS_USE_BIG_MEM
  208. // only for ESP since AVR has less HEAP
  209. // try to send data in one TCP package (only if some free Heap is there)
  210. if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) {
  211. DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
  212. uint8_t * dataPtr = (uint8_t *)malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
  213. if(dataPtr) {
  214. memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
  215. headerToPayload = true;
  216. useInternBuffer = true;
  217. payloadPtr = dataPtr;
  218. }
  219. }
  220. #endif
  221. // set Header Pointer
  222. if(headerToPayload) {
  223. // calculate offset in payload
  224. headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
  225. } else {
  226. headerPtr = &buffer[0];
  227. }
  228. if(client->cIsClient && useInternBuffer) {
  229. // if we use a Intern Buffer we can modify the data
  230. // by this fact its possible the do the masking
  231. for(uint8_t x = 0; x < sizeof(maskKey); x++) {
  232. maskKey[x] = random(0xFF);
  233. }
  234. }
  235. createHeader(headerPtr, opcode, length, client->cIsClient, maskKey, fin);
  236. if(client->cIsClient && useInternBuffer) {
  237. uint8_t * dataMaskPtr;
  238. if(headerToPayload) {
  239. dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
  240. } else {
  241. dataMaskPtr = payloadPtr;
  242. }
  243. for(size_t x = 0; x < length; x++) {
  244. dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
  245. }
  246. }
  247. #ifndef NODEBUG_WEBSOCKETS
  248. unsigned long start = micros();
  249. #endif
  250. if(headerToPayload) {
  251. // header has be added to payload
  252. // payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
  253. // offset in payload is calculatetd 14 - headerSize
  254. if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
  255. ret = false;
  256. }
  257. } else {
  258. // send header
  259. if(write(client, &buffer[0], headerSize) != headerSize) {
  260. ret = false;
  261. }
  262. if(payloadPtr && length > 0) {
  263. // send payload
  264. if(write(client, &payloadPtr[0], length) != length) {
  265. ret = false;
  266. }
  267. }
  268. }
  269. DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start));
  270. #ifdef WEBSOCKETS_USE_BIG_MEM
  271. if(useInternBuffer && payloadPtr) {
  272. free(payloadPtr);
  273. }
  274. #endif
  275. return ret;
  276. }
  277. /**
  278. * callen when HTTP header is done
  279. * @param client WSclient_t * ptr to the client struct
  280. */
  281. void WebSockets::headerDone(WSclient_t * client) {
  282. client->status = WSC_CONNECTED;
  283. client->cWsRXsize = 0;
  284. DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num);
  285. #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
  286. client->cHttpLine = "";
  287. handleWebsocket(client);
  288. #endif
  289. }
  290. /**
  291. * handle the WebSocket stream
  292. * @param client WSclient_t * ptr to the client struct
  293. */
  294. void WebSockets::handleWebsocket(WSclient_t * client) {
  295. if(client->cWsRXsize == 0) {
  296. handleWebsocketCb(client);
  297. }
  298. }
  299. /**
  300. * wait for
  301. * @param client
  302. * @param size
  303. */
  304. bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
  305. if(!client->tcp || !client->tcp->connected()) {
  306. return false;
  307. }
  308. if(size > WEBSOCKETS_MAX_HEADER_SIZE) {
  309. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size);
  310. return false;
  311. }
  312. if(client->cWsRXsize >= size) {
  313. return true;
  314. }
  315. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize);
  316. readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) {
  317. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok);
  318. if(ok) {
  319. client->cWsRXsize = size;
  320. server->handleWebsocketCb(client);
  321. } else {
  322. DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num);
  323. client->cWsRXsize = 0;
  324. // timeout or error
  325. server->clientDisconnect(client, 1002);
  326. }
  327. },
  328. this, size, std::placeholders::_1, std::placeholders::_2));
  329. return false;
  330. }
  331. void WebSockets::handleWebsocketCb(WSclient_t * client) {
  332. if(!client->tcp || !client->tcp->connected()) {
  333. return;
  334. }
  335. uint8_t * buffer = client->cWsHeader;
  336. WSMessageHeader_t * header = &client->cWsHeaderDecode;
  337. uint8_t * payload = NULL;
  338. uint8_t headerLen = 2;
  339. if(!handleWebsocketWaitFor(client, headerLen)) {
  340. return;
  341. }
  342. // split first 2 bytes in the data
  343. header->fin = ((*buffer >> 7) & 0x01);
  344. header->rsv1 = ((*buffer >> 6) & 0x01);
  345. header->rsv2 = ((*buffer >> 5) & 0x01);
  346. header->rsv3 = ((*buffer >> 4) & 0x01);
  347. header->opCode = (WSopcode_t)(*buffer & 0x0F);
  348. buffer++;
  349. header->mask = ((*buffer >> 7) & 0x01);
  350. header->payloadLen = (WSopcode_t)(*buffer & 0x7F);
  351. buffer++;
  352. if(header->payloadLen == 126) {
  353. headerLen += 2;
  354. if(!handleWebsocketWaitFor(client, headerLen)) {
  355. return;
  356. }
  357. header->payloadLen = buffer[0] << 8 | buffer[1];
  358. buffer += 2;
  359. } else if(header->payloadLen == 127) {
  360. headerLen += 8;
  361. // read 64bit integer as length
  362. if(!handleWebsocketWaitFor(client, headerLen)) {
  363. return;
  364. }
  365. if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
  366. // really too big!
  367. header->payloadLen = 0xFFFFFFFF;
  368. } else {
  369. header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
  370. }
  371. buffer += 8;
  372. }
  373. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num);
  374. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode);
  375. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen);
  376. if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
  377. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen);
  378. clientDisconnect(client, 1009);
  379. return;
  380. }
  381. if(header->mask) {
  382. headerLen += 4;
  383. if(!handleWebsocketWaitFor(client, headerLen)) {
  384. return;
  385. }
  386. header->maskKey = buffer;
  387. buffer += 4;
  388. }
  389. if(header->payloadLen > 0) {
  390. // if text data we need one more
  391. payload = (uint8_t *)malloc(header->payloadLen + 1);
  392. if(!payload) {
  393. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen);
  394. clientDisconnect(client, 1011);
  395. return;
  396. }
  397. readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload));
  398. } else {
  399. handleWebsocketPayloadCb(client, true, NULL);
  400. }
  401. }
  402. void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) {
  403. WSMessageHeader_t * header = &client->cWsHeaderDecode;
  404. if(ok) {
  405. if(header->payloadLen > 0) {
  406. payload[header->payloadLen] = 0x00;
  407. if(header->mask) {
  408. //decode XOR
  409. for(size_t i = 0; i < header->payloadLen; i++) {
  410. payload[i] = (payload[i] ^ header->maskKey[i % 4]);
  411. }
  412. }
  413. }
  414. switch(header->opCode) {
  415. case WSop_text:
  416. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload);
  417. // no break here!
  418. case WSop_binary:
  419. case WSop_continuation:
  420. messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
  421. break;
  422. case WSop_ping:
  423. // send pong back
  424. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ping received (%s)\n", client->num, payload ? (const char *)payload : "");
  425. sendFrame(client, WSop_pong, payload, header->payloadLen);
  426. messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
  427. break;
  428. case WSop_pong:
  429. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char *)payload : "");
  430. client->pongReceived = true;
  431. messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
  432. break;
  433. case WSop_close: {
  434. #ifndef NODEBUG_WEBSOCKETS
  435. uint16_t reasonCode = 1000;
  436. if(header->payloadLen >= 2) {
  437. reasonCode = payload[0] << 8 | payload[1];
  438. }
  439. #endif
  440. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d\n", client->num, reasonCode);
  441. if(header->payloadLen > 2) {
  442. DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2));
  443. } else {
  444. DEBUG_WEBSOCKETS("\n");
  445. }
  446. clientDisconnect(client, 1000);
  447. } break;
  448. default:
  449. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] got unknown opcode: %d\n", client->num, header->opCode);
  450. clientDisconnect(client, 1002);
  451. break;
  452. }
  453. if(payload) {
  454. free(payload);
  455. }
  456. // reset input
  457. client->cWsRXsize = 0;
  458. #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
  459. //register callback for next message
  460. handleWebsocketWaitFor(client, 2);
  461. #endif
  462. } else {
  463. DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num);
  464. free(payload);
  465. clientDisconnect(client, 1002);
  466. }
  467. }
  468. /**
  469. * generate the key for Sec-WebSocket-Accept
  470. * @param clientKey String
  471. * @return String Accept Key
  472. */
  473. String WebSockets::acceptKey(String & clientKey) {
  474. uint8_t sha1HashBin[20] = { 0 };
  475. #ifdef ESP8266
  476. sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
  477. #elif defined(ESP32)
  478. String data = clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  479. esp_sha(SHA1, (unsigned char *)data.c_str(), data.length(), &sha1HashBin[0]);
  480. #else
  481. clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  482. SHA1_CTX ctx;
  483. SHA1Init(&ctx);
  484. SHA1Update(&ctx, (const unsigned char *)clientKey.c_str(), clientKey.length());
  485. SHA1Final(&sha1HashBin[0], &ctx);
  486. #endif
  487. String key = base64_encode(sha1HashBin, 20);
  488. key.trim();
  489. return key;
  490. }
  491. /**
  492. * base64_encode
  493. * @param data uint8_t *
  494. * @param length size_t
  495. * @return base64 encoded String
  496. */
  497. String WebSockets::base64_encode(uint8_t * data, size_t length) {
  498. size_t size = ((length * 1.6f) + 1);
  499. char * buffer = (char *)malloc(size);
  500. if(buffer) {
  501. base64_encodestate _state;
  502. base64_init_encodestate(&_state);
  503. int len = base64_encode_block((const char *)&data[0], length, &buffer[0], &_state);
  504. len = base64_encode_blockend((buffer + len), &_state);
  505. String base64 = String(buffer);
  506. free(buffer);
  507. return base64;
  508. }
  509. return String("-FAIL-");
  510. }
  511. /**
  512. * read x byte from tcp or get timeout
  513. * @param client WSclient_t *
  514. * @param out uint8_t * data buffer
  515. * @param n size_t byte count
  516. * @return true if ok
  517. */
  518. bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) {
  519. #if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
  520. if(!client->tcp || !client->tcp->connected()) {
  521. return false;
  522. }
  523. client->tcp->readBytes(out, n, std::bind([](WSclient_t * client, bool ok, WSreadWaitCb cb) {
  524. if(cb) {
  525. cb(client, ok);
  526. }
  527. },
  528. client, std::placeholders::_1, cb));
  529. #else
  530. unsigned long t = millis();
  531. ssize_t len;
  532. DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t);
  533. while(n > 0) {
  534. if(client->tcp == NULL) {
  535. DEBUG_WEBSOCKETS("[readCb] tcp is null!\n");
  536. if(cb) {
  537. cb(client, false);
  538. }
  539. return false;
  540. }
  541. if(!client->tcp->connected()) {
  542. DEBUG_WEBSOCKETS("[readCb] not connected!\n");
  543. if(cb) {
  544. cb(client, false);
  545. }
  546. return false;
  547. }
  548. if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
  549. DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %lu\n", (millis() - t));
  550. if(cb) {
  551. cb(client, false);
  552. }
  553. return false;
  554. }
  555. if(!client->tcp->available()) {
  556. WEBSOCKETS_YIELD_MORE();
  557. continue;
  558. }
  559. len = client->tcp->read((uint8_t *)out, n);
  560. if(len > 0) {
  561. t = millis();
  562. out += len;
  563. n -= len;
  564. //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
  565. } else {
  566. //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
  567. }
  568. if(n > 0) {
  569. WEBSOCKETS_YIELD();
  570. }
  571. }
  572. if(cb) {
  573. cb(client, true);
  574. }
  575. WEBSOCKETS_YIELD();
  576. #endif
  577. return true;
  578. }
  579. /**
  580. * write x byte to tcp or get timeout
  581. * @param client WSclient_t *
  582. * @param out uint8_t * data buffer
  583. * @param n size_t byte count
  584. * @return bytes send
  585. */
  586. size_t WebSockets::write(WSclient_t * client, uint8_t * out, size_t n) {
  587. if(out == NULL)
  588. return 0;
  589. if(client == NULL)
  590. return 0;
  591. unsigned long t = millis();
  592. size_t len = 0;
  593. size_t total = 0;
  594. DEBUG_WEBSOCKETS("[write] n: %zu t: %lu\n", n, t);
  595. while(n > 0) {
  596. if(client->tcp == NULL) {
  597. DEBUG_WEBSOCKETS("[write] tcp is null!\n");
  598. break;
  599. }
  600. if(!client->tcp->connected()) {
  601. DEBUG_WEBSOCKETS("[write] not connected!\n");
  602. break;
  603. }
  604. if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
  605. DEBUG_WEBSOCKETS("[write] write TIMEOUT! %lu\n", (millis() - t));
  606. break;
  607. }
  608. len = client->tcp->write((const uint8_t *)out, n);
  609. if(len) {
  610. t = millis();
  611. out += len;
  612. n -= len;
  613. total += len;
  614. //DEBUG_WEBSOCKETS("write %d left %d!\n", len, n);
  615. } else {
  616. DEBUG_WEBSOCKETS("WS write %d failed left %d!\n", len, n);
  617. }
  618. if(n > 0) {
  619. WEBSOCKETS_YIELD();
  620. }
  621. }
  622. WEBSOCKETS_YIELD();
  623. return total;
  624. }
  625. size_t WebSockets::write(WSclient_t * client, const char * out) {
  626. if(client == NULL)
  627. return 0;
  628. if(out == NULL)
  629. return 0;
  630. return write(client, (uint8_t *)out, strlen(out));
  631. }
  632. /**
  633. * enable ping/pong heartbeat process
  634. * @param client WSclient_t *
  635. * @param pingInterval uint32_t how often ping will be sent
  636. * @param pongTimeout uint32_t millis after which pong should timout if not received
  637. * @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
  638. */
  639. void WebSockets::enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
  640. if(client == NULL)
  641. return;
  642. client->pingInterval = pingInterval;
  643. client->pongTimeout = pongTimeout;
  644. client->disconnectTimeoutCount = disconnectTimeoutCount;
  645. client->pongReceived = false;
  646. }
  647. /**
  648. * handle ping/pong heartbeat timeout process
  649. * @param client WSclient_t *
  650. */
  651. void WebSockets::handleHBTimeout(WSclient_t * client) {
  652. if(client->pingInterval) { // if heartbeat is enabled
  653. uint32_t pi = millis() - client->lastPing;
  654. if(client->pongReceived) {
  655. client->pongTimeoutCount = 0;
  656. } else {
  657. if(pi > client->pongTimeout) { // pong not received in time
  658. client->pongTimeoutCount++;
  659. client->lastPing = millis() - client->pingInterval - 500; // force ping on the next run
  660. DEBUG_WEBSOCKETS("[HBtimeout] pong TIMEOUT! lp=%d millis=%d pi=%d count=%d\n", client->lastPing, millis(), pi, client->pongTimeoutCount);
  661. if(client->disconnectTimeoutCount && client->pongTimeoutCount >= client->disconnectTimeoutCount) {
  662. DEBUG_WEBSOCKETS("[HBtimeout] count=%d, DISCONNECTING\n", client->pongTimeoutCount);
  663. clientDisconnect(client);
  664. }
  665. }
  666. }
  667. }
  668. }