WebSockets.cpp 24 KB

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